@@ -4,7 +4,7 @@ import * as fs from 'fs'
44import { tl } from '../util/intl'
55import { Path } from '../util/path'
66import { store } from '../util/store'
7- import { roundToN } from '../util/misc'
7+ import { isEqualVector , roundToN } from '../util/misc'
88import { compileMC } from '../compileLangMC'
99import { removeKeyGently } from '../util/misc'
1010import { generateTree , TreeBranch , TreeLeaf } from '../util/treeGen'
@@ -41,6 +41,8 @@ interface vanillaAnimationExporterSettings {
4141 autoDistance : number
4242 autoDistanceMovementThreshold : number
4343 manualDistance : number
44+ deduplicatePositionFrames : boolean
45+ deduplicateRotationFrames : boolean
4446}
4547
4648interface MCBConfig {
@@ -908,11 +910,19 @@ async function createMCFile(
908910 const boneTrees = Object . fromEntries (
909911 Object . keys ( bones ) . map ( ( v ) => [
910912 v ,
911- { root : '' , display : '' } ,
913+ {
914+ root : { v : '' , trimmed : false } ,
915+ display : { v : '' , trimmed : false } ,
916+ } ,
912917 ] )
913918 )
914919
915920 for ( const boneName of Object . keys ( bones ) ) {
921+ interface TreeReturn {
922+ v : string
923+ trimmed : boolean
924+ }
925+
916926 function createRootTree ( item : TreeBranch | TreeLeaf ) {
917927 switch ( item . type ) {
918928 case 'branch' :
@@ -927,6 +937,43 @@ async function createMCFile(
927937 }
928938 }
929939
940+ let lastPos = { x : NaN , y : NaN , z : NaN }
941+ function createDeduplicatedRootTree (
942+ item : TreeBranch | TreeLeaf
943+ ) : TreeReturn {
944+ switch ( item . type ) {
945+ case 'branch' :
946+ const inside : TreeReturn [ ] = item . items
947+ . map ( ( v : any ) =>
948+ createDeduplicatedRootTree ( v )
949+ )
950+ . filter ( ( v ) => ! v . trimmed )
951+ if ( inside . length == 0 ) {
952+ return { v : '' , trimmed : true }
953+ } else if ( inside . length == 1 ) {
954+ return inside [ 0 ]
955+ }
956+ // prettier-ignore
957+ return {
958+ v : `execute if score .this ${ scoreboards . frame } matches ${ item . min } ..${ item . max - 1 } run {
959+ name tree/${ boneName } _root_${ item . min } -${ item . max - 1 }
960+ ${ inside . reduce ( ( p , c ) => p + ( c . v ? c . v + '\n' : '' ) , '' ) }
961+ }` ,
962+ trimmed : false
963+ }
964+ case 'leaf' :
965+ const pos = getRot ( boneName , item )
966+ if ( isEqualVector ( pos , lastPos ) ) {
967+ return { v : '' , trimmed : true }
968+ }
969+ lastPos = pos
970+ return {
971+ v : `execute if score .this ${ scoreboards . frame } matches ${ item . index } run tp @s ^${ pos . x } ^${ pos . y } ^${ pos . z } ~ ~` ,
972+ trimmed : false ,
973+ }
974+ }
975+ }
976+
930977 function createDisplayTree ( item : TreeBranch | TreeLeaf ) {
931978 switch ( item . type ) {
932979 case 'branch' :
@@ -941,12 +988,53 @@ async function createMCFile(
941988 }
942989 }
943990
944- boneTrees [ boneName ] . root = animationTree . items
945- . map ( ( v : any ) => createRootTree ( v ) )
946- . join ( '\n' )
947- boneTrees [ boneName ] . display = animationTree . items
948- . map ( ( v : any ) => createDisplayTree ( v ) )
949- . join ( '\n' )
991+ let lastRot = { x : NaN , y : NaN , z : NaN }
992+ function createDeduplicatedDisplayTree (
993+ item : TreeBranch | TreeLeaf
994+ ) : TreeReturn {
995+ switch ( item . type ) {
996+ case 'branch' :
997+ const inside : TreeReturn [ ] = item . items
998+ . map ( ( v : any ) =>
999+ createDeduplicatedDisplayTree ( v )
1000+ )
1001+ . filter ( ( v ) => ! v . trimmed )
1002+ if ( inside . length == 0 ) {
1003+ return { v : '' , trimmed : true }
1004+ } else if ( inside . length == 1 ) {
1005+ return inside [ 0 ]
1006+ }
1007+ // prettier-ignore
1008+ return {
1009+ v : `execute if score .this ${ scoreboards . frame } matches ${ item . min } ..${ item . max - 1 } run {
1010+ name tree/${ boneName } _display_${ item . min } -${ item . max - 1 }
1011+ ${ inside . reduce ( ( p , c ) => p + ( c . v ? c . v + '\n' : '' ) , '' ) }
1012+ }` ,
1013+ trimmed : false
1014+ }
1015+ case 'leaf' :
1016+ const rot = getRot ( boneName , item )
1017+ if ( isEqualVector ( rot , lastRot ) ) {
1018+ return { v : '' , trimmed : true }
1019+ }
1020+ lastRot = rot
1021+ return {
1022+ v : `execute if score .this ${ scoreboards . frame } matches ${ item . index } run data modify entity @s Pose.Head set value [${ rot . x } f,${ rot . y } f,${ rot . z } f]` ,
1023+ trimmed : false ,
1024+ }
1025+ }
1026+ }
1027+
1028+ // prettier-ignore
1029+ boneTrees [ boneName ] . root =
1030+ exporterSettings . deduplicatePositionFrames
1031+ ? createDeduplicatedRootTree ( animationTree )
1032+ : { v : createRootTree ( animationTree ) , trimmed : false }
1033+ // prettier-ignore
1034+ boneTrees [ boneName ] . display =
1035+ exporterSettings . deduplicatePositionFrames
1036+ ? createDeduplicatedDisplayTree ( animationTree )
1037+ : { v : createDisplayTree ( animationTree ) , trimmed : false }
9501038 }
9511039 return boneTrees
9521040 }
@@ -1039,22 +1127,28 @@ async function createMCFile(
10391127 # Bone Roots
10401128 execute if entity @s[type=${ entityTypes . boneRoot } ] run {
10411129 name tree/root_bone_name
1042- ${ Object . entries ( boneTrees ) . map ( ( [ boneName , trees ] ) =>
1043- `execute if entity @s[tag=${ format ( tags . individualBone , { boneName} ) } ] run {
1130+ ${ Object . entries ( boneTrees ) . map ( ( [ boneName , trees ] ) => {
1131+ // Remove trimmed bone trees (Though there will never be any)
1132+ if ( trees . root . trimmed ) return ''
1133+ return `execute if entity @s[tag=${ format ( tags . individualBone , { boneName} ) } ] run {
10441134 name tree/${ boneName } _root_top
1045- ${ trees . root }
1135+ ${ trees . root . v }
10461136 }`
1137+ }
10471138 ) . join ( '\n' ) }
10481139 execute store result entity @s Air short 1 run scoreboard players get .this ${ scoreboards . frame }
10491140 }
10501141 # Bone Displays
10511142 execute if entity @s[type=${ entityTypes . boneDisplay } ] run {
10521143 name tree/display_bone_name
1053- ${ Object . entries ( boneTrees ) . map ( ( [ boneName , trees ] ) =>
1054- `execute if entity @s[tag=${ format ( tags . individualBone , { boneName} ) } ] run {
1144+ ${ Object . entries ( boneTrees ) . map ( ( [ boneName , trees ] ) => {
1145+ // Remove trimmed bone trees (Though there will never be any)
1146+ if ( trees . display . trimmed ) return ''
1147+ return `execute if entity @s[tag=${ format ( tags . individualBone , { boneName} ) } ] run {
10551148 name tree/${ boneName } _display_top
1056- ${ trees . display }
1149+ ${ trees . display . v }
10571150 }`
1151+ }
10581152 ) . join ( '\n' ) }
10591153 # Make sure rotation stays aligned with root entity
10601154 execute positioned as @s run tp @s ~ ~ ~ ~ ~
@@ -1487,6 +1581,32 @@ const Exporter = (AJ: any) => {
14871581 } ,
14881582 dependencies : [ 'vanillaAnimationExporter.autoDistance' ] ,
14891583 } ,
1584+ deduplicatePositionFrames : {
1585+ title : tl (
1586+ 'animatedJava.exporters.vanillaAnimation.settings.deduplicatePositionFrames.title'
1587+ ) ,
1588+ description : tl (
1589+ 'animatedJava.exporters.vanillaAnimation.settings.deduplicatePositionFrames.description'
1590+ ) ,
1591+ type : 'checkbox' ,
1592+ default : false ,
1593+ onUpdate ( d : aj . SettingDescriptor ) {
1594+ return d
1595+ } ,
1596+ } ,
1597+ deduplicateRotationFrames : {
1598+ title : tl (
1599+ 'animatedJava.exporters.vanillaAnimation.settings.deduplicateRotationFrames.title'
1600+ ) ,
1601+ description : tl (
1602+ 'animatedJava.exporters.vanillaAnimation.settings.deduplicateRotationFrames.description'
1603+ ) ,
1604+ type : 'checkbox' ,
1605+ default : true ,
1606+ onUpdate ( d : aj . SettingDescriptor ) {
1607+ return d
1608+ } ,
1609+ } ,
14901610 modelTag : {
14911611 title : tl (
14921612 'animatedJava.exporters.generic.settings.modelTag.title'
0 commit comments