Skip to content

Commit 86bdf13

Browse files
committed
🩹 Fix rebase issues when updating to origin
1 parent b0478f4 commit 86bdf13

File tree

5 files changed

+368
-4
lines changed

5 files changed

+368
-4
lines changed

src/systems/exporter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { translate } from '../util/translation'
1010
import { Variant } from '../variants'
1111
import { hashAnimations, renderProjectAnimations } from './animation-renderer'
1212
import datapackCompiler from './datapack-compiler'
13-
import { exportJSON } from './plugin-json-compiler/jsonCompiler'
13+
import { exportJSON } from './plugin-json-compiler'
1414
import resourcepackCompiler from './resourcepack-compiler'
1515
import { hashRig, renderRig } from './rig-renderer'
1616

src/systems/minecraft-temp/assetManager.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { PACKAGE } from '../../constants'
22
import { getCurrentVersion, getLatestVersion } from './versionManager'
33

44
import EVENTS from '@aj/util/events'
5-
import { app } from 'electron'
65
import { type Unzipped } from 'fflate'
76
import {
87
showOfflineError,
@@ -83,7 +82,7 @@ export async function getLatestVersionClientDownloadUrl() {
8382
}
8483

8584
function getCachedJarFilePath() {
86-
const userDataPath = (electron?.app ?? app).getPath('userData')
85+
const userDataPath = electron.app.getPath('userData')
8786
return PathModule.join(userDataPath, `${PACKAGE.name}/latest.jar`)
8887
}
8988

src/systems/node-configs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Alignment } from '@aj/blockbench-additions/outliner-elements/textDisplay'
2+
import { translate } from '@aj/util/translation'
23
import { NbtByte, NbtCompound, NbtFloat, NbtInt, NbtString, NbtTag } from 'deepslate/lib/nbt'
34
import { SerializableConfig } from './serializableConfig'
45
export type { Serialized } from './serializableConfig'
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
import {
2+
getKeyframeCommands,
3+
getKeyframeExecuteCondition,
4+
getKeyframeRepeat,
5+
getKeyframeRepeatFrequency,
6+
getKeyframeVariant,
7+
} from '../../blockbench-mods/misc/customKeyframes'
8+
import { type defaultValues } from '../../blueprintSettings'
9+
import { type EasingKey } from '../../util/easing'
10+
import { resolvePath } from '../../util/fileUtil'
11+
import { detectCircularReferences, mapObjEntries, scrubUndefined } from '../../util/misc'
12+
import { Variant } from '../../variants'
13+
import type { INodeTransform, IRenderedAnimation, IRenderedFrame } from '../animation-renderer'
14+
import type {
15+
AnyRenderedNode,
16+
IRenderedModel,
17+
IRenderedRig,
18+
IRenderedVariant,
19+
IRenderedVariantModel,
20+
} from '../rig-renderer'
21+
22+
type ExportedNodetransform = Omit<
23+
INodeTransform,
24+
'type' | 'name' | 'uuid' | 'node' | 'matrix' | 'decomposed' | 'executeCondition'
25+
> & {
26+
matrix: number[]
27+
decomposed: {
28+
translation: ArrayVector3
29+
left_rotation: ArrayVector4
30+
scale: ArrayVector3
31+
}
32+
pos: ArrayVector3
33+
rot: ArrayVector3
34+
scale: ArrayVector3
35+
execute_condition?: string
36+
}
37+
type ExportedRenderedNode = Omit<
38+
AnyRenderedNode,
39+
'node' | 'parentNode' | 'model' | 'boundingBox' | 'configs' | 'baseScale' | 'safe_name'
40+
> & {
41+
default_transform: ExportedNodetransform
42+
bounding_box?: { min: ArrayVector3; max: ArrayVector3 }
43+
configs?: Record<string, IBlueprintBoneConfigJSON>
44+
}
45+
type ExportedAnimationFrame = Omit<IRenderedFrame, 'nodes' | 'node_transforms'> & {
46+
node_transforms: Record<string, ExportedNodetransform>
47+
}
48+
type ExportedBakedAnimation = Omit<
49+
IRenderedAnimation,
50+
'uuid' | 'frames' | 'modified_nodes' | 'safe_name'
51+
> & {
52+
frames: ExportedAnimationFrame[]
53+
modified_nodes: string[]
54+
}
55+
interface ExportedKeyframe {
56+
time: number
57+
channel: string
58+
value?: [string, string, string]
59+
post?: [string, string, string]
60+
interpolation?:
61+
| {
62+
type: 'linear'
63+
easing: EasingKey
64+
easingArgs?: number[]
65+
}
66+
| {
67+
type: 'bezier'
68+
bezier_linked?: boolean
69+
bezier_left_time?: ArrayVector3
70+
bezier_left_value?: ArrayVector3
71+
bezier_right_time?: ArrayVector3
72+
bezier_right_value?: ArrayVector3
73+
}
74+
| {
75+
type: 'catmullrom'
76+
}
77+
| {
78+
type: 'step'
79+
}
80+
commands?: string
81+
variant?: string
82+
execute_condition?: string
83+
repeat?: boolean
84+
repeat_frequency?: number
85+
}
86+
type ExportedAnimator = ExportedKeyframe[]
87+
interface ExportedDynamicAnimation {
88+
name: string
89+
loop_mode: 'once' | 'hold' | 'loop'
90+
duration: number
91+
excluded_nodes: string[]
92+
animators: Record<string, ExportedAnimator>
93+
}
94+
interface ExportedTexture {
95+
name: string
96+
src: string
97+
}
98+
type ExportedVariantModel = Omit<
99+
IRenderedVariantModel,
100+
'model_path' | 'resource_location' | 'item_model'
101+
> & {
102+
model: IRenderedModel | null
103+
custom_model_data: number
104+
}
105+
type ExportedVariant = Omit<IRenderedVariant, 'models'> & {
106+
/**
107+
* A map of bone UUID -> IRenderedVariantModel
108+
*/
109+
models: Record<string, ExportedVariantModel>
110+
}
111+
112+
export interface IExportedJSON {
113+
/**
114+
* The Blueprint's Settings
115+
*/
116+
settings: {
117+
export_namespace: (typeof defaultValues)['export_namespace']
118+
bounding_box: (typeof defaultValues)['bounding_box']
119+
// Resource Pack Settings
120+
custom_model_data_offset: (typeof defaultValues)['custom_model_data_offset']
121+
// Plugin Settings
122+
baked_animations: (typeof defaultValues)['baked_animations']
123+
}
124+
textures: Record<string, ExportedTexture>
125+
nodes: Record<string, ExportedRenderedNode>
126+
variants: Record<string, ExportedVariant>
127+
/**
128+
* If `blueprint_settings.baked_animations` is true, this will be an array of `ExportedAnimation` objects. Otherwise, it will be an array of `AnimationUndoCopy` objects, just like the `.bbmodel`'s animation list.
129+
*/
130+
animations: Record<string, ExportedBakedAnimation> | Record<string, ExportedDynamicAnimation>
131+
}
132+
133+
function transferKey(obj: any, oldKey: string, newKey: string) {
134+
obj[newKey] = obj[oldKey]
135+
delete obj[oldKey]
136+
}
137+
138+
function serailizeKeyframe(kf: _Keyframe): ExportedKeyframe {
139+
const json = {
140+
time: kf.time,
141+
channel: kf.channel,
142+
commands: getKeyframeCommands(kf),
143+
variant: getKeyframeVariant(kf),
144+
execute_condition: getKeyframeExecuteCondition(kf),
145+
repeat: getKeyframeRepeat(kf),
146+
repeat_frequency: getKeyframeRepeatFrequency(kf),
147+
} as ExportedKeyframe
148+
149+
switch (json.channel) {
150+
case 'variant':
151+
case 'commands':
152+
break
153+
default: {
154+
json.value = [
155+
kf.get('x', 0).toString(),
156+
kf.get('y', 0).toString(),
157+
kf.get('z', 0).toString(),
158+
]
159+
json.interpolation = { type: kf.interpolation } as any
160+
}
161+
}
162+
163+
if (json.interpolation) {
164+
switch (json.interpolation.type) {
165+
case 'linear': {
166+
json.interpolation.easing = kf.easing!
167+
if (kf.easingArgs?.length) json.interpolation.easingArgs = kf.easingArgs
168+
break
169+
}
170+
case 'bezier': {
171+
json.interpolation.bezier_linked = kf.bezier_linked
172+
json.interpolation.bezier_left_time = kf.bezier_left_time.slice() as ArrayVector3
173+
json.interpolation.bezier_left_value = kf.bezier_left_value.slice() as ArrayVector3
174+
json.interpolation.bezier_right_time = kf.bezier_right_time.slice() as ArrayVector3
175+
json.interpolation.bezier_right_value =
176+
kf.bezier_right_value.slice() as ArrayVector3
177+
break
178+
}
179+
case 'catmullrom': {
180+
break
181+
}
182+
case 'step': {
183+
break
184+
}
185+
}
186+
}
187+
188+
if (kf.data_points.length === 2) {
189+
json.post = [
190+
kf.get('x', 1).toString(),
191+
kf.get('y', 1).toString(),
192+
kf.get('z', 1).toString(),
193+
]
194+
}
195+
196+
return json
197+
}
198+
199+
function serializeVariant(rig: IRenderedRig, variant: IRenderedVariant): ExportedVariant {
200+
const json: ExportedVariant = {
201+
...variant,
202+
models: mapObjEntries(variant.models, (uuid, model) => {
203+
const json: ExportedVariantModel = {
204+
model: model.model,
205+
custom_model_data: model.custom_model_data,
206+
}
207+
return [uuid, json]
208+
}),
209+
}
210+
return json
211+
}
212+
213+
export function exportJSON(options: {
214+
rig: IRenderedRig
215+
animations: IRenderedAnimation[]
216+
displayItemPath: string
217+
textureExportFolder: string
218+
modelExportFolder: string
219+
}) {
220+
const aj = Project!.animated_java
221+
const { rig, animations } = options
222+
223+
console.log('Exporting JSON...', options)
224+
225+
function serializeTexture(texture: Texture): ExportedTexture {
226+
return {
227+
name: texture.name,
228+
src: texture.getDataURL(),
229+
}
230+
}
231+
232+
const json: IExportedJSON = {
233+
settings: {
234+
export_namespace: aj.export_namespace,
235+
bounding_box: aj.bounding_box,
236+
custom_model_data_offset: aj.custom_model_data_offset,
237+
baked_animations: aj.baked_animations,
238+
},
239+
textures: mapObjEntries(rig.textures, (_, texture) => [
240+
texture.uuid,
241+
serializeTexture(texture),
242+
]),
243+
nodes: mapObjEntries(rig.nodes, (uuid, node) => [uuid, serailizeRenderedNode(node)]),
244+
variants: mapObjEntries(rig.variants, (uuid, variant) => [
245+
uuid,
246+
serializeVariant(rig, variant),
247+
]),
248+
animations: {},
249+
}
250+
251+
if (aj.baked_animations) {
252+
for (const animation of animations) {
253+
json.animations[animation.uuid] = serializeAnimation(animation)
254+
}
255+
} else {
256+
for (const animation of Blockbench.Animation.all) {
257+
const animJSON: ExportedDynamicAnimation = {
258+
name: animation.name,
259+
loop_mode: animation.loop,
260+
duration: animation.length,
261+
excluded_nodes: animation.excluded_nodes.map(node => node.value),
262+
animators: {},
263+
}
264+
for (const [uuid, animator] of Object.entries(animation.animators)) {
265+
// Only include animators with keyframes
266+
if (animator.keyframes.length === 0) continue
267+
animJSON.animators[uuid] = animator.keyframes.map(serailizeKeyframe)
268+
}
269+
json.animations[animation.uuid] = animJSON
270+
}
271+
}
272+
273+
console.log('Exported JSON:', json)
274+
if (detectCircularReferences(json)) {
275+
throw new Error('Circular references detected in exported JSON.')
276+
}
277+
console.log('Scrubbed:', scrubUndefined(json))
278+
279+
let exportPath: string
280+
try {
281+
exportPath = resolvePath(aj.json_file)
282+
} catch (e) {
283+
console.log(`Failed to resolve export path '${aj.json_file}'`)
284+
console.error(e)
285+
return
286+
}
287+
288+
fs.writeFileSync(exportPath, compileJSON(json).toString())
289+
}
290+
291+
function serailizeNodeTransform(node: INodeTransform): ExportedNodetransform {
292+
const json: ExportedNodetransform = {
293+
matrix: node.matrix.elements,
294+
decomposed: {
295+
translation: node.decomposed.translation.toArray(),
296+
left_rotation: node.decomposed.leftRotation.toArray() as ArrayVector4,
297+
scale: node.decomposed.scale.toArray(),
298+
},
299+
pos: node.pos,
300+
rot: node.rot,
301+
head_rot: node.head_rot,
302+
scale: node.scale,
303+
interpolation: node.interpolation,
304+
commands: node.commands,
305+
execute_condition: node.execute_condition,
306+
}
307+
return json
308+
}
309+
310+
function serailizeRenderedNode(node: AnyRenderedNode): ExportedRenderedNode {
311+
const json: any = { ...node }
312+
delete json.node
313+
delete json.parentNode
314+
delete json.safe_name
315+
delete json.model
316+
transferKey(json, 'lineWidth', 'line_width')
317+
transferKey(json, 'backgroundColor', 'background_color')
318+
transferKey(json, 'backgroundAlpha', 'background_alpha')
319+
320+
json.default_transform = serailizeNodeTransform(json.default_transform as INodeTransform)
321+
switch (node.type) {
322+
case 'bone': {
323+
delete json.boundingBox
324+
json.bounding_box = {
325+
min: node.bounding_box.min.toArray(),
326+
max: node.bounding_box.max.toArray(),
327+
}
328+
delete json.configs
329+
json.configs = { ...node.configs?.variants }
330+
const defaultVariant = Variant.getDefault()
331+
if (node.configs?.default && defaultVariant) {
332+
json.configs[defaultVariant.uuid] = node.configs.default
333+
}
334+
break
335+
}
336+
case 'text_display': {
337+
json.text = node.text?.toJSON()
338+
break
339+
}
340+
}
341+
return json as ExportedRenderedNode
342+
}
343+
344+
function serializeAnimation(animation: IRenderedAnimation): ExportedBakedAnimation {
345+
const json: ExportedBakedAnimation = {
346+
name: animation.name,
347+
duration: animation.duration,
348+
loop_delay: animation.loop_delay,
349+
loop_mode: animation.loop_mode,
350+
frames: [],
351+
modified_nodes: Object.keys(animation.modified_nodes),
352+
}
353+
354+
const frames: ExportedAnimationFrame[] = []
355+
for (const frame of animation.frames) {
356+
const nodeTransforms: Record<string, ExportedNodetransform> = {}
357+
for (const [uuid, nodeTransform] of Object.entries(frame.node_transforms)) {
358+
nodeTransforms[uuid] = serailizeNodeTransform(nodeTransform)
359+
}
360+
frames.push({ ...frame, node_transforms: nodeTransforms })
361+
}
362+
json.frames = frames
363+
364+
return json
365+
}

0 commit comments

Comments
 (0)