@@ -4,7 +4,7 @@ import * as fs from 'fs'
4
4
import { tl } from '../util/intl'
5
5
import { Path } from '../util/path'
6
6
import { store } from '../util/store'
7
- import { roundToN } from '../util/misc'
7
+ import { isEqualVector , roundToN } from '../util/misc'
8
8
import { compileMC } from '../compileLangMC'
9
9
import { removeKeyGently } from '../util/misc'
10
10
import { generateTree , TreeBranch , TreeLeaf } from '../util/treeGen'
@@ -41,6 +41,8 @@ interface vanillaAnimationExporterSettings {
41
41
autoDistance : number
42
42
autoDistanceMovementThreshold : number
43
43
manualDistance : number
44
+ deduplicatePositionFrames : boolean
45
+ deduplicateRotationFrames : boolean
44
46
}
45
47
46
48
interface MCBConfig {
@@ -908,11 +910,19 @@ async function createMCFile(
908
910
const boneTrees = Object . fromEntries (
909
911
Object . keys ( bones ) . map ( ( v ) => [
910
912
v ,
911
- { root : '' , display : '' } ,
913
+ {
914
+ root : { v : '' , trimmed : false } ,
915
+ display : { v : '' , trimmed : false } ,
916
+ } ,
912
917
] )
913
918
)
914
919
915
920
for ( const boneName of Object . keys ( bones ) ) {
921
+ interface TreeReturn {
922
+ v : string
923
+ trimmed : boolean
924
+ }
925
+
916
926
function createRootTree ( item : TreeBranch | TreeLeaf ) {
917
927
switch ( item . type ) {
918
928
case 'branch' :
@@ -927,6 +937,43 @@ async function createMCFile(
927
937
}
928
938
}
929
939
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
+
930
977
function createDisplayTree ( item : TreeBranch | TreeLeaf ) {
931
978
switch ( item . type ) {
932
979
case 'branch' :
@@ -941,12 +988,53 @@ async function createMCFile(
941
988
}
942
989
}
943
990
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 }
950
1038
}
951
1039
return boneTrees
952
1040
}
@@ -1039,22 +1127,28 @@ async function createMCFile(
1039
1127
# Bone Roots
1040
1128
execute if entity @s[type=${ entityTypes . boneRoot } ] run {
1041
1129
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 {
1044
1134
name tree/${ boneName } _root_top
1045
- ${ trees . root }
1135
+ ${ trees . root . v }
1046
1136
}`
1137
+ }
1047
1138
) . join ( '\n' ) }
1048
1139
execute store result entity @s Air short 1 run scoreboard players get .this ${ scoreboards . frame }
1049
1140
}
1050
1141
# Bone Displays
1051
1142
execute if entity @s[type=${ entityTypes . boneDisplay } ] run {
1052
1143
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 {
1055
1148
name tree/${ boneName } _display_top
1056
- ${ trees . display }
1149
+ ${ trees . display . v }
1057
1150
}`
1151
+ }
1058
1152
) . join ( '\n' ) }
1059
1153
# Make sure rotation stays aligned with root entity
1060
1154
execute positioned as @s run tp @s ~ ~ ~ ~ ~
@@ -1487,6 +1581,32 @@ const Exporter = (AJ: any) => {
1487
1581
} ,
1488
1582
dependencies : [ 'vanillaAnimationExporter.autoDistance' ] ,
1489
1583
} ,
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
+ } ,
1490
1610
modelTag : {
1491
1611
title : tl (
1492
1612
'animatedJava.exporters.generic.settings.modelTag.title'
0 commit comments