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.
@@ -256,7 +256,9 @@ var init_Caching = __esm({
256
256
  this.defaultBlockStatesCache,
257
257
  this.blockTintCache,
258
258
  this.entityModelCache,
259
- this.entityTextureCache
259
+ this.entityTextureCache,
260
+ this.entityAnimationCache,
261
+ this.blockEntityIndexCache
260
262
  ];
261
263
  }
262
264
  static clear() {
@@ -301,6 +303,8 @@ var init_Caching = __esm({
301
303
  _Caching.blockTintCache = _Caching.createAssetCache();
302
304
  _Caching.entityModelCache = _Caching.createAssetCache();
303
305
  _Caching.entityTextureCache = _Caching.createAssetCache();
306
+ _Caching.entityAnimationCache = _Caching.createAssetCache();
307
+ _Caching.blockEntityIndexCache = _Caching.createAssetCache();
304
308
  Caching = _Caching;
305
309
  }
306
310
  });
@@ -3079,7 +3083,7 @@ var init_InstanceReference = __esm({
3079
3083
  });
3080
3084
 
3081
3085
  // src/renderer/SceneObject.ts
3082
- 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";
3086
+ 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";
3083
3087
  function isSceneObject(obj) {
3084
3088
  return obj.isSceneObject;
3085
3089
  }
@@ -3287,7 +3291,7 @@ var init_SceneObject = __esm({
3287
3291
  mesh.instanceColor.needsUpdate = true;
3288
3292
  }
3289
3293
  if (this._scene) this._scene.stats.instanceCount++;
3290
- this.setMatrixAt(index, new Matrix43());
3294
+ this.setMatrixAt(index, new Matrix44());
3291
3295
  return reference;
3292
3296
  }
3293
3297
  /** Returns the live reference for a raycast hit on this object's instance mesh. */
@@ -3300,7 +3304,7 @@ var init_SceneObject = __esm({
3300
3304
  removeInstanceAt(index) {
3301
3305
  if (!this.instanceReferences.has(index)) return;
3302
3306
  const mesh = this.instanceMesh;
3303
- this.setMatrixAt(index, new Matrix43().makeScale(0, 0, 0));
3307
+ this.setMatrixAt(index, new Matrix44().makeScale(0, 0, 0));
3304
3308
  this.instanceReferences.delete(index);
3305
3309
  this.freeInstanceIndices.push(index);
3306
3310
  this._instanceCounter = this.instanceReferences.size;
@@ -3330,7 +3334,7 @@ var init_SceneObject = __esm({
3330
3334
  }
3331
3335
  //</editor-fold>
3332
3336
  //<editor-fold desc="TRANSFORMATION">
3333
- getMatrixAt(index, matrix = new Matrix43()) {
3337
+ getMatrixAt(index, matrix = new Matrix44()) {
3334
3338
  if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3335
3339
  this.instanceMesh.getMatrixAt(index, matrix);
3336
3340
  return matrix;
@@ -3349,7 +3353,7 @@ var init_SceneObject = __esm({
3349
3353
  const oldPosition = new Vector35();
3350
3354
  const oldRotation = new Quaternion3();
3351
3355
  const oldScale = new Vector35();
3352
- const matrix = new Matrix43();
3356
+ const matrix = new Matrix44();
3353
3357
  if (!scale || !rotation || !position) {
3354
3358
  this.getMatrixAt(index, matrix).decompose(oldPosition, oldRotation, oldScale);
3355
3359
  }
@@ -4104,7 +4108,7 @@ var init_ModelCulling = __esm({
4104
4108
  });
4105
4109
 
4106
4110
  // src/model/scene/ModelObject.ts
4107
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix47 } from "three";
4111
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix48 } from "three";
4108
4112
  function isModelObject(obj) {
4109
4113
  return obj.isModelObject;
4110
4114
  }
@@ -4183,12 +4187,12 @@ var init_ModelObject = __esm({
4183
4187
  }
4184
4188
  elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
4185
4189
  }
4186
- 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));
4187
- elGeo.applyMatrix4(new Matrix47().makeTranslation(el.from[0], el.from[1], el.from[2]));
4190
+ 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));
4191
+ elGeo.applyMatrix4(new Matrix48().makeTranslation(el.from[0], el.from[1], el.from[2]));
4188
4192
  if (el.rotation) {
4189
4193
  applyElementRotation(el.rotation, elGeo);
4190
4194
  }
4191
- elGeo.applyMatrix4(new Matrix47().makeTranslation(-8, -8, -8));
4195
+ elGeo.applyMatrix4(new Matrix48().makeTranslation(-8, -8, -8));
4192
4196
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4193
4197
  if (this.options.mergeMeshes) {
4194
4198
  allGeos.push(elGeo);
@@ -4797,6 +4801,59 @@ function shutdown() {
4797
4801
  init_AssetKey();
4798
4802
  init_AssetLoader();
4799
4803
  init_Assets();
4804
+
4805
+ // src/assets/BlockEntities.ts
4806
+ init_AssetKey();
4807
+ init_AssetLoader();
4808
+ init_source();
4809
+ init_Caching();
4810
+ import { Matrix4 as Matrix43 } from "three";
4811
+ var BlockEntities = class {
4812
+ /**
4813
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4814
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4815
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4816
+ */
4817
+ static async getIndex(root) {
4818
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4819
+ key.rootType = void 0;
4820
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4821
+ try {
4822
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4823
+ } catch (error) {
4824
+ console.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4825
+ return {};
4826
+ }
4827
+ });
4828
+ return index ?? {};
4829
+ }
4830
+ /** The index entry of a block ID such as `minecraft:chest`. */
4831
+ static entry(index, block) {
4832
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4833
+ return Array.isArray(entry?.parts) ? entry : void 0;
4834
+ }
4835
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4836
+ static resolve(index, block, state = {}) {
4837
+ const entry = this.entry(index, block);
4838
+ if (!entry) return void 0;
4839
+ const value = (property) => String(state[property]).toLowerCase();
4840
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4841
+ if (!parts.length) return void 0;
4842
+ let rotation = 0;
4843
+ if (entry.rotation) {
4844
+ const selected = value(entry.rotation.property);
4845
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4846
+ if (!Number.isFinite(rotation)) rotation = 0;
4847
+ }
4848
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4849
+ }
4850
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4851
+ static matrix(resolved, matrix = new Matrix43()) {
4852
+ 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));
4853
+ }
4854
+ };
4855
+
4856
+ // src/assets/index.ts
4800
4857
  init_BlockStates();
4801
4858
 
4802
4859
  // src/assets/Entities.ts
@@ -4867,6 +4924,15 @@ var Entities = class {
4867
4924
  })));
4868
4925
  return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
4869
4926
  }
4927
+ /**
4928
+ * Vanilla's keyframe animations of a model by name, or undefined when the model or the selected version has none.
4929
+ */
4930
+ static async getAnimations(modelKey) {
4931
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4932
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json");
4933
+ const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4934
+ return file?.animations;
4935
+ }
4870
4936
  static async resolveTexture(modelKey) {
4871
4937
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4872
4938
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
@@ -4943,6 +5009,119 @@ function trimCanvas(source) {
4943
5009
  return canvas;
4944
5010
  }
4945
5011
 
5012
+ // src/entity/EntityAnimation.ts
5013
+ var ENTITY_ANIMATION_CHANNELS = ["position", "rotation", "scale"];
5014
+ function catmullRom(delta, p0, p1, p22, p32) {
5015
+ 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);
5016
+ }
5017
+ function sampleEntityKeyframes(keyframes, time) {
5018
+ if (keyframes.length === 0) return [0, 0, 0];
5019
+ let low = 0, high = keyframes.length;
5020
+ while (low < high) {
5021
+ const middle = low + high >>> 1;
5022
+ if (time <= keyframes[middle].time) high = middle;
5023
+ else low = middle + 1;
5024
+ }
5025
+ const a = Math.max(0, low - 1);
5026
+ const b = Math.min(keyframes.length - 1, a + 1);
5027
+ const from = keyframes[a];
5028
+ const to = keyframes[b];
5029
+ const delta = a === b ? 0 : Math.min(1, Math.max(0, (time - from.time) / (to.time - from.time)));
5030
+ if (to.interpolation === "catmullrom") {
5031
+ const before = keyframes[Math.max(0, a - 1)].value;
5032
+ const after = keyframes[Math.min(keyframes.length - 1, b + 1)].value;
5033
+ return [0, 1, 2].map((i) => catmullRom(delta, before[i], from.value[i], to.value[i], after[i]));
5034
+ }
5035
+ const target = to.pre ?? to.value;
5036
+ return [0, 1, 2].map((i) => from.value[i] + (target[i] - from.value[i]) * delta);
5037
+ }
5038
+ function entityAnimationTime(animation, time, loop = animation.loop) {
5039
+ return loop && animation.length > 0 ? time % animation.length : time;
5040
+ }
5041
+ function sampleEntityAnimation(animation, time, loop = animation.loop) {
5042
+ const elapsed = entityAnimationTime(animation, time, loop);
5043
+ const pose = {};
5044
+ for (const [name, bone] of Object.entries(animation.bones)) {
5045
+ const sampled = pose[name] = {};
5046
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
5047
+ const keyframes = bone[channel];
5048
+ if (keyframes?.length) sampled[channel] = sampleEntityKeyframes(keyframes, elapsed);
5049
+ }
5050
+ }
5051
+ return pose;
5052
+ }
5053
+
5054
+ // src/entity/scene/EntityAnimationPlayer.ts
5055
+ var ZERO = [0, 0, 0];
5056
+ var EntityAnimationPlayer = class {
5057
+ constructor() {
5058
+ this.parts = [];
5059
+ this._time = 0;
5060
+ this.loop = false;
5061
+ this.speed = 1;
5062
+ }
5063
+ get animation() {
5064
+ return this._animation;
5065
+ }
5066
+ get time() {
5067
+ return this._time;
5068
+ }
5069
+ /**
5070
+ * Starts an animation on the named parts below each layer root. The current pose of a part becomes its default;
5071
+ * bones without a matching part are skipped.
5072
+ */
5073
+ play(layers, animation, options = {}) {
5074
+ this.stop();
5075
+ this._animation = animation;
5076
+ this._time = options.time ?? 0;
5077
+ this.loop = options.loop ?? animation.loop;
5078
+ this.speed = options.speed ?? 1;
5079
+ for (const layer of layers) {
5080
+ for (const bone of Object.keys(animation.bones)) {
5081
+ const object = layer.getObjectByName(`group:${bone}`);
5082
+ if (!object) continue;
5083
+ this.parts.push({ bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone() });
5084
+ }
5085
+ }
5086
+ this.apply();
5087
+ }
5088
+ /** Restores the default poses. Returns whether an animation was active. */
5089
+ stop() {
5090
+ if (!this._animation) return false;
5091
+ for (const part of this.parts) {
5092
+ part.object.position.copy(part.position);
5093
+ part.object.rotation.copy(part.rotation);
5094
+ part.object.scale.copy(part.scale);
5095
+ }
5096
+ this.clear();
5097
+ return true;
5098
+ }
5099
+ /** Forgets the animation without touching the parts, for parts that no longer exist. */
5100
+ clear() {
5101
+ this.parts = [];
5102
+ this._animation = void 0;
5103
+ this._time = 0;
5104
+ }
5105
+ setTime(seconds) {
5106
+ if (!this._animation) return false;
5107
+ this._time = seconds;
5108
+ this.apply();
5109
+ return true;
5110
+ }
5111
+ advance(deltaSeconds) {
5112
+ return this.setTime(this._time + deltaSeconds * this.speed);
5113
+ }
5114
+ apply() {
5115
+ const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
5116
+ for (const part of this.parts) {
5117
+ const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone];
5118
+ part.object.position.set(part.position.x + position[0], part.position.y + position[1], part.position.z + position[2]);
5119
+ part.object.rotation.set(part.rotation.x + rotation[0], part.rotation.y + rotation[1], part.rotation.z + rotation[2], part.rotation.order);
5120
+ part.object.scale.set(part.scale.x + scale[0], part.scale.y + scale[1], part.scale.z + scale[2]);
5121
+ }
5122
+ }
5123
+ };
5124
+
4946
5125
  // src/entity/scene/EntityObject.ts
4947
5126
  init_SceneObject();
4948
5127
  init_Caching();
@@ -4952,7 +5131,7 @@ init_model();
4952
5131
  init_ModelTextures();
4953
5132
  init_AssetKey();
4954
5133
  init_Materials();
4955
- import { Color as Color3, Euler as Euler4, Matrix4 as Matrix44, Object3D as Object3D3 } from "three";
5134
+ import { Color as Color3, Euler as Euler4, Matrix4 as Matrix45, Object3D as Object3D3 } from "three";
4956
5135
  init_three();
4957
5136
  var _EntityObject = class _EntityObject extends SceneObject {
4958
5137
  constructor(entity, options) {
@@ -4964,6 +5143,7 @@ var _EntityObject = class _EntityObject extends SceneObject {
4964
5143
  this.scrollMaterials = [];
4965
5144
  /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
4966
5145
  this.age = 0;
5146
+ this.animationPlayer = new EntityAnimationPlayer();
4967
5147
  this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
4968
5148
  }
4969
5149
  async init() {
@@ -4975,6 +5155,7 @@ var _EntityObject = class _EntityObject extends SceneObject {
4975
5155
  }
4976
5156
  disposeAndRemoveAllChildren() {
4977
5157
  this.clearScrollMaterials();
5158
+ this.animationPlayer.clear();
4978
5159
  super.disposeAndRemoveAllChildren();
4979
5160
  }
4980
5161
  clearScrollMaterials() {
@@ -4992,6 +5173,39 @@ var _EntityObject = class _EntityObject extends SceneObject {
4992
5173
  }
4993
5174
  this.notifyDirty();
4994
5175
  }
5176
+ //<editor-fold desc="ANIMATION">
5177
+ /** The animation started by {@link playAnimation}, until {@link stopAnimation} or disposal. */
5178
+ get animation() {
5179
+ return this.animationPlayer.animation;
5180
+ }
5181
+ /** Seconds since the animation started, before looping. */
5182
+ get animationTime() {
5183
+ return this.animationPlayer.time;
5184
+ }
5185
+ /**
5186
+ * Poses the object from a keyframe animation of {@link Entities.getAnimations}, replacing the current one.
5187
+ * Parts of every layer are matched by name and their current pose is the default the offsets add to.
5188
+ * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.
5189
+ */
5190
+ playAnimation(animation, options) {
5191
+ this.createMeshes();
5192
+ const layers = Object.keys(this.entityLayers).map((name) => this.getLayerGroup(name)).filter((layer) => !!layer);
5193
+ this.animationPlayer.play(layers, animation, options);
5194
+ this.notifyDirty();
5195
+ }
5196
+ /** Stops the animation and restores the default pose. */
5197
+ stopAnimation() {
5198
+ if (this.animationPlayer.stop()) this.notifyDirty();
5199
+ }
5200
+ /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
5201
+ setAnimationTime(seconds) {
5202
+ if (this.animationPlayer.setTime(seconds)) this.notifyDirty();
5203
+ }
5204
+ /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
5205
+ advanceAnimation(deltaSeconds) {
5206
+ if (this.animationPlayer.advance(deltaSeconds)) this.notifyDirty();
5207
+ }
5208
+ //</editor-fold>
4995
5209
  getLayerGroup(name) {
4996
5210
  return super.getGroupByName(`layer:${name}`);
4997
5211
  }
@@ -5025,7 +5239,7 @@ var _EntityObject = class _EntityObject extends SceneObject {
5025
5239
  const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
5026
5240
  if (transform) {
5027
5241
  for (const op of transform) {
5028
- const matrix = new Matrix44();
5242
+ const matrix = new Matrix45();
5029
5243
  if ("scale" in op) matrix.makeScale(...op.scale);
5030
5244
  else if ("translate" in op) matrix.makeTranslation(...op.translate);
5031
5245
  else matrix.makeRotationFromEuler(new Euler4(...op.rotate, "ZYX"));
@@ -5122,7 +5336,7 @@ function isEntityObject(obj) {
5122
5336
  init_MineRenderError();
5123
5337
 
5124
5338
  // src/export/SceneExporter.ts
5125
- 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";
5339
+ 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";
5126
5340
 
5127
5341
  // ../../node_modules/three/examples/jsm/exporters/OBJExporter.js
5128
5342
  import {
@@ -5548,7 +5762,7 @@ import {
5548
5762
  LinearMipmapLinearFilter,
5549
5763
  LinearMipmapNearestFilter,
5550
5764
  MathUtils as MathUtils2,
5551
- Matrix4 as Matrix45,
5765
+ Matrix4 as Matrix46,
5552
5766
  MirroredRepeatWrapping,
5553
5767
  NearestFilter as NearestFilter2,
5554
5768
  NearestMipmapLinearFilter,
@@ -6838,7 +7052,7 @@ var GLTFWriter = class {
6838
7052
  if (rootJoint === void 0) return null;
6839
7053
  const joints = [];
6840
7054
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
6841
- const temporaryBoneInverse = new Matrix45();
7055
+ const temporaryBoneInverse = new Matrix46();
6842
7056
  for (let i = 0; i < skeleton.bones.length; ++i) {
6843
7057
  joints.push(nodeMap.get(skeleton.bones[i]));
6844
7058
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
@@ -7304,7 +7518,7 @@ var GLTFMeshGpuInstancing = class {
7304
7518
  const translationAttr = new Float32Array(mesh.count * 3);
7305
7519
  const rotationAttr = new Float32Array(mesh.count * 4);
7306
7520
  const scaleAttr = new Float32Array(mesh.count * 3);
7307
- const matrix = new Matrix45();
7521
+ const matrix = new Matrix46();
7308
7522
  const position = new Vector38();
7309
7523
  const quaternion = new Quaternion4();
7310
7524
  const scale = new Vector38();
@@ -7478,7 +7692,7 @@ var SceneExporter = class {
7478
7692
  }
7479
7693
  };
7480
7694
  function localMatrix(object) {
7481
- return object.matrixAutoUpdate ? new Matrix46().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7695
+ return object.matrixAutoUpdate ? new Matrix47().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7482
7696
  }
7483
7697
  function createSnapshot(root, gltf, bakeColors = false) {
7484
7698
  const scene = new Scene4();
@@ -7590,7 +7804,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7590
7804
  const instanced = mesh;
7591
7805
  if (instanced.isInstancedMesh) {
7592
7806
  for (let i = 0; i < instanced.count; i++) {
7593
- const matrix = new Matrix46();
7807
+ const matrix = new Matrix47();
7594
7808
  instanced.getMatrixAt(i, matrix);
7595
7809
  const color = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
7596
7810
  addMesh(mesh, world.clone().multiply(matrix), color);
@@ -7604,7 +7818,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7604
7818
  try {
7605
7819
  const ancestors = [];
7606
7820
  for (let parent = root.parent; parent; parent = parent.parent) ancestors.unshift(parent);
7607
- const world = new Matrix46();
7821
+ const world = new Matrix47();
7608
7822
  for (const ancestor of ancestors) {
7609
7823
  if (!ancestor.visible) return { scene, dispose };
7610
7824
  world.multiply(localMatrix(ancestor));
@@ -8506,7 +8720,7 @@ var import_ts_deepmerge6 = __toESM(require_dist());
8506
8720
  init_MineRenderError();
8507
8721
  init_InstanceReference();
8508
8722
  init_AssetKey();
8509
- import { Euler as Euler8, Matrix4 as Matrix48, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8723
+ import { Euler as Euler8, Matrix4 as Matrix49, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8510
8724
  init_ModelCulling();
8511
8725
  init_FluidGeometry();
8512
8726
  var _BlockObject = class _BlockObject extends SceneObject {
@@ -8523,6 +8737,9 @@ var _BlockObject = class _BlockObject extends SceneObject {
8523
8737
  this._variants = [];
8524
8738
  this._models = [];
8525
8739
  this._cullMask = 0;
8740
+ this._entities = [];
8741
+ this._entityPlacement = new Matrix49();
8742
+ this._entityGeneration = 0;
8526
8743
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
8527
8744
  }
8528
8745
  async init() {
@@ -8542,6 +8759,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
8542
8759
  }
8543
8760
  clearModels() {
8544
8761
  this.removeModels(this._models.splice(0));
8762
+ this._entityGeneration++;
8763
+ this.removeEntities(this._entities.splice(0));
8545
8764
  this._fluidKey = void 0;
8546
8765
  this._fluidModel = void 0;
8547
8766
  this._isInstanced = false;
@@ -8553,6 +8772,57 @@ var _BlockObject = class _BlockObject extends SceneObject {
8553
8772
  if (!isInstanceReference(model)) model.dispose();
8554
8773
  }
8555
8774
  }
8775
+ removeEntities(entities) {
8776
+ for (const entity of entities) {
8777
+ entity.removeFromScene();
8778
+ entity.dispose();
8779
+ }
8780
+ }
8781
+ /** Entity models drawn for this block instead of its block model; they are scene children, unlike the block. */
8782
+ get blockEntities() {
8783
+ return this._entities;
8784
+ }
8785
+ /**
8786
+ * Draws the block through the dataset's block-entity index. Returns false, leaving nothing behind,
8787
+ * when the block is not listed or one of its models cannot be loaded.
8788
+ */
8789
+ async createBlockEntities() {
8790
+ const key = this.blockState.key;
8791
+ if (!key || this.options.displayPosition) return false;
8792
+ const generation = this._entityGeneration;
8793
+ const created = [];
8794
+ let resolved;
8795
+ try {
8796
+ resolved = BlockEntities.resolve(await BlockEntities.getIndex(key.root), key.toNamespacedString(), this.state);
8797
+ if (!resolved) return false;
8798
+ for (const part of resolved.parts) {
8799
+ const [namespace, path] = part.model.includes(":") ? part.model.split(":") : [key.namespace, part.model];
8800
+ const texture = part.textureLocation === void 0 ? void 0 : AssetKey.parse("textures", part.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
8801
+ if (texture) texture.root = key.root;
8802
+ const entity = await Entities.getEntity(new BasicAssetKey(namespace, path), texture, { layer: part.layer });
8803
+ if (!entity) throw new MineRenderError(`Missing entity model ${part.model}`);
8804
+ created.push(await this.scene.addEntity(entity, { wireframe: this.options.wireframe }));
8805
+ }
8806
+ } catch (error) {
8807
+ console.warn(`Could not draw block entity ${key.toNamespacedString()}, using its block model`, error);
8808
+ this.removeEntities(created);
8809
+ return false;
8810
+ }
8811
+ if (generation !== this._entityGeneration) {
8812
+ this.removeEntities(created);
8813
+ return true;
8814
+ }
8815
+ BlockEntities.matrix(resolved, this._entityPlacement);
8816
+ this._entities = created;
8817
+ this.placeEntities();
8818
+ return true;
8819
+ }
8820
+ placeEntities() {
8821
+ const matrix = new Matrix49().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
8822
+ for (const entity of this._entities) {
8823
+ matrix.decompose(entity.position, entity.quaternion, entity.scale);
8824
+ }
8825
+ }
8556
8826
  get isOccluding() {
8557
8827
  return this._models.some((model) => {
8558
8828
  const object = isInstanceReference(model) ? model.instanceable : model;
@@ -8634,7 +8904,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8634
8904
  () => new FluidModelObject2(kind, surface.sample, this.blockState.key, this.options)
8635
8905
  );
8636
8906
  const previous = this._fluidModel;
8637
- const matrix = previous ? this.getModelMatrix(previous) : new Matrix48().makeTranslation(...this.position.toArray());
8907
+ const matrix = previous ? this.getModelMatrix(previous) : new Matrix49().makeTranslation(...this.position.toArray());
8638
8908
  if (isInstanceReference(replacement)) replacement.setMatrix(matrix);
8639
8909
  else matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
8640
8910
  this._models = this._models.filter((model) => model !== previous);
@@ -8664,13 +8934,20 @@ var _BlockObject = class _BlockObject extends SceneObject {
8664
8934
  await this.updateFluid(this._fluidSampler);
8665
8935
  return;
8666
8936
  }
8937
+ const hasEntities = await this.createBlockEntities();
8667
8938
  const variantsToCreate = await this.mapStateToVariant(this.state);
8668
8939
  this._variants = variantsToCreate;
8669
8940
  for (let blockStateVariant of variantsToCreate) {
8941
+ if (hasEntities && !await this.hasGeometry(blockStateVariant)) continue;
8670
8942
  this._models.push(await this.createVariant(blockStateVariant));
8671
8943
  }
8672
8944
  await this.updateFluid(this._fluidSampler);
8673
8945
  }
8946
+ async hasGeometry(variant) {
8947
+ const modelKey = AssetKey.parse("models", variant.model);
8948
+ modelKey.root = this.blockState.key?.root;
8949
+ return !!(await Models.getMerged(modelKey))?.elements?.length;
8950
+ }
8674
8951
  getSingleVariant(variants) {
8675
8952
  return BlockStateResolver.choose(variants);
8676
8953
  }
@@ -8765,7 +9042,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8765
9042
  setPositionRotationScaleAt(index, position, rotation, scale) {
8766
9043
  super.setPositionRotationScaleAt(index, position, rotation, scale);
8767
9044
  }
8768
- getMatrixAt(index, matrix = new Matrix48()) {
9045
+ getMatrixAt(index, matrix = new Matrix49()) {
8769
9046
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
8770
9047
  const child = this._models[0];
8771
9048
  if (child && isModelObject(child)) {
@@ -8790,6 +9067,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8790
9067
  for (let model of this._models) {
8791
9068
  model.setPosition(position);
8792
9069
  }
9070
+ this.placeEntities();
8793
9071
  this.notifyDirty();
8794
9072
  }
8795
9073
  //TODO: should override rotation + scale methods
@@ -13329,7 +13607,7 @@ init_CubeFace();
13329
13607
  init_AssetKey();
13330
13608
  init_Models();
13331
13609
  init_CubeFace();
13332
- import { Matrix4 as Matrix49, Vector3 as Vector314 } from "three";
13610
+ import { Matrix4 as Matrix410, Vector3 as Vector314 } from "three";
13333
13611
  init_ModelCulling();
13334
13612
  init_ModelObject();
13335
13613
  init_UVMapper();
@@ -13392,7 +13670,7 @@ var SectionModels = class {
13392
13670
  });
13393
13671
  await object.init();
13394
13672
  const geometry = object.children[0].geometry;
13395
- geometry.applyMatrix4(new Matrix49().makeRotationFromEuler(rotation));
13673
+ geometry.applyMatrix4(new Matrix410().makeRotationFromEuler(rotation));
13396
13674
  object.clear();
13397
13675
  const cullFaces = CUBE_FACES.map((face) => {
13398
13676
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
@@ -13804,7 +14082,8 @@ var Chunk = class _Chunk {
13804
14082
  this.data.set(index, void 0);
13805
14083
  return void 0;
13806
14084
  }
13807
- const template = getFluidKind(blockState.key, stored.properties) ? void 0 : await this.sectionModels?.get(blockState, stored.properties);
14085
+ const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
14086
+ const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
13808
14087
  if (template) {
13809
14088
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
13810
14089
  this.meshDirty = true;
@@ -13913,6 +14192,7 @@ export {
13913
14192
  BUILTIN_GENERATED,
13914
14193
  BasicAssetKey,
13915
14194
  BatchedExecutor,
14195
+ BlockEntities,
13916
14196
  BlockObject,
13917
14197
  BlockStateResolver,
13918
14198
  BlockStates,
@@ -13936,7 +14216,9 @@ export {
13936
14216
  DefaultBlockStates,
13937
14217
  DisplayPosition,
13938
14218
  DisplayTransforms,
14219
+ ENTITY_ANIMATION_CHANNELS,
13939
14220
  Entities,
14221
+ EntityAnimationPlayer,
13940
14222
  EntityObject,
13941
14223
  Env,
13942
14224
  FluidModelObject,
@@ -14010,6 +14292,7 @@ export {
14010
14292
  createCanvas,
14011
14293
  createFluidGeometry,
14012
14294
  createImage,
14295
+ entityAnimationTime,
14013
14296
  estimateBytesUsed,
14014
14297
  getBlockFluidState,
14015
14298
  getFluidKind,
@@ -14043,6 +14326,8 @@ export {
14043
14326
  prefix,
14044
14327
  probeImageSize,
14045
14328
  registerBrowserEnv,
14329
+ sampleEntityAnimation,
14330
+ sampleEntityKeyframes,
14046
14331
  sampleFluid,
14047
14332
  serializeBoxGeometryKey,
14048
14333
  serializeImageKey,