minerender 2.1.0-alpha.40 → 2.1.0-alpha.42

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.
@@ -37,8 +37,8 @@ var require_dist = __commonJS({
37
37
  __assign = Object.assign || function(t) {
38
38
  for (var s, i = 1, n = arguments.length; i < n; i++) {
39
39
  s = arguments[i];
40
- for (var p11 in s) if (Object.prototype.hasOwnProperty.call(s, p11))
41
- t[p11] = s[p11];
40
+ for (var p10 in s) if (Object.prototype.hasOwnProperty.call(s, p10))
41
+ t[p10] = s[p10];
42
42
  }
43
43
  return t;
44
44
  };
@@ -139,6 +139,7 @@ __export(index_browser_exports, {
139
139
  BasicAssetKey: () => BasicAssetKey,
140
140
  BatchedExecutor: () => BatchedExecutor,
141
141
  BlockObject: () => BlockObject,
142
+ BlockStateResolver: () => BlockStateResolver,
142
143
  BlockStates: () => BlockStates,
143
144
  BlockTints: () => BlockTints,
144
145
  BrowserArchiveProxy: () => BrowserArchiveProxy,
@@ -197,6 +198,8 @@ __export(index_browser_exports, {
197
198
  SceneStats: () => SceneStats,
198
199
  SceneStatsDisplay: () => SceneStatsDisplay,
199
200
  SchematicParser: () => SchematicParser,
201
+ SectionMesh: () => SectionMesh,
202
+ SectionModels: () => SectionModels,
200
203
  SkinImage: () => SkinImage,
201
204
  SkinObject: () => SkinObject,
202
205
  SkinPart: () => SkinPart,
@@ -928,11 +931,11 @@ var ImageLoader = class _ImageLoader {
928
931
  static async loadCanvasData(src) {
929
932
  const keyStr = serializeImageKey({ src });
930
933
  const info = this.getInfo(src);
931
- const cached = Caching.rawImageCache.getIfPresent(keyStr);
934
+ const cached2 = Caching.rawImageCache.getIfPresent(keyStr);
932
935
  try {
933
936
  return await this.infoToCanvasData(await info);
934
937
  } catch (err) {
935
- if (cached && Caching.rawImageCache.getIfPresent(keyStr) === cached) {
938
+ if (cached2 && Caching.rawImageCache.getIfPresent(keyStr) === cached2) {
936
939
  Caching.rawImageCache.invalidate(keyStr);
937
940
  }
938
941
  throw err;
@@ -1067,8 +1070,8 @@ var Textures = class _Textures {
1067
1070
  }
1068
1071
  static getImage(key) {
1069
1072
  const keyStr = serializeTextureKey(key);
1070
- const cached = Caching.textureCache.peek(keyStr);
1071
- if (cached && TextureLoader.hasFailed(cached)) {
1073
+ const cached2 = Caching.textureCache.peek(keyStr);
1074
+ if (cached2 && TextureLoader.hasFailed(cached2)) {
1072
1075
  Caching.textureCache.invalidate(keyStr);
1073
1076
  }
1074
1077
  return Caching.textureCache.get(keyStr, (k) => {
@@ -1335,12 +1338,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1335
1338
  return a.join("/");
1336
1339
  }
1337
1340
  getFullPath() {
1338
- let p11 = [];
1341
+ let p10 = [];
1339
1342
  if (this.type) {
1340
- p11.push(this.type);
1343
+ p10.push(this.type);
1341
1344
  }
1342
- p11.push(this.path);
1343
- return p11.join("/");
1345
+ p10.push(this.path);
1346
+ return p10.join("/");
1344
1347
  }
1345
1348
  toNamespacedString() {
1346
1349
  return this.namespace + ":" + this.getFullPath();
@@ -1427,7 +1430,7 @@ var ModelMerger = class {
1427
1430
  merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1428
1431
  }
1429
1432
  }
1430
- merged.hierarchy = models.map((m) => m.parent).filter((p11) => `${p11}`);
1433
+ merged.hierarchy = models.map((m) => m.parent).filter((p10) => `${p10}`);
1431
1434
  merged.hierarchy.push(`${merged.parent}`);
1432
1435
  return merged;
1433
1436
  }
@@ -2134,7 +2137,7 @@ var ModelTextures = class {
2134
2137
  static async get(key) {
2135
2138
  const keyStr = key.serialize();
2136
2139
  const pending = this.preload(key);
2137
- const cached = Caching.textureAssetCache.getIfPresent(keyStr);
2140
+ const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
2138
2141
  try {
2139
2142
  const asset = await pending;
2140
2143
  if (asset) {
@@ -2142,7 +2145,7 @@ var ModelTextures = class {
2142
2145
  }
2143
2146
  return void 0;
2144
2147
  } catch (err) {
2145
- if (cached && Caching.textureAssetCache.getIfPresent(keyStr) === cached) {
2148
+ if (cached2 && Caching.textureAssetCache.getIfPresent(keyStr) === cached2) {
2146
2149
  Caching.textureAssetCache.invalidate(keyStr);
2147
2150
  }
2148
2151
  throw err;
@@ -4018,6 +4021,93 @@ var ModelCulling = class {
4018
4021
  }
4019
4022
  };
4020
4023
 
4024
+ // src/model/block/BlockStateResolver.ts
4025
+ var import_three17 = require("three");
4026
+ function matchesCondition(condition, state) {
4027
+ return Object.entries(condition).every(([key, value]) => {
4028
+ if (Array.isArray(value)) {
4029
+ if (key === "OR") return value.some((child) => matchesCondition(child, state));
4030
+ if (key === "AND") return value.every((child) => matchesCondition(child, state));
4031
+ return false;
4032
+ }
4033
+ return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
4034
+ });
4035
+ }
4036
+ var BlockStateResolver = class {
4037
+ static async defaults(blockState) {
4038
+ const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
4039
+ const state = {};
4040
+ if (defaults && Object.keys(defaults).length > 0) {
4041
+ for (const key in defaults) state[key] = defaults[key].default;
4042
+ } else if (blockState.variants) {
4043
+ const variant = Object.keys(blockState.variants)[0];
4044
+ if (variant) {
4045
+ for (const property of variant.split(",")) {
4046
+ const [key, value] = property.split("=");
4047
+ state[key] = value;
4048
+ }
4049
+ }
4050
+ } else if (blockState.multipart) {
4051
+ const condition = blockState.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
4052
+ for (const [key, value] of Object.entries(condition ?? {})) state[key] = value.split("|")[0];
4053
+ }
4054
+ return state;
4055
+ }
4056
+ static select(blockState, state, choose = this.choose) {
4057
+ return this.matching(blockState, state).map(choose);
4058
+ }
4059
+ static matching(blockState, state) {
4060
+ const out = [];
4061
+ if (blockState.variants) {
4062
+ if (Object.keys(blockState.variants).length === 1 && "" in blockState.variants) {
4063
+ out.push(blockState.variants[""]);
4064
+ } else {
4065
+ for (const variantKey in blockState.variants) {
4066
+ const matches = variantKey.split(",").every((property) => {
4067
+ const [key, value] = property.split("=");
4068
+ return `${state[key]}` === `${value}`;
4069
+ });
4070
+ if (matches) out.push(blockState.variants[variantKey]);
4071
+ }
4072
+ }
4073
+ } else if (blockState.multipart) {
4074
+ for (const part of blockState.multipart) {
4075
+ if (part.apply && (!part.when || matchesCondition(part.when, state))) {
4076
+ out.push(part.apply);
4077
+ }
4078
+ }
4079
+ }
4080
+ return out;
4081
+ }
4082
+ static choose(variants) {
4083
+ if (!Array.isArray(variants)) return variants;
4084
+ if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
4085
+ const total = variants.reduce((sum, variant) => {
4086
+ const weight = variant.weight ?? 1;
4087
+ if (!Number.isInteger(weight) || weight < 1) {
4088
+ throw new MineRenderError(`Invalid blockstate variant weight: ${weight}`);
4089
+ }
4090
+ return sum + weight;
4091
+ }, 0);
4092
+ let choice = Math.random() * total;
4093
+ for (const variant of variants) {
4094
+ choice -= variant.weight ?? 1;
4095
+ if (choice < 0) return variant;
4096
+ }
4097
+ return variants[variants.length - 1];
4098
+ }
4099
+ static rotation(variant) {
4100
+ const rotation = new import_three17.Euler();
4101
+ if (typeof variant.x !== "undefined") {
4102
+ rotation.x = toRadians(clampRotationDegrees(variant.x));
4103
+ }
4104
+ if (typeof variant.y !== "undefined") {
4105
+ rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4106
+ }
4107
+ return rotation;
4108
+ }
4109
+ };
4110
+
4021
4111
  // src/model/block/blockTints.json
4022
4112
  var blockTints_default = {
4023
4113
  "minecraft:large_fern": { source: "grass" },
@@ -4104,10 +4194,10 @@ var BlockTints = class {
4104
4194
  };
4105
4195
 
4106
4196
  // src/model/scene/ModelObject.ts
4107
- var import_three18 = require("three");
4197
+ var import_three19 = require("three");
4108
4198
 
4109
4199
  // src/three/BufferGeometryUtils.js
4110
- var import_three17 = require("three");
4200
+ var import_three18 = require("three");
4111
4201
  function computeTangents(geometry) {
4112
4202
  geometry.computeTangents();
4113
4203
  console.warn("THREE.BufferGeometryUtils: .computeTangents() has been removed. Use BufferGeometry.computeTangents() instead.");
@@ -4119,7 +4209,7 @@ function mergeBufferGeometries(geometries, useGroups = false) {
4119
4209
  const attributes = {};
4120
4210
  const morphAttributes = {};
4121
4211
  const morphTargetsRelative = geometries[0].morphTargetsRelative;
4122
- const mergedGeometry = new import_three17.BufferGeometry();
4212
+ const mergedGeometry = new import_three18.BufferGeometry();
4123
4213
  let offset = 0;
4124
4214
  for (let i = 0; i < geometries.length; ++i) {
4125
4215
  const geometry = geometries[i];
@@ -4243,7 +4333,7 @@ function mergeBufferAttributes(attributes) {
4243
4333
  array.set(attributes[i].array, offset);
4244
4334
  offset += attributes[i].array.length;
4245
4335
  }
4246
- return new import_three17.BufferAttribute(array, itemSize, normalized);
4336
+ return new import_three18.BufferAttribute(array, itemSize, normalized);
4247
4337
  }
4248
4338
  function interleaveAttributes(attributes) {
4249
4339
  let TypedArray;
@@ -4259,7 +4349,7 @@ function interleaveAttributes(attributes) {
4259
4349
  arrayLength += attribute.array.length;
4260
4350
  stride += attribute.itemSize;
4261
4351
  }
4262
- const interleavedBuffer = new import_three17.InterleavedBuffer(new TypedArray(arrayLength), stride);
4352
+ const interleavedBuffer = new import_three18.InterleavedBuffer(new TypedArray(arrayLength), stride);
4263
4353
  let offset = 0;
4264
4354
  const res = [];
4265
4355
  const getters = ["getX", "getY", "getZ", "getW"];
@@ -4268,7 +4358,7 @@ function interleaveAttributes(attributes) {
4268
4358
  const attribute = attributes[j];
4269
4359
  const itemSize = attribute.itemSize;
4270
4360
  const count = attribute.count;
4271
- const iba = new import_three17.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4361
+ const iba = new import_three18.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4272
4362
  res.push(iba);
4273
4363
  offset += itemSize;
4274
4364
  for (let c = 0; c < count; c++) {
@@ -4352,13 +4442,13 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4352
4442
  const name = attributeNames[i];
4353
4443
  const oldAttribute = geometry.getAttribute(name);
4354
4444
  const buffer = new oldAttribute.array.constructor(attrArrays[name]);
4355
- const attribute = new import_three17.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4445
+ const attribute = new import_three18.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4356
4446
  result.setAttribute(name, attribute);
4357
4447
  if (name in morphAttrsArrays) {
4358
4448
  for (let j = 0; j < morphAttrsArrays[name].length; j++) {
4359
4449
  const oldMorphAttribute = geometry.morphAttributes[name][j];
4360
4450
  const buffer2 = new oldMorphAttribute.array.constructor(morphAttrsArrays[name][j]);
4361
- const morphAttribute = new import_three17.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4451
+ const morphAttribute = new import_three18.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4362
4452
  result.morphAttributes[name][j] = morphAttribute;
4363
4453
  }
4364
4454
  }
@@ -4367,11 +4457,11 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4367
4457
  return result;
4368
4458
  }
4369
4459
  function toTrianglesDrawMode(geometry, drawMode) {
4370
- if (drawMode === import_three17.TrianglesDrawMode) {
4460
+ if (drawMode === import_three18.TrianglesDrawMode) {
4371
4461
  console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.");
4372
4462
  return geometry;
4373
4463
  }
4374
- if (drawMode === import_three17.TriangleFanDrawMode || drawMode === import_three17.TriangleStripDrawMode) {
4464
+ if (drawMode === import_three18.TriangleFanDrawMode || drawMode === import_three18.TriangleStripDrawMode) {
4375
4465
  let index = geometry.getIndex();
4376
4466
  if (index === null) {
4377
4467
  const indices = [];
@@ -4389,7 +4479,7 @@ function toTrianglesDrawMode(geometry, drawMode) {
4389
4479
  }
4390
4480
  const numberOfTriangles = index.count - 2;
4391
4481
  const newIndices = [];
4392
- if (drawMode === import_three17.TriangleFanDrawMode) {
4482
+ if (drawMode === import_three18.TriangleFanDrawMode) {
4393
4483
  for (let i = 1; i <= numberOfTriangles; i++) {
4394
4484
  newIndices.push(index.getX(0));
4395
4485
  newIndices.push(index.getX(i));
@@ -4425,15 +4515,15 @@ function computeMorphedAttributes(object) {
4425
4515
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4426
4516
  return null;
4427
4517
  }
4428
- const _vA = new import_three17.Vector3();
4429
- const _vB = new import_three17.Vector3();
4430
- const _vC = new import_three17.Vector3();
4431
- const _tempA = new import_three17.Vector3();
4432
- const _tempB = new import_three17.Vector3();
4433
- const _tempC = new import_three17.Vector3();
4434
- const _morphA = new import_three17.Vector3();
4435
- const _morphB = new import_three17.Vector3();
4436
- const _morphC = new import_three17.Vector3();
4518
+ const _vA = new import_three18.Vector3();
4519
+ const _vB = new import_three18.Vector3();
4520
+ const _vC = new import_three18.Vector3();
4521
+ const _tempA = new import_three18.Vector3();
4522
+ const _tempB = new import_three18.Vector3();
4523
+ const _tempC = new import_three18.Vector3();
4524
+ const _morphA = new import_three18.Vector3();
4525
+ const _morphB = new import_three18.Vector3();
4526
+ const _morphC = new import_three18.Vector3();
4437
4527
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4438
4528
  _vA.fromBufferAttribute(attribute, a2);
4439
4529
  _vB.fromBufferAttribute(attribute, b2);
@@ -4628,8 +4718,8 @@ function computeMorphedAttributes(object) {
4628
4718
  }
4629
4719
  }
4630
4720
  }
4631
- const morphedPositionAttribute = new import_three17.Float32BufferAttribute(modifiedPosition, 3);
4632
- const morphedNormalAttribute = new import_three17.Float32BufferAttribute(modifiedNormal, 3);
4721
+ const morphedPositionAttribute = new import_three18.Float32BufferAttribute(modifiedPosition, 3);
4722
+ const morphedNormalAttribute = new import_three18.Float32BufferAttribute(modifiedNormal, 3);
4633
4723
  return {
4634
4724
  positionAttribute,
4635
4725
  normalAttribute,
@@ -4685,7 +4775,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4685
4775
  elGeo.clearGroups();
4686
4776
  }
4687
4777
  if (this.options.uvLockRotation) {
4688
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three18.Euler(...this.options.uvLockRotation));
4778
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three19.Euler(...this.options.uvLockRotation));
4689
4779
  }
4690
4780
  if (this.options.tints) {
4691
4781
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4694,19 +4784,19 @@ var _ModelObject = class _ModelObject extends SceneObject {
4694
4784
  if (tintIndex === void 0 || tintIndex < 0) continue;
4695
4785
  const tint = this.options.tints[tintIndex];
4696
4786
  if (tint === void 0) continue;
4697
- const color = new import_three18.Color(tint);
4787
+ const color = new import_three19.Color(tint);
4698
4788
  for (let vertex = 0; vertex < 4; vertex++) {
4699
4789
  color.toArray(colors, (faceIndex * 4 + vertex) * 3);
4700
4790
  }
4701
4791
  }
4702
- elGeo.setAttribute("color", new import_three18.BufferAttribute(colors, 3));
4792
+ elGeo.setAttribute("color", new import_three19.BufferAttribute(colors, 3));
4703
4793
  }
4704
- elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4705
- elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4794
+ elGeo.applyMatrix4(new import_three19.Matrix4().makeTranslation((el.to[0] - el.from[0]) / 2, (el.to[1] - el.from[1]) / 2, (el.to[2] - el.from[2]) / 2));
4795
+ elGeo.applyMatrix4(new import_three19.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4706
4796
  if (el.rotation) {
4707
4797
  applyElementRotation(el.rotation, elGeo);
4708
4798
  }
4709
- elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(-8, -8, -8));
4799
+ elGeo.applyMatrix4(new import_three19.Matrix4().makeTranslation(-8, -8, -8));
4710
4800
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4711
4801
  if (this.options.mergeMeshes) {
4712
4802
  allGeos.push(elGeo);
@@ -4728,7 +4818,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4728
4818
  if (allGeos.length > 0) {
4729
4819
  combinedGeo = mergeBufferGeometries(allGeos);
4730
4820
  } else {
4731
- combinedGeo = new import_three18.BoxGeometry(16, 16, 16);
4821
+ combinedGeo = new import_three19.BoxGeometry(16, 16, 16);
4732
4822
  if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
4733
4823
  }
4734
4824
  combinedGeo.computeBoundingBox();
@@ -4783,18 +4873,7 @@ function isModelObject(obj) {
4783
4873
 
4784
4874
  // src/model/block/scene/BlockObject.ts
4785
4875
  var import_ts_deepmerge6 = __toESM(require_dist());
4786
- var import_three19 = require("three");
4787
- var p10 = prefix("BlockObject");
4788
- function matchesCondition(condition, state) {
4789
- return Object.entries(condition).every(([key, value]) => {
4790
- if (Array.isArray(value)) {
4791
- if (key === "OR") return value.some((child) => matchesCondition(child, state));
4792
- if (key === "AND") return value.every((child) => matchesCondition(child, state));
4793
- return false;
4794
- }
4795
- return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
4796
- });
4797
- }
4876
+ var import_three20 = require("three");
4798
4877
  var _BlockObject = class _BlockObject extends SceneObject {
4799
4878
  constructor(blockState, options) {
4800
4879
  super(options);
@@ -4813,22 +4892,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4813
4892
  }
4814
4893
  async init() {
4815
4894
  if (this.options.applyDefaultState) {
4816
- const defaultState = this.blockState.key ? await BlockStates.getDefaultState(this.blockState.key) : void 0;
4817
- if (defaultState && Object.keys(defaultState).length > 0) {
4818
- const state = {};
4819
- for (let k in defaultState) {
4820
- state[k] = defaultState[k].default;
4821
- }
4822
- this._setState(state);
4823
- } else {
4824
- if (this.blockState.variants) {
4825
- this._setState(Object.keys(this.blockState.variants)[0]);
4826
- } else if (this.blockState.multipart) {
4827
- const condition = this.blockState.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
4828
- const state = Object.fromEntries(Object.entries(condition ?? {}).map(([key, value]) => [key, value.split("|")[0]]));
4829
- this._setState(state);
4830
- }
4831
- }
4895
+ this._setState(await BlockStateResolver.defaults(this.blockState));
4832
4896
  }
4833
4897
  if (this.options.initialState !== void 0) this._setState(this.options.initialState);
4834
4898
  await this.recreateModels();
@@ -4866,12 +4930,12 @@ var _BlockObject = class _BlockObject extends SceneObject {
4866
4930
  getModelCullMask(model, worldMask) {
4867
4931
  const object = isInstanceReference(model) ? model.instanceable : model;
4868
4932
  if (object.options.displayPosition) return 0;
4869
- const position = new import_three19.Vector3();
4870
- const rotation = new import_three19.Quaternion();
4871
- const scale = new import_three19.Vector3();
4933
+ const position = new import_three20.Vector3();
4934
+ const rotation = new import_three20.Quaternion();
4935
+ const scale = new import_three20.Vector3();
4872
4936
  this.getModelMatrix(model).decompose(position, rotation, scale);
4873
- if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new import_three19.Vector3(1, 1, 1)) > 1e-10) return 0;
4874
- return ModelCulling.toLocalMask(worldMask, new import_three19.Euler().setFromQuaternion(rotation));
4937
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new import_three20.Vector3(1, 1, 1)) > 1e-10) return 0;
4938
+ return ModelCulling.toLocalMask(worldMask, new import_three20.Euler().setFromQuaternion(rotation));
4875
4939
  }
4876
4940
  async setCullMask(worldMask) {
4877
4941
  worldMask &= 63;
@@ -4919,39 +4983,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4919
4983
  return ref;
4920
4984
  }
4921
4985
  async mapStateToVariant(state) {
4922
- const out = [];
4923
- if (this.blockState.variants) {
4924
- if (Object.keys(this.blockState.variants).length === 1 && "" in this.blockState.variants) {
4925
- out.push(this.getSingleVariant(this.blockState.variants[""]));
4926
- } else {
4927
- for (let variantKey in this.blockState.variants) {
4928
- const split = variantKey.split(",");
4929
- let matches = true;
4930
- for (let s of split) {
4931
- const [k, v] = s.split("=");
4932
- if (`${state[k]}` !== `${v}`) {
4933
- matches = false;
4934
- break;
4935
- }
4936
- }
4937
- if (matches) {
4938
- const variants = this.blockState.variants[variantKey];
4939
- out.push(this.getSingleVariant(variants));
4940
- }
4941
- }
4942
- }
4943
- } else if (this.blockState.multipart) {
4944
- for (let part of this.blockState.multipart) {
4945
- if (!part.apply) {
4946
- console.debug(p10, "Missing apply for blockState part", part);
4947
- continue;
4948
- }
4949
- if (!part.when || matchesCondition(part.when, state)) {
4950
- out.push(this.getSingleVariant(part.apply));
4951
- }
4952
- }
4953
- }
4954
- return out;
4986
+ return BlockStateResolver.select(this.blockState, state, (variants) => this.getSingleVariant(variants));
4955
4987
  }
4956
4988
  async recreateModels() {
4957
4989
  this.clearModels();
@@ -4962,21 +4994,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4962
4994
  }
4963
4995
  }
4964
4996
  getSingleVariant(variants) {
4965
- if (!Array.isArray(variants)) return variants;
4966
- if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
4967
- const total = variants.reduce((sum, variant) => {
4968
- const weight = variant.weight ?? 1;
4969
- if (!Number.isInteger(weight) || weight < 1) {
4970
- throw new MineRenderError(`Invalid blockstate variant weight: ${weight}`);
4971
- }
4972
- return sum + weight;
4973
- }, 0);
4974
- let choice = Math.random() * total;
4975
- for (const variant of variants) {
4976
- choice -= variant.weight ?? 1;
4977
- if (choice < 0) return variant;
4978
- }
4979
- return variants[variants.length - 1];
4997
+ return BlockStateResolver.choose(variants);
4980
4998
  }
4981
4999
  // @deprecated
4982
5000
  async createAVariant(variants, instanceInfo) {
@@ -4984,13 +5002,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4984
5002
  await this.createVariant(variant);
4985
5003
  }
4986
5004
  async createVariant(variant) {
4987
- const rotation = new import_three19.Euler();
4988
- if (typeof variant.x !== "undefined") {
4989
- rotation.x = toRadians(clampRotationDegrees(variant.x));
4990
- }
4991
- if (typeof variant.y !== "undefined") {
4992
- rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4993
- }
5005
+ const rotation = BlockStateResolver.rotation(variant);
4994
5006
  const modelKey = AssetKey.parse("models", variant.model);
4995
5007
  modelKey.root = this.blockState.key?.root;
4996
5008
  const model = await Models.getMerged(modelKey);
@@ -5075,7 +5087,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
5075
5087
  setPositionRotationScaleAt(index, position, rotation, scale) {
5076
5088
  super.setPositionRotationScaleAt(index, position, rotation, scale);
5077
5089
  }
5078
- getMatrixAt(index, matrix = new import_three19.Matrix4()) {
5090
+ getMatrixAt(index, matrix = new import_three20.Matrix4()) {
5079
5091
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
5080
5092
  const child = this._models[0];
5081
5093
  if (child && isModelObject(child)) {
@@ -6931,7 +6943,7 @@ var StructureParser = class {
6931
6943
  };
6932
6944
 
6933
6945
  // src/renderer/MineRenderScene.ts
6934
- var import_three21 = require("three");
6946
+ var import_three22 = require("three");
6935
6947
  var import_ts_deepmerge8 = __toESM(require_dist());
6936
6948
 
6937
6949
  // src/skin/SkinPart.ts
@@ -7656,7 +7668,7 @@ var slimSkinGeometries = {
7656
7668
  var import_ts_deepmerge7 = __toESM(require_dist());
7657
7669
 
7658
7670
  // src/skin/SkinTextures.ts
7659
- var import_three20 = require("three");
7671
+ var import_three21 = require("three");
7660
7672
 
7661
7673
  // src/skin/SkinImage.ts
7662
7674
  var SkinImage = class {
@@ -7733,11 +7745,11 @@ var SkinTextures = class {
7733
7745
  });
7734
7746
  }
7735
7747
  static createMaterial(pixels, width, height, transparent = true) {
7736
- const texture = Textures.initTextureProps(new import_three20.DataTexture(pixels, width, height));
7737
- texture.colorSpace = import_three20.SRGBColorSpace;
7748
+ const texture = Textures.initTextureProps(new import_three21.DataTexture(pixels, width, height));
7749
+ texture.colorSpace = import_three21.SRGBColorSpace;
7738
7750
  texture.flipY = true;
7739
7751
  texture.needsUpdate = true;
7740
- return new import_three20.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three20.DoubleSide : import_three20.FrontSide, alphaTest: transparent ? 0.1 : 0 });
7752
+ return new import_three21.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three21.DoubleSide : import_three21.FrontSide, alphaTest: transparent ? 0.1 : 0 });
7741
7753
  }
7742
7754
  static isSlim(image, scale) {
7743
7755
  for (const [x, y] of [[46, 52], [54, 20]]) {
@@ -7964,7 +7976,7 @@ var SkinObject = class extends SceneObject {
7964
7976
  };
7965
7977
 
7966
7978
  // src/renderer/MineRenderScene.ts
7967
- var _MineRenderScene = class _MineRenderScene extends import_three21.Scene {
7979
+ var _MineRenderScene = class _MineRenderScene extends import_three22.Scene {
7968
7980
  constructor(options) {
7969
7981
  super();
7970
7982
  this.isMineRenderScene = true;
@@ -8088,17 +8100,17 @@ function isMineRenderScene(obj) {
8088
8100
  }
8089
8101
 
8090
8102
  // src/renderer/Renderer.ts
8091
- var import_three23 = require("three");
8103
+ var import_three24 = require("three");
8092
8104
  var import_ts_deepmerge9 = __toESM(require_dist());
8093
8105
  var import_stats = __toESM(require("stats.js"));
8094
8106
  var import_postprocessing = require("postprocessing");
8095
8107
 
8096
8108
  // src/three/OrbitControls.js
8097
- var import_three22 = require("three");
8109
+ var import_three23 = require("three");
8098
8110
  var _changeEvent = { type: "change" };
8099
8111
  var _startEvent = { type: "start" };
8100
8112
  var _endEvent = { type: "end" };
8101
- var OrbitControls = class extends import_three22.EventDispatcher {
8113
+ var OrbitControls = class extends import_three23.EventDispatcher {
8102
8114
  constructor(object, domElement) {
8103
8115
  super();
8104
8116
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
@@ -8107,7 +8119,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8107
8119
  this.domElement = domElement;
8108
8120
  this.domElement.style.touchAction = "none";
8109
8121
  this.enabled = true;
8110
- this.target = new import_three22.Vector3();
8122
+ this.target = new import_three23.Vector3();
8111
8123
  this.minDistance = 0;
8112
8124
  this.maxDistance = Infinity;
8113
8125
  this.minZoom = 0;
@@ -8129,8 +8141,8 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8129
8141
  this.autoRotate = false;
8130
8142
  this.autoRotateSpeed = 2;
8131
8143
  this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" };
8132
- this.mouseButtons = { LEFT: import_three22.MOUSE.ROTATE, MIDDLE: import_three22.MOUSE.DOLLY, RIGHT: import_three22.MOUSE.PAN };
8133
- this.touches = { ONE: import_three22.TOUCH.ROTATE, TWO: import_three22.TOUCH.DOLLY_PAN };
8144
+ this.mouseButtons = { LEFT: import_three23.MOUSE.ROTATE, MIDDLE: import_three23.MOUSE.DOLLY, RIGHT: import_three23.MOUSE.PAN };
8145
+ this.touches = { ONE: import_three23.TOUCH.ROTATE, TWO: import_three23.TOUCH.DOLLY_PAN };
8134
8146
  this.target0 = this.target.clone();
8135
8147
  this.position0 = this.object.position.clone();
8136
8148
  this.zoom0 = this.object.zoom;
@@ -8159,11 +8171,11 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8159
8171
  state = STATE.NONE;
8160
8172
  };
8161
8173
  this.update = function() {
8162
- const offset = new import_three22.Vector3();
8163
- const quat = new import_three22.Quaternion().setFromUnitVectors(object.up, new import_three22.Vector3(0, 1, 0));
8174
+ const offset = new import_three23.Vector3();
8175
+ const quat = new import_three23.Quaternion().setFromUnitVectors(object.up, new import_three23.Vector3(0, 1, 0));
8164
8176
  const quatInverse = quat.clone().invert();
8165
- const lastPosition = new import_three22.Vector3();
8166
- const lastQuaternion = new import_three22.Quaternion();
8177
+ const lastPosition = new import_three23.Vector3();
8178
+ const lastQuaternion = new import_three23.Quaternion();
8167
8179
  const twoPI = 2 * Math.PI;
8168
8180
  return function update() {
8169
8181
  const position = scope.object.position;
@@ -8249,20 +8261,20 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8249
8261
  };
8250
8262
  let state = STATE.NONE;
8251
8263
  const EPS = 1e-6;
8252
- const spherical = new import_three22.Spherical();
8253
- const sphericalDelta = new import_three22.Spherical();
8264
+ const spherical = new import_three23.Spherical();
8265
+ const sphericalDelta = new import_three23.Spherical();
8254
8266
  let scale = 1;
8255
- const panOffset = new import_three22.Vector3();
8267
+ const panOffset = new import_three23.Vector3();
8256
8268
  let zoomChanged = false;
8257
- const rotateStart = new import_three22.Vector2();
8258
- const rotateEnd = new import_three22.Vector2();
8259
- const rotateDelta = new import_three22.Vector2();
8260
- const panStart = new import_three22.Vector2();
8261
- const panEnd = new import_three22.Vector2();
8262
- const panDelta = new import_three22.Vector2();
8263
- const dollyStart = new import_three22.Vector2();
8264
- const dollyEnd = new import_three22.Vector2();
8265
- const dollyDelta = new import_three22.Vector2();
8269
+ const rotateStart = new import_three23.Vector2();
8270
+ const rotateEnd = new import_three23.Vector2();
8271
+ const rotateDelta = new import_three23.Vector2();
8272
+ const panStart = new import_three23.Vector2();
8273
+ const panEnd = new import_three23.Vector2();
8274
+ const panDelta = new import_three23.Vector2();
8275
+ const dollyStart = new import_three23.Vector2();
8276
+ const dollyEnd = new import_three23.Vector2();
8277
+ const dollyDelta = new import_three23.Vector2();
8266
8278
  const pointers = [];
8267
8279
  const pointerPositions = {};
8268
8280
  function getAutoRotationAngle() {
@@ -8278,7 +8290,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8278
8290
  sphericalDelta.phi -= angle;
8279
8291
  }
8280
8292
  const panLeft = function() {
8281
- const v = new import_three22.Vector3();
8293
+ const v = new import_three23.Vector3();
8282
8294
  return function panLeft2(distance, objectMatrix) {
8283
8295
  v.setFromMatrixColumn(objectMatrix, 0);
8284
8296
  v.multiplyScalar(-distance);
@@ -8286,7 +8298,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8286
8298
  };
8287
8299
  }();
8288
8300
  const panUp = function() {
8289
- const v = new import_three22.Vector3();
8301
+ const v = new import_three23.Vector3();
8290
8302
  return function panUp2(distance, objectMatrix) {
8291
8303
  if (scope.screenSpacePanning === true) {
8292
8304
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -8299,7 +8311,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8299
8311
  };
8300
8312
  }();
8301
8313
  const pan = function() {
8302
- const offset = new import_three22.Vector3();
8314
+ const offset = new import_three23.Vector3();
8303
8315
  return function pan2(deltaX, deltaY) {
8304
8316
  const element = scope.domElement;
8305
8317
  if (scope.object.isPerspectiveCamera) {
@@ -8548,12 +8560,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8548
8560
  mouseAction = -1;
8549
8561
  }
8550
8562
  switch (mouseAction) {
8551
- case import_three22.MOUSE.DOLLY:
8563
+ case import_three23.MOUSE.DOLLY:
8552
8564
  if (scope.enableZoom === false) return;
8553
8565
  handleMouseDownDolly(event);
8554
8566
  state = STATE.DOLLY;
8555
8567
  break;
8556
- case import_three22.MOUSE.ROTATE:
8568
+ case import_three23.MOUSE.ROTATE:
8557
8569
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
8558
8570
  if (scope.enablePan === false) return;
8559
8571
  handleMouseDownPan(event);
@@ -8564,7 +8576,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8564
8576
  state = STATE.ROTATE;
8565
8577
  }
8566
8578
  break;
8567
- case import_three22.MOUSE.PAN:
8579
+ case import_three23.MOUSE.PAN:
8568
8580
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
8569
8581
  if (scope.enableRotate === false) return;
8570
8582
  handleMouseDownRotate(event);
@@ -8620,12 +8632,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8620
8632
  switch (pointers.length) {
8621
8633
  case 1:
8622
8634
  switch (scope.touches.ONE) {
8623
- case import_three22.TOUCH.ROTATE:
8635
+ case import_three23.TOUCH.ROTATE:
8624
8636
  if (scope.enableRotate === false) return;
8625
8637
  handleTouchStartRotate();
8626
8638
  state = STATE.TOUCH_ROTATE;
8627
8639
  break;
8628
- case import_three22.TOUCH.PAN:
8640
+ case import_three23.TOUCH.PAN:
8629
8641
  if (scope.enablePan === false) return;
8630
8642
  handleTouchStartPan();
8631
8643
  state = STATE.TOUCH_PAN;
@@ -8636,12 +8648,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8636
8648
  break;
8637
8649
  case 2:
8638
8650
  switch (scope.touches.TWO) {
8639
- case import_three22.TOUCH.DOLLY_PAN:
8651
+ case import_three23.TOUCH.DOLLY_PAN:
8640
8652
  if (scope.enableZoom === false && scope.enablePan === false) return;
8641
8653
  handleTouchStartDollyPan();
8642
8654
  state = STATE.TOUCH_DOLLY_PAN;
8643
8655
  break;
8644
- case import_three22.TOUCH.DOLLY_ROTATE:
8656
+ case import_three23.TOUCH.DOLLY_ROTATE:
8645
8657
  if (scope.enableZoom === false && scope.enableRotate === false) return;
8646
8658
  handleTouchStartDollyRotate();
8647
8659
  state = STATE.TOUCH_DOLLY_ROTATE;
@@ -8708,7 +8720,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8708
8720
  function trackPointer(event) {
8709
8721
  let position = pointerPositions[event.pointerId];
8710
8722
  if (position === void 0) {
8711
- position = new import_three22.Vector2();
8723
+ position = new import_three23.Vector2();
8712
8724
  pointerPositions[event.pointerId] = position;
8713
8725
  }
8714
8726
  position.set(event.pageX, event.pageY);
@@ -8728,10 +8740,10 @@ var MapControls = class extends OrbitControls {
8728
8740
  constructor(object, domElement) {
8729
8741
  super(object, domElement);
8730
8742
  this.screenSpacePanning = false;
8731
- this.mouseButtons.LEFT = import_three22.MOUSE.PAN;
8732
- this.mouseButtons.RIGHT = import_three22.MOUSE.ROTATE;
8733
- this.touches.ONE = import_three22.TOUCH.PAN;
8734
- this.touches.TWO = import_three22.TOUCH.DOLLY_ROTATE;
8743
+ this.mouseButtons.LEFT = import_three23.MOUSE.PAN;
8744
+ this.mouseButtons.RIGHT = import_three23.MOUSE.ROTATE;
8745
+ this.touches.ONE = import_three23.TOUCH.PAN;
8746
+ this.touches.TWO = import_three23.TOUCH.DOLLY_ROTATE;
8735
8747
  }
8736
8748
  };
8737
8749
 
@@ -8768,14 +8780,14 @@ var _Renderer = class _Renderer {
8768
8780
  switch (this.options.camera.type) {
8769
8781
  case "perspective":
8770
8782
  default:
8771
- return new import_three23.PerspectiveCamera(
8783
+ return new import_three24.PerspectiveCamera(
8772
8784
  this.options.camera.perspective.fov,
8773
8785
  this.options.camera.perspective.aspect ?? this.viewWidth / this.viewHeight,
8774
8786
  this.options.camera.near,
8775
8787
  this.options.camera.far
8776
8788
  );
8777
8789
  case "orthographic":
8778
- return new import_three23.OrthographicCamera(
8790
+ return new import_three24.OrthographicCamera(
8779
8791
  this.options.camera.orthographic.left ?? this.viewWidth / -2,
8780
8792
  this.options.camera.orthographic.right ?? this.viewWidth / 2,
8781
8793
  this.options.camera.orthographic.top ?? this.viewHeight / 2,
@@ -8786,7 +8798,7 @@ var _Renderer = class _Renderer {
8786
8798
  }
8787
8799
  }
8788
8800
  createRenderer() {
8789
- const renderer = new import_three23.WebGLRenderer({
8801
+ const renderer = new import_three24.WebGLRenderer({
8790
8802
  antialias: this.options.render.antialias,
8791
8803
  alpha: true,
8792
8804
  powerPreference: "high-performance",
@@ -8794,8 +8806,8 @@ var _Renderer = class _Renderer {
8794
8806
  });
8795
8807
  renderer.setClearColor(0, 0);
8796
8808
  renderer.shadowMap.enabled = true;
8797
- renderer.shadowMap.type = import_three23.PCFSoftShadowMap;
8798
- renderer.outputColorSpace = import_three23.SRGBColorSpace;
8809
+ renderer.shadowMap.type = import_three24.PCFSoftShadowMap;
8810
+ renderer.outputColorSpace = import_three24.SRGBColorSpace;
8799
8811
  renderer.setSize(this.viewWidth, this.viewHeight);
8800
8812
  return renderer;
8801
8813
  }
@@ -8829,20 +8841,20 @@ var _Renderer = class _Renderer {
8829
8841
  window["__THREE_DEVTOOLS__"].dispatchEvent(new CustomEvent("observe", { detail: this.scene }));
8830
8842
  }
8831
8843
  if (this.options.debug.grid) {
8832
- const gridHelper = new import_three23.GridHelper(128, 16);
8844
+ const gridHelper = new import_three24.GridHelper(128, 16);
8833
8845
  this._debugHelpers.push(gridHelper);
8834
8846
  this.scene.add(gridHelper);
8835
- const gridHelper2 = new import_three23.GridHelper(128, 16);
8847
+ const gridHelper2 = new import_three24.GridHelper(128, 16);
8836
8848
  this._debugHelpers.push(gridHelper2);
8837
8849
  gridHelper2.rotation.x = 90 * (Math.PI / 180);
8838
8850
  this.scene.add(gridHelper2);
8839
- const gridHelper3 = new import_three23.GridHelper(128, 16);
8851
+ const gridHelper3 = new import_three24.GridHelper(128, 16);
8840
8852
  this._debugHelpers.push(gridHelper3);
8841
8853
  gridHelper3.rotation.z = 90 * (Math.PI / 180);
8842
8854
  this.scene.add(gridHelper3);
8843
8855
  }
8844
8856
  if (this.options.debug.axes) {
8845
- const axesHelper = new import_three23.AxesHelper(64);
8857
+ const axesHelper = new import_three24.AxesHelper(64);
8846
8858
  this._debugHelpers.push(axesHelper);
8847
8859
  this.scene.add(axesHelper);
8848
8860
  }
@@ -9046,8 +9058,8 @@ _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {
9046
9058
  top: void 0,
9047
9059
  bottom: void 0
9048
9060
  },
9049
- position: new import_three23.Vector3(50, 50, 50),
9050
- lookingAt: new import_three23.Vector3(0, 0, 0)
9061
+ position: new import_three24.Vector3(50, 50, 50),
9062
+ lookingAt: new import_three24.Vector3(0, 0, 0)
9051
9063
  },
9052
9064
  render: {
9053
9065
  fpsLimit: 60,
@@ -9487,32 +9499,121 @@ var AnvilParser = class {
9487
9499
  };
9488
9500
 
9489
9501
  // src/world/Chunk.ts
9502
+ var import_three29 = require("three");
9503
+
9504
+ // src/world/MineRenderWorld.ts
9505
+ var import_three27 = require("three");
9506
+
9507
+ // src/world/SectionModels.ts
9490
9508
  var import_three26 = require("three");
9509
+ var SectionModel = class extends ModelObject {
9510
+ applyTextures() {
9511
+ }
9512
+ };
9513
+ function cached(map, key, load) {
9514
+ let value = map.get(key);
9515
+ if (!value) {
9516
+ value = load().catch((error) => {
9517
+ map.delete(key);
9518
+ throw error;
9519
+ });
9520
+ map.set(key, value);
9521
+ }
9522
+ return value;
9523
+ }
9524
+ var SectionModels = class {
9525
+ constructor(maxAtlasSize = 2048) {
9526
+ this.maxAtlasSize = maxAtlasSize;
9527
+ this.states = /* @__PURE__ */ new WeakMap();
9528
+ this.models = /* @__PURE__ */ new WeakMap();
9529
+ this.templates = /* @__PURE__ */ new Set();
9530
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
9531
+ }
9532
+ async get(blockState, properties = {}) {
9533
+ if (!blockState.variants || blockState.multipart) return void 0;
9534
+ let states = this.states.get(blockState);
9535
+ if (!states) this.states.set(blockState, states = /* @__PURE__ */ new Map());
9536
+ const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
9537
+ const prepared = await cached(states, key, () => this.prepare(blockState, properties));
9538
+ if (!prepared) return void 0;
9539
+ return prepared.templates.get(BlockStateResolver.choose(prepared.variants));
9540
+ }
9541
+ async prepare(blockState, properties) {
9542
+ const state = { ...await BlockStateResolver.defaults(blockState), ...properties };
9543
+ const groups = BlockStateResolver.matching(blockState, state);
9544
+ if (groups.length !== 1) return void 0;
9545
+ const variants = groups[0];
9546
+ const choices = Array.isArray(variants) ? variants : [variants];
9547
+ if (!choices.length) return void 0;
9548
+ const templates = /* @__PURE__ */ new Map();
9549
+ for (const variant of choices) {
9550
+ const model = await Models.getMerged(AssetKey.parse("models", variant.model));
9551
+ if (!model) return void 0;
9552
+ const atlas = await UVMapper.getAtlas(model);
9553
+ const rotation = BlockStateResolver.rotation(variant);
9554
+ if (!atlas || atlas.hasAnimation || !ModelCulling.isOpaqueFullCube(atlas) || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize || ModelCulling.toLocalMask(63, rotation) !== 63) return void 0;
9555
+ const tints = await BlockTints.get(blockState.key, state, model);
9556
+ let models = this.models.get(model);
9557
+ if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
9558
+ const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
9559
+ const template = await cached(models, key, async () => {
9560
+ const object = new SectionModel(model, {
9561
+ instanceMeshes: false,
9562
+ mergeMeshes: false,
9563
+ tints,
9564
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
9565
+ });
9566
+ await object.init();
9567
+ const geometry = object.children[0].geometry;
9568
+ geometry.applyMatrix4(new import_three26.Matrix4().makeRotationFromEuler(rotation));
9569
+ object.clear();
9570
+ const cullFaces = CUBE_FACES.map((face) => {
9571
+ const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
9572
+ if (local < 0) return 0;
9573
+ const normal = new import_three26.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
9574
+ const world = CUBE_FACE_OFFSETS.findIndex(([x, y, z]) => Math.abs(normal.x - x) < 1e-6 && Math.abs(normal.y - y) < 1e-6 && Math.abs(normal.z - z) < 1e-6);
9575
+ return 1 << world;
9576
+ });
9577
+ const template2 = { geometry, atlas, cullFaces };
9578
+ this.templates.add(template2);
9579
+ return template2;
9580
+ });
9581
+ templates.set(variant, template);
9582
+ }
9583
+ return { variants, templates };
9584
+ }
9585
+ clear() {
9586
+ for (const template of this.templates) template.geometry.dispose();
9587
+ this.templates.clear();
9588
+ this.states = /* @__PURE__ */ new WeakMap();
9589
+ this.models = /* @__PURE__ */ new WeakMap();
9590
+ }
9591
+ };
9491
9592
 
9492
9593
  // src/world/MineRenderWorld.ts
9493
- var import_three25 = require("three");
9494
9594
  var MineRenderWorld = class {
9495
- constructor(scene) {
9595
+ constructor(scene, options = {}) {
9496
9596
  this._chunks = /* @__PURE__ */ new Map();
9497
9597
  this.pendingCulling = /* @__PURE__ */ new Map();
9498
9598
  this.scene = scene;
9599
+ if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
9499
9600
  }
9500
9601
  getBlockAt(posOrX, y, z) {
9501
9602
  if (typeof posOrX == "number") {
9502
- return this.getBlockAt(new import_three25.Vector3(posOrX, y, z));
9603
+ return this.getBlockAt(new import_three27.Vector3(posOrX, y, z));
9503
9604
  }
9504
9605
  if (isTripleArray(posOrX)) {
9505
- return this.getBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9606
+ return this.getBlockAt(new import_three27.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9506
9607
  }
9507
9608
  this.validatePosBounds(posOrX);
9508
9609
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
9509
9610
  }
9510
9611
  async setBlockAt(posOrX, yOrBlock, z, block) {
9511
9612
  if (typeof posOrX == "number") {
9512
- return this.setBlockAt(new import_three25.Vector3(posOrX, yOrBlock, z), block);
9613
+ return this.setBlockAt(new import_three27.Vector3(posOrX, yOrBlock, z), block);
9513
9614
  }
9514
9615
  if (isTripleArray(posOrX)) {
9515
- return this.setBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9616
+ return this.setBlockAt(new import_three27.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9516
9617
  }
9517
9618
  return this.placeBlock(posOrX, yOrBlock);
9518
9619
  }
@@ -9533,7 +9634,7 @@ var MineRenderWorld = class {
9533
9634
  const collect = async (positions2) => {
9534
9635
  for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
9535
9636
  };
9536
- const place = (block) => this.placeBlock(new import_three25.Vector3(...block.position), block, collect);
9637
+ const place = (block) => this.placeBlock(new import_three27.Vector3(...block.position), block, collect);
9537
9638
  const keys = /* @__PURE__ */ new Map();
9538
9639
  for (const block of multiblock.blocks) {
9539
9640
  if (Chunk.isAir(block)) continue;
@@ -9605,12 +9706,13 @@ var MineRenderWorld = class {
9605
9706
  for (const chunk of chunks) {
9606
9707
  await chunk.dispose();
9607
9708
  }
9709
+ this.sectionModels?.clear();
9608
9710
  }
9609
9711
  updateCulling(positions) {
9610
9712
  for (const pos of positions) {
9611
9713
  this.pendingCulling.set(pos.toArray().join(","), pos.clone());
9612
9714
  for (const offset of CUBE_FACE_OFFSETS) {
9613
- const neighbor = pos.clone().add(new import_three25.Vector3(...offset));
9715
+ const neighbor = pos.clone().add(new import_three27.Vector3(...offset));
9614
9716
  this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
9615
9717
  }
9616
9718
  }
@@ -9619,17 +9721,22 @@ var MineRenderWorld = class {
9619
9721
  while (this.pendingCulling.size) {
9620
9722
  const batch = [...this.pendingCulling.values()];
9621
9723
  this.pendingCulling.clear();
9724
+ const changed = /* @__PURE__ */ new Set();
9622
9725
  for (const pos of batch) {
9623
- const block = this.getBlockAt(pos)?.object;
9624
- if (!block) continue;
9726
+ const chunk = this.getChunkAt(pos);
9727
+ if (!chunk) continue;
9728
+ changed.add(chunk);
9729
+ if (!chunk.getBlockAt(pos)) continue;
9625
9730
  let mask = 0;
9626
9731
  for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
9627
- if (this.getBlockAt(pos.clone().add(new import_three25.Vector3(...offset)))?.object.isOccluding) {
9732
+ const neighbor = pos.clone().add(new import_three27.Vector3(...offset));
9733
+ if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
9628
9734
  mask |= 1 << face;
9629
9735
  }
9630
9736
  }
9631
- await block.setCullMask(mask);
9737
+ await chunk.setCullMaskAt(pos, mask);
9632
9738
  }
9739
+ for (const chunk of changed) chunk.rebuildSectionMesh();
9633
9740
  }
9634
9741
  } finally {
9635
9742
  this.culling = void 0;
@@ -9645,7 +9752,8 @@ var MineRenderWorld = class {
9645
9752
  Math.floor(pos.x / 16),
9646
9753
  Math.floor(pos.y / 16),
9647
9754
  Math.floor(pos.z / 16),
9648
- (positions) => this.updateCulling(positions)
9755
+ (positions) => this.updateCulling(positions),
9756
+ this.sectionModels
9649
9757
  );
9650
9758
  this._chunks.set(key, chunk);
9651
9759
  }
@@ -9655,14 +9763,14 @@ var MineRenderWorld = class {
9655
9763
  return this._chunks.get(this.worldPosToChunkKey(pos));
9656
9764
  }
9657
9765
  static worldToScenePosition(pos) {
9658
- return new import_three25.Vector3(
9766
+ return new import_three27.Vector3(
9659
9767
  pos.x * 16,
9660
9768
  pos.y * 16,
9661
9769
  pos.z * 16
9662
9770
  );
9663
9771
  }
9664
9772
  static sceneToWorldPosition(pos) {
9665
- return new import_three25.Vector3(
9773
+ return new import_three27.Vector3(
9666
9774
  pos.x / 16,
9667
9775
  pos.y / 16,
9668
9776
  pos.z / 16
@@ -9681,12 +9789,118 @@ var MineRenderWorld = class {
9681
9789
  }
9682
9790
  };
9683
9791
 
9792
+ // src/world/SectionMesh.ts
9793
+ var import_three28 = require("three");
9794
+ var SectionMesh = class _SectionMesh extends import_three28.Group {
9795
+ constructor() {
9796
+ super(...arguments);
9797
+ this.ownedMeshes = [];
9798
+ }
9799
+ static build(entries, maxAtlasSize = 2048) {
9800
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
9801
+ const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
9802
+ const atlases = [...new Set(visible.map((entry) => entry.template.atlas))].sort((a, b) => b.image.height - a.image.height);
9803
+ const pages = [];
9804
+ const locations = /* @__PURE__ */ new Map();
9805
+ for (const atlas of atlases) {
9806
+ const { width, height } = atlas.image;
9807
+ if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
9808
+ let selected;
9809
+ for (const page of pages) {
9810
+ const nextRow = page.x + width > maxAtlasSize;
9811
+ if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
9812
+ if (nextRow) {
9813
+ page.y += page.rowHeight;
9814
+ page.x = 0;
9815
+ page.rowHeight = 0;
9816
+ }
9817
+ selected = page;
9818
+ break;
9819
+ }
9820
+ if (!selected) {
9821
+ selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
9822
+ pages.push(selected);
9823
+ }
9824
+ const placement = { atlas, x: selected.x, y: selected.y };
9825
+ selected.placements.push(placement);
9826
+ selected.x += width;
9827
+ selected.rowHeight = Math.max(selected.rowHeight, height);
9828
+ selected.width = Math.max(selected.width, selected.x);
9829
+ selected.height = Math.max(selected.height, selected.y + height);
9830
+ locations.set(atlas, { page: selected, placement });
9831
+ }
9832
+ for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
9833
+ const section = new _SectionMesh();
9834
+ for (const page of pages) {
9835
+ const canvas = createCanvas(page.width, page.height);
9836
+ const context = canvas.getContext("2d");
9837
+ for (const { atlas, x, y } of page.placements) {
9838
+ context.drawImage(atlas.image.canvas, x, y);
9839
+ }
9840
+ const positions = [], normals = [], uvs = [], colors = [], indices = [];
9841
+ for (const { index, template, cullMask } of page.entries) {
9842
+ const position = template.geometry.getAttribute("position");
9843
+ const normal = template.geometry.getAttribute("normal");
9844
+ const uv = template.geometry.getAttribute("uv");
9845
+ const color = template.geometry.getAttribute("color");
9846
+ const sourceIndices = template.geometry.getIndex();
9847
+ const placement = locations.get(template.atlas).placement;
9848
+ const offsetX = index % 16 * 16;
9849
+ const offsetY = Math.floor(index / 256) * 16;
9850
+ const offsetZ = Math.floor(index / 16) % 16 * 16;
9851
+ for (let face = 0; face < 6; face++) {
9852
+ if (cullMask & template.cullFaces[face]) continue;
9853
+ const start = positions.length / 3;
9854
+ for (let corner = 0; corner < 4; corner++) {
9855
+ const vertex = face * 4 + corner;
9856
+ positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
9857
+ normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
9858
+ uvs.push(
9859
+ (placement.x + uv.getX(vertex) * template.atlas.image.width) / page.width,
9860
+ 1 - (placement.y + (1 - uv.getY(vertex)) * template.atlas.image.height) / page.height
9861
+ );
9862
+ colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
9863
+ }
9864
+ for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
9865
+ }
9866
+ }
9867
+ const geometry = new import_three28.BufferGeometry();
9868
+ geometry.setAttribute("position", new import_three28.Float32BufferAttribute(positions, 3));
9869
+ geometry.setAttribute("normal", new import_three28.Float32BufferAttribute(normals, 3));
9870
+ geometry.setAttribute("uv", new import_three28.Float32BufferAttribute(uvs, 2));
9871
+ geometry.setAttribute("color", new import_three28.Float32BufferAttribute(colors, 3));
9872
+ geometry.setIndex(indices);
9873
+ geometry.computeBoundingBox();
9874
+ geometry.computeBoundingSphere();
9875
+ const material = Materials.createShadedCanvasMaterial(canvas, false);
9876
+ material.vertexColors = true;
9877
+ const texture = material.uniforms?.map?.value ?? material.map;
9878
+ const mesh = new import_three28.Mesh(geometry, material);
9879
+ section.add(mesh);
9880
+ section.ownedMeshes.push({ mesh, texture });
9881
+ }
9882
+ return section;
9883
+ }
9884
+ dispose() {
9885
+ for (const { mesh, texture } of this.ownedMeshes.splice(0)) {
9886
+ mesh.removeFromParent();
9887
+ mesh.geometry.dispose();
9888
+ mesh.material.dispose();
9889
+ texture?.dispose();
9890
+ }
9891
+ this.removeFromParent();
9892
+ }
9893
+ };
9894
+
9684
9895
  // src/world/Chunk.ts
9685
9896
  var Chunk = class _Chunk {
9686
- constructor(scene, x, y, z, onBlocksChanged) {
9897
+ constructor(scene, x, y, z, onBlocksChanged, sectionModels) {
9687
9898
  this.onBlocksChanged = onBlocksChanged;
9899
+ this.sectionModels = sectionModels;
9688
9900
  this.data = new ChunkData();
9689
9901
  this.renderedBlocks = /* @__PURE__ */ new Map();
9902
+ this.sectionBlocks = /* @__PURE__ */ new Map();
9903
+ this.meshDirty = false;
9690
9904
  this.scene = scene;
9691
9905
  this.x = x;
9692
9906
  this.y = y;
@@ -9694,10 +9908,10 @@ var Chunk = class _Chunk {
9694
9908
  }
9695
9909
  getBlockAt(posOrX, y, z) {
9696
9910
  if (typeof posOrX == "number") {
9697
- return this.getBlockAt(new import_three26.Vector3(posOrX, y, z));
9911
+ return this.getBlockAt(new import_three29.Vector3(posOrX, y, z));
9698
9912
  }
9699
9913
  if (isTripleArray(posOrX)) {
9700
- return this.getBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9914
+ return this.getBlockAt(new import_three29.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9701
9915
  }
9702
9916
  const pos = this.worldPosToChunkPos(posOrX);
9703
9917
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -9705,10 +9919,10 @@ var Chunk = class _Chunk {
9705
9919
  }
9706
9920
  async setBlockAt(posOrX, yOrBlock, z, block) {
9707
9921
  if (typeof posOrX == "number") {
9708
- return this.setBlockAt(new import_three26.Vector3(posOrX, yOrBlock, z), block);
9922
+ return this.setBlockAt(new import_three29.Vector3(posOrX, yOrBlock, z), block);
9709
9923
  }
9710
9924
  if (isTripleArray(posOrX)) {
9711
- return this.setBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9925
+ return this.setBlockAt(new import_three29.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9712
9926
  }
9713
9927
  const worldPos = posOrX;
9714
9928
  const pos = this.worldPosToChunkPos(worldPos);
@@ -9725,7 +9939,8 @@ var Chunk = class _Chunk {
9725
9939
  const index = _Chunk.chunkPosToBlockIndex(pos);
9726
9940
  this.data.set(index, block);
9727
9941
  const readBlock = this.data.snapshot(index);
9728
- this.renderedBlocks.get(index)?.object.removeFromScene();
9942
+ this.renderedBlocks.get(index)?.object?.removeFromScene();
9943
+ if (this.sectionBlocks.delete(index)) this.meshDirty = true;
9729
9944
  this.renderedBlocks.delete(index);
9730
9945
  let object;
9731
9946
  try {
@@ -9736,13 +9951,19 @@ var Chunk = class _Chunk {
9736
9951
  this.data.set(index, void 0);
9737
9952
  return void 0;
9738
9953
  }
9739
- object = await this.scene.addBlock(blockState, {
9740
- mergeMeshes: true,
9741
- instanceMeshes: true,
9742
- maxInstanceCount: 2e3,
9743
- initialState: stored.properties
9744
- });
9745
- object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
9954
+ const template = await this.sectionModels?.get(blockState, stored.properties);
9955
+ if (template) {
9956
+ this.sectionBlocks.set(index, { index, template, cullMask: 0 });
9957
+ this.meshDirty = true;
9958
+ } else {
9959
+ object = await this.scene.addBlock(blockState, {
9960
+ mergeMeshes: true,
9961
+ instanceMeshes: true,
9962
+ maxInstanceCount: 2e3,
9963
+ initialState: stored.properties
9964
+ });
9965
+ object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
9966
+ }
9746
9967
  const info = { get block() {
9747
9968
  return readBlock();
9748
9969
  }, object };
@@ -9756,11 +9977,43 @@ var Chunk = class _Chunk {
9756
9977
  await onBlocksChanged?.([worldPos]);
9757
9978
  }
9758
9979
  }
9980
+ isOccludingAt(pos) {
9981
+ const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
9982
+ return this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false);
9983
+ }
9984
+ async setCullMaskAt(pos, mask) {
9985
+ const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
9986
+ const entry = this.sectionBlocks.get(index);
9987
+ if (entry) {
9988
+ if (entry.cullMask !== mask) {
9989
+ entry.cullMask = mask;
9990
+ this.meshDirty = true;
9991
+ }
9992
+ } else {
9993
+ await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
9994
+ }
9995
+ }
9996
+ rebuildSectionMesh() {
9997
+ if (!this.meshDirty) return;
9998
+ const next = this.sectionBlocks.size ? SectionMesh.build([...this.sectionBlocks.values()], this.sectionModels.maxAtlasSize) : void 0;
9999
+ this.sectionMesh?.dispose();
10000
+ this.sectionMesh = next;
10001
+ if (next) {
10002
+ next.position.set(this.x * 256, this.y * 256, this.z * 256);
10003
+ this.scene.add(next);
10004
+ }
10005
+ this.scene.dirty = true;
10006
+ this.meshDirty = false;
10007
+ }
9759
10008
  async clear(onBlocksChanged = this.onBlocksChanged) {
9760
10009
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
9761
- new import_three26.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
10010
+ new import_three29.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
9762
10011
  ));
9763
- for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
10012
+ for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
10013
+ this.sectionBlocks.clear();
10014
+ this.sectionMesh?.dispose();
10015
+ this.sectionMesh = void 0;
10016
+ this.meshDirty = false;
9764
10017
  this.renderedBlocks.clear();
9765
10018
  this.data.clear();
9766
10019
  await onBlocksChanged?.(positions);
@@ -9778,14 +10031,14 @@ var Chunk = class _Chunk {
9778
10031
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
9779
10032
  }
9780
10033
  worldPosToChunkPos(pos) {
9781
- return new import_three26.Vector3(
10034
+ return new import_three29.Vector3(
9782
10035
  pos.x - this.x * 16,
9783
10036
  pos.y - this.y * 16,
9784
10037
  pos.z - this.z * 16
9785
10038
  );
9786
10039
  }
9787
10040
  chunkPosToWorldPos(pos) {
9788
- return new import_three26.Vector3(
10041
+ return new import_three29.Vector3(
9789
10042
  this.x * 16 + pos.x,
9790
10043
  this.y * 16 + pos.y,
9791
10044
  this.z * 16 + pos.z