@heliguy-xyz/splat-viewer 1.0.0-rc.3 → 1.0.0-rc.5

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.
@@ -104580,7 +104580,11 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
104580
104580
  : { x: 0, y: 0, z: 0 };
104581
104581
  this.emitFlyEvent?.('fly-camera-move', {
104582
104582
  position: { x: pos.x, y: pos.y, z: pos.z },
104583
- velocity: { x: this._velocity.x, y: this._velocity.y, z: this._velocity.z },
104583
+ velocity: {
104584
+ x: this._velocity.x,
104585
+ y: this._velocity.y,
104586
+ z: this._velocity.z,
104587
+ },
104584
104588
  });
104585
104589
  this._lastMoveEmitTime = now;
104586
104590
  }
@@ -104611,7 +104615,8 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
104611
104615
  document.addEventListener('mousemove', this._onMouseMove);
104612
104616
  document.addEventListener('pointerlockchange', this._onPointerLockChange);
104613
104617
  const canvas = this.app.graphicsDevice.canvas;
104614
- this._onClickToLock = this._onClickToLock || (() => this._requestPointerLock());
104618
+ this._onClickToLock =
104619
+ this._onClickToLock || (() => this._requestPointerLock());
104615
104620
  canvas.addEventListener('mousedown', this._onClickToLock);
104616
104621
  };
104617
104622
  FlyCamera.prototype.deactivate = function () {
@@ -104661,7 +104666,11 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
104661
104666
  return {
104662
104667
  position: { x: pos.x, y: pos.y, z: pos.z },
104663
104668
  rotation: { pitch: this._pitch, yaw: this._yaw },
104664
- velocity: { x: this._velocity.x, y: this._velocity.y, z: this._velocity.z },
104669
+ velocity: {
104670
+ x: this._velocity.x,
104671
+ y: this._velocity.y,
104672
+ z: this._velocity.z,
104673
+ },
104665
104674
  isMoving: Math.abs(this._velocity.x) +
104666
104675
  Math.abs(this._velocity.y) +
104667
104676
  Math.abs(this._velocity.z) >
@@ -104669,17 +104678,47 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
104669
104678
  };
104670
104679
  };
104671
104680
  FlyCamera.prototype._handleKeyDown = function (e) {
104672
- this._pressed.add(e.code);
104681
+ const keys = [];
104682
+ if (e.code) {
104683
+ keys.push(e.code);
104684
+ }
104685
+ if (e.key) {
104686
+ keys.push(e.key);
104687
+ if (e.key.length === 1) {
104688
+ // Allow matching against both `KeyW` style codes and plain characters
104689
+ keys.push(`Key${e.key.toUpperCase()}`);
104690
+ keys.push(e.key.toUpperCase());
104691
+ keys.push(e.key.toLowerCase());
104692
+ }
104693
+ }
104694
+ for (const k of keys) {
104695
+ this._pressed.add(k);
104696
+ }
104673
104697
  };
104674
104698
  FlyCamera.prototype._handleKeyUp = function (e) {
104675
- this._pressed.delete(e.code);
104699
+ const keys = [];
104700
+ if (e.code) {
104701
+ keys.push(e.code);
104702
+ }
104703
+ if (e.key) {
104704
+ keys.push(e.key);
104705
+ if (e.key.length === 1) {
104706
+ keys.push(`Key${e.key.toUpperCase()}`);
104707
+ keys.push(e.key.toUpperCase());
104708
+ keys.push(e.key.toLowerCase());
104709
+ }
104710
+ }
104711
+ for (const k of keys) {
104712
+ this._pressed.delete(k);
104713
+ }
104676
104714
  };
104677
104715
  FlyCamera.prototype._handleMouseMove = function (e) {
104678
104716
  if (!this._isPointerLocked || !this._isActive)
104679
104717
  return;
104718
+ // Invert horizontal (yaw) rotation: moving mouse right rotates view left, and vice versa
104680
104719
  const dx = e.movementX * this.lookSensitivity;
104681
104720
  const dy = e.movementY * this.lookSensitivity * (this.invertY ? 1 : -1);
104682
- this._yaw = (this._yaw + dx) % 360;
104721
+ this._yaw = (this._yaw - dx) % 360;
104683
104722
  this._pitch = Math.max(-89, Math.min(89, this._pitch + dy));
104684
104723
  };
104685
104724
  FlyCamera.prototype._handlePointerLockChange = function () {
@@ -104702,15 +104741,19 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
104702
104741
  }
104703
104742
  }
104704
104743
  };
104705
- FlyCamera.prototype._updateVelocity = function (dt) {
104744
+ FlyCamera.prototype._updateVelocity = function (_dt) {
104706
104745
  // Input direction (local space)
104707
104746
  const kb = this.keyBindings;
104708
- const forward = this._pressed.has(kb.forward) ? 1 : 0;
104709
- const backward = this._pressed.has(kb.backward) ? 1 : 0;
104710
- const left = this._pressed.has(kb.left) ? 1 : 0;
104711
- const right = this._pressed.has(kb.right) ? 1 : 0;
104712
- const up = this._pressed.has(kb.up) ? 1 : 0;
104713
- const down = this._pressed.has(kb.down) ? 1 : 0;
104747
+ const isPressed = (binding, fallbacks) => {
104748
+ const all = [binding, ...fallbacks].filter(Boolean);
104749
+ return all.some(k => this._pressed.has(k));
104750
+ };
104751
+ const forward = isPressed(kb.forward, ['KeyW', 'w', 'W']) ? 1 : 0;
104752
+ const backward = isPressed(kb.backward, ['KeyS', 's', 'S']) ? 1 : 0;
104753
+ const left = isPressed(kb.left, ['KeyA', 'a', 'A']) ? 1 : 0;
104754
+ const right = isPressed(kb.right, ['KeyD', 'd', 'D']) ? 1 : 0;
104755
+ const up = isPressed(kb.up, ['KeyE', 'e', 'E']) ? 1 : 0;
104756
+ const down = isPressed(kb.down, ['KeyQ', 'q', 'Q']) ? 1 : 0;
104714
104757
  const inputZ = forward - backward;
104715
104758
  const inputX = right - left;
104716
104759
  const inputY = up - down;
@@ -104718,17 +104761,25 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
104718
104761
  const planarLen = Math.hypot(inputX, inputZ);
104719
104762
  const nx = planarLen > 0 ? inputX / planarLen : 0;
104720
104763
  const nz = planarLen > 0 ? inputZ / planarLen : 0;
104721
- // Effective speed with modifiers
104722
- const speed = this._getEffectiveSpeed();
104723
- // Compute direction vectors from current yaw/pitch
104724
- const yawRad = (this._yaw * Math.PI) / 180;
104725
- const pitchRad = (this._pitch * Math.PI) / 180;
104726
- // Forward vector (normalized)
104727
- const fwd = new Vec3(Math.cos(pitchRad) * Math.sin(yawRad), Math.sin(pitchRad), Math.cos(pitchRad) * Math.cos(yawRad));
104728
- // Right vector (perpendicular to yaw on horizontal plane)
104729
- const rightVec = new Vec3(Math.sin(yawRad - Math.PI / 2), 0, Math.cos(yawRad - Math.PI / 2));
104730
- // Up vector (world up)
104731
- const upVec = Vec3.UP.clone();
104764
+ // Effective speed with modifiers (scaled x2 as requested)
104765
+ const speed = this._getEffectiveSpeed() * 2;
104766
+ // Compute direction vectors from the camera entity so movement matches look direction
104767
+ const entity = this.entity;
104768
+ const fwd = entity?.forward && entity.forward.clone
104769
+ ? entity.forward.clone()
104770
+ : entity?.forward
104771
+ ? new Vec3(entity.forward.x, entity.forward.y, entity.forward.z)
104772
+ : new Vec3(0, 0, -1);
104773
+ const rightVec = entity?.right && entity.right.clone
104774
+ ? entity.right.clone()
104775
+ : entity?.right
104776
+ ? new Vec3(entity.right.x, entity.right.y, entity.right.z)
104777
+ : new Vec3(1, 0, 0);
104778
+ const upVec = entity?.up && entity.up.clone
104779
+ ? entity.up.clone()
104780
+ : entity?.up
104781
+ ? new Vec3(entity.up.x, entity.up.y, entity.up.z)
104782
+ : Vec3.UP.clone();
104732
104783
  // Target velocity in world space
104733
104784
  const target = new Vec3(0, 0, 0);
104734
104785
  target.add(fwd.mulScalar(nz * speed));
@@ -104763,7 +104814,9 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
104763
104814
  }
104764
104815
  };
104765
104816
  FlyCamera.prototype._applyConstraints = function () {
104766
- if (!this.enableCollision && this.minHeight == null && this.maxHeight == null) {
104817
+ if (!this.enableCollision &&
104818
+ this.minHeight == null &&
104819
+ this.maxHeight == null) {
104767
104820
  return;
104768
104821
  }
104769
104822
  const pos = this.entity.getPosition
@@ -140037,7 +140090,7 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140037
140090
  this.focus(result.entity);
140038
140091
  setTimeout(() => {
140039
140092
  const info = this.debugInfo();
140040
- console.log(info);
140093
+ // no-op
140041
140094
  if (!info.boundsRadius) {
140042
140095
  this.focus(result.entity);
140043
140096
  }
@@ -140833,6 +140886,7 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140833
140886
  if (!this.app)
140834
140887
  return;
140835
140888
  const canvas = this.app.graphicsDevice.canvas;
140889
+ // debug removed
140836
140890
  // Ensure canvas and its parent fill available space by default (non-destructive)
140837
140891
  if (!canvas.style.display)
140838
140892
  canvas.style.display = 'block';
@@ -140846,18 +140900,22 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140846
140900
  ps.width = '100%';
140847
140901
  if (!ps.height)
140848
140902
  ps.height = '100%';
140903
+ // debug removed
140849
140904
  }
140850
140905
  // Helper: apply resolution and notify dependents
140851
140906
  const applyResolution = (width, height) => {
140852
140907
  if (canvas.width === width && canvas.height === height)
140853
140908
  return;
140909
+ // track previous size if needed in future
140854
140910
  canvas.width = width;
140855
140911
  canvas.height = height;
140856
140912
  this.app.resizeCanvas(width, height);
140913
+ // debug removed
140857
140914
  if (this._orbit &&
140858
140915
  this._orbit.navigationCube &&
140859
140916
  typeof this._orbit.navigationCube.onCanvasResize === 'function') {
140860
140917
  this._orbit.navigationCube.onCanvasResize();
140918
+ // debug removed
140861
140919
  }
140862
140920
  };
140863
140921
  // Fallback: compute from CSS box * devicePixelRatio
@@ -140866,6 +140924,7 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140866
140924
  const pixelRatio = window.devicePixelRatio || 1;
140867
140925
  const width = Math.floor(rect.width * pixelRatio);
140868
140926
  const height = Math.floor(rect.height * pixelRatio);
140927
+ // debug removed
140869
140928
  applyResolution(width, height);
140870
140929
  };
140871
140930
  // Prefer ResizeObserver entry data (devicePixelContentBoxSize when available)
@@ -140876,6 +140935,7 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140876
140935
  if (dpcb && dpcb.length > 0) {
140877
140936
  width = Math.ceil(dpcb[0].inlineSize);
140878
140937
  height = Math.ceil(dpcb[0].blockSize);
140938
+ // debug removed
140879
140939
  }
140880
140940
  else {
140881
140941
  const cbs = entry.contentBoxSize;
@@ -140883,6 +140943,7 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140883
140943
  const pixelRatio = window.devicePixelRatio || 1;
140884
140944
  width = Math.ceil(cbs[0].inlineSize * pixelRatio);
140885
140945
  height = Math.ceil(cbs[0].blockSize * pixelRatio);
140946
+ // debug removed
140886
140947
  }
140887
140948
  }
140888
140949
  if (width !== null && height !== null) {
@@ -140894,6 +140955,7 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140894
140955
  };
140895
140956
  // Initial resolution setup
140896
140957
  updateResolution();
140958
+ // debug removed
140897
140959
  // Set up resize observer for automatic resolution updates
140898
140960
  if (window.ResizeObserver) {
140899
140961
  this._resizeObserver = new ResizeObserver((entries) => {
@@ -140922,16 +140984,53 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140922
140984
  }
140923
140985
  });
140924
140986
  // Observe both the canvas and its parent container for resize events
140925
- this._resizeObserver.observe(canvas);
140987
+ try {
140988
+ // Prefer device-pixel-content-box for pixel-perfect canvas sizing
140989
+ ;
140990
+ this._resizeObserver.observe(canvas, {
140991
+ box: 'device-pixel-content-box',
140992
+ });
140993
+ }
140994
+ catch {
140995
+ this._resizeObserver.observe(canvas);
140996
+ }
140997
+ // debug removed
140926
140998
  if (canvas.parentElement) {
140927
140999
  this._resizeObserver.observe(canvas.parentElement);
141000
+ // debug removed
140928
141001
  }
140929
141002
  }
140930
141003
  // Additionally listen to window resize to handle viewport changes
140931
141004
  // (e.g., when resizing dev tools). This mirrors superSplat behavior
140932
141005
  // and ensures the canvas always matches the available space.
140933
- window.addEventListener('resize', updateResolution);
140934
- this._resizeHandler = updateResolution;
141006
+ const onWindowResize = () => {
141007
+ updateResolution();
141008
+ };
141009
+ window.addEventListener('resize', onWindowResize);
141010
+ this._resizeHandler = onWindowResize;
141011
+ }
141012
+ /**
141013
+ * Force recalculation of canvas resolution based on current DOM size.
141014
+ * Useful when container layout changes are not captured by the observer.
141015
+ */
141016
+ updateCanvasResolution() {
141017
+ if (!this.app)
141018
+ return;
141019
+ const canvas = this.app.graphicsDevice.canvas;
141020
+ const rect = canvas.getBoundingClientRect();
141021
+ const pixelRatio = window.devicePixelRatio || 1;
141022
+ const width = Math.floor(rect.width * pixelRatio);
141023
+ const height = Math.floor(rect.height * pixelRatio);
141024
+ if (canvas.width !== width || canvas.height !== height) {
141025
+ canvas.width = width;
141026
+ canvas.height = height;
141027
+ this.app.resizeCanvas(width, height);
141028
+ if (this._orbit &&
141029
+ this._orbit.navigationCube &&
141030
+ typeof this._orbit.navigationCube.onCanvasResize === 'function') {
141031
+ this._orbit.navigationCube.onCanvasResize();
141032
+ }
141033
+ }
140935
141034
  }
140936
141035
  }
140937
141036
 
@@ -140962,6 +141061,7 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
140962
141061
  this._config = { ...DEFAULT_CONFIG };
140963
141062
  this._isInitialized = false;
140964
141063
  this._isDestroyed = false;
141064
+ this._hostResizeObserver = null;
140965
141065
  }
140966
141066
  // Observed attributes that trigger attributeChangedCallback
140967
141067
  static get observedAttributes() {
@@ -141309,6 +141409,22 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
141309
141409
  this._canvas.setAttribute('role', 'img');
141310
141410
  this._canvas.setAttribute('aria-label', '3D Splat Viewer');
141311
141411
  this.appendChild(this._canvas);
141412
+ // Observe host element for size changes and force canvas resolution update
141413
+ try {
141414
+ if (window.ResizeObserver) {
141415
+ this._hostResizeObserver = new ResizeObserver(() => {
141416
+ // Force resolution update in core to sync canvas with host/container
141417
+ try {
141418
+ this._core?.updateCanvasResolution();
141419
+ }
141420
+ catch { }
141421
+ });
141422
+ this._hostResizeObserver.observe(this);
141423
+ if (this.parentElement)
141424
+ this._hostResizeObserver.observe(this.parentElement);
141425
+ }
141426
+ }
141427
+ catch { }
141312
141428
  // Update configuration from attributes
141313
141429
  this._updateConfigFromAttributes();
141314
141430
  // Initialize the core viewer
@@ -141365,6 +141481,11 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
141365
141481
  return;
141366
141482
  }
141367
141483
  try {
141484
+ // Disconnect host resize observer
141485
+ if (this._hostResizeObserver) {
141486
+ this._hostResizeObserver.disconnect();
141487
+ this._hostResizeObserver = null;
141488
+ }
141368
141489
  // Clean up core
141369
141490
  if (this._core) {
141370
141491
  this._core.destroy();
@@ -141484,7 +141605,6 @@ fn fragmentMain(input: FragmentInput) -> FragmentOutput {
141484
141605
  return;
141485
141606
  }
141486
141607
  // Stats handling will be implemented when we add stats functionality
141487
- console.log('Stats change requested:', this.enableStats);
141488
141608
  }
141489
141609
  /**
141490
141610
  * Set up event listeners for the core viewer