minerender 2.1.0-alpha.54 → 2.1.0-alpha.55

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.
@@ -290,6 +290,12 @@ var init_Caching = __esm({
290
290
  static {
291
291
  this.entityTextureCache = _Caching.createAssetCache();
292
292
  }
293
+ static {
294
+ this.entityAnimationCache = _Caching.createAssetCache();
295
+ }
296
+ static {
297
+ this.blockEntityIndexCache = _Caching.createAssetCache();
298
+ }
293
299
  static createAssetCache() {
294
300
  return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
295
301
  }
@@ -314,7 +320,9 @@ var init_Caching = __esm({
314
320
  this.defaultBlockStatesCache,
315
321
  this.blockTintCache,
316
322
  this.entityModelCache,
317
- this.entityTextureCache
323
+ this.entityTextureCache,
324
+ this.entityAnimationCache,
325
+ this.blockEntityIndexCache
318
326
  ];
319
327
  }
320
328
  static clear() {
@@ -3161,7 +3169,7 @@ var init_InstanceReference = __esm({
3161
3169
  });
3162
3170
 
3163
3171
  // src/renderer/SceneObject.ts
3164
- import { Color as Color2, Euler as Euler3, InstancedBufferAttribute, InstancedMesh, MathUtils, Matrix4 as Matrix43, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion3, Vector3 as Vector35 } from "three";
3172
+ import { Color as Color2, Euler as Euler3, InstancedBufferAttribute, InstancedMesh, MathUtils, Matrix4 as Matrix44, Mesh as Mesh3, Object3D as Object3D2, Quaternion as Quaternion3, Vector3 as Vector35 } from "three";
3165
3173
  function isSceneObject(obj) {
3166
3174
  return obj.isSceneObject;
3167
3175
  }
@@ -3377,7 +3385,7 @@ var init_SceneObject = __esm({
3377
3385
  mesh.instanceColor.needsUpdate = true;
3378
3386
  }
3379
3387
  if (this._scene) this._scene.stats.instanceCount++;
3380
- this.setMatrixAt(index, new Matrix43());
3388
+ this.setMatrixAt(index, new Matrix44());
3381
3389
  return reference;
3382
3390
  }
3383
3391
  /** Returns the live reference for a raycast hit on this object's instance mesh. */
@@ -3390,7 +3398,7 @@ var init_SceneObject = __esm({
3390
3398
  removeInstanceAt(index) {
3391
3399
  if (!this.instanceReferences.has(index)) return;
3392
3400
  const mesh = this.instanceMesh;
3393
- this.setMatrixAt(index, new Matrix43().makeScale(0, 0, 0));
3401
+ this.setMatrixAt(index, new Matrix44().makeScale(0, 0, 0));
3394
3402
  this.instanceReferences.delete(index);
3395
3403
  this.freeInstanceIndices.push(index);
3396
3404
  this._instanceCounter = this.instanceReferences.size;
@@ -3420,7 +3428,7 @@ var init_SceneObject = __esm({
3420
3428
  }
3421
3429
  //</editor-fold>
3422
3430
  //<editor-fold desc="TRANSFORMATION">
3423
- getMatrixAt(index, matrix = new Matrix43()) {
3431
+ getMatrixAt(index, matrix = new Matrix44()) {
3424
3432
  if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3425
3433
  this.instanceMesh.getMatrixAt(index, matrix);
3426
3434
  return matrix;
@@ -3439,7 +3447,7 @@ var init_SceneObject = __esm({
3439
3447
  const oldPosition = new Vector35();
3440
3448
  const oldRotation = new Quaternion3();
3441
3449
  const oldScale = new Vector35();
3442
- const matrix = new Matrix43();
3450
+ const matrix = new Matrix44();
3443
3451
  if (!scale || !rotation || !position) {
3444
3452
  this.getMatrixAt(index, matrix).decompose(oldPosition, oldRotation, oldScale);
3445
3453
  }
@@ -4187,7 +4195,7 @@ var init_ModelCulling = __esm({
4187
4195
  });
4188
4196
 
4189
4197
  // src/model/scene/ModelObject.ts
4190
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix47 } from "three";
4198
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix48 } from "three";
4191
4199
  function isModelObject(obj) {
4192
4200
  return obj.isModelObject;
4193
4201
  }
@@ -4269,12 +4277,12 @@ var init_ModelObject = __esm({
4269
4277
  }
4270
4278
  elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
4271
4279
  }
4272
- elGeo.applyMatrix4(new Matrix47().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4273
- elGeo.applyMatrix4(new Matrix47().makeTranslation(el.from[0], el.from[1], el.from[2]));
4280
+ 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));
4281
+ elGeo.applyMatrix4(new Matrix48().makeTranslation(el.from[0], el.from[1], el.from[2]));
4274
4282
  if (el.rotation) {
4275
4283
  applyElementRotation(el.rotation, elGeo);
4276
4284
  }
4277
- elGeo.applyMatrix4(new Matrix47().makeTranslation(-8, -8, -8));
4285
+ elGeo.applyMatrix4(new Matrix48().makeTranslation(-8, -8, -8));
4278
4286
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4279
4287
  if (this.options.mergeMeshes) {
4280
4288
  allGeos.push(elGeo);
@@ -4843,6 +4851,59 @@ function shutdown() {
4843
4851
  init_AssetKey();
4844
4852
  init_AssetLoader();
4845
4853
  init_Assets();
4854
+
4855
+ // src/assets/BlockEntities.ts
4856
+ init_AssetKey();
4857
+ init_AssetLoader();
4858
+ init_source();
4859
+ init_Caching();
4860
+ import { Matrix4 as Matrix43 } from "three";
4861
+ var BlockEntities = class {
4862
+ /**
4863
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4864
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4865
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4866
+ */
4867
+ static async getIndex(root) {
4868
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4869
+ key.rootType = void 0;
4870
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4871
+ try {
4872
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4873
+ } catch (error) {
4874
+ console.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4875
+ return {};
4876
+ }
4877
+ });
4878
+ return index ?? {};
4879
+ }
4880
+ /** The index entry of a block ID such as `minecraft:chest`. */
4881
+ static entry(index, block) {
4882
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4883
+ return Array.isArray(entry?.parts) ? entry : void 0;
4884
+ }
4885
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4886
+ static resolve(index, block, state = {}) {
4887
+ const entry = this.entry(index, block);
4888
+ if (!entry) return void 0;
4889
+ const value = (property) => String(state[property]).toLowerCase();
4890
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4891
+ if (!parts.length) return void 0;
4892
+ let rotation = 0;
4893
+ if (entry.rotation) {
4894
+ const selected = value(entry.rotation.property);
4895
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4896
+ if (!Number.isFinite(rotation)) rotation = 0;
4897
+ }
4898
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4899
+ }
4900
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4901
+ static matrix(resolved, matrix = new Matrix43()) {
4902
+ return matrix.makeTranslation(8, 0, 8).multiply(new Matrix43().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new Matrix43().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
4903
+ }
4904
+ };
4905
+
4906
+ // src/assets/index.ts
4846
4907
  init_BlockStates();
4847
4908
 
4848
4909
  // src/assets/Entities.ts
@@ -4913,6 +4974,15 @@ var Entities = class {
4913
4974
  })));
4914
4975
  return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
4915
4976
  }
4977
+ /**
4978
+ * Vanilla's keyframe animations of a model by name, or undefined when the model or the selected version has none.
4979
+ */
4980
+ static async getAnimations(modelKey) {
4981
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4982
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json");
4983
+ const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4984
+ return file?.animations;
4985
+ }
4916
4986
  static async resolveTexture(modelKey) {
4917
4987
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4918
4988
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
@@ -4989,6 +5059,119 @@ function trimCanvas(source) {
4989
5059
  return canvas;
4990
5060
  }
4991
5061
 
5062
+ // src/entity/EntityAnimation.ts
5063
+ var ENTITY_ANIMATION_CHANNELS = ["position", "rotation", "scale"];
5064
+ function catmullRom(delta, p0, p1, p22, p32) {
5065
+ 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);
5066
+ }
5067
+ function sampleEntityKeyframes(keyframes, time) {
5068
+ if (keyframes.length === 0) return [0, 0, 0];
5069
+ let low = 0, high = keyframes.length;
5070
+ while (low < high) {
5071
+ const middle = low + high >>> 1;
5072
+ if (time <= keyframes[middle].time) high = middle;
5073
+ else low = middle + 1;
5074
+ }
5075
+ const a = Math.max(0, low - 1);
5076
+ const b = Math.min(keyframes.length - 1, a + 1);
5077
+ const from = keyframes[a];
5078
+ const to = keyframes[b];
5079
+ const delta = a === b ? 0 : Math.min(1, Math.max(0, (time - from.time) / (to.time - from.time)));
5080
+ if (to.interpolation === "catmullrom") {
5081
+ const before = keyframes[Math.max(0, a - 1)].value;
5082
+ const after = keyframes[Math.min(keyframes.length - 1, b + 1)].value;
5083
+ return [0, 1, 2].map((i) => catmullRom(delta, before[i], from.value[i], to.value[i], after[i]));
5084
+ }
5085
+ const target = to.pre ?? to.value;
5086
+ return [0, 1, 2].map((i) => from.value[i] + (target[i] - from.value[i]) * delta);
5087
+ }
5088
+ function entityAnimationTime(animation, time, loop = animation.loop) {
5089
+ return loop && animation.length > 0 ? time % animation.length : time;
5090
+ }
5091
+ function sampleEntityAnimation(animation, time, loop = animation.loop) {
5092
+ const elapsed = entityAnimationTime(animation, time, loop);
5093
+ const pose = {};
5094
+ for (const [name, bone] of Object.entries(animation.bones)) {
5095
+ const sampled = pose[name] = {};
5096
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
5097
+ const keyframes = bone[channel];
5098
+ if (keyframes?.length) sampled[channel] = sampleEntityKeyframes(keyframes, elapsed);
5099
+ }
5100
+ }
5101
+ return pose;
5102
+ }
5103
+
5104
+ // src/entity/scene/EntityAnimationPlayer.ts
5105
+ var ZERO = [0, 0, 0];
5106
+ var EntityAnimationPlayer = class {
5107
+ constructor() {
5108
+ this.parts = [];
5109
+ this._time = 0;
5110
+ this.loop = false;
5111
+ this.speed = 1;
5112
+ }
5113
+ get animation() {
5114
+ return this._animation;
5115
+ }
5116
+ get time() {
5117
+ return this._time;
5118
+ }
5119
+ /**
5120
+ * Starts an animation on the named parts below each layer root. The current pose of a part becomes its default;
5121
+ * bones without a matching part are skipped.
5122
+ */
5123
+ play(layers, animation, options = {}) {
5124
+ this.stop();
5125
+ this._animation = animation;
5126
+ this._time = options.time ?? 0;
5127
+ this.loop = options.loop ?? animation.loop;
5128
+ this.speed = options.speed ?? 1;
5129
+ for (const layer of layers) {
5130
+ for (const bone of Object.keys(animation.bones)) {
5131
+ const object = layer.getObjectByName(`group:${bone}`);
5132
+ if (!object) continue;
5133
+ this.parts.push({ bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone() });
5134
+ }
5135
+ }
5136
+ this.apply();
5137
+ }
5138
+ /** Restores the default poses. Returns whether an animation was active. */
5139
+ stop() {
5140
+ if (!this._animation) return false;
5141
+ for (const part of this.parts) {
5142
+ part.object.position.copy(part.position);
5143
+ part.object.rotation.copy(part.rotation);
5144
+ part.object.scale.copy(part.scale);
5145
+ }
5146
+ this.clear();
5147
+ return true;
5148
+ }
5149
+ /** Forgets the animation without touching the parts, for parts that no longer exist. */
5150
+ clear() {
5151
+ this.parts = [];
5152
+ this._animation = void 0;
5153
+ this._time = 0;
5154
+ }
5155
+ setTime(seconds) {
5156
+ if (!this._animation) return false;
5157
+ this._time = seconds;
5158
+ this.apply();
5159
+ return true;
5160
+ }
5161
+ advance(deltaSeconds) {
5162
+ return this.setTime(this._time + deltaSeconds * this.speed);
5163
+ }
5164
+ apply() {
5165
+ const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
5166
+ for (const part of this.parts) {
5167
+ const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone];
5168
+ part.object.position.set(part.position.x + position[0], part.position.y + position[1], part.position.z + position[2]);
5169
+ part.object.rotation.set(part.rotation.x + rotation[0], part.rotation.y + rotation[1], part.rotation.z + rotation[2], part.rotation.order);
5170
+ part.object.scale.set(part.scale.x + scale[0], part.scale.y + scale[1], part.scale.z + scale[2]);
5171
+ }
5172
+ }
5173
+ };
5174
+
4992
5175
  // src/entity/scene/EntityObject.ts
4993
5176
  init_SceneObject();
4994
5177
  init_Caching();
@@ -4998,7 +5181,7 @@ init_model();
4998
5181
  init_ModelTextures();
4999
5182
  init_AssetKey();
5000
5183
  init_Materials();
5001
- import { Color as Color3, Euler as Euler4, Matrix4 as Matrix44, Object3D as Object3D3 } from "three";
5184
+ import { Color as Color3, Euler as Euler4, Matrix4 as Matrix45, Object3D as Object3D3 } from "three";
5002
5185
  init_three();
5003
5186
  var EntityObject = class _EntityObject extends SceneObject {
5004
5187
  constructor(entity, options) {
@@ -5010,6 +5193,7 @@ var EntityObject = class _EntityObject extends SceneObject {
5010
5193
  this.scrollMaterials = [];
5011
5194
  /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
5012
5195
  this.age = 0;
5196
+ this.animationPlayer = new EntityAnimationPlayer();
5013
5197
  this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
5014
5198
  }
5015
5199
  static {
@@ -5024,6 +5208,7 @@ var EntityObject = class _EntityObject extends SceneObject {
5024
5208
  }
5025
5209
  disposeAndRemoveAllChildren() {
5026
5210
  this.clearScrollMaterials();
5211
+ this.animationPlayer.clear();
5027
5212
  super.disposeAndRemoveAllChildren();
5028
5213
  }
5029
5214
  clearScrollMaterials() {
@@ -5041,6 +5226,39 @@ var EntityObject = class _EntityObject extends SceneObject {
5041
5226
  }
5042
5227
  this.notifyDirty();
5043
5228
  }
5229
+ //<editor-fold desc="ANIMATION">
5230
+ /** The animation started by {@link playAnimation}, until {@link stopAnimation} or disposal. */
5231
+ get animation() {
5232
+ return this.animationPlayer.animation;
5233
+ }
5234
+ /** Seconds since the animation started, before looping. */
5235
+ get animationTime() {
5236
+ return this.animationPlayer.time;
5237
+ }
5238
+ /**
5239
+ * Poses the object from a keyframe animation of {@link Entities.getAnimations}, replacing the current one.
5240
+ * Parts of every layer are matched by name and their current pose is the default the offsets add to.
5241
+ * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.
5242
+ */
5243
+ playAnimation(animation, options) {
5244
+ this.createMeshes();
5245
+ const layers = Object.keys(this.entityLayers).map((name) => this.getLayerGroup(name)).filter((layer) => !!layer);
5246
+ this.animationPlayer.play(layers, animation, options);
5247
+ this.notifyDirty();
5248
+ }
5249
+ /** Stops the animation and restores the default pose. */
5250
+ stopAnimation() {
5251
+ if (this.animationPlayer.stop()) this.notifyDirty();
5252
+ }
5253
+ /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
5254
+ setAnimationTime(seconds) {
5255
+ if (this.animationPlayer.setTime(seconds)) this.notifyDirty();
5256
+ }
5257
+ /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
5258
+ advanceAnimation(deltaSeconds) {
5259
+ if (this.animationPlayer.advance(deltaSeconds)) this.notifyDirty();
5260
+ }
5261
+ //</editor-fold>
5044
5262
  getLayerGroup(name) {
5045
5263
  return super.getGroupByName(`layer:${name}`);
5046
5264
  }
@@ -5074,7 +5292,7 @@ var EntityObject = class _EntityObject extends SceneObject {
5074
5292
  const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
5075
5293
  if (transform) {
5076
5294
  for (const op of transform) {
5077
- const matrix = new Matrix44();
5295
+ const matrix = new Matrix45();
5078
5296
  if ("scale" in op) matrix.makeScale(...op.scale);
5079
5297
  else if ("translate" in op) matrix.makeTranslation(...op.translate);
5080
5298
  else matrix.makeRotationFromEuler(new Euler4(...op.rotate, "ZYX"));
@@ -5169,7 +5387,7 @@ function isEntityObject(obj) {
5169
5387
  init_MineRenderError();
5170
5388
 
5171
5389
  // src/export/SceneExporter.ts
5172
- import { ClampToEdgeWrapping as ClampToEdgeWrapping2, Color as Color7, Float32BufferAttribute as Float32BufferAttribute3, Matrix4 as Matrix46, Mesh as Mesh5, MeshBasicMaterial as MeshBasicMaterial2, NearestFilter as NearestFilter3, RGBAFormat as RGBAFormat3, Scene as Scene4, Source as Source2, Texture as Texture4, UnsignedByteType } from "three";
5390
+ import { ClampToEdgeWrapping as ClampToEdgeWrapping2, Color as Color7, Float32BufferAttribute as Float32BufferAttribute3, Matrix4 as Matrix47, Mesh as Mesh5, MeshBasicMaterial as MeshBasicMaterial2, NearestFilter as NearestFilter3, RGBAFormat as RGBAFormat3, Scene as Scene4, Source as Source2, Texture as Texture4, UnsignedByteType } from "three";
5173
5391
 
5174
5392
  // ../../node_modules/three/examples/jsm/exporters/OBJExporter.js
5175
5393
  import {
@@ -5595,7 +5813,7 @@ import {
5595
5813
  LinearMipmapLinearFilter,
5596
5814
  LinearMipmapNearestFilter,
5597
5815
  MathUtils as MathUtils2,
5598
- Matrix4 as Matrix45,
5816
+ Matrix4 as Matrix46,
5599
5817
  MirroredRepeatWrapping,
5600
5818
  NearestFilter as NearestFilter2,
5601
5819
  NearestMipmapLinearFilter,
@@ -6885,7 +7103,7 @@ var GLTFWriter = class {
6885
7103
  if (rootJoint === void 0) return null;
6886
7104
  const joints = [];
6887
7105
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
6888
- const temporaryBoneInverse = new Matrix45();
7106
+ const temporaryBoneInverse = new Matrix46();
6889
7107
  for (let i = 0; i < skeleton.bones.length; ++i) {
6890
7108
  joints.push(nodeMap.get(skeleton.bones[i]));
6891
7109
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
@@ -7351,7 +7569,7 @@ var GLTFMeshGpuInstancing = class {
7351
7569
  const translationAttr = new Float32Array(mesh.count * 3);
7352
7570
  const rotationAttr = new Float32Array(mesh.count * 4);
7353
7571
  const scaleAttr = new Float32Array(mesh.count * 3);
7354
- const matrix = new Matrix45();
7572
+ const matrix = new Matrix46();
7355
7573
  const position = new Vector38();
7356
7574
  const quaternion = new Quaternion4();
7357
7575
  const scale = new Vector38();
@@ -7525,7 +7743,7 @@ var SceneExporter = class {
7525
7743
  }
7526
7744
  };
7527
7745
  function localMatrix(object) {
7528
- return object.matrixAutoUpdate ? new Matrix46().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7746
+ return object.matrixAutoUpdate ? new Matrix47().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7529
7747
  }
7530
7748
  function createSnapshot(root, gltf, bakeColors = false) {
7531
7749
  const scene = new Scene4();
@@ -7637,7 +7855,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7637
7855
  const instanced = mesh;
7638
7856
  if (instanced.isInstancedMesh) {
7639
7857
  for (let i = 0; i < instanced.count; i++) {
7640
- const matrix = new Matrix46();
7858
+ const matrix = new Matrix47();
7641
7859
  instanced.getMatrixAt(i, matrix);
7642
7860
  const color = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
7643
7861
  addMesh(mesh, world.clone().multiply(matrix), color);
@@ -7651,7 +7869,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7651
7869
  try {
7652
7870
  const ancestors = [];
7653
7871
  for (let parent = root.parent; parent; parent = parent.parent) ancestors.unshift(parent);
7654
- const world = new Matrix46();
7872
+ const world = new Matrix47();
7655
7873
  for (const ancestor of ancestors) {
7656
7874
  if (!ancestor.visible) return { scene, dispose };
7657
7875
  world.multiply(localMatrix(ancestor));
@@ -8553,7 +8771,7 @@ var import_ts_deepmerge6 = __toESM(require_dist());
8553
8771
  init_MineRenderError();
8554
8772
  init_InstanceReference();
8555
8773
  init_AssetKey();
8556
- import { Euler as Euler8, Matrix4 as Matrix48, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8774
+ import { Euler as Euler8, Matrix4 as Matrix49, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8557
8775
  init_ModelCulling();
8558
8776
  init_FluidGeometry();
8559
8777
  var BlockObject = class _BlockObject extends SceneObject {
@@ -8570,6 +8788,9 @@ var BlockObject = class _BlockObject extends SceneObject {
8570
8788
  this._variants = [];
8571
8789
  this._models = [];
8572
8790
  this._cullMask = 0;
8791
+ this._entities = [];
8792
+ this._entityPlacement = new Matrix49();
8793
+ this._entityGeneration = 0;
8573
8794
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
8574
8795
  }
8575
8796
  static {
@@ -8594,6 +8815,8 @@ var BlockObject = class _BlockObject extends SceneObject {
8594
8815
  }
8595
8816
  clearModels() {
8596
8817
  this.removeModels(this._models.splice(0));
8818
+ this._entityGeneration++;
8819
+ this.removeEntities(this._entities.splice(0));
8597
8820
  this._fluidKey = void 0;
8598
8821
  this._fluidModel = void 0;
8599
8822
  this._isInstanced = false;
@@ -8605,6 +8828,57 @@ var BlockObject = class _BlockObject extends SceneObject {
8605
8828
  if (!isInstanceReference(model)) model.dispose();
8606
8829
  }
8607
8830
  }
8831
+ removeEntities(entities) {
8832
+ for (const entity of entities) {
8833
+ entity.removeFromScene();
8834
+ entity.dispose();
8835
+ }
8836
+ }
8837
+ /** Entity models drawn for this block instead of its block model; they are scene children, unlike the block. */
8838
+ get blockEntities() {
8839
+ return this._entities;
8840
+ }
8841
+ /**
8842
+ * Draws the block through the dataset's block-entity index. Returns false, leaving nothing behind,
8843
+ * when the block is not listed or one of its models cannot be loaded.
8844
+ */
8845
+ async createBlockEntities() {
8846
+ const key = this.blockState.key;
8847
+ if (!key || this.options.displayPosition) return false;
8848
+ const generation = this._entityGeneration;
8849
+ const created = [];
8850
+ let resolved;
8851
+ try {
8852
+ resolved = BlockEntities.resolve(await BlockEntities.getIndex(key.root), key.toNamespacedString(), this.state);
8853
+ if (!resolved) return false;
8854
+ for (const part of resolved.parts) {
8855
+ const [namespace, path] = part.model.includes(":") ? part.model.split(":") : [key.namespace, part.model];
8856
+ const texture = part.textureLocation === void 0 ? void 0 : AssetKey.parse("textures", part.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
8857
+ if (texture) texture.root = key.root;
8858
+ const entity = await Entities.getEntity(new BasicAssetKey(namespace, path), texture, { layer: part.layer });
8859
+ if (!entity) throw new MineRenderError(`Missing entity model ${part.model}`);
8860
+ created.push(await this.scene.addEntity(entity, { wireframe: this.options.wireframe }));
8861
+ }
8862
+ } catch (error) {
8863
+ console.warn(`Could not draw block entity ${key.toNamespacedString()}, using its block model`, error);
8864
+ this.removeEntities(created);
8865
+ return false;
8866
+ }
8867
+ if (generation !== this._entityGeneration) {
8868
+ this.removeEntities(created);
8869
+ return true;
8870
+ }
8871
+ BlockEntities.matrix(resolved, this._entityPlacement);
8872
+ this._entities = created;
8873
+ this.placeEntities();
8874
+ return true;
8875
+ }
8876
+ placeEntities() {
8877
+ const matrix = new Matrix49().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
8878
+ for (const entity of this._entities) {
8879
+ matrix.decompose(entity.position, entity.quaternion, entity.scale);
8880
+ }
8881
+ }
8608
8882
  get isOccluding() {
8609
8883
  return this._models.some((model) => {
8610
8884
  const object = isInstanceReference(model) ? model.instanceable : model;
@@ -8686,7 +8960,7 @@ var BlockObject = class _BlockObject extends SceneObject {
8686
8960
  () => new FluidModelObject2(kind, surface.sample, this.blockState.key, this.options)
8687
8961
  );
8688
8962
  const previous = this._fluidModel;
8689
- const matrix = previous ? this.getModelMatrix(previous) : new Matrix48().makeTranslation(...this.position.toArray());
8963
+ const matrix = previous ? this.getModelMatrix(previous) : new Matrix49().makeTranslation(...this.position.toArray());
8690
8964
  if (isInstanceReference(replacement)) replacement.setMatrix(matrix);
8691
8965
  else matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
8692
8966
  this._models = this._models.filter((model) => model !== previous);
@@ -8716,13 +8990,20 @@ var BlockObject = class _BlockObject extends SceneObject {
8716
8990
  await this.updateFluid(this._fluidSampler);
8717
8991
  return;
8718
8992
  }
8993
+ const hasEntities = await this.createBlockEntities();
8719
8994
  const variantsToCreate = await this.mapStateToVariant(this.state);
8720
8995
  this._variants = variantsToCreate;
8721
8996
  for (let blockStateVariant of variantsToCreate) {
8997
+ if (hasEntities && !await this.hasGeometry(blockStateVariant)) continue;
8722
8998
  this._models.push(await this.createVariant(blockStateVariant));
8723
8999
  }
8724
9000
  await this.updateFluid(this._fluidSampler);
8725
9001
  }
9002
+ async hasGeometry(variant) {
9003
+ const modelKey = AssetKey.parse("models", variant.model);
9004
+ modelKey.root = this.blockState.key?.root;
9005
+ return !!(await Models.getMerged(modelKey))?.elements?.length;
9006
+ }
8726
9007
  getSingleVariant(variants) {
8727
9008
  return BlockStateResolver.choose(variants);
8728
9009
  }
@@ -8817,7 +9098,7 @@ var BlockObject = class _BlockObject extends SceneObject {
8817
9098
  setPositionRotationScaleAt(index, position, rotation, scale) {
8818
9099
  super.setPositionRotationScaleAt(index, position, rotation, scale);
8819
9100
  }
8820
- getMatrixAt(index, matrix = new Matrix48()) {
9101
+ getMatrixAt(index, matrix = new Matrix49()) {
8821
9102
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
8822
9103
  const child = this._models[0];
8823
9104
  if (child && isModelObject(child)) {
@@ -8842,6 +9123,7 @@ var BlockObject = class _BlockObject extends SceneObject {
8842
9123
  for (let model of this._models) {
8843
9124
  model.setPosition(position);
8844
9125
  }
9126
+ this.placeEntities();
8845
9127
  this.notifyDirty();
8846
9128
  }
8847
9129
  //TODO: should override rotation + scale methods
@@ -13379,7 +13661,7 @@ init_CubeFace();
13379
13661
  init_AssetKey();
13380
13662
  init_Models();
13381
13663
  init_CubeFace();
13382
- import { Matrix4 as Matrix49, Vector3 as Vector314 } from "three";
13664
+ import { Matrix4 as Matrix410, Vector3 as Vector314 } from "three";
13383
13665
  init_ModelCulling();
13384
13666
  init_ModelObject();
13385
13667
  init_UVMapper();
@@ -13442,7 +13724,7 @@ var SectionModels = class {
13442
13724
  });
13443
13725
  await object.init();
13444
13726
  const geometry = object.children[0].geometry;
13445
- geometry.applyMatrix4(new Matrix49().makeRotationFromEuler(rotation));
13727
+ geometry.applyMatrix4(new Matrix410().makeRotationFromEuler(rotation));
13446
13728
  object.clear();
13447
13729
  const cullFaces = CUBE_FACES.map((face) => {
13448
13730
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
@@ -13854,7 +14136,8 @@ var Chunk = class _Chunk {
13854
14136
  this.data.set(index, void 0);
13855
14137
  return void 0;
13856
14138
  }
13857
- const template = getFluidKind(blockState.key, stored.properties) ? void 0 : await this.sectionModels?.get(blockState, stored.properties);
14139
+ const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
14140
+ const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
13858
14141
  if (template) {
13859
14142
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
13860
14143
  this.meshDirty = true;
@@ -13963,6 +14246,7 @@ export {
13963
14246
  BUILTIN_GENERATED,
13964
14247
  BasicAssetKey,
13965
14248
  BatchedExecutor,
14249
+ BlockEntities,
13966
14250
  BlockObject,
13967
14251
  BlockStateResolver,
13968
14252
  BlockStates,
@@ -13984,7 +14268,9 @@ export {
13984
14268
  DefaultBlockStates,
13985
14269
  DisplayPosition,
13986
14270
  DisplayTransforms,
14271
+ ENTITY_ANIMATION_CHANNELS,
13987
14272
  Entities,
14273
+ EntityAnimationPlayer,
13988
14274
  EntityObject,
13989
14275
  Env,
13990
14276
  FluidModelObject,
@@ -14060,6 +14346,7 @@ export {
14060
14346
  createCanvas2 as createCanvas,
14061
14347
  createFluidGeometry,
14062
14348
  createImage,
14349
+ entityAnimationTime,
14063
14350
  estimateBytesUsed,
14064
14351
  getBlockFluidState,
14065
14352
  getFluidKind,
@@ -14092,6 +14379,8 @@ export {
14092
14379
  mergeVertices,
14093
14380
  prefix,
14094
14381
  registerNodeEnv,
14382
+ sampleEntityAnimation,
14383
+ sampleEntityKeyframes,
14095
14384
  sampleFluid,
14096
14385
  serializeBoxGeometryKey,
14097
14386
  serializeImageKey,