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.
package/dist/bundle.js CHANGED
@@ -32176,7 +32176,9 @@ var MineRender = (() => {
32176
32176
  this.defaultBlockStatesCache,
32177
32177
  this.blockTintCache,
32178
32178
  this.entityModelCache,
32179
- this.entityTextureCache
32179
+ this.entityTextureCache,
32180
+ this.entityAnimationCache,
32181
+ this.blockEntityIndexCache
32180
32182
  ];
32181
32183
  }
32182
32184
  static clear() {
@@ -32221,6 +32223,8 @@ var MineRender = (() => {
32221
32223
  _Caching.blockTintCache = _Caching.createAssetCache();
32222
32224
  _Caching.entityModelCache = _Caching.createAssetCache();
32223
32225
  _Caching.entityTextureCache = _Caching.createAssetCache();
32226
+ _Caching.entityAnimationCache = _Caching.createAssetCache();
32227
+ _Caching.blockEntityIndexCache = _Caching.createAssetCache();
32224
32228
  Caching = _Caching;
32225
32229
  }
32226
32230
  });
@@ -83042,6 +83046,7 @@ ${key}` : key;
83042
83046
  BUILTIN_GENERATED: () => BUILTIN_GENERATED,
83043
83047
  BasicAssetKey: () => BasicAssetKey,
83044
83048
  BatchedExecutor: () => BatchedExecutor,
83049
+ BlockEntities: () => BlockEntities,
83045
83050
  BlockObject: () => BlockObject,
83046
83051
  BlockStateResolver: () => BlockStateResolver,
83047
83052
  BlockStates: () => BlockStates,
@@ -83065,7 +83070,9 @@ ${key}` : key;
83065
83070
  DefaultBlockStates: () => DefaultBlockStates,
83066
83071
  DisplayPosition: () => DisplayPosition,
83067
83072
  DisplayTransforms: () => DisplayTransforms,
83073
+ ENTITY_ANIMATION_CHANNELS: () => ENTITY_ANIMATION_CHANNELS,
83068
83074
  Entities: () => Entities,
83075
+ EntityAnimationPlayer: () => EntityAnimationPlayer,
83069
83076
  EntityObject: () => EntityObject,
83070
83077
  Env: () => Env,
83071
83078
  FluidModelObject: () => FluidModelObject,
@@ -83139,6 +83146,7 @@ ${key}` : key;
83139
83146
  createCanvas: () => createCanvas,
83140
83147
  createFluidGeometry: () => createFluidGeometry,
83141
83148
  createImage: () => createImage,
83149
+ entityAnimationTime: () => entityAnimationTime,
83142
83150
  estimateBytesUsed: () => estimateBytesUsed,
83143
83151
  getBlockFluidState: () => getBlockFluidState,
83144
83152
  getFluidKind: () => getFluidKind,
@@ -83172,6 +83180,8 @@ ${key}` : key;
83172
83180
  prefix: () => prefix,
83173
83181
  probeImageSize: () => probeImageSize,
83174
83182
  registerBrowserEnv: () => registerBrowserEnv,
83183
+ sampleEntityAnimation: () => sampleEntityAnimation,
83184
+ sampleEntityKeyframes: () => sampleEntityKeyframes,
83175
83185
  sampleFluid: () => sampleFluid,
83176
83186
  serializeBoxGeometryKey: () => serializeBoxGeometryKey,
83177
83187
  serializeImageKey: () => serializeImageKey,
@@ -83543,6 +83553,62 @@ ${key}` : key;
83543
83553
  init_AssetKey();
83544
83554
  init_AssetLoader();
83545
83555
  init_Assets();
83556
+
83557
+ // src/assets/BlockEntities.ts
83558
+ init_dirname();
83559
+ init_buffer2();
83560
+ init_process2();
83561
+ init_three_module();
83562
+ init_AssetKey();
83563
+ init_AssetLoader();
83564
+ init_source();
83565
+ init_Caching();
83566
+ var BlockEntities = class {
83567
+ /**
83568
+ * Loads the block index of a version. Versions and sources without one yield an empty index, and so does
83569
+ * a failed load: the index is optional, and blocks must not retry or fail with it one by one. The empty
83570
+ * result is cached like any other; `Caching.clear()` or `AssetLoader.setVersion()` forces a new attempt.
83571
+ */
83572
+ static async getIndex(root) {
83573
+ const key = new AssetKey("entity-models", "blocks", void 0, void 0, void 0, ".json", root);
83574
+ key.rootType = void 0;
83575
+ const index = await Caching.blockEntityIndexCache.get(key.serialize(), async () => {
83576
+ try {
83577
+ return await AssetLoader.get(key, "json" /* JSON */) ?? {};
83578
+ } catch (error) {
83579
+ console.warn("Could not load the block entity index; block entities are drawn as plain block models", error);
83580
+ return {};
83581
+ }
83582
+ });
83583
+ return index ?? {};
83584
+ }
83585
+ /** The index entry of a block ID such as `minecraft:chest`. */
83586
+ static entry(index, block) {
83587
+ const entry = Object.prototype.hasOwnProperty.call(index, block) ? index[block] : void 0;
83588
+ return Array.isArray(entry?.parts) ? entry : void 0;
83589
+ }
83590
+ /** Selects the parts and placement for a block ID such as `minecraft:chest`; undefined when nothing is drawn. */
83591
+ static resolve(index, block, state = {}) {
83592
+ const entry = this.entry(index, block);
83593
+ if (!entry) return void 0;
83594
+ const value = (property) => String(state[property]).toLowerCase();
83595
+ const parts = entry.parts.filter((part) => Object.entries(part.when ?? {}).every(([property, values]) => String(values).split("|").includes(value(property))));
83596
+ if (!parts.length) return void 0;
83597
+ let rotation = 0;
83598
+ if (entry.rotation) {
83599
+ const selected = value(entry.rotation.property);
83600
+ rotation = "degrees" in entry.rotation ? entry.rotation.degrees[selected] : Number(selected) * entry.rotation.step;
83601
+ if (!Number.isFinite(rotation)) rotation = 0;
83602
+ }
83603
+ return { parts, rotation, translation: entry.translation ?? [0, 0, 0] };
83604
+ }
83605
+ /** Placement in block space (0..16 per axis), applied on top of the model's own transform. */
83606
+ static matrix(resolved, matrix = new Matrix4()) {
83607
+ return matrix.makeTranslation(8, 0, 8).multiply(new Matrix4().makeRotationY(resolved.rotation * Math.PI / 180)).multiply(new Matrix4().makeTranslation(resolved.translation[0] - 8, resolved.translation[1], resolved.translation[2] - 8));
83608
+ }
83609
+ };
83610
+
83611
+ // src/assets/index.ts
83546
83612
  init_BlockStates();
83547
83613
 
83548
83614
  // src/assets/Entities.ts
@@ -83616,6 +83682,15 @@ ${key}` : key;
83616
83682
  })));
83617
83683
  return { ...layers[names[0]], id: model.id, layers, ...model.transform && { transform: model.transform } };
83618
83684
  }
83685
+ /**
83686
+ * Vanilla's keyframe animations of a model by name, or undefined when the model or the selected version has none.
83687
+ */
83688
+ static async getAnimations(modelKey) {
83689
+ const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
83690
+ const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models/animations", ".json");
83691
+ const file = await Caching.entityAnimationCache.get(key.serialize(), () => AssetLoader.get(key, "json" /* JSON */));
83692
+ return file?.animations;
83693
+ }
83619
83694
  static async resolveTexture(modelKey) {
83620
83695
  const path = isAssetKey(modelKey) ? modelKey.getFullPath() : modelKey.path;
83621
83696
  const key = new AssetKey(modelKey.namespace, path, void 0, void 0, "entity-models", ".json");
@@ -83731,6 +83806,51 @@ ${key}` : key;
83731
83806
  init_buffer2();
83732
83807
  init_process2();
83733
83808
 
83809
+ // src/entity/EntityAnimation.ts
83810
+ init_dirname();
83811
+ init_buffer2();
83812
+ init_process2();
83813
+ var ENTITY_ANIMATION_CHANNELS = ["position", "rotation", "scale"];
83814
+ function catmullRom(delta, p0, p1, p22, p32) {
83815
+ 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);
83816
+ }
83817
+ function sampleEntityKeyframes(keyframes, time) {
83818
+ if (keyframes.length === 0) return [0, 0, 0];
83819
+ let low = 0, high = keyframes.length;
83820
+ while (low < high) {
83821
+ const middle = low + high >>> 1;
83822
+ if (time <= keyframes[middle].time) high = middle;
83823
+ else low = middle + 1;
83824
+ }
83825
+ const a4 = Math.max(0, low - 1);
83826
+ const b4 = Math.min(keyframes.length - 1, a4 + 1);
83827
+ const from = keyframes[a4];
83828
+ const to = keyframes[b4];
83829
+ const delta = a4 === b4 ? 0 : Math.min(1, Math.max(0, (time - from.time) / (to.time - from.time)));
83830
+ if (to.interpolation === "catmullrom") {
83831
+ const before = keyframes[Math.max(0, a4 - 1)].value;
83832
+ const after = keyframes[Math.min(keyframes.length - 1, b4 + 1)].value;
83833
+ return [0, 1, 2].map((i4) => catmullRom(delta, before[i4], from.value[i4], to.value[i4], after[i4]));
83834
+ }
83835
+ const target = to.pre ?? to.value;
83836
+ return [0, 1, 2].map((i4) => from.value[i4] + (target[i4] - from.value[i4]) * delta);
83837
+ }
83838
+ function entityAnimationTime(animation, time, loop = animation.loop) {
83839
+ return loop && animation.length > 0 ? time % animation.length : time;
83840
+ }
83841
+ function sampleEntityAnimation(animation, time, loop = animation.loop) {
83842
+ const elapsed = entityAnimationTime(animation, time, loop);
83843
+ const pose = {};
83844
+ for (const [name2, bone] of Object.entries(animation.bones)) {
83845
+ const sampled = pose[name2] = {};
83846
+ for (const channel of ENTITY_ANIMATION_CHANNELS) {
83847
+ const keyframes = bone[channel];
83848
+ if (keyframes?.length) sampled[channel] = sampleEntityKeyframes(keyframes, elapsed);
83849
+ }
83850
+ }
83851
+ return pose;
83852
+ }
83853
+
83734
83854
  // src/entity/EntityModel.ts
83735
83855
  init_dirname();
83736
83856
  init_buffer2();
@@ -83741,6 +83861,80 @@ ${key}` : key;
83741
83861
  init_buffer2();
83742
83862
  init_process2();
83743
83863
 
83864
+ // src/entity/scene/EntityAnimationPlayer.ts
83865
+ init_dirname();
83866
+ init_buffer2();
83867
+ init_process2();
83868
+ var ZERO = [0, 0, 0];
83869
+ var EntityAnimationPlayer = class {
83870
+ constructor() {
83871
+ this.parts = [];
83872
+ this._time = 0;
83873
+ this.loop = false;
83874
+ this.speed = 1;
83875
+ }
83876
+ get animation() {
83877
+ return this._animation;
83878
+ }
83879
+ get time() {
83880
+ return this._time;
83881
+ }
83882
+ /**
83883
+ * Starts an animation on the named parts below each layer root. The current pose of a part becomes its default;
83884
+ * bones without a matching part are skipped.
83885
+ */
83886
+ play(layers, animation, options = {}) {
83887
+ this.stop();
83888
+ this._animation = animation;
83889
+ this._time = options.time ?? 0;
83890
+ this.loop = options.loop ?? animation.loop;
83891
+ this.speed = options.speed ?? 1;
83892
+ for (const layer of layers) {
83893
+ for (const bone of Object.keys(animation.bones)) {
83894
+ const object = layer.getObjectByName(`group:${bone}`);
83895
+ if (!object) continue;
83896
+ this.parts.push({ bone, object, position: object.position.clone(), rotation: object.rotation.clone(), scale: object.scale.clone() });
83897
+ }
83898
+ }
83899
+ this.apply();
83900
+ }
83901
+ /** Restores the default poses. Returns whether an animation was active. */
83902
+ stop() {
83903
+ if (!this._animation) return false;
83904
+ for (const part of this.parts) {
83905
+ part.object.position.copy(part.position);
83906
+ part.object.rotation.copy(part.rotation);
83907
+ part.object.scale.copy(part.scale);
83908
+ }
83909
+ this.clear();
83910
+ return true;
83911
+ }
83912
+ /** Forgets the animation without touching the parts, for parts that no longer exist. */
83913
+ clear() {
83914
+ this.parts = [];
83915
+ this._animation = void 0;
83916
+ this._time = 0;
83917
+ }
83918
+ setTime(seconds) {
83919
+ if (!this._animation) return false;
83920
+ this._time = seconds;
83921
+ this.apply();
83922
+ return true;
83923
+ }
83924
+ advance(deltaSeconds) {
83925
+ return this.setTime(this._time + deltaSeconds * this.speed);
83926
+ }
83927
+ apply() {
83928
+ const pose = sampleEntityAnimation(this._animation, this._time, this.loop);
83929
+ for (const part of this.parts) {
83930
+ const { position = ZERO, rotation = ZERO, scale = ZERO } = pose[part.bone];
83931
+ part.object.position.set(part.position.x + position[0], part.position.y + position[1], part.position.z + position[2]);
83932
+ part.object.rotation.set(part.rotation.x + rotation[0], part.rotation.y + rotation[1], part.rotation.z + rotation[2], part.rotation.order);
83933
+ part.object.scale.set(part.scale.x + scale[0], part.scale.y + scale[1], part.scale.z + scale[2]);
83934
+ }
83935
+ }
83936
+ };
83937
+
83744
83938
  // src/entity/scene/EntityObject.ts
83745
83939
  init_dirname();
83746
83940
  init_buffer2();
@@ -83765,6 +83959,7 @@ ${key}` : key;
83765
83959
  this.scrollMaterials = [];
83766
83960
  /** Entity age in ticks, as vanilla's `ageInTicks`; drives the scrolling render modes. */
83767
83961
  this.age = 0;
83962
+ this.animationPlayer = new EntityAnimationPlayer();
83768
83963
  this.options = (0, import_ts_deepmerge4.default)({}, _EntityObject.DEFAULT_OPTIONS, options ?? {});
83769
83964
  }
83770
83965
  async init() {
@@ -83776,6 +83971,7 @@ ${key}` : key;
83776
83971
  }
83777
83972
  disposeAndRemoveAllChildren() {
83778
83973
  this.clearScrollMaterials();
83974
+ this.animationPlayer.clear();
83779
83975
  super.disposeAndRemoveAllChildren();
83780
83976
  }
83781
83977
  clearScrollMaterials() {
@@ -83793,6 +83989,39 @@ ${key}` : key;
83793
83989
  }
83794
83990
  this.notifyDirty();
83795
83991
  }
83992
+ //<editor-fold desc="ANIMATION">
83993
+ /** The animation started by {@link playAnimation}, until {@link stopAnimation} or disposal. */
83994
+ get animation() {
83995
+ return this.animationPlayer.animation;
83996
+ }
83997
+ /** Seconds since the animation started, before looping. */
83998
+ get animationTime() {
83999
+ return this.animationPlayer.time;
84000
+ }
84001
+ /**
84002
+ * Poses the object from a keyframe animation of {@link Entities.getAnimations}, replacing the current one.
84003
+ * Parts of every layer are matched by name and their current pose is the default the offsets add to.
84004
+ * The object owns no clock: call {@link advanceAnimation} per frame, or {@link setAnimationTime}.
84005
+ */
84006
+ playAnimation(animation, options) {
84007
+ this.createMeshes();
84008
+ const layers = Object.keys(this.entityLayers).map((name2) => this.getLayerGroup(name2)).filter((layer) => !!layer);
84009
+ this.animationPlayer.play(layers, animation, options);
84010
+ this.notifyDirty();
84011
+ }
84012
+ /** Stops the animation and restores the default pose. */
84013
+ stopAnimation() {
84014
+ if (this.animationPlayer.stop()) this.notifyDirty();
84015
+ }
84016
+ /** Poses the object at an explicit time in seconds. Has no effect without an animation. */
84017
+ setAnimationTime(seconds) {
84018
+ if (this.animationPlayer.setTime(seconds)) this.notifyDirty();
84019
+ }
84020
+ /** Advances the animation by a frame delta in seconds, scaled by its speed. Has no effect without an animation. */
84021
+ advanceAnimation(deltaSeconds) {
84022
+ if (this.animationPlayer.advance(deltaSeconds)) this.notifyDirty();
84023
+ }
84024
+ //</editor-fold>
83796
84025
  getLayerGroup(name2) {
83797
84026
  return super.getGroupByName(`layer:${name2}`);
83798
84027
  }
@@ -87400,6 +87629,9 @@ property list uchar int vertex_index
87400
87629
  this._variants = [];
87401
87630
  this._models = [];
87402
87631
  this._cullMask = 0;
87632
+ this._entities = [];
87633
+ this._entityPlacement = new Matrix4();
87634
+ this._entityGeneration = 0;
87403
87635
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
87404
87636
  }
87405
87637
  async init() {
@@ -87419,6 +87651,8 @@ property list uchar int vertex_index
87419
87651
  }
87420
87652
  clearModels() {
87421
87653
  this.removeModels(this._models.splice(0));
87654
+ this._entityGeneration++;
87655
+ this.removeEntities(this._entities.splice(0));
87422
87656
  this._fluidKey = void 0;
87423
87657
  this._fluidModel = void 0;
87424
87658
  this._isInstanced = false;
@@ -87430,6 +87664,57 @@ property list uchar int vertex_index
87430
87664
  if (!isInstanceReference(model)) model.dispose();
87431
87665
  }
87432
87666
  }
87667
+ removeEntities(entities) {
87668
+ for (const entity of entities) {
87669
+ entity.removeFromScene();
87670
+ entity.dispose();
87671
+ }
87672
+ }
87673
+ /** Entity models drawn for this block instead of its block model; they are scene children, unlike the block. */
87674
+ get blockEntities() {
87675
+ return this._entities;
87676
+ }
87677
+ /**
87678
+ * Draws the block through the dataset's block-entity index. Returns false, leaving nothing behind,
87679
+ * when the block is not listed or one of its models cannot be loaded.
87680
+ */
87681
+ async createBlockEntities() {
87682
+ const key = this.blockState.key;
87683
+ if (!key || this.options.displayPosition) return false;
87684
+ const generation = this._entityGeneration;
87685
+ const created = [];
87686
+ let resolved;
87687
+ try {
87688
+ resolved = BlockEntities.resolve(await BlockEntities.getIndex(key.root), key.toNamespacedString(), this.state);
87689
+ if (!resolved) return false;
87690
+ for (const part of resolved.parts) {
87691
+ const [namespace, path] = part.model.includes(":") ? part.model.split(":") : [key.namespace, part.model];
87692
+ const texture = part.textureLocation === void 0 ? void 0 : AssetKey.parse("textures", part.textureLocation.replace(/^([^:]+:)?textures\//, "$1"));
87693
+ if (texture) texture.root = key.root;
87694
+ const entity = await Entities.getEntity(new BasicAssetKey(namespace, path), texture, { layer: part.layer });
87695
+ if (!entity) throw new MineRenderError(`Missing entity model ${part.model}`);
87696
+ created.push(await this.scene.addEntity(entity, { wireframe: this.options.wireframe }));
87697
+ }
87698
+ } catch (error) {
87699
+ console.warn(`Could not draw block entity ${key.toNamespacedString()}, using its block model`, error);
87700
+ this.removeEntities(created);
87701
+ return false;
87702
+ }
87703
+ if (generation !== this._entityGeneration) {
87704
+ this.removeEntities(created);
87705
+ return true;
87706
+ }
87707
+ BlockEntities.matrix(resolved, this._entityPlacement);
87708
+ this._entities = created;
87709
+ this.placeEntities();
87710
+ return true;
87711
+ }
87712
+ placeEntities() {
87713
+ const matrix = new Matrix4().makeTranslation(this.position.x - 8, this.position.y - 8, this.position.z - 8).multiply(this._entityPlacement);
87714
+ for (const entity of this._entities) {
87715
+ matrix.decompose(entity.position, entity.quaternion, entity.scale);
87716
+ }
87717
+ }
87433
87718
  get isOccluding() {
87434
87719
  return this._models.some((model) => {
87435
87720
  const object = isInstanceReference(model) ? model.instanceable : model;
@@ -87541,13 +87826,20 @@ property list uchar int vertex_index
87541
87826
  await this.updateFluid(this._fluidSampler);
87542
87827
  return;
87543
87828
  }
87829
+ const hasEntities = await this.createBlockEntities();
87544
87830
  const variantsToCreate = await this.mapStateToVariant(this.state);
87545
87831
  this._variants = variantsToCreate;
87546
87832
  for (let blockStateVariant of variantsToCreate) {
87833
+ if (hasEntities && !await this.hasGeometry(blockStateVariant)) continue;
87547
87834
  this._models.push(await this.createVariant(blockStateVariant));
87548
87835
  }
87549
87836
  await this.updateFluid(this._fluidSampler);
87550
87837
  }
87838
+ async hasGeometry(variant) {
87839
+ const modelKey = AssetKey.parse("models", variant.model);
87840
+ modelKey.root = this.blockState.key?.root;
87841
+ return !!(await Models.getMerged(modelKey))?.elements?.length;
87842
+ }
87551
87843
  getSingleVariant(variants) {
87552
87844
  return BlockStateResolver.choose(variants);
87553
87845
  }
@@ -87667,6 +87959,7 @@ property list uchar int vertex_index
87667
87959
  for (let model of this._models) {
87668
87960
  model.setPosition(position);
87669
87961
  }
87962
+ this.placeEntities();
87670
87963
  this.notifyDirty();
87671
87964
  }
87672
87965
  //TODO: should override rotation + scale methods
@@ -94553,7 +94846,8 @@ uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuf
94553
94846
  this.data.set(index, void 0);
94554
94847
  return void 0;
94555
94848
  }
94556
- const template = getFluidKind(blockState.key, stored.properties) ? void 0 : await this.sectionModels?.get(blockState, stored.properties);
94849
+ const perBlock = !this.sectionModels || getFluidKind(blockState.key, stored.properties) || blockState.key && BlockEntities.entry(await BlockEntities.getIndex(blockState.key.root), blockState.key.toNamespacedString());
94850
+ const template = perBlock ? void 0 : await this.sectionModels.get(blockState, stored.properties);
94557
94851
  if (template) {
94558
94852
  this.sectionBlocks.set(index, { index, template, cullMask: 0 });
94559
94853
  this.meshDirty = true;