minerender 2.1.0-alpha.41 → 2.1.0-alpha.43

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_node_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_node_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,
@@ -931,11 +934,11 @@ var ImageLoader = class _ImageLoader {
931
934
  static async loadCanvasData(src) {
932
935
  const keyStr = serializeImageKey({ src });
933
936
  const info = this.getInfo(src);
934
- const cached = Caching.rawImageCache.getIfPresent(keyStr);
937
+ const cached2 = Caching.rawImageCache.getIfPresent(keyStr);
935
938
  try {
936
939
  return await this.infoToCanvasData(await info);
937
940
  } catch (err) {
938
- if (cached && Caching.rawImageCache.getIfPresent(keyStr) === cached) {
941
+ if (cached2 && Caching.rawImageCache.getIfPresent(keyStr) === cached2) {
939
942
  Caching.rawImageCache.invalidate(keyStr);
940
943
  }
941
944
  throw err;
@@ -1072,8 +1075,8 @@ var Textures = class _Textures {
1072
1075
  }
1073
1076
  static getImage(key) {
1074
1077
  const keyStr = serializeTextureKey(key);
1075
- const cached = Caching.textureCache.peek(keyStr);
1076
- if (cached && TextureLoader.hasFailed(cached)) {
1078
+ const cached2 = Caching.textureCache.peek(keyStr);
1079
+ if (cached2 && TextureLoader.hasFailed(cached2)) {
1077
1080
  Caching.textureCache.invalidate(keyStr);
1078
1081
  }
1079
1082
  return Caching.textureCache.get(keyStr, (k) => {
@@ -1341,12 +1344,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1341
1344
  return a.join("/");
1342
1345
  }
1343
1346
  getFullPath() {
1344
- let p11 = [];
1347
+ let p10 = [];
1345
1348
  if (this.type) {
1346
- p11.push(this.type);
1349
+ p10.push(this.type);
1347
1350
  }
1348
- p11.push(this.path);
1349
- return p11.join("/");
1351
+ p10.push(this.path);
1352
+ return p10.join("/");
1350
1353
  }
1351
1354
  toNamespacedString() {
1352
1355
  return this.namespace + ":" + this.getFullPath();
@@ -1433,7 +1436,7 @@ var ModelMerger = class {
1433
1436
  merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1434
1437
  }
1435
1438
  }
1436
- merged.hierarchy = models.map((m) => m.parent).filter((p11) => `${p11}`);
1439
+ merged.hierarchy = models.map((m) => m.parent).filter((p10) => `${p10}`);
1437
1440
  merged.hierarchy.push(`${merged.parent}`);
1438
1441
  return merged;
1439
1442
  }
@@ -2162,7 +2165,7 @@ var ModelTextures = class {
2162
2165
  static async get(key) {
2163
2166
  const keyStr = key.serialize();
2164
2167
  const pending = this.preload(key);
2165
- const cached = Caching.textureAssetCache.getIfPresent(keyStr);
2168
+ const cached2 = Caching.textureAssetCache.getIfPresent(keyStr);
2166
2169
  try {
2167
2170
  const asset = await pending;
2168
2171
  if (asset) {
@@ -2170,7 +2173,7 @@ var ModelTextures = class {
2170
2173
  }
2171
2174
  return void 0;
2172
2175
  } catch (err) {
2173
- if (cached && Caching.textureAssetCache.getIfPresent(keyStr) === cached) {
2176
+ if (cached2 && Caching.textureAssetCache.getIfPresent(keyStr) === cached2) {
2174
2177
  Caching.textureAssetCache.invalidate(keyStr);
2175
2178
  }
2176
2179
  throw err;
@@ -3261,6 +3264,10 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3261
3264
  this.setMatrixAt(index, new import_three10.Matrix4());
3262
3265
  return reference;
3263
3266
  }
3267
+ /** Returns the live reference for a raycast hit on this object's instance mesh. */
3268
+ getInstanceReference(mesh, index) {
3269
+ return mesh === this.instanceMesh ? this.instanceReferences.get(index) : void 0;
3270
+ }
3264
3271
  isInstanceActive(index, reference) {
3265
3272
  return this.instanceReferences.get(index) === reference;
3266
3273
  }
@@ -3343,9 +3350,9 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3343
3350
  }
3344
3351
  getRotationAt(index, euler = new import_three10.Euler()) {
3345
3352
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3346
- const matrix = this.getMatrixAt(index);
3347
- euler.setFromRotationMatrix(matrix);
3348
- return euler;
3353
+ const rotation = new import_three10.Quaternion();
3354
+ this.getMatrixAt(index).decompose(new import_three10.Vector3(), rotation, new import_three10.Vector3());
3355
+ return euler.setFromQuaternion(rotation);
3349
3356
  }
3350
3357
  setScaleAt(index, scale) {
3351
3358
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
@@ -3595,11 +3602,12 @@ var GuiObject = class extends SceneObject {
3595
3602
  // src/inspector/SceneInspector.ts
3596
3603
  var import_three14 = require("three");
3597
3604
  var p7 = prefix("SceneInspector");
3598
- var help = "<span>Ctrl+Click to Select<br/></span><br/>";
3605
+ var help = "<span>Ctrl/Cmd+Click to Select<br/></span><br/>";
3599
3606
  var SceneInspector = class {
3600
3607
  constructor(renderer) {
3601
3608
  this.renderer = renderer;
3602
3609
  this.mouse = new import_three14.Vector2();
3610
+ this.listeners = new AbortController();
3603
3611
  this.objectInfoContainer = document.createElement("div");
3604
3612
  this.objectControlsContainer = document.createElement("div");
3605
3613
  this.raycaster = new import_three14.Raycaster();
@@ -3609,13 +3617,44 @@ var SceneInspector = class {
3609
3617
  this.objectInfoContainer.classList.add("minerender-inspector", "minerender-object-info");
3610
3618
  this.objectControlsContainer.classList.add("minerender-inspector", "minerender-object-controls");
3611
3619
  this.objectInfoContainer.innerHTML = help;
3612
- document.addEventListener("click", this.onClick.bind(this), {
3613
- passive: true
3614
- });
3620
+ const canvas = this.renderer.renderer.domElement;
3621
+ const options = { signal: this.listeners.signal };
3622
+ canvas.addEventListener("pointerdown", (event) => {
3623
+ this.pointer = event.button === 0 && (event.ctrlKey || event.metaKey) ? event : void 0;
3624
+ }, options);
3625
+ canvas.addEventListener("pointermove", (event) => {
3626
+ if (this.pointer?.pointerId === event.pointerId && Math.hypot(event.clientX - this.pointer.clientX, event.clientY - this.pointer.clientY) > 5) {
3627
+ this.pointer = void 0;
3628
+ }
3629
+ }, options);
3630
+ canvas.addEventListener("pointerup", (event) => {
3631
+ const start = this.pointer;
3632
+ this.pointer = void 0;
3633
+ if (start?.pointerId === event.pointerId && Math.hypot(event.clientX - start.clientX, event.clientY - start.clientY) <= 5) {
3634
+ this.onClick(event);
3635
+ }
3636
+ }, options);
3637
+ canvas.addEventListener("pointercancel", () => {
3638
+ this.pointer = void 0;
3639
+ }, options);
3640
+ canvas.addEventListener("pointerleave", () => {
3641
+ this.pointer = void 0;
3642
+ }, options);
3643
+ canvas.addEventListener("contextmenu", (event) => {
3644
+ if (this.pointer?.ctrlKey) event.preventDefault();
3645
+ }, options);
3646
+ }
3647
+ dispose() {
3648
+ this.listeners.abort();
3649
+ this.pointer = void 0;
3650
+ this.selectedObject = void 0;
3651
+ this.objectInfoContainer.remove();
3652
+ this.objectControlsContainer.remove();
3615
3653
  }
3616
3654
  findMeshChildren(from, out = []) {
3655
+ if (!from.visible) return out;
3617
3656
  for (let c of from.children) {
3618
- if (isMesh(c)) {
3657
+ if (c.visible && isMesh(c)) {
3619
3658
  out.push(c);
3620
3659
  }
3621
3660
  this.findMeshChildren(c, out);
@@ -3623,50 +3662,43 @@ var SceneInspector = class {
3623
3662
  return out;
3624
3663
  }
3625
3664
  onClick(event) {
3626
- if (event.target?.nodeName !== "CANVAS") return;
3627
- if (!event.ctrlKey) return;
3628
- setTimeout(() => {
3629
- this.mouse.x = event.clientX / window.innerWidth * 2 - 1;
3630
- this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
3631
- this.raycaster.setFromCamera(this.mouse, this.renderer.camera);
3632
- let start = Date.now();
3633
- let targets = this.findMeshChildren(this.renderer.scene);
3634
- const intersects = this.raycaster.intersectObjects(targets, false);
3635
- console.debug("Raycast took " + (Date.now() - start) + "ms for " + targets.length + " possible targets with " + intersects.length + " results");
3636
- if (intersects.length > 0) {
3637
- this.handleRaycasterObjects(intersects);
3638
- }
3639
- });
3665
+ const canvas = this.renderer.renderer.domElement;
3666
+ if (event.target !== canvas || event.button !== 0 || !(event.ctrlKey || event.metaKey)) return;
3667
+ const rect = canvas.getBoundingClientRect();
3668
+ const x = event.clientX - rect.left, y = event.clientY - rect.top;
3669
+ if (rect.width <= 0 || rect.height <= 0 || x < 0 || y < 0 || x >= rect.width || y >= rect.height) return;
3670
+ this.mouse.set(x / rect.width * 2 - 1, 1 - y / rect.height * 2);
3671
+ this.renderer.camera.updateWorldMatrix(true, false);
3672
+ this.renderer.scene.updateMatrixWorld(true);
3673
+ this.raycaster.setFromCamera(this.mouse, this.renderer.camera);
3674
+ this.handleRaycasterObjects(this.raycaster.intersectObjects(this.findMeshChildren(this.renderer.scene), false));
3640
3675
  }
3641
3676
  appendTo(el) {
3642
3677
  el.append(this.objectInfoContainer);
3643
3678
  el.append(this.objectControlsContainer);
3644
3679
  }
3645
3680
  handleRaycasterObjects(intersections) {
3646
- console.debug(intersections);
3647
- const firstIntersection = intersections[0];
3648
- if (firstIntersection && firstIntersection.object) {
3649
- const firstObject = firstIntersection.object;
3650
- let targetIntersection = void 0;
3651
- let targetObject = void 0;
3652
- const firstSceneObjectIntersection = intersections.find((i) => i.object && i.object.parent && isSceneObject(i.object.parent));
3653
- if (firstSceneObjectIntersection && firstSceneObjectIntersection.object) {
3654
- targetIntersection = firstSceneObjectIntersection;
3655
- targetObject = firstSceneObjectIntersection.object;
3656
- } else {
3657
- targetIntersection = firstIntersection;
3658
- targetObject = firstObject;
3681
+ const hit = intersections[0];
3682
+ if (!hit) return;
3683
+ const instance = this.getIntersectionInstance(hit);
3684
+ this.selectObject(instance?.instanceable ?? hit.object, hit, instance);
3685
+ }
3686
+ getIntersectionInstance(hit) {
3687
+ if (hit?.instanceId === void 0) return void 0;
3688
+ for (let parent = hit.object.parent; parent; parent = parent.parent) {
3689
+ if (isSceneObject(parent)) {
3690
+ const instance = parent.getInstanceReference(hit.object, hit.instanceId);
3691
+ if (instance) return instance;
3659
3692
  }
3660
- if (!targetIntersection || !targetObject) return;
3661
- this.selectObject(targetObject, targetIntersection);
3662
3693
  }
3694
+ return void 0;
3663
3695
  }
3664
- selectObject(targetObject, targetIntersection) {
3696
+ selectObject(targetObject, targetIntersection, instance = this.getIntersectionInstance(targetIntersection)) {
3665
3697
  this.selectedObject = targetObject;
3666
3698
  console.log(p7, "selected", targetObject);
3667
- this.redraw(targetObject, targetIntersection);
3699
+ this.redraw(targetObject, targetIntersection, instance);
3668
3700
  }
3669
- redraw(targetObject, targetIntersection) {
3701
+ redraw(targetObject, targetIntersection, instance) {
3670
3702
  this.objectInfoContainer.innerHTML = help;
3671
3703
  if (targetIntersection) {
3672
3704
  this.addInfoLine("Distance", "D", targetIntersection.distance);
@@ -3679,8 +3711,8 @@ var SceneInspector = class {
3679
3711
  if (isSceneObject(targetObject.parent)) {
3680
3712
  }
3681
3713
  }
3682
- if ("atlas" in targetObject) {
3683
- const atlas = targetObject["atlas"];
3714
+ const atlas = "atlas" in targetObject ? targetObject["atlas"] : void 0;
3715
+ if (atlas?.image.canvas) {
3684
3716
  const img = atlas.image.canvas;
3685
3717
  img.style.width = "100px";
3686
3718
  img.style.height = "100px";
@@ -3689,28 +3721,31 @@ var SceneInspector = class {
3689
3721
  this.objectInfoContainer.prepend(img);
3690
3722
  }
3691
3723
  this.objectControlsContainer.innerHTML = "";
3692
- this.addControls(targetObject, targetIntersection);
3724
+ this.addControls(targetObject, targetIntersection, instance);
3693
3725
  }
3694
- addControls(object, intersection) {
3726
+ addControls(object, intersection, instance) {
3695
3727
  const container = document.createElement("div");
3696
3728
  container.append(this.separator("Select Parent/Child"));
3697
3729
  if (object.parent) {
3698
3730
  container.append(this.buttonControl("Select Parent " + object.parent.constructor.name + " " + object.parent.name, "P", () => {
3699
- this.selectObject(object.parent, intersection);
3731
+ this.selectObject(object.parent, intersection, instance);
3700
3732
  }));
3701
3733
  }
3702
3734
  if (object.children.length > 0) {
3703
3735
  let i = 1;
3704
3736
  for (let child of object.children) {
3705
3737
  container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
3706
- this.selectObject(child, intersection);
3738
+ this.selectObject(child, intersection, instance);
3707
3739
  }));
3708
3740
  }
3709
3741
  }
3710
3742
  container.append(this.separator());
3711
- container.append(this.toggleControl("Visibility", "V", object.visible, (v) => object.visible = v));
3743
+ const selectedInstance = instance && (object === instance.instanceable || object === intersection?.object) ? instance : void 0;
3744
+ if (!selectedInstance) {
3745
+ container.append(this.toggleControl("Visibility", "V", object.visible, (v) => object.visible = v));
3746
+ }
3712
3747
  container.append(this.separator("Mesh"));
3713
- this.addObjectControls(object, intersection, container);
3748
+ this.addObjectControls(object, intersection, container, selectedInstance);
3714
3749
  container.append(this.separator());
3715
3750
  this.objectControlsContainer.append(container);
3716
3751
  }
@@ -3722,78 +3757,26 @@ var SceneInspector = class {
3722
3757
  this.objectInfoContainer.append(document.createElement("br"));
3723
3758
  return el;
3724
3759
  }
3725
- addObjectControls(target, intersection, container) {
3760
+ addObjectControls(target, intersection, container, instance) {
3726
3761
  const posRange = 16 * 16;
3727
3762
  const rotRange = 360;
3728
3763
  const scaleRange = 4;
3729
- if (typeof intersection !== "undefined" && typeof intersection.instanceId !== "undefined" && isSceneObject(target) && target.isInstanced) {
3730
- const scObj = target;
3731
- container.append(this.separator("Position"));
3732
- let pos = scObj.getPositionAt(intersection.instanceId);
3733
- container.append(this.rangeControl("X Position", "X", -posRange + pos.x, posRange + pos.x, pos.x, 1, (v) => {
3734
- pos = scObj.getPositionAt(intersection.instanceId);
3735
- pos.x = v;
3736
- scObj.setPositionAt(intersection.instanceId, pos);
3737
- }));
3738
- container.append(this.rangeControl("Y Position", "Y", -posRange + pos.y, posRange + pos.y, pos.y, 1, (v) => {
3739
- pos = scObj.getPositionAt(intersection.instanceId);
3740
- pos.y = v;
3741
- scObj.setPositionAt(intersection.instanceId, pos);
3742
- }));
3743
- container.append(this.rangeControl("Z Position", "Z", -posRange + pos.z, posRange + pos.z, pos.z, 1, (v) => {
3744
- pos = scObj.getPositionAt(intersection.instanceId);
3745
- pos.z = v;
3746
- scObj.setPositionAt(intersection.instanceId, pos);
3747
- }));
3748
- container.append(this.separator("Rotation"));
3749
- let rot = scObj.getRotationAt(intersection.instanceId);
3750
- container.append(this.rangeControl("X Rotation", "X", 0, rotRange, Math.round(toDegrees(rot.x)), 1, (v) => {
3751
- rot = scObj.getRotationAt(intersection.instanceId);
3752
- rot.x = toRadians(v);
3753
- scObj.setRotationAt(intersection.instanceId, rot);
3754
- }));
3755
- container.append(this.rangeControl("Y Rotation", "Y", 0, rotRange, Math.round(toDegrees(rot.y)), 1, (v) => {
3756
- rot = scObj.getRotationAt(intersection.instanceId);
3757
- rot.y = toRadians(v);
3758
- scObj.setRotationAt(intersection.instanceId, rot);
3759
- }));
3760
- container.append(this.rangeControl("Z Rotation", "Z", 0, rotRange, Math.round(toDegrees(rot.z)), 1, (v) => {
3761
- rot = scObj.getRotationAt(intersection.instanceId);
3762
- rot.z = toRadians(v);
3763
- scObj.setRotationAt(intersection.instanceId, rot);
3764
- }));
3765
- container.append(this.separator("Scale"));
3766
- let scl = scObj.getScale();
3767
- container.append(this.rangeControl("X Scale", "X", 0, scaleRange, scl.x, 0.1, (v) => {
3768
- scl = scObj.getScaleAt(intersection.instanceId);
3769
- scl.x = v;
3770
- scObj.setScaleAt(intersection.instanceId, scl);
3771
- }));
3772
- container.append(this.rangeControl("Y Scale", "Y", 0, scaleRange, scl.y, 0.1, (v) => {
3773
- scl = scObj.getScaleAt(intersection.instanceId);
3774
- scl.y = v;
3775
- scObj.setScaleAt(intersection.instanceId, scl);
3776
- }));
3777
- container.append(this.rangeControl("Z Scale", "Z", 0, scaleRange, scl.z, 0.1, (v) => {
3778
- scl = scObj.getScaleAt(intersection.instanceId);
3779
- scl.z = v;
3780
- scObj.setScaleAt(intersection.instanceId, scl);
3781
- }));
3782
- } else if (isTransformable(target)) {
3783
- const parent = target;
3764
+ const transform = instance ?? (isTransformable(target) ? target : void 0);
3765
+ if (transform) {
3766
+ const parent = transform;
3784
3767
  container.append(this.separator("Position"));
3785
3768
  let pos = parent.getPosition();
3786
- container.append(this.rangeControl("X Position", "X", -posRange, posRange, pos.x, 1, (v) => {
3769
+ container.append(this.rangeControl("X Position", "X", pos.x - posRange, pos.x + posRange, pos.x, 1, (v) => {
3787
3770
  pos = parent.getPosition();
3788
3771
  pos.x = v;
3789
3772
  parent.setPosition(pos);
3790
3773
  }));
3791
- container.append(this.rangeControl("Y Position", "Y", -posRange, posRange, pos.y, 1, (v) => {
3774
+ container.append(this.rangeControl("Y Position", "Y", pos.y - posRange, pos.y + posRange, pos.y, 1, (v) => {
3792
3775
  pos = parent.getPosition();
3793
3776
  pos.y = v;
3794
3777
  parent.setPosition(pos);
3795
3778
  }));
3796
- container.append(this.rangeControl("Z Position", "Z", -posRange, posRange, pos.z, 1, (v) => {
3779
+ container.append(this.rangeControl("Z Position", "Z", pos.z - posRange, pos.z + posRange, pos.z, 1, (v) => {
3797
3780
  pos = parent.getPosition();
3798
3781
  pos.z = v;
3799
3782
  parent.setPosition(pos);
@@ -3882,6 +3865,7 @@ var SceneInspector = class {
3882
3865
  toggle.checked = val;
3883
3866
  toggle.addEventListener("change", (e) => {
3884
3867
  change(toggle.checked);
3868
+ this.renderer.scene.dirty = true;
3885
3869
  });
3886
3870
  label.append(toggle);
3887
3871
  label.append(document.createElement("br"));
@@ -3894,7 +3878,6 @@ var SceneInspector = class {
3894
3878
  label.setAttribute("title", name);
3895
3879
  labelText.style.width = "18%";
3896
3880
  labelText.style.display = "inline-block";
3897
- labelText.innerText = `${id} (${val})`;
3898
3881
  const range = document.createElement("input");
3899
3882
  range.setAttribute("type", "range");
3900
3883
  range.style.width = "80%";
@@ -3902,8 +3885,10 @@ var SceneInspector = class {
3902
3885
  range.min = `${min}`;
3903
3886
  range.step = `${step}`;
3904
3887
  range.value = `${val}`;
3888
+ labelText.innerText = `${id} (${range.value})`;
3905
3889
  const onChange = () => {
3906
3890
  change(parseFloat(range.value));
3891
+ this.renderer.scene.dirty = true;
3907
3892
  labelText.innerText = `${id} (${range.value})`;
3908
3893
  };
3909
3894
  range.addEventListener("input", onChange);
@@ -4063,6 +4048,93 @@ var ModelCulling = class {
4063
4048
  }
4064
4049
  };
4065
4050
 
4051
+ // src/model/block/BlockStateResolver.ts
4052
+ var import_three17 = require("three");
4053
+ function matchesCondition(condition, state) {
4054
+ return Object.entries(condition).every(([key, value]) => {
4055
+ if (Array.isArray(value)) {
4056
+ if (key === "OR") return value.some((child) => matchesCondition(child, state));
4057
+ if (key === "AND") return value.every((child) => matchesCondition(child, state));
4058
+ return false;
4059
+ }
4060
+ return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
4061
+ });
4062
+ }
4063
+ var BlockStateResolver = class {
4064
+ static async defaults(blockState) {
4065
+ const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
4066
+ const state = {};
4067
+ if (defaults && Object.keys(defaults).length > 0) {
4068
+ for (const key in defaults) state[key] = defaults[key].default;
4069
+ } else if (blockState.variants) {
4070
+ const variant = Object.keys(blockState.variants)[0];
4071
+ if (variant) {
4072
+ for (const property of variant.split(",")) {
4073
+ const [key, value] = property.split("=");
4074
+ state[key] = value;
4075
+ }
4076
+ }
4077
+ } else if (blockState.multipart) {
4078
+ const condition = blockState.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
4079
+ for (const [key, value] of Object.entries(condition ?? {})) state[key] = value.split("|")[0];
4080
+ }
4081
+ return state;
4082
+ }
4083
+ static select(blockState, state, choose = this.choose) {
4084
+ return this.matching(blockState, state).map(choose);
4085
+ }
4086
+ static matching(blockState, state) {
4087
+ const out = [];
4088
+ if (blockState.variants) {
4089
+ if (Object.keys(blockState.variants).length === 1 && "" in blockState.variants) {
4090
+ out.push(blockState.variants[""]);
4091
+ } else {
4092
+ for (const variantKey in blockState.variants) {
4093
+ const matches = variantKey.split(",").every((property) => {
4094
+ const [key, value] = property.split("=");
4095
+ return `${state[key]}` === `${value}`;
4096
+ });
4097
+ if (matches) out.push(blockState.variants[variantKey]);
4098
+ }
4099
+ }
4100
+ } else if (blockState.multipart) {
4101
+ for (const part of blockState.multipart) {
4102
+ if (part.apply && (!part.when || matchesCondition(part.when, state))) {
4103
+ out.push(part.apply);
4104
+ }
4105
+ }
4106
+ }
4107
+ return out;
4108
+ }
4109
+ static choose(variants) {
4110
+ if (!Array.isArray(variants)) return variants;
4111
+ if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
4112
+ const total = variants.reduce((sum, variant) => {
4113
+ const weight = variant.weight ?? 1;
4114
+ if (!Number.isInteger(weight) || weight < 1) {
4115
+ throw new MineRenderError(`Invalid blockstate variant weight: ${weight}`);
4116
+ }
4117
+ return sum + weight;
4118
+ }, 0);
4119
+ let choice = Math.random() * total;
4120
+ for (const variant of variants) {
4121
+ choice -= variant.weight ?? 1;
4122
+ if (choice < 0) return variant;
4123
+ }
4124
+ return variants[variants.length - 1];
4125
+ }
4126
+ static rotation(variant) {
4127
+ const rotation = new import_three17.Euler();
4128
+ if (typeof variant.x !== "undefined") {
4129
+ rotation.x = toRadians(clampRotationDegrees(variant.x));
4130
+ }
4131
+ if (typeof variant.y !== "undefined") {
4132
+ rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4133
+ }
4134
+ return rotation;
4135
+ }
4136
+ };
4137
+
4066
4138
  // src/model/block/blockTints.json
4067
4139
  var blockTints_default = {
4068
4140
  "minecraft:large_fern": { source: "grass" },
@@ -4149,10 +4221,10 @@ var BlockTints = class {
4149
4221
  };
4150
4222
 
4151
4223
  // src/model/scene/ModelObject.ts
4152
- var import_three18 = require("three");
4224
+ var import_three19 = require("three");
4153
4225
 
4154
4226
  // src/three/BufferGeometryUtils.js
4155
- var import_three17 = require("three");
4227
+ var import_three18 = require("three");
4156
4228
  function computeTangents(geometry) {
4157
4229
  geometry.computeTangents();
4158
4230
  console.warn("THREE.BufferGeometryUtils: .computeTangents() has been removed. Use BufferGeometry.computeTangents() instead.");
@@ -4164,7 +4236,7 @@ function mergeBufferGeometries(geometries, useGroups = false) {
4164
4236
  const attributes = {};
4165
4237
  const morphAttributes = {};
4166
4238
  const morphTargetsRelative = geometries[0].morphTargetsRelative;
4167
- const mergedGeometry = new import_three17.BufferGeometry();
4239
+ const mergedGeometry = new import_three18.BufferGeometry();
4168
4240
  let offset = 0;
4169
4241
  for (let i = 0; i < geometries.length; ++i) {
4170
4242
  const geometry = geometries[i];
@@ -4288,7 +4360,7 @@ function mergeBufferAttributes(attributes) {
4288
4360
  array.set(attributes[i].array, offset);
4289
4361
  offset += attributes[i].array.length;
4290
4362
  }
4291
- return new import_three17.BufferAttribute(array, itemSize, normalized);
4363
+ return new import_three18.BufferAttribute(array, itemSize, normalized);
4292
4364
  }
4293
4365
  function interleaveAttributes(attributes) {
4294
4366
  let TypedArray;
@@ -4304,7 +4376,7 @@ function interleaveAttributes(attributes) {
4304
4376
  arrayLength += attribute.array.length;
4305
4377
  stride += attribute.itemSize;
4306
4378
  }
4307
- const interleavedBuffer = new import_three17.InterleavedBuffer(new TypedArray(arrayLength), stride);
4379
+ const interleavedBuffer = new import_three18.InterleavedBuffer(new TypedArray(arrayLength), stride);
4308
4380
  let offset = 0;
4309
4381
  const res = [];
4310
4382
  const getters = ["getX", "getY", "getZ", "getW"];
@@ -4313,7 +4385,7 @@ function interleaveAttributes(attributes) {
4313
4385
  const attribute = attributes[j];
4314
4386
  const itemSize = attribute.itemSize;
4315
4387
  const count = attribute.count;
4316
- const iba = new import_three17.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4388
+ const iba = new import_three18.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4317
4389
  res.push(iba);
4318
4390
  offset += itemSize;
4319
4391
  for (let c = 0; c < count; c++) {
@@ -4397,13 +4469,13 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4397
4469
  const name = attributeNames[i];
4398
4470
  const oldAttribute = geometry.getAttribute(name);
4399
4471
  const buffer = new oldAttribute.array.constructor(attrArrays[name]);
4400
- const attribute = new import_three17.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4472
+ const attribute = new import_three18.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4401
4473
  result.setAttribute(name, attribute);
4402
4474
  if (name in morphAttrsArrays) {
4403
4475
  for (let j = 0; j < morphAttrsArrays[name].length; j++) {
4404
4476
  const oldMorphAttribute = geometry.morphAttributes[name][j];
4405
4477
  const buffer2 = new oldMorphAttribute.array.constructor(morphAttrsArrays[name][j]);
4406
- const morphAttribute = new import_three17.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4478
+ const morphAttribute = new import_three18.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4407
4479
  result.morphAttributes[name][j] = morphAttribute;
4408
4480
  }
4409
4481
  }
@@ -4412,11 +4484,11 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4412
4484
  return result;
4413
4485
  }
4414
4486
  function toTrianglesDrawMode(geometry, drawMode) {
4415
- if (drawMode === import_three17.TrianglesDrawMode) {
4487
+ if (drawMode === import_three18.TrianglesDrawMode) {
4416
4488
  console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.");
4417
4489
  return geometry;
4418
4490
  }
4419
- if (drawMode === import_three17.TriangleFanDrawMode || drawMode === import_three17.TriangleStripDrawMode) {
4491
+ if (drawMode === import_three18.TriangleFanDrawMode || drawMode === import_three18.TriangleStripDrawMode) {
4420
4492
  let index = geometry.getIndex();
4421
4493
  if (index === null) {
4422
4494
  const indices = [];
@@ -4434,7 +4506,7 @@ function toTrianglesDrawMode(geometry, drawMode) {
4434
4506
  }
4435
4507
  const numberOfTriangles = index.count - 2;
4436
4508
  const newIndices = [];
4437
- if (drawMode === import_three17.TriangleFanDrawMode) {
4509
+ if (drawMode === import_three18.TriangleFanDrawMode) {
4438
4510
  for (let i = 1; i <= numberOfTriangles; i++) {
4439
4511
  newIndices.push(index.getX(0));
4440
4512
  newIndices.push(index.getX(i));
@@ -4470,15 +4542,15 @@ function computeMorphedAttributes(object) {
4470
4542
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4471
4543
  return null;
4472
4544
  }
4473
- const _vA = new import_three17.Vector3();
4474
- const _vB = new import_three17.Vector3();
4475
- const _vC = new import_three17.Vector3();
4476
- const _tempA = new import_three17.Vector3();
4477
- const _tempB = new import_three17.Vector3();
4478
- const _tempC = new import_three17.Vector3();
4479
- const _morphA = new import_three17.Vector3();
4480
- const _morphB = new import_three17.Vector3();
4481
- const _morphC = new import_three17.Vector3();
4545
+ const _vA = new import_three18.Vector3();
4546
+ const _vB = new import_three18.Vector3();
4547
+ const _vC = new import_three18.Vector3();
4548
+ const _tempA = new import_three18.Vector3();
4549
+ const _tempB = new import_three18.Vector3();
4550
+ const _tempC = new import_three18.Vector3();
4551
+ const _morphA = new import_three18.Vector3();
4552
+ const _morphB = new import_three18.Vector3();
4553
+ const _morphC = new import_three18.Vector3();
4482
4554
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4483
4555
  _vA.fromBufferAttribute(attribute, a2);
4484
4556
  _vB.fromBufferAttribute(attribute, b2);
@@ -4673,8 +4745,8 @@ function computeMorphedAttributes(object) {
4673
4745
  }
4674
4746
  }
4675
4747
  }
4676
- const morphedPositionAttribute = new import_three17.Float32BufferAttribute(modifiedPosition, 3);
4677
- const morphedNormalAttribute = new import_three17.Float32BufferAttribute(modifiedNormal, 3);
4748
+ const morphedPositionAttribute = new import_three18.Float32BufferAttribute(modifiedPosition, 3);
4749
+ const morphedNormalAttribute = new import_three18.Float32BufferAttribute(modifiedNormal, 3);
4678
4750
  return {
4679
4751
  positionAttribute,
4680
4752
  normalAttribute,
@@ -4733,7 +4805,7 @@ var ModelObject = class _ModelObject extends SceneObject {
4733
4805
  elGeo.clearGroups();
4734
4806
  }
4735
4807
  if (this.options.uvLockRotation) {
4736
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three18.Euler(...this.options.uvLockRotation));
4808
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three19.Euler(...this.options.uvLockRotation));
4737
4809
  }
4738
4810
  if (this.options.tints) {
4739
4811
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4742,19 +4814,19 @@ var ModelObject = class _ModelObject extends SceneObject {
4742
4814
  if (tintIndex === void 0 || tintIndex < 0) continue;
4743
4815
  const tint = this.options.tints[tintIndex];
4744
4816
  if (tint === void 0) continue;
4745
- const color = new import_three18.Color(tint);
4817
+ const color = new import_three19.Color(tint);
4746
4818
  for (let vertex = 0; vertex < 4; vertex++) {
4747
4819
  color.toArray(colors, (faceIndex * 4 + vertex) * 3);
4748
4820
  }
4749
4821
  }
4750
- elGeo.setAttribute("color", new import_three18.BufferAttribute(colors, 3));
4822
+ elGeo.setAttribute("color", new import_three19.BufferAttribute(colors, 3));
4751
4823
  }
4752
- 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));
4753
- elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4824
+ 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));
4825
+ elGeo.applyMatrix4(new import_three19.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4754
4826
  if (el.rotation) {
4755
4827
  applyElementRotation(el.rotation, elGeo);
4756
4828
  }
4757
- elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(-8, -8, -8));
4829
+ elGeo.applyMatrix4(new import_three19.Matrix4().makeTranslation(-8, -8, -8));
4758
4830
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4759
4831
  if (this.options.mergeMeshes) {
4760
4832
  allGeos.push(elGeo);
@@ -4776,7 +4848,7 @@ var ModelObject = class _ModelObject extends SceneObject {
4776
4848
  if (allGeos.length > 0) {
4777
4849
  combinedGeo = mergeBufferGeometries(allGeos);
4778
4850
  } else {
4779
- combinedGeo = new import_three18.BoxGeometry(16, 16, 16);
4851
+ combinedGeo = new import_three19.BoxGeometry(16, 16, 16);
4780
4852
  if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
4781
4853
  }
4782
4854
  combinedGeo.computeBoundingBox();
@@ -4829,18 +4901,7 @@ function isModelObject(obj) {
4829
4901
 
4830
4902
  // src/model/block/scene/BlockObject.ts
4831
4903
  var import_ts_deepmerge6 = __toESM(require_dist());
4832
- var import_three19 = require("three");
4833
- var p10 = prefix("BlockObject");
4834
- function matchesCondition(condition, state) {
4835
- return Object.entries(condition).every(([key, value]) => {
4836
- if (Array.isArray(value)) {
4837
- if (key === "OR") return value.some((child) => matchesCondition(child, state));
4838
- if (key === "AND") return value.every((child) => matchesCondition(child, state));
4839
- return false;
4840
- }
4841
- return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
4842
- });
4843
- }
4904
+ var import_three20 = require("three");
4844
4905
  var BlockObject = class _BlockObject extends SceneObject {
4845
4906
  constructor(blockState, options) {
4846
4907
  super(options);
@@ -4864,22 +4925,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4864
4925
  }
4865
4926
  async init() {
4866
4927
  if (this.options.applyDefaultState) {
4867
- const defaultState = this.blockState.key ? await BlockStates.getDefaultState(this.blockState.key) : void 0;
4868
- if (defaultState && Object.keys(defaultState).length > 0) {
4869
- const state = {};
4870
- for (let k in defaultState) {
4871
- state[k] = defaultState[k].default;
4872
- }
4873
- this._setState(state);
4874
- } else {
4875
- if (this.blockState.variants) {
4876
- this._setState(Object.keys(this.blockState.variants)[0]);
4877
- } else if (this.blockState.multipart) {
4878
- const condition = this.blockState.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
4879
- const state = Object.fromEntries(Object.entries(condition ?? {}).map(([key, value]) => [key, value.split("|")[0]]));
4880
- this._setState(state);
4881
- }
4882
- }
4928
+ this._setState(await BlockStateResolver.defaults(this.blockState));
4883
4929
  }
4884
4930
  if (this.options.initialState !== void 0) this._setState(this.options.initialState);
4885
4931
  await this.recreateModels();
@@ -4917,12 +4963,12 @@ var BlockObject = class _BlockObject extends SceneObject {
4917
4963
  getModelCullMask(model, worldMask) {
4918
4964
  const object = isInstanceReference(model) ? model.instanceable : model;
4919
4965
  if (object.options.displayPosition) return 0;
4920
- const position = new import_three19.Vector3();
4921
- const rotation = new import_three19.Quaternion();
4922
- const scale = new import_three19.Vector3();
4966
+ const position = new import_three20.Vector3();
4967
+ const rotation = new import_three20.Quaternion();
4968
+ const scale = new import_three20.Vector3();
4923
4969
  this.getModelMatrix(model).decompose(position, rotation, scale);
4924
- if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new import_three19.Vector3(1, 1, 1)) > 1e-10) return 0;
4925
- return ModelCulling.toLocalMask(worldMask, new import_three19.Euler().setFromQuaternion(rotation));
4970
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new import_three20.Vector3(1, 1, 1)) > 1e-10) return 0;
4971
+ return ModelCulling.toLocalMask(worldMask, new import_three20.Euler().setFromQuaternion(rotation));
4926
4972
  }
4927
4973
  async setCullMask(worldMask) {
4928
4974
  worldMask &= 63;
@@ -4970,39 +5016,7 @@ var BlockObject = class _BlockObject extends SceneObject {
4970
5016
  return ref;
4971
5017
  }
4972
5018
  async mapStateToVariant(state) {
4973
- const out = [];
4974
- if (this.blockState.variants) {
4975
- if (Object.keys(this.blockState.variants).length === 1 && "" in this.blockState.variants) {
4976
- out.push(this.getSingleVariant(this.blockState.variants[""]));
4977
- } else {
4978
- for (let variantKey in this.blockState.variants) {
4979
- const split = variantKey.split(",");
4980
- let matches = true;
4981
- for (let s of split) {
4982
- const [k, v] = s.split("=");
4983
- if (`${state[k]}` !== `${v}`) {
4984
- matches = false;
4985
- break;
4986
- }
4987
- }
4988
- if (matches) {
4989
- const variants = this.blockState.variants[variantKey];
4990
- out.push(this.getSingleVariant(variants));
4991
- }
4992
- }
4993
- }
4994
- } else if (this.blockState.multipart) {
4995
- for (let part of this.blockState.multipart) {
4996
- if (!part.apply) {
4997
- console.debug(p10, "Missing apply for blockState part", part);
4998
- continue;
4999
- }
5000
- if (!part.when || matchesCondition(part.when, state)) {
5001
- out.push(this.getSingleVariant(part.apply));
5002
- }
5003
- }
5004
- }
5005
- return out;
5019
+ return BlockStateResolver.select(this.blockState, state, (variants) => this.getSingleVariant(variants));
5006
5020
  }
5007
5021
  async recreateModels() {
5008
5022
  this.clearModels();
@@ -5013,21 +5027,7 @@ var BlockObject = class _BlockObject extends SceneObject {
5013
5027
  }
5014
5028
  }
5015
5029
  getSingleVariant(variants) {
5016
- if (!Array.isArray(variants)) return variants;
5017
- if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
5018
- const total = variants.reduce((sum, variant) => {
5019
- const weight = variant.weight ?? 1;
5020
- if (!Number.isInteger(weight) || weight < 1) {
5021
- throw new MineRenderError(`Invalid blockstate variant weight: ${weight}`);
5022
- }
5023
- return sum + weight;
5024
- }, 0);
5025
- let choice = Math.random() * total;
5026
- for (const variant of variants) {
5027
- choice -= variant.weight ?? 1;
5028
- if (choice < 0) return variant;
5029
- }
5030
- return variants[variants.length - 1];
5030
+ return BlockStateResolver.choose(variants);
5031
5031
  }
5032
5032
  // @deprecated
5033
5033
  async createAVariant(variants, instanceInfo) {
@@ -5035,13 +5035,7 @@ var BlockObject = class _BlockObject extends SceneObject {
5035
5035
  await this.createVariant(variant);
5036
5036
  }
5037
5037
  async createVariant(variant) {
5038
- const rotation = new import_three19.Euler();
5039
- if (typeof variant.x !== "undefined") {
5040
- rotation.x = toRadians(clampRotationDegrees(variant.x));
5041
- }
5042
- if (typeof variant.y !== "undefined") {
5043
- rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
5044
- }
5038
+ const rotation = BlockStateResolver.rotation(variant);
5045
5039
  const modelKey = AssetKey.parse("models", variant.model);
5046
5040
  modelKey.root = this.blockState.key?.root;
5047
5041
  const model = await Models.getMerged(modelKey);
@@ -5126,7 +5120,7 @@ var BlockObject = class _BlockObject extends SceneObject {
5126
5120
  setPositionRotationScaleAt(index, position, rotation, scale) {
5127
5121
  super.setPositionRotationScaleAt(index, position, rotation, scale);
5128
5122
  }
5129
- getMatrixAt(index, matrix = new import_three19.Matrix4()) {
5123
+ getMatrixAt(index, matrix = new import_three20.Matrix4()) {
5130
5124
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
5131
5125
  const child = this._models[0];
5132
5126
  if (child && isModelObject(child)) {
@@ -6978,7 +6972,7 @@ var StructureParser = class {
6978
6972
  };
6979
6973
 
6980
6974
  // src/renderer/MineRenderScene.ts
6981
- var import_three21 = require("three");
6975
+ var import_three22 = require("three");
6982
6976
  var import_ts_deepmerge8 = __toESM(require_dist());
6983
6977
 
6984
6978
  // src/skin/SkinPart.ts
@@ -7703,7 +7697,7 @@ var slimSkinGeometries = {
7703
7697
  var import_ts_deepmerge7 = __toESM(require_dist());
7704
7698
 
7705
7699
  // src/skin/SkinTextures.ts
7706
- var import_three20 = require("three");
7700
+ var import_three21 = require("three");
7707
7701
 
7708
7702
  // src/skin/SkinImage.ts
7709
7703
  var SkinImage = class {
@@ -7780,11 +7774,11 @@ var SkinTextures = class {
7780
7774
  });
7781
7775
  }
7782
7776
  static createMaterial(pixels, width, height, transparent = true) {
7783
- const texture = Textures.initTextureProps(new import_three20.DataTexture(pixels, width, height));
7784
- texture.colorSpace = import_three20.SRGBColorSpace;
7777
+ const texture = Textures.initTextureProps(new import_three21.DataTexture(pixels, width, height));
7778
+ texture.colorSpace = import_three21.SRGBColorSpace;
7785
7779
  texture.flipY = true;
7786
7780
  texture.needsUpdate = true;
7787
- return new import_three20.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three20.DoubleSide : import_three20.FrontSide, alphaTest: transparent ? 0.1 : 0 });
7781
+ return new import_three21.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three21.DoubleSide : import_three21.FrontSide, alphaTest: transparent ? 0.1 : 0 });
7788
7782
  }
7789
7783
  static isSlim(image, scale) {
7790
7784
  for (const [x, y] of [[46, 52], [54, 20]]) {
@@ -8011,7 +8005,7 @@ var SkinObject = class extends SceneObject {
8011
8005
  };
8012
8006
 
8013
8007
  // src/renderer/MineRenderScene.ts
8014
- var MineRenderScene = class _MineRenderScene extends import_three21.Scene {
8008
+ var MineRenderScene = class _MineRenderScene extends import_three22.Scene {
8015
8009
  constructor(options) {
8016
8010
  super();
8017
8011
  this.isMineRenderScene = true;
@@ -8136,17 +8130,17 @@ function isMineRenderScene(obj) {
8136
8130
  }
8137
8131
 
8138
8132
  // src/renderer/Renderer.ts
8139
- var import_three23 = require("three");
8133
+ var import_three24 = require("three");
8140
8134
  var import_ts_deepmerge9 = __toESM(require_dist());
8141
8135
  var import_stats = __toESM(require("stats.js"));
8142
8136
  var import_postprocessing = require("postprocessing");
8143
8137
 
8144
8138
  // src/three/OrbitControls.js
8145
- var import_three22 = require("three");
8139
+ var import_three23 = require("three");
8146
8140
  var _changeEvent = { type: "change" };
8147
8141
  var _startEvent = { type: "start" };
8148
8142
  var _endEvent = { type: "end" };
8149
- var OrbitControls = class extends import_three22.EventDispatcher {
8143
+ var OrbitControls = class extends import_three23.EventDispatcher {
8150
8144
  constructor(object, domElement) {
8151
8145
  super();
8152
8146
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
@@ -8155,7 +8149,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8155
8149
  this.domElement = domElement;
8156
8150
  this.domElement.style.touchAction = "none";
8157
8151
  this.enabled = true;
8158
- this.target = new import_three22.Vector3();
8152
+ this.target = new import_three23.Vector3();
8159
8153
  this.minDistance = 0;
8160
8154
  this.maxDistance = Infinity;
8161
8155
  this.minZoom = 0;
@@ -8177,8 +8171,8 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8177
8171
  this.autoRotate = false;
8178
8172
  this.autoRotateSpeed = 2;
8179
8173
  this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" };
8180
- this.mouseButtons = { LEFT: import_three22.MOUSE.ROTATE, MIDDLE: import_three22.MOUSE.DOLLY, RIGHT: import_three22.MOUSE.PAN };
8181
- this.touches = { ONE: import_three22.TOUCH.ROTATE, TWO: import_three22.TOUCH.DOLLY_PAN };
8174
+ this.mouseButtons = { LEFT: import_three23.MOUSE.ROTATE, MIDDLE: import_three23.MOUSE.DOLLY, RIGHT: import_three23.MOUSE.PAN };
8175
+ this.touches = { ONE: import_three23.TOUCH.ROTATE, TWO: import_three23.TOUCH.DOLLY_PAN };
8182
8176
  this.target0 = this.target.clone();
8183
8177
  this.position0 = this.object.position.clone();
8184
8178
  this.zoom0 = this.object.zoom;
@@ -8207,11 +8201,11 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8207
8201
  state = STATE.NONE;
8208
8202
  };
8209
8203
  this.update = function() {
8210
- const offset = new import_three22.Vector3();
8211
- const quat = new import_three22.Quaternion().setFromUnitVectors(object.up, new import_three22.Vector3(0, 1, 0));
8204
+ const offset = new import_three23.Vector3();
8205
+ const quat = new import_three23.Quaternion().setFromUnitVectors(object.up, new import_three23.Vector3(0, 1, 0));
8212
8206
  const quatInverse = quat.clone().invert();
8213
- const lastPosition = new import_three22.Vector3();
8214
- const lastQuaternion = new import_three22.Quaternion();
8207
+ const lastPosition = new import_three23.Vector3();
8208
+ const lastQuaternion = new import_three23.Quaternion();
8215
8209
  const twoPI = 2 * Math.PI;
8216
8210
  return function update() {
8217
8211
  const position = scope.object.position;
@@ -8297,20 +8291,20 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8297
8291
  };
8298
8292
  let state = STATE.NONE;
8299
8293
  const EPS = 1e-6;
8300
- const spherical = new import_three22.Spherical();
8301
- const sphericalDelta = new import_three22.Spherical();
8294
+ const spherical = new import_three23.Spherical();
8295
+ const sphericalDelta = new import_three23.Spherical();
8302
8296
  let scale = 1;
8303
- const panOffset = new import_three22.Vector3();
8297
+ const panOffset = new import_three23.Vector3();
8304
8298
  let zoomChanged = false;
8305
- const rotateStart = new import_three22.Vector2();
8306
- const rotateEnd = new import_three22.Vector2();
8307
- const rotateDelta = new import_three22.Vector2();
8308
- const panStart = new import_three22.Vector2();
8309
- const panEnd = new import_three22.Vector2();
8310
- const panDelta = new import_three22.Vector2();
8311
- const dollyStart = new import_three22.Vector2();
8312
- const dollyEnd = new import_three22.Vector2();
8313
- const dollyDelta = new import_three22.Vector2();
8299
+ const rotateStart = new import_three23.Vector2();
8300
+ const rotateEnd = new import_three23.Vector2();
8301
+ const rotateDelta = new import_three23.Vector2();
8302
+ const panStart = new import_three23.Vector2();
8303
+ const panEnd = new import_three23.Vector2();
8304
+ const panDelta = new import_three23.Vector2();
8305
+ const dollyStart = new import_three23.Vector2();
8306
+ const dollyEnd = new import_three23.Vector2();
8307
+ const dollyDelta = new import_three23.Vector2();
8314
8308
  const pointers = [];
8315
8309
  const pointerPositions = {};
8316
8310
  function getAutoRotationAngle() {
@@ -8326,7 +8320,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8326
8320
  sphericalDelta.phi -= angle;
8327
8321
  }
8328
8322
  const panLeft = function() {
8329
- const v = new import_three22.Vector3();
8323
+ const v = new import_three23.Vector3();
8330
8324
  return function panLeft2(distance, objectMatrix) {
8331
8325
  v.setFromMatrixColumn(objectMatrix, 0);
8332
8326
  v.multiplyScalar(-distance);
@@ -8334,7 +8328,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8334
8328
  };
8335
8329
  }();
8336
8330
  const panUp = function() {
8337
- const v = new import_three22.Vector3();
8331
+ const v = new import_three23.Vector3();
8338
8332
  return function panUp2(distance, objectMatrix) {
8339
8333
  if (scope.screenSpacePanning === true) {
8340
8334
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -8347,7 +8341,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8347
8341
  };
8348
8342
  }();
8349
8343
  const pan = function() {
8350
- const offset = new import_three22.Vector3();
8344
+ const offset = new import_three23.Vector3();
8351
8345
  return function pan2(deltaX, deltaY) {
8352
8346
  const element = scope.domElement;
8353
8347
  if (scope.object.isPerspectiveCamera) {
@@ -8596,12 +8590,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8596
8590
  mouseAction = -1;
8597
8591
  }
8598
8592
  switch (mouseAction) {
8599
- case import_three22.MOUSE.DOLLY:
8593
+ case import_three23.MOUSE.DOLLY:
8600
8594
  if (scope.enableZoom === false) return;
8601
8595
  handleMouseDownDolly(event);
8602
8596
  state = STATE.DOLLY;
8603
8597
  break;
8604
- case import_three22.MOUSE.ROTATE:
8598
+ case import_three23.MOUSE.ROTATE:
8605
8599
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
8606
8600
  if (scope.enablePan === false) return;
8607
8601
  handleMouseDownPan(event);
@@ -8612,7 +8606,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8612
8606
  state = STATE.ROTATE;
8613
8607
  }
8614
8608
  break;
8615
- case import_three22.MOUSE.PAN:
8609
+ case import_three23.MOUSE.PAN:
8616
8610
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
8617
8611
  if (scope.enableRotate === false) return;
8618
8612
  handleMouseDownRotate(event);
@@ -8668,12 +8662,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8668
8662
  switch (pointers.length) {
8669
8663
  case 1:
8670
8664
  switch (scope.touches.ONE) {
8671
- case import_three22.TOUCH.ROTATE:
8665
+ case import_three23.TOUCH.ROTATE:
8672
8666
  if (scope.enableRotate === false) return;
8673
8667
  handleTouchStartRotate();
8674
8668
  state = STATE.TOUCH_ROTATE;
8675
8669
  break;
8676
- case import_three22.TOUCH.PAN:
8670
+ case import_three23.TOUCH.PAN:
8677
8671
  if (scope.enablePan === false) return;
8678
8672
  handleTouchStartPan();
8679
8673
  state = STATE.TOUCH_PAN;
@@ -8684,12 +8678,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8684
8678
  break;
8685
8679
  case 2:
8686
8680
  switch (scope.touches.TWO) {
8687
- case import_three22.TOUCH.DOLLY_PAN:
8681
+ case import_three23.TOUCH.DOLLY_PAN:
8688
8682
  if (scope.enableZoom === false && scope.enablePan === false) return;
8689
8683
  handleTouchStartDollyPan();
8690
8684
  state = STATE.TOUCH_DOLLY_PAN;
8691
8685
  break;
8692
- case import_three22.TOUCH.DOLLY_ROTATE:
8686
+ case import_three23.TOUCH.DOLLY_ROTATE:
8693
8687
  if (scope.enableZoom === false && scope.enableRotate === false) return;
8694
8688
  handleTouchStartDollyRotate();
8695
8689
  state = STATE.TOUCH_DOLLY_ROTATE;
@@ -8756,7 +8750,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8756
8750
  function trackPointer(event) {
8757
8751
  let position = pointerPositions[event.pointerId];
8758
8752
  if (position === void 0) {
8759
- position = new import_three22.Vector2();
8753
+ position = new import_three23.Vector2();
8760
8754
  pointerPositions[event.pointerId] = position;
8761
8755
  }
8762
8756
  position.set(event.pageX, event.pageY);
@@ -8776,10 +8770,10 @@ var MapControls = class extends OrbitControls {
8776
8770
  constructor(object, domElement) {
8777
8771
  super(object, domElement);
8778
8772
  this.screenSpacePanning = false;
8779
- this.mouseButtons.LEFT = import_three22.MOUSE.PAN;
8780
- this.mouseButtons.RIGHT = import_three22.MOUSE.ROTATE;
8781
- this.touches.ONE = import_three22.TOUCH.PAN;
8782
- this.touches.TWO = import_three22.TOUCH.DOLLY_ROTATE;
8773
+ this.mouseButtons.LEFT = import_three23.MOUSE.PAN;
8774
+ this.mouseButtons.RIGHT = import_three23.MOUSE.ROTATE;
8775
+ this.touches.ONE = import_three23.TOUCH.PAN;
8776
+ this.touches.TWO = import_three23.TOUCH.DOLLY_ROTATE;
8783
8777
  }
8784
8778
  };
8785
8779
 
@@ -8824,8 +8818,8 @@ var Renderer = class _Renderer {
8824
8818
  top: void 0,
8825
8819
  bottom: void 0
8826
8820
  },
8827
- position: new import_three23.Vector3(50, 50, 50),
8828
- lookingAt: new import_three23.Vector3(0, 0, 0)
8821
+ position: new import_three24.Vector3(50, 50, 50),
8822
+ lookingAt: new import_three24.Vector3(0, 0, 0)
8829
8823
  },
8830
8824
  render: {
8831
8825
  fpsLimit: 60,
@@ -8855,14 +8849,14 @@ var Renderer = class _Renderer {
8855
8849
  switch (this.options.camera.type) {
8856
8850
  case "perspective":
8857
8851
  default:
8858
- return new import_three23.PerspectiveCamera(
8852
+ return new import_three24.PerspectiveCamera(
8859
8853
  this.options.camera.perspective.fov,
8860
8854
  this.options.camera.perspective.aspect ?? this.viewWidth / this.viewHeight,
8861
8855
  this.options.camera.near,
8862
8856
  this.options.camera.far
8863
8857
  );
8864
8858
  case "orthographic":
8865
- return new import_three23.OrthographicCamera(
8859
+ return new import_three24.OrthographicCamera(
8866
8860
  this.options.camera.orthographic.left ?? this.viewWidth / -2,
8867
8861
  this.options.camera.orthographic.right ?? this.viewWidth / 2,
8868
8862
  this.options.camera.orthographic.top ?? this.viewHeight / 2,
@@ -8873,7 +8867,7 @@ var Renderer = class _Renderer {
8873
8867
  }
8874
8868
  }
8875
8869
  createRenderer() {
8876
- const renderer = new import_three23.WebGLRenderer({
8870
+ const renderer = new import_three24.WebGLRenderer({
8877
8871
  antialias: this.options.render.antialias,
8878
8872
  alpha: true,
8879
8873
  powerPreference: "high-performance",
@@ -8881,8 +8875,8 @@ var Renderer = class _Renderer {
8881
8875
  });
8882
8876
  renderer.setClearColor(0, 0);
8883
8877
  renderer.shadowMap.enabled = true;
8884
- renderer.shadowMap.type = import_three23.PCFSoftShadowMap;
8885
- renderer.outputColorSpace = import_three23.SRGBColorSpace;
8878
+ renderer.shadowMap.type = import_three24.PCFSoftShadowMap;
8879
+ renderer.outputColorSpace = import_three24.SRGBColorSpace;
8886
8880
  renderer.setSize(this.viewWidth, this.viewHeight);
8887
8881
  return renderer;
8888
8882
  }
@@ -8916,20 +8910,20 @@ var Renderer = class _Renderer {
8916
8910
  window["__THREE_DEVTOOLS__"].dispatchEvent(new CustomEvent("observe", { detail: this.scene }));
8917
8911
  }
8918
8912
  if (this.options.debug.grid) {
8919
- const gridHelper = new import_three23.GridHelper(128, 16);
8913
+ const gridHelper = new import_three24.GridHelper(128, 16);
8920
8914
  this._debugHelpers.push(gridHelper);
8921
8915
  this.scene.add(gridHelper);
8922
- const gridHelper2 = new import_three23.GridHelper(128, 16);
8916
+ const gridHelper2 = new import_three24.GridHelper(128, 16);
8923
8917
  this._debugHelpers.push(gridHelper2);
8924
8918
  gridHelper2.rotation.x = 90 * (Math.PI / 180);
8925
8919
  this.scene.add(gridHelper2);
8926
- const gridHelper3 = new import_three23.GridHelper(128, 16);
8920
+ const gridHelper3 = new import_three24.GridHelper(128, 16);
8927
8921
  this._debugHelpers.push(gridHelper3);
8928
8922
  gridHelper3.rotation.z = 90 * (Math.PI / 180);
8929
8923
  this.scene.add(gridHelper3);
8930
8924
  }
8931
8925
  if (this.options.debug.axes) {
8932
- const axesHelper = new import_three23.AxesHelper(64);
8926
+ const axesHelper = new import_three24.AxesHelper(64);
8933
8927
  this._debugHelpers.push(axesHelper);
8934
8928
  this.scene.add(axesHelper);
8935
8929
  }
@@ -9536,32 +9530,121 @@ var AnvilParser = class {
9536
9530
  };
9537
9531
 
9538
9532
  // src/world/Chunk.ts
9533
+ var import_three29 = require("three");
9534
+
9535
+ // src/world/MineRenderWorld.ts
9536
+ var import_three27 = require("three");
9537
+
9538
+ // src/world/SectionModels.ts
9539
9539
  var import_three26 = require("three");
9540
+ var SectionModel = class extends ModelObject {
9541
+ applyTextures() {
9542
+ }
9543
+ };
9544
+ function cached(map, key, load) {
9545
+ let value = map.get(key);
9546
+ if (!value) {
9547
+ value = load().catch((error) => {
9548
+ map.delete(key);
9549
+ throw error;
9550
+ });
9551
+ map.set(key, value);
9552
+ }
9553
+ return value;
9554
+ }
9555
+ var SectionModels = class {
9556
+ constructor(maxAtlasSize = 2048) {
9557
+ this.maxAtlasSize = maxAtlasSize;
9558
+ this.states = /* @__PURE__ */ new WeakMap();
9559
+ this.models = /* @__PURE__ */ new WeakMap();
9560
+ this.templates = /* @__PURE__ */ new Set();
9561
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
9562
+ }
9563
+ async get(blockState, properties = {}) {
9564
+ if (!blockState.variants || blockState.multipart) return void 0;
9565
+ let states = this.states.get(blockState);
9566
+ if (!states) this.states.set(blockState, states = /* @__PURE__ */ new Map());
9567
+ const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
9568
+ const prepared = await cached(states, key, () => this.prepare(blockState, properties));
9569
+ if (!prepared) return void 0;
9570
+ return prepared.templates.get(BlockStateResolver.choose(prepared.variants));
9571
+ }
9572
+ async prepare(blockState, properties) {
9573
+ const state = { ...await BlockStateResolver.defaults(blockState), ...properties };
9574
+ const groups = BlockStateResolver.matching(blockState, state);
9575
+ if (groups.length !== 1) return void 0;
9576
+ const variants = groups[0];
9577
+ const choices = Array.isArray(variants) ? variants : [variants];
9578
+ if (!choices.length) return void 0;
9579
+ const templates = /* @__PURE__ */ new Map();
9580
+ for (const variant of choices) {
9581
+ const model = await Models.getMerged(AssetKey.parse("models", variant.model));
9582
+ if (!model) return void 0;
9583
+ const atlas = await UVMapper.getAtlas(model);
9584
+ const rotation = BlockStateResolver.rotation(variant);
9585
+ 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;
9586
+ const tints = await BlockTints.get(blockState.key, state, model);
9587
+ let models = this.models.get(model);
9588
+ if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
9589
+ const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
9590
+ const template = await cached(models, key, async () => {
9591
+ const object = new SectionModel(model, {
9592
+ instanceMeshes: false,
9593
+ mergeMeshes: false,
9594
+ tints,
9595
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
9596
+ });
9597
+ await object.init();
9598
+ const geometry = object.children[0].geometry;
9599
+ geometry.applyMatrix4(new import_three26.Matrix4().makeRotationFromEuler(rotation));
9600
+ object.clear();
9601
+ const cullFaces = CUBE_FACES.map((face) => {
9602
+ const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
9603
+ if (local < 0) return 0;
9604
+ const normal = new import_three26.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
9605
+ 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);
9606
+ return 1 << world;
9607
+ });
9608
+ const template2 = { geometry, atlas, cullFaces };
9609
+ this.templates.add(template2);
9610
+ return template2;
9611
+ });
9612
+ templates.set(variant, template);
9613
+ }
9614
+ return { variants, templates };
9615
+ }
9616
+ clear() {
9617
+ for (const template of this.templates) template.geometry.dispose();
9618
+ this.templates.clear();
9619
+ this.states = /* @__PURE__ */ new WeakMap();
9620
+ this.models = /* @__PURE__ */ new WeakMap();
9621
+ }
9622
+ };
9540
9623
 
9541
9624
  // src/world/MineRenderWorld.ts
9542
- var import_three25 = require("three");
9543
9625
  var MineRenderWorld = class {
9544
- constructor(scene) {
9626
+ constructor(scene, options = {}) {
9545
9627
  this._chunks = /* @__PURE__ */ new Map();
9546
9628
  this.pendingCulling = /* @__PURE__ */ new Map();
9547
9629
  this.scene = scene;
9630
+ if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
9548
9631
  }
9549
9632
  getBlockAt(posOrX, y, z) {
9550
9633
  if (typeof posOrX == "number") {
9551
- return this.getBlockAt(new import_three25.Vector3(posOrX, y, z));
9634
+ return this.getBlockAt(new import_three27.Vector3(posOrX, y, z));
9552
9635
  }
9553
9636
  if (isTripleArray(posOrX)) {
9554
- return this.getBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9637
+ return this.getBlockAt(new import_three27.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9555
9638
  }
9556
9639
  this.validatePosBounds(posOrX);
9557
9640
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
9558
9641
  }
9559
9642
  async setBlockAt(posOrX, yOrBlock, z, block) {
9560
9643
  if (typeof posOrX == "number") {
9561
- return this.setBlockAt(new import_three25.Vector3(posOrX, yOrBlock, z), block);
9644
+ return this.setBlockAt(new import_three27.Vector3(posOrX, yOrBlock, z), block);
9562
9645
  }
9563
9646
  if (isTripleArray(posOrX)) {
9564
- return this.setBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9647
+ return this.setBlockAt(new import_three27.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9565
9648
  }
9566
9649
  return this.placeBlock(posOrX, yOrBlock);
9567
9650
  }
@@ -9582,7 +9665,7 @@ var MineRenderWorld = class {
9582
9665
  const collect = async (positions2) => {
9583
9666
  for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
9584
9667
  };
9585
- const place = (block) => this.placeBlock(new import_three25.Vector3(...block.position), block, collect);
9668
+ const place = (block) => this.placeBlock(new import_three27.Vector3(...block.position), block, collect);
9586
9669
  const keys = /* @__PURE__ */ new Map();
9587
9670
  for (const block of multiblock.blocks) {
9588
9671
  if (Chunk.isAir(block)) continue;
@@ -9654,12 +9737,13 @@ var MineRenderWorld = class {
9654
9737
  for (const chunk of chunks) {
9655
9738
  await chunk.dispose();
9656
9739
  }
9740
+ this.sectionModels?.clear();
9657
9741
  }
9658
9742
  updateCulling(positions) {
9659
9743
  for (const pos of positions) {
9660
9744
  this.pendingCulling.set(pos.toArray().join(","), pos.clone());
9661
9745
  for (const offset of CUBE_FACE_OFFSETS) {
9662
- const neighbor = pos.clone().add(new import_three25.Vector3(...offset));
9746
+ const neighbor = pos.clone().add(new import_three27.Vector3(...offset));
9663
9747
  this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
9664
9748
  }
9665
9749
  }
@@ -9668,17 +9752,22 @@ var MineRenderWorld = class {
9668
9752
  while (this.pendingCulling.size) {
9669
9753
  const batch = [...this.pendingCulling.values()];
9670
9754
  this.pendingCulling.clear();
9755
+ const changed = /* @__PURE__ */ new Set();
9671
9756
  for (const pos of batch) {
9672
- const block = this.getBlockAt(pos)?.object;
9673
- if (!block) continue;
9757
+ const chunk = this.getChunkAt(pos);
9758
+ if (!chunk) continue;
9759
+ changed.add(chunk);
9760
+ if (!chunk.getBlockAt(pos)) continue;
9674
9761
  let mask = 0;
9675
9762
  for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
9676
- if (this.getBlockAt(pos.clone().add(new import_three25.Vector3(...offset)))?.object.isOccluding) {
9763
+ const neighbor = pos.clone().add(new import_three27.Vector3(...offset));
9764
+ if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
9677
9765
  mask |= 1 << face;
9678
9766
  }
9679
9767
  }
9680
- await block.setCullMask(mask);
9768
+ await chunk.setCullMaskAt(pos, mask);
9681
9769
  }
9770
+ for (const chunk of changed) chunk.rebuildSectionMesh();
9682
9771
  }
9683
9772
  } finally {
9684
9773
  this.culling = void 0;
@@ -9694,7 +9783,8 @@ var MineRenderWorld = class {
9694
9783
  Math.floor(pos.x / 16),
9695
9784
  Math.floor(pos.y / 16),
9696
9785
  Math.floor(pos.z / 16),
9697
- (positions) => this.updateCulling(positions)
9786
+ (positions) => this.updateCulling(positions),
9787
+ this.sectionModels
9698
9788
  );
9699
9789
  this._chunks.set(key, chunk);
9700
9790
  }
@@ -9704,14 +9794,14 @@ var MineRenderWorld = class {
9704
9794
  return this._chunks.get(this.worldPosToChunkKey(pos));
9705
9795
  }
9706
9796
  static worldToScenePosition(pos) {
9707
- return new import_three25.Vector3(
9797
+ return new import_three27.Vector3(
9708
9798
  pos.x * 16,
9709
9799
  pos.y * 16,
9710
9800
  pos.z * 16
9711
9801
  );
9712
9802
  }
9713
9803
  static sceneToWorldPosition(pos) {
9714
- return new import_three25.Vector3(
9804
+ return new import_three27.Vector3(
9715
9805
  pos.x / 16,
9716
9806
  pos.y / 16,
9717
9807
  pos.z / 16
@@ -9730,12 +9820,118 @@ var MineRenderWorld = class {
9730
9820
  }
9731
9821
  };
9732
9822
 
9823
+ // src/world/SectionMesh.ts
9824
+ var import_three28 = require("three");
9825
+ var SectionMesh = class _SectionMesh extends import_three28.Group {
9826
+ constructor() {
9827
+ super(...arguments);
9828
+ this.ownedMeshes = [];
9829
+ }
9830
+ static build(entries, maxAtlasSize = 2048) {
9831
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
9832
+ const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
9833
+ const atlases = [...new Set(visible.map((entry) => entry.template.atlas))].sort((a, b) => b.image.height - a.image.height);
9834
+ const pages = [];
9835
+ const locations = /* @__PURE__ */ new Map();
9836
+ for (const atlas of atlases) {
9837
+ const { width, height } = atlas.image;
9838
+ if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
9839
+ let selected;
9840
+ for (const page of pages) {
9841
+ const nextRow = page.x + width > maxAtlasSize;
9842
+ if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
9843
+ if (nextRow) {
9844
+ page.y += page.rowHeight;
9845
+ page.x = 0;
9846
+ page.rowHeight = 0;
9847
+ }
9848
+ selected = page;
9849
+ break;
9850
+ }
9851
+ if (!selected) {
9852
+ selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
9853
+ pages.push(selected);
9854
+ }
9855
+ const placement = { atlas, x: selected.x, y: selected.y };
9856
+ selected.placements.push(placement);
9857
+ selected.x += width;
9858
+ selected.rowHeight = Math.max(selected.rowHeight, height);
9859
+ selected.width = Math.max(selected.width, selected.x);
9860
+ selected.height = Math.max(selected.height, selected.y + height);
9861
+ locations.set(atlas, { page: selected, placement });
9862
+ }
9863
+ for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
9864
+ const section = new _SectionMesh();
9865
+ for (const page of pages) {
9866
+ const canvas = createCanvas2(page.width, page.height);
9867
+ const context = canvas.getContext("2d");
9868
+ for (const { atlas, x, y } of page.placements) {
9869
+ context.drawImage(atlas.image.canvas, x, y);
9870
+ }
9871
+ const positions = [], normals = [], uvs = [], colors = [], indices = [];
9872
+ for (const { index, template, cullMask } of page.entries) {
9873
+ const position = template.geometry.getAttribute("position");
9874
+ const normal = template.geometry.getAttribute("normal");
9875
+ const uv = template.geometry.getAttribute("uv");
9876
+ const color = template.geometry.getAttribute("color");
9877
+ const sourceIndices = template.geometry.getIndex();
9878
+ const placement = locations.get(template.atlas).placement;
9879
+ const offsetX = index % 16 * 16;
9880
+ const offsetY = Math.floor(index / 256) * 16;
9881
+ const offsetZ = Math.floor(index / 16) % 16 * 16;
9882
+ for (let face = 0; face < 6; face++) {
9883
+ if (cullMask & template.cullFaces[face]) continue;
9884
+ const start = positions.length / 3;
9885
+ for (let corner = 0; corner < 4; corner++) {
9886
+ const vertex = face * 4 + corner;
9887
+ positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
9888
+ normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
9889
+ uvs.push(
9890
+ (placement.x + uv.getX(vertex) * template.atlas.image.width) / page.width,
9891
+ 1 - (placement.y + (1 - uv.getY(vertex)) * template.atlas.image.height) / page.height
9892
+ );
9893
+ colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
9894
+ }
9895
+ for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
9896
+ }
9897
+ }
9898
+ const geometry = new import_three28.BufferGeometry();
9899
+ geometry.setAttribute("position", new import_three28.Float32BufferAttribute(positions, 3));
9900
+ geometry.setAttribute("normal", new import_three28.Float32BufferAttribute(normals, 3));
9901
+ geometry.setAttribute("uv", new import_three28.Float32BufferAttribute(uvs, 2));
9902
+ geometry.setAttribute("color", new import_three28.Float32BufferAttribute(colors, 3));
9903
+ geometry.setIndex(indices);
9904
+ geometry.computeBoundingBox();
9905
+ geometry.computeBoundingSphere();
9906
+ const material = Materials.createShadedCanvasMaterial(canvas, false);
9907
+ material.vertexColors = true;
9908
+ const texture = material.uniforms?.map?.value ?? material.map;
9909
+ const mesh = new import_three28.Mesh(geometry, material);
9910
+ section.add(mesh);
9911
+ section.ownedMeshes.push({ mesh, texture });
9912
+ }
9913
+ return section;
9914
+ }
9915
+ dispose() {
9916
+ for (const { mesh, texture } of this.ownedMeshes.splice(0)) {
9917
+ mesh.removeFromParent();
9918
+ mesh.geometry.dispose();
9919
+ mesh.material.dispose();
9920
+ texture?.dispose();
9921
+ }
9922
+ this.removeFromParent();
9923
+ }
9924
+ };
9925
+
9733
9926
  // src/world/Chunk.ts
9734
9927
  var Chunk = class _Chunk {
9735
- constructor(scene, x, y, z, onBlocksChanged) {
9928
+ constructor(scene, x, y, z, onBlocksChanged, sectionModels) {
9736
9929
  this.onBlocksChanged = onBlocksChanged;
9930
+ this.sectionModels = sectionModels;
9737
9931
  this.data = new ChunkData();
9738
9932
  this.renderedBlocks = /* @__PURE__ */ new Map();
9933
+ this.sectionBlocks = /* @__PURE__ */ new Map();
9934
+ this.meshDirty = false;
9739
9935
  this.scene = scene;
9740
9936
  this.x = x;
9741
9937
  this.y = y;
@@ -9743,10 +9939,10 @@ var Chunk = class _Chunk {
9743
9939
  }
9744
9940
  getBlockAt(posOrX, y, z) {
9745
9941
  if (typeof posOrX == "number") {
9746
- return this.getBlockAt(new import_three26.Vector3(posOrX, y, z));
9942
+ return this.getBlockAt(new import_three29.Vector3(posOrX, y, z));
9747
9943
  }
9748
9944
  if (isTripleArray(posOrX)) {
9749
- return this.getBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9945
+ return this.getBlockAt(new import_three29.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9750
9946
  }
9751
9947
  const pos = this.worldPosToChunkPos(posOrX);
9752
9948
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -9754,10 +9950,10 @@ var Chunk = class _Chunk {
9754
9950
  }
9755
9951
  async setBlockAt(posOrX, yOrBlock, z, block) {
9756
9952
  if (typeof posOrX == "number") {
9757
- return this.setBlockAt(new import_three26.Vector3(posOrX, yOrBlock, z), block);
9953
+ return this.setBlockAt(new import_three29.Vector3(posOrX, yOrBlock, z), block);
9758
9954
  }
9759
9955
  if (isTripleArray(posOrX)) {
9760
- return this.setBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9956
+ return this.setBlockAt(new import_three29.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9761
9957
  }
9762
9958
  const worldPos = posOrX;
9763
9959
  const pos = this.worldPosToChunkPos(worldPos);
@@ -9774,7 +9970,8 @@ var Chunk = class _Chunk {
9774
9970
  const index = _Chunk.chunkPosToBlockIndex(pos);
9775
9971
  this.data.set(index, block);
9776
9972
  const readBlock = this.data.snapshot(index);
9777
- this.renderedBlocks.get(index)?.object.removeFromScene();
9973
+ this.renderedBlocks.get(index)?.object?.removeFromScene();
9974
+ if (this.sectionBlocks.delete(index)) this.meshDirty = true;
9778
9975
  this.renderedBlocks.delete(index);
9779
9976
  let object;
9780
9977
  try {
@@ -9785,13 +9982,19 @@ var Chunk = class _Chunk {
9785
9982
  this.data.set(index, void 0);
9786
9983
  return void 0;
9787
9984
  }
9788
- object = await this.scene.addBlock(blockState, {
9789
- mergeMeshes: true,
9790
- instanceMeshes: true,
9791
- maxInstanceCount: 2e3,
9792
- initialState: stored.properties
9793
- });
9794
- object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
9985
+ const template = await this.sectionModels?.get(blockState, stored.properties);
9986
+ if (template) {
9987
+ this.sectionBlocks.set(index, { index, template, cullMask: 0 });
9988
+ this.meshDirty = true;
9989
+ } else {
9990
+ object = await this.scene.addBlock(blockState, {
9991
+ mergeMeshes: true,
9992
+ instanceMeshes: true,
9993
+ maxInstanceCount: 2e3,
9994
+ initialState: stored.properties
9995
+ });
9996
+ object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
9997
+ }
9795
9998
  const info = { get block() {
9796
9999
  return readBlock();
9797
10000
  }, object };
@@ -9805,11 +10008,43 @@ var Chunk = class _Chunk {
9805
10008
  await onBlocksChanged?.([worldPos]);
9806
10009
  }
9807
10010
  }
10011
+ isOccludingAt(pos) {
10012
+ const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
10013
+ return this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false);
10014
+ }
10015
+ async setCullMaskAt(pos, mask) {
10016
+ const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
10017
+ const entry = this.sectionBlocks.get(index);
10018
+ if (entry) {
10019
+ if (entry.cullMask !== mask) {
10020
+ entry.cullMask = mask;
10021
+ this.meshDirty = true;
10022
+ }
10023
+ } else {
10024
+ await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
10025
+ }
10026
+ }
10027
+ rebuildSectionMesh() {
10028
+ if (!this.meshDirty) return;
10029
+ const next = this.sectionBlocks.size ? SectionMesh.build([...this.sectionBlocks.values()], this.sectionModels.maxAtlasSize) : void 0;
10030
+ this.sectionMesh?.dispose();
10031
+ this.sectionMesh = next;
10032
+ if (next) {
10033
+ next.position.set(this.x * 256, this.y * 256, this.z * 256);
10034
+ this.scene.add(next);
10035
+ }
10036
+ this.scene.dirty = true;
10037
+ this.meshDirty = false;
10038
+ }
9808
10039
  async clear(onBlocksChanged = this.onBlocksChanged) {
9809
10040
  const positions = [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
9810
- new import_three26.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
10041
+ new import_three29.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
9811
10042
  ));
9812
- for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
10043
+ for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
10044
+ this.sectionBlocks.clear();
10045
+ this.sectionMesh?.dispose();
10046
+ this.sectionMesh = void 0;
10047
+ this.meshDirty = false;
9813
10048
  this.renderedBlocks.clear();
9814
10049
  this.data.clear();
9815
10050
  await onBlocksChanged?.(positions);
@@ -9827,14 +10062,14 @@ var Chunk = class _Chunk {
9827
10062
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
9828
10063
  }
9829
10064
  worldPosToChunkPos(pos) {
9830
- return new import_three26.Vector3(
10065
+ return new import_three29.Vector3(
9831
10066
  pos.x - this.x * 16,
9832
10067
  pos.y - this.y * 16,
9833
10068
  pos.z - this.z * 16
9834
10069
  );
9835
10070
  }
9836
10071
  chunkPosToWorldPos(pos) {
9837
- return new import_three26.Vector3(
10072
+ return new import_three29.Vector3(
9838
10073
  this.x * 16 + pos.x,
9839
10074
  this.y * 16 + pos.y,
9840
10075
  this.z * 16 + pos.z
@@ -9858,6 +10093,7 @@ var Chunk = class _Chunk {
9858
10093
  BasicAssetKey,
9859
10094
  BatchedExecutor,
9860
10095
  BlockObject,
10096
+ BlockStateResolver,
9861
10097
  BlockStates,
9862
10098
  BlockTints,
9863
10099
  BrowserArchiveProxy,
@@ -9916,6 +10152,8 @@ var Chunk = class _Chunk {
9916
10152
  SceneStats,
9917
10153
  SceneStatsDisplay,
9918
10154
  SchematicParser,
10155
+ SectionMesh,
10156
+ SectionModels,
9919
10157
  SkinImage,
9920
10158
  SkinObject,
9921
10159
  SkinPart,