minerender 2.1.0-alpha.42 → 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.
@@ -1073,6 +1073,8 @@ declare class SceneObject extends Object3D<Object3DEventMap & {
1073
1073
  get instanceCounter(): number;
1074
1074
  protected constructInstanceReference(i: number): InstanceReference<SceneObject>;
1075
1075
  nextInstance(): InstanceReference<SceneObject>;
1076
+ /** Returns the live reference for a raycast hit on this object's instance mesh. */
1077
+ getInstanceReference(mesh: Object3D, index: number): Maybe<InstanceReference<SceneObject>>;
1076
1078
  isInstanceActive(index: number, reference: InstanceReference<Instanceable>): boolean;
1077
1079
  removeInstanceAt(index: number): void;
1078
1080
  private growInstances;
@@ -1624,17 +1626,21 @@ declare class SceneInspector {
1624
1626
  protected readonly raycaster: Raycaster;
1625
1627
  private readonly mouse;
1626
1628
  private selectedObject?;
1629
+ private pointer?;
1630
+ private readonly listeners;
1627
1631
  constructor(renderer: Renderer);
1628
1632
  protected init(): void;
1633
+ dispose(): void;
1629
1634
  findMeshChildren(from: Object3D, out?: Mesh[]): Mesh<three.BufferGeometry<three.NormalBufferAttributes>, three.Material | three.Material[], three.Object3DEventMap>[];
1630
- onClick(event: any): void;
1635
+ onClick(event: MouseEvent): void;
1631
1636
  appendTo(el: HTMLElement): void;
1632
1637
  protected handleRaycasterObjects(intersections: Intersection[]): void;
1633
- selectObject(targetObject: Object3D, targetIntersection?: Intersection): void;
1634
- protected redraw(targetObject: Object3D, targetIntersection?: Intersection): void;
1635
- protected addControls(object: Object3D, intersection?: Intersection): void;
1638
+ private getIntersectionInstance;
1639
+ selectObject(targetObject: Object3D, targetIntersection?: Intersection, instance?: Maybe<InstanceReference<SceneObject>>): void;
1640
+ protected redraw(targetObject: Object3D, targetIntersection?: Intersection, instance?: InstanceReference<SceneObject>): void;
1641
+ protected addControls(object: Object3D, intersection?: Intersection, instance?: InstanceReference<SceneObject>): void;
1636
1642
  protected addInfoLine(name: string, id: string, value: any): HTMLElement;
1637
- protected addObjectControls(target: Object3D, intersection: Maybe<Intersection>, container: HTMLElement): void;
1643
+ protected addObjectControls(target: Object3D, intersection: Maybe<Intersection>, container: HTMLElement, instance?: InstanceReference<SceneObject>): void;
1638
1644
  protected buttonControl(name: string, id: string, click: () => void): HTMLElement;
1639
1645
  protected selectControl(name: string, id: string, options: string[], change: (v: string) => void): HTMLElement;
1640
1646
  protected toggleControl(name: string, id: string, val: boolean, change: (v: boolean) => void): HTMLElement;
@@ -1073,6 +1073,8 @@ declare class SceneObject extends Object3D<Object3DEventMap & {
1073
1073
  get instanceCounter(): number;
1074
1074
  protected constructInstanceReference(i: number): InstanceReference<SceneObject>;
1075
1075
  nextInstance(): InstanceReference<SceneObject>;
1076
+ /** Returns the live reference for a raycast hit on this object's instance mesh. */
1077
+ getInstanceReference(mesh: Object3D, index: number): Maybe<InstanceReference<SceneObject>>;
1076
1078
  isInstanceActive(index: number, reference: InstanceReference<Instanceable>): boolean;
1077
1079
  removeInstanceAt(index: number): void;
1078
1080
  private growInstances;
@@ -1624,17 +1626,21 @@ declare class SceneInspector {
1624
1626
  protected readonly raycaster: Raycaster;
1625
1627
  private readonly mouse;
1626
1628
  private selectedObject?;
1629
+ private pointer?;
1630
+ private readonly listeners;
1627
1631
  constructor(renderer: Renderer);
1628
1632
  protected init(): void;
1633
+ dispose(): void;
1629
1634
  findMeshChildren(from: Object3D, out?: Mesh[]): Mesh<three.BufferGeometry<three.NormalBufferAttributes>, three.Material | three.Material[], three.Object3DEventMap>[];
1630
- onClick(event: any): void;
1635
+ onClick(event: MouseEvent): void;
1631
1636
  appendTo(el: HTMLElement): void;
1632
1637
  protected handleRaycasterObjects(intersections: Intersection[]): void;
1633
- selectObject(targetObject: Object3D, targetIntersection?: Intersection): void;
1634
- protected redraw(targetObject: Object3D, targetIntersection?: Intersection): void;
1635
- protected addControls(object: Object3D, intersection?: Intersection): void;
1638
+ private getIntersectionInstance;
1639
+ selectObject(targetObject: Object3D, targetIntersection?: Intersection, instance?: Maybe<InstanceReference<SceneObject>>): void;
1640
+ protected redraw(targetObject: Object3D, targetIntersection?: Intersection, instance?: InstanceReference<SceneObject>): void;
1641
+ protected addControls(object: Object3D, intersection?: Intersection, instance?: InstanceReference<SceneObject>): void;
1636
1642
  protected addInfoLine(name: string, id: string, value: any): HTMLElement;
1637
- protected addObjectControls(target: Object3D, intersection: Maybe<Intersection>, container: HTMLElement): void;
1643
+ protected addObjectControls(target: Object3D, intersection: Maybe<Intersection>, container: HTMLElement, instance?: InstanceReference<SceneObject>): void;
1638
1644
  protected buttonControl(name: string, id: string, click: () => void): HTMLElement;
1639
1645
  protected selectControl(name: string, id: string, options: string[], change: (v: string) => void): HTMLElement;
1640
1646
  protected toggleControl(name: string, id: string, val: boolean, change: (v: boolean) => void): HTMLElement;
@@ -3264,6 +3264,10 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3264
3264
  this.setMatrixAt(index, new import_three10.Matrix4());
3265
3265
  return reference;
3266
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
+ }
3267
3271
  isInstanceActive(index, reference) {
3268
3272
  return this.instanceReferences.get(index) === reference;
3269
3273
  }
@@ -3346,9 +3350,9 @@ var SceneObject = class _SceneObject extends import_three10.Object3D {
3346
3350
  }
3347
3351
  getRotationAt(index, euler = new import_three10.Euler()) {
3348
3352
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
3349
- const matrix = this.getMatrixAt(index);
3350
- euler.setFromRotationMatrix(matrix);
3351
- 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);
3352
3356
  }
3353
3357
  setScaleAt(index, scale) {
3354
3358
  if (!this.isInstanced) throw new MineRenderError("Object is not instanced");
@@ -3598,11 +3602,12 @@ var GuiObject = class extends SceneObject {
3598
3602
  // src/inspector/SceneInspector.ts
3599
3603
  var import_three14 = require("three");
3600
3604
  var p7 = prefix("SceneInspector");
3601
- var help = "<span>Ctrl+Click to Select<br/></span><br/>";
3605
+ var help = "<span>Ctrl/Cmd+Click to Select<br/></span><br/>";
3602
3606
  var SceneInspector = class {
3603
3607
  constructor(renderer) {
3604
3608
  this.renderer = renderer;
3605
3609
  this.mouse = new import_three14.Vector2();
3610
+ this.listeners = new AbortController();
3606
3611
  this.objectInfoContainer = document.createElement("div");
3607
3612
  this.objectControlsContainer = document.createElement("div");
3608
3613
  this.raycaster = new import_three14.Raycaster();
@@ -3612,13 +3617,44 @@ var SceneInspector = class {
3612
3617
  this.objectInfoContainer.classList.add("minerender-inspector", "minerender-object-info");
3613
3618
  this.objectControlsContainer.classList.add("minerender-inspector", "minerender-object-controls");
3614
3619
  this.objectInfoContainer.innerHTML = help;
3615
- document.addEventListener("click", this.onClick.bind(this), {
3616
- passive: true
3617
- });
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();
3618
3653
  }
3619
3654
  findMeshChildren(from, out = []) {
3655
+ if (!from.visible) return out;
3620
3656
  for (let c of from.children) {
3621
- if (isMesh(c)) {
3657
+ if (c.visible && isMesh(c)) {
3622
3658
  out.push(c);
3623
3659
  }
3624
3660
  this.findMeshChildren(c, out);
@@ -3626,50 +3662,43 @@ var SceneInspector = class {
3626
3662
  return out;
3627
3663
  }
3628
3664
  onClick(event) {
3629
- if (event.target?.nodeName !== "CANVAS") return;
3630
- if (!event.ctrlKey) return;
3631
- setTimeout(() => {
3632
- this.mouse.x = event.clientX / window.innerWidth * 2 - 1;
3633
- this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
3634
- this.raycaster.setFromCamera(this.mouse, this.renderer.camera);
3635
- let start = Date.now();
3636
- let targets = this.findMeshChildren(this.renderer.scene);
3637
- const intersects = this.raycaster.intersectObjects(targets, false);
3638
- console.debug("Raycast took " + (Date.now() - start) + "ms for " + targets.length + " possible targets with " + intersects.length + " results");
3639
- if (intersects.length > 0) {
3640
- this.handleRaycasterObjects(intersects);
3641
- }
3642
- });
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));
3643
3675
  }
3644
3676
  appendTo(el) {
3645
3677
  el.append(this.objectInfoContainer);
3646
3678
  el.append(this.objectControlsContainer);
3647
3679
  }
3648
3680
  handleRaycasterObjects(intersections) {
3649
- console.debug(intersections);
3650
- const firstIntersection = intersections[0];
3651
- if (firstIntersection && firstIntersection.object) {
3652
- const firstObject = firstIntersection.object;
3653
- let targetIntersection = void 0;
3654
- let targetObject = void 0;
3655
- const firstSceneObjectIntersection = intersections.find((i) => i.object && i.object.parent && isSceneObject(i.object.parent));
3656
- if (firstSceneObjectIntersection && firstSceneObjectIntersection.object) {
3657
- targetIntersection = firstSceneObjectIntersection;
3658
- targetObject = firstSceneObjectIntersection.object;
3659
- } else {
3660
- targetIntersection = firstIntersection;
3661
- 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;
3662
3692
  }
3663
- if (!targetIntersection || !targetObject) return;
3664
- this.selectObject(targetObject, targetIntersection);
3665
3693
  }
3694
+ return void 0;
3666
3695
  }
3667
- selectObject(targetObject, targetIntersection) {
3696
+ selectObject(targetObject, targetIntersection, instance = this.getIntersectionInstance(targetIntersection)) {
3668
3697
  this.selectedObject = targetObject;
3669
3698
  console.log(p7, "selected", targetObject);
3670
- this.redraw(targetObject, targetIntersection);
3699
+ this.redraw(targetObject, targetIntersection, instance);
3671
3700
  }
3672
- redraw(targetObject, targetIntersection) {
3701
+ redraw(targetObject, targetIntersection, instance) {
3673
3702
  this.objectInfoContainer.innerHTML = help;
3674
3703
  if (targetIntersection) {
3675
3704
  this.addInfoLine("Distance", "D", targetIntersection.distance);
@@ -3682,8 +3711,8 @@ var SceneInspector = class {
3682
3711
  if (isSceneObject(targetObject.parent)) {
3683
3712
  }
3684
3713
  }
3685
- if ("atlas" in targetObject) {
3686
- const atlas = targetObject["atlas"];
3714
+ const atlas = "atlas" in targetObject ? targetObject["atlas"] : void 0;
3715
+ if (atlas?.image.canvas) {
3687
3716
  const img = atlas.image.canvas;
3688
3717
  img.style.width = "100px";
3689
3718
  img.style.height = "100px";
@@ -3692,28 +3721,31 @@ var SceneInspector = class {
3692
3721
  this.objectInfoContainer.prepend(img);
3693
3722
  }
3694
3723
  this.objectControlsContainer.innerHTML = "";
3695
- this.addControls(targetObject, targetIntersection);
3724
+ this.addControls(targetObject, targetIntersection, instance);
3696
3725
  }
3697
- addControls(object, intersection) {
3726
+ addControls(object, intersection, instance) {
3698
3727
  const container = document.createElement("div");
3699
3728
  container.append(this.separator("Select Parent/Child"));
3700
3729
  if (object.parent) {
3701
3730
  container.append(this.buttonControl("Select Parent " + object.parent.constructor.name + " " + object.parent.name, "P", () => {
3702
- this.selectObject(object.parent, intersection);
3731
+ this.selectObject(object.parent, intersection, instance);
3703
3732
  }));
3704
3733
  }
3705
3734
  if (object.children.length > 0) {
3706
3735
  let i = 1;
3707
3736
  for (let child of object.children) {
3708
3737
  container.append(this.buttonControl("Select Child " + child.constructor.name + " " + child.name, "C" + i++, () => {
3709
- this.selectObject(child, intersection);
3738
+ this.selectObject(child, intersection, instance);
3710
3739
  }));
3711
3740
  }
3712
3741
  }
3713
3742
  container.append(this.separator());
3714
- 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
+ }
3715
3747
  container.append(this.separator("Mesh"));
3716
- this.addObjectControls(object, intersection, container);
3748
+ this.addObjectControls(object, intersection, container, selectedInstance);
3717
3749
  container.append(this.separator());
3718
3750
  this.objectControlsContainer.append(container);
3719
3751
  }
@@ -3725,78 +3757,26 @@ var SceneInspector = class {
3725
3757
  this.objectInfoContainer.append(document.createElement("br"));
3726
3758
  return el;
3727
3759
  }
3728
- addObjectControls(target, intersection, container) {
3760
+ addObjectControls(target, intersection, container, instance) {
3729
3761
  const posRange = 16 * 16;
3730
3762
  const rotRange = 360;
3731
3763
  const scaleRange = 4;
3732
- if (typeof intersection !== "undefined" && typeof intersection.instanceId !== "undefined" && isSceneObject(target) && target.isInstanced) {
3733
- const scObj = target;
3734
- container.append(this.separator("Position"));
3735
- let pos = scObj.getPositionAt(intersection.instanceId);
3736
- container.append(this.rangeControl("X Position", "X", -posRange + pos.x, posRange + pos.x, pos.x, 1, (v) => {
3737
- pos = scObj.getPositionAt(intersection.instanceId);
3738
- pos.x = v;
3739
- scObj.setPositionAt(intersection.instanceId, pos);
3740
- }));
3741
- container.append(this.rangeControl("Y Position", "Y", -posRange + pos.y, posRange + pos.y, pos.y, 1, (v) => {
3742
- pos = scObj.getPositionAt(intersection.instanceId);
3743
- pos.y = v;
3744
- scObj.setPositionAt(intersection.instanceId, pos);
3745
- }));
3746
- container.append(this.rangeControl("Z Position", "Z", -posRange + pos.z, posRange + pos.z, pos.z, 1, (v) => {
3747
- pos = scObj.getPositionAt(intersection.instanceId);
3748
- pos.z = v;
3749
- scObj.setPositionAt(intersection.instanceId, pos);
3750
- }));
3751
- container.append(this.separator("Rotation"));
3752
- let rot = scObj.getRotationAt(intersection.instanceId);
3753
- container.append(this.rangeControl("X Rotation", "X", 0, rotRange, Math.round(toDegrees(rot.x)), 1, (v) => {
3754
- rot = scObj.getRotationAt(intersection.instanceId);
3755
- rot.x = toRadians(v);
3756
- scObj.setRotationAt(intersection.instanceId, rot);
3757
- }));
3758
- container.append(this.rangeControl("Y Rotation", "Y", 0, rotRange, Math.round(toDegrees(rot.y)), 1, (v) => {
3759
- rot = scObj.getRotationAt(intersection.instanceId);
3760
- rot.y = toRadians(v);
3761
- scObj.setRotationAt(intersection.instanceId, rot);
3762
- }));
3763
- container.append(this.rangeControl("Z Rotation", "Z", 0, rotRange, Math.round(toDegrees(rot.z)), 1, (v) => {
3764
- rot = scObj.getRotationAt(intersection.instanceId);
3765
- rot.z = toRadians(v);
3766
- scObj.setRotationAt(intersection.instanceId, rot);
3767
- }));
3768
- container.append(this.separator("Scale"));
3769
- let scl = scObj.getScale();
3770
- container.append(this.rangeControl("X Scale", "X", 0, scaleRange, scl.x, 0.1, (v) => {
3771
- scl = scObj.getScaleAt(intersection.instanceId);
3772
- scl.x = v;
3773
- scObj.setScaleAt(intersection.instanceId, scl);
3774
- }));
3775
- container.append(this.rangeControl("Y Scale", "Y", 0, scaleRange, scl.y, 0.1, (v) => {
3776
- scl = scObj.getScaleAt(intersection.instanceId);
3777
- scl.y = v;
3778
- scObj.setScaleAt(intersection.instanceId, scl);
3779
- }));
3780
- container.append(this.rangeControl("Z Scale", "Z", 0, scaleRange, scl.z, 0.1, (v) => {
3781
- scl = scObj.getScaleAt(intersection.instanceId);
3782
- scl.z = v;
3783
- scObj.setScaleAt(intersection.instanceId, scl);
3784
- }));
3785
- } else if (isTransformable(target)) {
3786
- const parent = target;
3764
+ const transform = instance ?? (isTransformable(target) ? target : void 0);
3765
+ if (transform) {
3766
+ const parent = transform;
3787
3767
  container.append(this.separator("Position"));
3788
3768
  let pos = parent.getPosition();
3789
- 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) => {
3790
3770
  pos = parent.getPosition();
3791
3771
  pos.x = v;
3792
3772
  parent.setPosition(pos);
3793
3773
  }));
3794
- 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) => {
3795
3775
  pos = parent.getPosition();
3796
3776
  pos.y = v;
3797
3777
  parent.setPosition(pos);
3798
3778
  }));
3799
- 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) => {
3800
3780
  pos = parent.getPosition();
3801
3781
  pos.z = v;
3802
3782
  parent.setPosition(pos);
@@ -3885,6 +3865,7 @@ var SceneInspector = class {
3885
3865
  toggle.checked = val;
3886
3866
  toggle.addEventListener("change", (e) => {
3887
3867
  change(toggle.checked);
3868
+ this.renderer.scene.dirty = true;
3888
3869
  });
3889
3870
  label.append(toggle);
3890
3871
  label.append(document.createElement("br"));
@@ -3897,7 +3878,6 @@ var SceneInspector = class {
3897
3878
  label.setAttribute("title", name);
3898
3879
  labelText.style.width = "18%";
3899
3880
  labelText.style.display = "inline-block";
3900
- labelText.innerText = `${id} (${val})`;
3901
3881
  const range = document.createElement("input");
3902
3882
  range.setAttribute("type", "range");
3903
3883
  range.style.width = "80%";
@@ -3905,8 +3885,10 @@ var SceneInspector = class {
3905
3885
  range.min = `${min}`;
3906
3886
  range.step = `${step}`;
3907
3887
  range.value = `${val}`;
3888
+ labelText.innerText = `${id} (${range.value})`;
3908
3889
  const onChange = () => {
3909
3890
  change(parseFloat(range.value));
3891
+ this.renderer.scene.dirty = true;
3910
3892
  labelText.innerText = `${id} (${range.value})`;
3911
3893
  };
3912
3894
  range.addEventListener("input", onChange);