minerender 2.1.0-alpha.139 → 2.1.0-alpha.140

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3491,8 +3491,17 @@ interface SceneEntityDefinition extends SceneObjectDefinitionBase {
3491
3491
  wireframe?: boolean;
3492
3492
  tints?: Record<string, string | number>;
3493
3493
  };
3494
- animation?: {
3494
+ /** Plays one named clip or blends clips with weights defaulting to 1, without normalization. */
3495
+ animation?: ({
3495
3496
  name: string;
3497
+ clips?: never;
3498
+ } | {
3499
+ name?: never;
3500
+ clips: {
3501
+ name: string;
3502
+ weight?: number;
3503
+ }[];
3504
+ }) & {
3496
3505
  loop?: boolean;
3497
3506
  speed?: number;
3498
3507
  time?: number;
@@ -95,6 +95,23 @@ var itemContext = (withDisplayContext) => (value, path) => {
95
95
  }
96
96
  });
97
97
  };
98
+ function entityAnimation(value, path) {
99
+ const animation = object(value, path);
100
+ if (["name", "clips"].filter((key) => animation[key] !== void 0).length !== 1) {
101
+ fail(path, "expected exactly one of name or clips");
102
+ }
103
+ fields(value, path, { name: text, clips: (value2, path2) => {
104
+ list((value3, path3) => {
105
+ fields(value3, path3, { name: text, weight: nonnegative }, ["name"]);
106
+ })(value2, path2);
107
+ if (!value2.length) fail(path2, "expected at least one clip");
108
+ const names = /* @__PURE__ */ new Set();
109
+ for (const [index, clip] of value2.entries()) {
110
+ if (names.has(clip.name)) fail(`${path2}[${index}].name`, `duplicate clip "${clip.name}"`);
111
+ names.add(clip.name);
112
+ }
113
+ }, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean });
114
+ }
98
115
  function guiLayer(value, path) {
99
116
  const layer = object(value, path);
100
117
  if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
@@ -197,9 +214,7 @@ function validateObject(value, path) {
197
214
  })
198
215
  });
199
216
  },
200
- animation: (value2, path2) => {
201
- fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
202
- }
217
+ animation: entityAnimation
203
218
  }, [...required, "asset"]);
204
219
  if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
205
220
  break;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/scene/SceneDocument.ts"],"sourcesContent":["import type { CapeLayout } from \"../skin/CapeLayout\";\nimport { SKIN_PARTS } from \"../skin/SkinPart\";\nimport type { TripleArray } from \"../model/Model\";\nimport { DISPLAY_POSITIONS, DisplayPosition } from \"../model/DisplayPosition\";\nimport type { GuiItemLayer, GuiTextLayer, GuiTextureLayer } from \"../gui/GuiLayer\";\n\n/** Portable scene setup. Positions use model units (16 per block); rotations use radians. */\nexport interface SceneDocument {\n format: \"minerender-scene\";\n version: 1;\n minecraftVersion?: string;\n camera?: { position: TripleArray; target: TripleArray };\n objects: SceneObjectDefinition[];\n}\n\nexport interface SceneObjectDefinitionBase {\n id: string;\n name?: string;\n position?: TripleArray;\n rotation?: TripleArray;\n scale?: TripleArray;\n visible?: boolean;\n}\n\nexport type SceneSkinPosePart = \"head\" | \"body\" | \"rightArm\" | \"leftArm\" | \"rightLeg\" | \"leftLeg\" | \"cape\";\n\nexport interface SceneSkinDefinition extends SceneObjectDefinitionBase {\n type: \"skin\";\n /** Texture URL, Minecraft username, or UUID. Omit for Steve, or Alex with slim: true, from the active asset version. */\n skin?: string;\n cape?: { texture: string; layout?: CapeLayout };\n options?: { slim?: boolean; legacy?: boolean };\n /** Mesh names from SkinPart; hiding a base part does not hide its overlay. */\n hiddenParts?: string[];\n /** Absolute local XYZ joint rotations in radians; omitted joints keep their default pose. */\n pose?: Partial<Record<SceneSkinPosePart, TripleArray>>;\n}\n\nexport interface SceneBlockDefinition extends SceneObjectDefinitionBase {\n type: \"block\";\n asset: string;\n state?: Record<string, string>;\n options?: { wireframe?: boolean; tints?: Record<number, number> };\n}\n\n/** Supplied item state; see ItemModelContext. Item references are item IDs such as minecraft:apple. */\nexport interface SceneItemContext {\n displayContext?: DisplayPosition | \"none\";\n properties?: Record<string, boolean | string | number>;\n components?: Record<string, unknown>;\n count?: number;\n itemReferences?: Record<string, string>;\n}\n\nexport interface SceneModelDefinition extends SceneObjectDefinitionBase {\n type: \"item\" | \"model\";\n asset: string;\n options?: { wireframe?: boolean; displayPosition?: DisplayPosition; tints?: Record<number, number> };\n /** Item state for `type: \"item\"` definitions only. */\n context?: SceneItemContext;\n}\n\nexport interface SceneEntityDefinition extends SceneObjectDefinitionBase {\n type: \"entity\";\n asset: string;\n texture?: string;\n layers?: string[];\n when?: string[];\n textures?: Record<string, string>;\n options?: { flip?: boolean; wireframe?: boolean; tints?: Record<string, string | number> };\n animation?: { name: string; loop?: boolean; speed?: number; time?: number; paused?: boolean };\n}\n\nexport type SceneGuiLayer = (Omit<GuiTextureLayer, \"texture\"> & { texture: string })\n | (Omit<GuiItemLayer, \"item\" | \"context\"> & { item: string; context?: Omit<SceneItemContext, \"displayContext\"> })\n | (Omit<GuiTextLayer, \"font\"> & { font?: string });\n\nexport interface SceneGuiDefinition extends SceneObjectDefinitionBase {\n type: \"gui\";\n layers: SceneGuiLayer[];\n}\n\nexport type SceneObjectDefinition = SceneSkinDefinition | SceneBlockDefinition | SceneModelDefinition\n | SceneEntityDefinition | SceneGuiDefinition;\n\ntype JsonObject = Record<string, unknown>;\ntype Validator = (value: unknown, path: string) => void;\n\nfunction fail(path: string, message: string): never {\n throw new Error(`${path}: ${message}`);\n}\n\nfunction object(value: unknown, path: string): JsonObject {\n if (!value || typeof value !== \"object\" || Array.isArray(value)\n || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, \"expected a JSON object\");\n for (const key of Object.keys(value)) {\n if ([\"__proto__\", \"prototype\", \"constructor\"].includes(key)) fail(`${path}.${key}`, \"unsupported property\");\n }\n return value as JsonObject;\n}\n\nfunction fields(value: unknown, path: string, allowed: Record<string, Validator>, required: string[] = []): JsonObject {\n const input = object(value, path);\n for (const key of required) if (input[key] === undefined) fail(`${path}.${key}`, \"required\");\n for (const [key, entry] of Object.entries(input)) {\n if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, \"unsupported property\");\n if (entry !== undefined) allowed[key](entry, `${path}.${key}`);\n }\n return input;\n}\n\nconst text: Validator = (value, path) => {\n if (typeof value !== \"string\" || !value.trim()) fail(path, \"expected a nonempty string\");\n};\nconst literalText: Validator = (value, path) => {\n if (typeof value !== \"string\") fail(path, \"expected a string\");\n};\nconst boolean: Validator = (value, path) => {\n if (typeof value !== \"boolean\") fail(path, \"expected a boolean\");\n};\nconst number: Validator = (value, path) => {\n if (typeof value !== \"number\" || !Number.isFinite(value)) fail(path, \"expected a finite number\");\n};\nconst positive: Validator = (value, path) => {\n number(value, path);\n if ((value as number) <= 0) fail(path, \"expected a positive number\");\n};\nconst nonnegative: Validator = (value, path) => {\n number(value, path);\n if ((value as number) < 0) fail(path, \"expected a nonnegative number\");\n};\nconst color: Validator = (value, path) => {\n number(value, path);\n if (!Number.isInteger(value) || (value as number) < 0 || (value as number) > 0xffffff) {\n fail(path, \"expected an RGB integer from 0 to 16777215\");\n }\n};\nconst asset: Validator = (value, path) => {\n text(value, path);\n if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value as string)\n || (value as string).split(/[:/]/).some(part => !part || part === \".\" || part === \"..\")) {\n fail(path, \"expected a resource ID such as minecraft:stone\");\n }\n};\nconst version: Validator = (value, path) => {\n text(value, path);\n if (!/^[a-zA-Z0-9._-]+$/.test(value as string)) fail(path, \"expected a Minecraft version without slashes\");\n};\nconst tuple = (size: number, item: Validator = number): Validator => (value, path) => {\n if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);\n for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);\n};\nconst list = (item: Validator): Validator => (value, path) => {\n if (!Array.isArray(value)) fail(path, \"expected an array\");\n for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);\n};\nconst dictionary = (item: Validator, numericKeys = false): Validator => (value, path) => {\n for (const [key, entry] of Object.entries(object(value, path))) {\n if (numericKeys && !/^\\d+$/.test(key)) fail(`${path}.${key}`, \"expected a nonnegative tint index\");\n item(entry, `${path}.${key}`);\n }\n};\nconst enumeration = (values: readonly string[]): Validator => (value, path) => {\n if (!values.includes(value as string)) fail(path, `expected one of: ${values.join(\", \")}`);\n};\nconst textStyle = { color, bold: boolean, italic: boolean };\nconst jsonValue: Validator = (value, path) => {\n if (value === null || typeof value === \"boolean\" || typeof value === \"string\") return;\n if (typeof value === \"number\") number(value, path);\n else if (Array.isArray(value)) list(jsonValue)(value, path);\n else dictionary(jsonValue)(value, path);\n};\nconst propertyValue: Validator = (value, path) => {\n if (typeof value === \"boolean\" || typeof value === \"string\") return;\n if (typeof value !== \"number\" || !Number.isFinite(value)) fail(path, \"expected a boolean, string, or finite number\");\n};\nconst itemContext = (withDisplayContext: boolean): Validator => (value, path) => {\n fields(value, path, {\n ...(withDisplayContext ? { displayContext: enumeration([...DISPLAY_POSITIONS, \"none\"]) } : {}),\n properties: dictionary(propertyValue), components: dictionary(jsonValue), itemReferences: dictionary(asset),\n count: (value, path) => { if (!Number.isInteger(value) || (value as number) < 0) fail(path, \"expected a nonnegative integer\"); }\n });\n};\n\nfunction guiLayer(value: unknown, path: string): void {\n const layer = object(value, path);\n if ([\"texture\", \"item\", \"text\"].filter(key => layer[key] !== undefined).length !== 1) {\n fail(path, \"expected exactly one of texture, item, or text\");\n }\n const layout = { name: text, position: tuple(2), size: tuple(2, positive) };\n if (layer.texture !== undefined) {\n fields(value, path, { ...layout, texture: asset, crop: (value, path) => {\n tuple(4)(value, path);\n const [x, y, width, height] = value as number[];\n nonnegative(x, `${path}[0]`); nonnegative(y, `${path}[1]`);\n positive(width, `${path}[2]`); positive(height, `${path}[3]`);\n } });\n } else if (layer.item !== undefined) {\n fields(value, path, { ...layout, item: asset, context: itemContext(false), tints: dictionary(color, true) });\n } else {\n fields(value, path, { ...layout, ...textStyle, font: asset, shadow: boolean, maxWidth: positive, lineHeight: positive,\n text: (value, path) => {\n if (typeof value === \"string\") return;\n list((run, path) => { fields(run, path, { text: literalText, ...textStyle }, [\"text\"]); })(value, path);\n }\n });\n }\n}\n\nfunction validateObject(value: unknown, path: string): void {\n const input = object(value, path);\n const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };\n const required = [\"id\", \"type\"];\n const renderOptions = { wireframe: boolean };\n switch (input.type) {\n case \"skin\":\n fields(value, path, { ...common, skin: text,\n cape: (value, path) => { fields(value, path, { texture: text, layout: enumeration([\"minecraft\", \"optifine\", \"labymod\"]) }, [\"texture\"]); },\n hiddenParts: list(enumeration(SKIN_PARTS)),\n pose: (value, path) => { fields(value, path, {\n head: tuple(3), body: tuple(3), rightArm: tuple(3), leftArm: tuple(3),\n rightLeg: tuple(3), leftLeg: tuple(3), cape: tuple(3)\n }); },\n options: (value, path) => { fields(value, path, { slim: boolean, legacy: boolean }); }\n }, required);\n break;\n case \"block\":\n fields(value, path, { ...common, asset, state: dictionary(text),\n options: (value, path) => { fields(value, path, { ...renderOptions, tints: dictionary(color, true) }); }\n }, [...required, \"asset\"]);\n break;\n case \"item\":\n case \"model\":\n fields(value, path, { ...common, asset, ...(input.type === \"item\" ? { context: itemContext(true) } : {}),\n options: (value, path) => { fields(value, path, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) }); }\n }, [...required, \"asset\"]);\n break;\n case \"entity\":\n fields(value, path, { ...common, asset, texture: asset, layers: list(text), when: list(text), textures: dictionary(asset),\n options: (value, path) => { fields(value, path, { ...renderOptions, flip: boolean,\n tints: dictionary((value, path) => { typeof value === \"string\" ? text(value, path) : color(value, path); }) }); },\n animation: (value, path) => { fields(value, path, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, [\"name\"]); }\n }, [...required, \"asset\"]);\n if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, \"expected at least one layer\");\n break;\n case \"gui\":\n fields(value, path, { ...common, layers: list(guiLayer) }, [...required, \"layers\"]);\n break;\n default:\n fail(`${path}.type`, \"expected skin, block, item, entity, model, or gui\");\n }\n}\n\n/** Validates and copies a scene document without requesting assets or changing the active asset version. */\nexport function parseSceneDocument(value: unknown): SceneDocument {\n if (typeof value === \"string\") {\n try { value = JSON.parse(value); }\n catch { fail(\"scene\", \"invalid JSON\"); }\n }\n const input = fields(value, \"scene\", {\n format: enumeration([\"minerender-scene\"]),\n version: (value, path) => { if (value !== 1) fail(path, \"only scene document version 1 is supported\"); },\n minecraftVersion: version,\n camera: (value, path) => { fields(value, path, { position: tuple(3), target: tuple(3) }, [\"position\", \"target\"]); },\n objects: list(validateObject)\n }, [\"format\", \"version\", \"objects\"]);\n const ids = new Set<string>();\n for (const [index, entry] of (input.objects as SceneObjectDefinition[]).entries()) {\n if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID \"${entry.id}\"`);\n ids.add(entry.id);\n }\n return JSON.parse(JSON.stringify(input)) as SceneDocument;\n}\n"],"mappings":"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","names":["value","path"]}
1
+ {"version":3,"sources":["../../../../src/scene/SceneDocument.ts"],"sourcesContent":["import type { CapeLayout } from \"../skin/CapeLayout\";\nimport { SKIN_PARTS } from \"../skin/SkinPart\";\nimport type { TripleArray } from \"../model/Model\";\nimport { DISPLAY_POSITIONS, DisplayPosition } from \"../model/DisplayPosition\";\nimport type { GuiItemLayer, GuiTextLayer, GuiTextureLayer } from \"../gui/GuiLayer\";\n\n/** Portable scene setup. Positions use model units (16 per block); rotations use radians. */\nexport interface SceneDocument {\n format: \"minerender-scene\";\n version: 1;\n minecraftVersion?: string;\n camera?: { position: TripleArray; target: TripleArray };\n objects: SceneObjectDefinition[];\n}\n\nexport interface SceneObjectDefinitionBase {\n id: string;\n name?: string;\n position?: TripleArray;\n rotation?: TripleArray;\n scale?: TripleArray;\n visible?: boolean;\n}\n\nexport type SceneSkinPosePart = \"head\" | \"body\" | \"rightArm\" | \"leftArm\" | \"rightLeg\" | \"leftLeg\" | \"cape\";\n\nexport interface SceneSkinDefinition extends SceneObjectDefinitionBase {\n type: \"skin\";\n /** Texture URL, Minecraft username, or UUID. Omit for Steve, or Alex with slim: true, from the active asset version. */\n skin?: string;\n cape?: { texture: string; layout?: CapeLayout };\n options?: { slim?: boolean; legacy?: boolean };\n /** Mesh names from SkinPart; hiding a base part does not hide its overlay. */\n hiddenParts?: string[];\n /** Absolute local XYZ joint rotations in radians; omitted joints keep their default pose. */\n pose?: Partial<Record<SceneSkinPosePart, TripleArray>>;\n}\n\nexport interface SceneBlockDefinition extends SceneObjectDefinitionBase {\n type: \"block\";\n asset: string;\n state?: Record<string, string>;\n options?: { wireframe?: boolean; tints?: Record<number, number> };\n}\n\n/** Supplied item state; see ItemModelContext. Item references are item IDs such as minecraft:apple. */\nexport interface SceneItemContext {\n displayContext?: DisplayPosition | \"none\";\n properties?: Record<string, boolean | string | number>;\n components?: Record<string, unknown>;\n count?: number;\n itemReferences?: Record<string, string>;\n}\n\nexport interface SceneModelDefinition extends SceneObjectDefinitionBase {\n type: \"item\" | \"model\";\n asset: string;\n options?: { wireframe?: boolean; displayPosition?: DisplayPosition; tints?: Record<number, number> };\n /** Item state for `type: \"item\"` definitions only. */\n context?: SceneItemContext;\n}\n\nexport interface SceneEntityDefinition extends SceneObjectDefinitionBase {\n type: \"entity\";\n asset: string;\n texture?: string;\n layers?: string[];\n when?: string[];\n textures?: Record<string, string>;\n options?: { flip?: boolean; wireframe?: boolean; tints?: Record<string, string | number> };\n /** Plays one named clip or blends clips with weights defaulting to 1, without normalization. */\n animation?: ({ name: string; clips?: never } | { name?: never; clips: { name: string; weight?: number }[] })\n & { loop?: boolean; speed?: number; time?: number; paused?: boolean };\n}\n\nexport type SceneGuiLayer = (Omit<GuiTextureLayer, \"texture\"> & { texture: string })\n | (Omit<GuiItemLayer, \"item\" | \"context\"> & { item: string; context?: Omit<SceneItemContext, \"displayContext\"> })\n | (Omit<GuiTextLayer, \"font\"> & { font?: string });\n\nexport interface SceneGuiDefinition extends SceneObjectDefinitionBase {\n type: \"gui\";\n layers: SceneGuiLayer[];\n}\n\nexport type SceneObjectDefinition = SceneSkinDefinition | SceneBlockDefinition | SceneModelDefinition\n | SceneEntityDefinition | SceneGuiDefinition;\n\ntype JsonObject = Record<string, unknown>;\ntype Validator = (value: unknown, path: string) => void;\n\nfunction fail(path: string, message: string): never {\n throw new Error(`${path}: ${message}`);\n}\n\nfunction object(value: unknown, path: string): JsonObject {\n if (!value || typeof value !== \"object\" || Array.isArray(value)\n || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail(path, \"expected a JSON object\");\n for (const key of Object.keys(value)) {\n if ([\"__proto__\", \"prototype\", \"constructor\"].includes(key)) fail(`${path}.${key}`, \"unsupported property\");\n }\n return value as JsonObject;\n}\n\nfunction fields(value: unknown, path: string, allowed: Record<string, Validator>, required: string[] = []): JsonObject {\n const input = object(value, path);\n for (const key of required) if (input[key] === undefined) fail(`${path}.${key}`, \"required\");\n for (const [key, entry] of Object.entries(input)) {\n if (!Object.prototype.hasOwnProperty.call(allowed, key)) fail(`${path}.${key}`, \"unsupported property\");\n if (entry !== undefined) allowed[key](entry, `${path}.${key}`);\n }\n return input;\n}\n\nconst text: Validator = (value, path) => {\n if (typeof value !== \"string\" || !value.trim()) fail(path, \"expected a nonempty string\");\n};\nconst literalText: Validator = (value, path) => {\n if (typeof value !== \"string\") fail(path, \"expected a string\");\n};\nconst boolean: Validator = (value, path) => {\n if (typeof value !== \"boolean\") fail(path, \"expected a boolean\");\n};\nconst number: Validator = (value, path) => {\n if (typeof value !== \"number\" || !Number.isFinite(value)) fail(path, \"expected a finite number\");\n};\nconst positive: Validator = (value, path) => {\n number(value, path);\n if ((value as number) <= 0) fail(path, \"expected a positive number\");\n};\nconst nonnegative: Validator = (value, path) => {\n number(value, path);\n if ((value as number) < 0) fail(path, \"expected a nonnegative number\");\n};\nconst color: Validator = (value, path) => {\n number(value, path);\n if (!Number.isInteger(value) || (value as number) < 0 || (value as number) > 0xffffff) {\n fail(path, \"expected an RGB integer from 0 to 16777215\");\n }\n};\nconst asset: Validator = (value, path) => {\n text(value, path);\n if (!/^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value as string)\n || (value as string).split(/[:/]/).some(part => !part || part === \".\" || part === \"..\")) {\n fail(path, \"expected a resource ID such as minecraft:stone\");\n }\n};\nconst version: Validator = (value, path) => {\n text(value, path);\n if (!/^[a-zA-Z0-9._-]+$/.test(value as string)) fail(path, \"expected a Minecraft version without slashes\");\n};\nconst tuple = (size: number, item: Validator = number): Validator => (value, path) => {\n if (!Array.isArray(value) || value.length !== size) fail(path, `expected ${size} numbers`);\n for (let index = 0; index < size; index++) item(value[index], `${path}[${index}]`);\n};\nconst list = (item: Validator): Validator => (value, path) => {\n if (!Array.isArray(value)) fail(path, \"expected an array\");\n for (let index = 0; index < value.length; index++) item(value[index], `${path}[${index}]`);\n};\nconst dictionary = (item: Validator, numericKeys = false): Validator => (value, path) => {\n for (const [key, entry] of Object.entries(object(value, path))) {\n if (numericKeys && !/^\\d+$/.test(key)) fail(`${path}.${key}`, \"expected a nonnegative tint index\");\n item(entry, `${path}.${key}`);\n }\n};\nconst enumeration = (values: readonly string[]): Validator => (value, path) => {\n if (!values.includes(value as string)) fail(path, `expected one of: ${values.join(\", \")}`);\n};\nconst textStyle = { color, bold: boolean, italic: boolean };\nconst jsonValue: Validator = (value, path) => {\n if (value === null || typeof value === \"boolean\" || typeof value === \"string\") return;\n if (typeof value === \"number\") number(value, path);\n else if (Array.isArray(value)) list(jsonValue)(value, path);\n else dictionary(jsonValue)(value, path);\n};\nconst propertyValue: Validator = (value, path) => {\n if (typeof value === \"boolean\" || typeof value === \"string\") return;\n if (typeof value !== \"number\" || !Number.isFinite(value)) fail(path, \"expected a boolean, string, or finite number\");\n};\nconst itemContext = (withDisplayContext: boolean): Validator => (value, path) => {\n fields(value, path, {\n ...(withDisplayContext ? { displayContext: enumeration([...DISPLAY_POSITIONS, \"none\"]) } : {}),\n properties: dictionary(propertyValue), components: dictionary(jsonValue), itemReferences: dictionary(asset),\n count: (value, path) => { if (!Number.isInteger(value) || (value as number) < 0) fail(path, \"expected a nonnegative integer\"); }\n });\n};\n\nfunction entityAnimation(value: unknown, path: string): void {\n const animation = object(value, path);\n if ([\"name\", \"clips\"].filter(key => animation[key] !== undefined).length !== 1) {\n fail(path, \"expected exactly one of name or clips\");\n }\n fields(value, path, { name: text, clips: (value, path) => {\n list((value, path) => { fields(value, path, { name: text, weight: nonnegative }, [\"name\"]); })(value, path);\n if (!(value as unknown[]).length) fail(path, \"expected at least one clip\");\n const names = new Set<string>();\n for (const [index, clip] of (value as { name: string }[]).entries()) {\n if (names.has(clip.name)) fail(`${path}[${index}].name`, `duplicate clip \"${clip.name}\"`);\n names.add(clip.name);\n }\n }, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean });\n}\n\nfunction guiLayer(value: unknown, path: string): void {\n const layer = object(value, path);\n if ([\"texture\", \"item\", \"text\"].filter(key => layer[key] !== undefined).length !== 1) {\n fail(path, \"expected exactly one of texture, item, or text\");\n }\n const layout = { name: text, position: tuple(2), size: tuple(2, positive) };\n if (layer.texture !== undefined) {\n fields(value, path, { ...layout, texture: asset, crop: (value, path) => {\n tuple(4)(value, path);\n const [x, y, width, height] = value as number[];\n nonnegative(x, `${path}[0]`); nonnegative(y, `${path}[1]`);\n positive(width, `${path}[2]`); positive(height, `${path}[3]`);\n } });\n } else if (layer.item !== undefined) {\n fields(value, path, { ...layout, item: asset, context: itemContext(false), tints: dictionary(color, true) });\n } else {\n fields(value, path, { ...layout, ...textStyle, font: asset, shadow: boolean, maxWidth: positive, lineHeight: positive,\n text: (value, path) => {\n if (typeof value === \"string\") return;\n list((run, path) => { fields(run, path, { text: literalText, ...textStyle }, [\"text\"]); })(value, path);\n }\n });\n }\n}\n\nfunction validateObject(value: unknown, path: string): void {\n const input = object(value, path);\n const common = { id: text, name: text, type: text, position: tuple(3), rotation: tuple(3), scale: tuple(3), visible: boolean };\n const required = [\"id\", \"type\"];\n const renderOptions = { wireframe: boolean };\n switch (input.type) {\n case \"skin\":\n fields(value, path, { ...common, skin: text,\n cape: (value, path) => { fields(value, path, { texture: text, layout: enumeration([\"minecraft\", \"optifine\", \"labymod\"]) }, [\"texture\"]); },\n hiddenParts: list(enumeration(SKIN_PARTS)),\n pose: (value, path) => { fields(value, path, {\n head: tuple(3), body: tuple(3), rightArm: tuple(3), leftArm: tuple(3),\n rightLeg: tuple(3), leftLeg: tuple(3), cape: tuple(3)\n }); },\n options: (value, path) => { fields(value, path, { slim: boolean, legacy: boolean }); }\n }, required);\n break;\n case \"block\":\n fields(value, path, { ...common, asset, state: dictionary(text),\n options: (value, path) => { fields(value, path, { ...renderOptions, tints: dictionary(color, true) }); }\n }, [...required, \"asset\"]);\n break;\n case \"item\":\n case \"model\":\n fields(value, path, { ...common, asset, ...(input.type === \"item\" ? { context: itemContext(true) } : {}),\n options: (value, path) => { fields(value, path, { ...renderOptions, displayPosition: enumeration(DISPLAY_POSITIONS), tints: dictionary(color, true) }); }\n }, [...required, \"asset\"]);\n break;\n case \"entity\":\n fields(value, path, { ...common, asset, texture: asset, layers: list(text), when: list(text), textures: dictionary(asset),\n options: (value, path) => { fields(value, path, { ...renderOptions, flip: boolean,\n tints: dictionary((value, path) => { typeof value === \"string\" ? text(value, path) : color(value, path); }) }); },\n animation: entityAnimation\n }, [...required, \"asset\"]);\n if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, \"expected at least one layer\");\n break;\n case \"gui\":\n fields(value, path, { ...common, layers: list(guiLayer) }, [...required, \"layers\"]);\n break;\n default:\n fail(`${path}.type`, \"expected skin, block, item, entity, model, or gui\");\n }\n}\n\n/** Validates and copies a scene document without requesting assets or changing the active asset version. */\nexport function parseSceneDocument(value: unknown): SceneDocument {\n if (typeof value === \"string\") {\n try { value = JSON.parse(value); }\n catch { fail(\"scene\", \"invalid JSON\"); }\n }\n const input = fields(value, \"scene\", {\n format: enumeration([\"minerender-scene\"]),\n version: (value, path) => { if (value !== 1) fail(path, \"only scene document version 1 is supported\"); },\n minecraftVersion: version,\n camera: (value, path) => { fields(value, path, { position: tuple(3), target: tuple(3) }, [\"position\", \"target\"]); },\n objects: list(validateObject)\n }, [\"format\", \"version\", \"objects\"]);\n const ids = new Set<string>();\n for (const [index, entry] of (input.objects as SceneObjectDefinition[]).entries()) {\n if (ids.has(entry.id)) fail(`scene.objects[${index}].id`, `duplicate object ID \"${entry.id}\"`);\n ids.add(entry.id);\n }\n return JSON.parse(JSON.stringify(input)) as SceneDocument;\n}\n"],"mappings":"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","names":["value","path"]}
@@ -161,10 +161,20 @@ var SceneDocumentLoader = class {
161
161
  const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
162
162
  object = entity;
163
163
  if (definition.animation) {
164
+ const animation = definition.animation;
164
165
  const animations = await Entities.getAnimations(key);
165
- const clip = animations?.[definition.animation.name];
166
- if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
167
- entity.playAnimation(clip, definition.animation);
166
+ if (animation.name !== void 0) {
167
+ const clip = animations && Object.prototype.hasOwnProperty.call(animations, animation.name) ? animations[animation.name] : void 0;
168
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${animation.name}"`);
169
+ entity.playAnimation(clip, animation);
170
+ } else {
171
+ const clips = animation.clips.map(({ name }) => {
172
+ const clip = animations && Object.prototype.hasOwnProperty.call(animations, name) ? animations[name] : void 0;
173
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${name}"`);
174
+ return clip;
175
+ });
176
+ entity.playAnimations(clips, { ...animation, weights: animation.clips.map((clip) => clip.weight ?? 1) });
177
+ }
168
178
  }
169
179
  break;
170
180
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/scene/SceneDocumentLoader.ts"],"sourcesContent":["import { Group } from \"three\";\nimport type { Object3D } from \"three\";\nimport { AssetKey } from \"../assets/AssetKey\";\nimport { AssetLoader } from \"../assets/AssetLoader\";\nimport { BlockStates } from \"../assets/BlockStates\";\nimport { Entities } from \"../assets/Entities\";\nimport { ItemModelContext, Models } from \"../assets/Models\";\nimport type { BasicMinecraftAsset } from \"../MinecraftAsset\";\nimport { DisplayPosition } from \"../model/DisplayPosition\";\nimport { MineRenderScene } from \"../renderer/MineRenderScene\";\nimport { isSceneObject, SceneObject } from \"../renderer/SceneObject\";\nimport type { SceneObjectOptions } from \"../renderer/SceneObjectOptions\";\nimport { EntityObject } from \"../entity/scene/EntityObject\";\nimport { SkinObject } from \"../skin/scene/SkinObject\";\nimport { Skins } from \"../skin/Skins\";\nimport { GuiObject } from \"../gui/scene/GuiObject\";\nimport type { GuiLayer } from \"../gui/GuiLayer\";\nimport { parseSceneDocument, SceneDocument, SceneItemContext, SceneObjectDefinition } from \"./SceneDocument\";\n\nfunction itemKey(asset: string): AssetKey {\n const key = AssetKey.parse(\"models\", asset);\n return key.type === \"item\" ? key : new AssetKey(key.namespace, key.getFullPath(), \"models\", \"item\");\n}\n\nfunction itemContext(context: SceneItemContext | undefined): ItemModelContext {\n const { itemReferences, ...rest } = context ?? {};\n return itemReferences\n ? { ...rest, itemReferences: Object.fromEntries(Object.entries(itemReferences).map(([id, item]) => [id, itemKey(item)])) }\n : rest;\n}\n\nexport interface LoadedSceneObject {\n readonly root: Group;\n readonly object: SceneObject;\n readonly definition: SceneObjectDefinition;\n /** Advances entity playback in seconds unless the definition has paused it. */\n advanceAnimation(delta: number): void;\n /** Removes this object and releases its owned resources. Safe to call more than once. */\n dispose(): void;\n}\n\nexport interface LoadedSceneDocument {\n readonly root: Group;\n readonly objects: LoadedSceneObject[];\n readonly definition: SceneDocument;\n advanceAnimations(delta: number): void;\n dispose(): void;\n}\n\n// Blocks create models and block entities through their scene, without forwarding a parent.\n// A nested scene keeps those visuals inside the object's transform group, including after state changes.\nclass DocumentObjectScene extends MineRenderScene {\n public async addSceneObject<A extends BasicMinecraftAsset, T extends SceneObject, O extends SceneObjectOptions>(\n _asset: A, supplier: () => T | Promise<T>, _options?: Partial<O>, parent: Object3D = this\n ): Promise<T> {\n const object = await supplier();\n return this.initialize(object, parent);\n }\n\n public async initialize<T extends SceneObject>(object: T, parent: Object3D = this): Promise<T> {\n object.scene = this;\n try {\n await object.init();\n parent.add(object);\n return object;\n } catch (error) {\n object.dispose();\n object.removeFromParent();\n throw error;\n }\n }\n\n public dispose(): void {\n for (const child of [...this.children]) {\n if (child.parent !== this) continue;\n if (isSceneObject(child)) child.dispose();\n child.removeFromParent();\n }\n }\n}\n\nasync function skinTexture(value: string, cape = false): Promise<string> {\n if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;\n const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));\n if (!texture) throw new Error(`Could not find ${cape ? \"cape\" : \"skin\"} for \"${value}\"`);\n return texture;\n}\n\n/** Loads portable scene documents using the asset sources already configured by the caller. */\nexport class SceneDocumentLoader {\n public static parse(value: unknown): SceneDocument {\n return parseSceneDocument(value);\n }\n\n /**\n * Loads objects concurrently and attaches them in document order after every load succeeds.\n * A failure waits for all loads, disposes staged objects, and reports the first error in document order.\n */\n public static async load(scene: MineRenderScene, value: unknown, parent: Object3D = scene): Promise<LoadedSceneDocument> {\n const definition = this.parse(value);\n if (definition.minecraftVersion !== undefined && definition.minecraftVersion !== AssetLoader.version) {\n throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion(\"${definition.minecraftVersion}\") before loading it`);\n }\n const root = new Group();\n root.name = \"MineRender scene document\";\n const objects: LoadedSceneObject[] = [];\n let disposed = false;\n const loaded: LoadedSceneDocument = {\n root, objects, definition,\n advanceAnimations: delta => { if (!disposed) for (const object of objects) object.advanceAnimation(delta); },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n root.removeFromParent();\n for (const object of objects) object.dispose();\n root.clear();\n scene.dirty = true;\n }\n };\n try {\n const results = await Promise.allSettled(definition.objects.map(object => this.loadValidatedObject(scene, object)));\n for (const result of results) if (result.status === \"fulfilled\") objects.push(result.value);\n const failure = results.find((result): result is PromiseRejectedResult => result.status === \"rejected\");\n if (failure) throw failure.reason;\n for (const object of objects) root.add(object.root);\n parent.add(root);\n scene.dirty = true;\n return loaded;\n } catch (error) {\n loaded.dispose();\n throw error;\n }\n }\n\n /** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */\n public static async loadObject(scene: MineRenderScene, value: SceneObjectDefinition, parent: Object3D = scene): Promise<LoadedSceneObject> {\n const definition = this.parse({ format: \"minerender-scene\", version: 1, objects: [value] }).objects[0];\n return this.loadValidatedObject(scene, definition, parent);\n }\n\n private static async loadValidatedObject(scene: MineRenderScene, definition: SceneObjectDefinition, parent?: Object3D): Promise<LoadedSceneObject> {\n const root = new Group();\n root.name = definition.name ?? definition.id;\n root.userData.sceneObjectId = definition.id;\n root.position.fromArray(definition.position ?? [0, 0, 0]);\n root.rotation.set(...(definition.rotation ?? [0, 0, 0]));\n root.scale.fromArray(definition.scale ?? [1, 1, 1]);\n root.visible = definition.visible ?? true;\n const content = new DocumentObjectScene();\n root.add(content);\n let object: SceneObject | undefined;\n try {\n switch (definition.type) {\n case \"skin\": {\n const skin = new SkinObject({ ...definition.options, instanceMeshes: false });\n object = skin;\n const texture = definition.skin ? await skinTexture(definition.skin)\n : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? \"slim/alex\" : \"wide/steve\"}.png`;\n await skin.setSkinTexture(texture);\n await content.initialize(skin);\n if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);\n for (const [part, rotation] of Object.entries(definition.pose ?? {})) {\n skin.getGroupByName(part)?.rotation.set(...rotation, \"XYZ\");\n }\n for (const part of definition.hiddenParts ?? []) {\n const mesh = skin.getMeshByName(part);\n if (mesh) mesh.visible = false;\n }\n break;\n }\n case \"block\": {\n const state = await BlockStates.get(AssetKey.parse(\"blockstates\", definition.asset));\n if (!state) throw new Error(`Could not load block ${definition.asset}`);\n object = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false }) as SceneObject;\n break;\n }\n case \"item\":\n case \"model\": {\n const key = definition.type === \"item\" ? itemKey(definition.asset) : AssetKey.parse(\"models\", definition.asset);\n const model = await Models.getMerged(key, definition.type === \"item\" ? itemContext(definition.context) : {});\n if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);\n object = await content.addModel(model, { ...(definition.type === \"item\" ? { displayPosition: DisplayPosition.GUI } : {}),\n ...definition.options, instanceMeshes: false }) as SceneObject;\n break;\n }\n case \"entity\": {\n const key = AssetKey.parse(\"entity-models\", definition.asset);\n const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse(\"textures\", definition.texture) : undefined, {\n layers: definition.layers, when: definition.when,\n textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse(\"textures\", texture)]))\n });\n if (!model) throw new Error(`Could not load entity ${definition.asset}`);\n const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false }) as EntityObject;\n object = entity;\n if (definition.animation) {\n const animations = await Entities.getAnimations(key);\n const clip = animations?.[definition.animation.name];\n if (!clip) throw new Error(`Entity ${definition.asset} has no animation \"${definition.animation.name}\"`);\n entity.playAnimation(clip, definition.animation);\n }\n break;\n }\n case \"gui\":\n object = await content.initialize(new GuiObject(definition.layers.map((layer): GuiLayer =>\n \"item\" in layer ? { ...layer, context: layer.context && itemContext(layer.context) } : layer)));\n break;\n }\n let disposed = false;\n const loaded: LoadedSceneObject = {\n root, object, definition,\n advanceAnimation: delta => {\n if (!disposed && definition.type === \"entity\" && !definition.animation?.paused) (object as EntityObject).advanceAnimation(delta);\n },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n root.removeFromParent();\n object!.dispose();\n object!.removeFromParent();\n content.dispose();\n root.clear();\n scene.dirty = true;\n }\n };\n parent?.add(root);\n scene.dirty = true;\n return loaded;\n } catch (error) {\n object?.dispose();\n object?.removeFromParent();\n content.dispose();\n root.removeFromParent();\n root.clear();\n throw new Error(`Scene object \"${definition.name ?? definition.id}\": ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/scene/SceneDocumentLoader.ts"],"sourcesContent":["import { Group } from \"three\";\nimport type { Object3D } from \"three\";\nimport { AssetKey } from \"../assets/AssetKey\";\nimport { AssetLoader } from \"../assets/AssetLoader\";\nimport { BlockStates } from \"../assets/BlockStates\";\nimport { Entities } from \"../assets/Entities\";\nimport { ItemModelContext, Models } from \"../assets/Models\";\nimport type { BasicMinecraftAsset } from \"../MinecraftAsset\";\nimport { DisplayPosition } from \"../model/DisplayPosition\";\nimport { MineRenderScene } from \"../renderer/MineRenderScene\";\nimport { isSceneObject, SceneObject } from \"../renderer/SceneObject\";\nimport type { SceneObjectOptions } from \"../renderer/SceneObjectOptions\";\nimport { EntityObject } from \"../entity/scene/EntityObject\";\nimport { SkinObject } from \"../skin/scene/SkinObject\";\nimport { Skins } from \"../skin/Skins\";\nimport { GuiObject } from \"../gui/scene/GuiObject\";\nimport type { GuiLayer } from \"../gui/GuiLayer\";\nimport { parseSceneDocument, SceneDocument, SceneItemContext, SceneObjectDefinition } from \"./SceneDocument\";\n\nfunction itemKey(asset: string): AssetKey {\n const key = AssetKey.parse(\"models\", asset);\n return key.type === \"item\" ? key : new AssetKey(key.namespace, key.getFullPath(), \"models\", \"item\");\n}\n\nfunction itemContext(context: SceneItemContext | undefined): ItemModelContext {\n const { itemReferences, ...rest } = context ?? {};\n return itemReferences\n ? { ...rest, itemReferences: Object.fromEntries(Object.entries(itemReferences).map(([id, item]) => [id, itemKey(item)])) }\n : rest;\n}\n\nexport interface LoadedSceneObject {\n readonly root: Group;\n readonly object: SceneObject;\n readonly definition: SceneObjectDefinition;\n /** Advances entity playback in seconds unless the definition has paused it. */\n advanceAnimation(delta: number): void;\n /** Removes this object and releases its owned resources. Safe to call more than once. */\n dispose(): void;\n}\n\nexport interface LoadedSceneDocument {\n readonly root: Group;\n readonly objects: LoadedSceneObject[];\n readonly definition: SceneDocument;\n advanceAnimations(delta: number): void;\n dispose(): void;\n}\n\n// Blocks create models and block entities through their scene, without forwarding a parent.\n// A nested scene keeps those visuals inside the object's transform group, including after state changes.\nclass DocumentObjectScene extends MineRenderScene {\n public async addSceneObject<A extends BasicMinecraftAsset, T extends SceneObject, O extends SceneObjectOptions>(\n _asset: A, supplier: () => T | Promise<T>, _options?: Partial<O>, parent: Object3D = this\n ): Promise<T> {\n const object = await supplier();\n return this.initialize(object, parent);\n }\n\n public async initialize<T extends SceneObject>(object: T, parent: Object3D = this): Promise<T> {\n object.scene = this;\n try {\n await object.init();\n parent.add(object);\n return object;\n } catch (error) {\n object.dispose();\n object.removeFromParent();\n throw error;\n }\n }\n\n public dispose(): void {\n for (const child of [...this.children]) {\n if (child.parent !== this) continue;\n if (isSceneObject(child)) child.dispose();\n child.removeFromParent();\n }\n }\n}\n\nasync function skinTexture(value: string, cape = false): Promise<string> {\n if (!/^[a-zA-Z0-9_-]{1,36}$/.test(value)) return value;\n const texture = await (cape ? Skins.capeFromUuidOrUsername(value) : Skins.fromUuidOrUsername(value));\n if (!texture) throw new Error(`Could not find ${cape ? \"cape\" : \"skin\"} for \"${value}\"`);\n return texture;\n}\n\n/** Loads portable scene documents using the asset sources already configured by the caller. */\nexport class SceneDocumentLoader {\n public static parse(value: unknown): SceneDocument {\n return parseSceneDocument(value);\n }\n\n /**\n * Loads objects concurrently and attaches them in document order after every load succeeds.\n * A failure waits for all loads, disposes staged objects, and reports the first error in document order.\n */\n public static async load(scene: MineRenderScene, value: unknown, parent: Object3D = scene): Promise<LoadedSceneDocument> {\n const definition = this.parse(value);\n if (definition.minecraftVersion !== undefined && definition.minecraftVersion !== AssetLoader.version) {\n throw new Error(`Scene requires Minecraft ${definition.minecraftVersion}; call AssetLoader.setVersion(\"${definition.minecraftVersion}\") before loading it`);\n }\n const root = new Group();\n root.name = \"MineRender scene document\";\n const objects: LoadedSceneObject[] = [];\n let disposed = false;\n const loaded: LoadedSceneDocument = {\n root, objects, definition,\n advanceAnimations: delta => { if (!disposed) for (const object of objects) object.advanceAnimation(delta); },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n root.removeFromParent();\n for (const object of objects) object.dispose();\n root.clear();\n scene.dirty = true;\n }\n };\n try {\n const results = await Promise.allSettled(definition.objects.map(object => this.loadValidatedObject(scene, object)));\n for (const result of results) if (result.status === \"fulfilled\") objects.push(result.value);\n const failure = results.find((result): result is PromiseRejectedResult => result.status === \"rejected\");\n if (failure) throw failure.reason;\n for (const object of objects) root.add(object.root);\n parent.add(root);\n scene.dirty = true;\n return loaded;\n } catch (error) {\n loaded.dispose();\n throw error;\n }\n }\n\n /** Loads an independently editable object. Its root owns the document's position, rotation, and scale. */\n public static async loadObject(scene: MineRenderScene, value: SceneObjectDefinition, parent: Object3D = scene): Promise<LoadedSceneObject> {\n const definition = this.parse({ format: \"minerender-scene\", version: 1, objects: [value] }).objects[0];\n return this.loadValidatedObject(scene, definition, parent);\n }\n\n private static async loadValidatedObject(scene: MineRenderScene, definition: SceneObjectDefinition, parent?: Object3D): Promise<LoadedSceneObject> {\n const root = new Group();\n root.name = definition.name ?? definition.id;\n root.userData.sceneObjectId = definition.id;\n root.position.fromArray(definition.position ?? [0, 0, 0]);\n root.rotation.set(...(definition.rotation ?? [0, 0, 0]));\n root.scale.fromArray(definition.scale ?? [1, 1, 1]);\n root.visible = definition.visible ?? true;\n const content = new DocumentObjectScene();\n root.add(content);\n let object: SceneObject | undefined;\n try {\n switch (definition.type) {\n case \"skin\": {\n const skin = new SkinObject({ ...definition.options, instanceMeshes: false });\n object = skin;\n const texture = definition.skin ? await skinTexture(definition.skin)\n : `${AssetLoader.ROOT}/assets/minecraft/textures/entity/player/${definition.options?.slim ? \"slim/alex\" : \"wide/steve\"}.png`;\n await skin.setSkinTexture(texture);\n await content.initialize(skin);\n if (definition.cape) await skin.setCapeTexture(await skinTexture(definition.cape.texture, true), definition.cape.layout);\n for (const [part, rotation] of Object.entries(definition.pose ?? {})) {\n skin.getGroupByName(part)?.rotation.set(...rotation, \"XYZ\");\n }\n for (const part of definition.hiddenParts ?? []) {\n const mesh = skin.getMeshByName(part);\n if (mesh) mesh.visible = false;\n }\n break;\n }\n case \"block\": {\n const state = await BlockStates.get(AssetKey.parse(\"blockstates\", definition.asset));\n if (!state) throw new Error(`Could not load block ${definition.asset}`);\n object = await content.addBlock(state, { ...definition.options, initialState: definition.state, instanceMeshes: false }) as SceneObject;\n break;\n }\n case \"item\":\n case \"model\": {\n const key = definition.type === \"item\" ? itemKey(definition.asset) : AssetKey.parse(\"models\", definition.asset);\n const model = await Models.getMerged(key, definition.type === \"item\" ? itemContext(definition.context) : {});\n if (!model) throw new Error(`Could not load ${definition.type} ${definition.asset}`);\n object = await content.addModel(model, { ...(definition.type === \"item\" ? { displayPosition: DisplayPosition.GUI } : {}),\n ...definition.options, instanceMeshes: false }) as SceneObject;\n break;\n }\n case \"entity\": {\n const key = AssetKey.parse(\"entity-models\", definition.asset);\n const model = await Entities.getEntity(key, definition.texture ? AssetKey.parse(\"textures\", definition.texture) : undefined, {\n layers: definition.layers, when: definition.when,\n textures: definition.textures && Object.fromEntries(Object.entries(definition.textures).map(([name, texture]) => [name, AssetKey.parse(\"textures\", texture)]))\n });\n if (!model) throw new Error(`Could not load entity ${definition.asset}`);\n const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false }) as EntityObject;\n object = entity;\n if (definition.animation) {\n const animation = definition.animation;\n const animations = await Entities.getAnimations(key);\n if (animation.name !== undefined) {\n const clip = animations && Object.prototype.hasOwnProperty.call(animations, animation.name) ? animations[animation.name] : undefined;\n if (!clip) throw new Error(`Entity ${definition.asset} has no animation \"${animation.name}\"`);\n entity.playAnimation(clip, animation);\n } else {\n const clips = animation.clips.map(({ name }) => {\n const clip = animations && Object.prototype.hasOwnProperty.call(animations, name) ? animations[name] : undefined;\n if (!clip) throw new Error(`Entity ${definition.asset} has no animation \"${name}\"`);\n return clip;\n });\n entity.playAnimations(clips, { ...animation, weights: animation.clips.map(clip => clip.weight ?? 1) });\n }\n }\n break;\n }\n case \"gui\":\n object = await content.initialize(new GuiObject(definition.layers.map((layer): GuiLayer =>\n \"item\" in layer ? { ...layer, context: layer.context && itemContext(layer.context) } : layer)));\n break;\n }\n let disposed = false;\n const loaded: LoadedSceneObject = {\n root, object, definition,\n advanceAnimation: delta => {\n if (!disposed && definition.type === \"entity\" && !definition.animation?.paused) (object as EntityObject).advanceAnimation(delta);\n },\n dispose: () => {\n if (disposed) return;\n disposed = true;\n root.removeFromParent();\n object!.dispose();\n object!.removeFromParent();\n content.dispose();\n root.clear();\n scene.dirty = true;\n }\n };\n parent?.add(root);\n scene.dirty = true;\n return loaded;\n } catch (error) {\n object?.dispose();\n object?.removeFromParent();\n content.dispose();\n root.removeFromParent();\n root.clear();\n throw new Error(`Scene object \"${definition.name ?? definition.id}\": ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -3491,8 +3491,17 @@ interface SceneEntityDefinition extends SceneObjectDefinitionBase {
3491
3491
  wireframe?: boolean;
3492
3492
  tints?: Record<string, string | number>;
3493
3493
  };
3494
- animation?: {
3494
+ /** Plays one named clip or blends clips with weights defaulting to 1, without normalization. */
3495
+ animation?: ({
3495
3496
  name: string;
3497
+ clips?: never;
3498
+ } | {
3499
+ name?: never;
3500
+ clips: {
3501
+ name: string;
3502
+ weight?: number;
3503
+ }[];
3504
+ }) & {
3496
3505
  loop?: boolean;
3497
3506
  speed?: number;
3498
3507
  time?: number;
@@ -16741,6 +16741,23 @@ var itemContext = (withDisplayContext) => (value, path) => {
16741
16741
  }
16742
16742
  });
16743
16743
  };
16744
+ function entityAnimation(value, path) {
16745
+ const animation = object3(value, path);
16746
+ if (["name", "clips"].filter((key) => animation[key] !== void 0).length !== 1) {
16747
+ fail(path, "expected exactly one of name or clips");
16748
+ }
16749
+ fields(value, path, { name: text, clips: (value2, path2) => {
16750
+ list((value3, path3) => {
16751
+ fields(value3, path3, { name: text, weight: nonnegative }, ["name"]);
16752
+ })(value2, path2);
16753
+ if (!value2.length) fail(path2, "expected at least one clip");
16754
+ const names = /* @__PURE__ */ new Set();
16755
+ for (const [index, clip] of value2.entries()) {
16756
+ if (names.has(clip.name)) fail(`${path2}[${index}].name`, `duplicate clip "${clip.name}"`);
16757
+ names.add(clip.name);
16758
+ }
16759
+ }, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean });
16760
+ }
16744
16761
  function guiLayer(value, path) {
16745
16762
  const layer = object3(value, path);
16746
16763
  if (["texture", "item", "text"].filter((key) => layer[key] !== void 0).length !== 1) {
@@ -16843,9 +16860,7 @@ function validateObject(value, path) {
16843
16860
  })
16844
16861
  });
16845
16862
  },
16846
- animation: (value2, path2) => {
16847
- fields(value2, path2, { name: text, loop: boolean, speed: nonnegative, time: nonnegative, paused: boolean }, ["name"]);
16848
- }
16863
+ animation: entityAnimation
16849
16864
  }, [...required, "asset"]);
16850
16865
  if (Array.isArray(input.layers) && !input.layers.length) fail(`${path}.layers`, "expected at least one layer");
16851
16866
  break;
@@ -17112,10 +17127,20 @@ var SceneDocumentLoader = class {
17112
17127
  const entity = await content.addEntity(model, { ...definition.options, instanceMeshes: false });
17113
17128
  object4 = entity;
17114
17129
  if (definition.animation) {
17130
+ const animation = definition.animation;
17115
17131
  const animations = await Entities.getAnimations(key);
17116
- const clip = animations?.[definition.animation.name];
17117
- if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${definition.animation.name}"`);
17118
- entity.playAnimation(clip, definition.animation);
17132
+ if (animation.name !== void 0) {
17133
+ const clip = animations && Object.prototype.hasOwnProperty.call(animations, animation.name) ? animations[animation.name] : void 0;
17134
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${animation.name}"`);
17135
+ entity.playAnimation(clip, animation);
17136
+ } else {
17137
+ const clips = animation.clips.map(({ name }) => {
17138
+ const clip = animations && Object.prototype.hasOwnProperty.call(animations, name) ? animations[name] : void 0;
17139
+ if (!clip) throw new Error(`Entity ${definition.asset} has no animation "${name}"`);
17140
+ return clip;
17141
+ });
17142
+ entity.playAnimations(clips, { ...animation, weights: animation.clips.map((clip) => clip.weight ?? 1) });
17143
+ }
17119
17144
  }
17120
17145
  break;
17121
17146
  }