Skip to content

Commit 994ec6d

Browse files
committed
🚧 Storage-Based Animation Fixes
- Fixed locators and cameras not animating when using storage-based animation. - Fixed animation storage data commands being too long. - Added a progress bar for generating animation storage.
1 parent ca6bfd9 commit 994ec6d

File tree

3 files changed

+107
-36
lines changed

3 files changed

+107
-36
lines changed

src/lang/en.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,7 @@ animated_java.dialog.blueprint_settings.teleportation_duration.description: The
164164
animated_java.dialog.blueprint_settings.use_storage_for_animation.title: Use Storage for Animation
165165
animated_java.dialog.blueprint_settings.use_storage_for_animation.description: |-
166166
Whether or not to use NBT storage to store animation data instead of functions.
167-
This will vastly reduce the number of functions in the generated Data Pack, but is 33% slower than the function method.
168-
WARNING: This animation method does not support cameras, or locators!
167+
This will vastly reduce the number of functions in the generated Data Pack, but is 42% slower than the function method.
169168
170169
# Plugin Settings
171170
animated_java.dialog.blueprint_settings.baked_animations.title: Baked Animations

src/systems/animated_java.mcb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ dir <%export_namespace%> {
124124

125125
function on_load {
126126
IF (use_storage_for_animation) {
127-
data modify storage aj.<%export_namespace%>:animations list set value {}
127+
REPEAT (animations) as animation {
128+
data remove storage aj.<%export_namespace%>:animations <%animation.storageSafeName%>
129+
}
128130
<%animationStorage.join('\n')%>
129131
}
130132
execute as @e[type=item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] \
@@ -323,18 +325,26 @@ dir <%export_namespace%> {
323325
IF (use_storage_for_animation) {
324326
function set_frame {
325327
#ARGS: {frame: int}
326-
REPEAT (animation.includedNodes) as node {
328+
REPEAT (animation.includedNodes.sort(nodeSorter)) as node {
327329
IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) {
328-
$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.storageSafeName%>.frames[$(frame)].<%node.name%>
330+
$execute on passengers run data modify entity @s[tag=<%TAGS.LOCAL_BONE(export_namespace, node.name)%>] {} merge from \
331+
storage aj.<%export_namespace%>:animations <%animation.storageSafeName%>.$(frame).<%node.type + '_' + node.name%>
332+
} ELSE IF (['locator', 'camera'].includes(node.type)) {
333+
$execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run data modify entity @s data.<%node.type + 's.' + node.name%> merge from \
334+
storage aj.<%export_namespace%>:animations <%animation.storageSafeName%>.$(frame).<%node.type + '_' + node.name%>
329335
}
330336
}
331337
execute on passengers run data modify entity @s[type=!marker] start_interpolation set value -1
332338
}
333339
function apply_frame {
334340
#ARGS: {frame: int}
335-
REPEAT (animation.includedNodes) as node {
341+
REPEAT (animation.includedNodes.sort(nodeSorter)) as node {
336342
IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) {
337-
$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.storageSafeName%>.frames[$(frame)].<%node.name%>
343+
$execute on passengers if entity @s[tag=<%TAGS.LOCAL_BONE(export_namespace, node.name)%>] run data modify entity @s {} merge from \
344+
storage aj.<%export_namespace%>:animations <%animation.storageSafeName%>.$(frame).<%node.type + '_' + node.name%>
345+
} ELSE IF (['locator', 'camera'].includes(node.type)) {
346+
$execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run data modify entity @s data.<%node.type + 's.' + node.name%> merge from \
347+
storage aj.<%export_namespace%>:animations <%animation.storageSafeName%>.$(frame).<%node.type + '_' + node.name%>
338348
}
339349
}
340350
}

src/systems/datapackCompiler.ts

Lines changed: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { JsonText } from './minecraft/jsonText'
2626
import { MAX_PROGRESS, PROGRESS, PROGRESS_DESCRIPTION } from '../interface/exportProgressDialog'
2727
import { eulerFromQuaternion, roundTo } from '../util/misc'
2828
import { setTimeout } from 'timers'
29+
import { MSLimiter } from '../util/msLimiter'
2930

3031
const BONE_TYPES = ['bone', 'text_display', 'item_display', 'block_display']
3132

@@ -424,40 +425,99 @@ class AJMeta {
424425
}
425426
}
426427

427-
function createAnimationStorage(animations: IRenderedAnimation[]) {
428-
const storage: string[] = []
428+
async function createAnimationStorage(animations: IRenderedAnimation[]) {
429+
PROGRESS_DESCRIPTION.set('Creating Animation Storage...')
430+
PROGRESS.set(0)
431+
MAX_PROGRESS.set(
432+
animations.length + animations.reduce((acc, anim) => acc + anim.frames.length, 0)
433+
)
434+
const dataCommands: string[] = []
435+
const limiter = new MSLimiter(16)
436+
429437
for (const animation of animations) {
430-
const frames = new NbtList([
431-
new NbtCompound(), // This compound is just to make the list 1-indexed
432-
])
433-
const animStorage = new NbtCompound().set('frames', frames)
434-
for (const frame of animation.frames) {
435-
const frameStorage = new NbtCompound()
436-
frames.add(frameStorage)
438+
PROGRESS_DESCRIPTION.set(`Creating Animation Storage for '${animation.name}'`)
439+
let frames = new NbtCompound()
440+
const addFrameDataCommand = () => {
441+
const str = `data modify storage aj.${
442+
Project!.animated_java.export_namespace
443+
}:animations ${animation.storageSafeName} merge value ${frames.toString()}`
444+
dataCommands.push(str)
445+
frames = new NbtCompound()
446+
}
447+
for (let i = 0; i < animation.frames.length; i++) {
448+
const frame = animation.frames[i]
449+
const thisFrame = new NbtCompound()
450+
frames.set(i.toString(), thisFrame)
437451
for (const node of frame.nodes) {
438-
if (!BONE_TYPES.includes(node.type)) continue
439-
frameStorage.set(
440-
node.type + '_' + node.name,
441-
new NbtCompound()
442-
.set('transformation', matrixToNbtFloatArray(node.matrix))
443-
.set('start_interpolation', new NbtInt(0))
444-
)
452+
if (BONE_TYPES.includes(node.type)) {
453+
thisFrame.set(
454+
node.type + '_' + node.name,
455+
new NbtCompound()
456+
.set('transformation', matrixToNbtFloatArray(node.matrix))
457+
.set('start_interpolation', new NbtInt(0))
458+
)
459+
} else {
460+
thisFrame.set(
461+
node.type + '_' + node.name,
462+
new NbtCompound()
463+
.set('posx', new NbtFloat(node.pos[0]))
464+
.set('posy', new NbtFloat(node.pos[1]))
465+
.set('posz', new NbtFloat(node.pos[2]))
466+
.set('rotx', new NbtFloat(node.rot[0]))
467+
.set('roty', new NbtFloat(node.rot[1]))
468+
)
469+
}
445470
}
471+
if (frames.toString().length > 1000000) {
472+
addFrameDataCommand()
473+
}
474+
PROGRESS.set(PROGRESS.get() + 1)
475+
await limiter.sync()
446476
}
447-
const str = `data modify storage aj.${
448-
Project!.animated_java.export_namespace
449-
}:animations list.${animation.storageSafeName} set value ${animStorage.toString()}`
450-
if (str.length > 2000000) {
451-
// FIXME - Temporary patch. Split each animation's storage into multiple commands if it's too large.
452-
throw new Error(
453-
`The animation storage for '${animation.name}' is too large! The data command must be less than 2000000 characters long. (Currently ${str.length} characters).`
454-
)
455-
}
456-
storage.push(str)
477+
addFrameDataCommand()
478+
PROGRESS.set(PROGRESS.get() + 1)
479+
await limiter.sync()
457480
}
458-
return storage
481+
482+
return dataCommands
459483
}
460484

485+
// function createAnimationStorage(animations: IRenderedAnimation[]) {
486+
// const storage: string[] = []
487+
488+
// for (const animation of animations) {
489+
// const frames = new NbtList([
490+
// new NbtCompound(), // This compound is just to make the list 1-indexed
491+
// ])
492+
// const animStorage = new NbtCompound().set('frames', frames)
493+
// for (const frame of animation.frames) {
494+
// const frameStorage = new NbtCompound()
495+
// frames.add(frameStorage)
496+
// for (const node of frame.nodes) {
497+
// if (!BONE_TYPES.includes(node.type)) continue
498+
// frameStorage.set(
499+
// node.type + '_' + node.name,
500+
// new NbtCompound()
501+
// .set('transformation', matrixToNbtFloatArray(node.matrix))
502+
// .set('start_interpolation', new NbtInt(0))
503+
// )
504+
// }
505+
// }
506+
507+
// const str = `data modify storage aj.${
508+
// Project!.animated_java.export_namespace
509+
// }:animations list.${animation.storageSafeName} set value ${animStorage.toString()}`
510+
// if (str.length > 2000000) {
511+
// // FIXME - Temporary patch. Split each animation's storage into multiple commands if it's too large.
512+
// throw new Error(
513+
// `The animation storage for '${animation.name}' is too large! The data command must be less than 2000000 characters long. (Currently ${str.length} characters).`
514+
// )
515+
// }
516+
// storage.push(str)
517+
// }
518+
// return storage
519+
// }
520+
461521
function createPassengerStorage(rig: IRenderedRig) {
462522
const bones = new NbtCompound()
463523
const locators = new NbtCompound()
@@ -476,7 +536,7 @@ function createPassengerStorage(rig: IRenderedRig) {
476536
.set('roty', new NbtFloat(Math.radToDeg(node.rot[1])))
477537
if (
478538
node.type === 'locator' &&
479-
(rig.nodeMap[node.uuid].node as Locator).config.use_entity
539+
(rig.nodeMap[node.uuid].node as Locator).config?.use_entity
480540
)
481541
data.set('uuid', new NbtString(''))
482542
;(node.type === 'camera' ? cameras : locators).set(node.name, data)
@@ -607,7 +667,9 @@ export async function compileDataPack(options: {
607667
matrixToNbtFloatArray,
608668
transformationToNbt,
609669
use_storage_for_animation: aj.use_storage_for_animation,
610-
animationStorage: createAnimationStorage(animations),
670+
animationStorage: aj.use_storage_for_animation
671+
? await createAnimationStorage(animations)
672+
: null,
611673
rigHash,
612674
animationHash,
613675
boundingBox: aj.bounding_box,

0 commit comments

Comments
 (0)