ris-framework 0.2.0-dev.14 → 0.2.0-dev.16

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.
Files changed (160) hide show
  1. package/CHANGELOG.md +39 -2
  2. package/README.md +8 -6
  3. package/dist/api/IFramework.d.ts +1 -1
  4. package/dist/api/IFramework.d.ts.map +1 -1
  5. package/dist/api/_export.d.ts +3 -0
  6. package/dist/api/_export.d.ts.map +1 -1
  7. package/dist/api/camera/CameraTransformUtilities.d.ts +65 -0
  8. package/dist/api/camera/CameraTransformUtilities.d.ts.map +1 -0
  9. package/dist/api/camera/ICamera.d.ts +36 -2
  10. package/dist/api/camera/ICamera.d.ts.map +1 -1
  11. package/dist/api/camera/OrbitCamera.d.ts +22 -14
  12. package/dist/api/camera/OrbitCamera.d.ts.map +1 -1
  13. package/dist/api/camera/OrthographicCamera.d.ts +33 -11
  14. package/dist/api/camera/OrthographicCamera.d.ts.map +1 -1
  15. package/dist/api/camera/PerspectiveCamera.d.ts +32 -18
  16. package/dist/api/camera/PerspectiveCamera.d.ts.map +1 -1
  17. package/dist/api/content/ContentManager.d.ts.map +1 -1
  18. package/dist/api/lights/AmbientLight.d.ts +7 -2
  19. package/dist/api/lights/AmbientLight.d.ts.map +1 -1
  20. package/dist/api/lights/DirectionalLight.d.ts +18 -4
  21. package/dist/api/lights/DirectionalLight.d.ts.map +1 -1
  22. package/dist/api/lights/ILight.d.ts +18 -0
  23. package/dist/api/lights/ILight.d.ts.map +1 -0
  24. package/dist/api/lights/PointLight.d.ts +17 -4
  25. package/dist/api/lights/PointLight.d.ts.map +1 -1
  26. package/dist/api/lights/_export.d.ts +1 -0
  27. package/dist/api/lights/_export.d.ts.map +1 -1
  28. package/dist/api/material/ForwardBlinnPhongMaterial.d.ts +2 -2
  29. package/dist/api/material/ForwardBlinnPhongMaterial.d.ts.map +1 -1
  30. package/dist/api/material/IMaterial.d.ts +7 -0
  31. package/dist/api/material/IMaterial.d.ts.map +1 -1
  32. package/dist/api/material/IMaterialFactory.d.ts +3 -2
  33. package/dist/api/material/IMaterialFactory.d.ts.map +1 -1
  34. package/dist/api/material/IUnlitMaterialDescriptor.d.ts +16 -0
  35. package/dist/api/material/IUnlitMaterialDescriptor.d.ts.map +1 -0
  36. package/dist/api/material/MaterialFactory.d.ts +3 -2
  37. package/dist/api/material/MaterialFactory.d.ts.map +1 -1
  38. package/dist/api/material/SpriteMaterial.d.ts +3 -0
  39. package/dist/api/material/SpriteMaterial.d.ts.map +1 -1
  40. package/dist/api/material/UnlitMaterial.d.ts +5 -12
  41. package/dist/api/material/UnlitMaterial.d.ts.map +1 -1
  42. package/dist/api/material/_export.d.ts +1 -1
  43. package/dist/api/material/_export.d.ts.map +1 -1
  44. package/dist/api/meshes/IRenderableMesh.d.ts +3 -14
  45. package/dist/api/meshes/IRenderableMesh.d.ts.map +1 -1
  46. package/dist/api/meshes/RenderableMesh.d.ts +5 -20
  47. package/dist/api/meshes/RenderableMesh.d.ts.map +1 -1
  48. package/dist/api/rendering/IGraphicsDevice.d.ts +2 -2
  49. package/dist/api/rendering/IGraphicsDevice.d.ts.map +1 -1
  50. package/dist/api/rendering/IRenderer.d.ts +6 -4
  51. package/dist/api/rendering/IRenderer.d.ts.map +1 -1
  52. package/dist/api/rendering/_export-rendering.d.ts +1 -0
  53. package/dist/api/rendering/_export-rendering.d.ts.map +1 -1
  54. package/dist/api/rendering/primitive/IPrimitiveStateDescriptor.d.ts +24 -0
  55. package/dist/api/rendering/primitive/IPrimitiveStateDescriptor.d.ts.map +1 -0
  56. package/dist/api/rendering/primitive/_export.d.ts +1 -1
  57. package/dist/api/rendering/primitive/_export.d.ts.map +1 -1
  58. package/dist/api/rendering/render-pipelines/IMainRenderTargetRenderPipeline.d.ts +5 -0
  59. package/dist/api/rendering/render-pipelines/IMainRenderTargetRenderPipeline.d.ts.map +1 -1
  60. package/dist/api/rendering/texture/ITextureDescriptor.d.ts +5 -1
  61. package/dist/api/rendering/texture/ITextureDescriptor.d.ts.map +1 -1
  62. package/dist/api/rendering/texture/ITextureFactory.d.ts +3 -1
  63. package/dist/api/rendering/texture/ITextureFactory.d.ts.map +1 -1
  64. package/dist/api/rendering/texture/TextureUtilities.d.ts +7 -0
  65. package/dist/api/rendering/texture/TextureUtilities.d.ts.map +1 -1
  66. package/dist/api/scene/ISceneObject.d.ts +12 -0
  67. package/dist/api/scene/ISceneObject.d.ts.map +1 -0
  68. package/dist/api/scene/Scene.d.ts +17 -6
  69. package/dist/api/scene/Scene.d.ts.map +1 -1
  70. package/dist/api/scene/node/Node.d.ts +26 -0
  71. package/dist/api/scene/node/Node.d.ts.map +1 -0
  72. package/dist/api/scene/node/TransformNode.d.ts +46 -0
  73. package/dist/api/scene/node/TransformNode.d.ts.map +1 -0
  74. package/dist/core/material/UnlitMaterialDescriptorUtilities.d.ts +17 -0
  75. package/dist/core/material/UnlitMaterialDescriptorUtilities.d.ts.map +1 -0
  76. package/dist/core/rendering/AGraphicsDevice.d.ts +2 -2
  77. package/dist/core/rendering/AGraphicsDevice.d.ts.map +1 -1
  78. package/dist/core/rendering/ARenderer.d.ts +5 -2
  79. package/dist/core/rendering/ARenderer.d.ts.map +1 -1
  80. package/dist/core/rendering/primitive/PrimitiveStateDescriptorUtilities.d.ts +15 -0
  81. package/dist/core/rendering/primitive/PrimitiveStateDescriptorUtilities.d.ts.map +1 -0
  82. package/dist/core/rendering/texture/TextureDescriptorUtilities.d.ts +2 -2
  83. package/dist/core/rendering/texture/TextureDescriptorUtilities.d.ts.map +1 -1
  84. package/dist/index.js +704 -272
  85. package/dist/webgl/WebGlGraphicsDevice.d.ts +2 -2
  86. package/dist/webgl/WebGlGraphicsDevice.d.ts.map +1 -1
  87. package/dist/webgl/lights/WebGlLightsBuffer.d.ts.map +1 -1
  88. package/dist/webgl/primitive/WebGlPrimitiveState.d.ts +3 -3
  89. package/dist/webgl/primitive/WebGlPrimitiveState.d.ts.map +1 -1
  90. package/dist/webgl/render-pipelines/WebGlMainRenderTargetRenderPipeline.d.ts +8 -0
  91. package/dist/webgl/render-pipelines/WebGlMainRenderTargetRenderPipeline.d.ts.map +1 -1
  92. package/dist/webgl/texture/WebGlTextureFactory.d.ts +1 -1
  93. package/dist/webgl/texture/WebGlTextureFactory.d.ts.map +1 -1
  94. package/dist/webgl/utilities/WebGlTextureUtilities.d.ts.map +1 -1
  95. package/dist/webgpu/WebGpuGraphicsDevice.d.ts +2 -2
  96. package/dist/webgpu/WebGpuGraphicsDevice.d.ts.map +1 -1
  97. package/dist/webgpu/primitive/WebGpuPrimitiveState.d.ts +2 -2
  98. package/dist/webgpu/primitive/WebGpuPrimitiveState.d.ts.map +1 -1
  99. package/dist/webgpu/render-pipelines/WebGpuMainRenderTargetRenderPipeline.d.ts +5 -0
  100. package/dist/webgpu/render-pipelines/WebGpuMainRenderTargetRenderPipeline.d.ts.map +1 -1
  101. package/dist/webgpu/texture/WebGpuTextureFactory.d.ts.map +1 -1
  102. package/package.json +2 -2
  103. package/src/Framework.ts +1 -1
  104. package/src/api/IFramework.ts +1 -1
  105. package/src/api/_export.ts +3 -0
  106. package/src/api/camera/CameraTransformUtilities.ts +182 -0
  107. package/src/api/camera/ICamera.ts +42 -2
  108. package/src/api/camera/OrbitCamera.ts +71 -38
  109. package/src/api/camera/OrthographicCamera.ts +71 -28
  110. package/src/api/camera/PerspectiveCamera.ts +69 -33
  111. package/src/api/content/ContentManager.ts +9 -5
  112. package/src/api/lights/AmbientLight.ts +8 -2
  113. package/src/api/lights/DirectionalLight.ts +30 -5
  114. package/src/api/lights/ILight.ts +22 -0
  115. package/src/api/lights/PointLight.ts +26 -5
  116. package/src/api/lights/_export.ts +1 -0
  117. package/src/api/material/ForwardBlinnPhongMaterial.ts +2 -2
  118. package/src/api/material/IMaterial.ts +8 -0
  119. package/src/api/material/IMaterialFactory.ts +3 -2
  120. package/src/api/material/IUnlitMaterialDescriptor.ts +18 -0
  121. package/src/api/material/MaterialFactory.ts +4 -3
  122. package/src/api/material/SpriteMaterial.ts +4 -0
  123. package/src/api/material/UnlitMaterial.ts +11 -18
  124. package/src/api/material/_export.ts +1 -1
  125. package/src/api/meshes/IRenderableMesh.ts +3 -18
  126. package/src/api/meshes/RenderableMesh.ts +15 -45
  127. package/src/api/rendering/IGraphicsDevice.ts +2 -2
  128. package/src/api/rendering/IRenderer.ts +6 -4
  129. package/src/api/rendering/_export-rendering.ts +2 -1
  130. package/src/api/rendering/primitive/IPrimitiveStateDescriptor.ts +27 -0
  131. package/src/api/rendering/primitive/_export.ts +1 -1
  132. package/src/api/rendering/render-pipelines/IMainRenderTargetRenderPipeline.ts +6 -0
  133. package/src/api/rendering/texture/ITextureDescriptor.ts +6 -1
  134. package/src/api/rendering/texture/ITextureFactory.ts +3 -1
  135. package/src/api/rendering/texture/TextureUtilities.ts +16 -0
  136. package/src/api/scene/ISceneObject.ts +13 -0
  137. package/src/api/scene/Scene.ts +52 -11
  138. package/src/api/scene/node/Node.ts +54 -0
  139. package/src/api/scene/node/TransformNode.ts +88 -0
  140. package/src/core/material/UnlitMaterialDescriptorUtilities.ts +21 -0
  141. package/src/core/rendering/AGraphicsDevice.ts +2 -2
  142. package/src/core/rendering/ARenderer.ts +23 -5
  143. package/src/core/rendering/primitive/PrimitiveStateDescriptorUtilities.ts +22 -0
  144. package/src/core/rendering/texture/TextureDescriptorUtilities.ts +3 -2
  145. package/src/webgl/WebGlGraphicsDevice.ts +2 -3
  146. package/src/webgl/lights/WebGlLightsBuffer.ts +21 -8
  147. package/src/webgl/primitive/WebGlPrimitiveState.ts +8 -6
  148. package/src/webgl/render-pipelines/WebGlMainRenderTargetRenderPipeline.ts +14 -0
  149. package/src/webgl/texture/WebGlTextureFactory.ts +27 -16
  150. package/src/webgl/utilities/WebGlTextureUtilities.ts +10 -4
  151. package/src/webgpu/WebGpuGraphicsDevice.ts +3 -3
  152. package/src/webgpu/primitive/WebGpuPrimitiveState.ts +7 -5
  153. package/src/webgpu/render-pipelines/WebGpuMainRenderTargetRenderPipeline.ts +6 -0
  154. package/src/webgpu/texture/WebGpuTextureFactory.ts +1 -1
  155. package/dist/api/material/ICameraMaterial.d.ts +0 -18
  156. package/dist/api/material/ICameraMaterial.d.ts.map +0 -1
  157. package/dist/api/rendering/primitive/PrimitiveStateDescriptor.d.ts +0 -24
  158. package/dist/api/rendering/primitive/PrimitiveStateDescriptor.d.ts.map +0 -1
  159. package/src/api/material/ICameraMaterial.ts +0 -22
  160. package/src/api/rendering/primitive/PrimitiveStateDescriptor.ts +0 -27
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { vec4, vec3, mat4, vec2, quat } from "gl-matrix";
1
+ import { vec4, vec3, quat, mat4, mat3, vec2 } from "gl-matrix";
2
2
  var RenderingBackend = /* @__PURE__ */ ((RenderingBackend2) => {
3
3
  RenderingBackend2[RenderingBackend2["DEFAULT"] = 0] = "DEFAULT";
4
4
  RenderingBackend2[RenderingBackend2["WEB_GPU"] = 1] = "WEB_GPU";
@@ -35,7 +35,7 @@ class IFrameworkConfiguration {
35
35
  backBufferSize;
36
36
  /** The texture filtering to configure */
37
37
  textureFiltering;
38
- /** The texture color space to use by default. By default, it is {@link ColorSpace.SRGB}. */
38
+ /** The texture color space to use by default. By default, it is {@link ColorSpace.LINEAR}. */
39
39
  textureColorSpace;
40
40
  /**
41
41
  * The HTMLCanvasElement to use for rendering.
@@ -1052,6 +1052,7 @@ class TextureUtilities {
1052
1052
  [TextureFormat.BC7_RGBA_UNORM]: m.BC7_RGBA,
1053
1053
  [TextureFormat.ETC2_RGBA8_UNORM]: m.ETC2_RGBA,
1054
1054
  [TextureFormat.PVRTC_4BPPV1_RGBA]: m.PVRTC1_4_RGBA,
1055
+ [TextureFormat.PVRTC_4BPPV1_RGB]: m.PVRTC1_4_RGB,
1055
1056
  // Basis transcodes to the same blocks for sRGB; only the GPU format differs.
1056
1057
  [TextureFormat.RGBA_8_UNORM_SRGB]: m.RGBA32,
1057
1058
  [TextureFormat.ASTC_4X4_RGBA_SRGB]: m.ASTC_4X4_RGBA,
@@ -1185,6 +1186,18 @@ class TextureUtilities {
1185
1186
  static isSrgbFormat(textureFormat) {
1186
1187
  return this._srgbFormats.includes(textureFormat);
1187
1188
  }
1189
+ /**
1190
+ * Returns the requested format if it is the file's format or its sRGB/linear variant.
1191
+ * @param fileFormat The format the data is stored in.
1192
+ * @param requested The explicitly requested format.
1193
+ * @throws If the requested format cannot reinterpret the data.
1194
+ */
1195
+ static resolveExplicitFormat(fileFormat, requested) {
1196
+ if (requested == fileFormat || requested == TextureUtilities.convertToSrgbVariant(fileFormat) || requested == TextureUtilities.convertToLinearVariant(fileFormat)) {
1197
+ return requested;
1198
+ }
1199
+ throw new Error(`Texture format ${requested} is not compatible with the file's format ${fileFormat}; only its sRGB or linear variant can be used.`);
1200
+ }
1188
1201
  }
1189
1202
  var BufferUsage = /* @__PURE__ */ ((BufferUsage2) => {
1190
1203
  BufferUsage2[BufferUsage2["NONE"] = 0] = "NONE";
@@ -1643,48 +1656,23 @@ let SamplerDescriptor$1 = class SamplerDescriptor {
1643
1656
  */
1644
1657
  mipMapFilter = MipMapSamplerFilter.NONE;
1645
1658
  };
1646
- var PrimitiveTopology = /* @__PURE__ */ ((PrimitiveTopology2) => {
1647
- PrimitiveTopology2[PrimitiveTopology2["LINE_LIST"] = 0] = "LINE_LIST";
1648
- PrimitiveTopology2[PrimitiveTopology2["LINE_STRIP"] = 1] = "LINE_STRIP";
1649
- PrimitiveTopology2[PrimitiveTopology2["POINT_LIST"] = 2] = "POINT_LIST";
1650
- PrimitiveTopology2[PrimitiveTopology2["TRIANGLE_LIST"] = 3] = "TRIANGLE_LIST";
1651
- PrimitiveTopology2[PrimitiveTopology2["TRIANGLE_STRIP"] = 4] = "TRIANGLE_STRIP";
1652
- return PrimitiveTopology2;
1653
- })(PrimitiveTopology || {});
1654
- var CullMode = /* @__PURE__ */ ((CullMode2) => {
1655
- CullMode2[CullMode2["NONE"] = 0] = "NONE";
1656
- CullMode2[CullMode2["BACK"] = 1] = "BACK";
1657
- CullMode2[CullMode2["FRONT"] = 2] = "FRONT";
1658
- return CullMode2;
1659
- })(CullMode || {});
1660
- var FrontFace = /* @__PURE__ */ ((FrontFace2) => {
1661
- FrontFace2[FrontFace2["CW"] = 0] = "CW";
1662
- FrontFace2[FrontFace2["CCW"] = 1] = "CCW";
1663
- return FrontFace2;
1664
- })(FrontFace || {});
1665
- class PrimitiveStateDescriptor {
1666
- /**
1667
- * The topology of the primitive.
1668
- * By default, it is set to TRIANGLE_LIST.
1669
- */
1670
- topology = PrimitiveTopology.TRIANGLE_LIST;
1671
- /**
1672
- * The culling mode.
1673
- * By default, it is set to BACK.
1674
- */
1675
- cullFace = CullMode.BACK;
1676
- /**
1677
- * The front face.
1678
- * By default, it is set to CCW(counter clock-wise).
1679
- */
1680
- frontFace = FrontFace.CCW;
1681
- }
1682
1659
  var PowerPreferenceType = /* @__PURE__ */ ((PowerPreferenceType2) => {
1683
1660
  PowerPreferenceType2["DEFAULT"] = "default";
1684
1661
  PowerPreferenceType2["LOW_POWER"] = "low-power";
1685
1662
  PowerPreferenceType2["HIGH_PERFORMANCE"] = "high-performance";
1686
1663
  return PowerPreferenceType2;
1687
1664
  })(PowerPreferenceType || {});
1665
+ var Culling = /* @__PURE__ */ ((Culling2) => {
1666
+ Culling2[Culling2["None"] = 0] = "None";
1667
+ Culling2[Culling2["Back"] = 1] = "Back";
1668
+ Culling2[Culling2["Front"] = 2] = "Front";
1669
+ return Culling2;
1670
+ })(Culling || {});
1671
+ var ColorSpace = /* @__PURE__ */ ((ColorSpace2) => {
1672
+ ColorSpace2["LINEAR"] = "LINEAR";
1673
+ ColorSpace2["SRGB"] = "SRGB";
1674
+ return ColorSpace2;
1675
+ })(ColorSpace || {});
1688
1676
  class MathHelper {
1689
1677
  /**
1690
1678
  * The value of half PI.
@@ -1887,9 +1875,272 @@ class MathHelper {
1887
1875
  throw new Error("Not implemented");
1888
1876
  }
1889
1877
  }
1878
+ class Node {
1879
+ _children = [];
1880
+ _parent;
1881
+ /** The child nodes of this node. */
1882
+ get children() {
1883
+ return this._children;
1884
+ }
1885
+ /** The parent node of this node, if any. */
1886
+ get parent() {
1887
+ return this._parent;
1888
+ }
1889
+ /**
1890
+ * Adds a child. It is removed from its previous parent first. Adding an existing child does nothing.
1891
+ * @param child The child to add.
1892
+ * @throws Error when adding the node to itself or to one of its descendants.
1893
+ */
1894
+ addChild(child) {
1895
+ if (child._parent === this) {
1896
+ return;
1897
+ }
1898
+ for (let n2 = this; n2; n2 = n2._parent) {
1899
+ if (n2 === child) {
1900
+ throw new Error("Node.addChild: a node cannot be added to itself or to one of its descendants.");
1901
+ }
1902
+ }
1903
+ child._parent?.removeChild(child);
1904
+ child._parent = this;
1905
+ this._children.push(child);
1906
+ }
1907
+ /**
1908
+ * Removes a child.
1909
+ * @param child The child to remove.
1910
+ * @returns True when it was a child of this node.
1911
+ */
1912
+ removeChild(child) {
1913
+ const index = this._children.indexOf(child);
1914
+ if (index < 0) {
1915
+ return false;
1916
+ }
1917
+ this._children.splice(index, 1);
1918
+ child._parent = void 0;
1919
+ return true;
1920
+ }
1921
+ }
1922
+ class TransformNode extends Node {
1923
+ _rotationQuat = quat.create();
1924
+ _localMatrix = mat4.create();
1925
+ _worldMatrix = mat4.create();
1926
+ // Cached copies to detect in-place edits; NaN forces the first computation.
1927
+ _lastPosition = vec3.fromValues(NaN, NaN, NaN);
1928
+ _lastRotation = vec3.create();
1929
+ _lastScale = vec3.create();
1930
+ /**
1931
+ * The constructor.
1932
+ */
1933
+ constructor() {
1934
+ super();
1935
+ this.position = vec3.fromValues(0, 0, 0);
1936
+ this.rotation = vec3.fromValues(0, 0, 0);
1937
+ this.scale = vec3.fromValues(1, 1, 1);
1938
+ }
1939
+ /** Position relative to the parent (the world, without one). Defaults to the origin. In-place edits are detected. */
1940
+ position;
1941
+ /**
1942
+ * Euler rotation in radians, applied in Y, X, Z order (yaw, pitch, roll): `R = Ry * Rx * Rz`.
1943
+ * Defaults to (0, 0, 0). In-place edits are detected.
1944
+ */
1945
+ rotation;
1946
+ /** Scale. Defaults to (1, 1, 1). In-place edits are detected. */
1947
+ scale;
1948
+ /** The local matrix `T * R * S`, recomputed only when position, rotation or scale changed. */
1949
+ get localMatrix() {
1950
+ if (!vec3.exactEquals(this._lastPosition, this.position) || !vec3.exactEquals(this._lastRotation, this.rotation) || !vec3.exactEquals(this._lastScale, this.scale)) {
1951
+ vec3.copy(this._lastPosition, this.position);
1952
+ vec3.copy(this._lastRotation, this.rotation);
1953
+ vec3.copy(this._lastScale, this.scale);
1954
+ const q = quat.identity(this._rotationQuat);
1955
+ quat.rotateY(q, q, this.rotation[1]);
1956
+ quat.rotateX(q, q, this.rotation[0]);
1957
+ quat.rotateZ(q, q, this.rotation[2]);
1958
+ mat4.fromRotationTranslationScale(this._localMatrix, q, this.position, this.scale);
1959
+ }
1960
+ return this._localMatrix;
1961
+ }
1962
+ /**
1963
+ * The model (world) matrix: {@link localMatrix}, or `parent.modelMatrix * localMatrix` when the parent is a
1964
+ * `TransformNode`. Always the same matrix object for a node without a parent transform.
1965
+ */
1966
+ get modelMatrix() {
1967
+ const parent = this.parent;
1968
+ if (parent instanceof TransformNode) {
1969
+ return mat4.multiply(this._worldMatrix, parent.modelMatrix, this.localMatrix);
1970
+ }
1971
+ return this.localMatrix;
1972
+ }
1973
+ /**
1974
+ * Updates the node and its transform children, recomputing the matrices if needed.
1975
+ * Called by `Scene.update` for scene nodes; optional otherwise.
1976
+ * @param gameTime The {@link GameTime}
1977
+ */
1978
+ update(gameTime) {
1979
+ void this.modelMatrix;
1980
+ for (const child of this.children) {
1981
+ if (child instanceof TransformNode) {
1982
+ child.update(gameTime);
1983
+ }
1984
+ }
1985
+ }
1986
+ }
1987
+ const _f = vec3.create();
1988
+ const _x = vec3.create();
1989
+ const _y = vec3.create();
1990
+ const _z = vec3.create();
1991
+ const _u = vec3.create();
1992
+ const _m = mat4.create();
1993
+ const _n = mat3.create();
1994
+ const _p = vec3.create();
1995
+ const _t = vec3.create();
1996
+ class CameraTransformUtilities {
1997
+ /**
1998
+ * Writes the view matrix `inverse(T * R)` from a world matrix, ignoring scale.
1999
+ * @param out The view matrix.
2000
+ * @param world The camera's world matrix (`transform.modelMatrix`).
2001
+ */
2002
+ static viewFromWorld(out, world) {
2003
+ mat4.copy(out, world);
2004
+ for (let c2 = 0; c2 < 3; c2++) {
2005
+ const i2 = c2 * 4;
2006
+ const length = Math.hypot(out[i2], out[i2 + 1], out[i2 + 2]) || 1;
2007
+ out[i2] /= length;
2008
+ out[i2 + 1] /= length;
2009
+ out[i2 + 2] /= length;
2010
+ }
2011
+ return mat4.invert(out, out) ?? mat4.identity(out);
2012
+ }
2013
+ /**
2014
+ * Writes the Euler rotation (the transform's Y-X-Z order) that makes a camera at `position` look at `target`
2015
+ * with `up` as the up hint (a non-vertical up rolls the camera). When `up` is parallel to the view direction,
2016
+ * the world axis least aligned with the view direction is used instead. Leaves `out` unchanged when the points are equal.
2017
+ * @param out The rotation.
2018
+ * @param position The camera position.
2019
+ * @param target The point to look at.
2020
+ * @param up The up hint. By default, world +Y.
2021
+ */
2022
+ static lookAtRotation(out, position, target, up = [0, 1, 0]) {
2023
+ vec3.sub(_f, target, position);
2024
+ if (vec3.sqrLen(_f) === 0) {
2025
+ return out;
2026
+ }
2027
+ vec3.normalize(_f, _f);
2028
+ vec3.negate(_z, _f);
2029
+ vec3.cross(_x, up, _z);
2030
+ if (vec3.sqrLen(_x) < 1e-12) {
2031
+ const ax = Math.abs(_f[0]), ay = Math.abs(_f[1]), az = Math.abs(_f[2]);
2032
+ vec3.set(_u, 0, 0, 0);
2033
+ _u[ay <= ax && ay <= az ? 1 : az <= ax ? 2 : 0] = 1;
2034
+ vec3.cross(_x, _u, _z);
2035
+ }
2036
+ vec3.normalize(_x, _x);
2037
+ vec3.cross(_y, _z, _x);
2038
+ const m23 = _z[1];
2039
+ out[0] = Math.asin(Math.max(-1, Math.min(1, -m23)));
2040
+ if (Math.abs(m23) < 0.9999999) {
2041
+ out[1] = Math.atan2(_z[0], _z[2]);
2042
+ out[2] = Math.atan2(_x[1], _y[1]);
2043
+ } else {
2044
+ out[1] = Math.atan2(-_x[2], _x[0]);
2045
+ out[2] = 0;
2046
+ }
2047
+ return out;
2048
+ }
2049
+ /**
2050
+ * Writes the camera's forward direction (unit length) from a world matrix.
2051
+ * @param out The direction.
2052
+ * @param world The camera's world matrix.
2053
+ */
2054
+ static forwardFromWorld(out, world) {
2055
+ vec3.set(out, -world[8], -world[9], -world[10]);
2056
+ return vec3.normalize(out, out);
2057
+ }
2058
+ /**
2059
+ * Rotates a camera transform to look at a world-space point with a world-space up hint.
2060
+ * When the transform has a `TransformNode` parent, the target and up are converted into the parent's space.
2061
+ * @param transform The camera transform.
2062
+ * @param target The world point.
2063
+ * @param up The world up hint.
2064
+ */
2065
+ static aimTransform(transform, target, up) {
2066
+ const parent = transform.parent;
2067
+ if (parent instanceof TransformNode) {
2068
+ mat4.invert(_m, parent.modelMatrix);
2069
+ vec3.transformMat4(_t, target, _m);
2070
+ mat3.fromMat4(_n, _m);
2071
+ vec3.transformMat3(_p, up, _n);
2072
+ CameraTransformUtilities.lookAtRotation(transform.rotation, transform.position, _t, _p);
2073
+ } else {
2074
+ CameraTransformUtilities.lookAtRotation(transform.rotation, transform.position, target, up);
2075
+ }
2076
+ }
2077
+ /**
2078
+ * Writes a world position into `transform.position`, converting into the parent's space when it has a `TransformNode` parent.
2079
+ */
2080
+ static setWorldPosition(transform, world) {
2081
+ const parent = transform.parent;
2082
+ if (parent instanceof TransformNode) {
2083
+ mat4.invert(_m, parent.modelMatrix);
2084
+ vec3.transformMat4(transform.position, world, _m);
2085
+ } else {
2086
+ vec3.copy(transform.position, world);
2087
+ }
2088
+ }
2089
+ }
2090
+ class CameraAim {
2091
+ constructor(_transform) {
2092
+ this._transform = _transform;
2093
+ }
2094
+ _transform;
2095
+ _target = vec3.create();
2096
+ _hasTarget = false;
2097
+ _up = vec3.fromValues(0, 1, 0);
2098
+ setEye(eye) {
2099
+ CameraTransformUtilities.setWorldPosition(this._transform(), eye);
2100
+ if (this._hasTarget) {
2101
+ this.aim();
2102
+ }
2103
+ }
2104
+ getEye(out = vec3.create()) {
2105
+ const m2 = this._transform().modelMatrix;
2106
+ return vec3.set(out, m2[12], m2[13], m2[14]);
2107
+ }
2108
+ setTarget(target) {
2109
+ vec3.copy(this._target, target);
2110
+ this._hasTarget = true;
2111
+ this.aim();
2112
+ }
2113
+ getTarget(out = vec3.create()) {
2114
+ if (this._hasTarget) {
2115
+ return vec3.copy(out, this._target);
2116
+ }
2117
+ const m2 = this._transform().modelMatrix;
2118
+ CameraTransformUtilities.forwardFromWorld(_f, m2);
2119
+ return vec3.set(out, m2[12] + _f[0], m2[13] + _f[1], m2[14] + _f[2]);
2120
+ }
2121
+ setUp(up) {
2122
+ vec3.copy(this._up, up);
2123
+ if (this._hasTarget) {
2124
+ this.aim();
2125
+ }
2126
+ }
2127
+ getUp(out = vec3.create()) {
2128
+ return vec3.copy(out, this._up);
2129
+ }
2130
+ /** One-shot look-at with the current up; forgets the stored target. */
2131
+ lookAt(target) {
2132
+ this._hasTarget = false;
2133
+ CameraTransformUtilities.aimTransform(this._transform(), target, this._up);
2134
+ }
2135
+ /** Re-aims at the stored target. */
2136
+ aim() {
2137
+ CameraTransformUtilities.aimTransform(this._transform(), this._target, this._up);
2138
+ }
2139
+ }
1890
2140
  class PerspectiveCamera {
1891
2141
  _framework;
1892
- _tempVec3 = vec3.create();
2142
+ _lastWorld = mat4.fromValues(NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2143
+ _positionData = [0, 0, 0, 0];
1893
2144
  _projectionMatrix = mat4.create();
1894
2145
  _viewMatrix = mat4.create();
1895
2146
  _projectionViewMatrix = mat4.create();
@@ -1911,15 +2162,11 @@ class PerspectiveCamera {
1911
2162
  this.aspectRatio = descriptor?.aspectRatio ?? 1;
1912
2163
  this.nearPlane = descriptor?.near ?? 0.01;
1913
2164
  this.farPlane = descriptor?.far ?? 100;
1914
- if (descriptor?.eye) {
1915
- vec3.copy(this.eye, descriptor.eye);
1916
- }
1917
- if (descriptor?.target) {
1918
- vec3.copy(this.target, descriptor.target);
1919
- }
2165
+ vec3.copy(this.transform.position, descriptor?.position ?? descriptor?.eye ?? [0, 0, -3]);
1920
2166
  if (descriptor?.up) {
1921
- vec3.copy(this.up, descriptor.up);
2167
+ this.setUp(descriptor.up);
1922
2168
  }
2169
+ this.setTarget(descriptor?.target ?? [0, 0, 0]);
1923
2170
  if (scene) {
1924
2171
  scene.add(this);
1925
2172
  }
@@ -1978,21 +2225,47 @@ class PerspectiveCamera {
1978
2225
  return this._positionBuffer;
1979
2226
  }
1980
2227
  /**
1981
- * The eye position of the camera.
1982
- */
1983
- eye = vec3.fromValues(0, 0, 3);
1984
- /**
1985
- * The target position of the camera.
2228
+ * The camera transform: the single source of the view. The camera looks down its local -Z axis;
2229
+ * the view matrix is `inverse(T * R)` of `transform.modelMatrix` (scale is ignored).
2230
+ * By default, at (0, 0, -3) looking at the origin.
1986
2231
  */
1987
- target = vec3.create();
2232
+ transform = new TransformNode();
2233
+ /** The unit direction the camera looks in, from the transform. */
2234
+ get direction() {
2235
+ return CameraTransformUtilities.forwardFromWorld(this._direction, this.transform.modelMatrix);
2236
+ }
2237
+ _aim = new CameraAim(() => this.transform);
1988
2238
  /**
1989
- * The up vector of the camera.
2239
+ * Rotates the transform once so the camera looks at a world point, using the current up ({@link getUp}).
2240
+ * Forgets the target stored by {@link setTarget}, so a later {@link setEye} does not re-aim.
2241
+ * @param target The point to look at.
1990
2242
  */
1991
- up = vec3.fromValues(0, 1, 0);
1992
- /** The direction of a camera. */
1993
- get direction() {
1994
- vec3.sub(this._direction, this.target, this.eye);
1995
- return this._direction;
2243
+ lookAt(target) {
2244
+ this._aim.lookAt(target);
2245
+ }
2246
+ /** @inheritDoc */
2247
+ setEye(eye) {
2248
+ this._aim.setEye(eye);
2249
+ }
2250
+ /** @inheritDoc */
2251
+ getEye(out) {
2252
+ return this._aim.getEye(out);
2253
+ }
2254
+ /** @inheritDoc */
2255
+ setTarget(target) {
2256
+ this._aim.setTarget(target);
2257
+ }
2258
+ /** @inheritDoc */
2259
+ getTarget(out) {
2260
+ return this._aim.getTarget(out);
2261
+ }
2262
+ /** @inheritDoc */
2263
+ setUp(up) {
2264
+ this._aim.setUp(up);
2265
+ }
2266
+ /** @inheritDoc */
2267
+ getUp(out) {
2268
+ return this._aim.getUp(out);
1996
2269
  }
1997
2270
  /**
1998
2271
  * The field of view of the camera.
@@ -2022,22 +2295,24 @@ class PerspectiveCamera {
2022
2295
  this._projectionBuffer = this._framework.bufferFactory.createUniformBuffer(this.projectionMatrix, bufferUsage);
2023
2296
  this._viewBuffer = this._framework.bufferFactory.createUniformBuffer(this.viewMatrix, bufferUsage);
2024
2297
  }
2025
- /**
2026
- * The eye in world space, as used by the view matrix: z is flipped for the right-handed look-at.
2027
- * This is what {@link positionBuffer} holds, std140 `vec3` (padded to 16 bytes).
2028
- */
2298
+ /** The world position, std140 `vec3` (padded to 16 bytes). This is what {@link positionBuffer} holds. */
2029
2299
  _worldEye() {
2030
- return [this.eye[0], this.eye[1], -this.eye[2], 0];
2300
+ const m2 = this.transform.modelMatrix;
2301
+ this._positionData[0] = m2[12];
2302
+ this._positionData[1] = m2[13];
2303
+ this._positionData[2] = m2[14];
2304
+ return this._positionData;
2031
2305
  }
2032
2306
  /**
2033
2307
  * Updates the camera matrices.
2034
2308
  */
2035
2309
  _updateMatrices() {
2036
2310
  mat4.perspectiveNO(this._projectionMatrix, this.fieldOfView, this.aspectRatio, this.nearPlane, this.farPlane);
2037
- this._tempVec3[0] = this.eye[0];
2038
- this._tempVec3[1] = this.eye[1];
2039
- this._tempVec3[2] = -this.eye[2];
2040
- mat4.lookAt(this.viewMatrix, this._tempVec3, this.target, this.up);
2311
+ const world = this.transform.modelMatrix;
2312
+ if (!mat4.exactEquals(this._lastWorld, world)) {
2313
+ mat4.copy(this._lastWorld, world);
2314
+ CameraTransformUtilities.viewFromWorld(this.viewMatrix, world);
2315
+ }
2041
2316
  mat4.multiply(this.projectionViewMatrix, this.projectionMatrix, this.viewMatrix);
2042
2317
  }
2043
2318
  /** @inheritDoc */
@@ -2414,8 +2689,8 @@ class OrbitCamera {
2414
2689
  this.supportedInputTypes = supportedInputTypes;
2415
2690
  this._coreCamera = coreCamera;
2416
2691
  this._inputManager = inputManager;
2417
- this.eye = vec3.fromValues(0, 0, -3);
2418
- this.target = vec3.fromValues(0, 0, 0);
2692
+ vec3.set(this._coreCamera.transform.position, 0, 0, 3);
2693
+ this._syncFromTransform();
2419
2694
  void this.POSITIVE_179_DEG;
2420
2695
  void this.findTarget;
2421
2696
  }
@@ -2431,6 +2706,7 @@ class OrbitCamera {
2431
2706
  _coreCamera;
2432
2707
  _inputManager;
2433
2708
  /** The rotation around the vertical axis. */
2709
+ _target = vec3.create();
2434
2710
  _yaw = 0;
2435
2711
  /** The tilt of the camera.*/
2436
2712
  _pitch = 0;
@@ -2443,7 +2719,6 @@ class OrbitCamera {
2443
2719
  /** The speed of pinch.*/
2444
2720
  pinchSpeed = 1;
2445
2721
  /** The up vector. */
2446
- up = vec3.fromValues(0, 1, 0);
2447
2722
  /** The clamp values of pitch axis */
2448
2723
  pitchClamp = vec2.fromValues(-this.POSITIVE_89_DEG, this.POSITIVE_89_DEG);
2449
2724
  /** @inheritDoc */
@@ -2484,33 +2759,56 @@ class OrbitCamera {
2484
2759
  }
2485
2760
  /** Orbit controls can be used if this button is pressed. */
2486
2761
  orbitButton = MouseButton.LEFT;
2487
- /**
2488
- * The eye position of the camera.
2489
- */
2490
- get eye() {
2491
- return this._coreCamera.eye;
2762
+ /** The orbit camera's transform: the core camera's transform. Its position is the eye; the rotation is set each update to look at {@link target}. */
2763
+ get transform() {
2764
+ return this._coreCamera.transform;
2492
2765
  }
2493
2766
  /**
2494
- * The eye position of the camera.
2767
+ * Sets the point the camera orbits around and looks at (the core camera's target). By default, the origin.
2768
+ * @param target The world point.
2495
2769
  */
2496
- set eye(value) {
2497
- this._coreCamera.eye = value;
2498
- this._setPitchAndYawFromDirection(this._coreCamera.direction);
2770
+ setTarget(target) {
2771
+ vec3.copy(this._target, target);
2772
+ this._syncFromTransform();
2499
2773
  }
2500
- /**
2501
- * The eye position of the camera.
2502
- */
2503
- get target() {
2504
- return this._coreCamera.target;
2774
+ /** @inheritDoc */
2775
+ getTarget(out = vec3.create()) {
2776
+ return vec3.copy(out, this._target);
2505
2777
  }
2506
2778
  /**
2507
- * The eye position of the camera.
2779
+ * Moves the camera to a world position and re-derives the orbit (pitch, yaw, distance) around the target.
2780
+ * @param eye The world position.
2508
2781
  */
2509
- set target(value) {
2510
- this._coreCamera.target = value;
2511
- this._setPitchAndYawFromDirection(this._coreCamera.direction);
2782
+ setEye(eye) {
2783
+ CameraTransformUtilities.setWorldPosition(this._coreCamera.transform, eye);
2784
+ this._syncFromTransform();
2785
+ }
2786
+ /** @inheritDoc */
2787
+ getEye(out) {
2788
+ return this._coreCamera.getEye(out);
2789
+ }
2790
+ /** Sets the up vector the core camera aims with (the orbit angles stay around world +Y, so this rolls the view). */
2791
+ setUp(up) {
2792
+ this._coreCamera.setUp(up);
2793
+ this._coreCamera.setTarget(this._target);
2794
+ }
2795
+ /** @inheritDoc */
2796
+ getUp(out) {
2797
+ return this._coreCamera.getUp(out);
2798
+ }
2799
+ /** The eye: `transform.position` (world space). */
2800
+ get _eye() {
2801
+ return this._coreCamera.transform.position;
2802
+ }
2803
+ /** Re-derives pitch and yaw from the eye and target, and aims the core camera at the target. */
2804
+ _syncFromTransform() {
2805
+ vec3.sub(this._tempDirection, this._target, this._eye);
2806
+ if (vec3.sqrLen(this._tempDirection) === 0) {
2807
+ return;
2808
+ }
2809
+ this._setPitchAndYawFromDirection(this._tempDirection);
2810
+ this._coreCamera.setTarget(this._target);
2512
2811
  }
2513
- /** Gets the core camera.*/
2514
2812
  get coreCamera() {
2515
2813
  return this._coreCamera;
2516
2814
  }
@@ -2539,11 +2837,11 @@ class OrbitCamera {
2539
2837
  vec3.normalize(lookDirection, lookDirection);
2540
2838
  const y2 = MathHelper.clamp(lookDirection[1], -1, 1);
2541
2839
  this._pitch = -this._pitchClamp(Math.asin(y2));
2542
- this._yaw = -Math.atan2(lookDirection[0], lookDirection[2]);
2840
+ this._yaw = -Math.atan2(lookDirection[0], 0 - lookDirection[2]);
2543
2841
  }
2544
2842
  _calculateOrbitMovement() {
2545
2843
  this._pitch = this._pitchClamp(this._pitch);
2546
- vec3.sub(this._tempVec3, this._coreCamera.target, this._coreCamera.eye);
2844
+ vec3.sub(this._tempVec3, this._target, this._eye);
2547
2845
  const radius = vec3.len(this._tempVec3);
2548
2846
  const sinPitch = Math.sin(this._pitch);
2549
2847
  const cosPitch = Math.cos(this._pitch);
@@ -2552,8 +2850,9 @@ class OrbitCamera {
2552
2850
  const z = radius * cosPitch * Math.cos(this._yaw);
2553
2851
  this._tempVec3[0] = x2;
2554
2852
  this._tempVec3[1] = y2;
2555
- this._tempVec3[2] = -z;
2556
- vec3.add(this._coreCamera.eye, this._coreCamera.target, this._tempVec3);
2853
+ this._tempVec3[2] = z;
2854
+ vec3.add(this._eye, this._target, this._tempVec3);
2855
+ this._coreCamera.setTarget(this._target);
2557
2856
  }
2558
2857
  /**
2559
2858
  * Handles the mouse movement for orbit camera.
@@ -2605,9 +2904,9 @@ class OrbitCamera {
2605
2904
  if (diff == 0) {
2606
2905
  return;
2607
2906
  }
2608
- vec3.copy(this._tempDirection, this.direction);
2907
+ vec3.sub(this._tempDirection, this._target, this._eye);
2609
2908
  vec3.normalize(this._tempDirection, this._tempDirection);
2610
- vec3.copy(this._tempNextEye, this.eye);
2909
+ vec3.copy(this._tempNextEye, this._eye);
2611
2910
  if (diff < 0) {
2612
2911
  vec3.scale(this._tempVec3, this._tempDirection, this.pinchSpeed * deltaTime);
2613
2912
  vec3.sub(this._tempNextEye, this._tempNextEye, this._tempVec3);
@@ -2615,16 +2914,16 @@ class OrbitCamera {
2615
2914
  vec3.scale(this._tempVec3, this._tempDirection, this.pinchSpeed * deltaTime);
2616
2915
  vec3.add(this._tempNextEye, this._tempNextEye, this._tempVec3);
2617
2916
  }
2618
- vec3.sub(this._tempNextDirection, this.target, this._tempNextEye);
2619
- const nextLength = this._tempNextDirection.length;
2917
+ vec3.sub(this._tempNextDirection, this._target, this._tempNextEye);
2918
+ const nextLength = vec3.len(this._tempNextDirection);
2620
2919
  vec3.normalize(this._tempNextDirection, this._tempNextDirection);
2621
2920
  vec3.sub(this._tempVec3, this._tempNextDirection, this._tempDirection);
2622
2921
  const lenSq = vec3.sqrLen(this._tempVec3);
2623
2922
  if (nextLength < this._coreCamera.nearPlane || nextLength > this._coreCamera.farPlane || lenSq > 0.1) {
2624
2923
  return;
2625
2924
  }
2626
- vec3.copy(this._coreCamera.eye, this._tempNextEye);
2627
- this._setPitchAndYawFromDirection(this._coreCamera.direction);
2925
+ vec3.copy(this._eye, this._tempNextEye);
2926
+ this._syncFromTransform();
2628
2927
  }
2629
2928
  /** Handles forward/backward movement */
2630
2929
  _handleMouseForwardBackwardMovement(mouseState, deltaTime) {
@@ -2632,7 +2931,7 @@ class OrbitCamera {
2632
2931
  if (mouseScrollPos == 0) {
2633
2932
  return;
2634
2933
  }
2635
- vec3.copy(this._tempDirection, this.direction);
2934
+ vec3.sub(this._tempDirection, this._target, this._eye);
2636
2935
  const distance = vec3.len(this._tempDirection);
2637
2936
  const step = MathHelper.map(
2638
2937
  distance,
@@ -2646,7 +2945,7 @@ class OrbitCamera {
2646
2945
  trueScrollSpeed = this.maxScrollSpeed;
2647
2946
  }
2648
2947
  vec3.normalize(this._tempDirection, this._tempDirection);
2649
- vec3.copy(this._tempNextEye, this.eye);
2948
+ vec3.copy(this._tempNextEye, this._eye);
2650
2949
  if (mouseScrollPos > 0) {
2651
2950
  vec3.scale(this._tempVec3, this._tempDirection, trueScrollSpeed * deltaTime);
2652
2951
  vec3.sub(this._tempNextEye, this._tempNextEye, this._tempVec3);
@@ -2654,16 +2953,16 @@ class OrbitCamera {
2654
2953
  vec3.scale(this._tempVec3, this._tempDirection, trueScrollSpeed * deltaTime);
2655
2954
  vec3.add(this._tempNextEye, this._tempNextEye, this._tempVec3);
2656
2955
  }
2657
- vec3.sub(this._tempNextDirection, this.target, this._tempNextEye);
2658
- const nextLength = this._tempNextDirection.length;
2956
+ vec3.sub(this._tempNextDirection, this._target, this._tempNextEye);
2957
+ const nextLength = vec3.len(this._tempNextDirection);
2659
2958
  vec3.normalize(this._tempNextDirection, this._tempNextDirection);
2660
2959
  vec3.sub(this._tempVec3, this._tempNextDirection, this._tempDirection);
2661
2960
  const lenSq = vec3.sqrLen(this._tempVec3);
2662
2961
  if (nextLength < this._coreCamera.nearPlane || nextLength > this._coreCamera.farPlane || lenSq > 0.01) {
2663
2962
  return;
2664
2963
  }
2665
- vec3.copy(this._coreCamera.eye, this._tempNextEye);
2666
- this._setPitchAndYawFromDirection(this._coreCamera.direction);
2964
+ vec3.copy(this._eye, this._tempNextEye);
2965
+ this._syncFromTransform();
2667
2966
  }
2668
2967
  /** @inheritDoc */
2669
2968
  update(time) {
@@ -2681,6 +2980,7 @@ class OrbitCamera {
2681
2980
  this._handleMouseForwardBackwardMovement(mouseState, deltaTime);
2682
2981
  }
2683
2982
  }
2983
+ this._coreCamera.setTarget(this._target);
2684
2984
  this._coreCamera.update(time);
2685
2985
  }
2686
2986
  /** @inheritDoc */
@@ -2689,12 +2989,11 @@ class OrbitCamera {
2689
2989
  }
2690
2990
  }
2691
2991
  class OrthographicCamera {
2692
- static UP_VECTOR = vec3.fromValues(0, 1, 0);
2693
2992
  _framework;
2694
2993
  /** @ts-ignore */
2695
2994
  _zSpaceLength;
2696
- _eye;
2697
- _target;
2995
+ _lastWorld = mat4.fromValues(NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2996
+ _direction = vec3.create();
2698
2997
  _left;
2699
2998
  _right;
2700
2999
  _top;
@@ -2705,9 +3004,49 @@ class OrthographicCamera {
2705
3004
  _projectionMatrix = mat4.create();
2706
3005
  _viewMatrix = mat4.create();
2707
3006
  _projectionViewMatrix = mat4.create();
2708
- position = vec3.create();
2709
- target = vec3.create();
2710
- direction = vec3.create();
3007
+ /**
3008
+ * The camera transform: the single source of the view. The camera looks down its local -Z axis;
3009
+ * the view matrix is `inverse(T * R)` of `transform.modelMatrix` (scale is ignored).
3010
+ * By default, at (0, 0, 1) looking down -Z at the origin.
3011
+ */
3012
+ transform = new TransformNode();
3013
+ /** The unit direction the camera looks in, from the transform. */
3014
+ get direction() {
3015
+ return CameraTransformUtilities.forwardFromWorld(this._direction, this.transform.modelMatrix);
3016
+ }
3017
+ _aim = new CameraAim(() => this.transform);
3018
+ /**
3019
+ * Rotates the transform once so the camera looks at a world point, using the current up ({@link getUp}).
3020
+ * Forgets the target stored by {@link setTarget}, so a later {@link setEye} does not re-aim.
3021
+ * @param target The point to look at.
3022
+ */
3023
+ lookAt(target) {
3024
+ this._aim.lookAt(target);
3025
+ }
3026
+ /** @inheritDoc */
3027
+ setEye(eye) {
3028
+ this._aim.setEye(eye);
3029
+ }
3030
+ /** @inheritDoc */
3031
+ getEye(out) {
3032
+ return this._aim.getEye(out);
3033
+ }
3034
+ /** @inheritDoc */
3035
+ setTarget(target) {
3036
+ this._aim.setTarget(target);
3037
+ }
3038
+ /** @inheritDoc */
3039
+ getTarget(out) {
3040
+ return this._aim.getTarget(out);
3041
+ }
3042
+ /** @inheritDoc */
3043
+ setUp(up) {
3044
+ this._aim.setUp(up);
3045
+ }
3046
+ /** @inheritDoc */
3047
+ getUp(out) {
3048
+ return this._aim.getUp(out);
3049
+ }
2711
3050
  _projectionBuffer = null;
2712
3051
  _viewBuffer = null;
2713
3052
  _projectionViewBuffer = null;
@@ -2732,8 +3071,7 @@ class OrthographicCamera {
2732
3071
  this._bottom = bottom;
2733
3072
  this._near = near;
2734
3073
  this._far = far;
2735
- this._eye = vec3.fromValues(0, 0, 1);
2736
- this._target = vec3.fromValues(0, 0, 0);
3074
+ vec3.set(this.transform.position, 0, 0, 1);
2737
3075
  this._zSpaceLength = 2;
2738
3076
  this._updateMatrices();
2739
3077
  }
@@ -2804,17 +3142,6 @@ class OrthographicCamera {
2804
3142
  this._isDirty = true;
2805
3143
  }
2806
3144
  /** @inheritDoc */
2807
- get eye() {
2808
- return this._eye;
2809
- }
2810
- /** @inheritDoc */
2811
- set eye(value) {
2812
- if (this._eye === value)
2813
- return;
2814
- this._eye = value;
2815
- this._isDirty = true;
2816
- }
2817
- /** @inheritDoc */
2818
3145
  get projectionMatrix() {
2819
3146
  return this._projectionMatrix;
2820
3147
  }
@@ -2842,18 +3169,25 @@ class OrthographicCamera {
2842
3169
  get positionBuffer() {
2843
3170
  return this._positionBuffer;
2844
3171
  }
2845
- /** Writes the world-space eye into the std140 position data (no z flip: the look-at uses the eye as is). */
3172
+ /** Writes the transform's world position into the std140 position data. */
2846
3173
  _updatePositionData() {
2847
- this._positionData[0] = this._eye[0];
2848
- this._positionData[1] = this._eye[1];
2849
- this._positionData[2] = this._eye[2];
3174
+ const m2 = this.transform.modelMatrix;
3175
+ this._positionData[0] = m2[12];
3176
+ this._positionData[1] = m2[13];
3177
+ this._positionData[2] = m2[14];
2850
3178
  return this._positionData;
2851
3179
  }
3180
+ /** True when the transform's world matrix changed since the view was last built. */
3181
+ _transformChanged() {
3182
+ return !mat4.exactEquals(this._lastWorld, this.transform.modelMatrix);
3183
+ }
2852
3184
  /**
2853
3185
  * Updates the camera matrices.
2854
3186
  */
2855
3187
  _updateMatrices() {
2856
- mat4.lookAt(this._viewMatrix, this._eye, this._target, OrthographicCamera.UP_VECTOR);
3188
+ const world = this.transform.modelMatrix;
3189
+ mat4.copy(this._lastWorld, world);
3190
+ CameraTransformUtilities.viewFromWorld(this._viewMatrix, world);
2857
3191
  mat4.orthoNO(this._projectionMatrix, this._left, this._right, this._bottom, this._top, this._near, this._far);
2858
3192
  mat4.multiply(this._projectionViewMatrix, this._projectionMatrix, this._viewMatrix);
2859
3193
  }
@@ -2890,7 +3224,7 @@ class OrthographicCamera {
2890
3224
  }
2891
3225
  /** @inheritDoc */
2892
3226
  update(_2) {
2893
- if (!this._isDirty)
3227
+ if (!this._isDirty && !this._transformChanged())
2894
3228
  return;
2895
3229
  this.updateBuffers();
2896
3230
  this._isDirty = false;
@@ -2975,6 +3309,25 @@ class ContentConfig {
2975
3309
  */
2976
3310
  keepDataCached = false;
2977
3311
  }
3312
+ var CullMode = /* @__PURE__ */ ((CullMode2) => {
3313
+ CullMode2[CullMode2["NONE"] = 0] = "NONE";
3314
+ CullMode2[CullMode2["BACK"] = 1] = "BACK";
3315
+ CullMode2[CullMode2["FRONT"] = 2] = "FRONT";
3316
+ return CullMode2;
3317
+ })(CullMode || {});
3318
+ var FrontFace = /* @__PURE__ */ ((FrontFace2) => {
3319
+ FrontFace2[FrontFace2["CW"] = 0] = "CW";
3320
+ FrontFace2[FrontFace2["CCW"] = 1] = "CCW";
3321
+ return FrontFace2;
3322
+ })(FrontFace || {});
3323
+ var PrimitiveTopology = /* @__PURE__ */ ((PrimitiveTopology2) => {
3324
+ PrimitiveTopology2[PrimitiveTopology2["LINE_LIST"] = 0] = "LINE_LIST";
3325
+ PrimitiveTopology2[PrimitiveTopology2["LINE_STRIP"] = 1] = "LINE_STRIP";
3326
+ PrimitiveTopology2[PrimitiveTopology2["POINT_LIST"] = 2] = "POINT_LIST";
3327
+ PrimitiveTopology2[PrimitiveTopology2["TRIANGLE_LIST"] = 3] = "TRIANGLE_LIST";
3328
+ PrimitiveTopology2[PrimitiveTopology2["TRIANGLE_STRIP"] = 4] = "TRIANGLE_STRIP";
3329
+ return PrimitiveTopology2;
3330
+ })(PrimitiveTopology || {});
2978
3331
  class RawImageData {
2979
3332
  /**
2980
3333
  * The constructor.
@@ -3603,16 +3956,6 @@ class Mesh {
3603
3956
  class RenderableMesh extends Mesh {
3604
3957
  _subMeshes = [];
3605
3958
  _parent;
3606
- // Transform; only used on the root.
3607
- _position = vec3.create();
3608
- _rotation = vec3.create();
3609
- _scale = vec3.fromValues(1, 1, 1);
3610
- _modelMatrix = mat4.create();
3611
- _rotationQuat = quat.create();
3612
- // Cached copies to detect in-place edits; NaN forces the first computation.
3613
- _lastPosition = vec3.fromValues(NaN, NaN, NaN);
3614
- _lastRotation = vec3.create();
3615
- _lastScale = vec3.create();
3616
3959
  _modelBuffer;
3617
3960
  /** The matrix last uploaded to the model buffer. */
3618
3961
  _uploadedMatrix = mat4.create();
@@ -3622,6 +3965,14 @@ class RenderableMesh extends Mesh {
3622
3965
  indexStart;
3623
3966
  /** @inheritDoc */
3624
3967
  indexCount;
3968
+ _transform = new TransformNode();
3969
+ /** @inheritDoc A sub mesh returns (and sets) the root's transform. */
3970
+ get transform() {
3971
+ return this._root._transform;
3972
+ }
3973
+ set transform(value) {
3974
+ this._root._transform = value;
3975
+ }
3625
3976
  /**
3626
3977
  * The constructor.
3627
3978
  * @param framework The framework.
@@ -3672,45 +4023,6 @@ class RenderableMesh extends Mesh {
3672
4023
  get _defaultGeometryFormat() {
3673
4024
  return this.material?.geometryFormat;
3674
4025
  }
3675
- /** @inheritDoc */
3676
- get position() {
3677
- return this._root._position;
3678
- }
3679
- set position(value) {
3680
- this._root._position = value;
3681
- }
3682
- /** @inheritDoc */
3683
- get rotation() {
3684
- return this._root._rotation;
3685
- }
3686
- set rotation(value) {
3687
- this._root._rotation = value;
3688
- }
3689
- /** @inheritDoc */
3690
- get scale() {
3691
- return this._root._scale;
3692
- }
3693
- set scale(value) {
3694
- this._root._scale = value;
3695
- }
3696
- /** @inheritDoc */
3697
- get modelMatrix() {
3698
- const root = this._root;
3699
- if (root !== this) {
3700
- return root.modelMatrix;
3701
- }
3702
- if (!vec3.exactEquals(this._lastPosition, this._position) || !vec3.exactEquals(this._lastRotation, this._rotation) || !vec3.exactEquals(this._lastScale, this._scale)) {
3703
- vec3.copy(this._lastPosition, this._position);
3704
- vec3.copy(this._lastRotation, this._rotation);
3705
- vec3.copy(this._lastScale, this._scale);
3706
- const q = quat.identity(this._rotationQuat);
3707
- quat.rotateY(q, q, this._rotation[1]);
3708
- quat.rotateX(q, q, this._rotation[0]);
3709
- quat.rotateZ(q, q, this._rotation[2]);
3710
- mat4.fromRotationTranslationScale(this._modelMatrix, q, this._position, this._scale);
3711
- }
3712
- return this._modelMatrix;
3713
- }
3714
4026
  /** @inheritDoc Created on first access and disposed with the root mesh. */
3715
4027
  get modelBuffer() {
3716
4028
  const root = this._root;
@@ -3718,7 +4030,7 @@ class RenderableMesh extends Mesh {
3718
4030
  return root.modelBuffer;
3719
4031
  }
3720
4032
  if (!this._modelBuffer) {
3721
- mat4.copy(this._uploadedMatrix, this.modelMatrix);
4033
+ mat4.copy(this._uploadedMatrix, this.transform.modelMatrix);
3722
4034
  this._modelBuffer = this._framework.bufferFactory.createUniformBuffer(
3723
4035
  this._uploadedMatrix,
3724
4036
  BufferUsage.UNIFORM | BufferUsage.COPY_DST,
@@ -3731,7 +4043,7 @@ class RenderableMesh extends Mesh {
3731
4043
  _updateModelBuffer() {
3732
4044
  const root = this._root;
3733
4045
  const buffer = root.modelBuffer;
3734
- const matrix = root.modelMatrix;
4046
+ const matrix = root.transform.modelMatrix;
3735
4047
  if (!mat4.exactEquals(root._uploadedMatrix, matrix)) {
3736
4048
  mat4.copy(root._uploadedMatrix, matrix);
3737
4049
  buffer.update(root._uploadedMatrix);
@@ -4422,11 +4734,11 @@ class InspectTextureMipsMaterial {
4422
4734
  this._fragmentDataBuffer.dispose();
4423
4735
  }
4424
4736
  }
4425
- class UnlitMaterialDescriptor {
4426
- /** The primitive state. */
4427
- primitiveState;
4428
- /** The camera used to render this material. */
4429
- camera;
4737
+ function resolveUnlitMaterialDescriptor(desc) {
4738
+ return {
4739
+ primitiveState: desc?.primitiveState,
4740
+ camera: desc?.camera
4741
+ };
4430
4742
  }
4431
4743
  class UnlitMaterial {
4432
4744
  _framework;
@@ -4448,16 +4760,17 @@ class UnlitMaterial {
4448
4760
  /**
4449
4761
  * The constructor.
4450
4762
  * @param framework The framework.
4451
- * @param config The optional material descriptor. A projection view buffer passed here is shared and is not disposed by this material.
4763
+ * @param descriptor The optional material descriptor. A projection view buffer passed here is shared and is not disposed by this material.
4452
4764
  */
4453
- constructor(framework, config) {
4765
+ constructor(framework, descriptor) {
4766
+ const config = resolveUnlitMaterialDescriptor(descriptor);
4454
4767
  this._framework = framework;
4455
4768
  const usage = BufferUsage.UNIFORM | BufferUsage.COPY_DST;
4456
- this._camera = config?.camera;
4457
- this._ownsProjectionViewBuffer = !config?.camera?.projectionViewBuffer;
4458
- this._projectionViewBuffer = config?.camera?.projectionViewBuffer ?? framework.bufferFactory.createUniformBuffer(mat4.create(), usage);
4769
+ this._camera = config.camera;
4770
+ this._ownsProjectionViewBuffer = !config.camera?.projectionViewBuffer;
4771
+ this._projectionViewBuffer = config.camera?.projectionViewBuffer ?? framework.bufferFactory.createUniformBuffer(mat4.create(), usage);
4459
4772
  this._materialBuffer = framework.bufferFactory.createUniformBuffer(this._materialData, usage);
4460
- this._primitiveState = config?.primitiveState;
4773
+ this._primitiveState = config.primitiveState;
4461
4774
  }
4462
4775
  /** Creates the pipeline with the current bindings. */
4463
4776
  _createPipeline(modelBuffer) {
@@ -4595,6 +4908,8 @@ class UnlitMaterial {
4595
4908
  }
4596
4909
  }
4597
4910
  class SpriteMaterial {
4911
+ /** The camera. Stored only: sprites render in screen space and use no camera buffers. */
4912
+ camera;
4598
4913
  _renderPipeline;
4599
4914
  /**
4600
4915
  * The constructor.
@@ -4666,9 +4981,6 @@ class MaterialFactory {
4666
4981
  return new ForwardBlinnPhongMaterial(this._framework, descriptor);
4667
4982
  }
4668
4983
  }
4669
- function isCameraMaterial(material) {
4670
- return !!material && "camera" in material;
4671
- }
4672
4984
  function isLightsMaterial(material) {
4673
4985
  return !!material && "directionalLights" in material && "pointLights" in material && "ambientLight" in material;
4674
4986
  }
@@ -4689,6 +5001,8 @@ class AmbientLight {
4689
5001
  this.intensity = descriptor?.intensity ?? 1;
4690
5002
  scene?.add(this);
4691
5003
  }
5004
+ /** The transform. It has no effect on an ambient light. */
5005
+ transform = new TransformNode();
4692
5006
  /** The unique id of this light. */
4693
5007
  id;
4694
5008
  /** The optional name for a light. */
@@ -4698,6 +5012,7 @@ class AmbientLight {
4698
5012
  /** Multiplies {@link color} when written to the lights buffer. */
4699
5013
  intensity;
4700
5014
  }
5015
+ const _target = vec3.create();
4701
5016
  class DirectionalLight {
4702
5017
  /**
4703
5018
  * The constructor.
@@ -4710,7 +5025,7 @@ class DirectionalLight {
4710
5025
  this.name = descriptor?.name;
4711
5026
  this.color = descriptor?.color?.copy() ?? Color.white();
4712
5027
  this.intensity = descriptor?.intensity ?? 1;
4713
- this.direction = descriptor?.direction ? vec3.clone(descriptor.direction) : vec3.fromValues(0, -1, 0);
5028
+ this.setDirection(descriptor?.direction ?? [0, -1, 0]);
4714
5029
  scene?.add(this);
4715
5030
  }
4716
5031
  /** The unique id of this light. */
@@ -4721,8 +5036,25 @@ class DirectionalLight {
4721
5036
  color;
4722
5037
  /** Multiplies {@link color} when written to the lights buffer. */
4723
5038
  intensity;
4724
- /** The direction in which the light travels. */
4725
- direction;
5039
+ /** The transform. Its world rotation sets the direction (forward is -Z). */
5040
+ transform = new TransformNode();
5041
+ /**
5042
+ * Rotates the transform so the light travels along a world-space direction (converted into the parent's space
5043
+ * when parented). A zero vector leaves the rotation unchanged.
5044
+ * @param direction The world direction; it does not need to be normalized.
5045
+ */
5046
+ setDirection(direction) {
5047
+ const m2 = this.transform.modelMatrix;
5048
+ vec3.set(_target, m2[12] + direction[0], m2[13] + direction[1], m2[14] + direction[2]);
5049
+ CameraTransformUtilities.aimTransform(this.transform, _target, [0, 1, 0]);
5050
+ }
5051
+ /**
5052
+ * Gets the world-space direction in which the light travels (unit length), derived from the transform.
5053
+ * @param out The optional output vector.
5054
+ */
5055
+ getDirection(out = vec3.create()) {
5056
+ return CameraTransformUtilities.forwardFromWorld(out, this.transform.modelMatrix);
5057
+ }
4726
5058
  }
4727
5059
  class PointLight {
4728
5060
  /** The default constant attenuation. */
@@ -4742,7 +5074,7 @@ class PointLight {
4742
5074
  this.name = descriptor?.name;
4743
5075
  this.color = descriptor?.color?.copy() ?? Color.white();
4744
5076
  this.intensity = descriptor?.intensity ?? 1;
4745
- this.position = descriptor?.position ? vec3.clone(descriptor.position) : vec3.create();
5077
+ vec3.copy(this.transform.position, descriptor?.position ?? [0, 0, 0]);
4746
5078
  this.constant = descriptor?.constant ?? PointLight.DEFAULT_CONSTANT;
4747
5079
  this.linear = descriptor?.linear ?? PointLight.DEFAULT_LINEAR;
4748
5080
  this.quadratic = descriptor?.quadratic ?? PointLight.DEFAULT_QUADRATIC;
@@ -4756,8 +5088,23 @@ class PointLight {
4756
5088
  color;
4757
5089
  /** Multiplies {@link color} when written to the lights buffer. */
4758
5090
  intensity;
4759
- /** World-space position of the light. */
4760
- position;
5091
+ /** The transform. Its world position is the light position. */
5092
+ transform = new TransformNode();
5093
+ /**
5094
+ * Moves the light to a world position (converted into the parent's space when parented).
5095
+ * @param position The world position.
5096
+ */
5097
+ setPosition(position) {
5098
+ CameraTransformUtilities.setWorldPosition(this.transform, position);
5099
+ }
5100
+ /**
5101
+ * Gets the world position of the light, read from the transform.
5102
+ * @param out The optional output vector.
5103
+ */
5104
+ getPosition(out = vec3.create()) {
5105
+ const m2 = this.transform.modelMatrix;
5106
+ return vec3.set(out, m2[12], m2[13], m2[14]);
5107
+ }
4761
5108
  /** Constant attenuation term. */
4762
5109
  constant;
4763
5110
  /** Linear attenuation term. */
@@ -4816,6 +5163,7 @@ class SceneMesh {
4816
5163
  }
4817
5164
  class Scene {
4818
5165
  _sceneMeshes = [];
5166
+ _nodes = [];
4819
5167
  _directionalLights = [];
4820
5168
  _pointLights = [];
4821
5169
  /** The directional light slots (matches the Blinn-Phong material). */
@@ -4855,6 +5203,10 @@ class Scene {
4855
5203
  get sceneMeshes() {
4856
5204
  return this._sceneMeshes;
4857
5205
  }
5206
+ /** The scene objects (anything with a transform, including root meshes), in the order they were added. */
5207
+ get nodes() {
5208
+ return this._nodes;
5209
+ }
4858
5210
  /** The root meshes in this scene, in the order they were added. */
4859
5211
  get meshes() {
4860
5212
  return this._sceneMeshes.map((s2) => s2.mesh);
@@ -4879,23 +5231,32 @@ class Scene {
4879
5231
  this._activeAmbientLight = value;
4880
5232
  }
4881
5233
  /**
4882
- * Adds a mesh, a light or a camera. Adding an item that is already in the scene does nothing.
4883
- * - Renderable mesh or {@link SceneMesh}: drawn by {@link render}; its materials get the scene camera and lights.
4884
- * A mesh is wrapped in a `SceneMesh`, so {@link dispose} also disposes its materials.
4885
- * - Ambient light: added to {@link ambientLights} (use {@link activeAmbientLight} to make it the lit one).
4886
- * - Directional / point light: added to {@link directionalLights} / {@link pointLights}.
4887
- * - Camera: becomes the scene {@link camera}.
4888
- * Constructors given a scene (lights, the perspective camera) call this themselves.
4889
- * @param item The item to add.
4890
- * @throws Error when all slots for a light kind are in use (3 directional, 5 point), or for an unsupported item.
4891
- */
5234
+ * Adds a mesh, a light or a camera. Adding an item that is already in the scene does nothing.
5235
+ * - Renderable mesh or {@link SceneMesh}: drawn by {@link render}; its materials get the scene camera and lights.
5236
+ * A mesh is wrapped in a `SceneMesh`, so {@link dispose} also disposes its materials.
5237
+ * The mesh is also added to {@link nodes}.
5238
+ * - {@link ISceneObject} (anything with a `transform`): added to {@link nodes}; {@link update} updates its transform.
5239
+ * - Lights are also added to {@link nodes} (they are scene objects).
5240
+ * - Ambient light: added to {@link ambientLights} (use {@link activeAmbientLight} to make it the lit one).
5241
+ * - Directional / point light: added to {@link directionalLights} / {@link pointLights}.
5242
+ * - Camera: becomes the scene {@link camera} and is added to {@link nodes} (cameras are scene objects).
5243
+ * Checked before the generic scene object case.
5244
+ * Constructors given a scene (lights, the perspective camera) call this themselves.
5245
+ * @param item The item to add.
5246
+ * @throws Error when all slots for a light kind are in use (3 directional, 5 point), or for an unsupported item.
5247
+ */
4892
5248
  add(item) {
4893
5249
  if (item instanceof AmbientLight) {
4894
5250
  this._addAmbientLight(item);
5251
+ this._addNode(item);
4895
5252
  } else if (item instanceof DirectionalLight || item instanceof PointLight) {
4896
5253
  this._addLight(item);
5254
+ this._addNode(item);
4897
5255
  } else if (Scene._isCamera(item)) {
4898
5256
  this.camera = item;
5257
+ if (Scene._isSceneObject(item)) {
5258
+ this._addNode(item);
5259
+ }
4899
5260
  } else if (item instanceof SceneMesh || Scene._isMesh(item)) {
4900
5261
  const mesh = item instanceof SceneMesh ? item.mesh : item;
4901
5262
  if (!this._sceneMeshes.some((s2) => s2.mesh === mesh)) {
@@ -4903,15 +5264,18 @@ class Scene {
4903
5264
  this._sceneMeshes.push(sceneMesh);
4904
5265
  this._bind(sceneMesh);
4905
5266
  }
5267
+ this._addNode(mesh);
5268
+ } else if (Scene._isSceneObject(item)) {
5269
+ this._addNode(item);
4906
5270
  } else {
4907
- throw new Error("Scene.add: expected a mesh, an AmbientLight, DirectionalLight, PointLight or a camera.");
5271
+ throw new Error("Scene.add: expected a mesh, a scene object, an AmbientLight, DirectionalLight, PointLight or a camera.");
4908
5272
  }
4909
5273
  }
4910
5274
  /**
4911
5275
  * Removes a mesh (or scene mesh), a light or a camera. The mesh and its materials are not disposed.
4912
5276
  * Removing the active camera clears {@link camera}.
4913
5277
  * @param item The item to remove.
4914
- * @returns True when the item was in the scene (for a camera: was the active camera).
5278
+ * @returns True when the item was in the scene.
4915
5279
  * @throws Error when removing the active ambient light (set another {@link activeAmbientLight} first), or for an unsupported item.
4916
5280
  */
4917
5281
  remove(item) {
@@ -4919,14 +5283,17 @@ class Scene {
4919
5283
  if (item === this._activeAmbientLight) {
4920
5284
  throw new Error("Scene.remove: cannot remove the active ambient light; set scene.activeAmbientLight to another light first.");
4921
5285
  }
5286
+ Scene._removeFrom(this._nodes, item);
4922
5287
  return Scene._removeFrom(this.ambientLights, item);
4923
5288
  }
4924
5289
  if (item instanceof DirectionalLight || item instanceof PointLight) {
5290
+ Scene._removeFrom(this._nodes, item);
4925
5291
  return item instanceof DirectionalLight ? Scene._removeFrom(this._directionalLights, item) : Scene._removeFrom(this._pointLights, item);
4926
5292
  }
4927
5293
  if (Scene._isCamera(item)) {
5294
+ const wasNode = Scene._removeFrom(this._nodes, item);
4928
5295
  if (item !== this._activeCamera) {
4929
- return false;
5296
+ return wasNode;
4930
5297
  }
4931
5298
  this._activeCamera = void 0;
4932
5299
  return true;
@@ -4934,13 +5301,17 @@ class Scene {
4934
5301
  if (item instanceof SceneMesh || Scene._isMesh(item)) {
4935
5302
  const mesh = item instanceof SceneMesh ? item.mesh : item;
4936
5303
  const index = this._sceneMeshes.findIndex((s2) => s2.mesh === mesh);
5304
+ Scene._removeFrom(this._nodes, mesh);
4937
5305
  if (index < 0) {
4938
5306
  return false;
4939
5307
  }
4940
5308
  this._sceneMeshes.splice(index, 1);
4941
5309
  return true;
4942
5310
  }
4943
- throw new Error("Scene.remove: expected a mesh, an AmbientLight, DirectionalLight, PointLight or a camera.");
5311
+ if (Scene._isSceneObject(item)) {
5312
+ return Scene._removeFrom(this._nodes, item);
5313
+ }
5314
+ throw new Error("Scene.remove: expected a mesh, a scene object, an AmbientLight, DirectionalLight, PointLight or a camera.");
4944
5315
  }
4945
5316
  /**
4946
5317
  * Adds a directional or point light.
@@ -4986,18 +5357,30 @@ class Scene {
4986
5357
  const camera = item;
4987
5358
  return typeof camera === "object" && camera !== null && "projectionViewBuffer" in camera && "viewMatrix" in camera && typeof camera.updateBuffers === "function";
4988
5359
  }
5360
+ /** Scene objects have a TransformNode as transform. */
5361
+ static _isSceneObject(item) {
5362
+ return typeof item === "object" && item !== null && item.transform instanceof TransformNode;
5363
+ }
5364
+ _addNode(node) {
5365
+ if (!this._nodes.includes(node)) {
5366
+ this._nodes.push(node);
5367
+ }
5368
+ }
4989
5369
  /** Renderable meshes have render() and sub meshes. */
4990
5370
  static _isMesh(item) {
4991
5371
  const mesh = item;
4992
5372
  return typeof mesh === "object" && mesh !== null && typeof mesh.render === "function" && "subMeshes" in mesh;
4993
5373
  }
4994
5374
  /**
4995
- * Updates the scene: assigns {@link camera}, the active ambient light and the light lists to the mesh materials.
5375
+ * Updates the scene: updates the transforms of {@link nodes}, assigns {@link camera}, the active ambient light and the light lists to the mesh materials.
4996
5376
  * Each lights material uploads its lights buffer when drawn, only if the lights changed.
4997
5377
  * @param _time The game time.
4998
5378
  * @public
4999
5379
  */
5000
5380
  update(_time) {
5381
+ for (const node of this._nodes) {
5382
+ node.transform.update(_time);
5383
+ }
5001
5384
  for (const sceneMesh of this._sceneMeshes) {
5002
5385
  this._bind(sceneMesh);
5003
5386
  }
@@ -5026,7 +5409,7 @@ class Scene {
5026
5409
  return;
5027
5410
  }
5028
5411
  for (const material of sceneMesh.materials) {
5029
- if (isCameraMaterial(material) && material.camera !== camera) {
5412
+ if (material.camera !== camera) {
5030
5413
  material.camera = camera;
5031
5414
  }
5032
5415
  }
@@ -5049,6 +5432,7 @@ class Scene {
5049
5432
  sceneMesh.dispose();
5050
5433
  }
5051
5434
  this._sceneMeshes.length = 0;
5435
+ this._nodes.length = 0;
5052
5436
  }
5053
5437
  }
5054
5438
  var Key = /* @__PURE__ */ ((Key2) => {
@@ -5324,6 +5708,7 @@ class CpuImageProcessor {
5324
5708
  return promise;
5325
5709
  }
5326
5710
  }
5711
+ const _v = vec3.create();
5327
5712
  class WebGlLightsBuffer {
5328
5713
  /** Floats for the ambient light: vec3 padded to 16 bytes. */
5329
5714
  static AMBIENT_FLOAT_COUNT = 4;
@@ -5465,9 +5850,14 @@ class WebGlLightsBuffer {
5465
5850
  const light = this._directionalLights[i2];
5466
5851
  this._writeColor(offset, light?.color, light?.intensity ?? 0);
5467
5852
  this._data[offset + 3] = 0;
5468
- this._data[offset + 4] = light ? light.direction[0] : 0;
5469
- this._data[offset + 5] = light ? light.direction[1] : 0;
5470
- this._data[offset + 6] = light ? light.direction[2] : 0;
5853
+ if (light) {
5854
+ light.getDirection(_v);
5855
+ } else {
5856
+ vec3.zero(_v);
5857
+ }
5858
+ this._data[offset + 4] = _v[0];
5859
+ this._data[offset + 5] = _v[1];
5860
+ this._data[offset + 6] = _v[2];
5471
5861
  this._data[offset + 7] = 0;
5472
5862
  }
5473
5863
  const pointLightsStart = WebGlLightsBuffer.AMBIENT_FLOAT_COUNT + this.maxDirectionalLights * WebGlLightsBuffer.DIRECTIONAL_LIGHT_FLOAT_COUNT;
@@ -5476,9 +5866,14 @@ class WebGlLightsBuffer {
5476
5866
  const light = this._pointLights[i2];
5477
5867
  this._writeColor(offset, light?.color, light?.intensity ?? 0);
5478
5868
  this._data[offset + 3] = light ? light.constant : 1;
5479
- this._data[offset + 4] = light ? light.position[0] : 0;
5480
- this._data[offset + 5] = light ? light.position[1] : 0;
5481
- this._data[offset + 6] = light ? light.position[2] : 0;
5869
+ if (light) {
5870
+ light.getPosition(_v);
5871
+ } else {
5872
+ vec3.zero(_v);
5873
+ }
5874
+ this._data[offset + 4] = _v[0];
5875
+ this._data[offset + 5] = _v[1];
5876
+ this._data[offset + 6] = _v[2];
5482
5877
  this._data[offset + 7] = light ? light.linear : 0;
5483
5878
  this._data[offset + 8] = light ? light.quadratic : 0;
5484
5879
  this._data[offset + 9] = 0;
@@ -5709,7 +6104,8 @@ function resolveTextureDescriptor(desc) {
5709
6104
  label: desc?.label ?? null,
5710
6105
  data: desc?.data,
5711
6106
  generateMipmaps: desc?.generateMipmaps ?? false,
5712
- blockSize: desc?.blockSize
6107
+ blockSize: desc?.blockSize,
6108
+ preferredColorSpace: desc?.preferredColorSpace
5713
6109
  };
5714
6110
  }
5715
6111
  const KTX2_EXTENSION = ".ktx2";
@@ -5766,7 +6162,7 @@ class ContentManager {
5766
6162
  }
5767
6163
  return tex;
5768
6164
  }
5769
- let format = this._framework.renderer.preferredTextureFormat;
6165
+ let format = desc.preferredColorSpace ? desc.preferredColorSpace == ColorSpace.SRGB ? TextureFormat.RGBA_8_UNORM_SRGB : TextureFormat.RGBA_8_UNORM : this._framework.renderer.preferredTextureFormat;
5770
6166
  if (desc.textureFormat != TextureFormat.UNDEFINED) {
5771
6167
  format = desc.textureFormat;
5772
6168
  }
@@ -5777,8 +6173,8 @@ class ContentManager {
5777
6173
  data.baseHeight,
5778
6174
  data.getData(0),
5779
6175
  data.channels,
5780
- path,
5781
- TextureUsage.TEXTURE_BINDING | TextureUsage.COPY_SRC,
6176
+ desc.label ?? path,
6177
+ textureDescriptor?.textureUsage ?? TextureUsage.TEXTURE_BINDING | TextureUsage.COPY_SRC,
5782
6178
  format,
5783
6179
  desc.generateMipmaps
5784
6180
  );
@@ -5786,7 +6182,7 @@ class ContentManager {
5786
6182
  /** @inheritDoc */
5787
6183
  loadTexture2DFromPixels(width, height, pixels) {
5788
6184
  const view = new Uint8Array(pixels);
5789
- return this._framework.textureFactory.create(width, height, view);
6185
+ return this._framework.textureFactory.create(width, height, view, 4, void 0, void 0, TextureFormat.RGBA_8_UNORM);
5790
6186
  }
5791
6187
  /** @inheritDoc */
5792
6188
  async loadMeshAsync(path, geometryFormat) {
@@ -5814,17 +6210,6 @@ class ContentManager {
5814
6210
  });
5815
6211
  }
5816
6212
  }
5817
- var Culling = /* @__PURE__ */ ((Culling2) => {
5818
- Culling2[Culling2["None"] = 0] = "None";
5819
- Culling2[Culling2["Back"] = 1] = "Back";
5820
- Culling2[Culling2["Front"] = 2] = "Front";
5821
- return Culling2;
5822
- })(Culling || {});
5823
- var ColorSpace = /* @__PURE__ */ ((ColorSpace2) => {
5824
- ColorSpace2["LINEAR"] = "LINEAR";
5825
- ColorSpace2["SRGB"] = "SRGB";
5826
- return ColorSpace2;
5827
- })(ColorSpace || {});
5828
6213
  class ARenderer {
5829
6214
  _framework;
5830
6215
  /**
@@ -5838,6 +6223,7 @@ class ARenderer {
5838
6223
  _swapChainRequiresResize = false;
5839
6224
  _mainRenderPassDirty = true;
5840
6225
  _clearColor = Color.lightPink();
6226
+ _colorSpace;
5841
6227
  _mainRenderTargetRequiresResize = false;
5842
6228
  _swapChain = null;
5843
6229
  _mainRenderTarget = null;
@@ -5863,9 +6249,24 @@ class ARenderer {
5863
6249
  throw new Error("Method not implemented.");
5864
6250
  }
5865
6251
  /** @inheritdoc */
5866
- preferredTextureFormat;
6252
+ get preferredTextureFormat() {
6253
+ return this._colorSpace == ColorSpace.SRGB ? TextureFormat.RGBA_8_UNORM_SRGB : TextureFormat.RGBA_8_UNORM;
6254
+ }
6255
+ /** @inheritDoc */
6256
+ get colorSpace() {
6257
+ return this._colorSpace;
6258
+ }
5867
6259
  /** @inheritDoc */
5868
- preferredColorSpace;
6260
+ set colorSpace(colorSpace) {
6261
+ if (colorSpace == this._colorSpace) {
6262
+ return;
6263
+ }
6264
+ this._colorSpace = colorSpace;
6265
+ if (this._mainRenderTargetPipeline) {
6266
+ this._mainRenderTargetPipeline.colorSpace = colorSpace;
6267
+ this._mainRenderTargetRequiresResize = true;
6268
+ }
6269
+ }
5869
6270
  /** @inheritdoc */
5870
6271
  get preferredDepthStencilFormat() {
5871
6272
  return this._preferredDepthStencilFormat;
@@ -5899,8 +6300,7 @@ class ARenderer {
5899
6300
  this._windowManager = framework.windowManager;
5900
6301
  this._currentBackBufferSize[0] = this._windowManager.canvas.width;
5901
6302
  this._currentBackBufferSize[1] = this._windowManager.canvas.height;
5902
- this.preferredColorSpace = configuration.textureColorSpace;
5903
- this.preferredTextureFormat = configuration.textureColorSpace == ColorSpace.SRGB ? TextureFormat.RGBA_8_UNORM_SRGB : TextureFormat.RGBA_8_UNORM;
6303
+ this._colorSpace = configuration.textureColorSpace;
5904
6304
  }
5905
6305
  backend = RenderingBackend.WEB_GL;
5906
6306
  /** @inheritDoc */
@@ -5958,6 +6358,7 @@ class ARenderer {
5958
6358
  this.preferredDepthStencilFormat
5959
6359
  );
5960
6360
  this._mainRenderTargetPipeline = this._framework.renderPipelineFactory.createMainFrameBufferPipeline(this._mainRenderTarget);
6361
+ this._mainRenderTargetPipeline.colorSpace = this._colorSpace;
5961
6362
  this._createMainRenderPass();
5962
6363
  this._mainRenderTargetRequiresResize = false;
5963
6364
  }
@@ -7113,6 +7514,7 @@ class WebGlTextureUtilities {
7113
7514
  if (useTexStorage) {
7114
7515
  gl.texStorage2D(gl.TEXTURE_2D, mipLevels, internalFormat, baseWidth, baseHeight);
7115
7516
  }
7517
+ gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
7116
7518
  if (data && data.length > 0) {
7117
7519
  let width = baseWidth;
7118
7520
  let height = baseHeight;
@@ -7122,9 +7524,7 @@ class WebGlTextureUtilities {
7122
7524
  if (useTexStorage) {
7123
7525
  gl.compressedTexSubImage2D(gl.TEXTURE_2D, i2, 0, 0, width, height, internalFormat, mipLevelData, 0);
7124
7526
  } else {
7125
- const alignedWidth = AlignUtilities.align(width, blockSize[0]);
7126
- const alignedHeight = AlignUtilities.align(height, blockSize[1]);
7127
- gl.compressedTexImage2D(gl.TEXTURE_2D, i2, internalFormat, alignedWidth, alignedHeight, 0, mipLevelData, 0);
7527
+ gl.compressedTexImage2D(gl.TEXTURE_2D, i2, internalFormat, width, height, 0, mipLevelData, 0);
7128
7528
  }
7129
7529
  } else {
7130
7530
  throw new Error("unsupported data type");
@@ -7133,6 +7533,7 @@ class WebGlTextureUtilities {
7133
7533
  height = Math.max(1, Math.floor(height / 2));
7134
7534
  }
7135
7535
  }
7536
+ gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
7136
7537
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
7137
7538
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
7138
7539
  if (anisotropy > 1) {
@@ -8034,6 +8435,13 @@ class AGraphicsDevice {
8034
8435
  this._defaultBlendState.dispose();
8035
8436
  }
8036
8437
  }
8438
+ function resolvePrimitiveStateDescriptor(desc) {
8439
+ return {
8440
+ topology: desc?.topology ?? PrimitiveTopology.TRIANGLE_LIST,
8441
+ cullFace: desc?.cullFace ?? CullMode.BACK,
8442
+ frontFace: desc?.frontFace ?? FrontFace.CCW
8443
+ };
8444
+ }
8037
8445
  class WebGlPrimitiveState {
8038
8446
  _cullingEnabled;
8039
8447
  _glPrimitiveType;
@@ -8042,12 +8450,13 @@ class WebGlPrimitiveState {
8042
8450
  /**
8043
8451
  * The constructor.
8044
8452
  * @param _gl The WebGL2 Rendering Context.
8045
- * @param descriptor The descriptor of the primitive state.
8453
+ * @param descriptor The descriptor of the primitive state; missing fields use the defaults.
8046
8454
  */
8047
8455
  constructor(_gl, descriptor) {
8048
- this.topology = descriptor.topology;
8049
- this.cullFace = descriptor.cullFace;
8050
- this.frontFace = descriptor.frontFace;
8456
+ const resolved = resolvePrimitiveStateDescriptor(descriptor);
8457
+ this.topology = resolved.topology;
8458
+ this.cullFace = resolved.cullFace;
8459
+ this.frontFace = resolved.frontFace;
8051
8460
  this._cullingEnabled = this.cullFace != CullMode.NONE;
8052
8461
  this._glPrimitiveType = WebGlConverter.convertPrimitiveType(this.topology);
8053
8462
  this._glCullFace = WebGlConverter.convertCullFace(this.cullFace);
@@ -8281,7 +8690,6 @@ class WebGlGraphicsDevice extends AGraphicsDevice {
8281
8690
  }
8282
8691
  /** @inheritdoc */
8283
8692
  createPrimitiveState(descriptor) {
8284
- descriptor = descriptor ?? new PrimitiveStateDescriptor();
8285
8693
  return new WebGlPrimitiveState(this._gl, descriptor);
8286
8694
  }
8287
8695
  }
@@ -8986,28 +9394,28 @@ class WebGlTextureFactory {
8986
9394
  let transcodeFormat = m.NO_SELECTION;
8987
9395
  let textureFormat = desc.textureFormat;
8988
9396
  const gpuFeatures = this._framework.graphicsDevice.features;
8989
- const srgb = ktxTexture.oetf == u.SRGB;
8990
- const preferSrgb = this._framework.renderer.preferredColorSpace == ColorSpace.SRGB;
9397
+ const preferSrgb = (descriptor?.preferredColorSpace ?? this._framework.renderer.colorSpace) == ColorSpace.SRGB;
9398
+ const userSpecifiedTextureFormat = desc.textureFormat != TextureFormat.UNDEFINED;
8991
9399
  if (ktxTexture.needsTranscoding && vkFormat == n.UNDEFINED) {
8992
9400
  if (desc.textureFormat == TextureFormat.UNDEFINED) {
8993
9401
  if (gpuFeatures.supportsTextureCompressionBC) {
8994
9402
  transcodeFormat = m.BC7_RGBA;
8995
- textureFormat = srgb ? TextureFormat.BC7_RGBA_SRGB : TextureFormat.BC7_RGBA_UNORM;
9403
+ textureFormat = preferSrgb ? TextureFormat.BC7_RGBA_SRGB : TextureFormat.BC7_RGBA_UNORM;
8996
9404
  } else if (gpuFeatures.supportsTextureCompressionASTC) {
8997
9405
  transcodeFormat = m.ASTC_4X4_RGBA;
8998
- textureFormat = srgb ? TextureFormat.ASTC_4X4_RGBA_SRGB : TextureFormat.ASTC_4X4_RGBA_UNORM;
9406
+ textureFormat = preferSrgb ? TextureFormat.ASTC_4X4_RGBA_SRGB : TextureFormat.ASTC_4X4_RGBA_UNORM;
8999
9407
  } else if (gpuFeatures.supportsTextureCompressionS3TC) {
9000
9408
  transcodeFormat = m.BC3_RGBA;
9001
- textureFormat = srgb ? TextureFormat.BC3_RGBA_SRGB : TextureFormat.BC3_RGBA_UNORM;
9409
+ textureFormat = preferSrgb ? TextureFormat.BC3_RGBA_SRGB : TextureFormat.BC3_RGBA_UNORM;
9002
9410
  } else if (gpuFeatures.supportsTextureCompressionETC2) {
9003
9411
  transcodeFormat = m.ETC2_RGBA;
9004
- textureFormat = srgb ? TextureFormat.ETC2_RGBA8_SRGB : TextureFormat.ETC2_RGBA8_UNORM;
9412
+ textureFormat = preferSrgb ? TextureFormat.ETC2_RGBA8_SRGB : TextureFormat.ETC2_RGBA8_UNORM;
9005
9413
  } else if (gpuFeatures.supportsTextureCompressionPVRTC) {
9006
9414
  transcodeFormat = m.PVRTC1_4_RGBA;
9007
9415
  textureFormat = TextureFormat.PVRTC_4BPPV1_RGBA;
9008
9416
  } else {
9009
9417
  transcodeFormat = m.RGBA32;
9010
- textureFormat = srgb ? TextureFormat.RGBA_8_UNORM_SRGB : TextureFormat.RGBA_8_UNORM;
9418
+ textureFormat = preferSrgb ? TextureFormat.RGBA_8_UNORM_SRGB : TextureFormat.RGBA_8_UNORM;
9011
9419
  }
9012
9420
  } else {
9013
9421
  transcodeFormat = TextureUtilities.convertTextureFormatToKtxTranscodeFormat(textureFormat);
@@ -9015,10 +9423,14 @@ class WebGlTextureFactory {
9015
9423
  ktxTexture.transcodeBasis(transcodeFormat, i.HIGH_QUALITY);
9016
9424
  } else {
9017
9425
  textureFormat = TextureUtilities.convertVkFormatToTextureFormat(vkFormat);
9018
- if (!srgb && preferSrgb && !TextureUtilities.isSrgbFormat(textureFormat)) {
9019
- textureFormat = TextureUtilities.convertToSrgbVariant(textureFormat);
9020
- } else if (srgb && !preferSrgb && TextureUtilities.isSrgbFormat(textureFormat)) {
9021
- textureFormat = TextureUtilities.convertToLinearVariant(textureFormat);
9426
+ if (userSpecifiedTextureFormat) {
9427
+ textureFormat = TextureUtilities.resolveExplicitFormat(textureFormat, desc.textureFormat);
9428
+ } else {
9429
+ if (preferSrgb && !TextureUtilities.isSrgbFormat(textureFormat)) {
9430
+ textureFormat = TextureUtilities.convertToSrgbVariant(textureFormat);
9431
+ } else if (!preferSrgb && TextureUtilities.isSrgbFormat(textureFormat)) {
9432
+ textureFormat = TextureUtilities.convertToLinearVariant(textureFormat);
9433
+ }
9022
9434
  }
9023
9435
  }
9024
9436
  const blockSize = ktxTexture.getTextureFormatInfo(ktxTexture.vkFormat);
@@ -9042,7 +9454,10 @@ class WebGlTextureFactory {
9042
9454
  return texture;
9043
9455
  }
9044
9456
  /** @inheritdoc */
9045
- create(width, height, data, channels = 4, label = null, textureUsage = TextureUsage.COPY_DST | TextureUsage.TEXTURE_BINDING, textureFormat = TextureFormat.RGBA_8_UNORM, useMipMaps = false) {
9457
+ create(width, height, data, channels = 4, label = null, textureUsage = TextureUsage.COPY_DST | TextureUsage.TEXTURE_BINDING, textureFormat = TextureFormat.UNDEFINED, useMipMaps = false) {
9458
+ if (textureFormat == TextureFormat.UNDEFINED) {
9459
+ textureFormat = this._framework.renderer.preferredTextureFormat;
9460
+ }
9046
9461
  if (!data) {
9047
9462
  throw new Error("Data must not be null.");
9048
9463
  }
@@ -9282,6 +9697,7 @@ class WebGlMainRenderTargetRenderPipeline extends AWebGlRenderPipeline {
9282
9697
  _indexBuffer = null;
9283
9698
  _mainRenderTarget = null;
9284
9699
  _colorConversionUniformLocation = null;
9700
+ /** The shader's `u_colorConversion`: 1 encodes linear to sRGB on output, 0 passes through. */
9285
9701
  _colorSpaceValue = 0;
9286
9702
  /**
9287
9703
  * The constructor.
@@ -9295,6 +9711,16 @@ class WebGlMainRenderTargetRenderPipeline extends AWebGlRenderPipeline {
9295
9711
  this._colorSpaceValue = 1;
9296
9712
  }
9297
9713
  }
9714
+ /**
9715
+ * The color space. With SRGB the main render target is sRGB (sampled back as linear) and the shader
9716
+ * encodes to sRGB for the screen; with LINEAR the values are passed through. Takes effect on the next frame.
9717
+ */
9718
+ get colorSpace() {
9719
+ return this._colorSpaceValue == 1 ? ColorSpace.SRGB : ColorSpace.LINEAR;
9720
+ }
9721
+ set colorSpace(value) {
9722
+ this._colorSpaceValue = value == ColorSpace.SRGB ? 1 : 0;
9723
+ }
9298
9724
  /** @inheritDoc */
9299
9725
  geometryFormat = GeometryFormat.POS3_COLOR4_TEXTURECOORDS2;
9300
9726
  /** @inheritdoc */
@@ -11238,9 +11664,10 @@ class WebGpuPrimitiveState {
11238
11664
  * @param descriptor The primitive state descriptor.
11239
11665
  */
11240
11666
  constructor(descriptor) {
11241
- this.topology = descriptor.topology;
11242
- this.cullFace = descriptor.cullFace;
11243
- this.frontFace = descriptor.frontFace;
11667
+ const resolved = resolvePrimitiveStateDescriptor(descriptor);
11668
+ this.topology = resolved.topology;
11669
+ this.cullFace = resolved.cullFace;
11670
+ this.frontFace = resolved.frontFace;
11244
11671
  this.gpuTopology = WebGpuConverter.convertTopology(this.topology);
11245
11672
  this.gpuCullMode = WebGpuConverter.convertCullMode(this.cullFace);
11246
11673
  this.gpuFrontFace = WebGpuConverter.convertFrontFace(this.frontFace);
@@ -11372,7 +11799,7 @@ class WebGpuGraphicsDevice extends AGraphicsDevice {
11372
11799
  }
11373
11800
  /** @inheritDoc */
11374
11801
  createPrimitiveState(descriptor) {
11375
- return new WebGpuPrimitiveState(descriptor ?? new PrimitiveStateDescriptor());
11802
+ return new WebGpuPrimitiveState(descriptor);
11376
11803
  }
11377
11804
  /** @inheritDoc */
11378
11805
  setupDebugCallback() {
@@ -11453,7 +11880,7 @@ class WebGpuTextureFactory {
11453
11880
  throw new Error("createFromKtx2 is not implemented for WebGPU.");
11454
11881
  }
11455
11882
  /** @inheritDoc */
11456
- create(width, height, data, channels = 4, label = null, textureUsage = TextureUsage.COPY_DST | TextureUsage.TEXTURE_BINDING, textureFormat = TextureFormat.RGBA_8_UNORM, useMipMaps = false) {
11883
+ create(width, height, data, channels = 4, label = null, textureUsage = TextureUsage.COPY_DST | TextureUsage.TEXTURE_BINDING, textureFormat = TextureFormat.UNDEFINED, useMipMaps = false) {
11457
11884
  if (!data) {
11458
11885
  throw new Error("Data must not be null.");
11459
11886
  }
@@ -11647,6 +12074,10 @@ class WebGpuMainRenderTargetRenderPipeline extends AWebGpuRenderPipeline {
11647
12074
  this._graphicsDevice.swapChain?.textureFormat ?? this._framework.renderer.preferredTextureFormat
11648
12075
  );
11649
12076
  }
12077
+ /**
12078
+ * The color space. Stored only; it has no effect on WebGPU yet.
12079
+ */
12080
+ colorSpace = ColorSpace.LINEAR;
11650
12081
  _getBindGroup() {
11651
12082
  if (!this._bindGroup) {
11652
12083
  this._bindGroup = this._graphicsDevice.device.createBindGroup({
@@ -12371,7 +12802,7 @@ class Framework {
12371
12802
  textureFiltering: this._configuration.textureFiltering ?? TextureSamplerFilteringPreset.BILINEAR,
12372
12803
  alpha: this._configuration.alpha ?? false,
12373
12804
  powerPreference: this._configuration.powerPreference ?? PowerPreferenceType.DEFAULT,
12374
- textureColorSpace: this._configuration.textureColorSpace ?? ColorSpace.SRGB
12805
+ textureColorSpace: this._configuration.textureColorSpace ?? ColorSpace.LINEAR
12375
12806
  };
12376
12807
  let backend = this._configuration.renderingBackend ?? RenderingBackend.WEB_GL;
12377
12808
  if (backend === RenderingBackend.DEFAULT) {
@@ -12602,9 +13033,11 @@ export {
12602
13033
  Button,
12603
13034
  CameraFactory,
12604
13035
  Color,
13036
+ ColorSpace,
12605
13037
  ContentConfig,
12606
13038
  CpuImageProcessor,
12607
13039
  CullMode,
13040
+ Culling,
12608
13041
  DEFAULT_DIRECTIONAL_LIGHTS_COUNT,
12609
13042
  DEFAULT_POINT_LIGHTS_COUNT,
12610
13043
  DirectionalLight,
@@ -12633,6 +13066,7 @@ export {
12633
13066
  MipMapSamplerFilter,
12634
13067
  MouseButton,
12635
13068
  MouseState,
13069
+ Node,
12636
13070
  ObjLoader,
12637
13071
  ObjMaterialDefinition,
12638
13072
  ObjParser,
@@ -12641,7 +13075,6 @@ export {
12641
13075
  PerspectiveCamera,
12642
13076
  PointLight,
12643
13077
  PowerPreferenceType,
12644
- PrimitiveStateDescriptor,
12645
13078
  PrimitiveTopology,
12646
13079
  QuadMesh,
12647
13080
  RawImageData,
@@ -12673,12 +13106,11 @@ export {
12673
13106
  TouchLocation,
12674
13107
  TouchLocationState,
12675
13108
  TouchPanelCapabilities,
13109
+ TransformNode,
12676
13110
  UnlitMaterial,
12677
- UnlitMaterialDescriptor,
12678
13111
  VertexAttribute,
12679
13112
  VertexFormat,
12680
13113
  VertexStepMode,
12681
13114
  WindowBounds,
12682
- isCameraMaterial,
12683
13115
  isLightsMaterial
12684
13116
  };