minerender 2.1.0-alpha.59 → 2.1.0-alpha.60

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.
@@ -1084,6 +1084,26 @@ var init_Materials = __esm({
1084
1084
  }
1085
1085
  });
1086
1086
 
1087
+ // src/model/Model.ts
1088
+ function isDoubleArray(obj) {
1089
+ return Array.isArray(obj) && obj.length === 2;
1090
+ }
1091
+ function isTripleArray(obj) {
1092
+ return Array.isArray(obj) && obj.length === 3;
1093
+ }
1094
+ function isQuadArray(obj) {
1095
+ return Array.isArray(obj) && obj.length === 4;
1096
+ }
1097
+ var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
1098
+ var init_Model = __esm({
1099
+ "src/model/Model.ts"() {
1100
+ ITEM_GENERATED = "item/generated";
1101
+ BUILTIN_GENERATED = "builtin/generated";
1102
+ BUILTIN_ENTITY = "builtin/entity";
1103
+ DEFAULT_ELEMENTS = [];
1104
+ }
1105
+ });
1106
+
1087
1107
  // ../../node_modules/ts-deepmerge/dist/index.js
1088
1108
  var require_dist = __commonJS({
1089
1109
  "../../node_modules/ts-deepmerge/dist/index.js"(exports) {
@@ -1342,6 +1362,7 @@ var init_DisplayTransforms = __esm({
1342
1362
  var import_ts_deepmerge, ModelMerger;
1343
1363
  var init_ModelMerger = __esm({
1344
1364
  "src/model/ModelMerger.ts"() {
1365
+ init_Model();
1345
1366
  init_Models();
1346
1367
  import_ts_deepmerge = __toESM(require_dist());
1347
1368
  init_AssetKey();
@@ -1367,6 +1388,7 @@ var init_ModelMerger = __esm({
1367
1388
  }
1368
1389
  const models = [];
1369
1390
  const parentKey = AssetKey.parse("models", model.parent);
1391
+ if (parentKey.namespace === "minecraft" && parentKey.getFullPath() === BUILTIN_ENTITY) return models;
1370
1392
  parentKey.root = model.key?.root;
1371
1393
  const parentModel = await Models.getRaw(parentKey);
1372
1394
  if (parentModel) {
@@ -1433,12 +1455,12 @@ var init_Models = __esm({
1433
1455
  const result = await AssetLoader.getFirst([itemKey, key], "json" /* JSON */);
1434
1456
  if (!result) return void 0;
1435
1457
  if (result.key.assetType !== "items") return { ...result.asset, key };
1436
- const reference = this.defaultItemModel(result.asset.model, key);
1437
- const modelKey = AssetKey.parse("models", reference);
1458
+ const selected = this.defaultItemModel(result.asset.model, key);
1459
+ const modelKey = AssetKey.parse("models", selected.model);
1438
1460
  modelKey.root = key.root;
1439
1461
  const model2 = await this.getRaw(modelKey);
1440
- if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${reference}`);
1441
- return model2;
1462
+ if (!model2) throw new Error(`Item ${key.toNamespacedString()} references missing model ${selected.model}`);
1463
+ return selected.special ? { ...model2, special: selected.special } : model2;
1442
1464
  });
1443
1465
  return model ? { ...model, key: Object.assign(new AssetKey("", ""), model.key) } : void 0;
1444
1466
  }
@@ -1449,8 +1471,25 @@ var init_Models = __esm({
1449
1471
  }
1450
1472
  switch (node.type.replace(/^minecraft:/, "")) {
1451
1473
  case "model":
1452
- if (typeof node.model === "string" && node.model) return node.model;
1474
+ if (typeof node.model === "string" && node.model) return { model: node.model };
1453
1475
  break;
1476
+ case "special": {
1477
+ if (!node.base || !node.model || typeof node.model !== "object") break;
1478
+ const special = node.model;
1479
+ switch (special.type) {
1480
+ case "chest":
1481
+ case "minecraft:chest":
1482
+ case "bed":
1483
+ case "minecraft:bed":
1484
+ if (typeof special.texture === "string" && special.texture) return { model: node.base, special };
1485
+ break;
1486
+ case "head":
1487
+ case "minecraft:head":
1488
+ if (typeof special.kind === "string" && special.kind) return { model: node.base, special };
1489
+ break;
1490
+ }
1491
+ throw new Error(`Unsupported special item renderer ${special.type} for ${key.toNamespacedString()}`);
1492
+ }
1454
1493
  case "condition":
1455
1494
  return this.defaultItemModel(node.on_false, key);
1456
1495
  case "select": {
@@ -3012,6 +3051,102 @@ var init_UVMapper = __esm({
3012
3051
  }
3013
3052
  });
3014
3053
 
3054
+ // src/MinecraftCubeTexture.ts
3055
+ var MinecraftCubeTexture;
3056
+ var init_MinecraftCubeTexture = __esm({
3057
+ "src/MinecraftCubeTexture.ts"() {
3058
+ init_UVMapper();
3059
+ init_CubeFace();
3060
+ MinecraftCubeTexture = class {
3061
+ constructor(u, v, width, height) {
3062
+ this.u = u;
3063
+ this.v = v;
3064
+ this.width = width;
3065
+ this.height = height;
3066
+ }
3067
+ mapW(x) {
3068
+ return x / this.width;
3069
+ }
3070
+ mapH(x) {
3071
+ return 1 - x / this.height;
3072
+ }
3073
+ getUvEast(w, h, l) {
3074
+ return [
3075
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
3076
+ [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
3077
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l)],
3078
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)]
3079
+ ];
3080
+ }
3081
+ getUvWest(w, h, l) {
3082
+ return [
3083
+ [this.mapW(this.u + l), this.mapH(this.v + l + h)],
3084
+ [this.mapW(this.u), this.mapH(this.v + l + h)],
3085
+ [this.mapW(this.u + l), this.mapH(this.v + l)],
3086
+ [this.mapW(this.u), this.mapH(this.v + l)]
3087
+ ];
3088
+ }
3089
+ getUvUp(w, h, l) {
3090
+ return [
3091
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)],
3092
+ [this.mapW(this.u + l + w + w), this.mapH(this.v + l)],
3093
+ [this.mapW(this.u + l + w), this.mapH(this.v)],
3094
+ [this.mapW(this.u + l + w + w), this.mapH(this.v)]
3095
+ ];
3096
+ }
3097
+ getUvDown(w, h, l) {
3098
+ return [
3099
+ [this.mapW(this.u + l), this.mapH(this.v)],
3100
+ [this.mapW(this.u + l + w), this.mapH(this.v)],
3101
+ [this.mapW(this.u + l), this.mapH(this.v + l)],
3102
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)]
3103
+ ];
3104
+ }
3105
+ getUvSouth(w, h, l) {
3106
+ return [
3107
+ [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l + h)],
3108
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
3109
+ [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l)],
3110
+ [this.mapW(this.u + l + w + l), this.mapH(this.v + l)]
3111
+ ];
3112
+ }
3113
+ getUvNorth(w, h, l) {
3114
+ return [
3115
+ [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
3116
+ [this.mapW(this.u + l), this.mapH(this.v + l + h)],
3117
+ [this.mapW(this.u + l + w), this.mapH(this.v + l)],
3118
+ [this.mapW(this.u + l), this.mapH(this.v + l)]
3119
+ ];
3120
+ }
3121
+ getFaceUv(face, w, h, l) {
3122
+ switch (face) {
3123
+ case "east" /* EAST */:
3124
+ return this.getUvEast(w, h, l);
3125
+ case "west" /* WEST */:
3126
+ return this.getUvWest(w, h, l);
3127
+ case "up" /* UP */:
3128
+ return this.getUvUp(w, h, l);
3129
+ case "down" /* DOWN */:
3130
+ return this.getUvDown(w, h, l);
3131
+ case "south" /* SOUTH */:
3132
+ return this.getUvSouth(w, h, l);
3133
+ case "north" /* NORTH */:
3134
+ return this.getUvNorth(w, h, l);
3135
+ }
3136
+ }
3137
+ toUvArray(w, h, l, mirror = false) {
3138
+ const uv = [];
3139
+ for (let i = 0; i < CUBE_FACES.length; i++) {
3140
+ const face = mirror && i < 2 ? CUBE_FACES[1 - i] : CUBE_FACES[i];
3141
+ const corners = this.getFaceUv(face, w, h, l);
3142
+ UVMapper.setFaceUvInArray(uv, i * 4, mirror ? [corners[1], corners[0], corners[3], corners[2]] : corners);
3143
+ }
3144
+ return uv;
3145
+ }
3146
+ };
3147
+ }
3148
+ });
3149
+
3015
3150
  // src/error/MineRenderError.ts
3016
3151
  var MineRenderError;
3017
3152
  var init_MineRenderError = __esm({
@@ -3028,6 +3163,248 @@ var init_MineRenderError = __esm({
3028
3163
  }
3029
3164
  });
3030
3165
 
3166
+ // src/assets/Entities.ts
3167
+ var Entities;
3168
+ var init_Entities = __esm({
3169
+ "src/assets/Entities.ts"() {
3170
+ init_AssetKey();
3171
+ init_AssetLoader();
3172
+ init_source();
3173
+ init_Caching();
3174
+ init_Assets();
3175
+ init_MineRenderError();
3176
+ init_ModelTextures();
3177
+ Entities = class {
3178
+ static async getEntityList() {
3179
+ const collect = async (path) => {
3180
+ const prefix2 = path ? `${path}/` : "";
3181
+ const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
3182
+ const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
3183
+ if (!list) return [];
3184
+ const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
3185
+ const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
3186
+ return [...files, ...children.flat()];
3187
+ };
3188
+ return collect("");
3189
+ }
3190
+ static async getModelFile(modelKey) {
3191
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3192
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", isAssetKey(modelKey) ? modelKey.root : void 0);
3193
+ return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3194
+ }
3195
+ static async getLayerList(modelKey) {
3196
+ return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
3197
+ }
3198
+ /** The dataset's extra draws on top of `main`; their `when` labels are the states `EntityModelOptions.when` can enable. */
3199
+ static async getPassList(modelKey) {
3200
+ return (await this.getModelFile(modelKey))?.passes ?? [];
3201
+ }
3202
+ static async getEntity(modelKey, textureKey, options) {
3203
+ const model = await this.getModelFile(modelKey);
3204
+ if (!model) return void 0;
3205
+ const passes = options?.layers || options?.layer !== void 0 ? [] : (model.passes ?? []).filter((pass) => pass.when === void 0 || options?.when?.includes(pass.when));
3206
+ const draws = [...new Set(options?.layers ?? [options?.layer ?? "main"])].map((layer) => ({ layer }));
3207
+ if (draws.length === 0) throw new MineRenderError(`Entity ${model.id} requires at least one layer`);
3208
+ draws.push(...passes);
3209
+ const names = [];
3210
+ const selected = draws.map((pass) => {
3211
+ const layer = model.layers[pass.layer];
3212
+ if (!layer) {
3213
+ throw new MineRenderError(`Entity ${model.id} has no layer "${pass.layer}". Available layers: ${Object.keys(model.layers).join(", ")}`);
3214
+ }
3215
+ let name = pass.layer;
3216
+ for (let n = 2; names.includes(name); n++) name = `${pass.layer}#${n}`;
3217
+ names.push(name);
3218
+ return { name, layer, pass };
3219
+ });
3220
+ const layers = Object.fromEntries(await Promise.all(selected.map(async ({ name, layer, pass }, index) => {
3221
+ const override = options?.textures?.[name] ?? (index === 0 ? textureKey : void 0);
3222
+ const textureLocation = pass.textureLocation ?? layer.textureLocation;
3223
+ let texture;
3224
+ if (override) {
3225
+ const path = isAssetKey(override) ? override.getFullPath() : override.path;
3226
+ texture = isAssetKey(override) && override.assetType === "textures" && path.startsWith("entity/") ? override : new AssetKey(override.namespace, path, "textures", "entity", "assets", ".png", isAssetKey(override) ? override.root : void 0);
3227
+ } else if (textureLocation !== void 0) {
3228
+ texture = AssetKey.parse("textures", textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
3229
+ if (isAssetKey(modelKey)) texture.root = modelKey.root;
3230
+ } else {
3231
+ texture = await this.resolveTexture(modelKey);
3232
+ }
3233
+ const render = pass.render ?? layer.render;
3234
+ return [name, { key: override ?? modelKey, texture, layer, ...render && { render }, ...pass.tint && { tint: pass.tint } }];
3235
+ })));
3236
+ return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
3237
+ }
3238
+ /**
3239
+ * Vanilla's keyframe animations of a model by name, or undefined when the model or the selected version has none.
3240
+ */
3241
+ static async getAnimations(modelKey) {
3242
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3243
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json");
3244
+ const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
3245
+ return file?.animations;
3246
+ }
3247
+ static async resolveTexture(modelKey) {
3248
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
3249
+ const root = isAssetKey(modelKey) ? modelKey.root : void 0;
3250
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json", root);
3251
+ return Caching.entityTextureCache.get(key.serialize(), async () => {
3252
+ const name = path.split("/").pop();
3253
+ for (const candidate of [path, `${path}/${name}`]) {
3254
+ const texture2 = new AssetKey(modelKey.namespace, candidate, "textures", "entity", "assets", ".png", root);
3255
+ if (await ModelTextures.get(texture2)) return texture2;
3256
+ }
3257
+ const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json", root);
3258
+ const list = await AssetLoader.get(listKey, "list" /* LIST */);
3259
+ const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
3260
+ const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
3261
+ if (!file) return void 0;
3262
+ const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json", root);
3263
+ const variant = await AssetLoader.get(variantKey, "json" /* JSON */);
3264
+ const assets = variant?.assets;
3265
+ const textureId = variant?.asset_id ?? (typeof assets?.wild === "string" ? assets.wild : Object.values(assets ?? {}).find((value) => typeof value === "string"));
3266
+ if (typeof textureId !== "string") return void 0;
3267
+ const texture = AssetKey.parse("textures", textureId);
3268
+ texture.root = root;
3269
+ return texture;
3270
+ });
3271
+ }
3272
+ /** @deprecated Use getEntityList(); block entities share the entity dataset. */
3273
+ static getBlockList() {
3274
+ return this.getEntityList();
3275
+ }
3276
+ /** @deprecated Use getEntity(); block entities share the entity dataset. */
3277
+ static getBlock(modelKey, textureKey, options) {
3278
+ return this.getEntity(modelKey, textureKey, options);
3279
+ }
3280
+ };
3281
+ }
3282
+ });
3283
+
3284
+ // src/entity/EntityAnimation.ts
3285
+ function catmullRom(delta, p0, p1, p22, p32) {
3286
+ return 0.5 * (2 * p1 + (p22 - p0) * delta + (2 * p0 - 5 * p1 + 4 * p22 - p32) * delta * delta + (3 * p1 - p0 - 3 * p22 + p32) * delta * delta * delta);
3287
+ }
3288
+ function sampleEntityKeyframes(keyframes, time) {
3289
+ if (keyframes.length === 0) return [0, 0, 0];
3290
+ let low = 0, high = keyframes.length;
3291
+ while (low < high) {
3292
+ const middle = low + high >>> 1;
3293
+ if (time <= keyframes[middle].time) high = middle;
3294
+ else low = middle + 1;
3295
+ }
3296
+ const a = Math.max(0, low - 1);
3297
+ const b = Math.min(keyframes.length - 1, a + 1);
3298
+ const from = keyframes[a];
3299
+ const to = keyframes[b];
3300
+ const delta = a === b ? 0 : Math.min(1, Math.max(0, (time - from.time) / (to.time - from.time)));
3301
+ if (to.interpolation === "catmullrom") {
3302
+ const before = keyframes[Math.max(0, a - 1)].value;
3303
+ const after = keyframes[Math.min(keyframes.length - 1, b + 1)].value;
3304
+ return [0, 1, 2].map((i) => catmullRom(delta, before[i], from.value[i], to.value[i], after[i]));
3305
+ }
3306
+ const target = to.pre ?? to.value;
3307
+ return [0, 1, 2].map((i) => from.value[i] + (target[i] - from.value[i]) * delta);
3308
+ }
3309
+ function entityAnimationTime(animation, time, loop = animation.loop) {
3310
+ return loop && animation.length > 0 ? time % animation.length : time;
3311
+ }
3312
+ function sampleEntityAnimation(animation, time, loop = animation.loop) {
3313
+ const elapsed = entityAnimationTime(animation, time, loop);
3314
+ const pose = {};
3315
+ for (const [name, bone] of Object.entries(animation.bones)) {
3316
+ const sampled = pose[name] = {};
3317
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
3318
+ const keyframes = bone[channel];
3319
+ if (keyframes?.length) sampled[channel] = sampleEntityKeyframes(keyframes, elapsed);
3320
+ }
3321
+ }
3322
+ return pose;
3323
+ }
3324
+ var ENTITY_ANIMATION_CHANNELS;
3325
+ var init_EntityAnimation = __esm({
3326
+ "src/entity/EntityAnimation.ts"() {
3327
+ ENTITY_ANIMATION_CHANNELS = ["position", "rotation", "scale"];
3328
+ }
3329
+ });
3330
+
3331
+ // src/entity/scene/EntityAnimationPlayer.ts
3332
+ var ZERO, EntityAnimationPlayer;
3333
+ var init_EntityAnimationPlayer = __esm({
3334
+ "src/entity/scene/EntityAnimationPlayer.ts"() {
3335
+ init_EntityAnimation();
3336
+ ZERO = [0, 0, 0];
3337
+ EntityAnimationPlayer = class {
3338
+ constructor() {
3339
+ this.parts = [];
3340
+ this._time = 0;
3341
+ this.loop = false;
3342
+ this.speed = 1;
3343
+ }
3344
+ get animation() {
3345
+ return this._animation;
3346
+ }
3347
+ get time() {
3348
+ return this._time;
3349
+ }
3350
+ /**
3351
+ * Starts an animation on the named parts below each layer root. The current pose of a part becomes its default;
3352
+ * bones without a matching part are skipped.
3353
+ */
3354
+ play(layers, animation, options = {}) {
3355
+ this.stop();
3356
+ this._animation = animation;
3357
+ this._time = options.time ?? 0;
3358
+ this.loop = options.loop ?? animation.loop;
3359
+ this.speed = options.speed ?? 1;
3360
+ for (const layer of layers) {
3361
+ for (const bone of Object.keys(animation.bones)) {
3362
+ const object = layer.getObjectByName(`group:${bone}`);
3363
+ if (!object) continue;
3364
+ this.parts.push({ bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone() });
3365
+ }
3366
+ }
3367
+ this.apply();
3368
+ }
3369
+ /** Restores the default poses. Returns whether an animation was active. */
3370
+ stop() {
3371
+ if (!this._animation) return false;
3372
+ for (const part of this.parts) {
3373
+ part.object.position.copy(part.position);
3374
+ part.object.rotation.copy(part.rotation);
3375
+ part.object.scale.copy(part.scale);
3376
+ }
3377
+ this.clear();
3378
+ return true;
3379
+ }
3380
+ /** Forgets the animation without touching the parts, for parts that no longer exist. */
3381
+ clear() {
3382
+ this.parts = [];
3383
+ this._animation = void 0;
3384
+ this._time = 0;
3385
+ }
3386
+ setTime(seconds) {
3387
+ if (!this._animation) return false;
3388
+ this._time = seconds;
3389
+ this.apply();
3390
+ return true;
3391
+ }
3392
+ advance(deltaSeconds) {
3393
+ return this.setTime(this._time + deltaSeconds * this.speed);
3394
+ }
3395
+ apply() {
3396
+ const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
3397
+ for (const part of this.parts) {
3398
+ const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone];
3399
+ part.object.position.set(part.position.x + position[0], part.position.y + position[1], part.position.z + position[2]);
3400
+ part.object.rotation.set(part.rotation.x + rotation[0], part.rotation.y + rotation[1], part.rotation.z + rotation[2], part.rotation.order);
3401
+ part.object.scale.set(part.scale.x + scale[0], part.scale.y + scale[1], part.scale.z + scale[2]);
3402
+ }
3403
+ }
3404
+ };
3405
+ }
3406
+ });
3407
+
3031
3408
  // src/instance/InstanceReference.ts
3032
3409
  function isInstanceReference(obj) {
3033
3410
  return obj.isInstanceReference;
@@ -3524,6 +3901,241 @@ var init_SceneObject = __esm({
3524
3901
  }
3525
3902
  });
3526
3903
 
3904
+ // src/entity/scene/EntityObject.ts
3905
+ import { Color as Color3, Euler as Euler4, Matrix4 as Matrix45, Object3D as Object3D3 } from "three";
3906
+ function isEntityObject(obj) {
3907
+ return obj.isEntityObject;
3908
+ }
3909
+ var import_ts_deepmerge4, _EntityObject, EntityObject;
3910
+ var init_EntityObject = __esm({
3911
+ "src/entity/scene/EntityObject.ts"() {
3912
+ init_SceneObject();
3913
+ init_Caching();
3914
+ import_ts_deepmerge4 = __toESM(require_dist());
3915
+ init_Ticker();
3916
+ init_model();
3917
+ init_ModelTextures();
3918
+ init_AssetKey();
3919
+ init_Materials();
3920
+ init_MinecraftCubeTexture();
3921
+ init_three();
3922
+ init_EntityAnimationPlayer();
3923
+ _EntityObject = class _EntityObject extends SceneObject {
3924
+ constructor(entity, options) {
3925
+ super();
3926
+ this.entity = entity;
3927
+ this.isEntityObject = true;
3928
+ this.meshesCreated = false;
3929
+ this.geometries = /* @__PURE__ */ new Set();
3930
+ this.disposeWireframes = [];
3931
+ /** Scrolling materials are owned: their texture offset follows this entity's age. */
3932
+ this.scrollMaterials = [];
3933
+ /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
3934
+ this.age = 0;
3935
+ this.animationPlayer = new EntityAnimationPlayer();
3936
+ this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
3937
+ this.addEventListener("added", () => this.updateScrollSubscription());
3938
+ this.addEventListener("removed", () => this.updateScrollSubscription());
3939
+ }
3940
+ async init() {
3941
+ this.createMeshes();
3942
+ await this.applyTextures();
3943
+ }
3944
+ dispose() {
3945
+ super.dispose();
3946
+ }
3947
+ disposeAndRemoveAllChildren() {
3948
+ this.clearScrollMaterials();
3949
+ for (const geometry of this.geometries) geometry.dispose();
3950
+ this.geometries.clear();
3951
+ for (const dispose of this.disposeWireframes.splice(0)) dispose();
3952
+ this.animationPlayer.clear();
3953
+ super.disposeAndRemoveAllChildren();
3954
+ }
3955
+ clearScrollMaterials() {
3956
+ Ticker.remove(this.scrollTicker);
3957
+ this.scrollTicker = void 0;
3958
+ for (const { material } of this.scrollMaterials.splice(0)) {
3959
+ material.map?.dispose();
3960
+ material.dispose();
3961
+ }
3962
+ }
3963
+ updateScrollSubscription() {
3964
+ if (this.parent && this.scrollMaterials.length) {
3965
+ if (this.scrollTicker === void 0) {
3966
+ this.scrollTicker = Ticker.add(() => {
3967
+ this.age++;
3968
+ this.updateScroll();
3969
+ });
3970
+ }
3971
+ } else {
3972
+ Ticker.remove(this.scrollTicker);
3973
+ this.scrollTicker = void 0;
3974
+ }
3975
+ }
3976
+ /** Applies the entity age to the scrolling materials; the ticker calls this 20 times per second. */
3977
+ updateScroll() {
3978
+ for (const { offset, speed } of this.scrollMaterials) {
3979
+ offset.set(this.age * speed[0] % 1, 0 - this.age * speed[1] % 1);
3980
+ }
3981
+ this.notifyDirty();
3982
+ }
3983
+ //<editor-fold desc="ANIMATION">
3984
+ /** The animation started by {@link playAnimation}, until {@link stopAnimation} or disposal. */
3985
+ get animation() {
3986
+ return this.animationPlayer.animation;
3987
+ }
3988
+ /** Seconds since the animation started, before looping. */
3989
+ get animationTime() {
3990
+ return this.animationPlayer.time;
3991
+ }
3992
+ /**
3993
+ * Poses the object from a keyframe animation of {@link Entities.getAnimations}, replacing the current one.
3994
+ * Parts of every layer are matched by name and their current pose is the default the offsets add to.
3995
+ * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.
3996
+ */
3997
+ playAnimation(animation, options) {
3998
+ this.createMeshes();
3999
+ const layers = Object.keys(this.entityLayers).map((name) => this.getLayerGroup(name)).filter((layer) => !!layer);
4000
+ this.animationPlayer.play(layers, animation, options);
4001
+ this.notifyDirty();
4002
+ }
4003
+ /** Stops the animation and restores the default pose. */
4004
+ stopAnimation() {
4005
+ if (this.animationPlayer.stop()) this.notifyDirty();
4006
+ }
4007
+ /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
4008
+ setAnimationTime(seconds) {
4009
+ if (this.animationPlayer.setTime(seconds)) this.notifyDirty();
4010
+ }
4011
+ /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
4012
+ advanceAnimation(deltaSeconds) {
4013
+ if (this.animationPlayer.advance(deltaSeconds)) this.notifyDirty();
4014
+ }
4015
+ //</editor-fold>
4016
+ getLayerGroup(name) {
4017
+ return super.getGroupByName(`layer:${name}`);
4018
+ }
4019
+ getGroupByName(name, layerName) {
4020
+ return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
4021
+ }
4022
+ getMeshByName(name, layerName) {
4023
+ return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
4024
+ }
4025
+ get entityLayers() {
4026
+ return this.entity.layers ?? { main: this.entity };
4027
+ }
4028
+ getTextureKey(layer) {
4029
+ if (layer.texture) return layer.texture;
4030
+ const key = layer.key;
4031
+ return new AssetKey(
4032
+ key.namespace,
4033
+ isAssetKey(key) ? key.getFullPath() : key.path,
4034
+ "textures",
4035
+ "entity",
4036
+ "assets",
4037
+ ".png"
4038
+ );
4039
+ }
4040
+ async loadTextures(layer = this.entity) {
4041
+ return ModelTextures.get(this.getTextureKey(layer));
4042
+ }
4043
+ createMeshes(force = false) {
4044
+ if (this.meshesCreated && !force) return;
4045
+ const modelRoot = new Object3D3();
4046
+ const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
4047
+ if (transform) {
4048
+ for (const op of transform) {
4049
+ const matrix = new Matrix45();
4050
+ if ("scale" in op) matrix.makeScale(...op.scale);
4051
+ else if ("translate" in op) matrix.makeTranslation(...op.translate);
4052
+ else matrix.makeRotationFromEuler(new Euler4(...op.rotate, "ZYX"));
4053
+ modelRoot.matrix.multiply(matrix);
4054
+ }
4055
+ modelRoot.matrixAutoUpdate = false;
4056
+ } else if (this.options.flip ?? true) {
4057
+ modelRoot.scale.set(-1, -1, 1);
4058
+ }
4059
+ this.add(modelRoot);
4060
+ Object.entries(this.entityLayers).forEach(([name, layer], index) => {
4061
+ const group = this.createGroup(`layer:${name}`);
4062
+ modelRoot.add(group);
4063
+ const inward = !Materials.entityModeCulls(layer.render ?? layer.layer.render);
4064
+ this.createPart("root", layer.layer.root, group, layer.layer.texture, Materials.MISSING_TEXTURE, index, inward);
4065
+ });
4066
+ this.meshesCreated = true;
4067
+ }
4068
+ createPart(name, part, parent, textureSize, material, renderOrder, inward = true) {
4069
+ const anchor = this.createGroup(name);
4070
+ anchor.position.fromArray(part.pose.offset);
4071
+ anchor.rotation.set(...part.pose.rotation, "ZYX");
4072
+ anchor.scale.fromArray(part.pose.scale ?? [1, 1, 1]);
4073
+ parent.add(anchor);
4074
+ const size = part.texture ?? textureSize;
4075
+ for (const cube of part.cubes) {
4076
+ const [width, height, depth] = cube.size;
4077
+ const [growX, growY, growZ] = cube.grow ?? [0, 0, 0];
4078
+ const texture = new MinecraftCubeTexture(...cube.uv, ...size);
4079
+ const uv = size[0] === 0 || size[1] === 0 ? new Array(48).fill(0) : texture.toUvArray(width, height, depth, cube.mirror);
4080
+ const geometry = this._getBoxGeometryForDimensionsAndUv(
4081
+ width + growX * 2,
4082
+ height + growY * 2,
4083
+ depth + growZ * 2,
4084
+ uv
4085
+ ).clone();
4086
+ this.geometries.add(geometry);
4087
+ geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);
4088
+ if (inward && Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {
4089
+ const index = Array.from(geometry.getIndex().array);
4090
+ geometry.setIndex(index.concat(index.slice().reverse()));
4091
+ }
4092
+ const mesh = this.createMesh(name, geometry, material);
4093
+ mesh.renderOrder = renderOrder;
4094
+ anchor.add(mesh);
4095
+ if (this.options.wireframe) this.disposeWireframes.push(addWireframeToMesh(geometry, mesh));
4096
+ }
4097
+ for (const [childName, child] of Object.entries(part.children)) {
4098
+ this.createPart(childName, child, anchor, size, material, renderOrder, inward);
4099
+ }
4100
+ }
4101
+ async applyTextures() {
4102
+ this.clearScrollMaterials();
4103
+ await Promise.all(Object.entries(this.entityLayers).map(async ([name, layer]) => {
4104
+ const mode = layer.render ?? layer.layer.render ?? "cutout";
4105
+ const tint = layer.tint === void 0 ? void 0 : this.options.tints?.[layer.tint];
4106
+ const scroll = Materials.entityModeScroll(mode);
4107
+ const assetKeyStr = this.getTextureKey(layer).serialize();
4108
+ const keyStr = `entity:${mode}:${tint === void 0 ? "" : new Color3(tint).getHexString()}:${assetKeyStr}`;
4109
+ let mat = scroll ? void 0 : Caching.materialCache.getIfPresent(keyStr);
4110
+ if (!mat) {
4111
+ const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
4112
+ const imageData = await this.loadTextures(layer);
4113
+ if (!imageData) return;
4114
+ const canvas = imageData.data.canvas;
4115
+ const createMaterial = () => Materials.createEntityCanvasMaterial(canvas, mode, tint);
4116
+ mat = !scroll && cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
4117
+ if (scroll) {
4118
+ this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
4119
+ }
4120
+ }
4121
+ this.getLayerGroup(name)?.traverse((object) => {
4122
+ if (isMesh(object)) object.material = mat;
4123
+ });
4124
+ }));
4125
+ if (!this.children.length) {
4126
+ this.clearScrollMaterials();
4127
+ } else if (this.scrollMaterials.length) {
4128
+ this.updateScroll();
4129
+ }
4130
+ this.updateScrollSubscription();
4131
+ this.notifyDirty();
4132
+ }
4133
+ };
4134
+ _EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS);
4135
+ EntityObject = _EntityObject;
4136
+ }
4137
+ });
4138
+
3527
4139
  // src/three/BufferGeometryUtils.js
3528
4140
  import {
3529
4141
  BufferAttribute as BufferAttribute4,
@@ -4112,8 +4724,121 @@ var init_ModelCulling = __esm({
4112
4724
  }
4113
4725
  });
4114
4726
 
4727
+ // src/model/SpecialItems.ts
4728
+ import { Matrix4 as Matrix48 } from "three";
4729
+ var SpecialItems;
4730
+ var init_SpecialItems = __esm({
4731
+ "src/model/SpecialItems.ts"() {
4732
+ init_AssetKey();
4733
+ init_Entities();
4734
+ SpecialItems = class {
4735
+ /** Resolves the static entity parts drawn by a special item renderer, in model units. */
4736
+ static async getParts(special, root) {
4737
+ const texture = (id, directory) => {
4738
+ const key = AssetKey.parse("textures", id);
4739
+ return new AssetKey(
4740
+ key.namespace,
4741
+ [directory, key.getFullPath()].filter(Boolean).join("/"),
4742
+ "textures",
4743
+ "entity",
4744
+ "assets",
4745
+ ".png",
4746
+ root
4747
+ );
4748
+ };
4749
+ const load = async (id, textureKey, transform, rotations = {}) => {
4750
+ const key = new AssetKey("minecraft", id, void 0, void 0, "entity-models", ".json", root);
4751
+ const model = await Entities.getEntity(key, textureKey, { layer: "main" });
4752
+ if (!model) throw new Error(`Missing entity model minecraft:${id} for special item ${special.type}`);
4753
+ return { model, transform, rotations };
4754
+ };
4755
+ switch (special.type) {
4756
+ case "minecraft:chest":
4757
+ case "chest": {
4758
+ const angle = -(special.openness ?? 0) * Math.PI / 2;
4759
+ const chest = await load("chest", texture(special.texture, "chest"), new Matrix48(), {
4760
+ lid: [angle, 0, 0],
4761
+ lock: [angle, 0, 0]
4762
+ });
4763
+ chest.model = { ...chest.model, render: "solid", layers: void 0 };
4764
+ return [chest];
4765
+ }
4766
+ case "minecraft:bed":
4767
+ case "bed": {
4768
+ const transform = new Matrix48().makeTranslation(0, 9, 0).multiply(new Matrix48().makeRotationX(Math.PI / 2)).multiply(new Matrix48().makeTranslation(8, 8, 8)).multiply(new Matrix48().makeRotationZ(Math.PI)).multiply(new Matrix48().makeTranslation(-8, -8, -8));
4769
+ const key = texture(special.texture, "bed");
4770
+ return Promise.all([
4771
+ load("bed_head", key, transform),
4772
+ load("bed_foot", key, new Matrix48().makeTranslation(0, 0, -16).multiply(transform))
4773
+ ]);
4774
+ }
4775
+ case "minecraft:head":
4776
+ case "head": {
4777
+ const models = {
4778
+ skeleton: "skeleton_skull",
4779
+ wither_skeleton: "wither_skeleton_skull",
4780
+ zombie: "zombie_head",
4781
+ creeper: "creeper_head",
4782
+ dragon: "dragon_skull",
4783
+ piglin: "piglin_head"
4784
+ };
4785
+ const id = Object.prototype.hasOwnProperty.call(models, special.kind) ? models[special.kind] : void 0;
4786
+ if (!id) throw new Error(`Unsupported special item head kind ${special.kind}`);
4787
+ const animation = (special.animation ?? 0) * Math.PI * 0.2;
4788
+ const rotations = { head: [0, Math.PI, 0] };
4789
+ if (special.kind === "dragon") rotations.jaw = [(Math.sin(animation) + 1) * 0.2, 0, 0];
4790
+ if (special.kind === "piglin") {
4791
+ rotations.left_ear = [0, 0, -(Math.cos(animation * 1.2) + 2.5) * 0.2];
4792
+ rotations.right_ear = [0, 0, (Math.cos(animation) + 2.5) * 0.2];
4793
+ }
4794
+ return [await load(
4795
+ id,
4796
+ special.texture ? texture(special.texture, "") : void 0,
4797
+ new Matrix48().makeTranslation(8, 0, 8).multiply(new Matrix48().makeScale(-1, -1, 1)),
4798
+ rotations
4799
+ )];
4800
+ }
4801
+ }
4802
+ }
4803
+ /** An owned material with model-preview shading; its entity texture remains shared. */
4804
+ static createMaterial(source, shade) {
4805
+ const material = source.clone();
4806
+ if (shade) {
4807
+ material.onBeforeCompile = (shader) => {
4808
+ shader.vertexShader = `varying float itemLight;
4809
+ ${shader.vertexShader}`.replace("#include <begin_vertex>", `
4810
+ #include <begin_vertex>
4811
+ vec3 N = inverseTransformDirection(normalMatrix * normal, viewMatrix);
4812
+ itemLight = (1.0 + N.y) * 0.25 - N.x * N.x * 0.15 + N.z * N.z * 0.05 + 0.5;
4813
+ `);
4814
+ shader.fragmentShader = `varying float itemLight;
4815
+ ${shader.fragmentShader}`.replace(
4816
+ "#include <color_fragment>",
4817
+ "#include <color_fragment>\ndiffuseColor.rgb *= itemLight;"
4818
+ );
4819
+ };
4820
+ material.customProgramCacheKey = () => "special-item-side";
4821
+ }
4822
+ return material;
4823
+ }
4824
+ };
4825
+ }
4826
+ });
4827
+
4828
+ // src/model/GuiLight.ts
4829
+ var GuiLight;
4830
+ var init_GuiLight = __esm({
4831
+ "src/model/GuiLight.ts"() {
4832
+ GuiLight = /* @__PURE__ */ ((GuiLight2) => {
4833
+ GuiLight2["FRONT"] = "front";
4834
+ GuiLight2["SIDE"] = "side";
4835
+ return GuiLight2;
4836
+ })(GuiLight || {});
4837
+ }
4838
+ });
4839
+
4115
4840
  // src/model/scene/ModelObject.ts
4116
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix48 } from "three";
4841
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix49 } from "three";
4117
4842
  function isModelObject(obj) {
4118
4843
  return obj.isModelObject;
4119
4844
  }
@@ -4128,21 +4853,60 @@ var init_ModelObject = __esm({
4128
4853
  import_ts_deepmerge5 = __toESM(require_dist());
4129
4854
  init_log();
4130
4855
  init_CubeFace();
4856
+ init_DisplayPosition();
4131
4857
  init_DisplayTransforms();
4132
4858
  init_ModelCulling();
4859
+ init_SpecialItems();
4860
+ init_EntityObject();
4861
+ init_GuiLight();
4133
4862
  p7 = prefix("ModelObject");
4134
4863
  _ModelObject = class _ModelObject extends SceneObject {
4135
4864
  constructor(originalModel, options) {
4136
4865
  super(options);
4137
4866
  this.originalModel = originalModel;
4138
4867
  this.isModelObject = true;
4868
+ this.specialMaterials = /* @__PURE__ */ new Map();
4139
4869
  this.meshesCreated = false;
4140
4870
  this.options = (0, import_ts_deepmerge5.default)({}, _ModelObject.DEFAULT_OPTIONS, options ?? {});
4871
+ if (originalModel.special) this.options.instanceMeshes = false;
4141
4872
  if (this.options.tints) this.options.tints = { ...this.options.tints };
4142
4873
  this.addEventListener("added", () => this.updateAnimationSubscription());
4143
4874
  this.addEventListener("removed", () => this.updateAnimationSubscription());
4144
4875
  }
4145
4876
  async init() {
4877
+ const special = this.originalModel.special;
4878
+ if (special) {
4879
+ const parts = await SpecialItems.getParts(special, this.originalModel.key?.root);
4880
+ const transform = this.options.displayPosition ? DisplayTransforms.getMatrix(this.originalModel.display, this.options.displayPosition) : new Matrix49();
4881
+ transform.multiply(new Matrix49().makeTranslation(-8, -8, -8));
4882
+ try {
4883
+ for (const part of parts) {
4884
+ const object = new EntityObject(part.model, { flip: false, wireframe: this.options.wireframe });
4885
+ object.matrix.copy(transform).multiply(part.transform);
4886
+ object.matrixAutoUpdate = false;
4887
+ this.add(object);
4888
+ await object.init();
4889
+ object.iterateAllMeshes((mesh) => {
4890
+ const source = mesh.material;
4891
+ let material = this.specialMaterials.get(source);
4892
+ if (!material) {
4893
+ const front = this.options.displayPosition === "gui" /* GUI */ && this.originalModel.gui_light === "front" /* FRONT */;
4894
+ material = SpecialItems.createMaterial(source, !front);
4895
+ this.specialMaterials.set(source, material);
4896
+ }
4897
+ mesh.material = material;
4898
+ });
4899
+ for (const [name, rotation] of Object.entries(part.rotations)) {
4900
+ object.getGroupByName(name)?.rotation.set(...rotation, "ZYX");
4901
+ }
4902
+ }
4903
+ } catch (error) {
4904
+ this.disposeAndRemoveAllChildren();
4905
+ throw error;
4906
+ }
4907
+ this.notifyDirty();
4908
+ return;
4909
+ }
4146
4910
  await this.loadTextures();
4147
4911
  this.createMeshes();
4148
4912
  this.applyTextures();
@@ -4192,12 +4956,12 @@ var init_ModelObject = __esm({
4192
4956
  }
4193
4957
  elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
4194
4958
  }
4195
- elGeo.applyMatrix4(new Matrix48().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4196
- elGeo.applyMatrix4(new Matrix48().makeTranslation(el.from[0], el.from[1], el.from[2]));
4959
+ elGeo.applyMatrix4(new Matrix49().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4960
+ elGeo.applyMatrix4(new Matrix49().makeTranslation(el.from[0], el.from[1], el.from[2]));
4197
4961
  if (el.rotation) {
4198
4962
  applyElementRotation(el.rotation, elGeo);
4199
4963
  }
4200
- elGeo.applyMatrix4(new Matrix48().makeTranslation(-8, -8, -8));
4964
+ elGeo.applyMatrix4(new Matrix49().makeTranslation(-8, -8, -8));
4201
4965
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4202
4966
  if (this.options.mergeMeshes) {
4203
4967
  allGeos.push(elGeo);
@@ -4287,6 +5051,8 @@ var init_ModelObject = __esm({
4287
5051
  this.atlasMaterial?.dispose();
4288
5052
  this.atlasTexture = void 0;
4289
5053
  this.atlasMaterial = void 0;
5054
+ for (const material of this.specialMaterials.values()) material.dispose();
5055
+ this.specialMaterials.clear();
4290
5056
  super.disposeAndRemoveAllChildren();
4291
5057
  }
4292
5058
  };
@@ -4677,96 +5443,8 @@ var Meshes = class _Meshes {
4677
5443
  }
4678
5444
  };
4679
5445
 
4680
- // src/MinecraftCubeTexture.ts
4681
- init_UVMapper();
4682
- init_CubeFace();
4683
- var MinecraftCubeTexture = class {
4684
- constructor(u, v, width, height) {
4685
- this.u = u;
4686
- this.v = v;
4687
- this.width = width;
4688
- this.height = height;
4689
- }
4690
- mapW(x) {
4691
- return x / this.width;
4692
- }
4693
- mapH(x) {
4694
- return 1 - x / this.height;
4695
- }
4696
- getUvEast(w, h, l) {
4697
- return [
4698
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
4699
- [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
4700
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l)],
4701
- [this.mapW(this.u + l + w), this.mapH(this.v + l)]
4702
- ];
4703
- }
4704
- getUvWest(w, h, l) {
4705
- return [
4706
- [this.mapW(this.u + l), this.mapH(this.v + l + h)],
4707
- [this.mapW(this.u), this.mapH(this.v + l + h)],
4708
- [this.mapW(this.u + l), this.mapH(this.v + l)],
4709
- [this.mapW(this.u), this.mapH(this.v + l)]
4710
- ];
4711
- }
4712
- getUvUp(w, h, l) {
4713
- return [
4714
- [this.mapW(this.u + l + w), this.mapH(this.v + l)],
4715
- [this.mapW(this.u + l + w + w), this.mapH(this.v + l)],
4716
- [this.mapW(this.u + l + w), this.mapH(this.v)],
4717
- [this.mapW(this.u + l + w + w), this.mapH(this.v)]
4718
- ];
4719
- }
4720
- getUvDown(w, h, l) {
4721
- return [
4722
- [this.mapW(this.u + l), this.mapH(this.v)],
4723
- [this.mapW(this.u + l + w), this.mapH(this.v)],
4724
- [this.mapW(this.u + l), this.mapH(this.v + l)],
4725
- [this.mapW(this.u + l + w), this.mapH(this.v + l)]
4726
- ];
4727
- }
4728
- getUvSouth(w, h, l) {
4729
- return [
4730
- [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l + h)],
4731
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l + h)],
4732
- [this.mapW(this.u + l + w + l + w), this.mapH(this.v + l)],
4733
- [this.mapW(this.u + l + w + l), this.mapH(this.v + l)]
4734
- ];
4735
- }
4736
- getUvNorth(w, h, l) {
4737
- return [
4738
- [this.mapW(this.u + l + w), this.mapH(this.v + l + h)],
4739
- [this.mapW(this.u + l), this.mapH(this.v + l + h)],
4740
- [this.mapW(this.u + l + w), this.mapH(this.v + l)],
4741
- [this.mapW(this.u + l), this.mapH(this.v + l)]
4742
- ];
4743
- }
4744
- getFaceUv(face, w, h, l) {
4745
- switch (face) {
4746
- case "east" /* EAST */:
4747
- return this.getUvEast(w, h, l);
4748
- case "west" /* WEST */:
4749
- return this.getUvWest(w, h, l);
4750
- case "up" /* UP */:
4751
- return this.getUvUp(w, h, l);
4752
- case "down" /* DOWN */:
4753
- return this.getUvDown(w, h, l);
4754
- case "south" /* SOUTH */:
4755
- return this.getUvSouth(w, h, l);
4756
- case "north" /* NORTH */:
4757
- return this.getUvNorth(w, h, l);
4758
- }
4759
- }
4760
- toUvArray(w, h, l, mirror = false) {
4761
- const uv = [];
4762
- for (let i = 0; i < CUBE_FACES.length; i++) {
4763
- const face = mirror && i < 2 ? CUBE_FACES[1 - i] : CUBE_FACES[i];
4764
- const corners = this.getFaceUv(face, w, h, l);
4765
- UVMapper.setFaceUvInArray(uv, i * 4, mirror ? [corners[1], corners[0], corners[3], corners[2]] : corners);
4766
- }
4767
- return uv;
4768
- }
4769
- };
5446
+ // src/index.ts
5447
+ init_MinecraftCubeTexture();
4770
5448
 
4771
5449
  // src/SceneStats.ts
4772
5450
  var SceneStats = class {
@@ -4860,116 +5538,7 @@ var BlockEntities = class {
4860
5538
 
4861
5539
  // src/assets/index.ts
4862
5540
  init_BlockStates();
4863
-
4864
- // src/assets/Entities.ts
4865
- init_AssetKey();
4866
- init_AssetLoader();
4867
- init_source();
4868
- init_Caching();
4869
- init_Assets();
4870
- init_MineRenderError();
4871
- init_ModelTextures();
4872
- var Entities = class {
4873
- static async getEntityList() {
4874
- const collect = async (path) => {
4875
- const prefix2 = path ? `${path}/` : "";
4876
- const key = new AssetKey(DEFAULT_NAMESPACE, `${prefix2}_list`, void 0, void 0, "entity-models", ".json");
4877
- const list = await Caching.listAssetCache.get(key.serialize(), () => AssetLoader.get(key, "list" /* LIST */));
4878
- if (!list) return [];
4879
- const files = list.files.filter((file) => file.endsWith(".json") && file !== "_list.json").map((file) => `${prefix2}${file.slice(0, -5)}`);
4880
- const children = await Promise.all(list.directories.map((directory) => collect(`${prefix2}${directory}`)));
4881
- return [...files, ...children.flat()];
4882
- };
4883
- return collect("");
4884
- }
4885
- static async getModelFile(modelKey) {
4886
- const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4887
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
4888
- return Caching.entityModelCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4889
- }
4890
- static async getLayerList(modelKey) {
4891
- return Object.keys((await this.getModelFile(modelKey))?.layers ?? {});
4892
- }
4893
- /** The dataset's extra draws on top of `main`; their `when` labels are the states `EntityModelOptions.when` can enable. */
4894
- static async getPassList(modelKey) {
4895
- return (await this.getModelFile(modelKey))?.passes ?? [];
4896
- }
4897
- static async getEntity(modelKey, textureKey, options) {
4898
- const model = await this.getModelFile(modelKey);
4899
- if (!model) return void 0;
4900
- const passes = options?.layers || options?.layer !== void 0 ? [] : (model.passes ?? []).filter((pass) => pass.when === void 0 || options?.when?.includes(pass.when));
4901
- const draws = [...new Set(options?.layers ?? [options?.layer ?? "main"])].map((layer) => ({ layer }));
4902
- if (draws.length === 0) throw new MineRenderError(`Entity ${model.id} requires at least one layer`);
4903
- draws.push(...passes);
4904
- const names = [];
4905
- const selected = draws.map((pass) => {
4906
- const layer = model.layers[pass.layer];
4907
- if (!layer) {
4908
- throw new MineRenderError(`Entity ${model.id} has no layer "${pass.layer}". Available layers: ${Object.keys(model.layers).join(", ")}`);
4909
- }
4910
- let name = pass.layer;
4911
- for (let n = 2; names.includes(name); n++) name = `${pass.layer}#${n}`;
4912
- names.push(name);
4913
- return { name, layer, pass };
4914
- });
4915
- const layers = Object.fromEntries(await Promise.all(selected.map(async ({ name, layer, pass }, index) => {
4916
- const override = options?.textures?.[name] ?? (index === 0 ? textureKey : void 0);
4917
- const textureLocation = pass.textureLocation ?? layer.textureLocation;
4918
- let texture;
4919
- if (override) {
4920
- const path = isAssetKey(override) ? override.getFullPath() : override.path;
4921
- texture = isAssetKey(override) && override.assetType === "textures" && path.startsWith("entity/") ? override : new AssetKey(override.namespace, path, "textures", "entity", "assets", ".png", isAssetKey(override) ? override.root : void 0);
4922
- } else if (textureLocation !== void 0) {
4923
- texture = AssetKey.parse("textures", textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
4924
- } else {
4925
- texture = await this.resolveTexture(modelKey);
4926
- }
4927
- const render = pass.render ?? layer.render;
4928
- return [name, { key: override ?? modelKey, texture, layer, ...render && { render }, ...pass.tint && { tint: pass.tint } }];
4929
- })));
4930
- return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
4931
- }
4932
- /**
4933
- * Vanilla's keyframe animations of a model by name, or undefined when the model or the selected version has none.
4934
- */
4935
- static async getAnimations(modelKey) {
4936
- const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4937
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json");
4938
- const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4939
- return file?.animations;
4940
- }
4941
- static async resolveTexture(modelKey) {
4942
- const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4943
- const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
4944
- return Caching.entityTextureCache.get(key.serialize(), async () => {
4945
- const name = path.split("/").pop();
4946
- for (const candidate of [path, `${path}/${name}`]) {
4947
- const texture = new AssetKey(modelKey.namespace, candidate, "textures", "entity", "assets", ".png");
4948
- if (await ModelTextures.get(texture)) return texture;
4949
- }
4950
- const listKey = new AssetKey(modelKey.namespace, "_list", `${name}_variant`, void 0, "data", ".json");
4951
- const list = await AssetLoader.get(listKey, "list" /* LIST */);
4952
- const files = list?.files.filter((file2) => file2.endsWith(".json") && file2 !== "_list.json");
4953
- const file = files?.find((file2) => file2 === "temperate.json") ?? files?.[0];
4954
- if (!file) return void 0;
4955
- const variantKey = new AssetKey(modelKey.namespace, file.slice(0, -5), `${name}_variant`, void 0, "data", ".json");
4956
- const variant = await AssetLoader.get(variantKey, "json" /* JSON */);
4957
- const assets = variant?.assets;
4958
- const textureId = variant?.asset_id ?? (typeof assets?.wild === "string" ? assets.wild : Object.values(assets ?? {}).find((value) => typeof value === "string"));
4959
- return typeof textureId === "string" ? AssetKey.parse("textures", textureId) : void 0;
4960
- });
4961
- }
4962
- /** @deprecated Use getEntityList(); block entities share the entity dataset. */
4963
- static getBlockList() {
4964
- return this.getEntityList();
4965
- }
4966
- /** @deprecated Use getEntity(); block entities share the entity dataset. */
4967
- static getBlock(modelKey, textureKey, options) {
4968
- return this.getEntity(modelKey, textureKey, options);
4969
- }
4970
- };
4971
-
4972
- // src/assets/index.ts
5541
+ init_Entities();
4973
5542
  init_ModelTextures();
4974
5543
  init_Models();
4975
5544
  init_source();
@@ -5014,346 +5583,12 @@ function trimCanvas(source) {
5014
5583
  return canvas;
5015
5584
  }
5016
5585
 
5017
- // src/entity/EntityAnimation.ts
5018
- var ENTITY_ANIMATION_CHANNELS = ["position", "rotation", "scale"];
5019
- function catmullRom(delta, p0, p1, p22, p32) {
5020
- return 0.5 * (2 * p1 + (p22 - p0) * delta + (2 * p0 - 5 * p1 + 4 * p22 - p32) * delta * delta + (3 * p1 - p0 - 3 * p22 + p32) * delta * delta * delta);
5021
- }
5022
- function sampleEntityKeyframes(keyframes, time) {
5023
- if (keyframes.length === 0) return [0, 0, 0];
5024
- let low = 0, high = keyframes.length;
5025
- while (low < high) {
5026
- const middle = low + high >>> 1;
5027
- if (time <= keyframes[middle].time) high = middle;
5028
- else low = middle + 1;
5029
- }
5030
- const a = Math.max(0, low - 1);
5031
- const b = Math.min(keyframes.length - 1, a + 1);
5032
- const from = keyframes[a];
5033
- const to = keyframes[b];
5034
- const delta = a === b ? 0 : Math.min(1, Math.max(0, (time - from.time) / (to.time - from.time)));
5035
- if (to.interpolation === "catmullrom") {
5036
- const before = keyframes[Math.max(0, a - 1)].value;
5037
- const after = keyframes[Math.min(keyframes.length - 1, b + 1)].value;
5038
- return [0, 1, 2].map((i) => catmullRom(delta, before[i], from.value[i], to.value[i], after[i]));
5039
- }
5040
- const target = to.pre ?? to.value;
5041
- return [0, 1, 2].map((i) => from.value[i] + (target[i] - from.value[i]) * delta);
5042
- }
5043
- function entityAnimationTime(animation, time, loop = animation.loop) {
5044
- return loop && animation.length > 0 ? time % animation.length : time;
5045
- }
5046
- function sampleEntityAnimation(animation, time, loop = animation.loop) {
5047
- const elapsed = entityAnimationTime(animation, time, loop);
5048
- const pose = {};
5049
- for (const [name, bone] of Object.entries(animation.bones)) {
5050
- const sampled = pose[name] = {};
5051
- for (const channel of ENTITY_ANIMATION_CHANNELS) {
5052
- const keyframes = bone[channel];
5053
- if (keyframes?.length) sampled[channel] = sampleEntityKeyframes(keyframes, elapsed);
5054
- }
5055
- }
5056
- return pose;
5057
- }
5058
-
5059
- // src/entity/scene/EntityAnimationPlayer.ts
5060
- var ZERO = [0, 0, 0];
5061
- var EntityAnimationPlayer = class {
5062
- constructor() {
5063
- this.parts = [];
5064
- this._time = 0;
5065
- this.loop = false;
5066
- this.speed = 1;
5067
- }
5068
- get animation() {
5069
- return this._animation;
5070
- }
5071
- get time() {
5072
- return this._time;
5073
- }
5074
- /**
5075
- * Starts an animation on the named parts below each layer root. The current pose of a part becomes its default;
5076
- * bones without a matching part are skipped.
5077
- */
5078
- play(layers, animation, options = {}) {
5079
- this.stop();
5080
- this._animation = animation;
5081
- this._time = options.time ?? 0;
5082
- this.loop = options.loop ?? animation.loop;
5083
- this.speed = options.speed ?? 1;
5084
- for (const layer of layers) {
5085
- for (const bone of Object.keys(animation.bones)) {
5086
- const object = layer.getObjectByName(`group:${bone}`);
5087
- if (!object) continue;
5088
- this.parts.push({ bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone() });
5089
- }
5090
- }
5091
- this.apply();
5092
- }
5093
- /** Restores the default poses. Returns whether an animation was active. */
5094
- stop() {
5095
- if (!this._animation) return false;
5096
- for (const part of this.parts) {
5097
- part.object.position.copy(part.position);
5098
- part.object.rotation.copy(part.rotation);
5099
- part.object.scale.copy(part.scale);
5100
- }
5101
- this.clear();
5102
- return true;
5103
- }
5104
- /** Forgets the animation without touching the parts, for parts that no longer exist. */
5105
- clear() {
5106
- this.parts = [];
5107
- this._animation = void 0;
5108
- this._time = 0;
5109
- }
5110
- setTime(seconds) {
5111
- if (!this._animation) return false;
5112
- this._time = seconds;
5113
- this.apply();
5114
- return true;
5115
- }
5116
- advance(deltaSeconds) {
5117
- return this.setTime(this._time + deltaSeconds * this.speed);
5118
- }
5119
- apply() {
5120
- const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
5121
- for (const part of this.parts) {
5122
- const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone];
5123
- part.object.position.set(part.position.x + position[0], part.position.y + position[1], part.position.z + position[2]);
5124
- part.object.rotation.set(part.rotation.x + rotation[0], part.rotation.y + rotation[1], part.rotation.z + rotation[2], part.rotation.order);
5125
- part.object.scale.set(part.scale.x + scale[0], part.scale.y + scale[1], part.scale.z + scale[2]);
5126
- }
5127
- }
5128
- };
5586
+ // src/entity/index.ts
5587
+ init_EntityAnimation();
5129
5588
 
5130
- // src/entity/scene/EntityObject.ts
5131
- init_SceneObject();
5132
- init_Caching();
5133
- var import_ts_deepmerge4 = __toESM(require_dist());
5134
- init_Ticker();
5135
- init_model();
5136
- init_ModelTextures();
5137
- init_AssetKey();
5138
- init_Materials();
5139
- import { Color as Color3, Euler as Euler4, Matrix4 as Matrix45, Object3D as Object3D3 } from "three";
5140
- init_three();
5141
- var _EntityObject = class _EntityObject extends SceneObject {
5142
- constructor(entity, options) {
5143
- super();
5144
- this.entity = entity;
5145
- this.isEntityObject = true;
5146
- this.meshesCreated = false;
5147
- this.geometries = /* @__PURE__ */ new Set();
5148
- this.disposeWireframes = [];
5149
- /** Scrolling materials are owned: their texture offset follows this entity's age. */
5150
- this.scrollMaterials = [];
5151
- /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
5152
- this.age = 0;
5153
- this.animationPlayer = new EntityAnimationPlayer();
5154
- this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
5155
- this.addEventListener("added", () => this.updateScrollSubscription());
5156
- this.addEventListener("removed", () => this.updateScrollSubscription());
5157
- }
5158
- async init() {
5159
- this.createMeshes();
5160
- await this.applyTextures();
5161
- }
5162
- dispose() {
5163
- super.dispose();
5164
- }
5165
- disposeAndRemoveAllChildren() {
5166
- this.clearScrollMaterials();
5167
- for (const geometry of this.geometries) geometry.dispose();
5168
- this.geometries.clear();
5169
- for (const dispose of this.disposeWireframes.splice(0)) dispose();
5170
- this.animationPlayer.clear();
5171
- super.disposeAndRemoveAllChildren();
5172
- }
5173
- clearScrollMaterials() {
5174
- Ticker.remove(this.scrollTicker);
5175
- this.scrollTicker = void 0;
5176
- for (const { material } of this.scrollMaterials.splice(0)) {
5177
- material.map?.dispose();
5178
- material.dispose();
5179
- }
5180
- }
5181
- updateScrollSubscription() {
5182
- if (this.parent && this.scrollMaterials.length) {
5183
- if (this.scrollTicker === void 0) {
5184
- this.scrollTicker = Ticker.add(() => {
5185
- this.age++;
5186
- this.updateScroll();
5187
- });
5188
- }
5189
- } else {
5190
- Ticker.remove(this.scrollTicker);
5191
- this.scrollTicker = void 0;
5192
- }
5193
- }
5194
- /** Applies the entity age to the scrolling materials; the ticker calls this 20 times per second. */
5195
- updateScroll() {
5196
- for (const { offset, speed } of this.scrollMaterials) {
5197
- offset.set(this.age * speed[0] % 1, 0 - this.age * speed[1] % 1);
5198
- }
5199
- this.notifyDirty();
5200
- }
5201
- //<editor-fold desc="ANIMATION">
5202
- /** The animation started by {@link playAnimation}, until {@link stopAnimation} or disposal. */
5203
- get animation() {
5204
- return this.animationPlayer.animation;
5205
- }
5206
- /** Seconds since the animation started, before looping. */
5207
- get animationTime() {
5208
- return this.animationPlayer.time;
5209
- }
5210
- /**
5211
- * Poses the object from a keyframe animation of {@link Entities.getAnimations}, replacing the current one.
5212
- * Parts of every layer are matched by name and their current pose is the default the offsets add to.
5213
- * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.
5214
- */
5215
- playAnimation(animation, options) {
5216
- this.createMeshes();
5217
- const layers = Object.keys(this.entityLayers).map((name) => this.getLayerGroup(name)).filter((layer) => !!layer);
5218
- this.animationPlayer.play(layers, animation, options);
5219
- this.notifyDirty();
5220
- }
5221
- /** Stops the animation and restores the default pose. */
5222
- stopAnimation() {
5223
- if (this.animationPlayer.stop()) this.notifyDirty();
5224
- }
5225
- /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
5226
- setAnimationTime(seconds) {
5227
- if (this.animationPlayer.setTime(seconds)) this.notifyDirty();
5228
- }
5229
- /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
5230
- advanceAnimation(deltaSeconds) {
5231
- if (this.animationPlayer.advance(deltaSeconds)) this.notifyDirty();
5232
- }
5233
- //</editor-fold>
5234
- getLayerGroup(name) {
5235
- return super.getGroupByName(`layer:${name}`);
5236
- }
5237
- getGroupByName(name, layerName) {
5238
- return layerName === void 0 ? super.getGroupByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`group:${name}`);
5239
- }
5240
- getMeshByName(name, layerName) {
5241
- return layerName === void 0 ? super.getMeshByName(name) : this.getLayerGroup(layerName)?.getObjectByName(`mesh:${name}`);
5242
- }
5243
- get entityLayers() {
5244
- return this.entity.layers ?? { main: this.entity };
5245
- }
5246
- getTextureKey(layer) {
5247
- if (layer.texture) return layer.texture;
5248
- const key = layer.key;
5249
- return new AssetKey(
5250
- key.namespace,
5251
- isAssetKey(key) ? key.getFullPath() : key.path,
5252
- "textures",
5253
- "entity",
5254
- "assets",
5255
- ".png"
5256
- );
5257
- }
5258
- async loadTextures(layer = this.entity) {
5259
- return ModelTextures.get(this.getTextureKey(layer));
5260
- }
5261
- createMeshes(force = false) {
5262
- if (this.meshesCreated && !force) return;
5263
- const modelRoot = new Object3D3();
5264
- const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
5265
- if (transform) {
5266
- for (const op of transform) {
5267
- const matrix = new Matrix45();
5268
- if ("scale" in op) matrix.makeScale(...op.scale);
5269
- else if ("translate" in op) matrix.makeTranslation(...op.translate);
5270
- else matrix.makeRotationFromEuler(new Euler4(...op.rotate, "ZYX"));
5271
- modelRoot.matrix.multiply(matrix);
5272
- }
5273
- modelRoot.matrixAutoUpdate = false;
5274
- } else if (this.options.flip ?? true) {
5275
- modelRoot.scale.set(-1, -1, 1);
5276
- }
5277
- this.add(modelRoot);
5278
- Object.entries(this.entityLayers).forEach(([name, layer], index) => {
5279
- const group = this.createGroup(`layer:${name}`);
5280
- modelRoot.add(group);
5281
- const inward = !Materials.entityModeCulls(layer.render ?? layer.layer.render);
5282
- this.createPart("root", layer.layer.root, group, layer.layer.texture, Materials.MISSING_TEXTURE, index, inward);
5283
- });
5284
- this.meshesCreated = true;
5285
- }
5286
- createPart(name, part, parent, textureSize, material, renderOrder, inward = true) {
5287
- const anchor = this.createGroup(name);
5288
- anchor.position.fromArray(part.pose.offset);
5289
- anchor.rotation.set(...part.pose.rotation, "ZYX");
5290
- anchor.scale.fromArray(part.pose.scale ?? [1, 1, 1]);
5291
- parent.add(anchor);
5292
- const size = part.texture ?? textureSize;
5293
- for (const cube of part.cubes) {
5294
- const [width, height, depth] = cube.size;
5295
- const [growX, growY, growZ] = cube.grow ?? [0, 0, 0];
5296
- const texture = new MinecraftCubeTexture(...cube.uv, ...size);
5297
- const uv = size[0] === 0 || size[1] === 0 ? new Array(48).fill(0) : texture.toUvArray(width, height, depth, cube.mirror);
5298
- const geometry = this._getBoxGeometryForDimensionsAndUv(
5299
- width + growX * 2,
5300
- height + growY * 2,
5301
- depth + growZ * 2,
5302
- uv
5303
- ).clone();
5304
- this.geometries.add(geometry);
5305
- geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);
5306
- if (inward && Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {
5307
- const index = Array.from(geometry.getIndex().array);
5308
- geometry.setIndex(index.concat(index.slice().reverse()));
5309
- }
5310
- const mesh = this.createMesh(name, geometry, material);
5311
- mesh.renderOrder = renderOrder;
5312
- anchor.add(mesh);
5313
- if (this.options.wireframe) this.disposeWireframes.push(addWireframeToMesh(geometry, mesh));
5314
- }
5315
- for (const [childName, child] of Object.entries(part.children)) {
5316
- this.createPart(childName, child, anchor, size, material, renderOrder, inward);
5317
- }
5318
- }
5319
- async applyTextures() {
5320
- this.clearScrollMaterials();
5321
- await Promise.all(Object.entries(this.entityLayers).map(async ([name, layer]) => {
5322
- const mode = layer.render ?? layer.layer.render ?? "cutout";
5323
- const tint = layer.tint === void 0 ? void 0 : this.options.tints?.[layer.tint];
5324
- const scroll = Materials.entityModeScroll(mode);
5325
- const assetKeyStr = this.getTextureKey(layer).serialize();
5326
- const keyStr = `entity:${mode}:${tint === void 0 ? "" : new Color3(tint).getHexString()}:${assetKeyStr}`;
5327
- let mat = scroll ? void 0 : Caching.materialCache.getIfPresent(keyStr);
5328
- if (!mat) {
5329
- const cachedAsset = Caching.textureAssetCache.getIfPresent(assetKeyStr);
5330
- const imageData = await this.loadTextures(layer);
5331
- if (!imageData) return;
5332
- const canvas = imageData.data.canvas;
5333
- const createMaterial = () => Materials.createEntityCanvasMaterial(canvas, mode, tint);
5334
- mat = !scroll && cachedAsset && Caching.textureAssetCache.getIfPresent(assetKeyStr) === cachedAsset ? Caching.materialCache.get(keyStr, createMaterial) : createMaterial();
5335
- if (scroll) {
5336
- this.scrollMaterials.push({ material: mat, offset: mat.map.offset, speed: scroll });
5337
- }
5338
- }
5339
- this.getLayerGroup(name)?.traverse((object) => {
5340
- if (isMesh(object)) object.material = mat;
5341
- });
5342
- }));
5343
- if (!this.children.length) {
5344
- this.clearScrollMaterials();
5345
- } else if (this.scrollMaterials.length) {
5346
- this.updateScroll();
5347
- }
5348
- this.updateScrollSubscription();
5349
- this.notifyDirty();
5350
- }
5351
- };
5352
- _EntityObject.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS);
5353
- var EntityObject = _EntityObject;
5354
- function isEntityObject(obj) {
5355
- return obj.isEntityObject;
5356
- }
5589
+ // src/entity/scene/index.ts
5590
+ init_EntityAnimationPlayer();
5591
+ init_EntityObject();
5357
5592
 
5358
5593
  // src/error/index.ts
5359
5594
  init_MineRenderError();
@@ -7982,16 +8217,8 @@ init_SceneObject();
7982
8217
  init_Materials();
7983
8218
  init_ModelObject();
7984
8219
  init_DisplayPosition();
7985
- import { Box2, Box3 as Box32, Mesh as Mesh7, PlaneGeometry as PlaneGeometry2, Vector2 as Vector23 } from "three";
7986
-
7987
- // src/model/GuiLight.ts
7988
- var GuiLight = /* @__PURE__ */ ((GuiLight2) => {
7989
- GuiLight2["FRONT"] = "front";
7990
- GuiLight2["SIDE"] = "side";
7991
- return GuiLight2;
7992
- })(GuiLight || {});
7993
-
7994
- // src/gui/scene/GuiObject.ts
8220
+ init_GuiLight();
8221
+ import { Box2, Box3 as Box32, Matrix4 as Matrix410, Mesh as Mesh7, PlaneGeometry as PlaneGeometry2, Vector2 as Vector23 } from "three";
7995
8222
  var GuiObject = class extends SceneObject {
7996
8223
  constructor(textureLayers, options) {
7997
8224
  super({ ...options, instanceMeshes: false, mergeMeshes: false });
@@ -8014,13 +8241,13 @@ var GuiObject = class extends SceneObject {
8014
8241
  const item = await this.createItem(layer, index);
8015
8242
  item.position.set(x + width2 / 2, -y - height2 / 2, 0);
8016
8243
  item.scale.set(width2 / 16, height2 / 16, width2 / 16);
8017
- item.updateMatrix();
8244
+ item.updateWorldMatrix(true, true);
8245
+ const inverse = this.matrixWorld.clone().invert();
8018
8246
  const bounds = new Box32();
8019
8247
  item.iterateAllMeshes((mesh2) => {
8020
8248
  mesh2.geometry.computeBoundingBox();
8021
- bounds.union(mesh2.geometry.boundingBox);
8249
+ bounds.union(mesh2.geometry.boundingBox.clone().applyMatrix4(new Matrix410().multiplyMatrices(inverse, mesh2.matrixWorld)));
8022
8250
  });
8023
- bounds.applyMatrix4(item.matrix);
8024
8251
  item.position.z = depth - bounds.min.z;
8025
8252
  depth += bounds.max.z - bounds.min.z + 0.01;
8026
8253
  this.bounds.expandByPoint(new Vector23(x, y));
@@ -8075,7 +8302,7 @@ var GuiObject = class extends SceneObject {
8075
8302
  this.add(item);
8076
8303
  await item.init();
8077
8304
  item.iterateAllMeshes((mesh) => {
8078
- this.geometries.add(mesh.geometry);
8305
+ if (!model.special) this.geometries.add(mesh.geometry);
8079
8306
  mesh.name = `mesh:${layer.name ?? index}`;
8080
8307
  mesh.renderOrder = index;
8081
8308
  for (const material of Array.isArray(mesh.material) ? mesh.material : [mesh.material]) {
@@ -8533,26 +8760,12 @@ function isInstanceable(obj) {
8533
8760
  // src/model/index.ts
8534
8761
  init_DisplayPosition();
8535
8762
  init_DisplayTransforms();
8536
-
8537
- // src/model/Model.ts
8538
- var ITEM_GENERATED = "item/generated";
8539
- var BUILTIN_GENERATED = "builtin/generated";
8540
- var BUILTIN_ENTITY = "builtin/entity";
8541
- var DEFAULT_ELEMENTS = [];
8542
- function isDoubleArray(obj) {
8543
- return Array.isArray(obj) && obj.length === 2;
8544
- }
8545
- function isTripleArray(obj) {
8546
- return Array.isArray(obj) && obj.length === 3;
8547
- }
8548
- function isQuadArray(obj) {
8549
- return Array.isArray(obj) && obj.length === 4;
8550
- }
8551
-
8552
- // src/model/index.ts
8763
+ init_GuiLight();
8764
+ init_Model();
8553
8765
  init_ModelCulling();
8554
8766
  init_ModelGenerator();
8555
8767
  init_ModelMerger();
8768
+ init_SpecialItems();
8556
8769
 
8557
8770
  // src/model/block/BlockStateResolver.ts
8558
8771
  init_BlockStates();
@@ -8743,9 +8956,10 @@ var import_ts_deepmerge6 = __toESM(require_dist());
8743
8956
  init_MineRenderError();
8744
8957
  init_InstanceReference();
8745
8958
  init_AssetKey();
8746
- import { Euler as Euler8, Matrix4 as Matrix49, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8959
+ import { Euler as Euler8, Matrix4 as Matrix411, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8747
8960
  init_ModelCulling();
8748
8961
  init_FluidGeometry();
8962
+ init_Entities();
8749
8963
  var _BlockObject = class _BlockObject extends SceneObject {
8750
8964
  constructor(blockState, options) {
8751
8965
  super(options);
@@ -8761,7 +8975,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8761
8975
  this._models = [];
8762
8976
  this._cullMask = 0;
8763
8977
  this._entities = [];
8764
- this._entityPlacement = new Matrix49();
8978
+ this._entityPlacement = new Matrix411();
8765
8979
  this._entityGeneration = 0;
8766
8980
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
8767
8981
  }
@@ -8841,7 +9055,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8841
9055
  return true;
8842
9056
  }
8843
9057
  placeEntities() {
8844
- const matrix = new Matrix49().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
9058
+ const matrix = new Matrix411().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
8845
9059
  for (const entity of this._entities) {
8846
9060
  matrix.decompose(entity.position, entity.quaternion, entity.scale);
8847
9061
  }
@@ -8927,7 +9141,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8927
9141
  () => new FluidModelObject2(kind, surface.sample, this.blockState.key, this.options)
8928
9142
  );
8929
9143
  const previous = this._fluidModel;
8930
- const matrix = previous ? this.getModelMatrix(previous) : new Matrix49().makeTranslation(...this.position.toArray());
9144
+ const matrix = previous ? this.getModelMatrix(previous) : new Matrix411().makeTranslation(...this.position.toArray());
8931
9145
  if (isInstanceReference(replacement)) replacement.setMatrix(matrix);
8932
9146
  else matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
8933
9147
  this._models = this._models.filter((model) => model !== previous);
@@ -9065,7 +9279,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
9065
9279
  setPositionRotationScaleAt(index, position, rotation, scale) {
9066
9280
  super.setPositionRotationScaleAt(index, position, rotation, scale);
9067
9281
  }
9068
- getMatrixAt(index, matrix = new Matrix49()) {
9282
+ getMatrixAt(index, matrix = new Matrix411()) {
9069
9283
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
9070
9284
  const child = this._models[0];
9071
9285
  if (child && isModelObject(child)) {
@@ -11672,7 +11886,7 @@ init_util();
11672
11886
  init_Caching();
11673
11887
  init_CacheKey();
11674
11888
  init_MineRenderError();
11675
- import { DataTexture as DataTexture2, DoubleSide as DoubleSide4, FrontSide as FrontSide2, MeshBasicMaterial as MeshBasicMaterial5, SRGBColorSpace as SRGBColorSpace5 } from "three";
11889
+ import { DataTexture as DataTexture2, DoubleSide as DoubleSide4, FrontSide as FrontSide2, MeshBasicMaterial as MeshBasicMaterial6, SRGBColorSpace as SRGBColorSpace5 } from "three";
11676
11890
 
11677
11891
  // src/skin/SkinImage.ts
11678
11892
  init_Caching();
@@ -11757,7 +11971,7 @@ var SkinTextures = class {
11757
11971
  texture.colorSpace = SRGBColorSpace5;
11758
11972
  texture.flipY = true;
11759
11973
  texture.needsUpdate = true;
11760
- return new MeshBasicMaterial5({ map: texture, transparent, side: transparent ? DoubleSide4 : FrontSide2, alphaTest: transparent ? 0.1 : 0 });
11974
+ return new MeshBasicMaterial6({ map: texture, transparent, side: transparent ? DoubleSide4 : FrontSide2, alphaTest: transparent ? 0.1 : 0 });
11761
11975
  }
11762
11976
  static isSlim(image, scale) {
11763
11977
  for (const [x, y] of [[46, 52], [54, 20]]) {
@@ -11985,6 +12199,7 @@ var SkinObject = class extends SceneObject {
11985
12199
  };
11986
12200
 
11987
12201
  // src/renderer/MineRenderScene.ts
12202
+ init_EntityObject();
11988
12203
  var _MineRenderScene = class _MineRenderScene extends Scene5 {
11989
12204
  constructor(options) {
11990
12205
  super();
@@ -12115,9 +12330,10 @@ function isMineRenderScene(obj) {
12115
12330
  import { AxesHelper as AxesHelper2, GridHelper, OrthographicCamera, PCFSoftShadowMap, PerspectiveCamera as PerspectiveCamera2, SRGBColorSpace as SRGBColorSpace6, Vector3 as Vector313, WebGLRenderer as WebGLRenderer2 } from "three";
12116
12331
  var import_ts_deepmerge9 = __toESM(require_dist());
12117
12332
  init_util();
12333
+ init_Model();
12334
+ init_three();
12118
12335
  import Stats from "stats.js";
12119
12336
  import { EffectComposer, RenderPass } from "postprocessing";
12120
- init_three();
12121
12337
 
12122
12338
  // src/three/OrbitControls.js
12123
12339
  import {
@@ -13620,6 +13836,7 @@ init_BlockStates();
13620
13836
  init_AssetKey();
13621
13837
 
13622
13838
  // src/world/MineRenderWorld.ts
13839
+ init_Model();
13623
13840
  import { Vector3 as Vector315 } from "three";
13624
13841
  init_BatchedExecutor();
13625
13842
  init_AssetKey();
@@ -13630,7 +13847,7 @@ init_CubeFace();
13630
13847
  init_AssetKey();
13631
13848
  init_Models();
13632
13849
  init_CubeFace();
13633
- import { Matrix4 as Matrix410, Vector3 as Vector314 } from "three";
13850
+ import { Matrix4 as Matrix412, Vector3 as Vector314 } from "three";
13634
13851
  init_ModelCulling();
13635
13852
  init_ModelObject();
13636
13853
  init_UVMapper();
@@ -13693,7 +13910,7 @@ var SectionModels = class {
13693
13910
  });
13694
13911
  await object.init();
13695
13912
  const geometry = object.children[0].geometry;
13696
- geometry.applyMatrix4(new Matrix410().makeRotationFromEuler(rotation));
13913
+ geometry.applyMatrix4(new Matrix412().makeRotationFromEuler(rotation));
13697
13914
  object.clear();
13698
13915
  const cullFaces = CUBE_FACES.map((face) => {
13699
13916
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
@@ -13940,6 +14157,9 @@ var MineRenderWorld = class {
13940
14157
  }
13941
14158
  };
13942
14159
 
14160
+ // src/world/Chunk.ts
14161
+ init_Model();
14162
+
13943
14163
  // src/world/SectionMesh.ts
13944
14164
  init_CanvasCompat();
13945
14165
  init_Materials();
@@ -14288,6 +14508,7 @@ export {
14288
14508
  SkinPart,
14289
14509
  SkinTextures,
14290
14510
  Skins,
14511
+ SpecialItems,
14291
14512
  StructureParser,
14292
14513
  TextureAtlas,
14293
14514
  TextureLoader,