minerender 2.1.0-alpha.37 → 2.1.0-alpha.38

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
@@ -48096,6 +48096,7 @@ return ${compiler2.callType("value", tagType)}
48096
48096
  BrowserCache: () => BrowserCache,
48097
48097
  BrowserEnv: () => BrowserEnv,
48098
48098
  CUBE_FACES: () => CUBE_FACES,
48099
+ CUBE_FACE_OFFSETS: () => CUBE_FACE_OFFSETS,
48099
48100
  Caching: () => Caching,
48100
48101
  CanvasImage: () => CanvasImage,
48101
48102
  Chunk: () => Chunk,
@@ -48129,6 +48130,7 @@ return ${compiler2.callType("value", tagType)}
48129
48130
  MineRenderWorld: () => MineRenderWorld,
48130
48131
  MinecraftCubeTexture: () => MinecraftCubeTexture,
48131
48132
  MinecraftFaceUV: () => MinecraftFaceUV,
48133
+ ModelCulling: () => ModelCulling,
48132
48134
  ModelGenerator: () => ModelGenerator,
48133
48135
  ModelMerger: () => ModelMerger,
48134
48136
  ModelObject: () => ModelObject,
@@ -68052,6 +68054,14 @@ return ${compiler2.callType("value", tagType)}
68052
68054
  return CubeFaceIndex2;
68053
68055
  })(CubeFaceIndex || {});
68054
68056
  var CUBE_FACES = Object.values(CubeFace);
68057
+ var CUBE_FACE_OFFSETS = [
68058
+ [1, 0, 0],
68059
+ [-1, 0, 0],
68060
+ [0, 1, 0],
68061
+ [0, -1, 0],
68062
+ [0, 0, 1],
68063
+ [0, 0, -1]
68064
+ ];
68055
68065
 
68056
68066
  // src/Disposable.ts
68057
68067
  init_dirname();
@@ -82275,6 +82285,44 @@ ${key}` : key;
82275
82285
  return Array.isArray(obj) && obj.length === 4;
82276
82286
  }
82277
82287
 
82288
+ // src/model/ModelCulling.ts
82289
+ init_dirname();
82290
+ init_buffer2();
82291
+ init_process2();
82292
+ var ModelCulling = class {
82293
+ static toLocalMask(worldMask, rotation) {
82294
+ let localMask = 0;
82295
+ for (const [localFace, offset] of CUBE_FACE_OFFSETS.entries()) {
82296
+ const normal = new Vector3(...offset).applyEuler(rotation);
82297
+ const worldFace = CUBE_FACE_OFFSETS.findIndex(([x4, y5, z4]) => Math.abs(normal.x - x4) < 1e-6 && Math.abs(normal.y - y5) < 1e-6 && Math.abs(normal.z - z4) < 1e-6);
82298
+ if (worldFace < 0) return 0;
82299
+ if (worldMask & 1 << worldFace) localMask |= 1 << localFace;
82300
+ }
82301
+ return localMask;
82302
+ }
82303
+ static isOpaqueFullCube(atlas) {
82304
+ if (!atlas || atlas.hasTransparency !== false || atlas.model.elements?.length !== 1) return false;
82305
+ const element = atlas.model.elements[0];
82306
+ if (element.from.length !== 3 || !element.from.every((value) => value === 0) || element.to.length !== 3 || !element.to.every((value) => value === 16) || element.rotation && element.rotation.angle !== 0 || element.mappedUv?.length !== 48 || !element.mappedUv.every(Number.isFinite)) return false;
82307
+ if (!(atlas.image.width > 0 && atlas.image.height > 0)) return false;
82308
+ return CUBE_FACES.every((face, faceIndex) => {
82309
+ const texture = element.faces[face]?.texture;
82310
+ if (!texture?.startsWith("#")) return false;
82311
+ const key = texture.slice(1);
82312
+ const position = atlas.positions[key];
82313
+ const size = atlas.sizes[key];
82314
+ if (position?.length !== 2 || !position.every(Number.isFinite) || size?.length !== 2 || !size.every((value) => value > 0 && Number.isFinite(value))) return false;
82315
+ for (let vertex2 = 0; vertex2 < 4; vertex2++) {
82316
+ const uvIndex = (faceIndex * 4 + vertex2) * 2;
82317
+ const x4 = element.mappedUv[uvIndex] * atlas.image.width;
82318
+ const y5 = (1 - element.mappedUv[uvIndex + 1]) * atlas.image.height;
82319
+ if (x4 < position[0] - 1e-6 || x4 > position[0] + size[0] + 1e-6 || y5 < position[1] - 1e-6 || y5 > position[1] + size[1] + 1e-6) return false;
82320
+ }
82321
+ return true;
82322
+ });
82323
+ }
82324
+ };
82325
+
82278
82326
  // src/model/ModelElement.ts
82279
82327
  init_dirname();
82280
82328
  init_buffer2();
@@ -82960,6 +83008,9 @@ ${key}` : key;
82960
83008
  get textureAtlas() {
82961
83009
  return this.atlas;
82962
83010
  }
83011
+ get isOpaqueFullCube() {
83012
+ return !this.options.displayPosition && ModelCulling.isOpaqueFullCube(this.atlas);
83013
+ }
82963
83014
  //TODO: support for replacing textures
82964
83015
  async loadTextures() {
82965
83016
  this.atlas = await UVMapper.getAtlas(this.originalModel);
@@ -82973,6 +83024,15 @@ ${key}` : key;
82973
83024
  if (this.atlas.model.elements) {
82974
83025
  this.atlas.model.elements?.forEach((el) => {
82975
83026
  const elGeo = this._getBoxGeometryFromElement(el).clone();
83027
+ if (this.options.cullMask) {
83028
+ const indices = Array.from(elGeo.getIndex().array);
83029
+ elGeo.setIndex(indices.filter((_4, index) => {
83030
+ const face = CUBE_FACES[Math.floor(index / 6)];
83031
+ const direction = CUBE_FACES.findIndex((name2) => name2 === el.faces[face]?.cullface);
83032
+ return direction < 0 || !(this.options.cullMask & 1 << direction);
83033
+ }));
83034
+ elGeo.clearGroups();
83035
+ }
82976
83036
  if (this.options.uvLockRotation) {
82977
83037
  UVMapper.lockUvs(elGeo, el.faces, this.atlas, new Euler(...this.options.uvLockRotation));
82978
83038
  }
@@ -83096,6 +83156,7 @@ ${key}` : key;
83096
83156
  this._instanceModel = [];
83097
83157
  this._variants = [];
83098
83158
  this._models = [];
83159
+ this._cullMask = 0;
83099
83160
  this.options = (0, import_ts_deepmerge6.default)({}, _BlockObject.DEFAULT_OPTIONS, options ?? {});
83100
83161
  }
83101
83162
  async init() {
@@ -83129,12 +83190,69 @@ ${key}` : key;
83129
83190
  super.removeFromScene();
83130
83191
  }
83131
83192
  clearModels() {
83132
- for (const model of this._models.splice(0)) {
83193
+ this.removeModels(this._models.splice(0));
83194
+ this._isInstanced = false;
83195
+ this._instanceCounter = 0;
83196
+ }
83197
+ removeModels(models) {
83198
+ for (const model of models) {
83133
83199
  model.removeFromScene();
83134
83200
  if (!isInstanceReference(model)) model.dispose();
83135
83201
  }
83136
- this._isInstanced = false;
83137
- this._instanceCounter = 0;
83202
+ }
83203
+ get isOccluding() {
83204
+ return this._models.some((model) => {
83205
+ const object = isInstanceReference(model) ? model.instanceable : model;
83206
+ return object.isOpaqueFullCube && this.getModelCullMask(model, 63) === 63;
83207
+ });
83208
+ }
83209
+ getModelMatrix(model) {
83210
+ if (isInstanceReference(model)) return model.getMatrix();
83211
+ if (model.matrixAutoUpdate) model.updateMatrix();
83212
+ return model.matrix.clone();
83213
+ }
83214
+ getModelCullMask(model, worldMask) {
83215
+ const object = isInstanceReference(model) ? model.instanceable : model;
83216
+ if (object.options.displayPosition) return 0;
83217
+ const position = new Vector3();
83218
+ const rotation = new Quaternion();
83219
+ const scale = new Vector3();
83220
+ this.getModelMatrix(model).decompose(position, rotation, scale);
83221
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new Vector3(1, 1, 1)) > 1e-10) return 0;
83222
+ return ModelCulling.toLocalMask(worldMask, new Euler().setFromQuaternion(rotation));
83223
+ }
83224
+ async setCullMask(worldMask) {
83225
+ worldMask &= 63;
83226
+ const replacements = [];
83227
+ try {
83228
+ for (const model of this._models) {
83229
+ const object = isInstanceReference(model) ? model.instanceable : model;
83230
+ const cullMask = this.getModelCullMask(model, worldMask);
83231
+ if (cullMask === (object.options.cullMask ?? 0)) {
83232
+ replacements.push(model);
83233
+ continue;
83234
+ }
83235
+ const matrix = this.getModelMatrix(model);
83236
+ const replacement = await this.scene.addModel(object.originalModel, { ...object.options, cullMask });
83237
+ replacements.push(replacement);
83238
+ if (isInstanceReference(replacement)) {
83239
+ replacement.setMatrix(matrix);
83240
+ } else {
83241
+ replacement.matrix.copy(matrix);
83242
+ matrix.decompose(replacement.position, replacement.quaternion, replacement.scale);
83243
+ replacement.matrixAutoUpdate = object.matrixAutoUpdate;
83244
+ replacement.visible = object.visible;
83245
+ }
83246
+ }
83247
+ } catch (error) {
83248
+ this.removeModels(replacements.filter((model) => !this._models.includes(model)));
83249
+ throw error;
83250
+ }
83251
+ const previous = this._models;
83252
+ this._models = replacements;
83253
+ this._cullMask = worldMask;
83254
+ this.removeModels(previous.filter((model) => !replacements.includes(model)));
83255
+ this.notifyDirty();
83138
83256
  }
83139
83257
  get state() {
83140
83258
  return this._state;
@@ -83227,7 +83345,8 @@ ${key}` : key;
83227
83345
  const options = {
83228
83346
  ...this.options,
83229
83347
  tints: model ? await BlockTints.get(this.blockState.key, this.state, model, this.options.tints) : this.options.tints,
83230
- uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
83348
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0,
83349
+ cullMask: this.options.displayPosition ? 0 : ModelCulling.toLocalMask(this._cullMask, rotation)
83231
83350
  };
83232
83351
  const obj = await this.scene.addModel(model, options);
83233
83352
  if (isInstanceReference(obj) || obj.isInstanced) {
@@ -84539,11 +84658,12 @@ ${key}` : key;
84539
84658
  console.log("instanceMeshes + key");
84540
84659
  let key = asset.key.serialize();
84541
84660
  if (isModelObject(obj)) {
84542
- const { displayPosition, uvLockRotation, tints } = obj.options;
84661
+ const { displayPosition, uvLockRotation, tints, cullMask } = obj.options;
84543
84662
  if (displayPosition) key += `|display:${displayPosition}`;
84544
84663
  if (uvLockRotation) key += `|uvlock:${uvLockRotation.join(",")}`;
84545
84664
  const palette = Object.entries(tints ?? {}).sort(([a4], [b4]) => Number(a4) - Number(b4));
84546
84665
  if (palette.length) key += `|tints:${JSON.stringify(palette)}`;
84666
+ if (cullMask) key += `|cull:${cullMask}`;
84547
84667
  }
84548
84668
  return this.instanceManager.getOrCreate(key, async () => {
84549
84669
  obj.scene = this;
@@ -87635,6 +87755,7 @@ uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuf
87635
87755
  var MineRenderWorld = class {
87636
87756
  constructor(scene) {
87637
87757
  this._chunks = /* @__PURE__ */ new Map();
87758
+ this.pendingCulling = /* @__PURE__ */ new Map();
87638
87759
  this.scene = scene;
87639
87760
  }
87640
87761
  getBlockAt(posOrX, y5, z4) {
@@ -87695,16 +87816,54 @@ uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuf
87695
87816
  }
87696
87817
  }
87697
87818
  async clear() {
87698
- for (const chunk of this._chunks.values()) {
87819
+ await this.culling;
87820
+ const chunks = [...this._chunks.values()];
87821
+ this._chunks.clear();
87822
+ for (const chunk of chunks) {
87699
87823
  await chunk.dispose();
87700
87824
  }
87701
- this._chunks.clear();
87825
+ }
87826
+ updateCulling(positions) {
87827
+ for (const pos of positions) {
87828
+ this.pendingCulling.set(pos.toArray().join(","), pos.clone());
87829
+ for (const offset of CUBE_FACE_OFFSETS) {
87830
+ const neighbor = pos.clone().add(new Vector3(...offset));
87831
+ this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
87832
+ }
87833
+ }
87834
+ return this.culling ?? (this.culling = Promise.resolve().then(async () => {
87835
+ try {
87836
+ while (this.pendingCulling.size) {
87837
+ const batch = [...this.pendingCulling.values()];
87838
+ this.pendingCulling.clear();
87839
+ for (const pos of batch) {
87840
+ const block = this.getBlockAt(pos)?.object;
87841
+ if (!block) continue;
87842
+ let mask = 0;
87843
+ for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
87844
+ if (this.getBlockAt(pos.clone().add(new Vector3(...offset)))?.object.isOccluding) {
87845
+ mask |= 1 << face;
87846
+ }
87847
+ }
87848
+ await block.setCullMask(mask);
87849
+ }
87850
+ }
87851
+ } finally {
87852
+ this.culling = void 0;
87853
+ }
87854
+ }));
87702
87855
  }
87703
87856
  getOrCreateChunkAt(pos) {
87704
87857
  const key = this.worldPosToChunkKey(pos);
87705
87858
  let chunk = this._chunks.get(key);
87706
87859
  if (typeof chunk === "undefined") {
87707
- chunk = new Chunk(this.scene, Math.floor(pos.x / 16), Math.floor(pos.y / 16), Math.floor(pos.z / 16));
87860
+ chunk = new Chunk(
87861
+ this.scene,
87862
+ Math.floor(pos.x / 16),
87863
+ Math.floor(pos.y / 16),
87864
+ Math.floor(pos.z / 16),
87865
+ (positions) => this.updateCulling(positions)
87866
+ );
87708
87867
  this._chunks.set(key, chunk);
87709
87868
  }
87710
87869
  return chunk;
@@ -87741,7 +87900,8 @@ uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuf
87741
87900
 
87742
87901
  // src/world/Chunk.ts
87743
87902
  var Chunk = class _Chunk {
87744
- constructor(scene, x4, y5, z4) {
87903
+ constructor(scene, x4, y5, z4, onBlocksChanged) {
87904
+ this.onBlocksChanged = onBlocksChanged;
87745
87905
  this.data = new ChunkData();
87746
87906
  this.renderedBlocks = /* @__PURE__ */ new Map();
87747
87907
  this.scene = scene;
@@ -87784,10 +87944,10 @@ uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuf
87784
87944
  const readBlock = this.data.snapshot(index);
87785
87945
  this.renderedBlocks.get(index)?.object.removeFromScene();
87786
87946
  this.renderedBlocks.delete(index);
87787
- if (!readBlock) return void 0;
87788
- const stored = readBlock();
87789
87947
  let object;
87790
87948
  try {
87949
+ if (!readBlock) return void 0;
87950
+ const stored = readBlock();
87791
87951
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
87792
87952
  if (!blockState) {
87793
87953
  this.data.set(index, void 0);
@@ -87809,12 +87969,18 @@ uniform float opacity;varying vec2 vUv;void main(){vec4 texel=texture2D(inputBuf
87809
87969
  this.data.set(index, void 0);
87810
87970
  object?.removeFromScene();
87811
87971
  throw error;
87972
+ } finally {
87973
+ await this.onBlocksChanged?.([worldPos]);
87812
87974
  }
87813
87975
  }
87814
87976
  async clear() {
87977
+ const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
87978
+ new Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
87979
+ ));
87815
87980
  for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
87816
87981
  this.renderedBlocks.clear();
87817
87982
  this.data.clear();
87983
+ await this.onBlocksChanged?.(positions);
87818
87984
  }
87819
87985
  async dispose() {
87820
87986
  await this.clear();