@flighthq/camera 0.3.0-next.906.07cea63 → 0.3.0

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.
package/dist/camera.d.ts CHANGED
@@ -6,5 +6,5 @@ export declare function setCamera3DAspect(camera: Camera3D, aspect: number): voi
6
6
  export declare function setCamera3DJitter(camera: Camera3D, x: number, y: number): void;
7
7
  export declare function setCamera3DViewMatrix4FromLookAt(camera: Camera3D, eye: Readonly<Vector3Like>, target: Readonly<Vector3Like>, up: Readonly<Vector3Like>): void;
8
8
  export declare function setCamera3DViewMatrix4FromMatrix4(camera: Camera3D, view: Readonly<Matrix4Like>): void;
9
- export declare function updateCamera3DInverseViewProjection(camera: Camera3D, aspect: number): boolean;
9
+ export declare function updateCamera3DInverseViewProjection(camera: Readonly<Camera3D>, aspect: number): boolean;
10
10
  //# sourceMappingURL=camera.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAUpG,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,eAAe,CAAC,GAAG,QAAQ,CASxE;AAUD,wBAAgB,uCAAuC,CACrD,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAGT;AAQD,wBAAgB,gCAAgC,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGnH;AAQD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOxE;AAID,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAG9E;AAOD,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,QAAQ,EAChB,GAAG,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC1B,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC7B,EAAE,EAAE,QAAQ,CAAC,WAAW,CAAC,GACxB,IAAI,CAEN;AAID,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,IAAI,CAErG;AAQD,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAO7F"}
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAUpG,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,eAAe,CAAC,GAAG,QAAQ,CASxE;AASD,wBAAgB,uCAAuC,CACrD,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAGT;AAQD,wBAAgB,gCAAgC,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAInH;AAQD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOxE;AAID,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAG9E;AAOD,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,QAAQ,EAChB,GAAG,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC1B,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC7B,EAAE,EAAE,QAAQ,CAAC,WAAW,CAAC,GACxB,IAAI,CAEN;AAID,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,IAAI,CAErG;AAMD,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAIvG"}
package/dist/camera.js CHANGED
@@ -3,10 +3,10 @@ import { createMatrix4, createVector2, inverseMatrix4, multiplyMatrix4, setMatri
3
3
  import { setProjectionMatrix4 } from './projection';
4
4
  // Allocates a 3D camera. The camera stores its projection descriptor, a world->view Matrix4
5
5
  // (`view`, initialized to identity), the clip-plane distances `near`/`far`, the per-frame
6
- // sub-pixel NDC `jitter` (consumed by TAA, initialized to zero), and a cached
7
- // `inverseViewProjection` (consumed by TAA / velocity / fog / depth-of-field, initialized to
8
- // identity). The view matrix is canonical: the camera has no separate Transform3D — a Matrix4 is
9
- // the only world->view representation.
6
+ // sub-pixel NDC `jitter` (applied to every projection, initialized to zero), and the last
7
+ // `inverseViewProjection` computed for a reconstruction pass (initialized to identity). The view
8
+ // matrix is canonical: the camera has no separate Transform3D — a Matrix4 is the only world->view
9
+ // representation.
10
10
  export function createCamera3D(opts) {
11
11
  return createEntity({
12
12
  far: opts.far,
@@ -19,12 +19,11 @@ export function createCamera3D(opts) {
19
19
  }
20
20
  // Writes the inverse of the camera's view-projection matrix into `out` and returns true, or
21
21
  // returns false (writing NaN into `out`) when the view-projection is non-invertible. `aspect`
22
- // is the viewport width / height. This is the matrix the existing TAA / velocity / fog /
23
- // depth-of-field effects consume to reconstruct world position from NDC.
22
+ // is the viewport width / height. Skybox and other world-position reconstruction passes use this
23
+ // calculation to refresh Camera3D.inverseViewProjection immediately before consuming it.
24
24
  //
25
25
  // Reads camera fields into a scratch matrix before writing `out`, so it is safe even if `out`
26
- // aliases the camera's own `inverseViewProjection` or `view`. Use
27
- // `updateCamera3DInverseViewProjection` instead to safely update the cached field.
26
+ // aliases the camera's own `inverseViewProjection` or `view`.
28
27
  export function getCamera3DInverseViewProjectionMatrix4(out, camera, aspect) {
29
28
  getCamera3DViewProjectionMatrix4(__scratchViewProjection, camera, aspect);
30
29
  return inverseMatrix4(out, __scratchViewProjection);
@@ -37,6 +36,7 @@ export function getCamera3DInverseViewProjectionMatrix4(out, camera, aspect) {
37
36
  // aliases the camera's own `view`.
38
37
  export function getCamera3DViewProjectionMatrix4(out, camera, aspect) {
39
38
  setProjectionMatrix4(__scratchProjection, camera.projection, aspect, camera.near, camera.far);
39
+ applyCamera3DProjectionJitter(__scratchProjection, camera.jitter.x, camera.jitter.y);
40
40
  multiplyMatrix4(out, __scratchProjection, camera.view);
41
41
  }
42
42
  // Sets the viewport aspect ratio (width / height) on the camera's projection, in place. For a
@@ -53,8 +53,8 @@ export function setCamera3DAspect(camera, aspect) {
53
53
  }
54
54
  projection.halfWidth = projection.halfHeight * aspect;
55
55
  }
56
- // Sets the camera's per-frame sub-pixel jitter offset (in NDC), in place. TAA reads this when
57
- // building the jittered projection matrix.
56
+ // Sets the camera's per-frame sub-pixel jitter offset (in NDC), in place. Every view-projection
57
+ // helper applies it to clip X/Y before the perspective divide.
58
58
  export function setCamera3DJitter(camera, x, y) {
59
59
  camera.jitter.x = x;
60
60
  camera.jitter.y = y;
@@ -72,20 +72,30 @@ export function setCamera3DViewMatrix4FromLookAt(camera, eye, target, up) {
72
72
  export function setCamera3DViewMatrix4FromMatrix4(camera, view) {
73
73
  camera.view.m.set(view.m);
74
74
  }
75
- // Recomputes and stores the inverse view-projection into camera.inverseViewProjection for the
76
- // given aspect. Returns true on success or false when the matrix is non-invertible (leaving the
77
- // cached field untouched). `aspect` is viewport width / height.
78
- //
79
- // Call this once per frame after setting the view matrix and before any effects that read
80
- // camera.inverseViewProjection (TAA, velocity, fog, depth-of-field).
75
+ // Refreshes the camera's required inverse-view-projection cache for the supplied viewport aspect.
76
+ // Returns true on success or false when projection×view is non-invertible, leaving the prior valid
77
+ // cache untouched. Reconstruction consumers call this immediately before reading the field so it
78
+ // cannot silently drift after mutable camera inputs change.
81
79
  export function updateCamera3DInverseViewProjection(camera, aspect) {
82
- // Write into a scratch first so the cache is never clobbered with NaN on failure.
83
80
  const ok = getCamera3DInverseViewProjectionMatrix4(__scratchInverse, camera, aspect);
84
- if (ok) {
81
+ if (ok)
85
82
  camera.inverseViewProjection.m.set(__scratchInverse.m);
86
- }
87
83
  return ok;
88
84
  }
85
+ // Applies an NDC translation to a projection matrix: clip.xy += jitter * clip.w. Expressing it as
86
+ // row operations handles perspective (offset in m[8]/m[9]) and orthographic (m[12]/m[13]) matrices
87
+ // uniformly.
88
+ function applyCamera3DProjectionJitter(out, x, y) {
89
+ const m = out.m;
90
+ m[0] += x * m[3];
91
+ m[4] += x * m[7];
92
+ m[8] += x * m[11];
93
+ m[12] += x * m[15];
94
+ m[1] += y * m[3];
95
+ m[5] += y * m[7];
96
+ m[9] += y * m[11];
97
+ m[13] += y * m[15];
98
+ }
89
99
  // Scratch matrices reused by the view-projection helpers. Single-threaded; not re-entrant.
90
100
  const __scratchInverse = createMatrix4();
91
101
  const __scratchProjection = createMatrix4();
@@ -1 +1 @@
1
- {"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"camera.js","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EACL,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,4FAA4F;AAC5F,0FAA0F;AAC1F,0FAA0F;AAC1F,iGAAiG;AACjG,kGAAkG;AAClG,kBAAkB;AAClB,MAAM,UAAU,cAAc,CAAC,IAA+B;IAC5D,OAAO,YAAY,CAAC;QAClB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,qBAAqB,EAAE,aAAa,EAAE;QACtC,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,IAAI,EAAE,aAAa,EAAE;KACtB,CAAC,CAAC;AACL,CAAC;AAED,4FAA4F;AAC5F,8FAA8F;AAC9F,iGAAiG;AACjG,yFAAyF;AACzF,EAAE;AACF,8FAA8F;AAC9F,8DAA8D;AAC9D,MAAM,UAAU,uCAAuC,CACrD,GAAgB,EAChB,MAA0B,EAC1B,MAAc;IAEd,gCAAgC,CAAC,uBAAuB,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1E,OAAO,cAAc,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC;AACtD,CAAC;AAED,6FAA6F;AAC7F,gGAAgG;AAChG,UAAU;AACV,EAAE;AACF,8FAA8F;AAC9F,mCAAmC;AACnC,MAAM,UAAU,gCAAgC,CAAC,GAAgB,EAAE,MAA0B,EAAE,MAAc;IAC3G,oBAAoB,CAAC,mBAAmB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC9F,6BAA6B,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACrF,eAAe,CAAC,GAAG,EAAE,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AACzD,CAAC;AAED,8FAA8F;AAC9F,iGAAiG;AACjG,iGAAiG;AACjG,+FAA+F;AAC/F,gGAAgG;AAChG,iDAAiD;AACjD,MAAM,UAAU,iBAAiB,CAAC,MAAgB,EAAE,MAAc;IAChE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,IAAI,UAAU,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACtC,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,UAAU,CAAC,SAAS,GAAG,UAAU,CAAC,UAAU,GAAG,MAAM,CAAC;AACxD,CAAC;AAED,gGAAgG;AAChG,+DAA+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,MAAgB,EAAE,CAAS,EAAE,CAAS;IACtE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACpB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC;AAED,iGAAiG;AACjG,gGAAgG;AAChG,4BAA4B;AAC5B,EAAE;AACF,4FAA4F;AAC5F,MAAM,UAAU,gCAAgC,CAC9C,MAAgB,EAChB,GAA0B,EAC1B,MAA6B,EAC7B,EAAyB;IAEzB,gBAAgB,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,kGAAkG;AAClG,qFAAqF;AACrF,MAAM,UAAU,iCAAiC,CAAC,MAAgB,EAAE,IAA2B;IAC7F,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,kGAAkG;AAClG,mGAAmG;AACnG,iGAAiG;AACjG,4DAA4D;AAC5D,MAAM,UAAU,mCAAmC,CAAC,MAA0B,EAAE,MAAc;IAC5F,MAAM,EAAE,GAAG,uCAAuC,CAAC,gBAAgB,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACrF,IAAI,EAAE;QAAE,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC/D,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,kGAAkG;AAClG,mGAAmG;AACnG,aAAa;AACb,SAAS,6BAA6B,CAAC,GAAgB,EAAE,CAAS,EAAE,CAAS;IAC3E,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AACrB,CAAC;AAED,2FAA2F;AAC3F,MAAM,gBAAgB,GAAG,aAAa,EAAE,CAAC;AACzC,MAAM,mBAAmB,GAAG,aAAa,EAAE,CAAC;AAC5C,MAAM,uBAAuB,GAAG,aAAa,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { configureDirectionalShadowCamera3D, configureDirectionalShadowCamera3DTightFit, createCamera2D, createCamera3D, createOrthographicProjection, createPerspectiveProjection, getCamera2DParallaxPoint, getCamera2DViewMatrix, getCamera2DVisibleBounds, getCamera3DForward, getCamera3DFrustum, getCamera3DFrustumCorners, getCamera3DInverseViewProjectionMatrix4, getCamera3DLinearDepth, getCamera3DPosition, getCamera3DRayThroughBoundingSphere, getCamera3DRight, getCamera3DScreenToWorldRay, getCamera3DUp, getCamera3DViewProjectionMatrix4, getCamera3DViewSpaceZ, getCamera3DWorldToScreen, intersectCamera3DRayWithPlane, isBoxInCamera3DFrustum, isOrthographicProjection, isPerspectiveProjection, isPointInCamera3DFrustum, isSphereInCamera3DFrustum, projectCamera2DPoint, setCamera3DAspect, setCamera3DJitter, setCamera3DViewMatrix4FromLookAt, setCamera3DViewMatrix4FromMatrix4, setProjectionMatrix4, unprojectCamera2DPoint, updateCamera3DInverseViewProjection, zoomCamera2DAtScreenPoint, } from './contract';
1
+ export { configureDirectionalShadowCamera3D, configureDirectionalShadowCamera3DTightFit, createCamera2D, createCamera3D, createOrthographicProjection, createPerspectiveProjection, getCamera2DParallaxPoint, getCamera2DViewMatrix, getCamera2DVisibleBounds, getCamera3DForward, getCamera3DFrustum, getCamera3DFrustumCorners, getCamera3DInverseViewProjectionMatrix4, getCamera3DLinearDepth, getCamera3DPosition, getCamera3DRayThroughBoundingSphere, getCamera3DRight, getCamera3DScreenToWorldRay, getCamera3DUp, getCamera3DViewProjectionMatrix4, getCamera3DViewSpaceZ, getCamera3DWorldToScreen, getOrthographicProjectionTexelSize, intersectCamera3DRayWithPlane, isBoxInCamera3DFrustum, isOrthographicProjection, isPerspectiveProjection, isPointInCamera3DFrustum, isSphereInCamera3DFrustum, projectCamera2DPoint, setCamera3DAspect, setCamera3DJitter, setCamera3DViewMatrix4FromLookAt, setCamera3DViewMatrix4FromMatrix4, setProjectionMatrix4, unprojectCamera2DPoint, updateCamera3DInverseViewProjection, zoomCamera2DAtScreenPoint, } from './contract';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,EAClC,0CAA0C,EAC1C,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,uCAAuC,EACvC,sBAAsB,EACtB,mBAAmB,EACnB,mCAAmC,EACnC,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,EACb,gCAAgC,EAChC,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,gCAAgC,EAChC,iCAAiC,EACjC,oBAAoB,EACpB,sBAAsB,EACtB,mCAAmC,EACnC,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,EAClC,0CAA0C,EAC1C,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,uCAAuC,EACvC,sBAAsB,EACtB,mBAAmB,EACnB,mCAAmC,EACnC,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,EACb,gCAAgC,EAChC,qBAAqB,EACrB,wBAAwB,EACxB,kCAAkC,EAClC,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,gCAAgC,EAChC,iCAAiC,EACjC,oBAAoB,EACpB,sBAAsB,EACtB,mCAAmC,EACnC,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { configureDirectionalShadowCamera3D, configureDirectionalShadowCamera3DTightFit, createCamera2D, createCamera3D, createOrthographicProjection, createPerspectiveProjection, getCamera2DParallaxPoint, getCamera2DViewMatrix, getCamera2DVisibleBounds, getCamera3DForward, getCamera3DFrustum, getCamera3DFrustumCorners, getCamera3DInverseViewProjectionMatrix4, getCamera3DLinearDepth, getCamera3DPosition, getCamera3DRayThroughBoundingSphere, getCamera3DRight, getCamera3DScreenToWorldRay, getCamera3DUp, getCamera3DViewProjectionMatrix4, getCamera3DViewSpaceZ, getCamera3DWorldToScreen, intersectCamera3DRayWithPlane, isBoxInCamera3DFrustum, isOrthographicProjection, isPerspectiveProjection, isPointInCamera3DFrustum, isSphereInCamera3DFrustum, projectCamera2DPoint, setCamera3DAspect, setCamera3DJitter, setCamera3DViewMatrix4FromLookAt, setCamera3DViewMatrix4FromMatrix4, setProjectionMatrix4, unprojectCamera2DPoint, updateCamera3DInverseViewProjection, zoomCamera2DAtScreenPoint, } from './contract';
1
+ export { configureDirectionalShadowCamera3D, configureDirectionalShadowCamera3DTightFit, createCamera2D, createCamera3D, createOrthographicProjection, createPerspectiveProjection, getCamera2DParallaxPoint, getCamera2DViewMatrix, getCamera2DVisibleBounds, getCamera3DForward, getCamera3DFrustum, getCamera3DFrustumCorners, getCamera3DInverseViewProjectionMatrix4, getCamera3DLinearDepth, getCamera3DPosition, getCamera3DRayThroughBoundingSphere, getCamera3DRight, getCamera3DScreenToWorldRay, getCamera3DUp, getCamera3DViewProjectionMatrix4, getCamera3DViewSpaceZ, getCamera3DWorldToScreen, getOrthographicProjectionTexelSize, intersectCamera3DRayWithPlane, isBoxInCamera3DFrustum, isOrthographicProjection, isPerspectiveProjection, isPointInCamera3DFrustum, isSphereInCamera3DFrustum, projectCamera2DPoint, setCamera3DAspect, setCamera3DJitter, setCamera3DViewMatrix4FromLookAt, setCamera3DViewMatrix4FromMatrix4, setProjectionMatrix4, unprojectCamera2DPoint, updateCamera3DInverseViewProjection, zoomCamera2DAtScreenPoint, } from './contract';
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,EAClC,0CAA0C,EAC1C,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,uCAAuC,EACvC,sBAAsB,EACtB,mBAAmB,EACnB,mCAAmC,EACnC,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,EACb,gCAAgC,EAChC,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,gCAAgC,EAChC,iCAAiC,EACjC,oBAAoB,EACpB,sBAAsB,EACtB,mCAAmC,EACnC,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,EAClC,0CAA0C,EAC1C,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,uCAAuC,EACvC,sBAAsB,EACtB,mBAAmB,EACnB,mCAAmC,EACnC,gBAAgB,EAChB,2BAA2B,EAC3B,aAAa,EACb,gCAAgC,EAChC,qBAAqB,EACrB,wBAAwB,EACxB,kCAAkC,EAClC,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,gCAAgC,EAChC,iCAAiC,EACjC,oBAAoB,EACpB,sBAAsB,EACtB,mCAAmC,EACnC,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
@@ -2,6 +2,7 @@ import type { Matrix4Like } from '@flighthq/types/contract';
2
2
  import type { OrthographicProjection, OrthographicProjectionOptions, PerspectiveProjection, PerspectiveProjectionOptions, Projection } from '@flighthq/types/contract';
3
3
  export declare function createOrthographicProjection(opts: Readonly<OrthographicProjectionOptions>): OrthographicProjection;
4
4
  export declare function createPerspectiveProjection(opts: Readonly<PerspectiveProjectionOptions>): PerspectiveProjection;
5
+ export declare function getOrthographicProjectionTexelSize(projection: Readonly<OrthographicProjection>, pixelWidth: number, pixelHeight: number): number;
5
6
  export declare function isOrthographicProjection(projection: Readonly<Projection>): projection is OrthographicProjection;
6
7
  export declare function isPerspectiveProjection(projection: Readonly<Projection>): projection is PerspectiveProjection;
7
8
  export declare function setProjectionMatrix4(out: Matrix4Like, projection: Readonly<Projection>, aspect: number, near: number, far: number): void;
@@ -1 +1 @@
1
- {"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EACV,sBAAsB,EACtB,6BAA6B,EAC7B,qBAAqB,EACrB,4BAA4B,EAC5B,UAAU,EACX,MAAM,0BAA0B,CAAC;AAKlC,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,QAAQ,CAAC,6BAA6B,CAAC,GAAG,sBAAsB,CAMlH;AAID,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,QAAQ,CAAC,4BAA4B,CAAC,GAAG,qBAAqB,CAM/G;AAGD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,IAAI,sBAAsB,CAE/G;AAGD,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,IAAI,qBAAqB,CAE7G;AAUD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,IAAI,CAWN"}
1
+ {"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EACV,sBAAsB,EACtB,6BAA6B,EAC7B,qBAAqB,EACrB,4BAA4B,EAC5B,UAAU,EACX,MAAM,0BAA0B,CAAC;AAKlC,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,QAAQ,CAAC,6BAA6B,CAAC,GAAG,sBAAsB,CAMlH;AAID,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,QAAQ,CAAC,4BAA4B,CAAC,GAAG,qBAAqB,CAM/G;AAKD,wBAAgB,kCAAkC,CAChD,UAAU,EAAE,QAAQ,CAAC,sBAAsB,CAAC,EAC5C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAGD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,IAAI,sBAAsB,CAE/G;AAGD,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,IAAI,qBAAqB,CAE7G;AAUD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,IAAI,CAWN"}
@@ -18,6 +18,12 @@ export function createPerspectiveProjection(opts) {
18
18
  kind: 'perspective',
19
19
  };
20
20
  }
21
+ // Returns the larger world-space footprint of one texel across an orthographic projection. Using the
22
+ // larger axis gives receiver offsets one conservative, aspect-independent unit even when the shadow
23
+ // map or light projection is not square. `pixelWidth`/`pixelHeight` must be positive.
24
+ export function getOrthographicProjectionTexelSize(projection, pixelWidth, pixelHeight) {
25
+ return Math.max((projection.halfWidth * 2) / pixelWidth, (projection.halfHeight * 2) / pixelHeight);
26
+ }
21
27
  // True when the projection is an orthographic descriptor. Narrows the discriminated union.
22
28
  export function isOrthographicProjection(projection) {
23
29
  return projection.kind === 'orthographic';
@@ -1 +1 @@
1
- {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAU5F,0FAA0F;AAC1F,wFAAwF;AACxF,wEAAwE;AACxE,MAAM,UAAU,4BAA4B,CAAC,IAA6C;IACxF,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,IAAI,EAAE,cAAc;KACrB,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,gGAAgG;AAChG,MAAM,UAAU,2BAA2B,CAAC,IAA4C;IACtF,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,aAAa;KACpB,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,wBAAwB,CAAC,UAAgC;IACvE,OAAO,UAAU,CAAC,IAAI,KAAK,cAAc,CAAC;AAC5C,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,uBAAuB,CAAC,UAAgC;IACtE,OAAO,UAAU,CAAC,IAAI,KAAK,aAAa,CAAC;AAC3C,CAAC;AAED,iGAAiG;AACjG,uFAAuF;AACvF,iGAAiG;AACjG,iGAAiG;AACjG,6CAA6C;AAC7C,EAAE;AACF,4FAA4F;AAC5F,yCAAyC;AACzC,MAAM,UAAU,oBAAoB,CAClC,GAAgB,EAChB,UAAgC,EAChC,MAAc,EACd,IAAY,EACZ,GAAW;IAEX,IAAI,UAAU,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACtC,0FAA0F;QAC1F,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;QACpD,qBAAqB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;IACvC,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC;IACzC,sBAAsB,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AACzF,CAAC"}
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAU5F,0FAA0F;AAC1F,wFAAwF;AACxF,wEAAwE;AACxE,MAAM,UAAU,4BAA4B,CAAC,IAA6C;IACxF,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,IAAI,EAAE,cAAc;KACrB,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,gGAAgG;AAChG,MAAM,UAAU,2BAA2B,CAAC,IAA4C;IACtF,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,aAAa;KACpB,CAAC;AACJ,CAAC;AAED,qGAAqG;AACrG,oGAAoG;AACpG,sFAAsF;AACtF,MAAM,UAAU,kCAAkC,CAChD,UAA4C,EAC5C,UAAkB,EAClB,WAAmB;IAEnB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC,UAAU,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC;AACtG,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,wBAAwB,CAAC,UAAgC;IACvE,OAAO,UAAU,CAAC,IAAI,KAAK,cAAc,CAAC;AAC5C,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,uBAAuB,CAAC,UAAgC;IACtE,OAAO,UAAU,CAAC,IAAI,KAAK,aAAa,CAAC;AAC3C,CAAC;AAED,iGAAiG;AACjG,uFAAuF;AACvF,iGAAiG;AACjG,iGAAiG;AACjG,6CAA6C;AAC7C,EAAE;AACF,4FAA4F;AAC5F,yCAAyC;AACzC,MAAM,UAAU,oBAAoB,CAClC,GAAgB,EAChB,UAAgC,EAChC,MAAc,EACd,IAAY,EACZ,GAAW;IAEX,IAAI,UAAU,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACtC,0FAA0F;QAC1F,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;QACpD,qBAAqB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;IACvC,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC;IACzC,sBAAsB,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AACzF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "name": "@flighthq/camera",
3
- "version": "0.3.0-next.906.07cea63",
3
+ "version": "0.3.0",
4
+ "author": "Joshua Granick and other contributors",
5
+ "license": "MIT",
4
6
  "repository": {
5
7
  "type": "git",
6
8
  "url": "https://github.com/flighthq/flight.git",
@@ -36,9 +38,9 @@
36
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
37
39
  },
38
40
  "dependencies": {
39
- "@flighthq/entity": "0.3.0-next.906.07cea63",
40
- "@flighthq/geometry": "0.3.0-next.906.07cea63",
41
- "@flighthq/types": "0.3.0-next.906.07cea63"
41
+ "@flighthq/entity": "0.3.0",
42
+ "@flighthq/geometry": "0.3.0",
43
+ "@flighthq/types": "0.3.0"
42
44
  },
43
45
  "devDependencies": {
44
46
  "typescript": "^5.3.0"
@@ -89,6 +89,23 @@ describe('getCamera3DViewProjectionMatrix4', () => {
89
89
  }
90
90
  });
91
91
 
92
+ it('applies the camera jitter as an NDC projection offset', () => {
93
+ const camera = createCamera3D({
94
+ far: 100,
95
+ near: 0.1,
96
+ projection: createPerspectiveProjection({ aspect: 1, fovY: 1 }),
97
+ });
98
+ const base = createMatrix4();
99
+ getCamera3DViewProjectionMatrix4(base, camera, 1);
100
+
101
+ setCamera3DJitter(camera, 0.25, -0.5);
102
+ const jittered = createMatrix4();
103
+ getCamera3DViewProjectionMatrix4(jittered, camera, 1);
104
+
105
+ expect(jittered.m[8]).toBeCloseTo(base.m[8] - 0.25);
106
+ expect(jittered.m[9]).toBeCloseTo(base.m[9] + 0.5);
107
+ });
108
+
92
109
  it('is safe when out aliases the camera view', () => {
93
110
  const projection = createPerspectiveProjection({ aspect: 1, fovY: 1 });
94
111
  const camera = createCamera3D({ far: 100, near: 0.1, projection });
@@ -181,35 +198,32 @@ describe('setCamera3DViewMatrix4FromMatrix4', () => {
181
198
  });
182
199
 
183
200
  describe('updateCamera3DInverseViewProjection', () => {
184
- it('stores the inverse view-projection into camera.inverseViewProjection and returns true', () => {
185
- const projection = createPerspectiveProjection({ aspect: 1, fovY: 1 });
186
- const camera = createCamera3D({ far: 100, near: 0.1, projection });
187
- setCamera3DViewMatrix4FromLookAt(camera, createVector3(1, 2, 5), createVector3(0, 0, 0), createVector3(0, 1, 0));
188
-
189
- const result = updateCamera3DInverseViewProjection(camera, 1.5);
190
- expect(result).toBe(true);
201
+ it('refreshes the required cache from the jittered projection', () => {
202
+ const camera = createCamera3D({
203
+ far: 100,
204
+ near: 0.1,
205
+ projection: createPerspectiveProjection({ aspect: 1, fovY: 1 }),
206
+ });
207
+ setCamera3DJitter(camera, 0.25, -0.5);
208
+ expect(updateCamera3DInverseViewProjection(camera, 1.5)).toBe(true);
191
209
 
192
- // Compute expected inverse independently.
193
210
  const vp = createMatrix4();
194
211
  getCamera3DViewProjectionMatrix4(vp, camera, 1.5);
195
212
  const expected = createMatrix4();
196
213
  inverseMatrix4(expected, vp);
197
-
198
- for (let i = 0; i < 16; i++) {
199
- expect(camera.inverseViewProjection.m[i]).toBeCloseTo(expected.m[i]);
200
- }
214
+ for (let i = 0; i < 16; i++) expect(camera.inverseViewProjection.m[i]).toBeCloseTo(expected.m[i]);
201
215
  });
202
216
 
203
- it('returns false and leaves inverseViewProjection untouched for a non-invertible matrix', () => {
204
- const projection = createPerspectiveProjection({ aspect: 1, fovY: 1 });
205
- const camera = createCamera3D({ far: 100, near: 0.1, projection });
206
- camera.near = 1;
207
- camera.far = 1; // Degenerate.
208
- // Store a known value so we can confirm it was not overwritten.
217
+ it('leaves the last valid cache untouched for a non-invertible projection', () => {
218
+ const camera = createCamera3D({
219
+ far: 100,
220
+ near: 0.1,
221
+ projection: createPerspectiveProjection({ aspect: 1, fovY: 1 }),
222
+ });
209
223
  camera.inverseViewProjection.m[0] = 42;
210
-
211
- const result = updateCamera3DInverseViewProjection(camera, 1);
212
- expect(result).toBe(false);
224
+ camera.near = 1;
225
+ camera.far = 1;
226
+ expect(updateCamera3DInverseViewProjection(camera, 1)).toBe(false);
213
227
  expect(camera.inverseViewProjection.m[0]).toBe(42);
214
228
  });
215
229
  });
@@ -4,6 +4,7 @@ import type { OrthographicProjection, PerspectiveProjection } from '@flighthq/ty
4
4
  import {
5
5
  createOrthographicProjection,
6
6
  createPerspectiveProjection,
7
+ getOrthographicProjectionTexelSize,
7
8
  isOrthographicProjection,
8
9
  isPerspectiveProjection,
9
10
  setProjectionMatrix4,
@@ -32,6 +33,15 @@ describe('createPerspectiveProjection', () => {
32
33
  });
33
34
  });
34
35
 
36
+ describe('getOrthographicProjectionTexelSize', () => {
37
+ it('uses the larger world-space texel footprint for a non-square projection and map', () => {
38
+ const projection = createOrthographicProjection({ halfHeight: 20, halfWidth: 10 });
39
+
40
+ expect(getOrthographicProjectionTexelSize(projection, 100, 100)).toBe(0.4);
41
+ expect(getOrthographicProjectionTexelSize(projection, 50, 200)).toBe(0.4);
42
+ });
43
+ });
44
+
35
45
  describe('isOrthographicProjection', () => {
36
46
  it('is true for orthographic and false for perspective', () => {
37
47
  const ortho = createOrthographicProjection({ halfHeight: 1, halfWidth: 1 });