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_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;
@@ -3210,6 +3213,10 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3210
3213
  this.setMatrixAt(index, new import_three10.Matrix4());
3211
3214
  return reference;
3212
3215
  }
3216
+ /** Returns the live reference for a raycast hit on this object's instance mesh. */
3217
+ getInstanceReference(mesh, index) {
3218
+ return mesh === this.instanceMesh ? this.instanceReferences.get(index) : void 0;
3219
+ }
3213
3220
  isInstanceActive(index, reference) {
3214
3221
  return this.instanceReferences.get(index) === reference;
3215
3222
  }
@@ -3292,9 +3299,9 @@ var _SceneObject = class _SceneObject extends import_three10.Object3D {
3292
3299
  }
3293
3300
  getRotationAt(index, euler = new import_three10.Euler()) {
3294
3301
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3295
- const matrix = this.getMatrixAt(index);
3296
- euler.setFromRotationMatrix(matrix);
3297
- return euler;
3302
+ const rotation = new import_three10.Quaternion();
3303
+ this.getMatrixAt(index).decompose(new import_three10.Vector3(), rotation, new import_three10.Vector3());
3304
+ return euler.setFromQuaternion(rotation);
3298
3305
  }
3299
3306
  setScaleAt(index, scale) {
3300
3307
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
@@ -3550,11 +3557,12 @@ var GuiObject = class extends SceneObject {
3550
3557
  // src/inspector/SceneInspector.ts
3551
3558
  var import_three14 = require("three");
3552
3559
  var p7 = prefix("SceneInspector");
3553
- var help = "<span>Ctrl+Click to Select<br/></span><br/>";
3560
+ var help = "<span>Ctrl/Cmd+Click to Select<br/></span><br/>";
3554
3561
  var SceneInspector = class {
3555
3562
  constructor(renderer) {
3556
3563
  this.renderer = renderer;
3557
3564
  this.mouse = new import_three14.Vector2();
3565
+ this.listeners = new AbortController();
3558
3566
  this.objectInfoContainer = document.createElement("div");
3559
3567
  this.objectControlsContainer = document.createElement("div");
3560
3568
  this.raycaster = new import_three14.Raycaster();
@@ -3564,13 +3572,44 @@ var SceneInspector = class {
3564
3572
  this.objectInfoContainer.classList.add("minerender-inspector", "minerender-object-info");
3565
3573
  this.objectControlsContainer.classList.add("minerender-inspector", "minerender-object-controls");
3566
3574
  this.objectInfoContainer.innerHTML = help;
3567
- document.addEventListener("click", this.onClick.bind(this), {
3568
- passive: true
3569
- });
3575
+ const canvas = this.renderer.renderer.domElement;
3576
+ const options = { signal: this.listeners.signal };
3577
+ canvas.addEventListener("pointerdown", (event) => {
3578
+ this.pointer = event.button === 0 && (event.ctrlKey || event.metaKey) ? event : void 0;
3579
+ }, options);
3580
+ canvas.addEventListener("pointermove", (event) => {
3581
+ if (this.pointer?.pointerId === event.pointerId && Math.hypot(event.clientX - this.pointer.clientX, event.clientY - this.pointer.clientY) > 5) {
3582
+ this.pointer = void 0;
3583
+ }
3584
+ }, options);
3585
+ canvas.addEventListener("pointerup", (event) => {
3586
+ const start = this.pointer;
3587
+ this.pointer = void 0;
3588
+ if (start?.pointerId === event.pointerId && Math.hypot(event.clientX - start.clientX, event.clientY - start.clientY) <= 5) {
3589
+ this.onClick(event);
3590
+ }
3591
+ }, options);
3592
+ canvas.addEventListener("pointercancel", () => {
3593
+ this.pointer = void 0;
3594
+ }, options);
3595
+ canvas.addEventListener("pointerleave", () => {
3596
+ this.pointer = void 0;
3597
+ }, options);
3598
+ canvas.addEventListener("contextmenu", (event) => {
3599
+ if (this.pointer?.ctrlKey) event.preventDefault();
3600
+ }, options);
3601
+ }
3602
+ dispose() {
3603
+ this.listeners.abort();
3604
+ this.pointer = void 0;
3605
+ this.selectedObject = void 0;
3606
+ this.objectInfoContainer.remove();
3607
+ this.objectControlsContainer.remove();
3570
3608
  }
3571
3609
  findMeshChildren(from, out = []) {
3610
+ if (!from.visible) return out;
3572
3611
  for (let c of from.children) {
3573
- if (isMesh(c)) {
3612
+ if (c.visible && isMesh(c)) {
3574
3613
  out.push(c);
3575
3614
  }
3576
3615
  this.findMeshChildren(c, out);
@@ -3578,50 +3617,43 @@ var SceneInspector = class {
3578
3617
  return out;
3579
3618
  }
3580
3619
  onClick(event) {
3581
- if (event.target?.nodeName !== "CANVAS") return;
3582
- if (!event.ctrlKey) return;
3583
- setTimeout(() => {
3584
- this.mouse.x = event.clientX / window.innerWidth * 2 - 1;
3585
- this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
3586
- this.raycaster.setFromCamera(this.mouse, this.renderer.camera);
3587
- let start = Date.now();
3588
- let targets = this.findMeshChildren(this.renderer.scene);
3589
- const intersects = this.raycaster.intersectObjects(targets, false);
3590
- console.debug("Raycast took " + (Date.now() - start) + "ms for " + targets.length + " possible targets with " + intersects.length + " results");
3591
- if (intersects.length > 0) {
3592
- this.handleRaycasterObjects(intersects);
3593
- }
3594
- });
3620
+ const canvas = this.renderer.renderer.domElement;
3621
+ if (event.target !== canvas || event.button !== 0 || !(event.ctrlKey || event.metaKey)) return;
3622
+ const rect = canvas.getBoundingClientRect();
3623
+ const x = event.clientX - rect.left, y = event.clientY - rect.top;
3624
+ if (rect.width <= 0 || rect.height <= 0 || x < 0 || y < 0 || x >= rect.width || y >= rect.height) return;
3625
+ this.mouse.set(x / rect.width * 2 - 1, 1 - y / rect.height * 2);
3626
+ this.renderer.camera.updateWorldMatrix(true, false);
3627
+ this.renderer.scene.updateMatrixWorld(true);
3628
+ this.raycaster.setFromCamera(this.mouse, this.renderer.camera);
3629
+ this.handleRaycasterObjects(this.raycaster.intersectObjects(this.findMeshChildren(this.renderer.scene), false));
3595
3630
  }
3596
3631
  appendTo(el) {
3597
3632
  el.append(this.objectInfoContainer);
3598
3633
  el.append(this.objectControlsContainer);
3599
3634
  }
3600
3635
  handleRaycasterObjects(intersections) {
3601
- console.debug(intersections);
3602
- const firstIntersection = intersections[0];
3603
- if (firstIntersection && firstIntersection.object) {
3604
- const firstObject = firstIntersection.object;
3605
- let targetIntersection = void 0;
3606
- let targetObject = void 0;
3607
- const firstSceneObjectIntersection = intersections.find((i) => i.object && i.object.parent && isSceneObject(i.object.parent));
3608
- if (firstSceneObjectIntersection && firstSceneObjectIntersection.object) {
3609
- targetIntersection = firstSceneObjectIntersection;
3610
- targetObject = firstSceneObjectIntersection.object;
3611
- } else {
3612
- targetIntersection = firstIntersection;
3613
- targetObject = firstObject;
3636
+ const hit = intersections[0];
3637
+ if (!hit) return;
3638
+ const instance = this.getIntersectionInstance(hit);
3639
+ this.selectObject(instance?.instanceable ?? hit.object, hit, instance);
3640
+ }
3641
+ getIntersectionInstance(hit) {
3642
+ if (hit?.instanceId === void 0) return void 0;
3643
+ for (let parent = hit.object.parent; parent; parent = parent.parent) {
3644
+ if (isSceneObject(parent)) {
3645
+ const instance = parent.getInstanceReference(hit.object, hit.instanceId);
3646
+ if (instance) return instance;
3614
3647
  }
3615
- if (!targetIntersection || !targetObject) return;
3616
- this.selectObject(targetObject, targetIntersection);
3617
3648
  }
3649
+ return void 0;
3618
3650
  }
3619
- selectObject(targetObject, targetIntersection) {
3651
+ selectObject(targetObject, targetIntersection, instance = this.getIntersectionInstance(targetIntersection)) {
3620
3652
  this.selectedObject = targetObject;
3621
3653
  console.log(p7, "selected", targetObject);
3622
- this.redraw(targetObject, targetIntersection);
3654
+ this.redraw(targetObject, targetIntersection, instance);
3623
3655
  }
3624
- redraw(targetObject, targetIntersection) {
3656
+ redraw(targetObject, targetIntersection, instance) {
3625
3657
  this.objectInfoContainer.innerHTML = help;
3626
3658
  if (targetIntersection) {
3627
3659
  this.addInfoLine("Distance", "D", targetIntersection.distance);
@@ -3634,8 +3666,8 @@ var SceneInspector = class {
3634
3666
  if (isSceneObject(targetObject.parent)) {
3635
3667
  }
3636
3668
  }
3637
- if ("atlas" in targetObject) {
3638
- const atlas = targetObject["atlas"];
3669
+ const atlas = "atlas" in targetObject ? targetObject["atlas"] : void 0;
3670
+ if (atlas?.image.canvas) {
3639
3671
  const img = atlas.image.canvas;
3640
3672
  img.style.width = "100px";
3641
3673
  img.style.height = "100px";
@@ -3644,28 +3676,31 @@ var SceneInspector = class {
3644
3676
  this.objectInfoContainer.prepend(img);
3645
3677
  }
3646
3678
  this.objectControlsContainer.innerHTML = "";
3647
- this.addControls(targetObject, targetIntersection);
3679
+ this.addControls(targetObject, targetIntersection, instance);
3648
3680
  }
3649
- addControls(object, intersection) {
3681
+ addControls(object, intersection, instance) {
3650
3682
  const container = document.createElement("div");
3651
3683
  container.append(this.separator("Select Parent/Child"));
3652
3684
  if (object.parent) {
3653
3685
  container.append(this.buttonControl("Select Parent " + object.parent.constructor.name + " " + object.parent.name, "P", () => {
3654
- this.selectObject(object.parent, intersection);
3686
+ this.selectObject(object.parent, intersection, instance);
3655
3687
  }));
3656
3688
  }
3657
3689
  if (object.children.length > 0) {
3658
3690
  let i = 1;
3659
3691
  for (let child of object.children) {
3660
3692
  container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
3661
- this.selectObject(child, intersection);
3693
+ this.selectObject(child, intersection, instance);
3662
3694
  }));
3663
3695
  }
3664
3696
  }
3665
3697
  container.append(this.separator());
3666
- container.append(this.toggleControl("Visibility", "V", object.visible, (v) => object.visible = v));
3698
+ const selectedInstance = instance && (object === instance.instanceable || object === intersection?.object) ? instance : void 0;
3699
+ if (!selectedInstance) {
3700
+ container.append(this.toggleControl("Visibility", "V", object.visible, (v) => object.visible = v));
3701
+ }
3667
3702
  container.append(this.separator("Mesh"));
3668
- this.addObjectControls(object, intersection, container);
3703
+ this.addObjectControls(object, intersection, container, selectedInstance);
3669
3704
  container.append(this.separator());
3670
3705
  this.objectControlsContainer.append(container);
3671
3706
  }
@@ -3677,78 +3712,26 @@ var SceneInspector = class {
3677
3712
  this.objectInfoContainer.append(document.createElement("br"));
3678
3713
  return el;
3679
3714
  }
3680
- addObjectControls(target, intersection, container) {
3715
+ addObjectControls(target, intersection, container, instance) {
3681
3716
  const posRange = 16 * 16;
3682
3717
  const rotRange = 360;
3683
3718
  const scaleRange = 4;
3684
- if (typeof intersection !== "undefined" && typeof intersection.instanceId !== "undefined" && isSceneObject(target) && target.isInstanced) {
3685
- const scObj = target;
3686
- container.append(this.separator("Position"));
3687
- let pos = scObj.getPositionAt(intersection.instanceId);
3688
- container.append(this.rangeControl("X Position", "X", -posRange + pos.x, posRange + pos.x, pos.x, 1, (v) => {
3689
- pos = scObj.getPositionAt(intersection.instanceId);
3690
- pos.x = v;
3691
- scObj.setPositionAt(intersection.instanceId, pos);
3692
- }));
3693
- container.append(this.rangeControl("Y Position", "Y", -posRange + pos.y, posRange + pos.y, pos.y, 1, (v) => {
3694
- pos = scObj.getPositionAt(intersection.instanceId);
3695
- pos.y = v;
3696
- scObj.setPositionAt(intersection.instanceId, pos);
3697
- }));
3698
- container.append(this.rangeControl("Z Position", "Z", -posRange + pos.z, posRange + pos.z, pos.z, 1, (v) => {
3699
- pos = scObj.getPositionAt(intersection.instanceId);
3700
- pos.z = v;
3701
- scObj.setPositionAt(intersection.instanceId, pos);
3702
- }));
3703
- container.append(this.separator("Rotation"));
3704
- let rot = scObj.getRotationAt(intersection.instanceId);
3705
- container.append(this.rangeControl("X Rotation", "X", 0, rotRange, Math.round(toDegrees(rot.x)), 1, (v) => {
3706
- rot = scObj.getRotationAt(intersection.instanceId);
3707
- rot.x = toRadians(v);
3708
- scObj.setRotationAt(intersection.instanceId, rot);
3709
- }));
3710
- container.append(this.rangeControl("Y Rotation", "Y", 0, rotRange, Math.round(toDegrees(rot.y)), 1, (v) => {
3711
- rot = scObj.getRotationAt(intersection.instanceId);
3712
- rot.y = toRadians(v);
3713
- scObj.setRotationAt(intersection.instanceId, rot);
3714
- }));
3715
- container.append(this.rangeControl("Z Rotation", "Z", 0, rotRange, Math.round(toDegrees(rot.z)), 1, (v) => {
3716
- rot = scObj.getRotationAt(intersection.instanceId);
3717
- rot.z = toRadians(v);
3718
- scObj.setRotationAt(intersection.instanceId, rot);
3719
- }));
3720
- container.append(this.separator("Scale"));
3721
- let scl = scObj.getScale();
3722
- container.append(this.rangeControl("X Scale", "X", 0, scaleRange, scl.x, 0.1, (v) => {
3723
- scl = scObj.getScaleAt(intersection.instanceId);
3724
- scl.x = v;
3725
- scObj.setScaleAt(intersection.instanceId, scl);
3726
- }));
3727
- container.append(this.rangeControl("Y Scale", "Y", 0, scaleRange, scl.y, 0.1, (v) => {
3728
- scl = scObj.getScaleAt(intersection.instanceId);
3729
- scl.y = v;
3730
- scObj.setScaleAt(intersection.instanceId, scl);
3731
- }));
3732
- container.append(this.rangeControl("Z Scale", "Z", 0, scaleRange, scl.z, 0.1, (v) => {
3733
- scl = scObj.getScaleAt(intersection.instanceId);
3734
- scl.z = v;
3735
- scObj.setScaleAt(intersection.instanceId, scl);
3736
- }));
3737
- } else if (isTransformable(target)) {
3738
- const parent = target;
3719
+ const transform = instance ?? (isTransformable(target) ? target : void 0);
3720
+ if (transform) {
3721
+ const parent = transform;
3739
3722
  container.append(this.separator("Position"));
3740
3723
  let pos = parent.getPosition();
3741
- container.append(this.rangeControl("X Position", "X", -posRange, posRange, pos.x, 1, (v) => {
3724
+ container.append(this.rangeControl("X Position", "X", pos.x - posRange, pos.x + posRange, pos.x, 1, (v) => {
3742
3725
  pos = parent.getPosition();
3743
3726
  pos.x = v;
3744
3727
  parent.setPosition(pos);
3745
3728
  }));
3746
- container.append(this.rangeControl("Y Position", "Y", -posRange, posRange, pos.y, 1, (v) => {
3729
+ container.append(this.rangeControl("Y Position", "Y", pos.y - posRange, pos.y + posRange, pos.y, 1, (v) => {
3747
3730
  pos = parent.getPosition();
3748
3731
  pos.y = v;
3749
3732
  parent.setPosition(pos);
3750
3733
  }));
3751
- container.append(this.rangeControl("Z Position", "Z", -posRange, posRange, pos.z, 1, (v) => {
3734
+ container.append(this.rangeControl("Z Position", "Z", pos.z - posRange, pos.z + posRange, pos.z, 1, (v) => {
3752
3735
  pos = parent.getPosition();
3753
3736
  pos.z = v;
3754
3737
  parent.setPosition(pos);
@@ -3837,6 +3820,7 @@ var SceneInspector = class {
3837
3820
  toggle.checked = val;
3838
3821
  toggle.addEventListener("change", (e) => {
3839
3822
  change(toggle.checked);
3823
+ this.renderer.scene.dirty = true;
3840
3824
  });
3841
3825
  label.append(toggle);
3842
3826
  label.append(document.createElement("br"));
@@ -3849,7 +3833,6 @@ var SceneInspector = class {
3849
3833
  label.setAttribute("title", name);
3850
3834
  labelText.style.width = "18%";
3851
3835
  labelText.style.display = "inline-block";
3852
- labelText.innerText = `${id} (${val})`;
3853
3836
  const range = document.createElement("input");
3854
3837
  range.setAttribute("type", "range");
3855
3838
  range.style.width = "80%";
@@ -3857,8 +3840,10 @@ var SceneInspector = class {
3857
3840
  range.min = `${min}`;
3858
3841
  range.step = `${step}`;
3859
3842
  range.value = `${val}`;
3843
+ labelText.innerText = `${id} (${range.value})`;
3860
3844
  const onChange = () => {
3861
3845
  change(parseFloat(range.value));
3846
+ this.renderer.scene.dirty = true;
3862
3847
  labelText.innerText = `${id} (${range.value})`;
3863
3848
  };
3864
3849
  range.addEventListener("input", onChange);
@@ -4018,6 +4003,93 @@ var ModelCulling = class {
4018
4003
  }
4019
4004
  };
4020
4005
 
4006
+ // src/model/block/BlockStateResolver.ts
4007
+ var import_three17 = require("three");
4008
+ function matchesCondition(condition, state) {
4009
+ return Object.entries(condition).every(([key, value]) => {
4010
+ if (Array.isArray(value)) {
4011
+ if (key === "OR") return value.some((child) => matchesCondition(child, state));
4012
+ if (key === "AND") return value.every((child) => matchesCondition(child, state));
4013
+ return false;
4014
+ }
4015
+ return typeof value === "string" && state[key] !== void 0 && value.split("|").includes(`${state[key]}`);
4016
+ });
4017
+ }
4018
+ var BlockStateResolver = class {
4019
+ static async defaults(blockState) {
4020
+ const defaults = blockState.key ? await BlockStates.getDefaultState(blockState.key) : void 0;
4021
+ const state = {};
4022
+ if (defaults && Object.keys(defaults).length > 0) {
4023
+ for (const key in defaults) state[key] = defaults[key].default;
4024
+ } else if (blockState.variants) {
4025
+ const variant = Object.keys(blockState.variants)[0];
4026
+ if (variant) {
4027
+ for (const property of variant.split(",")) {
4028
+ const [key, value] = property.split("=");
4029
+ state[key] = value;
4030
+ }
4031
+ }
4032
+ } else if (blockState.multipart) {
4033
+ const condition = blockState.multipart.map((part) => part.when).find((when) => when !== void 0 && Object.values(when).every((value) => typeof value === "string"));
4034
+ for (const [key, value] of Object.entries(condition ?? {})) state[key] = value.split("|")[0];
4035
+ }
4036
+ return state;
4037
+ }
4038
+ static select(blockState, state, choose = this.choose) {
4039
+ return this.matching(blockState, state).map(choose);
4040
+ }
4041
+ static matching(blockState, state) {
4042
+ const out = [];
4043
+ if (blockState.variants) {
4044
+ if (Object.keys(blockState.variants).length === 1 && "" in blockState.variants) {
4045
+ out.push(blockState.variants[""]);
4046
+ } else {
4047
+ for (const variantKey in blockState.variants) {
4048
+ const matches = variantKey.split(",").every((property) => {
4049
+ const [key, value] = property.split("=");
4050
+ return `${state[key]}` === `${value}`;
4051
+ });
4052
+ if (matches) out.push(blockState.variants[variantKey]);
4053
+ }
4054
+ }
4055
+ } else if (blockState.multipart) {
4056
+ for (const part of blockState.multipart) {
4057
+ if (part.apply && (!part.when || matchesCondition(part.when, state))) {
4058
+ out.push(part.apply);
4059
+ }
4060
+ }
4061
+ }
4062
+ return out;
4063
+ }
4064
+ static choose(variants) {
4065
+ if (!Array.isArray(variants)) return variants;
4066
+ if (!variants.length) throw new MineRenderError("Blockstate variant arrays must not be empty");
4067
+ const total = variants.reduce((sum, variant) => {
4068
+ const weight = variant.weight ?? 1;
4069
+ if (!Number.isInteger(weight) || weight < 1) {
4070
+ throw new MineRenderError(`Invalid blockstate variant weight: ${weight}`);
4071
+ }
4072
+ return sum + weight;
4073
+ }, 0);
4074
+ let choice = Math.random() * total;
4075
+ for (const variant of variants) {
4076
+ choice -= variant.weight ?? 1;
4077
+ if (choice < 0) return variant;
4078
+ }
4079
+ return variants[variants.length - 1];
4080
+ }
4081
+ static rotation(variant) {
4082
+ const rotation = new import_three17.Euler();
4083
+ if (typeof variant.x !== "undefined") {
4084
+ rotation.x = toRadians(clampRotationDegrees(variant.x));
4085
+ }
4086
+ if (typeof variant.y !== "undefined") {
4087
+ rotation.y = toRadians(clampRotationDegrees(typeof variant.x !== "undefined" ? variant.y : 360 - variant.y));
4088
+ }
4089
+ return rotation;
4090
+ }
4091
+ };
4092
+
4021
4093
  // src/model/block/blockTints.json
4022
4094
  var blockTints_default = {
4023
4095
  "minecraft:large_fern": { source: "grass" },
@@ -4104,10 +4176,10 @@ var BlockTints = class {
4104
4176
  };
4105
4177
 
4106
4178
  // src/model/scene/ModelObject.ts
4107
- var import_three18 = require("three");
4179
+ var import_three19 = require("three");
4108
4180
 
4109
4181
  // src/three/BufferGeometryUtils.js
4110
- var import_three17 = require("three");
4182
+ var import_three18 = require("three");
4111
4183
  function computeTangents(geometry) {
4112
4184
  geometry.computeTangents();
4113
4185
  console.warn("THREE.BufferGeometryUtils: .computeTangents() has been removed. Use BufferGeometry.computeTangents() instead.");
@@ -4119,7 +4191,7 @@ function mergeBufferGeometries(geometries, useGroups = false) {
4119
4191
  const attributes = {};
4120
4192
  const morphAttributes = {};
4121
4193
  const morphTargetsRelative = geometries[0].morphTargetsRelative;
4122
- const mergedGeometry = new import_three17.BufferGeometry();
4194
+ const mergedGeometry = new import_three18.BufferGeometry();
4123
4195
  let offset = 0;
4124
4196
  for (let i = 0; i < geometries.length; ++i) {
4125
4197
  const geometry = geometries[i];
@@ -4243,7 +4315,7 @@ function mergeBufferAttributes(attributes) {
4243
4315
  array.set(attributes[i].array, offset);
4244
4316
  offset += attributes[i].array.length;
4245
4317
  }
4246
- return new import_three17.BufferAttribute(array, itemSize, normalized);
4318
+ return new import_three18.BufferAttribute(array, itemSize, normalized);
4247
4319
  }
4248
4320
  function interleaveAttributes(attributes) {
4249
4321
  let TypedArray;
@@ -4259,7 +4331,7 @@ function interleaveAttributes(attributes) {
4259
4331
  arrayLength += attribute.array.length;
4260
4332
  stride += attribute.itemSize;
4261
4333
  }
4262
- const interleavedBuffer = new import_three17.InterleavedBuffer(new TypedArray(arrayLength), stride);
4334
+ const interleavedBuffer = new import_three18.InterleavedBuffer(new TypedArray(arrayLength), stride);
4263
4335
  let offset = 0;
4264
4336
  const res = [];
4265
4337
  const getters = ["getX", "getY", "getZ", "getW"];
@@ -4268,7 +4340,7 @@ function interleaveAttributes(attributes) {
4268
4340
  const attribute = attributes[j];
4269
4341
  const itemSize = attribute.itemSize;
4270
4342
  const count = attribute.count;
4271
- const iba = new import_three17.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4343
+ const iba = new import_three18.InterleavedBufferAttribute(interleavedBuffer, itemSize, offset, attribute.normalized);
4272
4344
  res.push(iba);
4273
4345
  offset += itemSize;
4274
4346
  for (let c = 0; c < count; c++) {
@@ -4352,13 +4424,13 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4352
4424
  const name = attributeNames[i];
4353
4425
  const oldAttribute = geometry.getAttribute(name);
4354
4426
  const buffer = new oldAttribute.array.constructor(attrArrays[name]);
4355
- const attribute = new import_three17.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4427
+ const attribute = new import_three18.BufferAttribute(buffer, oldAttribute.itemSize, oldAttribute.normalized);
4356
4428
  result.setAttribute(name, attribute);
4357
4429
  if (name in morphAttrsArrays) {
4358
4430
  for (let j = 0; j < morphAttrsArrays[name].length; j++) {
4359
4431
  const oldMorphAttribute = geometry.morphAttributes[name][j];
4360
4432
  const buffer2 = new oldMorphAttribute.array.constructor(morphAttrsArrays[name][j]);
4361
- const morphAttribute = new import_three17.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4433
+ const morphAttribute = new import_three18.BufferAttribute(buffer2, oldMorphAttribute.itemSize, oldMorphAttribute.normalized);
4362
4434
  result.morphAttributes[name][j] = morphAttribute;
4363
4435
  }
4364
4436
  }
@@ -4367,11 +4439,11 @@ function mergeVertices(geometry, tolerance = 1e-4) {
4367
4439
  return result;
4368
4440
  }
4369
4441
  function toTrianglesDrawMode(geometry, drawMode) {
4370
- if (drawMode === import_three17.TrianglesDrawMode) {
4442
+ if (drawMode === import_three18.TrianglesDrawMode) {
4371
4443
  console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.");
4372
4444
  return geometry;
4373
4445
  }
4374
- if (drawMode === import_three17.TriangleFanDrawMode || drawMode === import_three17.TriangleStripDrawMode) {
4446
+ if (drawMode === import_three18.TriangleFanDrawMode || drawMode === import_three18.TriangleStripDrawMode) {
4375
4447
  let index = geometry.getIndex();
4376
4448
  if (index === null) {
4377
4449
  const indices = [];
@@ -4389,7 +4461,7 @@ function toTrianglesDrawMode(geometry, drawMode) {
4389
4461
  }
4390
4462
  const numberOfTriangles = index.count - 2;
4391
4463
  const newIndices = [];
4392
- if (drawMode === import_three17.TriangleFanDrawMode) {
4464
+ if (drawMode === import_three18.TriangleFanDrawMode) {
4393
4465
  for (let i = 1; i <= numberOfTriangles; i++) {
4394
4466
  newIndices.push(index.getX(0));
4395
4467
  newIndices.push(index.getX(i));
@@ -4425,15 +4497,15 @@ function computeMorphedAttributes(object) {
4425
4497
  console.error("THREE.BufferGeometryUtils: Geometry is not of type BufferGeometry.");
4426
4498
  return null;
4427
4499
  }
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();
4500
+ const _vA = new import_three18.Vector3();
4501
+ const _vB = new import_three18.Vector3();
4502
+ const _vC = new import_three18.Vector3();
4503
+ const _tempA = new import_three18.Vector3();
4504
+ const _tempB = new import_three18.Vector3();
4505
+ const _tempC = new import_three18.Vector3();
4506
+ const _morphA = new import_three18.Vector3();
4507
+ const _morphB = new import_three18.Vector3();
4508
+ const _morphC = new import_three18.Vector3();
4437
4509
  function _calculateMorphedAttributeData(object2, material2, attribute, morphAttribute, morphTargetsRelative2, a2, b2, c2, modifiedAttributeArray) {
4438
4510
  _vA.fromBufferAttribute(attribute, a2);
4439
4511
  _vB.fromBufferAttribute(attribute, b2);
@@ -4628,8 +4700,8 @@ function computeMorphedAttributes(object) {
4628
4700
  }
4629
4701
  }
4630
4702
  }
4631
- const morphedPositionAttribute = new import_three17.Float32BufferAttribute(modifiedPosition, 3);
4632
- const morphedNormalAttribute = new import_three17.Float32BufferAttribute(modifiedNormal, 3);
4703
+ const morphedPositionAttribute = new import_three18.Float32BufferAttribute(modifiedPosition, 3);
4704
+ const morphedNormalAttribute = new import_three18.Float32BufferAttribute(modifiedNormal, 3);
4633
4705
  return {
4634
4706
  positionAttribute,
4635
4707
  normalAttribute,
@@ -4685,7 +4757,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4685
4757
  elGeo.clearGroups();
4686
4758
  }
4687
4759
  if (this.options.uvLockRotation) {
4688
- UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three18.Euler(...this.options.uvLockRotation));
4760
+ UVMapper.lockUvs(elGeo, el.faces, this.atlas, new import_three19.Euler(...this.options.uvLockRotation));
4689
4761
  }
4690
4762
  if (this.options.tints) {
4691
4763
  const colors = new Float32Array(elGeo.getAttribute("position").count * 3).fill(1);
@@ -4694,19 +4766,19 @@ var _ModelObject = class _ModelObject extends SceneObject {
4694
4766
  if (tintIndex === void 0 || tintIndex < 0) continue;
4695
4767
  const tint = this.options.tints[tintIndex];
4696
4768
  if (tint === void 0) continue;
4697
- const color = new import_three18.Color(tint);
4769
+ const color = new import_three19.Color(tint);
4698
4770
  for (let vertex = 0; vertex < 4; vertex++) {
4699
4771
  color.toArray(colors, (faceIndex * 4 + vertex) * 3);
4700
4772
  }
4701
4773
  }
4702
- elGeo.setAttribute("color", new import_three18.BufferAttribute(colors, 3));
4774
+ elGeo.setAttribute("color", new import_three19.BufferAttribute(colors, 3));
4703
4775
  }
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]));
4776
+ 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));
4777
+ elGeo.applyMatrix4(new import_three19.Matrix4().makeTranslation(el.from[0], el.from[1], el.from[2]));
4706
4778
  if (el.rotation) {
4707
4779
  applyElementRotation(el.rotation, elGeo);
4708
4780
  }
4709
- elGeo.applyMatrix4(new import_three18.Matrix4().makeTranslation(-8, -8, -8));
4781
+ elGeo.applyMatrix4(new import_three19.Matrix4().makeTranslation(-8, -8, -8));
4710
4782
  if (displayTransform) DisplayTransforms.apply(elGeo, displayTransform);
4711
4783
  if (this.options.mergeMeshes) {
4712
4784
  allGeos.push(elGeo);
@@ -4728,7 +4800,7 @@ var _ModelObject = class _ModelObject extends SceneObject {
4728
4800
  if (allGeos.length > 0) {
4729
4801
  combinedGeo = mergeBufferGeometries(allGeos);
4730
4802
  } else {
4731
- combinedGeo = new import_three18.BoxGeometry(16, 16, 16);
4803
+ combinedGeo = new import_three19.BoxGeometry(16, 16, 16);
4732
4804
  if (displayTransform) DisplayTransforms.apply(combinedGeo, displayTransform);
4733
4805
  }
4734
4806
  combinedGeo.computeBoundingBox();
@@ -4783,18 +4855,7 @@ function isModelObject(obj) {
4783
4855
 
4784
4856
  // src/model/block/scene/BlockObject.ts
4785
4857
  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
- }
4858
+ var import_three20 = require("three");
4798
4859
  var _BlockObject = class _BlockObject extends SceneObject {
4799
4860
  constructor(blockState, options) {
4800
4861
  super(options);
@@ -4813,22 +4874,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4813
4874
  }
4814
4875
  async init() {
4815
4876
  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
- }
4877
+ this._setState(await BlockStateResolver.defaults(this.blockState));
4832
4878
  }
4833
4879
  if (this.options.initialState !== void 0) this._setState(this.options.initialState);
4834
4880
  await this.recreateModels();
@@ -4866,12 +4912,12 @@ var _BlockObject = class _BlockObject extends SceneObject {
4866
4912
  getModelCullMask(model, worldMask) {
4867
4913
  const object = isInstanceReference(model) ? model.instanceable : model;
4868
4914
  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();
4915
+ const position = new import_three20.Vector3();
4916
+ const rotation = new import_three20.Quaternion();
4917
+ const scale = new import_three20.Vector3();
4872
4918
  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));
4919
+ if (position.distanceToSquared(this.position) > 1e-10 || scale.distanceToSquared(new import_three20.Vector3(1, 1, 1)) > 1e-10) return 0;
4920
+ return ModelCulling.toLocalMask(worldMask, new import_three20.Euler().setFromQuaternion(rotation));
4875
4921
  }
4876
4922
  async setCullMask(worldMask) {
4877
4923
  worldMask &= 63;
@@ -4919,39 +4965,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4919
4965
  return ref;
4920
4966
  }
4921
4967
  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;
4968
+ return BlockStateResolver.select(this.blockState, state, (variants) => this.getSingleVariant(variants));
4955
4969
  }
4956
4970
  async recreateModels() {
4957
4971
  this.clearModels();
@@ -4962,21 +4976,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4962
4976
  }
4963
4977
  }
4964
4978
  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];
4979
+ return BlockStateResolver.choose(variants);
4980
4980
  }
4981
4981
  // @deprecated
4982
4982
  async createAVariant(variants, instanceInfo) {
@@ -4984,13 +4984,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
4984
4984
  await this.createVariant(variant);
4985
4985
  }
4986
4986
  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
- }
4987
+ const rotation = BlockStateResolver.rotation(variant);
4994
4988
  const modelKey = AssetKey.parse("models", variant.model);
4995
4989
  modelKey.root = this.blockState.key?.root;
4996
4990
  const model = await Models.getMerged(modelKey);
@@ -5075,7 +5069,7 @@ var _BlockObject = class _BlockObject extends SceneObject {
5075
5069
  setPositionRotationScaleAt(index, position, rotation, scale) {
5076
5070
  super.setPositionRotationScaleAt(index, position, rotation, scale);
5077
5071
  }
5078
- getMatrixAt(index, matrix = new import_three19.Matrix4()) {
5072
+ getMatrixAt(index, matrix = new import_three20.Matrix4()) {
5079
5073
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
5080
5074
  const child = this._models[0];
5081
5075
  if (child && isModelObject(child)) {
@@ -6931,7 +6925,7 @@ var StructureParser = class {
6931
6925
  };
6932
6926
 
6933
6927
  // src/renderer/MineRenderScene.ts
6934
- var import_three21 = require("three");
6928
+ var import_three22 = require("three");
6935
6929
  var import_ts_deepmerge8 = __toESM(require_dist());
6936
6930
 
6937
6931
  // src/skin/SkinPart.ts
@@ -7656,7 +7650,7 @@ var slimSkinGeometries = {
7656
7650
  var import_ts_deepmerge7 = __toESM(require_dist());
7657
7651
 
7658
7652
  // src/skin/SkinTextures.ts
7659
- var import_three20 = require("three");
7653
+ var import_three21 = require("three");
7660
7654
 
7661
7655
  // src/skin/SkinImage.ts
7662
7656
  var SkinImage = class {
@@ -7733,11 +7727,11 @@ var SkinTextures = class {
7733
7727
  });
7734
7728
  }
7735
7729
  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;
7730
+ const texture = Textures.initTextureProps(new import_three21.DataTexture(pixels, width, height));
7731
+ texture.colorSpace = import_three21.SRGBColorSpace;
7738
7732
  texture.flipY = true;
7739
7733
  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 });
7734
+ return new import_three21.MeshBasicMaterial({ map: texture, transparent, side: transparent ? import_three21.DoubleSide : import_three21.FrontSide, alphaTest: transparent ? 0.1 : 0 });
7741
7735
  }
7742
7736
  static isSlim(image, scale) {
7743
7737
  for (const [x, y] of [[46, 52], [54, 20]]) {
@@ -7964,7 +7958,7 @@ var SkinObject = class extends SceneObject {
7964
7958
  };
7965
7959
 
7966
7960
  // src/renderer/MineRenderScene.ts
7967
- var _MineRenderScene = class _MineRenderScene extends import_three21.Scene {
7961
+ var _MineRenderScene = class _MineRenderScene extends import_three22.Scene {
7968
7962
  constructor(options) {
7969
7963
  super();
7970
7964
  this.isMineRenderScene = true;
@@ -8088,17 +8082,17 @@ function isMineRenderScene(obj) {
8088
8082
  }
8089
8083
 
8090
8084
  // src/renderer/Renderer.ts
8091
- var import_three23 = require("three");
8085
+ var import_three24 = require("three");
8092
8086
  var import_ts_deepmerge9 = __toESM(require_dist());
8093
8087
  var import_stats = __toESM(require("stats.js"));
8094
8088
  var import_postprocessing = require("postprocessing");
8095
8089
 
8096
8090
  // src/three/OrbitControls.js
8097
- var import_three22 = require("three");
8091
+ var import_three23 = require("three");
8098
8092
  var _changeEvent = { type: "change" };
8099
8093
  var _startEvent = { type: "start" };
8100
8094
  var _endEvent = { type: "end" };
8101
- var OrbitControls = class extends import_three22.EventDispatcher {
8095
+ var OrbitControls = class extends import_three23.EventDispatcher {
8102
8096
  constructor(object, domElement) {
8103
8097
  super();
8104
8098
  if (domElement === void 0) console.warn('THREE.OrbitControls: The second parameter "domElement" is now mandatory.');
@@ -8107,7 +8101,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8107
8101
  this.domElement = domElement;
8108
8102
  this.domElement.style.touchAction = "none";
8109
8103
  this.enabled = true;
8110
- this.target = new import_three22.Vector3();
8104
+ this.target = new import_three23.Vector3();
8111
8105
  this.minDistance = 0;
8112
8106
  this.maxDistance = Infinity;
8113
8107
  this.minZoom = 0;
@@ -8129,8 +8123,8 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8129
8123
  this.autoRotate = false;
8130
8124
  this.autoRotateSpeed = 2;
8131
8125
  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 };
8126
+ this.mouseButtons = { LEFT: import_three23.MOUSE.ROTATE, MIDDLE: import_three23.MOUSE.DOLLY, RIGHT: import_three23.MOUSE.PAN };
8127
+ this.touches = { ONE: import_three23.TOUCH.ROTATE, TWO: import_three23.TOUCH.DOLLY_PAN };
8134
8128
  this.target0 = this.target.clone();
8135
8129
  this.position0 = this.object.position.clone();
8136
8130
  this.zoom0 = this.object.zoom;
@@ -8159,11 +8153,11 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8159
8153
  state = STATE.NONE;
8160
8154
  };
8161
8155
  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));
8156
+ const offset = new import_three23.Vector3();
8157
+ const quat = new import_three23.Quaternion().setFromUnitVectors(object.up, new import_three23.Vector3(0, 1, 0));
8164
8158
  const quatInverse = quat.clone().invert();
8165
- const lastPosition = new import_three22.Vector3();
8166
- const lastQuaternion = new import_three22.Quaternion();
8159
+ const lastPosition = new import_three23.Vector3();
8160
+ const lastQuaternion = new import_three23.Quaternion();
8167
8161
  const twoPI = 2 * Math.PI;
8168
8162
  return function update() {
8169
8163
  const position = scope.object.position;
@@ -8249,20 +8243,20 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8249
8243
  };
8250
8244
  let state = STATE.NONE;
8251
8245
  const EPS = 1e-6;
8252
- const spherical = new import_three22.Spherical();
8253
- const sphericalDelta = new import_three22.Spherical();
8246
+ const spherical = new import_three23.Spherical();
8247
+ const sphericalDelta = new import_three23.Spherical();
8254
8248
  let scale = 1;
8255
- const panOffset = new import_three22.Vector3();
8249
+ const panOffset = new import_three23.Vector3();
8256
8250
  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();
8251
+ const rotateStart = new import_three23.Vector2();
8252
+ const rotateEnd = new import_three23.Vector2();
8253
+ const rotateDelta = new import_three23.Vector2();
8254
+ const panStart = new import_three23.Vector2();
8255
+ const panEnd = new import_three23.Vector2();
8256
+ const panDelta = new import_three23.Vector2();
8257
+ const dollyStart = new import_three23.Vector2();
8258
+ const dollyEnd = new import_three23.Vector2();
8259
+ const dollyDelta = new import_three23.Vector2();
8266
8260
  const pointers = [];
8267
8261
  const pointerPositions = {};
8268
8262
  function getAutoRotationAngle() {
@@ -8278,7 +8272,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8278
8272
  sphericalDelta.phi -= angle;
8279
8273
  }
8280
8274
  const panLeft = function() {
8281
- const v = new import_three22.Vector3();
8275
+ const v = new import_three23.Vector3();
8282
8276
  return function panLeft2(distance, objectMatrix) {
8283
8277
  v.setFromMatrixColumn(objectMatrix, 0);
8284
8278
  v.multiplyScalar(-distance);
@@ -8286,7 +8280,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8286
8280
  };
8287
8281
  }();
8288
8282
  const panUp = function() {
8289
- const v = new import_three22.Vector3();
8283
+ const v = new import_three23.Vector3();
8290
8284
  return function panUp2(distance, objectMatrix) {
8291
8285
  if (scope.screenSpacePanning === true) {
8292
8286
  v.setFromMatrixColumn(objectMatrix, 1);
@@ -8299,7 +8293,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8299
8293
  };
8300
8294
  }();
8301
8295
  const pan = function() {
8302
- const offset = new import_three22.Vector3();
8296
+ const offset = new import_three23.Vector3();
8303
8297
  return function pan2(deltaX, deltaY) {
8304
8298
  const element = scope.domElement;
8305
8299
  if (scope.object.isPerspectiveCamera) {
@@ -8548,12 +8542,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8548
8542
  mouseAction = -1;
8549
8543
  }
8550
8544
  switch (mouseAction) {
8551
- case import_three22.MOUSE.DOLLY:
8545
+ case import_three23.MOUSE.DOLLY:
8552
8546
  if (scope.enableZoom === false) return;
8553
8547
  handleMouseDownDolly(event);
8554
8548
  state = STATE.DOLLY;
8555
8549
  break;
8556
- case import_three22.MOUSE.ROTATE:
8550
+ case import_three23.MOUSE.ROTATE:
8557
8551
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
8558
8552
  if (scope.enablePan === false) return;
8559
8553
  handleMouseDownPan(event);
@@ -8564,7 +8558,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8564
8558
  state = STATE.ROTATE;
8565
8559
  }
8566
8560
  break;
8567
- case import_three22.MOUSE.PAN:
8561
+ case import_three23.MOUSE.PAN:
8568
8562
  if (event.ctrlKey || event.metaKey || event.shiftKey) {
8569
8563
  if (scope.enableRotate === false) return;
8570
8564
  handleMouseDownRotate(event);
@@ -8620,12 +8614,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8620
8614
  switch (pointers.length) {
8621
8615
  case 1:
8622
8616
  switch (scope.touches.ONE) {
8623
- case import_three22.TOUCH.ROTATE:
8617
+ case import_three23.TOUCH.ROTATE:
8624
8618
  if (scope.enableRotate === false) return;
8625
8619
  handleTouchStartRotate();
8626
8620
  state = STATE.TOUCH_ROTATE;
8627
8621
  break;
8628
- case import_three22.TOUCH.PAN:
8622
+ case import_three23.TOUCH.PAN:
8629
8623
  if (scope.enablePan === false) return;
8630
8624
  handleTouchStartPan();
8631
8625
  state = STATE.TOUCH_PAN;
@@ -8636,12 +8630,12 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8636
8630
  break;
8637
8631
  case 2:
8638
8632
  switch (scope.touches.TWO) {
8639
- case import_three22.TOUCH.DOLLY_PAN:
8633
+ case import_three23.TOUCH.DOLLY_PAN:
8640
8634
  if (scope.enableZoom === false && scope.enablePan === false) return;
8641
8635
  handleTouchStartDollyPan();
8642
8636
  state = STATE.TOUCH_DOLLY_PAN;
8643
8637
  break;
8644
- case import_three22.TOUCH.DOLLY_ROTATE:
8638
+ case import_three23.TOUCH.DOLLY_ROTATE:
8645
8639
  if (scope.enableZoom === false && scope.enableRotate === false) return;
8646
8640
  handleTouchStartDollyRotate();
8647
8641
  state = STATE.TOUCH_DOLLY_ROTATE;
@@ -8708,7 +8702,7 @@ var OrbitControls = class extends import_three22.EventDispatcher {
8708
8702
  function trackPointer(event) {
8709
8703
  let position = pointerPositions[event.pointerId];
8710
8704
  if (position === void 0) {
8711
- position = new import_three22.Vector2();
8705
+ position = new import_three23.Vector2();
8712
8706
  pointerPositions[event.pointerId] = position;
8713
8707
  }
8714
8708
  position.set(event.pageX, event.pageY);
@@ -8728,10 +8722,10 @@ var MapControls = class extends OrbitControls {
8728
8722
  constructor(object, domElement) {
8729
8723
  super(object, domElement);
8730
8724
  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;
8725
+ this.mouseButtons.LEFT = import_three23.MOUSE.PAN;
8726
+ this.mouseButtons.RIGHT = import_three23.MOUSE.ROTATE;
8727
+ this.touches.ONE = import_three23.TOUCH.PAN;
8728
+ this.touches.TWO = import_three23.TOUCH.DOLLY_ROTATE;
8735
8729
  }
8736
8730
  };
8737
8731
 
@@ -8768,14 +8762,14 @@ var _Renderer = class _Renderer {
8768
8762
  switch (this.options.camera.type) {
8769
8763
  case "perspective":
8770
8764
  default:
8771
- return new import_three23.PerspectiveCamera(
8765
+ return new import_three24.PerspectiveCamera(
8772
8766
  this.options.camera.perspective.fov,
8773
8767
  this.options.camera.perspective.aspect ?? this.viewWidth / this.viewHeight,
8774
8768
  this.options.camera.near,
8775
8769
  this.options.camera.far
8776
8770
  );
8777
8771
  case "orthographic":
8778
- return new import_three23.OrthographicCamera(
8772
+ return new import_three24.OrthographicCamera(
8779
8773
  this.options.camera.orthographic.left ?? this.viewWidth / -2,
8780
8774
  this.options.camera.orthographic.right ?? this.viewWidth / 2,
8781
8775
  this.options.camera.orthographic.top ?? this.viewHeight / 2,
@@ -8786,7 +8780,7 @@ var _Renderer = class _Renderer {
8786
8780
  }
8787
8781
  }
8788
8782
  createRenderer() {
8789
- const renderer = new import_three23.WebGLRenderer({
8783
+ const renderer = new import_three24.WebGLRenderer({
8790
8784
  antialias: this.options.render.antialias,
8791
8785
  alpha: true,
8792
8786
  powerPreference: "high-performance",
@@ -8794,8 +8788,8 @@ var _Renderer = class _Renderer {
8794
8788
  });
8795
8789
  renderer.setClearColor(0, 0);
8796
8790
  renderer.shadowMap.enabled = true;
8797
- renderer.shadowMap.type = import_three23.PCFSoftShadowMap;
8798
- renderer.outputColorSpace = import_three23.SRGBColorSpace;
8791
+ renderer.shadowMap.type = import_three24.PCFSoftShadowMap;
8792
+ renderer.outputColorSpace = import_three24.SRGBColorSpace;
8799
8793
  renderer.setSize(this.viewWidth, this.viewHeight);
8800
8794
  return renderer;
8801
8795
  }
@@ -8829,20 +8823,20 @@ var _Renderer = class _Renderer {
8829
8823
  window["__THREE_DEVTOOLS__"].dispatchEvent(new CustomEvent("observe", { detail: this.scene }));
8830
8824
  }
8831
8825
  if (this.options.debug.grid) {
8832
- const gridHelper = new import_three23.GridHelper(128, 16);
8826
+ const gridHelper = new import_three24.GridHelper(128, 16);
8833
8827
  this._debugHelpers.push(gridHelper);
8834
8828
  this.scene.add(gridHelper);
8835
- const gridHelper2 = new import_three23.GridHelper(128, 16);
8829
+ const gridHelper2 = new import_three24.GridHelper(128, 16);
8836
8830
  this._debugHelpers.push(gridHelper2);
8837
8831
  gridHelper2.rotation.x = 90 * (Math.PI / 180);
8838
8832
  this.scene.add(gridHelper2);
8839
- const gridHelper3 = new import_three23.GridHelper(128, 16);
8833
+ const gridHelper3 = new import_three24.GridHelper(128, 16);
8840
8834
  this._debugHelpers.push(gridHelper3);
8841
8835
  gridHelper3.rotation.z = 90 * (Math.PI / 180);
8842
8836
  this.scene.add(gridHelper3);
8843
8837
  }
8844
8838
  if (this.options.debug.axes) {
8845
- const axesHelper = new import_three23.AxesHelper(64);
8839
+ const axesHelper = new import_three24.AxesHelper(64);
8846
8840
  this._debugHelpers.push(axesHelper);
8847
8841
  this.scene.add(axesHelper);
8848
8842
  }
@@ -9046,8 +9040,8 @@ _Renderer.DEFAULT_OPTIONS = (0, import_ts_deepmerge9.default)({}, {
9046
9040
  top: void 0,
9047
9041
  bottom: void 0
9048
9042
  },
9049
- position: new import_three23.Vector3(50, 50, 50),
9050
- lookingAt: new import_three23.Vector3(0, 0, 0)
9043
+ position: new import_three24.Vector3(50, 50, 50),
9044
+ lookingAt: new import_three24.Vector3(0, 0, 0)
9051
9045
  },
9052
9046
  render: {
9053
9047
  fpsLimit: 60,
@@ -9487,32 +9481,121 @@ var AnvilParser = class {
9487
9481
  };
9488
9482
 
9489
9483
  // src/world/Chunk.ts
9484
+ var import_three29 = require("three");
9485
+
9486
+ // src/world/MineRenderWorld.ts
9487
+ var import_three27 = require("three");
9488
+
9489
+ // src/world/SectionModels.ts
9490
9490
  var import_three26 = require("three");
9491
+ var SectionModel = class extends ModelObject {
9492
+ applyTextures() {
9493
+ }
9494
+ };
9495
+ function cached(map, key, load) {
9496
+ let value = map.get(key);
9497
+ if (!value) {
9498
+ value = load().catch((error) => {
9499
+ map.delete(key);
9500
+ throw error;
9501
+ });
9502
+ map.set(key, value);
9503
+ }
9504
+ return value;
9505
+ }
9506
+ var SectionModels = class {
9507
+ constructor(maxAtlasSize = 2048) {
9508
+ this.maxAtlasSize = maxAtlasSize;
9509
+ this.states = /* @__PURE__ */ new WeakMap();
9510
+ this.models = /* @__PURE__ */ new WeakMap();
9511
+ this.templates = /* @__PURE__ */ new Set();
9512
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("maxAtlasSize must be a positive integer");
9513
+ }
9514
+ async get(blockState, properties = {}) {
9515
+ if (!blockState.variants || blockState.multipart) return void 0;
9516
+ let states = this.states.get(blockState);
9517
+ if (!states) this.states.set(blockState, states = /* @__PURE__ */ new Map());
9518
+ const key = JSON.stringify(Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)));
9519
+ const prepared = await cached(states, key, () => this.prepare(blockState, properties));
9520
+ if (!prepared) return void 0;
9521
+ return prepared.templates.get(BlockStateResolver.choose(prepared.variants));
9522
+ }
9523
+ async prepare(blockState, properties) {
9524
+ const state = { ...await BlockStateResolver.defaults(blockState), ...properties };
9525
+ const groups = BlockStateResolver.matching(blockState, state);
9526
+ if (groups.length !== 1) return void 0;
9527
+ const variants = groups[0];
9528
+ const choices = Array.isArray(variants) ? variants : [variants];
9529
+ if (!choices.length) return void 0;
9530
+ const templates = /* @__PURE__ */ new Map();
9531
+ for (const variant of choices) {
9532
+ const model = await Models.getMerged(AssetKey.parse("models", variant.model));
9533
+ if (!model) return void 0;
9534
+ const atlas = await UVMapper.getAtlas(model);
9535
+ const rotation = BlockStateResolver.rotation(variant);
9536
+ 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;
9537
+ const tints = await BlockTints.get(blockState.key, state, model);
9538
+ let models = this.models.get(model);
9539
+ if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
9540
+ const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
9541
+ const template = await cached(models, key, async () => {
9542
+ const object = new SectionModel(model, {
9543
+ instanceMeshes: false,
9544
+ mergeMeshes: false,
9545
+ tints,
9546
+ uvLockRotation: variant.uvlock && (rotation.x !== 0 || rotation.y !== 0) ? [rotation.x, rotation.y, rotation.z] : void 0
9547
+ });
9548
+ await object.init();
9549
+ const geometry = object.children[0].geometry;
9550
+ geometry.applyMatrix4(new import_three26.Matrix4().makeRotationFromEuler(rotation));
9551
+ object.clear();
9552
+ const cullFaces = CUBE_FACES.map((face) => {
9553
+ const local = CUBE_FACES.findIndex((name) => name === atlas.model.elements[0].faces[face]?.cullface);
9554
+ if (local < 0) return 0;
9555
+ const normal = new import_three26.Vector3(...CUBE_FACE_OFFSETS[local]).applyEuler(rotation);
9556
+ 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);
9557
+ return 1 << world;
9558
+ });
9559
+ const template2 = { geometry, atlas, cullFaces };
9560
+ this.templates.add(template2);
9561
+ return template2;
9562
+ });
9563
+ templates.set(variant, template);
9564
+ }
9565
+ return { variants, templates };
9566
+ }
9567
+ clear() {
9568
+ for (const template of this.templates) template.geometry.dispose();
9569
+ this.templates.clear();
9570
+ this.states = /* @__PURE__ */ new WeakMap();
9571
+ this.models = /* @__PURE__ */ new WeakMap();
9572
+ }
9573
+ };
9491
9574
 
9492
9575
  // src/world/MineRenderWorld.ts
9493
- var import_three25 = require("three");
9494
9576
  var MineRenderWorld = class {
9495
- constructor(scene) {
9577
+ constructor(scene, options = {}) {
9496
9578
  this._chunks = /* @__PURE__ */ new Map();
9497
9579
  this.pendingCulling = /* @__PURE__ */ new Map();
9498
9580
  this.scene = scene;
9581
+ if (options.sectionMeshing) this.sectionModels = new SectionModels(options.maxAtlasSize);
9499
9582
  }
9500
9583
  getBlockAt(posOrX, y, z) {
9501
9584
  if (typeof posOrX == "number") {
9502
- return this.getBlockAt(new import_three25.Vector3(posOrX, y, z));
9585
+ return this.getBlockAt(new import_three27.Vector3(posOrX, y, z));
9503
9586
  }
9504
9587
  if (isTripleArray(posOrX)) {
9505
- return this.getBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9588
+ return this.getBlockAt(new import_three27.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9506
9589
  }
9507
9590
  this.validatePosBounds(posOrX);
9508
9591
  return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
9509
9592
  }
9510
9593
  async setBlockAt(posOrX, yOrBlock, z, block) {
9511
9594
  if (typeof posOrX == "number") {
9512
- return this.setBlockAt(new import_three25.Vector3(posOrX, yOrBlock, z), block);
9595
+ return this.setBlockAt(new import_three27.Vector3(posOrX, yOrBlock, z), block);
9513
9596
  }
9514
9597
  if (isTripleArray(posOrX)) {
9515
- return this.setBlockAt(new import_three25.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9598
+ return this.setBlockAt(new import_three27.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9516
9599
  }
9517
9600
  return this.placeBlock(posOrX, yOrBlock);
9518
9601
  }
@@ -9533,7 +9616,7 @@ var MineRenderWorld = class {
9533
9616
  const collect = async (positions2) => {
9534
9617
  for (const pos of positions2) changes.set(pos.toArray().join(","), pos);
9535
9618
  };
9536
- const place = (block) => this.placeBlock(new import_three25.Vector3(...block.position), block, collect);
9619
+ const place = (block) => this.placeBlock(new import_three27.Vector3(...block.position), block, collect);
9537
9620
  const keys = /* @__PURE__ */ new Map();
9538
9621
  for (const block of multiblock.blocks) {
9539
9622
  if (Chunk.isAir(block)) continue;
@@ -9605,12 +9688,13 @@ var MineRenderWorld = class {
9605
9688
  for (const chunk of chunks) {
9606
9689
  await chunk.dispose();
9607
9690
  }
9691
+ this.sectionModels?.clear();
9608
9692
  }
9609
9693
  updateCulling(positions) {
9610
9694
  for (const pos of positions) {
9611
9695
  this.pendingCulling.set(pos.toArray().join(","), pos.clone());
9612
9696
  for (const offset of CUBE_FACE_OFFSETS) {
9613
- const neighbor = pos.clone().add(new import_three25.Vector3(...offset));
9697
+ const neighbor = pos.clone().add(new import_three27.Vector3(...offset));
9614
9698
  this.pendingCulling.set(neighbor.toArray().join(","), neighbor);
9615
9699
  }
9616
9700
  }
@@ -9619,17 +9703,22 @@ var MineRenderWorld = class {
9619
9703
  while (this.pendingCulling.size) {
9620
9704
  const batch = [...this.pendingCulling.values()];
9621
9705
  this.pendingCulling.clear();
9706
+ const changed = /* @__PURE__ */ new Set();
9622
9707
  for (const pos of batch) {
9623
- const block = this.getBlockAt(pos)?.object;
9624
- if (!block) continue;
9708
+ const chunk = this.getChunkAt(pos);
9709
+ if (!chunk) continue;
9710
+ changed.add(chunk);
9711
+ if (!chunk.getBlockAt(pos)) continue;
9625
9712
  let mask = 0;
9626
9713
  for (const [face, offset] of CUBE_FACE_OFFSETS.entries()) {
9627
- if (this.getBlockAt(pos.clone().add(new import_three25.Vector3(...offset)))?.object.isOccluding) {
9714
+ const neighbor = pos.clone().add(new import_three27.Vector3(...offset));
9715
+ if (this.getChunkAt(neighbor)?.isOccludingAt(neighbor)) {
9628
9716
  mask |= 1 << face;
9629
9717
  }
9630
9718
  }
9631
- await block.setCullMask(mask);
9719
+ await chunk.setCullMaskAt(pos, mask);
9632
9720
  }
9721
+ for (const chunk of changed) chunk.rebuildSectionMesh();
9633
9722
  }
9634
9723
  } finally {
9635
9724
  this.culling = void 0;
@@ -9645,7 +9734,8 @@ var MineRenderWorld = class {
9645
9734
  Math.floor(pos.x / 16),
9646
9735
  Math.floor(pos.y / 16),
9647
9736
  Math.floor(pos.z / 16),
9648
- (positions) => this.updateCulling(positions)
9737
+ (positions) => this.updateCulling(positions),
9738
+ this.sectionModels
9649
9739
  );
9650
9740
  this._chunks.set(key, chunk);
9651
9741
  }
@@ -9655,14 +9745,14 @@ var MineRenderWorld = class {
9655
9745
  return this._chunks.get(this.worldPosToChunkKey(pos));
9656
9746
  }
9657
9747
  static worldToScenePosition(pos) {
9658
- return new import_three25.Vector3(
9748
+ return new import_three27.Vector3(
9659
9749
  pos.x * 16,
9660
9750
  pos.y * 16,
9661
9751
  pos.z * 16
9662
9752
  );
9663
9753
  }
9664
9754
  static sceneToWorldPosition(pos) {
9665
- return new import_three25.Vector3(
9755
+ return new import_three27.Vector3(
9666
9756
  pos.x / 16,
9667
9757
  pos.y / 16,
9668
9758
  pos.z / 16
@@ -9681,12 +9771,118 @@ var MineRenderWorld = class {
9681
9771
  }
9682
9772
  };
9683
9773
 
9774
+ // src/world/SectionMesh.ts
9775
+ var import_three28 = require("three");
9776
+ var SectionMesh = class _SectionMesh extends import_three28.Group {
9777
+ constructor() {
9778
+ super(...arguments);
9779
+ this.ownedMeshes = [];
9780
+ }
9781
+ static build(entries, maxAtlasSize = 2048) {
9782
+ if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
9783
+ const visible = entries.filter((entry) => entry.template.cullFaces.some((direction) => !(entry.cullMask & direction)));
9784
+ const atlases = [...new Set(visible.map((entry) => entry.template.atlas))].sort((a, b) => b.image.height - a.image.height);
9785
+ const pages = [];
9786
+ const locations = /* @__PURE__ */ new Map();
9787
+ for (const atlas of atlases) {
9788
+ const { width, height } = atlas.image;
9789
+ if (width > maxAtlasSize || height > maxAtlasSize) throw new RangeError("Model atlas exceeds the section atlas size limit");
9790
+ let selected;
9791
+ for (const page of pages) {
9792
+ const nextRow = page.x + width > maxAtlasSize;
9793
+ if ((nextRow ? page.y + page.rowHeight : page.y) + height > maxAtlasSize) continue;
9794
+ if (nextRow) {
9795
+ page.y += page.rowHeight;
9796
+ page.x = 0;
9797
+ page.rowHeight = 0;
9798
+ }
9799
+ selected = page;
9800
+ break;
9801
+ }
9802
+ if (!selected) {
9803
+ selected = { placements: [], entries: [], x: 0, y: 0, rowHeight: 0, width: 0, height: 0 };
9804
+ pages.push(selected);
9805
+ }
9806
+ const placement = { atlas, x: selected.x, y: selected.y };
9807
+ selected.placements.push(placement);
9808
+ selected.x += width;
9809
+ selected.rowHeight = Math.max(selected.rowHeight, height);
9810
+ selected.width = Math.max(selected.width, selected.x);
9811
+ selected.height = Math.max(selected.height, selected.y + height);
9812
+ locations.set(atlas, { page: selected, placement });
9813
+ }
9814
+ for (const entry of visible) locations.get(entry.template.atlas).page.entries.push(entry);
9815
+ const section = new _SectionMesh();
9816
+ for (const page of pages) {
9817
+ const canvas = createCanvas(page.width, page.height);
9818
+ const context = canvas.getContext("2d");
9819
+ for (const { atlas, x, y } of page.placements) {
9820
+ context.drawImage(atlas.image.canvas, x, y);
9821
+ }
9822
+ const positions = [], normals = [], uvs = [], colors = [], indices = [];
9823
+ for (const { index, template, cullMask } of page.entries) {
9824
+ const position = template.geometry.getAttribute("position");
9825
+ const normal = template.geometry.getAttribute("normal");
9826
+ const uv = template.geometry.getAttribute("uv");
9827
+ const color = template.geometry.getAttribute("color");
9828
+ const sourceIndices = template.geometry.getIndex();
9829
+ const placement = locations.get(template.atlas).placement;
9830
+ const offsetX = index % 16 * 16;
9831
+ const offsetY = Math.floor(index / 256) * 16;
9832
+ const offsetZ = Math.floor(index / 16) % 16 * 16;
9833
+ for (let face = 0; face < 6; face++) {
9834
+ if (cullMask & template.cullFaces[face]) continue;
9835
+ const start = positions.length / 3;
9836
+ for (let corner = 0; corner < 4; corner++) {
9837
+ const vertex = face * 4 + corner;
9838
+ positions.push(position.getX(vertex) + offsetX, position.getY(vertex) + offsetY, position.getZ(vertex) + offsetZ);
9839
+ normals.push(normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex));
9840
+ uvs.push(
9841
+ (placement.x + uv.getX(vertex) * template.atlas.image.width) / page.width,
9842
+ 1 - (placement.y + (1 - uv.getY(vertex)) * template.atlas.image.height) / page.height
9843
+ );
9844
+ colors.push(color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1);
9845
+ }
9846
+ for (let corner = 0; corner < 6; corner++) indices.push(start + sourceIndices.getX(face * 6 + corner) - face * 4);
9847
+ }
9848
+ }
9849
+ const geometry = new import_three28.BufferGeometry();
9850
+ geometry.setAttribute("position", new import_three28.Float32BufferAttribute(positions, 3));
9851
+ geometry.setAttribute("normal", new import_three28.Float32BufferAttribute(normals, 3));
9852
+ geometry.setAttribute("uv", new import_three28.Float32BufferAttribute(uvs, 2));
9853
+ geometry.setAttribute("color", new import_three28.Float32BufferAttribute(colors, 3));
9854
+ geometry.setIndex(indices);
9855
+ geometry.computeBoundingBox();
9856
+ geometry.computeBoundingSphere();
9857
+ const material = Materials.createShadedCanvasMaterial(canvas, false);
9858
+ material.vertexColors = true;
9859
+ const texture = material.uniforms?.map?.value ?? material.map;
9860
+ const mesh = new import_three28.Mesh(geometry, material);
9861
+ section.add(mesh);
9862
+ section.ownedMeshes.push({ mesh, texture });
9863
+ }
9864
+ return section;
9865
+ }
9866
+ dispose() {
9867
+ for (const { mesh, texture } of this.ownedMeshes.splice(0)) {
9868
+ mesh.removeFromParent();
9869
+ mesh.geometry.dispose();
9870
+ mesh.material.dispose();
9871
+ texture?.dispose();
9872
+ }
9873
+ this.removeFromParent();
9874
+ }
9875
+ };
9876
+
9684
9877
  // src/world/Chunk.ts
9685
9878
  var Chunk = class _Chunk {
9686
- constructor(scene, x, y, z, onBlocksChanged) {
9879
+ constructor(scene, x, y, z, onBlocksChanged, sectionModels) {
9687
9880
  this.onBlocksChanged = onBlocksChanged;
9881
+ this.sectionModels = sectionModels;
9688
9882
  this.data = new ChunkData();
9689
9883
  this.renderedBlocks = /* @__PURE__ */ new Map();
9884
+ this.sectionBlocks = /* @__PURE__ */ new Map();
9885
+ this.meshDirty = false;
9690
9886
  this.scene = scene;
9691
9887
  this.x = x;
9692
9888
  this.y = y;
@@ -9694,10 +9890,10 @@ var Chunk = class _Chunk {
9694
9890
  }
9695
9891
  getBlockAt(posOrX, y, z) {
9696
9892
  if (typeof posOrX == "number") {
9697
- return this.getBlockAt(new import_three26.Vector3(posOrX, y, z));
9893
+ return this.getBlockAt(new import_three29.Vector3(posOrX, y, z));
9698
9894
  }
9699
9895
  if (isTripleArray(posOrX)) {
9700
- return this.getBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9896
+ return this.getBlockAt(new import_three29.Vector3(posOrX[0], posOrX[1], posOrX[2]));
9701
9897
  }
9702
9898
  const pos = this.worldPosToChunkPos(posOrX);
9703
9899
  const index = _Chunk.chunkPosToBlockIndex(pos);
@@ -9705,10 +9901,10 @@ var Chunk = class _Chunk {
9705
9901
  }
9706
9902
  async setBlockAt(posOrX, yOrBlock, z, block) {
9707
9903
  if (typeof posOrX == "number") {
9708
- return this.setBlockAt(new import_three26.Vector3(posOrX, yOrBlock, z), block);
9904
+ return this.setBlockAt(new import_three29.Vector3(posOrX, yOrBlock, z), block);
9709
9905
  }
9710
9906
  if (isTripleArray(posOrX)) {
9711
- return this.setBlockAt(new import_three26.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9907
+ return this.setBlockAt(new import_three29.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
9712
9908
  }
9713
9909
  const worldPos = posOrX;
9714
9910
  const pos = this.worldPosToChunkPos(worldPos);
@@ -9725,7 +9921,8 @@ var Chunk = class _Chunk {
9725
9921
  const index = _Chunk.chunkPosToBlockIndex(pos);
9726
9922
  this.data.set(index, block);
9727
9923
  const readBlock = this.data.snapshot(index);
9728
- this.renderedBlocks.get(index)?.object.removeFromScene();
9924
+ this.renderedBlocks.get(index)?.object?.removeFromScene();
9925
+ if (this.sectionBlocks.delete(index)) this.meshDirty = true;
9729
9926
  this.renderedBlocks.delete(index);
9730
9927
  let object;
9731
9928
  try {
@@ -9736,13 +9933,19 @@ var Chunk = class _Chunk {
9736
9933
  this.data.set(index, void 0);
9737
9934
  return void 0;
9738
9935
  }
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));
9936
+ const template = await this.sectionModels?.get(blockState, stored.properties);
9937
+ if (template) {
9938
+ this.sectionBlocks.set(index, { index, template, cullMask: 0 });
9939
+ this.meshDirty = true;
9940
+ } else {
9941
+ object = await this.scene.addBlock(blockState, {
9942
+ mergeMeshes: true,
9943
+ instanceMeshes: true,
9944
+ maxInstanceCount: 2e3,
9945
+ initialState: stored.properties
9946
+ });
9947
+ object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
9948
+ }
9746
9949
  const info = { get block() {
9747
9950
  return readBlock();
9748
9951
  }, object };
@@ -9756,11 +9959,43 @@ var Chunk = class _Chunk {
9756
9959
  await onBlocksChanged?.([worldPos]);
9757
9960
  }
9758
9961
  }
9962
+ isOccludingAt(pos) {
9963
+ const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
9964
+ return this.sectionBlocks.has(index) || (this.renderedBlocks.get(index)?.object?.isOccluding ?? false);
9965
+ }
9966
+ async setCullMaskAt(pos, mask) {
9967
+ const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
9968
+ const entry = this.sectionBlocks.get(index);
9969
+ if (entry) {
9970
+ if (entry.cullMask !== mask) {
9971
+ entry.cullMask = mask;
9972
+ this.meshDirty = true;
9973
+ }
9974
+ } else {
9975
+ await this.renderedBlocks.get(index)?.object?.setCullMask(mask);
9976
+ }
9977
+ }
9978
+ rebuildSectionMesh() {
9979
+ if (!this.meshDirty) return;
9980
+ const next = this.sectionBlocks.size ? SectionMesh.build([...this.sectionBlocks.values()], this.sectionModels.maxAtlasSize) : void 0;
9981
+ this.sectionMesh?.dispose();
9982
+ this.sectionMesh = next;
9983
+ if (next) {
9984
+ next.position.set(this.x * 256, this.y * 256, this.z * 256);
9985
+ this.scene.add(next);
9986
+ }
9987
+ this.scene.dirty = true;
9988
+ this.meshDirty = false;
9989
+ }
9759
9990
  async clear(onBlocksChanged = this.onBlocksChanged) {
9760
9991
  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)
9992
+ new import_three29.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
9762
9993
  ));
9763
- for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
9994
+ for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
9995
+ this.sectionBlocks.clear();
9996
+ this.sectionMesh?.dispose();
9997
+ this.sectionMesh = void 0;
9998
+ this.meshDirty = false;
9764
9999
  this.renderedBlocks.clear();
9765
10000
  this.data.clear();
9766
10001
  await onBlocksChanged?.(positions);
@@ -9778,14 +10013,14 @@ var Chunk = class _Chunk {
9778
10013
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
9779
10014
  }
9780
10015
  worldPosToChunkPos(pos) {
9781
- return new import_three26.Vector3(
10016
+ return new import_three29.Vector3(
9782
10017
  pos.x - this.x * 16,
9783
10018
  pos.y - this.y * 16,
9784
10019
  pos.z - this.z * 16
9785
10020
  );
9786
10021
  }
9787
10022
  chunkPosToWorldPos(pos) {
9788
- return new import_three26.Vector3(
10023
+ return new import_three29.Vector3(
9789
10024
  this.x * 16 + pos.x,
9790
10025
  this.y * 16 + pos.y,
9791
10026
  this.z * 16 + pos.z