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.
@@ -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() {
@@ -1770,6 +1778,11 @@ function addWireframeToMesh(geo, mesh, color = 16777215, w = 2) {
1770
1778
  mesh.add(wireframe);
1771
1779
  let axes = new AxesHelper(1);
1772
1780
  mesh.add(axes);
1781
+ return () => {
1782
+ wireGeo.dispose();
1783
+ wireMat.dispose();
1784
+ axes.dispose();
1785
+ };
1773
1786
  }
1774
1787
  var init_model = __esm({
1775
1788
  "src/util/model.ts"() {
@@ -3161,7 +3174,7 @@ var init_InstanceReference = __esm({
3161
3174
  });
3162
3175
 
3163
3176
  // 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";
3177
+ 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
3178
  function isSceneObject(obj) {
3166
3179
  return obj.isSceneObject;
3167
3180
  }
@@ -3377,7 +3390,7 @@ var init_SceneObject = __esm({
3377
3390
  mesh.instanceColor.needsUpdate = true;
3378
3391
  }
3379
3392
  if (this._scene) this._scene.stats.instanceCount++;
3380
- this.setMatrixAt(index, new Matrix43());
3393
+ this.setMatrixAt(index, new Matrix44());
3381
3394
  return reference;
3382
3395
  }
3383
3396
  /** Returns the live reference for a raycast hit on this object's instance mesh. */
@@ -3390,7 +3403,7 @@ var init_SceneObject = __esm({
3390
3403
  removeInstanceAt(index) {
3391
3404
  if (!this.instanceReferences.has(index)) return;
3392
3405
  const mesh = this.instanceMesh;
3393
- this.setMatrixAt(index, new Matrix43().makeScale(0, 0, 0));
3406
+ this.setMatrixAt(index, new Matrix44().makeScale(0, 0, 0));
3394
3407
  this.instanceReferences.delete(index);
3395
3408
  this.freeInstanceIndices.push(index);
3396
3409
  this._instanceCounter = this.instanceReferences.size;
@@ -3420,7 +3433,7 @@ var init_SceneObject = __esm({
3420
3433
  }
3421
3434
  //</editor-fold>
3422
3435
  //<editor-fold desc="TRANSFORMATION">
3423
- getMatrixAt(index, matrix = new Matrix43()) {
3436
+ getMatrixAt(index, matrix = new Matrix44()) {
3424
3437
  if (!this.instanceReferences.has(index)) throw new MineRenderError("Instance is not active");
3425
3438
  this.instanceMesh.getMatrixAt(index, matrix);
3426
3439
  return matrix;
@@ -3439,7 +3452,7 @@ var init_SceneObject = __esm({
3439
3452
  const oldPosition = new Vector35();
3440
3453
  const oldRotation = new Quaternion3();
3441
3454
  const oldScale = new Vector35();
3442
- const matrix = new Matrix43();
3455
+ const matrix = new Matrix44();
3443
3456
  if (!scale || !rotation || !position) {
3444
3457
  this.getMatrixAt(index, matrix).decompose(oldPosition, oldRotation, oldScale);
3445
3458
  }
@@ -4187,7 +4200,7 @@ var init_ModelCulling = __esm({
4187
4200
  });
4188
4201
 
4189
4202
  // src/model/scene/ModelObject.ts
4190
- import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix47 } from "three";
4203
+ import { BoxGeometry as BoxGeometry5, BufferAttribute as BufferAttribute5, Color as Color8, Euler as Euler6, Matrix4 as Matrix48 } from "three";
4191
4204
  function isModelObject(obj) {
4192
4205
  return obj.isModelObject;
4193
4206
  }
@@ -4269,12 +4282,12 @@ var init_ModelObject = __esm({
4269
4282
  }
4270
4283
  elGeo.setAttribute("color", new BufferAttribute5(colors, 3));
4271
4284
  }
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]));
4285
+ 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));
4286
+ elGeo.applyMatrix4(new Matrix48().makeTranslation(el.from[0], el.from[1], el.from[2]));
4274
4287
  if (el.rotation) {
4275
4288
  applyElementRotation(el.rotation, elGeo);
4276
4289
  }
4277
- elGeo.applyMatrix4(new Matrix47().makeTranslation(-8, -8, -8));
4290
+ elGeo.applyMatrix4(new Matrix48().makeTranslation(-8, -8, -8));
4278
4291
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4279
4292
  if (this.options.mergeMeshes) {
4280
4293
  allGeos.push(elGeo);
@@ -4843,6 +4856,59 @@ function shutdown() {
4843
4856
  init_AssetKey();
4844
4857
  init_AssetLoader();
4845
4858
  init_Assets();
4859
+
4860
+ // src/assets/BlockEntities.ts
4861
+ init_AssetKey();
4862
+ init_AssetLoader();
4863
+ init_source();
4864
+ init_Caching();
4865
+ import { Matrix4 as Matrix43 } from "three";
4866
+ var BlockEntities = class {
4867
+ /**
4868
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
4869
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
4870
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
4871
+ */
4872
+ static async getIndex(root) {
4873
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
4874
+ key.rootType = void 0;
4875
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
4876
+ try {
4877
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
4878
+ } catch (error) {
4879
+ console.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
4880
+ return {};
4881
+ }
4882
+ });
4883
+ return index ?? {};
4884
+ }
4885
+ /** The index entry of a block ID such as `minecraft:chest`. */
4886
+ static entry(index, block) {
4887
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
4888
+ return Array.isArray(entry?.parts) ? entry : void 0;
4889
+ }
4890
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
4891
+ static resolve(index, block, state = {}) {
4892
+ const entry = this.entry(index, block);
4893
+ if (!entry) return void 0;
4894
+ const value = (property) => String(state[property]).toLowerCase();
4895
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
4896
+ if (!parts.length) return void 0;
4897
+ let rotation = 0;
4898
+ if (entry.rotation) {
4899
+ const selected = value(entry.rotation.property);
4900
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
4901
+ if (!Number.isFinite(rotation)) rotation = 0;
4902
+ }
4903
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
4904
+ }
4905
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
4906
+ static matrix(resolved, matrix = new Matrix43()) {
4907
+ 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));
4908
+ }
4909
+ };
4910
+
4911
+ // src/assets/index.ts
4846
4912
  init_BlockStates();
4847
4913
 
4848
4914
  // src/assets/Entities.ts
@@ -4913,6 +4979,15 @@ var Entities = class {
4913
4979
  })));
4914
4980
  return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
4915
4981
  }
4982
+ /**
4983
+ * Vanilla's keyframe animations of a model by name, or undefined when the model or the selected version has none.
4984
+ */
4985
+ static async getAnimations(modelKey) {
4986
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4987
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json");
4988
+ const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
4989
+ return file?.animations;
4990
+ }
4916
4991
  static async resolveTexture(modelKey) {
4917
4992
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
4918
4993
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
@@ -4989,6 +5064,119 @@ function trimCanvas(source) {
4989
5064
  return canvas;
4990
5065
  }
4991
5066
 
5067
+ // src/entity/EntityAnimation.ts
5068
+ var ENTITY_ANIMATION_CHANNELS = ["position", "rotation", "scale"];
5069
+ function catmullRom(delta, p0, p1, p22, p32) {
5070
+ 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);
5071
+ }
5072
+ function sampleEntityKeyframes(keyframes, time) {
5073
+ if (keyframes.length === 0) return [0, 0, 0];
5074
+ let low = 0, high = keyframes.length;
5075
+ while (low < high) {
5076
+ const middle = low + high >>> 1;
5077
+ if (time <= keyframes[middle].time) high = middle;
5078
+ else low = middle + 1;
5079
+ }
5080
+ const a = Math.max(0, low - 1);
5081
+ const b = Math.min(keyframes.length - 1, a + 1);
5082
+ const from = keyframes[a];
5083
+ const to = keyframes[b];
5084
+ const delta = a === b ? 0 : Math.min(1, Math.max(0, (time - from.time) / (to.time - from.time)));
5085
+ if (to.interpolation === "catmullrom") {
5086
+ const before = keyframes[Math.max(0, a - 1)].value;
5087
+ const after = keyframes[Math.min(keyframes.length - 1, b + 1)].value;
5088
+ return [0, 1, 2].map((i) => catmullRom(delta, before[i], from.value[i], to.value[i], after[i]));
5089
+ }
5090
+ const target = to.pre ?? to.value;
5091
+ return [0, 1, 2].map((i) => from.value[i] + (target[i] - from.value[i]) * delta);
5092
+ }
5093
+ function entityAnimationTime(animation, time, loop = animation.loop) {
5094
+ return loop && animation.length > 0 ? time % animation.length : time;
5095
+ }
5096
+ function sampleEntityAnimation(animation, time, loop = animation.loop) {
5097
+ const elapsed = entityAnimationTime(animation, time, loop);
5098
+ const pose = {};
5099
+ for (const [name, bone] of Object.entries(animation.bones)) {
5100
+ const sampled = pose[name] = {};
5101
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
5102
+ const keyframes = bone[channel];
5103
+ if (keyframes?.length) sampled[channel] = sampleEntityKeyframes(keyframes, elapsed);
5104
+ }
5105
+ }
5106
+ return pose;
5107
+ }
5108
+
5109
+ // src/entity/scene/EntityAnimationPlayer.ts
5110
+ var ZERO = [0, 0, 0];
5111
+ var EntityAnimationPlayer = class {
5112
+ constructor() {
5113
+ this.parts = [];
5114
+ this._time = 0;
5115
+ this.loop = false;
5116
+ this.speed = 1;
5117
+ }
5118
+ get animation() {
5119
+ return this._animation;
5120
+ }
5121
+ get time() {
5122
+ return this._time;
5123
+ }
5124
+ /**
5125
+ * Starts an animation on the named parts below each layer root. The current pose of a part becomes its default;
5126
+ * bones without a matching part are skipped.
5127
+ */
5128
+ play(layers, animation, options = {}) {
5129
+ this.stop();
5130
+ this._animation = animation;
5131
+ this._time = options.time ?? 0;
5132
+ this.loop = options.loop ?? animation.loop;
5133
+ this.speed = options.speed ?? 1;
5134
+ for (const layer of layers) {
5135
+ for (const bone of Object.keys(animation.bones)) {
5136
+ const object = layer.getObjectByName(`group:${bone}`);
5137
+ if (!object) continue;
5138
+ this.parts.push({ bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone() });
5139
+ }
5140
+ }
5141
+ this.apply();
5142
+ }
5143
+ /** Restores the default poses. Returns whether an animation was active. */
5144
+ stop() {
5145
+ if (!this._animation) return false;
5146
+ for (const part of this.parts) {
5147
+ part.object.position.copy(part.position);
5148
+ part.object.rotation.copy(part.rotation);
5149
+ part.object.scale.copy(part.scale);
5150
+ }
5151
+ this.clear();
5152
+ return true;
5153
+ }
5154
+ /** Forgets the animation without touching the parts, for parts that no longer exist. */
5155
+ clear() {
5156
+ this.parts = [];
5157
+ this._animation = void 0;
5158
+ this._time = 0;
5159
+ }
5160
+ setTime(seconds) {
5161
+ if (!this._animation) return false;
5162
+ this._time = seconds;
5163
+ this.apply();
5164
+ return true;
5165
+ }
5166
+ advance(deltaSeconds) {
5167
+ return this.setTime(this._time + deltaSeconds * this.speed);
5168
+ }
5169
+ apply() {
5170
+ const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
5171
+ for (const part of this.parts) {
5172
+ const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone];
5173
+ part.object.position.set(part.position.x + position[0], part.position.y + position[1], part.position.z + position[2]);
5174
+ part.object.rotation.set(part.rotation.x + rotation[0], part.rotation.y + rotation[1], part.rotation.z + rotation[2], part.rotation.order);
5175
+ part.object.scale.set(part.scale.x + scale[0], part.scale.y + scale[1], part.scale.z + scale[2]);
5176
+ }
5177
+ }
5178
+ };
5179
+
4992
5180
  // src/entity/scene/EntityObject.ts
4993
5181
  init_SceneObject();
4994
5182
  init_Caching();
@@ -4998,7 +5186,7 @@ init_model();
4998
5186
  init_ModelTextures();
4999
5187
  init_AssetKey();
5000
5188
  init_Materials();
5001
- import { Color as Color3, Euler as Euler4, Matrix4 as Matrix44, Object3D as Object3D3 } from "three";
5189
+ import { Color as Color3, Euler as Euler4, Matrix4 as Matrix45, Object3D as Object3D3 } from "three";
5002
5190
  init_three();
5003
5191
  var EntityObject = class _EntityObject extends SceneObject {
5004
5192
  constructor(entity, options) {
@@ -5006,11 +5194,16 @@ var EntityObject = class _EntityObject extends SceneObject {
5006
5194
  this.entity = entity;
5007
5195
  this.isEntityObject = true;
5008
5196
  this.meshesCreated = false;
5197
+ this.geometries = /* @__PURE__ */ new Set();
5198
+ this.disposeWireframes = [];
5009
5199
  /** Scrolling materials are owned: their texture offset follows this entity's age. */
5010
5200
  this.scrollMaterials = [];
5011
5201
  /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
5012
5202
  this.age = 0;
5203
+ this.animationPlayer = new EntityAnimationPlayer();
5013
5204
  this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
5205
+ this.addEventListener("added", () => this.updateScrollSubscription());
5206
+ this.addEventListener("removed", () => this.updateScrollSubscription());
5014
5207
  }
5015
5208
  static {
5016
5209
  this.DEFAULT_OPTIONS = (0, import_ts_deepmerge4.default)({}, SceneObject.DEFAULT_OPTIONS);
@@ -5024,6 +5217,10 @@ var EntityObject = class _EntityObject extends SceneObject {
5024
5217
  }
5025
5218
  disposeAndRemoveAllChildren() {
5026
5219
  this.clearScrollMaterials();
5220
+ for (const geometry of this.geometries) geometry.dispose();
5221
+ this.geometries.clear();
5222
+ for (const dispose of this.disposeWireframes.splice(0)) dispose();
5223
+ this.animationPlayer.clear();
5027
5224
  super.disposeAndRemoveAllChildren();
5028
5225
  }
5029
5226
  clearScrollMaterials() {
@@ -5034,6 +5231,19 @@ var EntityObject = class _EntityObject extends SceneObject {
5034
5231
  material.dispose();
5035
5232
  }
5036
5233
  }
5234
+ updateScrollSubscription() {
5235
+ if (this.parent && this.scrollMaterials.length) {
5236
+ if (this.scrollTicker === void 0) {
5237
+ this.scrollTicker = Ticker.add(() => {
5238
+ this.age++;
5239
+ this.updateScroll();
5240
+ });
5241
+ }
5242
+ } else {
5243
+ Ticker.remove(this.scrollTicker);
5244
+ this.scrollTicker = void 0;
5245
+ }
5246
+ }
5037
5247
  /** Applies the entity age to the scrolling materials; the ticker calls this 20 times per second. */
5038
5248
  updateScroll() {
5039
5249
  for (const { offset, speed } of this.scrollMaterials) {
@@ -5041,6 +5251,39 @@ var EntityObject = class _EntityObject extends SceneObject {
5041
5251
  }
5042
5252
  this.notifyDirty();
5043
5253
  }
5254
+ //<editor-fold desc="ANIMATION">
5255
+ /** The animation started by {@link playAnimation}, until {@link stopAnimation} or disposal. */
5256
+ get animation() {
5257
+ return this.animationPlayer.animation;
5258
+ }
5259
+ /** Seconds since the animation started, before looping. */
5260
+ get animationTime() {
5261
+ return this.animationPlayer.time;
5262
+ }
5263
+ /**
5264
+ * Poses the object from a keyframe animation of {@link Entities.getAnimations}, replacing the current one.
5265
+ * Parts of every layer are matched by name and their current pose is the default the offsets add to.
5266
+ * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.
5267
+ */
5268
+ playAnimation(animation, options) {
5269
+ this.createMeshes();
5270
+ const layers = Object.keys(this.entityLayers).map((name) => this.getLayerGroup(name)).filter((layer) => !!layer);
5271
+ this.animationPlayer.play(layers, animation, options);
5272
+ this.notifyDirty();
5273
+ }
5274
+ /** Stops the animation and restores the default pose. */
5275
+ stopAnimation() {
5276
+ if (this.animationPlayer.stop()) this.notifyDirty();
5277
+ }
5278
+ /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
5279
+ setAnimationTime(seconds) {
5280
+ if (this.animationPlayer.setTime(seconds)) this.notifyDirty();
5281
+ }
5282
+ /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
5283
+ advanceAnimation(deltaSeconds) {
5284
+ if (this.animationPlayer.advance(deltaSeconds)) this.notifyDirty();
5285
+ }
5286
+ //</editor-fold>
5044
5287
  getLayerGroup(name) {
5045
5288
  return super.getGroupByName(`layer:${name}`);
5046
5289
  }
@@ -5074,7 +5317,7 @@ var EntityObject = class _EntityObject extends SceneObject {
5074
5317
  const transform = this.options.flip === void 0 ? this.entity.transform : void 0;
5075
5318
  if (transform) {
5076
5319
  for (const op of transform) {
5077
- const matrix = new Matrix44();
5320
+ const matrix = new Matrix45();
5078
5321
  if ("scale" in op) matrix.makeScale(...op.scale);
5079
5322
  else if ("translate" in op) matrix.makeTranslation(...op.translate);
5080
5323
  else matrix.makeRotationFromEuler(new Euler4(...op.rotate, "ZYX"));
@@ -5111,6 +5354,7 @@ var EntityObject = class _EntityObject extends SceneObject {
5111
5354
  depth + growZ * 2,
5112
5355
  uv
5113
5356
  ).clone();
5357
+ this.geometries.add(geometry);
5114
5358
  geometry.translate(cube.origin[0] + width / 2, cube.origin[1] + height / 2, cube.origin[2] + depth / 2);
5115
5359
  if (inward && Math.min(width + growX * 2, height + growY * 2, depth + growZ * 2) > 0) {
5116
5360
  const index = Array.from(geometry.getIndex().array);
@@ -5119,7 +5363,7 @@ var EntityObject = class _EntityObject extends SceneObject {
5119
5363
  const mesh = this.createMesh(name, geometry, material);
5120
5364
  mesh.renderOrder = renderOrder;
5121
5365
  anchor.add(mesh);
5122
- if (this.options.wireframe) addWireframeToMesh(geometry, mesh);
5366
+ if (this.options.wireframe) this.disposeWireframes.push(addWireframeToMesh(geometry, mesh));
5123
5367
  }
5124
5368
  for (const [childName, child] of Object.entries(part.children)) {
5125
5369
  this.createPart(childName, child, anchor, size, material, renderOrder, inward);
@@ -5153,11 +5397,8 @@ var EntityObject = class _EntityObject extends SceneObject {
5153
5397
  this.clearScrollMaterials();
5154
5398
  } else if (this.scrollMaterials.length) {
5155
5399
  this.updateScroll();
5156
- this.scrollTicker = Ticker.add(() => {
5157
- this.age++;
5158
- this.updateScroll();
5159
- });
5160
5400
  }
5401
+ this.updateScrollSubscription();
5161
5402
  this.notifyDirty();
5162
5403
  }
5163
5404
  };
@@ -5169,7 +5410,7 @@ function isEntityObject(obj) {
5169
5410
  init_MineRenderError();
5170
5411
 
5171
5412
  // 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";
5413
+ 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
5414
 
5174
5415
  // ../../node_modules/three/examples/jsm/exporters/OBJExporter.js
5175
5416
  import {
@@ -5595,7 +5836,7 @@ import {
5595
5836
  LinearMipmapLinearFilter,
5596
5837
  LinearMipmapNearestFilter,
5597
5838
  MathUtils as MathUtils2,
5598
- Matrix4 as Matrix45,
5839
+ Matrix4 as Matrix46,
5599
5840
  MirroredRepeatWrapping,
5600
5841
  NearestFilter as NearestFilter2,
5601
5842
  NearestMipmapLinearFilter,
@@ -6885,7 +7126,7 @@ var GLTFWriter = class {
6885
7126
  if (rootJoint === void 0) return null;
6886
7127
  const joints = [];
6887
7128
  const inverseBindMatrices = new Float32Array(skeleton.bones.length * 16);
6888
- const temporaryBoneInverse = new Matrix45();
7129
+ const temporaryBoneInverse = new Matrix46();
6889
7130
  for (let i = 0; i < skeleton.bones.length; ++i) {
6890
7131
  joints.push(nodeMap.get(skeleton.bones[i]));
6891
7132
  temporaryBoneInverse.copy(skeleton.boneInverses[i]);
@@ -7351,7 +7592,7 @@ var GLTFMeshGpuInstancing = class {
7351
7592
  const translationAttr = new Float32Array(mesh.count * 3);
7352
7593
  const rotationAttr = new Float32Array(mesh.count * 4);
7353
7594
  const scaleAttr = new Float32Array(mesh.count * 3);
7354
- const matrix = new Matrix45();
7595
+ const matrix = new Matrix46();
7355
7596
  const position = new Vector38();
7356
7597
  const quaternion = new Quaternion4();
7357
7598
  const scale = new Vector38();
@@ -7525,7 +7766,7 @@ var SceneExporter = class {
7525
7766
  }
7526
7767
  };
7527
7768
  function localMatrix(object) {
7528
- return object.matrixAutoUpdate ? new Matrix46().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7769
+ return object.matrixAutoUpdate ? new Matrix47().compose(object.position, object.quaternion, object.scale) : object.matrix.clone();
7529
7770
  }
7530
7771
  function createSnapshot(root, gltf, bakeColors = false) {
7531
7772
  const scene = new Scene4();
@@ -7637,7 +7878,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7637
7878
  const instanced = mesh;
7638
7879
  if (instanced.isInstancedMesh) {
7639
7880
  for (let i = 0; i < instanced.count; i++) {
7640
- const matrix = new Matrix46();
7881
+ const matrix = new Matrix47();
7641
7882
  instanced.getMatrixAt(i, matrix);
7642
7883
  const color = instanced.instanceColor ? new Color7().fromBufferAttribute(instanced.instanceColor, i) : void 0;
7643
7884
  addMesh(mesh, world.clone().multiply(matrix), color);
@@ -7651,7 +7892,7 @@ function createSnapshot(root, gltf, bakeColors = false) {
7651
7892
  try {
7652
7893
  const ancestors = [];
7653
7894
  for (let parent = root.parent; parent; parent = parent.parent) ancestors.unshift(parent);
7654
- const world = new Matrix46();
7895
+ const world = new Matrix47();
7655
7896
  for (const ancestor of ancestors) {
7656
7897
  if (!ancestor.visible) return { scene, dispose };
7657
7898
  world.multiply(localMatrix(ancestor));
@@ -8553,7 +8794,7 @@ var import_ts_deepmerge6 = __toESM(require_dist());
8553
8794
  init_MineRenderError();
8554
8795
  init_InstanceReference();
8555
8796
  init_AssetKey();
8556
- import { Euler as Euler8, Matrix4 as Matrix48, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8797
+ import { Euler as Euler8, Matrix4 as Matrix49, Quaternion as Quaternion5, Vector3 as Vector311 } from "three";
8557
8798
  init_ModelCulling();
8558
8799
  init_FluidGeometry();
8559
8800
  var BlockObject = class _BlockObject extends SceneObject {
@@ -8570,6 +8811,9 @@ var BlockObject = class _BlockObject extends SceneObject {
8570
8811
  this._variants = [];
8571
8812
  this._models = [];
8572
8813
  this._cullMask = 0;
8814
+ this._entities = [];
8815
+ this._entityPlacement = new Matrix49();
8816
+ this._entityGeneration = 0;
8573
8817
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
8574
8818
  }
8575
8819
  static {
@@ -8594,6 +8838,8 @@ var BlockObject = class _BlockObject extends SceneObject {
8594
8838
  }
8595
8839
  clearModels() {
8596
8840
  this.removeModels(this._models.splice(0));
8841
+ this._entityGeneration++;
8842
+ this.removeEntities(this._entities.splice(0));
8597
8843
  this._fluidKey = void 0;
8598
8844
  this._fluidModel = void 0;
8599
8845
  this._isInstanced = false;
@@ -8605,6 +8851,57 @@ var BlockObject = class _BlockObject extends SceneObject {
8605
8851
  if (!isInstanceReference(model)) model.dispose();
8606
8852
  }
8607
8853
  }
8854
+ removeEntities(entities) {
8855
+ for (const entity of entities) {
8856
+ entity.removeFromScene();
8857
+ entity.dispose();
8858
+ }
8859
+ }
8860
+ /** Entity models drawn for this block instead of its block model; they are scene children, unlike the block. */
8861
+ get blockEntities() {
8862
+ return this._entities;
8863
+ }
8864
+ /**
8865
+ * Draws the block through the dataset's block-entity index. Returns false, leaving nothing behind,
8866
+ * when the block is not listed or one of its models cannot be loaded.
8867
+ */
8868
+ async createBlockEntities() {
8869
+ const key = this.blockState.key;
8870
+ if (!key || this.options.displayPosition) return false;
8871
+ const generation = this._entityGeneration;
8872
+ const created = [];
8873
+ let resolved;
8874
+ try {
8875
+ resolved = BlockEntities.resolve(await BlockEntities.getIndex(key.root), key.toNamespacedString(), this.state);
8876
+ if (!resolved) return false;
8877
+ for (const part of resolved.parts) {
8878
+ const [namespace, path] = part.model.includes(":") ? part.model.split(":") : [key.namespace, part.model];
8879
+ const texture = part.textureLocation === void 0 ? void 0 : AssetKey.parse("textures", part.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
8880
+ if (texture) texture.root = key.root;
8881
+ const entity = await Entities.getEntity(new BasicAssetKey(namespace, path), texture, { layer: part.layer });
8882
+ if (!entity) throw new MineRenderError(`Missing entity model ${part.model}`);
8883
+ created.push(await this.scene.addEntity(entity, { wireframe: this.options.wireframe }));
8884
+ }
8885
+ } catch (error) {
8886
+ console.warn(`Could not draw block entity ${key.toNamespacedString()}, using its block model`, error);
8887
+ this.removeEntities(created);
8888
+ return false;
8889
+ }
8890
+ if (generation !== this._entityGeneration) {
8891
+ this.removeEntities(created);
8892
+ return true;
8893
+ }
8894
+ BlockEntities.matrix(resolved, this._entityPlacement);
8895
+ this._entities = created;
8896
+ this.placeEntities();
8897
+ return true;
8898
+ }
8899
+ placeEntities() {
8900
+ const matrix = new Matrix49().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
8901
+ for (const entity of this._entities) {
8902
+ matrix.decompose(entity.position, entity.quaternion, entity.scale);
8903
+ }
8904
+ }
8608
8905
  get isOccluding() {
8609
8906
  return this._models.some((model) => {
8610
8907
  const object = isInstanceReference(model) ? model.instanceable : model;
@@ -8686,7 +8983,7 @@ var BlockObject = class _BlockObject extends SceneObject {
8686
8983
  () => new FluidModelObject2(kind, surface.sample, this.blockState.key, this.options)
8687
8984
  );
8688
8985
  const previous = this._fluidModel;
8689
- const matrix = previous ? this.getModelMatrix(previous) : new Matrix48().makeTranslation(...this.position.toArray());
8986
+ const matrix = previous ? this.getModelMatrix(previous) : new Matrix49().makeTranslation(...this.position.toArray());
8690
8987
  if (isInstanceReference(replacement)) replacement.setMatrix(matrix);
8691
8988
  else matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
8692
8989
  this._models = this._models.filter((model) => model !== previous);
@@ -8716,13 +9013,20 @@ var BlockObject = class _BlockObject extends SceneObject {
8716
9013
  await this.updateFluid(this._fluidSampler);
8717
9014
  return;
8718
9015
  }
9016
+ const hasEntities = await this.createBlockEntities();
8719
9017
  const variantsToCreate = await this.mapStateToVariant(this.state);
8720
9018
  this._variants = variantsToCreate;
8721
9019
  for (let blockStateVariant of variantsToCreate) {
9020
+ if (hasEntities && !await this.hasGeometry(blockStateVariant)) continue;
8722
9021
  this._models.push(await this.createVariant(blockStateVariant));
8723
9022
  }
8724
9023
  await this.updateFluid(this._fluidSampler);
8725
9024
  }
9025
+ async hasGeometry(variant) {
9026
+ const modelKey = AssetKey.parse("models", variant.model);
9027
+ modelKey.root = this.blockState.key?.root;
9028
+ return !!(await Models.getMerged(modelKey))?.elements?.length;
9029
+ }
8726
9030
  getSingleVariant(variants) {
8727
9031
  return BlockStateResolver.choose(variants);
8728
9032
  }
@@ -8817,7 +9121,7 @@ var BlockObject = class _BlockObject extends SceneObject {
8817
9121
  setPositionRotationScaleAt(index, position, rotation, scale) {
8818
9122
  super.setPositionRotationScaleAt(index, position, rotation, scale);
8819
9123
  }
8820
- getMatrixAt(index, matrix = new Matrix48()) {
9124
+ getMatrixAt(index, matrix = new Matrix49()) {
8821
9125
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
8822
9126
  const child = this._models[0];
8823
9127
  if (child && isModelObject(child)) {
@@ -8842,6 +9146,7 @@ var BlockObject = class _BlockObject extends SceneObject {
8842
9146
  for (let model of this._models) {
8843
9147
  model.setPosition(position);
8844
9148
  }
9149
+ this.placeEntities();
8845
9150
  this.notifyDirty();
8846
9151
  }
8847
9152
  //TODO: should override rotation + scale methods
@@ -13379,7 +13684,7 @@ init_CubeFace();
13379
13684
  init_AssetKey();
13380
13685
  init_Models();
13381
13686
  init_CubeFace();
13382
- import { Matrix4 as Matrix49, Vector3 as Vector314 } from "three";
13687
+ import { Matrix4 as Matrix410, Vector3 as Vector314 } from "three";
13383
13688
  init_ModelCulling();
13384
13689
  init_ModelObject();
13385
13690
  init_UVMapper();
@@ -13442,7 +13747,7 @@ var SectionModels = class {
13442
13747
  });
13443
13748
  await object.init();
13444
13749
  const geometry = object.children[0].geometry;
13445
- geometry.applyMatrix4(new Matrix49().makeRotationFromEuler(rotation));
13750
+ geometry.applyMatrix4(new Matrix410().makeRotationFromEuler(rotation));
13446
13751
  object.clear();
13447
13752
  const cullFaces = CUBE_FACES.map((face) => {
13448
13753
  const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
@@ -13854,7 +14159,8 @@ var Chunk = class _Chunk {
13854
14159
  this.data.set(index, void 0);
13855
14160
  return void 0;
13856
14161
  }
13857
- const template = getFluidKind(blockState.key, stored.properties) ? void 0 : await this.sectionModels?.get(blockState, stored.properties);
14162
+ const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
14163
+ const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
13858
14164
  if (template) {
13859
14165
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
13860
14166
  this.meshDirty = true;
@@ -13963,6 +14269,7 @@ export {
13963
14269
  BUILTIN_GENERATED,
13964
14270
  BasicAssetKey,
13965
14271
  BatchedExecutor,
14272
+ BlockEntities,
13966
14273
  BlockObject,
13967
14274
  BlockStateResolver,
13968
14275
  BlockStates,
@@ -13984,7 +14291,9 @@ export {
13984
14291
  DefaultBlockStates,
13985
14292
  DisplayPosition,
13986
14293
  DisplayTransforms,
14294
+ ENTITY_ANIMATION_CHANNELS,
13987
14295
  Entities,
14296
+ EntityAnimationPlayer,
13988
14297
  EntityObject,
13989
14298
  Env,
13990
14299
  FluidModelObject,
@@ -14060,6 +14369,7 @@ export {
14060
14369
  createCanvas2 as createCanvas,
14061
14370
  createFluidGeometry,
14062
14371
  createImage,
14372
+ entityAnimationTime,
14063
14373
  estimateBytesUsed,
14064
14374
  getBlockFluidState,
14065
14375
  getFluidKind,
@@ -14092,6 +14402,8 @@ export {
14092
14402
  mergeVertices,
14093
14403
  prefix,
14094
14404
  registerNodeEnv,
14405
+ sampleEntityAnimation,
14406
+ sampleEntityKeyframes,
14095
14407
  sampleFluid,
14096
14408
  serializeBoxGeometryKey,
14097
14409
  serializeImageKey,