minerender 2.1.0-alpha.54 → 2.1.0-alpha.59

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
  });
@@ -1725,6 +1729,11 @@ function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
1725
1729
  mesh.add(wireframe);
1726
1730
  let axes = new AxesHelper(1);
1727
1731
  mesh.add(axes);
1732
+ return () => {
1733
+ wireGeo.dispose();
1734
+ wireMat.dispose();
1735
+ axes.dispose();
1736
+ };
1728
1737
  }
1729
1738
  var init_model = __esm({
1730
1739
  "src/util/model.ts"() {
@@ -3079,7 +3088,7 @@ var init_InstanceReference = __esm({
3079
3088
  });
3080
3089
 
3081
3090
  // 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";
3091
+ 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
3092
  function isSceneObject(obj) {
3084
3093
  return obj.isSceneObject;
3085
3094
  }
@@ -3287,7 +3296,7 @@ var init_SceneObject = __esm({
3287
3296
  mesh.instanceColor.needsUpdate = true;
3288
3297
  }
3289
3298
  if (this._scene) this._scene.stats.instanceCount++;
3290
- this.setMatrixAt(index, new Matrix43());
3299
+ this.setMatrixAt(index, new Matrix44());
3291
3300
  return reference;
3292
3301
  }
3293
3302
  /** Returns the live reference for a raycast hit on this object's instance mesh. */
@@ -3300,7 +3309,7 @@ var init_SceneObject = __esm({
3300
3309
  removeInstanceAt(index) {
3301
3310
  if (!this.instanceReferences.has(index)) return;
3302
3311
  const mesh = this.instanceMesh;
3303
- this.setMatrixAt(index, new Matrix43().makeScale(0, 0, 0));
3312
+ this.setMatrixAt(index, new Matrix44().makeScale(0, 0, 0));
3304
3313
  this.instanceReferences.delete(index);
3305
3314
  this.freeInstanceIndices.push(index);
3306
3315
  this._instanceCounter = this.instanceReferences.size;
@@ -3330,7 +3339,7 @@ var init_SceneObject = __esm({
3330
3339
  }
3331
3340
  //</editor-fold>
3332
3341
  //<editor-fold desc="TRANSFORMATION">
3333
- getMatrixAt(index, matrix = new Matrix43()) {
3342
+ getMatrixAt(index, matrix = new Matrix44()) {
3334
3343
  if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3335
3344
  this.instanceMesh.getMatrixAt(index, matrix);
3336
3345
  return matrix;
@@ -3349,7 +3358,7 @@ var init_SceneObject = __esm({
3349
3358
  const oldPosition = new Vector35();
3350
3359
  const oldRotation = new Quaternion3();
3351
3360
  const oldScale = new Vector35();
3352
- const matrix = new Matrix43();
3361
+ const matrix = new Matrix44();
3353
3362
  if (!scale || !rotation || !position) {
3354
3363
  this.getMatrixAt(index, matrix).decompose(oldPosition, oldRotation, oldScale);
3355
3364
  }
@@ -4104,7 +4113,7 @@ var init_ModelCulling = __esm({
4104
4113
  });
4105
4114
 
4106
4115
  // src/model/scene/ModelObject.ts
4107
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix47 } from "three";
4116
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix48 } from "three";
4108
4117
  function isModelObject(obj) {
4109
4118
  return obj.isModelObject;
4110
4119
  }
@@ -4183,12 +4192,12 @@ var init_ModelObject = __esm({
4183
4192
  }
4184
4193
  elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
4185
4194
  }
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]));
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]));
4188
4197
  if (el.rotation) {
4189
4198
  applyElementRotation(el.rotation, elGeo);
4190
4199
  }
4191
- elGeo.applyMatrix4(new Matrix47().makeTranslation(-8, -8, -8));
4200
+ elGeo.applyMatrix4(new Matrix48().makeTranslation(-8, -8, -8));
4192
4201
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4193
4202
  if (this.options.mergeMeshes) {
4194
4203
  allGeos.push(elGeo);
@@ -4797,6 +4806,59 @@ function shutdown() {
4797
4806
  init_AssetKey();
4798
4807
  init_AssetLoader();
4799
4808
  init_Assets();
4809
+
4810
+ // src/assets/BlockEntities.ts
4811
+ init_AssetKey();
4812
+ init_AssetLoader();
4813
+ init_source();
4814
+ init_Caching();
4815
+ import { Matrix4 as Matrix43 } from "three";
4816
+ var BlockEntities = class {
4817
+ /**
4818
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4819
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4820
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4821
+ */
4822
+ static async getIndex(root) {
4823
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4824
+ key.rootType = void 0;
4825
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4826
+ try {
4827
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4828
+ } catch (error) {
4829
+ console.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4830
+ return {};
4831
+ }
4832
+ });
4833
+ return index ?? {};
4834
+ }
4835
+ /** The index entry of a block ID such as `minecraft:chest`. */
4836
+ static entry(index, block) {
4837
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4838
+ return Array.isArray(entry?.parts) ? entry : void 0;
4839
+ }
4840
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4841
+ static resolve(index, block, state = {}) {
4842
+ const entry = this.entry(index, block);
4843
+ if (!entry) return void 0;
4844
+ const value = (property) => String(state[property]).toLowerCase();
4845
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4846
+ if (!parts.length) return void 0;
4847
+ let rotation = 0;
4848
+ if (entry.rotation) {
4849
+ const selected = value(entry.rotation.property);
4850
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4851
+ if (!Number.isFinite(rotation)) rotation = 0;
4852
+ }
4853
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4854
+ }
4855
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4856
+ static matrix(resolved, matrix = new Matrix43()) {
4857
+ 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));
4858
+ }
4859
+ };
4860
+
4861
+ // src/assets/index.ts
4800
4862
  init_BlockStates();
4801
4863
 
4802
4864
  // src/assets/Entities.ts
@@ -4867,6 +4929,15 @@ var Entities = class {
4867
4929
  })));
4868
4930
  return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
4869
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
+ }
4870
4941
  static async resolveTexture(modelKey) {
4871
4942
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4872
4943
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
@@ -4943,6 +5014,119 @@ function trimCanvas(source) {
4943
5014
  return canvas;
4944
5015
  }
4945
5016
 
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
+ };
5129
+
4946
5130
  // src/entity/scene/EntityObject.ts
4947
5131
  init_SceneObject();
4948
5132
  init_Caching();
@@ -4952,7 +5136,7 @@ init_model();
4952
5136
  init_ModelTextures();
4953
5137
  init_AssetKey();
4954
5138
  init_Materials();
4955
- import { Color as Color3, Euler as Euler4, Matrix4 as Matrix44, Object3D as Object3D3 } from "three";
5139
+ import { Color as Color3, Euler as Euler4, Matrix4 as Matrix45, Object3D as Object3D3 } from "three";
4956
5140
  init_three();
4957
5141
  var _EntityObject = class _EntityObject extends SceneObject {
4958
5142
  constructor(entity, options) {
@@ -4960,11 +5144,16 @@ var _EntityObject = class _EntityObject extends SceneObject {
4960
5144
  this.entity = entity;
4961
5145
  this.isEntityObject = true;
4962
5146
  this.meshesCreated = false;
5147
+ this.geometries = /* @__PURE__ */ new Set();
5148
+ this.disposeWireframes = [];
4963
5149
  /** Scrolling materials are owned: their texture offset follows this entity's age. */
4964
5150
  this.scrollMaterials = [];
4965
5151
  /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
4966
5152
  this.age = 0;
5153
+ this.animationPlayer = new EntityAnimationPlayer();
4967
5154
  this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
5155
+ this.addEventListener("added", () => this.updateScrollSubscription());
5156
+ this.addEventListener("removed", () => this.updateScrollSubscription());
4968
5157
  }
4969
5158
  async init() {
4970
5159
  this.createMeshes();
@@ -4975,6 +5164,10 @@ var _EntityObject = class _EntityObject extends SceneObject {
4975
5164
  }
4976
5165
  disposeAndRemoveAllChildren() {
4977
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();
4978
5171
  super.disposeAndRemoveAllChildren();
4979
5172
  }
4980
5173
  clearScrollMaterials() {
@@ -4985,6 +5178,19 @@ var _EntityObject = class _EntityObject extends SceneObject {
4985
5178
  material.dispose();
4986
5179
  }
4987
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
+ }
4988
5194
  /** Applies the entity age to the scrolling materials; the ticker calls this 20 times per second. */
4989
5195
  updateScroll() {
4990
5196
  for (const { offset, speed } of this.scrollMaterials) {
@@ -4992,6 +5198,39 @@ var _EntityObject = class _EntityObject extends SceneObject {
4992
5198
  }
4993
5199
  this.notifyDirty();
4994
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>
4995
5234
  getLayerGroup(name) {
4996
5235
  return super.getGroupByName(`layer:${name}`);
4997
5236
  }
@@ -5025,7 +5264,7 @@ var _EntityObject = class _EntityObject extends SceneObject {
5025
5264
  const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
5026
5265
  if (transform) {
5027
5266
  for (const op of transform) {
5028
- const matrix = new Matrix44();
5267
+ const matrix = new Matrix45();
5029
5268
  if ("scale" in op) matrix.makeScale(...op.scale);
5030
5269
  else if ("translate" in op) matrix.makeTranslation(...op.translate);
5031
5270
  else matrix.makeRotationFromEuler(new Euler4(...op.rotate, "ZYX"));
@@ -5062,6 +5301,7 @@ var _EntityObject = class _EntityObject extends SceneObject {
5062
5301
  depth + growZ * 2,
5063
5302
  uv
5064
5303
  ).clone();
5304
+ this.geometries.add(geometry);
5065
5305
  geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);
5066
5306
  if (inward && Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {
5067
5307
  const index = Array.from(geometry.getIndex().array);
@@ -5070,7 +5310,7 @@ var _EntityObject = class _EntityObject extends SceneObject {
5070
5310
  const mesh = this.createMesh(name, geometry, material);
5071
5311
  mesh.renderOrder = renderOrder;
5072
5312
  anchor.add(mesh);
5073
- if (this.options.wireframe) addWireframeToMesh(geometry, mesh);
5313
+ if (this.options.wireframe) this.disposeWireframes.push(addWireframeToMesh(geometry, mesh));
5074
5314
  }
5075
5315
  for (const [childName, child] of Object.entries(part.children)) {
5076
5316
  this.createPart(childName, child, anchor, size, material, renderOrder, inward);
@@ -5104,11 +5344,8 @@ var _EntityObject = class _EntityObject extends SceneObject {
5104
5344
  this.clearScrollMaterials();
5105
5345
  } else if (this.scrollMaterials.length) {
5106
5346
  this.updateScroll();
5107
- this.scrollTicker = Ticker.add(() => {
5108
- this.age++;
5109
- this.updateScroll();
5110
- });
5111
5347
  }
5348
+ this.updateScrollSubscription();
5112
5349
  this.notifyDirty();
5113
5350
  }
5114
5351
  };
@@ -5122,7 +5359,7 @@ function isEntityObject(obj) {
5122
5359
  init_MineRenderError();
5123
5360
 
5124
5361
  // 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";
5362
+ 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
5363
 
5127
5364
  // ../../node_modules/three/examples/jsm/exporters/OBJExporter.js
5128
5365
  import {
@@ -5548,7 +5785,7 @@ import {
5548
5785
  LinearMipmapLinearFilter,
5549
5786
  LinearMipmapNearestFilter,
5550
5787
  MathUtils as MathUtils2,
5551
- Matrix4 as Matrix45,
5788
+ Matrix4 as Matrix46,
5552
5789
  MirroredRepeatWrapping,
5553
5790
  NearestFilter as NearestFilter2,
5554
5791
  NearestMipmapLinearFilter,
@@ -6838,7 +7075,7 @@ var GLTFWriter = class {
6838
7075
  if (rootJoint === void 0) return null;
6839
7076
  const joints = [];
6840
7077
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
6841
- const temporaryBoneInverse = new Matrix45();
7078
+ const temporaryBoneInverse = new Matrix46();
6842
7079
  for (let i = 0; i < skeleton.bones.length; ++i) {
6843
7080
  joints.push(nodeMap.get(skeleton.bones[i]));
6844
7081
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
@@ -7304,7 +7541,7 @@ var GLTFMeshGpuInstancing = class {
7304
7541
  const translationAttr = new Float32Array(mesh.count * 3);
7305
7542
  const rotationAttr = new Float32Array(mesh.count * 4);
7306
7543
  const scaleAttr = new Float32Array(mesh.count * 3);
7307
- const matrix = new Matrix45();
7544
+ const matrix = new Matrix46();
7308
7545
  const position = new Vector38();
7309
7546
  const quaternion = new Quaternion4();
7310
7547
  const scale = new Vector38();
@@ -7478,7 +7715,7 @@ var SceneExporter = class {
7478
7715
  }
7479
7716
  };
7480
7717
  function localMatrix(object) {
7481
- return object.matrixAutoUpdate ? new Matrix46().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7718
+ return object.matrixAutoUpdate ? new Matrix47().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7482
7719
  }
7483
7720
  function createSnapshot(root, gltf, bakeColors = false) {
7484
7721
  const scene = new Scene4();
@@ -7590,7 +7827,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7590
7827
  const instanced = mesh;
7591
7828
  if (instanced.isInstancedMesh) {
7592
7829
  for (let i = 0; i < instanced.count; i++) {
7593
- const matrix = new Matrix46();
7830
+ const matrix = new Matrix47();
7594
7831
  instanced.getMatrixAt(i, matrix);
7595
7832
  const color = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
7596
7833
  addMesh(mesh, world.clone().multiply(matrix), color);
@@ -7604,7 +7841,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7604
7841
  try {
7605
7842
  const ancestors = [];
7606
7843
  for (let parent = root.parent; parent; parent = parent.parent) ancestors.unshift(parent);
7607
- const world = new Matrix46();
7844
+ const world = new Matrix47();
7608
7845
  for (const ancestor of ancestors) {
7609
7846
  if (!ancestor.visible) return { scene, dispose };
7610
7847
  world.multiply(localMatrix(ancestor));
@@ -8506,7 +8743,7 @@ var import_ts_deepmerge6 = __toESM(require_dist());
8506
8743
  init_MineRenderError();
8507
8744
  init_InstanceReference();
8508
8745
  init_AssetKey();
8509
- import { Euler as Euler8, Matrix4 as Matrix48, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8746
+ import { Euler as Euler8, Matrix4 as Matrix49, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8510
8747
  init_ModelCulling();
8511
8748
  init_FluidGeometry();
8512
8749
  var _BlockObject = class _BlockObject extends SceneObject {
@@ -8523,6 +8760,9 @@ var _BlockObject = class _BlockObject extends SceneObject {
8523
8760
  this._variants = [];
8524
8761
  this._models = [];
8525
8762
  this._cullMask = 0;
8763
+ this._entities = [];
8764
+ this._entityPlacement = new Matrix49();
8765
+ this._entityGeneration = 0;
8526
8766
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
8527
8767
  }
8528
8768
  async init() {
@@ -8542,6 +8782,8 @@ var _BlockObject = class _BlockObject extends SceneObject {
8542
8782
  }
8543
8783
  clearModels() {
8544
8784
  this.removeModels(this._models.splice(0));
8785
+ this._entityGeneration++;
8786
+ this.removeEntities(this._entities.splice(0));
8545
8787
  this._fluidKey = void 0;
8546
8788
  this._fluidModel = void 0;
8547
8789
  this._isInstanced = false;
@@ -8553,6 +8795,57 @@ var _BlockObject = class _BlockObject extends SceneObject {
8553
8795
  if (!isInstanceReference(model)) model.dispose();
8554
8796
  }
8555
8797
  }
8798
+ removeEntities(entities) {
8799
+ for (const entity of entities) {
8800
+ entity.removeFromScene();
8801
+ entity.dispose();
8802
+ }
8803
+ }
8804
+ /** Entity models drawn for this block instead of its block model; they are scene children, unlike the block. */
8805
+ get blockEntities() {
8806
+ return this._entities;
8807
+ }
8808
+ /**
8809
+ * Draws the block through the dataset's block-entity index. Returns false, leaving nothing behind,
8810
+ * when the block is not listed or one of its models cannot be loaded.
8811
+ */
8812
+ async createBlockEntities() {
8813
+ const key = this.blockState.key;
8814
+ if (!key || this.options.displayPosition) return false;
8815
+ const generation = this._entityGeneration;
8816
+ const created = [];
8817
+ let resolved;
8818
+ try {
8819
+ resolved = BlockEntities.resolve(await BlockEntities.getIndex(key.root), key.toNamespacedString(), this.state);
8820
+ if (!resolved) return false;
8821
+ for (const part of resolved.parts) {
8822
+ const [namespace, path] = part.model.includes(":") ? part.model.split(":") : [key.namespace, part.model];
8823
+ const texture = part.textureLocation === void 0 ? void 0 : AssetKey.parse("textures", part.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
8824
+ if (texture) texture.root = key.root;
8825
+ const entity = await Entities.getEntity(new BasicAssetKey(namespace, path), texture, { layer: part.layer });
8826
+ if (!entity) throw new MineRenderError(`Missing entity model ${part.model}`);
8827
+ created.push(await this.scene.addEntity(entity, { wireframe: this.options.wireframe }));
8828
+ }
8829
+ } catch (error) {
8830
+ console.warn(`Could not draw block entity ${key.toNamespacedString()}, using its block model`, error);
8831
+ this.removeEntities(created);
8832
+ return false;
8833
+ }
8834
+ if (generation !== this._entityGeneration) {
8835
+ this.removeEntities(created);
8836
+ return true;
8837
+ }
8838
+ BlockEntities.matrix(resolved, this._entityPlacement);
8839
+ this._entities = created;
8840
+ this.placeEntities();
8841
+ return true;
8842
+ }
8843
+ placeEntities() {
8844
+ const matrix = new Matrix49().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
8845
+ for (const entity of this._entities) {
8846
+ matrix.decompose(entity.position, entity.quaternion, entity.scale);
8847
+ }
8848
+ }
8556
8849
  get isOccluding() {
8557
8850
  return this._models.some((model) => {
8558
8851
  const object = isInstanceReference(model) ? model.instanceable : model;
@@ -8634,7 +8927,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8634
8927
  () => new FluidModelObject2(kind, surface.sample, this.blockState.key, this.options)
8635
8928
  );
8636
8929
  const previous = this._fluidModel;
8637
- const matrix = previous ? this.getModelMatrix(previous) : new Matrix48().makeTranslation(...this.position.toArray());
8930
+ const matrix = previous ? this.getModelMatrix(previous) : new Matrix49().makeTranslation(...this.position.toArray());
8638
8931
  if (isInstanceReference(replacement)) replacement.setMatrix(matrix);
8639
8932
  else matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
8640
8933
  this._models = this._models.filter((model) => model !== previous);
@@ -8664,13 +8957,20 @@ var _BlockObject = class _BlockObject extends SceneObject {
8664
8957
  await this.updateFluid(this._fluidSampler);
8665
8958
  return;
8666
8959
  }
8960
+ const hasEntities = await this.createBlockEntities();
8667
8961
  const variantsToCreate = await this.mapStateToVariant(this.state);
8668
8962
  this._variants = variantsToCreate;
8669
8963
  for (let blockStateVariant of variantsToCreate) {
8964
+ if (hasEntities && !await this.hasGeometry(blockStateVariant)) continue;
8670
8965
  this._models.push(await this.createVariant(blockStateVariant));
8671
8966
  }
8672
8967
  await this.updateFluid(this._fluidSampler);
8673
8968
  }
8969
+ async hasGeometry(variant) {
8970
+ const modelKey = AssetKey.parse("models", variant.model);
8971
+ modelKey.root = this.blockState.key?.root;
8972
+ return !!(await Models.getMerged(modelKey))?.elements?.length;
8973
+ }
8674
8974
  getSingleVariant(variants) {
8675
8975
  return BlockStateResolver.choose(variants);
8676
8976
  }
@@ -8765,7 +9065,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8765
9065
  setPositionRotationScaleAt(index, position, rotation, scale) {
8766
9066
  super.setPositionRotationScaleAt(index, position, rotation, scale);
8767
9067
  }
8768
- getMatrixAt(index, matrix = new Matrix48()) {
9068
+ getMatrixAt(index, matrix = new Matrix49()) {
8769
9069
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
8770
9070
  const child = this._models[0];
8771
9071
  if (child && isModelObject(child)) {
@@ -8790,6 +9090,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
8790
9090
  for (let model of this._models) {
8791
9091
  model.setPosition(position);
8792
9092
  }
9093
+ this.placeEntities();
8793
9094
  this.notifyDirty();
8794
9095
  }
8795
9096
  //TODO: should override rotation + scale methods
@@ -13329,7 +13630,7 @@ init_CubeFace();
13329
13630
  init_AssetKey();
13330
13631
  init_Models();
13331
13632
  init_CubeFace();
13332
- import { Matrix4 as Matrix49, Vector3 as Vector314 } from "three";
13633
+ import { Matrix4 as Matrix410, Vector3 as Vector314 } from "three";
13333
13634
  init_ModelCulling();
13334
13635
  init_ModelObject();
13335
13636
  init_UVMapper();
@@ -13392,7 +13693,7 @@ var SectionModels = class {
13392
13693
  });
13393
13694
  await object.init();
13394
13695
  const geometry = object.children[0].geometry;
13395
- geometry.applyMatrix4(new Matrix49().makeRotationFromEuler(rotation));
13696
+ geometry.applyMatrix4(new Matrix410().makeRotationFromEuler(rotation));
13396
13697
  object.clear();
13397
13698
  const cullFaces = CUBE_FACES.map((face) => {
13398
13699
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
@@ -13804,7 +14105,8 @@ var Chunk = class _Chunk {
13804
14105
  this.data.set(index, void 0);
13805
14106
  return void 0;
13806
14107
  }
13807
- const template = getFluidKind(blockState.key, stored.properties) ? void 0 : await this.sectionModels?.get(blockState, stored.properties);
14108
+ const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
14109
+ const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
13808
14110
  if (template) {
13809
14111
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
13810
14112
  this.meshDirty = true;
@@ -13913,6 +14215,7 @@ export {
13913
14215
  BUILTIN_GENERATED,
13914
14216
  BasicAssetKey,
13915
14217
  BatchedExecutor,
14218
+ BlockEntities,
13916
14219
  BlockObject,
13917
14220
  BlockStateResolver,
13918
14221
  BlockStates,
@@ -13936,7 +14239,9 @@ export {
13936
14239
  DefaultBlockStates,
13937
14240
  DisplayPosition,
13938
14241
  DisplayTransforms,
14242
+ ENTITY_ANIMATION_CHANNELS,
13939
14243
  Entities,
14244
+ EntityAnimationPlayer,
13940
14245
  EntityObject,
13941
14246
  Env,
13942
14247
  FluidModelObject,
@@ -14010,6 +14315,7 @@ export {
14010
14315
  createCanvas,
14011
14316
  createFluidGeometry,
14012
14317
  createImage,
14318
+ entityAnimationTime,
14013
14319
  estimateBytesUsed,
14014
14320
  getBlockFluidState,
14015
14321
  getFluidKind,
@@ -14043,6 +14349,8 @@ export {
14043
14349
  prefix,
14044
14350
  probeImageSize,
14045
14351
  registerBrowserEnv,
14352
+ sampleEntityAnimation,
14353
+ sampleEntityKeyframes,
14046
14354
  sampleFluid,
14047
14355
  serializeBoxGeometryKey,
14048
14356
  serializeImageKey,