Skip to content

Commit 4537946

Browse files
committed
🤖 Performance Improvements
- Changed bone selection to full macro-UUID selection. 33% faster on average. - Reduced the number of branches in the summon function. - Brought back frame deduplication. - Fixed tweening between animations not updating bones that only change on the first frame.
1 parent ebd8580 commit 4537946

File tree

6 files changed

+165
-150
lines changed

6 files changed

+165
-150
lines changed

TODO.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@
103103
- [x] Locator rotation inheritance support - looks like they've supported it all this time...
104104
- [x] Apply variant keyframes in animations.
105105
- [x] Figure out how cameras will work.
106+
- [x] See how much swapping to a static list of UUIDs for selecting bones effects performance.
106107
- [ ] Check for references to non-existant functions in merged function tags, and remove them.
107-
- [ ] When applying variants, remove any bones that have no elements with textured faces.
108+
- [ ] When applying variants, remove / replace any bones that have / had no elements with textured faces.
108109

109110
# Resource Pack
110111

src/lang/en.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,10 @@ animated_java.dialog.blueprint_settings.teleportation_duration.title: Teleport D
131131
animated_java.dialog.blueprint_settings.teleportation_duration.description: The duration of the teleportation between keyframes in ticks. This is the time over which the model will visually interpolate between it's old position to it's new position. Higher values will cause animations to lose precision.
132132

133133
animated_java.dialog.blueprint_settings.use_storage_for_animation.title: Use Storage for Animation
134-
animated_java.dialog.blueprint_settings.use_storage_for_animation.description: Whether or not to use NBT storage to store animation data instead of functions. This will vastly reduce the number of functions in the generated Data Pack, but is 33% slower than the function method.
134+
animated_java.dialog.blueprint_settings.use_storage_for_animation.description: |-
135+
Whether or not to use NBT storage to store animation data instead of functions.
136+
This will vastly reduce the number of functions in the generated Data Pack, but is 33% slower than the function method.
137+
WARNING: This animation method does not support cameras, or locators!
135138
136139
## Bone Config Dialog
137140
animated_java.dialog.bone_config.title: Bone Config

src/systems/animated_java.mcb

Lines changed: 93 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -160,33 +160,35 @@ dir <%export_namespace%> {
160160
execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.name)%>] run \
161161
function *<%export_namespace%>/animations/<%animation.name%>/zzz/on_tick
162162
}
163-
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'locator')) as node {
164-
IF (node.config?.use_entity) {
165-
execute on passengers if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] run { with entity @s data.locators.<%node.name%>
166-
$execute as $(uuid) positioned ^$(posx) ^$(posy) ^$(posz) rotated ~$(roty) ~$(rotx) run {
167-
tp @s ~ ~ ~ ~ ~
168-
<%%
169-
if (node.config?.ticking_commands) {
170-
emit.mcb(node.config.ticking_commands)
171-
}
172-
%%>
163+
execute on passengers if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] run {
164+
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'locator')) as node {
165+
IF (node.config?.use_entity) {
166+
block { with entity @s data.locators.<%node.name%>
167+
$execute as $(uuid) positioned ^$(posx) ^$(posy) ^$(posz) rotated ~$(roty) ~$(rotx) run {
168+
tp @s ~ ~ ~ ~ ~
169+
<%%
170+
if (node.config?.ticking_commands) {
171+
emit.mcb(node.config.ticking_commands)
172+
}
173+
%%>
174+
}
173175
}
174-
}
175-
} ELSE IF (node.config?.ticking_commands) {
176-
execute on passengers if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] run { with entity @s data.locators.<%node.name%>
177-
$execute positioned ^$(posx) ^$(posy) ^$(posz) rotated ~$(roty) ~$(rotx) run {
178-
<%%
179-
if (node.config?.ticking_commands) {
180-
emit.mcb(node.config.ticking_commands)
181-
}
182-
%%>
176+
} ELSE IF (node.config?.ticking_commands) {
177+
block { with entity @s data.locators.<%node.name%>
178+
$execute positioned ^$(posx) ^$(posy) ^$(posz) rotated ~$(roty) ~$(rotx) run {
179+
<%%
180+
if (node.config?.ticking_commands) {
181+
emit.mcb(node.config.ticking_commands)
182+
}
183+
%%>
184+
}
183185
}
184186
}
185187
}
186-
}
187-
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'camera')) as node {
188-
execute on passengers if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] run { with entity @s data.cameras.<%node.name%>
189-
$execute as $(uuid) positioned ^$(posx) ^$(posy) ^$(posz) rotated ~$(roty) ~$(rotx) run tp @s ~ ~ ~ ~ ~
188+
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'camera')) as node {
189+
block { with entity @s data.cameras.<%node.name%>
190+
$execute as $(uuid) positioned ^$(posx) ^$(posy) ^$(posz) rotated ~$(roty) ~$(rotx) run tp @s ~ ~ ~ ~ ~
191+
}
190192
}
191193
}
192194
# Rotation Logic
@@ -286,6 +288,7 @@ dir <%export_namespace%> {
286288

287289
scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%>
288290
scoreboard players add @s <%OBJECTIVES.TWEEN_DURATION()%> 1
291+
execute at @s run function ./zzz/apply_frame {frame: 0}
289292
$execute at @s run function ./zzz/apply_frame {frame: $(to_frame)}
290293
execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%>
291294
}
@@ -317,6 +320,15 @@ dir <%export_namespace%> {
317320
scoreboard players add @s <%OBJECTIVES.FRAME()%> 1
318321
}
319322
IF (use_storage_for_animation) {
323+
function set_frame {
324+
#ARGS: {frame: int}
325+
REPEAT (animation.includedNodes) as node {
326+
IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) {
327+
$execute on passengers run data modify entity @s[tag=<%TAGS.LOCAL_BONE(export_namespace, node.name)%>] {} merge from storage aj.<%export_namespace%>:animations list.<%animation.name%>.frames[$(frame)].<%node.name%>
328+
}
329+
}
330+
execute on passengers run data modify entity @s[type=!marker] start_interpolation set value -1
331+
}
320332
function apply_frame {
321333
#ARGS: {frame: int}
322334
REPEAT (animation.includedNodes) as node {
@@ -329,12 +341,14 @@ dir <%export_namespace%> {
329341
function set_frame {
330342
# Sets the frame without interpolation
331343
#ARGS: {frame: int}
332-
$function ./frames/$(frame)
333-
execute on passengers run data modify entity @s start_interpolation set value -1
344+
$execute on passengers if entity @s[type=marker] run \
345+
function ./frames/$(frame) with entity @s data.bones
346+
execute on passengers run data modify entity @s[type=!marker] start_interpolation set value -1
334347
}
335348
function apply_frame {
336349
#ARGS: {frame: int}
337-
$function ./frames/$(frame)
350+
$execute on passengers if entity @s[type=marker] run \
351+
function ./frames/$(frame) with entity @s data.bones
338352
<%%
339353
// A record of node uuid to IAnimationNode.
340354
// Keeps track of the last time a bone was updated.
@@ -351,6 +365,10 @@ dir <%export_namespace%> {
351365
if (!variant) return
352366
emit.mcb(`function *${export_namespace}/variants/${variant.name}/apply`)
353367
}
368+
global.merged = {
369+
locators: {},
370+
cameras: {}
371+
}
354372
%%>
355373
REPEAT (frame.nodes.sort(nodeSorter)) as node {
356374
IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) {
@@ -362,15 +380,13 @@ dir <%export_namespace%> {
362380
# It's actually faster to use an on passeners call that checks tag immediately
363381
# than to spread it into a function that runs a bunch of tag checks on each bone.
364382
IF (node.interpolation === 'pre-post' || global.isStepInterpolation) {
365-
execute on passengers if entity @s[tag=<%TAGS.LOCAL_BONE(export_namespace, node.name)%>] run \
366-
data merge entity @s { \
383+
$data merge entity $(<%node.type + '_' + node.name%>) { \
367384
transformation: <%matrixToNbtFloatArray(node.matrix).toString()%>, \
368385
start_interpolation: -1, \
369386
interpolation_duration: 0 \
370387
}
371388
} ELSE {
372-
execute on passengers if entity @s[tag=<%TAGS.LOCAL_BONE(export_namespace, node.name)%>] run \
373-
data merge entity @s { \
389+
$data merge entity $(<%node.type + '_' + node.name%>) { \
374390
transformation: <%matrixToNbtFloatArray(node.matrix).toString()%>, \
375391
start_interpolation: 0, \
376392
interpolation_duration: <%interpolation_duration%> \
@@ -380,15 +396,17 @@ dir <%export_namespace%> {
380396
IF (rig.nodeMap[node.uuid]?.config?.use_entity || rig.nodeMap[node.uuid]?.config?.ticking_commands) {
381397
<%%
382398
global.rot = getRotationFromQuaternion(node.rot)
383-
%%>
384-
execute on passengers if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] run \
385-
data modify entity @s data.locators.<%node.name%> merge value { \
386-
posx: <%node.pos.x%>, posy: <%node.pos.y%>, posz: <%node.pos.z%>, \
387-
rotx: <%global.rot.x%>, roty: <%global.rot.y%>, \
399+
global.merged.locators[node.name] = {
400+
posx: node.pos.x,
401+
posy: node.pos.y,
402+
posz: node.pos.z,
403+
rotx: global.rot.x,
404+
roty: global.rot.y
388405
}
406+
%%>
389407
}
390408
IF (node.commands) {
391-
execute <%node.execute_condition ? node.execute_condition.trim() + ' ' : ''%>positioned \
409+
execute on vehicle <%node.execute_condition ? node.execute_condition.trim() + ' ' : ''%>positioned \
392410
^<%roundTo(node.pos.x, 10)%> \
393411
^<%roundTo(node.pos.y, 10)%> \
394412
^<%roundTo(node.pos.z, 10)%> \
@@ -405,15 +423,20 @@ dir <%export_namespace%> {
405423
IF (rig.nodeMap[node.uuid]) {
406424
<%%
407425
global.rot = getRotationFromQuaternion(node.rot)
408-
%%>
409-
execute on passengers if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] run \
410-
data modify entity @s data.cameras.<%node.name%> merge value { \
411-
posx: <%node.pos.x%>, posy: <%node.pos.y%>, posz: <%node.pos.z%>, \
412-
rotx: <%global.rot.x%>, roty: <%global.rot.y%>, \
426+
global.merged.cameras[node.name] = {
427+
posx: node.pos.x,
428+
posy: node.pos.y,
429+
posz: node.pos.z,
430+
rotx: global.rot.x,
431+
roty: global.rot.y
413432
}
433+
%%>
414434
}
415435
}
416436
}
437+
IF (Object.keys(global.merged.locators).length > 0 || Object.keys(global.merged.cameras).length > 0) {
438+
data modify entity @s data merge value <%JSON.stringify(global.merged)%>
439+
}
417440
}
418441
}
419442
}
@@ -448,34 +471,41 @@ dir <%export_namespace%> {
448471
execute as @e[type=item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run {
449472
execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1
450473

451-
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'locator')) as locator {
452-
IF (locator.config && locator.config.use_entity) {
453-
summon <%locator.config.entity_type%> ~ ~ ~ {Tags:['<%TAGS.NEW()%>', '<%TAGS.GLOBAL_LOCATOR()%>', '<%TAGS.PROJECT_LOCATOR(export_namespace)%>', '<%TAGS.LOCAL_LOCATOR(export_namespace, locator.name)%>']}
454-
execute as @e[type=<%locator.config.entity_type%>,tag=<%TAGS.NEW()%>,tag=<%TAGS.GLOBAL_LOCATOR()%>,limit=1,distance=..0.01] run {
455-
tag @s remove <%TAGS.NEW()%>
474+
execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run {
475+
function *global/internal/gu/convert_uuid_array_to_string with entity @s
476+
data modify entity @s data.bones.data_data set from storage aj:uuid main.out
456477

457-
function *global/internal/gu/convert_uuid_array_to_string with entity @s
458-
execute as @e[type=item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run {
459-
data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out
478+
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'locator')) as locator {
479+
IF (locator.config && locator.config.use_entity) {
480+
summon <%locator.config.entity_type%> ~ ~ ~ {Tags:['<%TAGS.NEW()%>', '<%TAGS.GLOBAL_LOCATOR()%>', '<%TAGS.PROJECT_LOCATOR(export_namespace)%>', '<%TAGS.LOCAL_LOCATOR(export_namespace, locator.name)%>']}
481+
execute as @e[type=<%locator.config.entity_type%>,tag=<%TAGS.NEW()%>,tag=<%TAGS.GLOBAL_LOCATOR()%>,limit=1,distance=..0.01] run {
482+
tag @s remove <%TAGS.NEW()%>
483+
484+
function *global/internal/gu/convert_uuid_array_to_string with entity @s
485+
<%%
486+
if (locator.config.summon_commands) {
487+
emit.mcb(locator.config.summon_commands)
488+
}
489+
%%>
460490
}
461-
<%%
462-
if (locator.config.summon_commands) {
463-
emit.mcb(locator.config.summon_commands)
464-
}
465-
%%>
491+
data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out
466492
}
467493
}
468-
}
469494

470-
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'camera')) as camera {
471-
summon item_display ~ ~ ~ {Tags:['<%TAGS.NEW()%>', '<%TAGS.GLOBAL_CAMERA()%>', '<%TAGS.PROJECT_CAMERA(export_namespace)%>', '<%TAGS.LOCAL_CAMERA(export_namespace, camera.name)%>'], teleport_duration: 2}
472-
execute as @e[type=item_display,tag=<%TAGS.NEW()%>,tag=<%TAGS.GLOBAL_CAMERA()%>,limit=1,distance=..0.01] run {
473-
tag @s remove <%TAGS.NEW()%>
495+
REPEAT (Object.values(rig.nodeMap).filter(v => v.type === 'camera')) as camera {
496+
summon item_display ~ ~ ~ {Tags:['<%TAGS.NEW()%>', '<%TAGS.GLOBAL_CAMERA()%>', '<%TAGS.PROJECT_CAMERA(export_namespace)%>', '<%TAGS.LOCAL_CAMERA(export_namespace, camera.name)%>'], teleport_duration: 2}
497+
execute as @e[type=item_display,tag=<%TAGS.NEW()%>,tag=<%TAGS.GLOBAL_CAMERA()%>,limit=1,distance=..0.01] run {
498+
tag @s remove <%TAGS.NEW()%>
474499

475-
function *global/internal/gu/convert_uuid_array_to_string with entity @s
476-
execute as @e[type=item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run {
477-
data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out
500+
function *global/internal/gu/convert_uuid_array_to_string with entity @s
478501
}
502+
data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out
503+
}
504+
505+
REPEAT (Object.values(rig.nodeMap).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as bone {
506+
execute on vehicle on passengers if entity @s[tag=<%TAGS.LOCAL_BONE(export_namespace, bone.name)%>] run \
507+
function *global/internal/gu/convert_uuid_array_to_string with entity @s
508+
data modify entity @s data.bones.<%bone.type + '_' + bone.name%> set from storage aj:uuid main.out
479509
}
480510
}
481511

@@ -515,7 +545,7 @@ dir <%export_namespace%> {
515545
}
516546
# Attempt to apply the animation frame, if it fails, print an error.
517547
execute store success score #success <%OBJECTIVES.I()%> run { with storage aj:temp args
518-
$execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage aj:temp args
548+
$execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/set_frame with storage aj:temp args
519549
execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1
520550
return fail
521551
}

src/systems/animationRenderer.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ function getNodeMatrix(node: OutlinerElement, scale: number) {
2929

3030
const scaleVec = new THREE.Vector3().setScalar(scale)
3131
// Hacky way to force the matrix to update in-game
32-
scaleVec.x += Math.random() * 0.00001
33-
scaleVec.y += Math.random() * 0.00001
34-
scaleVec.z += Math.random() * 0.00001
32+
// scaleVec.x += Math.random() * 0.00001
33+
// scaleVec.y += Math.random() * 0.00001
34+
// scaleVec.z += Math.random() * 0.00001
3535
matrixWorld.scale(scaleVec)
3636

3737
if (node instanceof TextDisplay) {
@@ -136,7 +136,7 @@ export function getAnimationNodes(
136136
matrix = getNodeMatrix(node.node, node.scale)
137137
// Only add the frame if the matrix has changed.
138138
// NOTE - Disabled because it causes issues with vanilla interpolation.
139-
// if (lastFrame && lastFrame.matrix.equals(matrix)) continue
139+
if (lastFrame && lastFrame.matrix.equals(matrix)) continue
140140
// Inherit instant interpolation from parent
141141
if (node.parentNode) {
142142
const parentKeyframes = keyframeCache.get(node.parentNode.uuid)
@@ -155,7 +155,7 @@ export function getAnimationNodes(
155155
interpolation = 'pre-post'
156156
}
157157

158-
// lastFrameCache.set(uuid, { matrix, keyframe })
158+
lastFrameCache.set(uuid, { matrix, keyframe })
159159
break
160160
}
161161
case 'locator': {

src/systems/datapackCompiler.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ async function generateRootEntityPassengers(rig: IRenderedRig, rigHash: string)
205205
const aj = Project!.animated_java
206206
const passengers: NbtList = new NbtList()
207207

208-
const { locators, cameras } = createPositionStorage(rig)
208+
const { locators, cameras, bones } = createPassengerStorage(rig)
209+
209210
passengers.add(
210211
new NbtCompound()
211212
.set('id', new NbtString('minecraft:marker'))
@@ -223,6 +224,7 @@ async function generateRootEntityPassengers(rig: IRenderedRig, rigHash: string)
223224
.set('rigHash', new NbtString(rigHash))
224225
.set('locators', locators)
225226
.set('cameras', cameras)
227+
.set('bones', bones)
226228
)
227229
)
228230

@@ -445,24 +447,41 @@ function getRotationFromQuaternion(q: THREE.Quaternion) {
445447
return rot
446448
}
447449

448-
function createPositionStorage(rig: IRenderedRig) {
450+
function createPassengerStorage(rig: IRenderedRig) {
451+
const bones = new NbtCompound()
449452
const locators = new NbtCompound()
450453
const cameras = new NbtCompound()
454+
// Data entity
455+
bones.set('data_data', new NbtString(''))
451456
for (const node of Object.values(rig.defaultPose)) {
452-
if (node.type !== 'locator' && node.type !== 'camera') continue
453-
const rot = getRotationFromQuaternion(node.rot)
454-
;(node.type === 'locator' ? locators : cameras).set(
455-
node.name,
456-
new NbtCompound()
457-
.set('uuid', new NbtString(''))
458-
.set('posx', new NbtFloat(node.pos.x))
459-
.set('posy', new NbtFloat(node.pos.y))
460-
.set('posz', new NbtFloat(node.pos.z))
461-
.set('rotx', new NbtFloat(Math.radToDeg(rot.x)))
462-
.set('roty', new NbtFloat(Math.radToDeg(rot.y)))
463-
)
457+
switch (node.type) {
458+
case 'locator':
459+
case 'camera': {
460+
const rot = getRotationFromQuaternion(node.rot)
461+
const data = new NbtCompound()
462+
.set('posx', new NbtFloat(node.pos.x))
463+
.set('posy', new NbtFloat(node.pos.y))
464+
.set('posz', new NbtFloat(node.pos.z))
465+
.set('rotx', new NbtFloat(Math.radToDeg(rot.x)))
466+
.set('roty', new NbtFloat(Math.radToDeg(rot.y)))
467+
if (
468+
node.type === 'locator' &&
469+
(rig.nodeMap[node.uuid].node as Locator).config.use_entity
470+
)
471+
data.set('uuid', new NbtString(''))
472+
;(node.type === 'camera' ? cameras : locators).set(node.name, data)
473+
break
474+
}
475+
case 'bone':
476+
case 'text_display':
477+
case 'item_display':
478+
case 'block_display': {
479+
bones.set(node.type + '_' + node.name, new NbtString(''))
480+
break
481+
}
482+
}
464483
}
465-
return { locators, cameras }
484+
return { locators, cameras, bones }
466485
}
467486

468487
function nodeSorter(a: AnyRenderedNode, b: AnyRenderedNode): number {

0 commit comments

Comments
 (0)