ris-framework 0.1.0 → 0.2.0-dev.11
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/CHANGELOG.md +49 -1
- package/README.md +15 -4
- package/dist/Framework.d.ts +9 -3
- package/dist/Framework.d.ts.map +1 -1
- package/dist/api/IFramework.d.ts +7 -6
- package/dist/api/IFramework.d.ts.map +1 -1
- package/dist/api/_export.d.ts +2 -0
- package/dist/api/_export.d.ts.map +1 -1
- package/dist/api/camera/CameraFactory.d.ts +3 -2
- package/dist/api/camera/CameraFactory.d.ts.map +1 -1
- package/dist/api/camera/ICamera.d.ts +5 -0
- package/dist/api/camera/ICamera.d.ts.map +1 -1
- package/dist/api/camera/ICameraFactory.d.ts +5 -6
- package/dist/api/camera/ICameraFactory.d.ts.map +1 -1
- package/dist/api/camera/OrthographicCamera.d.ts +7 -0
- package/dist/api/camera/OrthographicCamera.d.ts.map +1 -1
- package/dist/api/camera/PerspectiveCamera.d.ts +30 -6
- package/dist/api/camera/PerspectiveCamera.d.ts.map +1 -1
- package/dist/api/content/ContentManager.d.ts +67 -2
- package/dist/api/content/ContentManager.d.ts.map +1 -1
- package/dist/api/content/IContentManager.d.ts +32 -1
- package/dist/api/content/IContentManager.d.ts.map +1 -1
- package/dist/api/content/ShaderModuleContent.d.ts.map +1 -1
- package/dist/api/data/Color.d.ts +5 -0
- package/dist/api/data/Color.d.ts.map +1 -1
- package/dist/api/geometry/BaseGeometry.d.ts +9 -0
- package/dist/api/geometry/BaseGeometry.d.ts.map +1 -1
- package/dist/api/geometry/GeometryBuilder.d.ts +3 -1
- package/dist/api/geometry/GeometryBuilder.d.ts.map +1 -1
- package/dist/api/geometry/IGeometryBuilder.d.ts +24 -0
- package/dist/api/geometry/IGeometryBuilder.d.ts.map +1 -1
- package/dist/api/geometry/_export-geometry.d.ts +1 -0
- package/dist/api/geometry/_export-geometry.d.ts.map +1 -1
- package/dist/api/input/InputManager.d.ts +4 -0
- package/dist/api/input/InputManager.d.ts.map +1 -1
- package/dist/api/lights/AmbientLight.d.ts +35 -0
- package/dist/api/lights/AmbientLight.d.ts.map +1 -0
- package/dist/api/lights/DirectionalLight.d.ts +40 -0
- package/dist/api/lights/DirectionalLight.d.ts.map +1 -0
- package/dist/api/lights/LightFactory.d.ts +18 -0
- package/dist/api/lights/LightFactory.d.ts.map +1 -0
- package/dist/api/lights/LightId.d.ts +6 -0
- package/dist/api/lights/LightId.d.ts.map +1 -0
- package/dist/api/lights/LightsBuffer.d.ts +70 -0
- package/dist/api/lights/LightsBuffer.d.ts.map +1 -0
- package/dist/api/lights/PointLight.d.ts +59 -0
- package/dist/api/lights/PointLight.d.ts.map +1 -0
- package/dist/api/lights/_export.d.ts +6 -0
- package/dist/api/lights/_export.d.ts.map +1 -0
- package/dist/api/loaders/ObjGeometryMapper.d.ts +19 -0
- package/dist/api/loaders/ObjGeometryMapper.d.ts.map +1 -0
- package/dist/api/loaders/_export.d.ts +1 -0
- package/dist/api/loaders/_export.d.ts.map +1 -1
- package/dist/api/loaders/obj/ObjLoader.d.ts +29 -0
- package/dist/api/loaders/obj/ObjLoader.d.ts.map +1 -0
- package/dist/api/loaders/obj/ObjTypes.d.ts +103 -0
- package/dist/api/loaders/obj/ObjTypes.d.ts.map +1 -0
- package/dist/api/loaders/obj/index.d.ts +3 -0
- package/dist/api/loaders/obj/index.d.ts.map +1 -0
- package/dist/api/material/ForwardBlinnPhongMaterial.d.ts +127 -0
- package/dist/api/material/ForwardBlinnPhongMaterial.d.ts.map +1 -0
- package/dist/api/material/IMaterial.d.ts +15 -6
- package/dist/api/material/IMaterial.d.ts.map +1 -1
- package/dist/api/material/IMaterialFactory.d.ts +17 -2
- package/dist/api/material/IMaterialFactory.d.ts.map +1 -1
- package/dist/api/material/InspectTextureMipsMaterial.d.ts +16 -7
- package/dist/api/material/InspectTextureMipsMaterial.d.ts.map +1 -1
- package/dist/api/material/MaterialFactory.d.ts +6 -0
- package/dist/api/material/MaterialFactory.d.ts.map +1 -1
- package/dist/api/material/SpriteMaterial.d.ts +51 -0
- package/dist/api/material/SpriteMaterial.d.ts.map +1 -0
- package/dist/api/material/UnlitMaterial.d.ts +28 -10
- package/dist/api/material/UnlitMaterial.d.ts.map +1 -1
- package/dist/api/material/_export.d.ts +2 -0
- package/dist/api/material/_export.d.ts.map +1 -1
- package/dist/api/meshes/IMesh.d.ts +35 -5
- package/dist/api/meshes/IMesh.d.ts.map +1 -1
- package/dist/api/meshes/IMeshFactory.d.ts +25 -4
- package/dist/api/meshes/IMeshFactory.d.ts.map +1 -1
- package/dist/api/meshes/Mesh.d.ts +58 -6
- package/dist/api/meshes/Mesh.d.ts.map +1 -1
- package/dist/api/meshes/MeshFactory.d.ts +7 -3
- package/dist/api/meshes/MeshFactory.d.ts.map +1 -1
- package/dist/api/meshes/QuadMesh.d.ts +5 -1
- package/dist/api/meshes/QuadMesh.d.ts.map +1 -1
- package/dist/api/meshes/SubMesh.d.ts +47 -0
- package/dist/api/meshes/SubMesh.d.ts.map +1 -0
- package/dist/api/meshes/_export.d.ts +1 -0
- package/dist/api/meshes/_export.d.ts.map +1 -1
- package/dist/api/rendering/render-pipelines/IForwardBlinnPhongRenderPipeline.d.ts +56 -0
- package/dist/api/rendering/render-pipelines/IForwardBlinnPhongRenderPipeline.d.ts.map +1 -0
- package/dist/api/rendering/render-pipelines/IRenderPipeline.d.ts +3 -0
- package/dist/api/rendering/render-pipelines/IRenderPipeline.d.ts.map +1 -1
- package/dist/api/rendering/render-pipelines/IRenderPipelineFactory.d.ts +29 -0
- package/dist/api/rendering/render-pipelines/IRenderPipelineFactory.d.ts.map +1 -1
- package/dist/api/rendering/render-pipelines/_export-render-pipelines.d.ts +1 -0
- package/dist/api/rendering/render-pipelines/_export-render-pipelines.d.ts.map +1 -1
- package/dist/api/scene/Scene.d.ts +104 -0
- package/dist/api/scene/Scene.d.ts.map +1 -0
- package/dist/api/shader/IShaderModuleLoader.d.ts +4 -1
- package/dist/api/shader/IShaderModuleLoader.d.ts.map +1 -1
- package/dist/api/shader/{shader-loader.d.ts → ShaderLoader.d.ts} +1 -1
- package/dist/api/shader/ShaderLoader.d.ts.map +1 -0
- package/dist/api/sprites/SpriteBatchDrawable.d.ts +1 -1
- package/dist/api/sprites/SpriteBatchDrawable.d.ts.map +1 -1
- package/dist/api/sprites/SpriteBatchMesh.d.ts +14 -1
- package/dist/api/sprites/SpriteBatchMesh.d.ts.map +1 -1
- package/dist/api/utilities/GeometryUtilities.d.ts.map +1 -1
- package/dist/api/window/WindowManager.d.ts +4 -0
- package/dist/api/window/WindowManager.d.ts.map +1 -1
- package/dist/core/rendering/vertex-buffer-layout.d.ts +1 -0
- package/dist/core/rendering/vertex-buffer-layout.d.ts.map +1 -1
- package/dist/index.js +2252 -260
- package/dist/webgl/WebGlGraphicsDevice.d.ts.map +1 -1
- package/dist/webgl/lights/WebGlAmbientLightFactory.d.ts +17 -0
- package/dist/webgl/lights/WebGlAmbientLightFactory.d.ts.map +1 -0
- package/dist/webgl/lights/WebGlLightsBuffer.d.ts +55 -0
- package/dist/webgl/lights/WebGlLightsBuffer.d.ts.map +1 -0
- package/dist/webgl/render-pipelines/AWebGlRenderPipeline.d.ts +3 -1
- package/dist/webgl/render-pipelines/AWebGlRenderPipeline.d.ts.map +1 -1
- package/dist/webgl/render-pipelines/WebGlMainRenderTargetRenderPipeline.d.ts +3 -0
- package/dist/webgl/render-pipelines/WebGlMainRenderTargetRenderPipeline.d.ts.map +1 -1
- package/dist/webgl/render-pipelines/WebGlRenderPipelineFactory.d.ts +3 -1
- package/dist/webgl/render-pipelines/WebGlRenderPipelineFactory.d.ts.map +1 -1
- package/dist/webgl/render-pipelines/inspect/WebGlInspectTextureMipsRenderPipeline.d.ts +3 -1
- package/dist/webgl/render-pipelines/inspect/WebGlInspectTextureMipsRenderPipeline.d.ts.map +1 -1
- package/dist/webgl/render-pipelines/material/WebGlBlinnPhongRenderPipeline.d.ts +88 -0
- package/dist/webgl/render-pipelines/material/WebGlBlinnPhongRenderPipeline.d.ts.map +1 -0
- package/dist/webgl/render-pipelines/material/WebGlUnlitRenderPipeline.d.ts +3 -1
- package/dist/webgl/render-pipelines/material/WebGlUnlitRenderPipeline.d.ts.map +1 -1
- package/dist/webgl/render-pipelines/sprite/WebGlSpriteRenderPipeline.d.ts +3 -1
- package/dist/webgl/render-pipelines/sprite/WebGlSpriteRenderPipeline.d.ts.map +1 -1
- package/dist/webgl/shader/WebGlShaderModule.d.ts +4 -1
- package/dist/webgl/shader/WebGlShaderModule.d.ts.map +1 -1
- package/dist/webgl/shader/WebGlShaderModuleLoader.d.ts +16 -5
- package/dist/webgl/shader/WebGlShaderModuleLoader.d.ts.map +1 -1
- package/dist/webgl/texture/WebGlTexture2D.d.ts +8 -1
- package/dist/webgl/texture/WebGlTexture2D.d.ts.map +1 -1
- package/dist/webgl/utilities/webgl-filter-anisotropic-utilities.d.ts +0 -1
- package/dist/webgl/utilities/webgl-filter-anisotropic-utilities.d.ts.map +1 -1
- package/package.json +4 -4
- package/shaders/glsl/forward_blinn_phong_material_fs.glsl +110 -0
- package/shaders/glsl/forward_blinn_phong_material_vs.glsl +32 -0
- package/shaders/glsl/sprite_fs.glsl +1 -1
- package/shaders/glsl/unlit_material_fs.glsl +1 -1
- package/src/Framework.ts +37 -13
- package/src/api/IFramework.ts +8 -6
- package/src/api/_export.ts +2 -0
- package/src/api/camera/CameraFactory.ts +6 -6
- package/src/api/camera/ICamera.ts +6 -0
- package/src/api/camera/ICameraFactory.ts +5 -10
- package/src/api/camera/OrthographicCamera.ts +23 -1
- package/src/api/camera/PerspectiveCamera.ts +60 -15
- package/src/api/content/ContentManager.ts +201 -5
- package/src/api/content/IContentManager.ts +33 -1
- package/src/api/content/ShaderModuleContent.ts +9 -0
- package/src/api/data/Color.ts +7 -1
- package/src/api/geometry/BaseGeometry.ts +23 -0
- package/src/api/geometry/GeometryBuilder.ts +14 -2
- package/src/api/geometry/IGeometryBuilder.ts +31 -1
- package/src/api/geometry/_export-geometry.ts +2 -1
- package/src/api/input/InputManager.ts +14 -5
- package/src/api/lights/AmbientLight.ts +53 -0
- package/src/api/lights/DirectionalLight.ts +60 -0
- package/src/api/lights/LightFactory.ts +21 -0
- package/src/api/lights/LightId.ts +9 -0
- package/src/api/lights/LightsBuffer.ts +87 -0
- package/src/api/lights/PointLight.ts +91 -0
- package/src/api/lights/_export.ts +5 -0
- package/src/api/loaders/ObjGeometryMapper.ts +28 -0
- package/src/api/loaders/_export.ts +2 -1
- package/src/api/loaders/obj/ObjLoader.ts +270 -0
- package/src/api/loaders/obj/ObjTypes.ts +124 -0
- package/src/api/loaders/obj/index.ts +5 -0
- package/src/api/material/ForwardBlinnPhongMaterial.ts +302 -0
- package/src/api/material/IMaterial.ts +16 -6
- package/src/api/material/IMaterialFactory.ts +19 -2
- package/src/api/material/InspectTextureMipsMaterial.ts +63 -24
- package/src/api/material/MaterialFactory.ts +12 -0
- package/src/api/material/SpriteMaterial.ts +83 -0
- package/src/api/material/UnlitMaterial.ts +101 -29
- package/src/api/material/_export.ts +3 -1
- package/src/api/meshes/IMesh.ts +45 -6
- package/src/api/meshes/IMeshFactory.ts +27 -4
- package/src/api/meshes/Mesh.ts +163 -41
- package/src/api/meshes/MeshFactory.ts +30 -6
- package/src/api/meshes/QuadMesh.ts +6 -2
- package/src/api/meshes/SubMesh.ts +71 -0
- package/src/api/meshes/_export.ts +1 -0
- package/src/api/rendering/render-pipelines/IForwardBlinnPhongRenderPipeline.ts +66 -0
- package/src/api/rendering/render-pipelines/IRenderPipeline.ts +4 -0
- package/src/api/rendering/render-pipelines/IRenderPipelineFactory.ts +39 -0
- package/src/api/rendering/render-pipelines/_export-render-pipelines.ts +2 -1
- package/src/api/scene/Scene.ts +264 -0
- package/src/api/shader/IShaderModuleLoader.ts +2 -1
- package/src/api/sprites/SpriteBatchDrawable.ts +12 -16
- package/src/api/sprites/SpriteBatchMesh.ts +23 -4
- package/src/api/utilities/GeometryUtilities.ts +2 -0
- package/src/api/window/WindowManager.ts +22 -2
- package/src/core/rendering/vertex-buffer-layout.ts +32 -1
- package/src/main.ts +0 -1
- package/src/webgl/WebGlGraphicsDevice.ts +0 -1
- package/src/webgl/lights/WebGlAmbientLightFactory.ts +35 -0
- package/src/webgl/lights/WebGlLightsBuffer.ts +215 -0
- package/src/webgl/render-pipelines/AWebGlRenderPipeline.ts +7 -1
- package/src/webgl/render-pipelines/WebGlMainRenderTargetRenderPipeline.ts +3 -0
- package/src/webgl/render-pipelines/WebGlRenderPipelineFactory.ts +24 -2
- package/src/webgl/render-pipelines/inspect/WebGlInspectTextureMipsRenderPipeline.ts +16 -6
- package/src/webgl/render-pipelines/material/WebGlBlinnPhongRenderPipeline.ts +300 -0
- package/src/webgl/render-pipelines/material/WebGlUnlitRenderPipeline.ts +16 -6
- package/src/webgl/render-pipelines/sprite/WebGlSpriteRenderPipeline.ts +11 -7
- package/src/webgl/shader/WebGlShaderModule.ts +11 -4
- package/src/webgl/shader/WebGlShaderModuleLoader.ts +29 -10
- package/src/webgl/texture/WebGlTexture2D.ts +19 -5
- package/src/webgl/utilities/webgl-filter-anisotropic-utilities.ts +9 -6
- package/dist/api/shader/shader-loader.d.ts.map +0 -1
- /package/src/api/shader/{shader-loader.ts → ShaderLoader.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { vec4, vec3, mat4, vec2 } from "gl-matrix";
|
|
1
|
+
import { vec4, vec3, mat4, vec2, quat } from "gl-matrix";
|
|
3
2
|
var RenderingBackend = /* @__PURE__ */ ((RenderingBackend2) => {
|
|
4
3
|
RenderingBackend2[RenderingBackend2["DEFAULT"] = 0] = "DEFAULT";
|
|
5
4
|
RenderingBackend2[RenderingBackend2["WEB_GPU"] = 1] = "WEB_GPU";
|
|
@@ -284,6 +283,13 @@ class Color {
|
|
|
284
283
|
*/
|
|
285
284
|
lerp(a2, b2, t2, o2) {
|
|
286
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Copy self and return new instance with same values.
|
|
288
|
+
* @return Color
|
|
289
|
+
* */
|
|
290
|
+
copy() {
|
|
291
|
+
return new Color(this.r, this.g, this.b, this.a);
|
|
292
|
+
}
|
|
287
293
|
}
|
|
288
294
|
class TextureDescriptor {
|
|
289
295
|
/**
|
|
@@ -953,6 +959,142 @@ var GeometryFormat = /* @__PURE__ */ ((GeometryFormat2) => {
|
|
|
953
959
|
GeometryFormat2[GeometryFormat2["POS3_TEXTURECOORDS2"] = 4] = "POS3_TEXTURECOORDS2";
|
|
954
960
|
return GeometryFormat2;
|
|
955
961
|
})(GeometryFormat || {});
|
|
962
|
+
class BaseGeometry {
|
|
963
|
+
/**
|
|
964
|
+
* Initializes a new instance of the BaseGeometry class.
|
|
965
|
+
* @param positions The optional positions of the geometry.
|
|
966
|
+
* @param indices The optional indices of the geometry.
|
|
967
|
+
* @param normals The optional normals of the geometry.
|
|
968
|
+
* @param textureCoords The optional texture coordinates of the geometry.
|
|
969
|
+
* @param colors The optional colors of the geometry.
|
|
970
|
+
*/
|
|
971
|
+
constructor(positions, indices, normals, textureCoords, colors) {
|
|
972
|
+
this.positions = positions;
|
|
973
|
+
this.indices = indices;
|
|
974
|
+
this.normals = normals;
|
|
975
|
+
this.textureCoords = textureCoords;
|
|
976
|
+
this.colors = colors;
|
|
977
|
+
if (this.indices && this.indices.length > 0) {
|
|
978
|
+
this.vertexCount = this.indices.length / 3;
|
|
979
|
+
} else if (this.positions && this.positions.length > 0) {
|
|
980
|
+
this.vertexCount = this.positions.length / 3;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
/** @inheritdoc */
|
|
984
|
+
vertexCount = 0;
|
|
985
|
+
/** @inheritdoc */
|
|
986
|
+
positions;
|
|
987
|
+
/** @inheritdoc */
|
|
988
|
+
positionsFormat = VertexFormat.FLOAT_32X3;
|
|
989
|
+
/** @inheritdoc */
|
|
990
|
+
colors;
|
|
991
|
+
/** @inheritdoc */
|
|
992
|
+
colorFormat = VertexFormat.FLOAT_32X4;
|
|
993
|
+
/** @inheritdoc */
|
|
994
|
+
textureCoords;
|
|
995
|
+
/** @inheritdoc */
|
|
996
|
+
textureFormat = VertexFormat.FLOAT_32X2;
|
|
997
|
+
/** @inheritdoc */
|
|
998
|
+
normals;
|
|
999
|
+
/** @inheritdoc */
|
|
1000
|
+
normalsFormat = VertexFormat.FLOAT_32X3;
|
|
1001
|
+
/** The indices of geometry. */
|
|
1002
|
+
indices;
|
|
1003
|
+
/**
|
|
1004
|
+
* Pushes vertex data from source format to destination format.
|
|
1005
|
+
* @param index The index of the vertex to push.
|
|
1006
|
+
* @param source The source vertex data.
|
|
1007
|
+
* @param sourceFormat The vertex format of the source vertex data.
|
|
1008
|
+
* @param destination The destination array to push the vertex data to.
|
|
1009
|
+
* @param destinationFormat The vertex format of the destination vertex data.
|
|
1010
|
+
*/
|
|
1011
|
+
pushFormat(index, source, sourceFormat, destination, destinationFormat) {
|
|
1012
|
+
let _0 = 0;
|
|
1013
|
+
let _1 = 0;
|
|
1014
|
+
let _2 = 0;
|
|
1015
|
+
let _3 = 0;
|
|
1016
|
+
if (sourceFormat == VertexFormat.FLOAT_32) {
|
|
1017
|
+
_0 = source[index];
|
|
1018
|
+
} else if (sourceFormat == VertexFormat.FLOAT_32X2) {
|
|
1019
|
+
_0 = source[index * 2];
|
|
1020
|
+
_1 = source[index * 2 + 1];
|
|
1021
|
+
} else if (sourceFormat == VertexFormat.FLOAT_32X3) {
|
|
1022
|
+
_0 = source[index * 3];
|
|
1023
|
+
_1 = source[index * 3 + 1];
|
|
1024
|
+
_2 = source[index * 3 + 2];
|
|
1025
|
+
} else if (sourceFormat == VertexFormat.FLOAT_32X4) {
|
|
1026
|
+
_0 = source[index * 4];
|
|
1027
|
+
_1 = source[index * 4 + 1];
|
|
1028
|
+
_2 = source[index * 4 + 2];
|
|
1029
|
+
_3 = source[index * 4 + 3];
|
|
1030
|
+
}
|
|
1031
|
+
destination.push(_0);
|
|
1032
|
+
if (destinationFormat >= VertexFormat.FLOAT_32X2) {
|
|
1033
|
+
destination.push(_1);
|
|
1034
|
+
}
|
|
1035
|
+
if (destinationFormat >= VertexFormat.FLOAT_32X3) {
|
|
1036
|
+
destination.push(_2);
|
|
1037
|
+
}
|
|
1038
|
+
if (destinationFormat >= VertexFormat.FLOAT_32X4) {
|
|
1039
|
+
destination.push(_3);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Converts the geometry data to interleaved vertex data based on the specified geometry format.
|
|
1044
|
+
* @param format The geometry format to convert the geometry data to.
|
|
1045
|
+
* @returns The interleaved vertex data as an array of numbers.
|
|
1046
|
+
*/
|
|
1047
|
+
toInterleaved(format) {
|
|
1048
|
+
let interleavedData = [];
|
|
1049
|
+
switch (format) {
|
|
1050
|
+
case GeometryFormat.POS3_COLOR4: {
|
|
1051
|
+
let count = this.positions.length / 3;
|
|
1052
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
1053
|
+
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
1054
|
+
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
1055
|
+
}
|
|
1056
|
+
break;
|
|
1057
|
+
}
|
|
1058
|
+
case GeometryFormat.POS3_TEXTURECOORDS2: {
|
|
1059
|
+
let count = this.positions.length / 3;
|
|
1060
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
1061
|
+
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
1062
|
+
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X2);
|
|
1063
|
+
}
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2: {
|
|
1067
|
+
let count = this.positions.length / 3;
|
|
1068
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
1069
|
+
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
1070
|
+
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
1071
|
+
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X2);
|
|
1072
|
+
}
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2_NORMAL3: {
|
|
1076
|
+
let count = this.positions.length / 3;
|
|
1077
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
1078
|
+
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
1079
|
+
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
1080
|
+
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X2);
|
|
1081
|
+
this.pushFormat(i2, this.normals, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
1082
|
+
}
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2_ALIGNED: {
|
|
1086
|
+
let count = this.positions.length / 3;
|
|
1087
|
+
for (let i2 = 0; i2 < count; i2++) {
|
|
1088
|
+
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X4);
|
|
1089
|
+
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
1090
|
+
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X4);
|
|
1091
|
+
}
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
return interleavedData;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
956
1098
|
var LoadAction = /* @__PURE__ */ ((LoadAction2) => {
|
|
957
1099
|
LoadAction2[LoadAction2["CLEAR"] = 0] = "CLEAR";
|
|
958
1100
|
LoadAction2[LoadAction2["LOAD"] = 1] = "LOAD";
|
|
@@ -1398,19 +1540,30 @@ class PerspectiveCamera {
|
|
|
1398
1540
|
_viewBuffer = null;
|
|
1399
1541
|
_projectionBuffer = null;
|
|
1400
1542
|
/**
|
|
1401
|
-
* The constructor.
|
|
1543
|
+
* The constructor. Call `initialize()` before use (the camera factory does).
|
|
1402
1544
|
* @param framework The framework.
|
|
1403
|
-
* @param
|
|
1404
|
-
* @
|
|
1405
|
-
* @param
|
|
1406
|
-
* @param farPlane The far plane.
|
|
1545
|
+
* @param descriptor The optional descriptor; omitted fields use the defaults documented on {@link PerspectiveCameraDescriptor}.
|
|
1546
|
+
* `fov` is in degrees; {@link fieldOfView} holds it in radians. Vectors are copied.
|
|
1547
|
+
* @param scene The optional scene; the camera assigns itself as `scene.camera`.
|
|
1407
1548
|
*/
|
|
1408
|
-
constructor(framework,
|
|
1549
|
+
constructor(framework, descriptor, scene) {
|
|
1409
1550
|
this._framework = framework;
|
|
1410
|
-
this.fieldOfView = fov;
|
|
1411
|
-
this.aspectRatio = aspectRatio;
|
|
1412
|
-
this.nearPlane =
|
|
1413
|
-
this.farPlane =
|
|
1551
|
+
this.fieldOfView = MathHelper.toRadians(descriptor?.fov ?? 60);
|
|
1552
|
+
this.aspectRatio = descriptor?.aspectRatio ?? 1;
|
|
1553
|
+
this.nearPlane = descriptor?.near ?? 0.01;
|
|
1554
|
+
this.farPlane = descriptor?.far ?? 100;
|
|
1555
|
+
if (descriptor?.eye) {
|
|
1556
|
+
vec3.copy(this.eye, descriptor.eye);
|
|
1557
|
+
}
|
|
1558
|
+
if (descriptor?.target) {
|
|
1559
|
+
vec3.copy(this.target, descriptor.target);
|
|
1560
|
+
}
|
|
1561
|
+
if (descriptor?.up) {
|
|
1562
|
+
vec3.copy(this.up, descriptor.up);
|
|
1563
|
+
}
|
|
1564
|
+
if (scene) {
|
|
1565
|
+
scene.add(this);
|
|
1566
|
+
}
|
|
1414
1567
|
this._updateMatrices();
|
|
1415
1568
|
}
|
|
1416
1569
|
/**
|
|
@@ -1505,11 +1658,18 @@ class PerspectiveCamera {
|
|
|
1505
1658
|
*/
|
|
1506
1659
|
initialize() {
|
|
1507
1660
|
const bufferUsage = BufferUsage.UNIFORM | BufferUsage.COPY_DST;
|
|
1508
|
-
this._positionBuffer = this._framework.bufferFactory.createUniformBuffer(this.
|
|
1661
|
+
this._positionBuffer = this._framework.bufferFactory.createUniformBuffer(this._worldEye(), bufferUsage);
|
|
1509
1662
|
this._projectionViewBuffer = this._framework.bufferFactory.createUniformBuffer(this.projectionViewMatrix, bufferUsage);
|
|
1510
1663
|
this._projectionBuffer = this._framework.bufferFactory.createUniformBuffer(this.projectionMatrix, bufferUsage);
|
|
1511
1664
|
this._viewBuffer = this._framework.bufferFactory.createUniformBuffer(this.viewMatrix, bufferUsage);
|
|
1512
1665
|
}
|
|
1666
|
+
/**
|
|
1667
|
+
* The eye in world space, as used by the view matrix: z is flipped for the right-handed look-at.
|
|
1668
|
+
* This is what {@link positionBuffer} holds, std140 `vec3` (padded to 16 bytes).
|
|
1669
|
+
*/
|
|
1670
|
+
_worldEye() {
|
|
1671
|
+
return [this.eye[0], this.eye[1], -this.eye[2], 0];
|
|
1672
|
+
}
|
|
1513
1673
|
/**
|
|
1514
1674
|
* Updates the camera matrices.
|
|
1515
1675
|
*/
|
|
@@ -1528,7 +1688,7 @@ class PerspectiveCamera {
|
|
|
1528
1688
|
}
|
|
1529
1689
|
/** @inheritDoc */
|
|
1530
1690
|
updateBuffers() {
|
|
1531
|
-
this._positionBuffer.update(this.
|
|
1691
|
+
this._positionBuffer.update(this._worldEye());
|
|
1532
1692
|
this._projectionBuffer.update(this.projectionMatrix);
|
|
1533
1693
|
this._viewBuffer.update(this.viewMatrix);
|
|
1534
1694
|
this._projectionViewBuffer.update(this.projectionViewMatrix);
|
|
@@ -2192,6 +2352,9 @@ class OrthographicCamera {
|
|
|
2192
2352
|
_projectionBuffer = null;
|
|
2193
2353
|
_viewBuffer = null;
|
|
2194
2354
|
_projectionViewBuffer = null;
|
|
2355
|
+
_positionBuffer = null;
|
|
2356
|
+
/** std140 vec3 world-space eye, padded to 4 floats. */
|
|
2357
|
+
_positionData = [0, 0, 0, 0];
|
|
2195
2358
|
/**
|
|
2196
2359
|
*
|
|
2197
2360
|
* @param framework
|
|
@@ -2210,7 +2373,7 @@ class OrthographicCamera {
|
|
|
2210
2373
|
this._bottom = bottom;
|
|
2211
2374
|
this._near = near;
|
|
2212
2375
|
this._far = far;
|
|
2213
|
-
this._eye = vec3.fromValues(0, 0,
|
|
2376
|
+
this._eye = vec3.fromValues(0, 0, 1);
|
|
2214
2377
|
this._target = vec3.fromValues(0, 0, 0);
|
|
2215
2378
|
this._zSpaceLength = 2;
|
|
2216
2379
|
this._updateMatrices();
|
|
@@ -2316,6 +2479,17 @@ class OrthographicCamera {
|
|
|
2316
2479
|
get viewBuffer() {
|
|
2317
2480
|
return this._viewBuffer;
|
|
2318
2481
|
}
|
|
2482
|
+
/** @inheritDoc */
|
|
2483
|
+
get positionBuffer() {
|
|
2484
|
+
return this._positionBuffer;
|
|
2485
|
+
}
|
|
2486
|
+
/** Writes the world-space eye into the std140 position data (no z flip: the look-at uses the eye as is). */
|
|
2487
|
+
_updatePositionData() {
|
|
2488
|
+
this._positionData[0] = this._eye[0];
|
|
2489
|
+
this._positionData[1] = this._eye[1];
|
|
2490
|
+
this._positionData[2] = this._eye[2];
|
|
2491
|
+
return this._positionData;
|
|
2492
|
+
}
|
|
2319
2493
|
/**
|
|
2320
2494
|
* Updates the camera matrices.
|
|
2321
2495
|
*/
|
|
@@ -2341,6 +2515,11 @@ class OrthographicCamera {
|
|
|
2341
2515
|
BufferUsage.UNIFORM,
|
|
2342
2516
|
"OrthographicCamera.projectionViewBuffer"
|
|
2343
2517
|
);
|
|
2518
|
+
this._positionBuffer = this._framework.bufferFactory.createUniformBuffer(
|
|
2519
|
+
this._updatePositionData(),
|
|
2520
|
+
BufferUsage.UNIFORM,
|
|
2521
|
+
"OrthographicCamera.positionBuffer"
|
|
2522
|
+
);
|
|
2344
2523
|
}
|
|
2345
2524
|
/** @inheritDoc */
|
|
2346
2525
|
updateBuffers() {
|
|
@@ -2348,6 +2527,7 @@ class OrthographicCamera {
|
|
|
2348
2527
|
this.projectionBuffer.update(this.projectionMatrix);
|
|
2349
2528
|
this.viewBuffer.update(this.viewMatrix);
|
|
2350
2529
|
this.projectionViewBuffer.update(this.projectionViewMatrix);
|
|
2530
|
+
this.positionBuffer.update(this._updatePositionData());
|
|
2351
2531
|
}
|
|
2352
2532
|
/** @inheritDoc */
|
|
2353
2533
|
update(_2) {
|
|
@@ -2361,6 +2541,7 @@ class OrthographicCamera {
|
|
|
2361
2541
|
this.projectionBuffer.dispose();
|
|
2362
2542
|
this.projectionViewBuffer.dispose();
|
|
2363
2543
|
this.viewBuffer.dispose();
|
|
2544
|
+
this.positionBuffer.dispose();
|
|
2364
2545
|
}
|
|
2365
2546
|
}
|
|
2366
2547
|
class CameraFactory {
|
|
@@ -2391,12 +2572,11 @@ class CameraFactory {
|
|
|
2391
2572
|
}
|
|
2392
2573
|
/** @inheritDoc */
|
|
2393
2574
|
createOrbitCamera(fov = 60, aspectRatio = 1, near = 0.01, far = 100) {
|
|
2394
|
-
return new OrbitCamera(this.createPerspectiveCamera(fov, aspectRatio, near, far), this._framework.input);
|
|
2575
|
+
return new OrbitCamera(this.createPerspectiveCamera({ fov, aspectRatio, near, far }), this._framework.input);
|
|
2395
2576
|
}
|
|
2396
2577
|
/** @inheritDoc */
|
|
2397
|
-
createPerspectiveCamera(
|
|
2398
|
-
|
|
2399
|
-
const camera = new PerspectiveCamera(this._framework, fov, aspectRatio, near, far);
|
|
2578
|
+
createPerspectiveCamera(descriptor, scene) {
|
|
2579
|
+
const camera = new PerspectiveCamera(this._framework, descriptor, scene);
|
|
2400
2580
|
camera.initialize();
|
|
2401
2581
|
return camera;
|
|
2402
2582
|
}
|
|
@@ -2534,6 +2714,268 @@ class ImageLoader {
|
|
|
2534
2714
|
);
|
|
2535
2715
|
}
|
|
2536
2716
|
}
|
|
2717
|
+
class ObjMaterialDefinition {
|
|
2718
|
+
/**
|
|
2719
|
+
* The constructor.
|
|
2720
|
+
* @param name The material name from `newmtl`.
|
|
2721
|
+
*/
|
|
2722
|
+
constructor(name) {
|
|
2723
|
+
this.name = name;
|
|
2724
|
+
}
|
|
2725
|
+
name;
|
|
2726
|
+
/** Ambient colour (`Ka`). */
|
|
2727
|
+
ambientColor;
|
|
2728
|
+
/** Diffuse colour (`Kd`). */
|
|
2729
|
+
diffuseColor;
|
|
2730
|
+
/** Specular colour (`Ks`). */
|
|
2731
|
+
specularColor;
|
|
2732
|
+
/** Emissive colour (`Ke`). */
|
|
2733
|
+
emissiveColor;
|
|
2734
|
+
/** Specular exponent (`Ns`). */
|
|
2735
|
+
specularExponent;
|
|
2736
|
+
/** Opacity, from `d`, or `1 - Tr` when only `Tr` is given. */
|
|
2737
|
+
opacity;
|
|
2738
|
+
/** Optical density / index of refraction (`Ni`). */
|
|
2739
|
+
opticalDensity;
|
|
2740
|
+
/** Illumination model (`illum`). */
|
|
2741
|
+
illuminationModel;
|
|
2742
|
+
/**
|
|
2743
|
+
* Texture maps by statement name, e.g. `map_Kd`, `map_Ks`, `map_Bump`, `bump`, `disp`, `decal`, `refl`.
|
|
2744
|
+
* The value is the file name (last token); map options such as `-bm 1` are kept in {@link mapOptions}.
|
|
2745
|
+
*/
|
|
2746
|
+
maps = {};
|
|
2747
|
+
/** Options written before the file name of a map statement, e.g. `{ map_Bump: "-bm 0.5" }`. */
|
|
2748
|
+
mapOptions = {};
|
|
2749
|
+
}
|
|
2750
|
+
function tokens(line) {
|
|
2751
|
+
return line.trim().split(/\s+/);
|
|
2752
|
+
}
|
|
2753
|
+
function readColor(parts) {
|
|
2754
|
+
if (parts.length < 2 || isNaN(parseFloat(parts[1]))) {
|
|
2755
|
+
return void 0;
|
|
2756
|
+
}
|
|
2757
|
+
const r3 = parseFloat(parts[1]);
|
|
2758
|
+
const g2 = parts.length > 2 ? parseFloat(parts[2]) : r3;
|
|
2759
|
+
const b2 = parts.length > 3 ? parseFloat(parts[3]) : r3;
|
|
2760
|
+
return [r3, g2, b2];
|
|
2761
|
+
}
|
|
2762
|
+
class ObjLoader {
|
|
2763
|
+
/**
|
|
2764
|
+
* Parses .mtl text.
|
|
2765
|
+
* @param text The .mtl contents.
|
|
2766
|
+
* @returns The material definitions, in file order.
|
|
2767
|
+
*/
|
|
2768
|
+
parseMtl(text) {
|
|
2769
|
+
const materials = [];
|
|
2770
|
+
let current;
|
|
2771
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
2772
|
+
const line = rawLine.trim();
|
|
2773
|
+
if (line.length == 0 || line.startsWith("#")) {
|
|
2774
|
+
continue;
|
|
2775
|
+
}
|
|
2776
|
+
const parts = tokens(line);
|
|
2777
|
+
const keyword = parts[0];
|
|
2778
|
+
if (keyword == "newmtl") {
|
|
2779
|
+
current = new ObjMaterialDefinition(line.substring(keyword.length).trim());
|
|
2780
|
+
materials.push(current);
|
|
2781
|
+
continue;
|
|
2782
|
+
}
|
|
2783
|
+
if (!current) {
|
|
2784
|
+
continue;
|
|
2785
|
+
}
|
|
2786
|
+
switch (keyword) {
|
|
2787
|
+
case "Ka":
|
|
2788
|
+
current.ambientColor = readColor(parts);
|
|
2789
|
+
break;
|
|
2790
|
+
case "Kd":
|
|
2791
|
+
current.diffuseColor = readColor(parts);
|
|
2792
|
+
break;
|
|
2793
|
+
case "Ks":
|
|
2794
|
+
current.specularColor = readColor(parts);
|
|
2795
|
+
break;
|
|
2796
|
+
case "Ke":
|
|
2797
|
+
current.emissiveColor = readColor(parts);
|
|
2798
|
+
break;
|
|
2799
|
+
case "Ns":
|
|
2800
|
+
current.specularExponent = parseFloat(parts[1]);
|
|
2801
|
+
break;
|
|
2802
|
+
case "Ni":
|
|
2803
|
+
current.opticalDensity = parseFloat(parts[1]);
|
|
2804
|
+
break;
|
|
2805
|
+
case "illum":
|
|
2806
|
+
current.illuminationModel = parseInt(parts[1]);
|
|
2807
|
+
break;
|
|
2808
|
+
case "d":
|
|
2809
|
+
current.opacity = parseFloat(parts[parts.length - 1]);
|
|
2810
|
+
break;
|
|
2811
|
+
case "Tr":
|
|
2812
|
+
if (current.opacity === void 0) {
|
|
2813
|
+
current.opacity = 1 - parseFloat(parts[1]);
|
|
2814
|
+
}
|
|
2815
|
+
break;
|
|
2816
|
+
default:
|
|
2817
|
+
if (keyword.startsWith("map_") || keyword == "bump" || keyword == "disp" || keyword == "decal" || keyword == "refl" || keyword == "norm") {
|
|
2818
|
+
if (parts.length >= 2) {
|
|
2819
|
+
current.maps[keyword] = parts[parts.length - 1];
|
|
2820
|
+
if (parts.length > 2) {
|
|
2821
|
+
current.mapOptions[keyword] = parts.slice(1, -1).join(" ");
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
break;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
return materials;
|
|
2829
|
+
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Parses .obj text.
|
|
2832
|
+
* @param text The .obj contents.
|
|
2833
|
+
* @param materials Material definitions to use for `usemtl`, e.g. from {@link parseMtl}. Default none.
|
|
2834
|
+
* @returns The geometry, materials and raw data.
|
|
2835
|
+
*/
|
|
2836
|
+
fromText(text, materials = []) {
|
|
2837
|
+
const rawPositions = [];
|
|
2838
|
+
const rawTextureCoords = [];
|
|
2839
|
+
const rawNormals = [];
|
|
2840
|
+
const faceIndices = [];
|
|
2841
|
+
const materialLibraries = [];
|
|
2842
|
+
const materialRanges = [];
|
|
2843
|
+
const positions = [];
|
|
2844
|
+
const textureCoords = [];
|
|
2845
|
+
const normals = [];
|
|
2846
|
+
const colors = [];
|
|
2847
|
+
const indices = [];
|
|
2848
|
+
const vertexLookup = /* @__PURE__ */ new Map();
|
|
2849
|
+
const materialsByName = new Map(materials.map((m2) => [m2.name, m2]));
|
|
2850
|
+
let materialName;
|
|
2851
|
+
let rangeStart = 0;
|
|
2852
|
+
const closeRange = () => {
|
|
2853
|
+
if (indices.length > rangeStart) {
|
|
2854
|
+
materialRanges.push({ materialName, indexStart: rangeStart, indexCount: indices.length - rangeStart });
|
|
2855
|
+
}
|
|
2856
|
+
rangeStart = indices.length;
|
|
2857
|
+
};
|
|
2858
|
+
const resolve = (value, count) => {
|
|
2859
|
+
if (value === void 0 || value.length == 0) {
|
|
2860
|
+
return -1;
|
|
2861
|
+
}
|
|
2862
|
+
const index = parseInt(value);
|
|
2863
|
+
return index < 0 ? count + index : index - 1;
|
|
2864
|
+
};
|
|
2865
|
+
const addCorner = (p2, t2, n2) => {
|
|
2866
|
+
faceIndices.push(p2, t2, n2);
|
|
2867
|
+
const key = `${p2}/${t2}/${n2}/${materialName ?? ""}`;
|
|
2868
|
+
let vertex = vertexLookup.get(key);
|
|
2869
|
+
if (vertex === void 0) {
|
|
2870
|
+
vertex = positions.length / 3;
|
|
2871
|
+
vertexLookup.set(key, vertex);
|
|
2872
|
+
positions.push(rawPositions[p2 * 3], rawPositions[p2 * 3 + 1], rawPositions[p2 * 3 + 2]);
|
|
2873
|
+
textureCoords.push(t2 >= 0 ? rawTextureCoords[t2 * 2] : 0, t2 >= 0 ? rawTextureCoords[t2 * 2 + 1] : 0);
|
|
2874
|
+
normals.push(n2 >= 0 ? rawNormals[n2 * 3] : 0, n2 >= 0 ? rawNormals[n2 * 3 + 1] : 0, n2 >= 0 ? rawNormals[n2 * 3 + 2] : 0);
|
|
2875
|
+
const material = materialName !== void 0 ? materialsByName.get(materialName) : void 0;
|
|
2876
|
+
const kd = material?.diffuseColor ?? [1, 1, 1];
|
|
2877
|
+
colors.push(kd[0], kd[1], kd[2], material?.opacity ?? 1);
|
|
2878
|
+
}
|
|
2879
|
+
indices.push(vertex);
|
|
2880
|
+
};
|
|
2881
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
2882
|
+
const line = rawLine.trim();
|
|
2883
|
+
if (line.length == 0 || line.startsWith("#")) {
|
|
2884
|
+
continue;
|
|
2885
|
+
}
|
|
2886
|
+
const parts = tokens(line);
|
|
2887
|
+
switch (parts[0]) {
|
|
2888
|
+
case "v":
|
|
2889
|
+
rawPositions.push(parseFloat(parts[1]), parseFloat(parts[2]), parseFloat(parts[3]));
|
|
2890
|
+
break;
|
|
2891
|
+
case "vt":
|
|
2892
|
+
rawTextureCoords.push(parseFloat(parts[1]), parts.length > 2 ? parseFloat(parts[2]) : 0);
|
|
2893
|
+
break;
|
|
2894
|
+
case "vn":
|
|
2895
|
+
rawNormals.push(parseFloat(parts[1]), parseFloat(parts[2]), parseFloat(parts[3]));
|
|
2896
|
+
break;
|
|
2897
|
+
case "f": {
|
|
2898
|
+
const corners = parts.slice(1).map((corner) => {
|
|
2899
|
+
const [p2, t2, n2] = corner.split("/");
|
|
2900
|
+
return [
|
|
2901
|
+
resolve(p2, rawPositions.length / 3),
|
|
2902
|
+
resolve(t2, rawTextureCoords.length / 2),
|
|
2903
|
+
resolve(n2, rawNormals.length / 3)
|
|
2904
|
+
];
|
|
2905
|
+
});
|
|
2906
|
+
for (let i2 = 1; i2 + 1 < corners.length; i2++) {
|
|
2907
|
+
addCorner(corners[0][0], corners[0][1], corners[0][2]);
|
|
2908
|
+
addCorner(corners[i2][0], corners[i2][1], corners[i2][2]);
|
|
2909
|
+
addCorner(corners[i2 + 1][0], corners[i2 + 1][1], corners[i2 + 1][2]);
|
|
2910
|
+
}
|
|
2911
|
+
break;
|
|
2912
|
+
}
|
|
2913
|
+
case "mtllib":
|
|
2914
|
+
materialLibraries.push(...parts.slice(1));
|
|
2915
|
+
break;
|
|
2916
|
+
case "usemtl":
|
|
2917
|
+
closeRange();
|
|
2918
|
+
materialName = line.substring(parts[0].length).trim();
|
|
2919
|
+
break;
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
closeRange();
|
|
2923
|
+
if (positions.length == 0) {
|
|
2924
|
+
throw new Error("Invalid obj file.");
|
|
2925
|
+
}
|
|
2926
|
+
return {
|
|
2927
|
+
geometry: {
|
|
2928
|
+
positions,
|
|
2929
|
+
textureCoords,
|
|
2930
|
+
normals,
|
|
2931
|
+
colors,
|
|
2932
|
+
indices,
|
|
2933
|
+
vertexCount: positions.length / 3,
|
|
2934
|
+
materialRanges
|
|
2935
|
+
},
|
|
2936
|
+
materials,
|
|
2937
|
+
materialLibraries,
|
|
2938
|
+
raw: { positions: rawPositions, textureCoords: rawTextureCoords, normals: rawNormals, faceIndices }
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Loads an .obj file and the .mtl files it references with `mtllib`.
|
|
2943
|
+
* A missing or failing .mtl is skipped (logged as a warning); the model still loads with white vertex colours.
|
|
2944
|
+
* @param filePath The .obj URL.
|
|
2945
|
+
* @param options Optional: how to resolve `mtllib` names, or skip materials.
|
|
2946
|
+
* @returns The geometry, materials and raw data.
|
|
2947
|
+
*/
|
|
2948
|
+
async loadAsync(filePath, options) {
|
|
2949
|
+
const response = await fetch(filePath);
|
|
2950
|
+
if (!response.ok) {
|
|
2951
|
+
throw new Error(`Failed to load OBJ '${filePath}': ${response.status} ${response.statusText}`);
|
|
2952
|
+
}
|
|
2953
|
+
const text = await response.text();
|
|
2954
|
+
const materials = [];
|
|
2955
|
+
const libraries = options?.ignoreMaterials ? [] : text.split(/\r?\n/).map((line) => tokens(line)).filter((parts) => parts[0] == "mtllib").flatMap((parts) => parts.slice(1));
|
|
2956
|
+
const opaqueUrl = /^(data|blob):/i.test(filePath);
|
|
2957
|
+
for (const library of libraries) {
|
|
2958
|
+
try {
|
|
2959
|
+
let mtlUrl;
|
|
2960
|
+
if (options?.resolveMaterialUrl) {
|
|
2961
|
+
mtlUrl = options.resolveMaterialUrl(library);
|
|
2962
|
+
} else if (opaqueUrl) {
|
|
2963
|
+
throw new Error("the OBJ was loaded from a data: or blob: URL (e.g. a bundler-inlined asset), so the name cannot be resolved; pass options.resolveMaterialUrl");
|
|
2964
|
+
} else {
|
|
2965
|
+
mtlUrl = filePath.substring(0, filePath.lastIndexOf("/") + 1) + library;
|
|
2966
|
+
}
|
|
2967
|
+
const mtlResponse = await fetch(mtlUrl);
|
|
2968
|
+
if (!mtlResponse.ok) {
|
|
2969
|
+
throw new Error(`${mtlResponse.status} ${mtlResponse.statusText}`);
|
|
2970
|
+
}
|
|
2971
|
+
materials.push(...this.parseMtl(await mtlResponse.text()));
|
|
2972
|
+
} catch (error) {
|
|
2973
|
+
console.warn(`ObjLoader: could not load material library '${library}'.`, error);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
return this.fromText(text, materials);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2537
2979
|
class WindowBounds {
|
|
2538
2980
|
/**
|
|
2539
2981
|
* Creates window bounds with the given width and height.
|
|
@@ -2557,6 +2999,8 @@ class GeometryUtilities {
|
|
|
2557
2999
|
switch (geometryFormat) {
|
|
2558
3000
|
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2:
|
|
2559
3001
|
return (3 + 4 + 2) * Float32Array.BYTES_PER_ELEMENT;
|
|
3002
|
+
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2_NORMAL3:
|
|
3003
|
+
return (3 + 4 + 2 + 3) * Float32Array.BYTES_PER_ELEMENT;
|
|
2560
3004
|
default:
|
|
2561
3005
|
throw new Error("Unknown geometryFormat");
|
|
2562
3006
|
}
|
|
@@ -2580,8 +3024,88 @@ class MeshParameters {
|
|
|
2580
3024
|
*/
|
|
2581
3025
|
colors;
|
|
2582
3026
|
}
|
|
3027
|
+
class SubMesh {
|
|
3028
|
+
/**
|
|
3029
|
+
* The constructor.
|
|
3030
|
+
* @param parent The mesh that owns the buffers.
|
|
3031
|
+
* @param material The material of this submesh.
|
|
3032
|
+
* @param indexStart The first index of the range.
|
|
3033
|
+
* @param indexCount The number of indices in the range.
|
|
3034
|
+
*/
|
|
3035
|
+
constructor(parent, material, indexStart, indexCount) {
|
|
3036
|
+
this.parent = parent;
|
|
3037
|
+
this.material = material;
|
|
3038
|
+
this.indexStart = indexStart;
|
|
3039
|
+
this.indexCount = indexCount;
|
|
3040
|
+
}
|
|
3041
|
+
parent;
|
|
3042
|
+
material;
|
|
3043
|
+
indexStart;
|
|
3044
|
+
indexCount;
|
|
3045
|
+
/** The parent's vertex buffer. */
|
|
3046
|
+
get vertexBuffer() {
|
|
3047
|
+
return this.parent.vertexBuffer;
|
|
3048
|
+
}
|
|
3049
|
+
/** The parent's index buffer. */
|
|
3050
|
+
get indexBuffer() {
|
|
3051
|
+
return this.parent.indexBuffer;
|
|
3052
|
+
}
|
|
3053
|
+
/** The parent's position; setting it sets the parent's. */
|
|
3054
|
+
get position() {
|
|
3055
|
+
return this.parent.position;
|
|
3056
|
+
}
|
|
3057
|
+
set position(value) {
|
|
3058
|
+
this.parent.position = value;
|
|
3059
|
+
}
|
|
3060
|
+
/** The parent's rotation; setting it sets the parent's. */
|
|
3061
|
+
get rotation() {
|
|
3062
|
+
return this.parent.rotation;
|
|
3063
|
+
}
|
|
3064
|
+
set rotation(value) {
|
|
3065
|
+
this.parent.rotation = value;
|
|
3066
|
+
}
|
|
3067
|
+
/** The parent's scale; setting it sets the parent's. */
|
|
3068
|
+
get scale() {
|
|
3069
|
+
return this.parent.scale;
|
|
3070
|
+
}
|
|
3071
|
+
set scale(value) {
|
|
3072
|
+
this.parent.scale = value;
|
|
3073
|
+
}
|
|
3074
|
+
/** The parent's model matrix. */
|
|
3075
|
+
get modelMatrix() {
|
|
3076
|
+
return this.parent.modelMatrix;
|
|
3077
|
+
}
|
|
3078
|
+
/** The parent's model buffer. */
|
|
3079
|
+
get modelBuffer() {
|
|
3080
|
+
return this.parent.modelBuffer;
|
|
3081
|
+
}
|
|
3082
|
+
/** Draws this submesh with its material, using the parent's transform (model buffer). */
|
|
3083
|
+
render() {
|
|
3084
|
+
this.parent.__assignModelBuffer(this.material);
|
|
3085
|
+
this.material.beforeRender();
|
|
3086
|
+
this.material.renderMesh(this);
|
|
3087
|
+
}
|
|
3088
|
+
/** Does nothing: the buffers belong to the parent and the material to the caller. */
|
|
3089
|
+
dispose() {
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
2583
3092
|
class Mesh {
|
|
3093
|
+
/**
|
|
3094
|
+
* The constructor.
|
|
3095
|
+
* @param _framework The framework.
|
|
3096
|
+
* @param scene The scene. The mesh adds itself to it and removes itself on dispose.
|
|
3097
|
+
* `null` only for internal meshes drawn outside a scene (e.g. the sprite batch).
|
|
3098
|
+
* @param _material The material. Optional for a mesh that only holds submeshes.
|
|
3099
|
+
*/
|
|
3100
|
+
constructor(_framework, scene, _material) {
|
|
3101
|
+
this._framework = _framework;
|
|
3102
|
+
this.scene = scene;
|
|
3103
|
+
this._material = _material;
|
|
3104
|
+
scene?.add(this);
|
|
3105
|
+
}
|
|
2584
3106
|
_framework;
|
|
3107
|
+
scene;
|
|
3108
|
+
_material;
|
|
2585
3109
|
_bufferUsage = BufferUsage.VERTEX;
|
|
2586
3110
|
_state = State.CREATED;
|
|
2587
3111
|
_vertexData;
|
|
@@ -2592,12 +3116,95 @@ class Mesh {
|
|
|
2592
3116
|
_normals;
|
|
2593
3117
|
_vertexBuffer;
|
|
2594
3118
|
_indexBuffer;
|
|
3119
|
+
_subMeshes = [];
|
|
3120
|
+
_modelMatrix = mat4.create();
|
|
3121
|
+
_rotationQuat = quat.create();
|
|
3122
|
+
// Cached copies to detect in-place edits; NaN forces the first computation.
|
|
3123
|
+
_lastPosition = vec3.fromValues(NaN, NaN, NaN);
|
|
3124
|
+
_lastRotation = vec3.create();
|
|
3125
|
+
_lastScale = vec3.create();
|
|
3126
|
+
_modelBuffer;
|
|
3127
|
+
/** The matrix last uploaded to the model buffer. */
|
|
3128
|
+
_uploadedMatrix = mat4.create();
|
|
3129
|
+
/** @inheritDoc */
|
|
3130
|
+
position = vec3.create();
|
|
3131
|
+
/** @inheritDoc */
|
|
3132
|
+
rotation = vec3.create();
|
|
3133
|
+
/** @inheritDoc */
|
|
3134
|
+
scale = vec3.fromValues(1, 1, 1);
|
|
3135
|
+
/** @inheritDoc */
|
|
3136
|
+
get modelMatrix() {
|
|
3137
|
+
if (!vec3.exactEquals(this._lastPosition, this.position) || !vec3.exactEquals(this._lastRotation, this.rotation) || !vec3.exactEquals(this._lastScale, this.scale)) {
|
|
3138
|
+
vec3.copy(this._lastPosition, this.position);
|
|
3139
|
+
vec3.copy(this._lastRotation, this.rotation);
|
|
3140
|
+
vec3.copy(this._lastScale, this.scale);
|
|
3141
|
+
const q = quat.identity(this._rotationQuat);
|
|
3142
|
+
quat.rotateY(q, q, this.rotation[1]);
|
|
3143
|
+
quat.rotateX(q, q, this.rotation[0]);
|
|
3144
|
+
quat.rotateZ(q, q, this.rotation[2]);
|
|
3145
|
+
mat4.fromRotationTranslationScale(this._modelMatrix, q, this.position, this.scale);
|
|
3146
|
+
}
|
|
3147
|
+
return this._modelMatrix;
|
|
3148
|
+
}
|
|
3149
|
+
/** @inheritDoc Created on first access and disposed with the mesh. */
|
|
3150
|
+
get modelBuffer() {
|
|
3151
|
+
if (!this._modelBuffer) {
|
|
3152
|
+
mat4.copy(this._uploadedMatrix, this.modelMatrix);
|
|
3153
|
+
this._modelBuffer = this._framework.bufferFactory.createUniformBuffer(
|
|
3154
|
+
this._uploadedMatrix,
|
|
3155
|
+
BufferUsage.UNIFORM | BufferUsage.COPY_DST,
|
|
3156
|
+
"Mesh.modelBuffer"
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
return this._modelBuffer;
|
|
3160
|
+
}
|
|
3161
|
+
/** Uploads the model matrix when the transform changed since the last upload. */
|
|
3162
|
+
_updateModelBuffer() {
|
|
3163
|
+
const buffer = this.modelBuffer;
|
|
3164
|
+
const matrix = this.modelMatrix;
|
|
3165
|
+
if (!mat4.exactEquals(this._uploadedMatrix, matrix)) {
|
|
3166
|
+
mat4.copy(this._uploadedMatrix, matrix);
|
|
3167
|
+
buffer.update(this._uploadedMatrix);
|
|
3168
|
+
}
|
|
3169
|
+
return buffer;
|
|
3170
|
+
}
|
|
2595
3171
|
/**
|
|
2596
|
-
*
|
|
2597
|
-
*
|
|
3172
|
+
* Assigns this mesh's model buffer to a material that uses one (has a `modelBuffer` property),
|
|
3173
|
+
* only when it differs. Materials without one (e.g. sprites) are skipped and no buffer is created for them.
|
|
3174
|
+
* @internal
|
|
2598
3175
|
*/
|
|
2599
|
-
|
|
2600
|
-
|
|
3176
|
+
__assignModelBuffer(material) {
|
|
3177
|
+
if (!("modelBuffer" in material)) {
|
|
3178
|
+
return;
|
|
3179
|
+
}
|
|
3180
|
+
const buffer = this._updateModelBuffer();
|
|
3181
|
+
if (material.modelBuffer !== buffer) {
|
|
3182
|
+
material.modelBuffer = buffer;
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
/** The material. Undefined for a mesh drawn through its submeshes. */
|
|
3186
|
+
get material() {
|
|
3187
|
+
return this._material;
|
|
3188
|
+
}
|
|
3189
|
+
/** Sets the material. */
|
|
3190
|
+
set material(material) {
|
|
3191
|
+
this._material = material;
|
|
3192
|
+
}
|
|
3193
|
+
/** The submeshes. They share this mesh's vertex and index buffers. */
|
|
3194
|
+
get subMeshes() {
|
|
3195
|
+
return this._subMeshes;
|
|
3196
|
+
}
|
|
3197
|
+
/**
|
|
3198
|
+
* Adds a submesh that draws a range of this mesh's index buffer with its own material.
|
|
3199
|
+
* @param material The material of the submesh.
|
|
3200
|
+
* @param indexStart The first index of the range.
|
|
3201
|
+
* @param indexCount The number of indices in the range.
|
|
3202
|
+
* @returns The submesh.
|
|
3203
|
+
*/
|
|
3204
|
+
addSubMesh(material, indexStart, indexCount) {
|
|
3205
|
+
const subMesh = new SubMesh(this, material, indexStart, indexCount);
|
|
3206
|
+
this._subMeshes.push(subMesh);
|
|
3207
|
+
return subMesh;
|
|
2601
3208
|
}
|
|
2602
3209
|
/**
|
|
2603
3210
|
* The state of a mesh.
|
|
@@ -2726,11 +3333,14 @@ class Mesh {
|
|
|
2726
3333
|
* Sets the mesh from a geometry.
|
|
2727
3334
|
*
|
|
2728
3335
|
* This will create vertex buffer and index buffer from geometry.
|
|
2729
|
-
* Positions, Colors, TextureCoordinates and Normals will be ignored.
|
|
2730
3336
|
* @param geometry - The geometry.
|
|
2731
|
-
* @param
|
|
3337
|
+
* @param geometryFormat - The vertex layout. Defaults to the material's geometry format.
|
|
2732
3338
|
*/
|
|
2733
|
-
setGeometry(geometry,
|
|
3339
|
+
setGeometry(geometry, geometryFormat) {
|
|
3340
|
+
const format = geometryFormat ?? this._material?.geometryFormat;
|
|
3341
|
+
if (format === void 0) {
|
|
3342
|
+
throw new Error("Cannot create buffers for a Mesh without a material or geometry format.");
|
|
3343
|
+
}
|
|
2734
3344
|
if (geometry.vertexCount == 0) {
|
|
2735
3345
|
const message = "Cannot create buffers for a Mesh as Mesh has no positions. Check that geometry.vertexCount is set.";
|
|
2736
3346
|
throw new Error(message);
|
|
@@ -2759,25 +3369,44 @@ class Mesh {
|
|
|
2759
3369
|
this._colors = meshParameters.colors;
|
|
2760
3370
|
this.applyChanges();
|
|
2761
3371
|
}
|
|
2762
|
-
/**
|
|
2763
|
-
|
|
2764
|
-
|
|
3372
|
+
/** @inheritDoc */
|
|
3373
|
+
render() {
|
|
3374
|
+
if (this.subMeshes?.length > 0) {
|
|
3375
|
+
for (let i2 = 0; i2 < this.subMeshes.length; ++i2) {
|
|
3376
|
+
this.subMeshes[i2].render();
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
if (this.material) {
|
|
3380
|
+
this.__assignModelBuffer(this.material);
|
|
3381
|
+
this.material.beforeRender();
|
|
3382
|
+
this.material.renderMesh(this);
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
/**
|
|
3386
|
+
* Dispose of the mesh.
|
|
3387
|
+
*/
|
|
2765
3388
|
dispose() {
|
|
3389
|
+
this.scene?.remove(this);
|
|
3390
|
+
this._subMeshes.length = 0;
|
|
2766
3391
|
this._vertexBuffer?.dispose();
|
|
2767
3392
|
this._vertexBuffer = void 0;
|
|
2768
3393
|
this._indexBuffer?.dispose();
|
|
2769
3394
|
this._indexBuffer = void 0;
|
|
3395
|
+
this._modelBuffer?.dispose();
|
|
3396
|
+
this._modelBuffer = void 0;
|
|
2770
3397
|
}
|
|
2771
3398
|
}
|
|
2772
3399
|
class QuadMesh extends Mesh {
|
|
2773
3400
|
/**
|
|
2774
3401
|
* The constructor.
|
|
2775
3402
|
* @param framework The framework.
|
|
3403
|
+
* @param scene The scene the quad adds itself to.
|
|
3404
|
+
* @param material The material.
|
|
2776
3405
|
* @param _isUpdatable Makes mesh updatable.
|
|
2777
3406
|
* @param scale The scale factor for geometry. If not set, it is set to (1,1).
|
|
2778
3407
|
*/
|
|
2779
|
-
constructor(framework, _isUpdatable, scale) {
|
|
2780
|
-
super(framework);
|
|
3408
|
+
constructor(framework, scene, material, _isUpdatable, scale) {
|
|
3409
|
+
super(framework, scene, material);
|
|
2781
3410
|
this._isUpdatable = _isUpdatable;
|
|
2782
3411
|
if (_isUpdatable) {
|
|
2783
3412
|
this._bufferUsage = BufferUsage.VERTEX | BufferUsage.COPY_DST;
|
|
@@ -2887,38 +3516,65 @@ class MeshFactory {
|
|
|
2887
3516
|
}
|
|
2888
3517
|
_framework;
|
|
2889
3518
|
/** @inheritDoc */
|
|
2890
|
-
create(geometry,
|
|
2891
|
-
const mesh = new Mesh(this._framework);
|
|
2892
|
-
mesh.setGeometry(geometry
|
|
3519
|
+
create(scene, geometry, material) {
|
|
3520
|
+
const mesh = new Mesh(this._framework, scene, material);
|
|
3521
|
+
mesh.setGeometry(geometry);
|
|
3522
|
+
return mesh;
|
|
3523
|
+
}
|
|
3524
|
+
/** @inheritDoc */
|
|
3525
|
+
createWithSubMeshes(scene, geometry, subMeshes) {
|
|
3526
|
+
if (subMeshes.length == 0) {
|
|
3527
|
+
throw new Error("createWithSubMeshes needs at least one submesh.");
|
|
3528
|
+
}
|
|
3529
|
+
const format = subMeshes[0].material.geometryFormat;
|
|
3530
|
+
if (subMeshes.some((s2) => s2.material.geometryFormat != format)) {
|
|
3531
|
+
throw new Error("All submesh materials must use the same geometry format, as they share one vertex buffer.");
|
|
3532
|
+
}
|
|
3533
|
+
const mesh = new Mesh(this._framework, scene);
|
|
3534
|
+
mesh.setGeometry(geometry, format);
|
|
3535
|
+
for (const subMesh of subMeshes) {
|
|
3536
|
+
mesh.addSubMesh(subMesh.material, subMesh.indexStart, subMesh.indexCount);
|
|
3537
|
+
}
|
|
2893
3538
|
return mesh;
|
|
2894
3539
|
}
|
|
2895
3540
|
/** @inheritDoc */
|
|
2896
|
-
createQuadMesh(
|
|
2897
|
-
|
|
2898
|
-
quadMesh.initialize();
|
|
2899
|
-
return quadMesh;
|
|
3541
|
+
createQuadMesh(_isUpdatable, _scale) {
|
|
3542
|
+
throw new Error("Meshes not implemented.");
|
|
2900
3543
|
}
|
|
2901
3544
|
}
|
|
2902
3545
|
class InspectTextureMipsMaterial {
|
|
2903
3546
|
_framework;
|
|
2904
3547
|
_mipLevel = [0];
|
|
3548
|
+
/** Created when the model buffer is assigned (the pipeline requires one). */
|
|
2905
3549
|
_pipeline;
|
|
3550
|
+
/** Texture and sampler set before the pipeline exists; applied when it is created. */
|
|
3551
|
+
_pendingTexture;
|
|
3552
|
+
_pendingSampler;
|
|
3553
|
+
_samplerSet = false;
|
|
2906
3554
|
_projectionViewBuffer;
|
|
2907
3555
|
_fragmentDataBuffer;
|
|
2908
3556
|
_modelBuffer;
|
|
2909
|
-
_previousModelMatrix = mat4.create();
|
|
2910
3557
|
constructor(framework) {
|
|
2911
3558
|
this._framework = framework;
|
|
2912
|
-
void this._framework;
|
|
2913
3559
|
const usage = BufferUsage.UNIFORM | BufferUsage.COPY_DST;
|
|
2914
|
-
this._projectionViewBuffer = framework.bufferFactory.createUniformBuffer(
|
|
2915
|
-
this._modelBuffer = framework.bufferFactory.createUniformBuffer(this._previousModelMatrix, usage);
|
|
3560
|
+
this._projectionViewBuffer = framework.bufferFactory.createUniformBuffer(mat4.create(), usage);
|
|
2916
3561
|
this._fragmentDataBuffer = framework.bufferFactory.createUniformBuffer(this._mipLevel, usage);
|
|
2917
|
-
|
|
3562
|
+
}
|
|
3563
|
+
/** Creates the pipeline with the current bindings. */
|
|
3564
|
+
_createPipeline(modelBuffer) {
|
|
3565
|
+
const pipeline = this._framework.renderPipelineFactory.createInspectTextureMipsRenderPipeline(
|
|
2918
3566
|
this._projectionViewBuffer,
|
|
2919
|
-
|
|
3567
|
+
modelBuffer,
|
|
2920
3568
|
this._fragmentDataBuffer
|
|
2921
3569
|
);
|
|
3570
|
+
if (this._pendingTexture) {
|
|
3571
|
+
pipeline.spriteTexture = this._pendingTexture;
|
|
3572
|
+
}
|
|
3573
|
+
if (this._samplerSet) {
|
|
3574
|
+
pipeline.textureSampler = this._pendingSampler;
|
|
3575
|
+
}
|
|
3576
|
+
this._pipeline = pipeline;
|
|
3577
|
+
return pipeline;
|
|
2922
3578
|
}
|
|
2923
3579
|
/**
|
|
2924
3580
|
* The mip level to inspect.
|
|
@@ -2928,30 +3584,55 @@ class InspectTextureMipsMaterial {
|
|
|
2928
3584
|
* The texture to inspect.
|
|
2929
3585
|
*/
|
|
2930
3586
|
get texture() {
|
|
2931
|
-
return this._pipeline.
|
|
3587
|
+
return this._pipeline?.spriteTexture ?? this._pendingTexture;
|
|
2932
3588
|
}
|
|
2933
3589
|
/**
|
|
2934
3590
|
* The texture to inspect.
|
|
2935
3591
|
*/
|
|
2936
3592
|
set texture(value) {
|
|
2937
|
-
this.
|
|
3593
|
+
this._pendingTexture = value;
|
|
3594
|
+
if (this._pipeline) {
|
|
3595
|
+
this._pipeline.spriteTexture = value;
|
|
3596
|
+
}
|
|
2938
3597
|
}
|
|
2939
3598
|
/**
|
|
2940
3599
|
* The texture sampler.
|
|
2941
3600
|
*/
|
|
2942
3601
|
get textureSampler() {
|
|
2943
|
-
return this._pipeline.textureSampler;
|
|
3602
|
+
return this._pipeline ? this._pipeline.textureSampler : this._pendingSampler;
|
|
2944
3603
|
}
|
|
2945
3604
|
/**
|
|
2946
3605
|
* The texture sampler.
|
|
2947
3606
|
*/
|
|
2948
3607
|
set textureSampler(value) {
|
|
2949
|
-
this.
|
|
3608
|
+
this._pendingSampler = value;
|
|
3609
|
+
this._samplerSet = true;
|
|
3610
|
+
if (this._pipeline) {
|
|
3611
|
+
this._pipeline.textureSampler = value;
|
|
3612
|
+
}
|
|
2950
3613
|
}
|
|
2951
3614
|
/**
|
|
2952
|
-
* The model
|
|
3615
|
+
* The model buffer (std140 `mat4`). Owned by the mesh, which assigns it before drawing; never disposed by this material.
|
|
3616
|
+
* Undefined until assigned: rendering without one throws.
|
|
2953
3617
|
*/
|
|
2954
|
-
|
|
3618
|
+
get modelBuffer() {
|
|
3619
|
+
return this._modelBuffer;
|
|
3620
|
+
}
|
|
3621
|
+
/** Binds the model buffer to the pipeline. */
|
|
3622
|
+
set modelBuffer(value) {
|
|
3623
|
+
if (value === this._modelBuffer) {
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
this._modelBuffer = value;
|
|
3627
|
+
if (!value) {
|
|
3628
|
+
return;
|
|
3629
|
+
}
|
|
3630
|
+
if (this._pipeline) {
|
|
3631
|
+
this._pipeline.modelBuffer = value;
|
|
3632
|
+
} else {
|
|
3633
|
+
this._createPipeline(value);
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
2955
3636
|
/**
|
|
2956
3637
|
* The geometry format.
|
|
2957
3638
|
*/
|
|
@@ -2962,44 +3643,49 @@ class InspectTextureMipsMaterial {
|
|
|
2962
3643
|
this._mipLevel[0] = this.mipLevel;
|
|
2963
3644
|
this._fragmentDataBuffer.update(this._mipLevel);
|
|
2964
3645
|
}
|
|
2965
|
-
if (!mat4.equals(this._previousModelMatrix, this.modelMatrix)) {
|
|
2966
|
-
mat4.copy(this._previousModelMatrix, this.modelMatrix);
|
|
2967
|
-
this._modelBuffer.update(this._previousModelMatrix);
|
|
2968
|
-
}
|
|
2969
3646
|
}
|
|
2970
3647
|
/** @inheritDoc */
|
|
2971
3648
|
renderMesh(mesh) {
|
|
2972
|
-
this.render(mesh.vertexBuffer, mesh.indexBuffer);
|
|
3649
|
+
this.render(mesh.vertexBuffer, mesh.indexBuffer, mesh.indexCount ?? -1, mesh.indexStart ?? 0);
|
|
2973
3650
|
}
|
|
2974
3651
|
/** @inheritDoc */
|
|
2975
|
-
render(vertexBuffer, indexBuffer) {
|
|
2976
|
-
this.
|
|
3652
|
+
render(vertexBuffer, indexBuffer, indicesCount, indicesOffset) {
|
|
3653
|
+
if (!this._modelBuffer) {
|
|
3654
|
+
throw new Error("InspectTextureMipsMaterial: no modelBuffer. Render it through a mesh (the mesh assigns its model buffer) or set material.modelBuffer.");
|
|
3655
|
+
}
|
|
3656
|
+
(this._pipeline ?? this._createPipeline(this._modelBuffer)).render(vertexBuffer, indexBuffer, indicesCount, indicesOffset);
|
|
2977
3657
|
}
|
|
2978
3658
|
/** @inheritDoc */
|
|
2979
3659
|
dispose() {
|
|
2980
|
-
this._pipeline
|
|
3660
|
+
this._pipeline?.dispose();
|
|
3661
|
+
this._pipeline = void 0;
|
|
2981
3662
|
this._projectionViewBuffer.dispose();
|
|
2982
|
-
this._modelBuffer.dispose();
|
|
2983
3663
|
this._fragmentDataBuffer.dispose();
|
|
2984
3664
|
}
|
|
2985
3665
|
}
|
|
2986
3666
|
class UnlitMaterialDescriptor {
|
|
2987
3667
|
/** The primitive state. */
|
|
2988
3668
|
primitiveState;
|
|
2989
|
-
/** The
|
|
2990
|
-
|
|
3669
|
+
/** The camera used to render this material. */
|
|
3670
|
+
camera;
|
|
2991
3671
|
}
|
|
2992
3672
|
class UnlitMaterial {
|
|
2993
3673
|
_framework;
|
|
2994
3674
|
_color = Color.white();
|
|
2995
3675
|
_materialData = [1, 1, 1, 1];
|
|
3676
|
+
/** Created when the model buffer is assigned (the pipeline requires one). */
|
|
2996
3677
|
_pipeline;
|
|
3678
|
+
_primitiveState;
|
|
3679
|
+
/** Texture and sampler set before the pipeline exists; applied when it is created. */
|
|
3680
|
+
_pendingTexture;
|
|
3681
|
+
_pendingSampler;
|
|
3682
|
+
_samplerSet = false;
|
|
2997
3683
|
_projectionViewBuffer;
|
|
2998
3684
|
/** False when the projection view buffer came from the descriptor. The caller owns that one. */
|
|
2999
3685
|
_ownsProjectionViewBuffer;
|
|
3686
|
+
_camera;
|
|
3000
3687
|
_materialBuffer;
|
|
3001
3688
|
_modelBuffer;
|
|
3002
|
-
_previousModelMatrix = mat4.create();
|
|
3003
3689
|
/**
|
|
3004
3690
|
* The constructor.
|
|
3005
3691
|
* @param framework The framework.
|
|
@@ -3007,18 +3693,54 @@ class UnlitMaterial {
|
|
|
3007
3693
|
*/
|
|
3008
3694
|
constructor(framework, config) {
|
|
3009
3695
|
this._framework = framework;
|
|
3010
|
-
void this._framework;
|
|
3011
3696
|
const usage = BufferUsage.UNIFORM | BufferUsage.COPY_DST;
|
|
3012
|
-
this.
|
|
3013
|
-
this.
|
|
3014
|
-
this.
|
|
3697
|
+
this._camera = config?.camera;
|
|
3698
|
+
this._ownsProjectionViewBuffer = !config?.camera?.projectionViewBuffer;
|
|
3699
|
+
this._projectionViewBuffer = config?.camera?.projectionViewBuffer ?? framework.bufferFactory.createUniformBuffer(mat4.create(), usage);
|
|
3015
3700
|
this._materialBuffer = framework.bufferFactory.createUniformBuffer(this._materialData, usage);
|
|
3016
|
-
this.
|
|
3701
|
+
this._primitiveState = config?.primitiveState;
|
|
3702
|
+
}
|
|
3703
|
+
/** Creates the pipeline with the current bindings. */
|
|
3704
|
+
_createPipeline(modelBuffer) {
|
|
3705
|
+
const pipeline = this._framework.renderPipelineFactory.createUnlitRenderPipeline(
|
|
3017
3706
|
this._projectionViewBuffer,
|
|
3018
|
-
|
|
3707
|
+
modelBuffer,
|
|
3019
3708
|
this._materialBuffer,
|
|
3020
|
-
|
|
3709
|
+
this._primitiveState
|
|
3021
3710
|
);
|
|
3711
|
+
if (this._pendingTexture) {
|
|
3712
|
+
pipeline.diffuseTexture = this._pendingTexture;
|
|
3713
|
+
}
|
|
3714
|
+
if (this._samplerSet) {
|
|
3715
|
+
pipeline.diffuseTextureSampler = this._pendingSampler;
|
|
3716
|
+
}
|
|
3717
|
+
this._pipeline = pipeline;
|
|
3718
|
+
return pipeline;
|
|
3719
|
+
}
|
|
3720
|
+
/**
|
|
3721
|
+
* The camera. Setting it binds the camera's projection view buffer to the pipeline
|
|
3722
|
+
* (the material's own placeholder buffer is disposed). Setting undefined keeps the last bound buffer.
|
|
3723
|
+
*/
|
|
3724
|
+
get camera() {
|
|
3725
|
+
return this._camera;
|
|
3726
|
+
}
|
|
3727
|
+
/** @inheritDoc */
|
|
3728
|
+
set camera(camera) {
|
|
3729
|
+
if (camera === this._camera) {
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
this._camera = camera;
|
|
3733
|
+
if (!camera) {
|
|
3734
|
+
return;
|
|
3735
|
+
}
|
|
3736
|
+
if (this._ownsProjectionViewBuffer) {
|
|
3737
|
+
this._projectionViewBuffer.dispose();
|
|
3738
|
+
this._ownsProjectionViewBuffer = false;
|
|
3739
|
+
}
|
|
3740
|
+
this._projectionViewBuffer = camera.projectionViewBuffer;
|
|
3741
|
+
if (this._pipeline) {
|
|
3742
|
+
this._pipeline.projectionViewBuffer = this._projectionViewBuffer;
|
|
3743
|
+
}
|
|
3022
3744
|
}
|
|
3023
3745
|
/**
|
|
3024
3746
|
* The diffuse color.
|
|
@@ -3028,30 +3750,55 @@ class UnlitMaterial {
|
|
|
3028
3750
|
* The texture to inspect.
|
|
3029
3751
|
*/
|
|
3030
3752
|
get diffuseTexture() {
|
|
3031
|
-
return this._pipeline.
|
|
3753
|
+
return this._pipeline?.diffuseTexture ?? this._pendingTexture;
|
|
3032
3754
|
}
|
|
3033
3755
|
/**
|
|
3034
3756
|
* The texture to inspect.
|
|
3035
3757
|
*/
|
|
3036
3758
|
set diffuseTexture(value) {
|
|
3037
|
-
this.
|
|
3759
|
+
this._pendingTexture = value;
|
|
3760
|
+
if (this._pipeline) {
|
|
3761
|
+
this._pipeline.diffuseTexture = value;
|
|
3762
|
+
}
|
|
3038
3763
|
}
|
|
3039
3764
|
/**
|
|
3040
3765
|
* The texture sampler.
|
|
3041
3766
|
*/
|
|
3042
3767
|
get diffuseTextureSampler() {
|
|
3043
|
-
return this._pipeline.diffuseTextureSampler;
|
|
3768
|
+
return this._pipeline ? this._pipeline.diffuseTextureSampler : this._pendingSampler;
|
|
3044
3769
|
}
|
|
3045
3770
|
/**
|
|
3046
3771
|
* The texture sampler.
|
|
3047
3772
|
*/
|
|
3048
3773
|
set diffuseTextureSampler(value) {
|
|
3049
|
-
this.
|
|
3774
|
+
this._pendingSampler = value;
|
|
3775
|
+
this._samplerSet = true;
|
|
3776
|
+
if (this._pipeline) {
|
|
3777
|
+
this._pipeline.diffuseTextureSampler = value;
|
|
3778
|
+
}
|
|
3050
3779
|
}
|
|
3051
3780
|
/**
|
|
3052
|
-
* The model
|
|
3781
|
+
* The model buffer (std140 `mat4`). Owned by the mesh, which assigns it before drawing; never disposed by this material.
|
|
3782
|
+
* Undefined until assigned: rendering without one throws.
|
|
3053
3783
|
*/
|
|
3054
|
-
|
|
3784
|
+
get modelBuffer() {
|
|
3785
|
+
return this._modelBuffer;
|
|
3786
|
+
}
|
|
3787
|
+
/** Binds the model buffer to the pipeline. */
|
|
3788
|
+
set modelBuffer(value) {
|
|
3789
|
+
if (value === this._modelBuffer) {
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
this._modelBuffer = value;
|
|
3793
|
+
if (!value) {
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
if (this._pipeline) {
|
|
3797
|
+
this._pipeline.modelBuffer = value;
|
|
3798
|
+
} else {
|
|
3799
|
+
this._createPipeline(value);
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3055
3802
|
/**
|
|
3056
3803
|
* The geometry format.
|
|
3057
3804
|
*/
|
|
@@ -3066,26 +3813,321 @@ class UnlitMaterial {
|
|
|
3066
3813
|
this._materialData[3] = this._color.a;
|
|
3067
3814
|
this._materialBuffer.update(this._materialData);
|
|
3068
3815
|
}
|
|
3069
|
-
if (!mat4.equals(this._previousModelMatrix, this.modelMatrix)) {
|
|
3070
|
-
mat4.copy(this._previousModelMatrix, this.modelMatrix);
|
|
3071
|
-
this._modelBuffer.update(this._previousModelMatrix);
|
|
3072
|
-
}
|
|
3073
3816
|
}
|
|
3074
3817
|
/** @inheritDoc */
|
|
3075
3818
|
renderMesh(mesh) {
|
|
3076
|
-
this.render(mesh.vertexBuffer, mesh.indexBuffer);
|
|
3819
|
+
this.render(mesh.vertexBuffer, mesh.indexBuffer, mesh.indexCount ?? -1, mesh.indexStart ?? 0);
|
|
3077
3820
|
}
|
|
3078
3821
|
/** @inheritDoc */
|
|
3079
|
-
render(vertexBuffer, indexBuffer) {
|
|
3080
|
-
this.
|
|
3822
|
+
render(vertexBuffer, indexBuffer, indicesCount, indicesOffset) {
|
|
3823
|
+
if (!this._modelBuffer) {
|
|
3824
|
+
throw new Error("UnlitMaterial: no modelBuffer. Render it through a mesh (the mesh assigns its model buffer) or set material.modelBuffer.");
|
|
3825
|
+
}
|
|
3826
|
+
(this._pipeline ?? this._createPipeline(this._modelBuffer)).render(vertexBuffer, indexBuffer, indicesCount, indicesOffset);
|
|
3081
3827
|
}
|
|
3082
3828
|
/** @inheritDoc */
|
|
3083
3829
|
dispose() {
|
|
3084
|
-
this._pipeline
|
|
3830
|
+
this._pipeline?.dispose();
|
|
3831
|
+
this._pipeline = void 0;
|
|
3085
3832
|
if (this._ownsProjectionViewBuffer) {
|
|
3086
3833
|
this._projectionViewBuffer.dispose();
|
|
3087
3834
|
}
|
|
3088
|
-
this.
|
|
3835
|
+
this._materialBuffer.dispose();
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
class SpriteMaterial {
|
|
3839
|
+
_renderPipeline;
|
|
3840
|
+
/**
|
|
3841
|
+
* The constructor.
|
|
3842
|
+
* @param framework - The framework.
|
|
3843
|
+
* @param descriptor - The sprite material descriptor.
|
|
3844
|
+
*/
|
|
3845
|
+
constructor(framework, descriptor) {
|
|
3846
|
+
this._renderPipeline = framework.renderPipelineFactory.createSpriteRenderPipeline(descriptor.projectionViewBuffer);
|
|
3847
|
+
this._renderPipeline.textureSampler = descriptor.sampler;
|
|
3848
|
+
this._renderPipeline.spriteTexture = descriptor.texture;
|
|
3849
|
+
}
|
|
3850
|
+
/** @inheritDoc */
|
|
3851
|
+
get geometryFormat() {
|
|
3852
|
+
return this._renderPipeline.geometryFormat;
|
|
3853
|
+
}
|
|
3854
|
+
/** The sprite texture. */
|
|
3855
|
+
get texture() {
|
|
3856
|
+
return this._renderPipeline.spriteTexture;
|
|
3857
|
+
}
|
|
3858
|
+
/** The sprite texture. */
|
|
3859
|
+
set texture(value) {
|
|
3860
|
+
this._renderPipeline.spriteTexture = value;
|
|
3861
|
+
}
|
|
3862
|
+
/** @inheritDoc */
|
|
3863
|
+
beforeRender() {
|
|
3864
|
+
}
|
|
3865
|
+
/** @inheritDoc */
|
|
3866
|
+
renderMesh(mesh) {
|
|
3867
|
+
this.render(mesh.vertexBuffer, mesh.indexBuffer, mesh.indexCount ?? -1, mesh.indexStart ?? 0);
|
|
3868
|
+
}
|
|
3869
|
+
/**
|
|
3870
|
+
* Renders the material.
|
|
3871
|
+
* @param vertexBuffer - The vertex buffer.
|
|
3872
|
+
* @param indexBuffer - The index buffer.
|
|
3873
|
+
* @param indicesCount - The number of indices to draw. If -1 draws all indices.
|
|
3874
|
+
* @param indicesOffset - Indices offset. Defaults to 0.
|
|
3875
|
+
*/
|
|
3876
|
+
render(vertexBuffer, indexBuffer, indicesCount, indicesOffset) {
|
|
3877
|
+
this._renderPipeline.render(vertexBuffer, indexBuffer, indicesCount, indicesOffset);
|
|
3878
|
+
}
|
|
3879
|
+
/** @inheritDoc */
|
|
3880
|
+
dispose() {
|
|
3881
|
+
this._renderPipeline.dispose();
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
class ForwardBlinnPhongMaterialDescriptor {
|
|
3885
|
+
/** The primitive state. */
|
|
3886
|
+
primitiveState;
|
|
3887
|
+
/** The projection view buffer. */
|
|
3888
|
+
projectionViewBuffer;
|
|
3889
|
+
/** The camera position buffer. */
|
|
3890
|
+
cameraPositionBuffer;
|
|
3891
|
+
/** The lights buffer. The shader is compiled for its light counts. Can also be set later through `material.lightsBuffer`. */
|
|
3892
|
+
lightsBuffer;
|
|
3893
|
+
}
|
|
3894
|
+
class ForwardBlinnPhongMaterial {
|
|
3895
|
+
_framework;
|
|
3896
|
+
_lightsBuffer;
|
|
3897
|
+
_diffuseColorCache;
|
|
3898
|
+
_ambientColorCache;
|
|
3899
|
+
_specularColorCache;
|
|
3900
|
+
_shininessCache;
|
|
3901
|
+
// std140: each vec3 starts on a 16-byte boundary, so pad each colour to 4 floats.
|
|
3902
|
+
_materialData = [
|
|
3903
|
+
0.1,
|
|
3904
|
+
0.1,
|
|
3905
|
+
0.1,
|
|
3906
|
+
0,
|
|
3907
|
+
// Ambient
|
|
3908
|
+
0.8,
|
|
3909
|
+
0.8,
|
|
3910
|
+
0.8,
|
|
3911
|
+
0,
|
|
3912
|
+
// Diffuse
|
|
3913
|
+
0.5,
|
|
3914
|
+
0.5,
|
|
3915
|
+
0.5,
|
|
3916
|
+
32
|
|
3917
|
+
// Specular, shininess (the float fills the vec3's padding)
|
|
3918
|
+
];
|
|
3919
|
+
/** Created when the model buffer is assigned (the pipeline requires one). */
|
|
3920
|
+
_pipeline;
|
|
3921
|
+
_primitiveState;
|
|
3922
|
+
/** Texture and sampler set before the pipeline exists; applied when it is created. */
|
|
3923
|
+
_pendingTexture;
|
|
3924
|
+
_pendingSampler;
|
|
3925
|
+
_samplerSet = false;
|
|
3926
|
+
_projectionViewBuffer;
|
|
3927
|
+
_camera;
|
|
3928
|
+
_cameraPositionBuffer;
|
|
3929
|
+
_materialBuffer;
|
|
3930
|
+
_modelBuffer;
|
|
3931
|
+
/**
|
|
3932
|
+
* The constructor.
|
|
3933
|
+
* @param framework The framework.
|
|
3934
|
+
* @param descriptor The material descriptor. Its projection view and camera position buffers are shared and are not disposed by this material.
|
|
3935
|
+
* @throws Error when `descriptor`, `descriptor.projectionViewBuffer` or `descriptor.cameraPositionBuffer` is missing.
|
|
3936
|
+
*/
|
|
3937
|
+
constructor(framework, descriptor) {
|
|
3938
|
+
const missing = [
|
|
3939
|
+
descriptor?.projectionViewBuffer ? void 0 : "descriptor.projectionViewBuffer",
|
|
3940
|
+
descriptor?.cameraPositionBuffer ? void 0 : "descriptor.cameraPositionBuffer"
|
|
3941
|
+
].filter((x2) => x2 !== void 0);
|
|
3942
|
+
if (missing.length > 0) {
|
|
3943
|
+
throw new Error(`ForwardBlinnPhongMaterial: ${missing.join(" and ")} ${missing.length > 1 ? "are" : "is"} required (e.g. camera.projectionViewBuffer and camera.positionBuffer).`);
|
|
3944
|
+
}
|
|
3945
|
+
this._framework = framework;
|
|
3946
|
+
const usage = BufferUsage.UNIFORM | BufferUsage.COPY_DST;
|
|
3947
|
+
this._diffuseColorCache = this.diffuseColor.copy();
|
|
3948
|
+
this._ambientColorCache = this.ambientColor.copy();
|
|
3949
|
+
this._specularColorCache = this.specularColor.copy();
|
|
3950
|
+
this._shininessCache = this.shininess;
|
|
3951
|
+
this._projectionViewBuffer = descriptor.projectionViewBuffer;
|
|
3952
|
+
this._cameraPositionBuffer = descriptor.cameraPositionBuffer;
|
|
3953
|
+
this._materialBuffer = framework.bufferFactory.createUniformBuffer(this._materialData, usage);
|
|
3954
|
+
this._primitiveState = descriptor.primitiveState;
|
|
3955
|
+
this._lightsBuffer = descriptor.lightsBuffer;
|
|
3956
|
+
}
|
|
3957
|
+
/** Creates the pipeline with the current bindings. */
|
|
3958
|
+
_createPipeline(modelBuffer) {
|
|
3959
|
+
const pipeline = this._framework.renderPipelineFactory.createForwardBlinnPhongRenderPipeline({
|
|
3960
|
+
projectionViewBuffer: this._projectionViewBuffer,
|
|
3961
|
+
modelBuffer,
|
|
3962
|
+
materialBuffer: this._materialBuffer,
|
|
3963
|
+
cameraPositionBuffer: this._cameraPositionBuffer,
|
|
3964
|
+
lightsBuffer: this._lightsBuffer?.buffer,
|
|
3965
|
+
maxDirectionalLights: this._lightsBuffer?.maxDirectionalLights,
|
|
3966
|
+
maxPointLights: this._lightsBuffer?.maxPointLights,
|
|
3967
|
+
primitiveState: this._primitiveState
|
|
3968
|
+
});
|
|
3969
|
+
if (this._pendingTexture) {
|
|
3970
|
+
pipeline.diffuseTexture = this._pendingTexture;
|
|
3971
|
+
}
|
|
3972
|
+
if (this._samplerSet) {
|
|
3973
|
+
pipeline.diffuseTextureSampler = this._pendingSampler;
|
|
3974
|
+
}
|
|
3975
|
+
this._pipeline = pipeline;
|
|
3976
|
+
return pipeline;
|
|
3977
|
+
}
|
|
3978
|
+
/**
|
|
3979
|
+
* The ambient color of this material.
|
|
3980
|
+
* Alpha is ignored. By default, it is (0.1,0.1,0.1).
|
|
3981
|
+
*/
|
|
3982
|
+
ambientColor = new Color(0.1, 0.1, 0.1);
|
|
3983
|
+
/**
|
|
3984
|
+
* The diffuse color of this material.
|
|
3985
|
+
* Alpha is ignored. By default, it is (0.8,0.8,0.8).
|
|
3986
|
+
*/
|
|
3987
|
+
diffuseColor = new Color(0.8, 0.8, 0.8);
|
|
3988
|
+
/**
|
|
3989
|
+
* The specular color of this material (MTL `Ks`).
|
|
3990
|
+
* Alpha is ignored. By default, it is (0.5,0.5,0.5).
|
|
3991
|
+
*/
|
|
3992
|
+
specularColor = new Color(0.5, 0.5, 0.5);
|
|
3993
|
+
/**
|
|
3994
|
+
* The specular exponent (MTL `Ns`). Higher values give smaller, sharper highlights. By default, it is 32.
|
|
3995
|
+
*/
|
|
3996
|
+
shininess = 32;
|
|
3997
|
+
/**
|
|
3998
|
+
* The camera. Setting it binds the camera's projection view and position buffers to the pipeline
|
|
3999
|
+
* (used by the scene when its camera changes). Setting undefined keeps the last bound buffers.
|
|
4000
|
+
* Undefined until set: the constructor only takes the buffers.
|
|
4001
|
+
*/
|
|
4002
|
+
get camera() {
|
|
4003
|
+
return this._camera;
|
|
4004
|
+
}
|
|
4005
|
+
/** @inheritDoc */
|
|
4006
|
+
set camera(camera) {
|
|
4007
|
+
if (camera === this._camera) {
|
|
4008
|
+
return;
|
|
4009
|
+
}
|
|
4010
|
+
this._camera = camera;
|
|
4011
|
+
if (!camera) {
|
|
4012
|
+
return;
|
|
4013
|
+
}
|
|
4014
|
+
if (this._projectionViewBuffer !== camera.projectionViewBuffer) {
|
|
4015
|
+
this._projectionViewBuffer = camera.projectionViewBuffer;
|
|
4016
|
+
if (this._pipeline) {
|
|
4017
|
+
this._pipeline.projectionViewBuffer = this._projectionViewBuffer;
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
if (this._cameraPositionBuffer !== camera.positionBuffer) {
|
|
4021
|
+
this._cameraPositionBuffer = camera.positionBuffer;
|
|
4022
|
+
if (this._pipeline) {
|
|
4023
|
+
this._pipeline.cameraPositionBuffer = this._cameraPositionBuffer;
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
/** The lights buffer with the scene's lights. Shared, not disposed by this material. */
|
|
4028
|
+
get lightsBuffer() {
|
|
4029
|
+
return this._lightsBuffer;
|
|
4030
|
+
}
|
|
4031
|
+
/** The lights buffer with the scene's lights. Shared, not disposed by this material. */
|
|
4032
|
+
set lightsBuffer(lightsBuffer) {
|
|
4033
|
+
this._lightsBuffer = lightsBuffer;
|
|
4034
|
+
if (lightsBuffer && this._pipeline) {
|
|
4035
|
+
this._pipeline.setLightCounts(lightsBuffer.maxDirectionalLights, lightsBuffer.maxPointLights);
|
|
4036
|
+
this._pipeline.lightsBuffer = lightsBuffer.buffer;
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
/**
|
|
4040
|
+
* The texture to inspect.
|
|
4041
|
+
*/
|
|
4042
|
+
get diffuseTexture() {
|
|
4043
|
+
return this._pipeline?.diffuseTexture ?? this._pendingTexture;
|
|
4044
|
+
}
|
|
4045
|
+
/**
|
|
4046
|
+
* The texture to inspect.
|
|
4047
|
+
*/
|
|
4048
|
+
set diffuseTexture(value) {
|
|
4049
|
+
this._pendingTexture = value;
|
|
4050
|
+
if (this._pipeline) {
|
|
4051
|
+
this._pipeline.diffuseTexture = value;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
/**
|
|
4055
|
+
* The texture sampler.
|
|
4056
|
+
*/
|
|
4057
|
+
get diffuseTextureSampler() {
|
|
4058
|
+
return this._pipeline ? this._pipeline.diffuseTextureSampler : this._pendingSampler;
|
|
4059
|
+
}
|
|
4060
|
+
/**
|
|
4061
|
+
* The texture sampler.
|
|
4062
|
+
*/
|
|
4063
|
+
set diffuseTextureSampler(value) {
|
|
4064
|
+
this._pendingSampler = value;
|
|
4065
|
+
this._samplerSet = true;
|
|
4066
|
+
if (this._pipeline) {
|
|
4067
|
+
this._pipeline.diffuseTextureSampler = value;
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
/**
|
|
4071
|
+
* The model buffer (std140 `mat4`). Owned by the mesh, which assigns it before drawing; never disposed by this material.
|
|
4072
|
+
* Undefined until assigned: rendering without one throws.
|
|
4073
|
+
*/
|
|
4074
|
+
get modelBuffer() {
|
|
4075
|
+
return this._modelBuffer;
|
|
4076
|
+
}
|
|
4077
|
+
/** Binds the model buffer to the pipeline. */
|
|
4078
|
+
set modelBuffer(value) {
|
|
4079
|
+
if (value === this._modelBuffer) {
|
|
4080
|
+
return;
|
|
4081
|
+
}
|
|
4082
|
+
this._modelBuffer = value;
|
|
4083
|
+
if (!value) {
|
|
4084
|
+
return;
|
|
4085
|
+
}
|
|
4086
|
+
if (this._pipeline) {
|
|
4087
|
+
this._pipeline.modelBuffer = value;
|
|
4088
|
+
} else {
|
|
4089
|
+
this._createPipeline(value);
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
/**
|
|
4093
|
+
* The geometry format.
|
|
4094
|
+
*/
|
|
4095
|
+
geometryFormat = GeometryFormat.POS3_COLOR4_TEXTURECOORDS2_NORMAL3;
|
|
4096
|
+
/** @inheritDoc */
|
|
4097
|
+
beforeRender() {
|
|
4098
|
+
if (!this._diffuseColorCache.equals(this.diffuseColor) || !this._ambientColorCache.equals(this.ambientColor) || !this._specularColorCache.equals(this.specularColor) || this._shininessCache !== this.shininess) {
|
|
4099
|
+
this._ambientColorCache.set(this.ambientColor);
|
|
4100
|
+
this._diffuseColorCache.set(this.diffuseColor);
|
|
4101
|
+
this._specularColorCache.set(this.specularColor);
|
|
4102
|
+
this._shininessCache = this.shininess;
|
|
4103
|
+
this._materialData[0] = this._ambientColorCache.r;
|
|
4104
|
+
this._materialData[1] = this._ambientColorCache.g;
|
|
4105
|
+
this._materialData[2] = this._ambientColorCache.b;
|
|
4106
|
+
this._materialData[4] = this._diffuseColorCache.r;
|
|
4107
|
+
this._materialData[5] = this._diffuseColorCache.g;
|
|
4108
|
+
this._materialData[6] = this._diffuseColorCache.b;
|
|
4109
|
+
this._materialData[8] = this._specularColorCache.r;
|
|
4110
|
+
this._materialData[9] = this._specularColorCache.g;
|
|
4111
|
+
this._materialData[10] = this._specularColorCache.b;
|
|
4112
|
+
this._materialData[11] = this._shininessCache;
|
|
4113
|
+
this._materialBuffer.update(this._materialData);
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
/** @inheritDoc */
|
|
4117
|
+
renderMesh(mesh) {
|
|
4118
|
+
this.render(mesh.vertexBuffer, mesh.indexBuffer, mesh.indexCount ?? -1, mesh.indexStart ?? 0);
|
|
4119
|
+
}
|
|
4120
|
+
/** @inheritDoc */
|
|
4121
|
+
render(vertexBuffer, indexBuffer, indicesCount, indicesOffset) {
|
|
4122
|
+
if (!this._modelBuffer) {
|
|
4123
|
+
throw new Error("ForwardBlinnPhongMaterial: no modelBuffer. Render it through a mesh (the mesh assigns its model buffer) or set material.modelBuffer.");
|
|
4124
|
+
}
|
|
4125
|
+
(this._pipeline ?? this._createPipeline(this._modelBuffer)).render(vertexBuffer, indexBuffer, indicesCount, indicesOffset);
|
|
4126
|
+
}
|
|
4127
|
+
/** @inheritDoc */
|
|
4128
|
+
dispose() {
|
|
4129
|
+
this._pipeline?.dispose();
|
|
4130
|
+
this._pipeline = void 0;
|
|
3089
4131
|
this._materialBuffer.dispose();
|
|
3090
4132
|
}
|
|
3091
4133
|
}
|
|
@@ -3106,6 +4148,318 @@ class MaterialFactory {
|
|
|
3106
4148
|
createUnlitMaterial(descriptor) {
|
|
3107
4149
|
return new UnlitMaterial(this._framework, descriptor);
|
|
3108
4150
|
}
|
|
4151
|
+
/** @inheritDoc */
|
|
4152
|
+
createSpriteMaterial(descriptor) {
|
|
4153
|
+
return new SpriteMaterial(this._framework, descriptor);
|
|
4154
|
+
}
|
|
4155
|
+
/** @inheritDoc */
|
|
4156
|
+
createForwardBlinnPhongMaterial(descriptor) {
|
|
4157
|
+
return new ForwardBlinnPhongMaterial(this._framework, descriptor);
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
let nextLightId = 1;
|
|
4161
|
+
function createLightId() {
|
|
4162
|
+
return nextLightId++;
|
|
4163
|
+
}
|
|
4164
|
+
class AmbientLight {
|
|
4165
|
+
/**
|
|
4166
|
+
* The constructor.
|
|
4167
|
+
* @param descriptor The optional descriptor. The colour is copied.
|
|
4168
|
+
* @param scene The optional scene to register with.
|
|
4169
|
+
*/
|
|
4170
|
+
constructor(descriptor, scene) {
|
|
4171
|
+
this.id = descriptor?.id ?? createLightId();
|
|
4172
|
+
this.name = descriptor?.name;
|
|
4173
|
+
this.color = descriptor?.color?.copy() ?? Color.black();
|
|
4174
|
+
this.intensity = descriptor?.intensity ?? 1;
|
|
4175
|
+
scene?.add(this);
|
|
4176
|
+
}
|
|
4177
|
+
/** The unique id of this light. */
|
|
4178
|
+
id;
|
|
4179
|
+
/** The optional name for a light. */
|
|
4180
|
+
name;
|
|
4181
|
+
/** The light color. Alpha is ignored. */
|
|
4182
|
+
color;
|
|
4183
|
+
/** Multiplies {@link color} when written to the lights buffer. */
|
|
4184
|
+
intensity;
|
|
4185
|
+
}
|
|
4186
|
+
class DirectionalLight {
|
|
4187
|
+
/**
|
|
4188
|
+
* The constructor.
|
|
4189
|
+
* @param descriptor The optional descriptor. Colour and direction are copied.
|
|
4190
|
+
* @param scene The optional scene; the light adds itself via {@link Scene.add}.
|
|
4191
|
+
* @throws Error when the scene has no free directional light slot left (the light is then not in the scene).
|
|
4192
|
+
*/
|
|
4193
|
+
constructor(descriptor, scene) {
|
|
4194
|
+
this.id = descriptor?.id ?? createLightId();
|
|
4195
|
+
this.name = descriptor?.name;
|
|
4196
|
+
this.color = descriptor?.color?.copy() ?? Color.white();
|
|
4197
|
+
this.intensity = descriptor?.intensity ?? 1;
|
|
4198
|
+
this.direction = descriptor?.direction ? vec3.clone(descriptor.direction) : vec3.fromValues(0, -1, 0);
|
|
4199
|
+
scene?.add(this);
|
|
4200
|
+
}
|
|
4201
|
+
/** The unique id of this light. */
|
|
4202
|
+
id;
|
|
4203
|
+
/** The optional name for a light. */
|
|
4204
|
+
name;
|
|
4205
|
+
/** The light colour. Alpha is ignored. */
|
|
4206
|
+
color;
|
|
4207
|
+
/** Multiplies {@link color} when written to the lights buffer. */
|
|
4208
|
+
intensity;
|
|
4209
|
+
/** The direction in which the light travels. */
|
|
4210
|
+
direction;
|
|
4211
|
+
}
|
|
4212
|
+
class PointLight {
|
|
4213
|
+
/** The default constant attenuation. */
|
|
4214
|
+
static DEFAULT_CONSTANT = 1;
|
|
4215
|
+
/** The default linear attenuation. */
|
|
4216
|
+
static DEFAULT_LINEAR = 0.09;
|
|
4217
|
+
/** The default quadratic attenuation. */
|
|
4218
|
+
static DEFAULT_QUADRATIC = 0.032;
|
|
4219
|
+
/**
|
|
4220
|
+
* The constructor.
|
|
4221
|
+
* @param descriptor The optional descriptor. Colour and position are copied.
|
|
4222
|
+
* @param scene The optional scene; the light adds itself via {@link Scene.add}.
|
|
4223
|
+
* @throws Error when the scene has no free point light slot left (the light is then not in the scene).
|
|
4224
|
+
*/
|
|
4225
|
+
constructor(descriptor, scene) {
|
|
4226
|
+
this.id = descriptor?.id ?? createLightId();
|
|
4227
|
+
this.name = descriptor?.name;
|
|
4228
|
+
this.color = descriptor?.color?.copy() ?? Color.white();
|
|
4229
|
+
this.intensity = descriptor?.intensity ?? 1;
|
|
4230
|
+
this.position = descriptor?.position ? vec3.clone(descriptor.position) : vec3.create();
|
|
4231
|
+
this.constant = descriptor?.constant ?? PointLight.DEFAULT_CONSTANT;
|
|
4232
|
+
this.linear = descriptor?.linear ?? PointLight.DEFAULT_LINEAR;
|
|
4233
|
+
this.quadratic = descriptor?.quadratic ?? PointLight.DEFAULT_QUADRATIC;
|
|
4234
|
+
scene?.add(this);
|
|
4235
|
+
}
|
|
4236
|
+
/** The unique id of this light. */
|
|
4237
|
+
id;
|
|
4238
|
+
/** The optional name for a light. */
|
|
4239
|
+
name;
|
|
4240
|
+
/** The light colour. Alpha is ignored. */
|
|
4241
|
+
color;
|
|
4242
|
+
/** Multiplies {@link color} when written to the lights buffer. */
|
|
4243
|
+
intensity;
|
|
4244
|
+
/** World-space position of the light. */
|
|
4245
|
+
position;
|
|
4246
|
+
/** Constant attenuation term. */
|
|
4247
|
+
constant;
|
|
4248
|
+
/** Linear attenuation term. */
|
|
4249
|
+
linear;
|
|
4250
|
+
/** Quadratic attenuation term. */
|
|
4251
|
+
quadratic;
|
|
4252
|
+
}
|
|
4253
|
+
const DEFAULT_DIRECTIONAL_LIGHTS_COUNT = 3;
|
|
4254
|
+
const DEFAULT_POINT_LIGHTS_COUNT = 3;
|
|
4255
|
+
class Scene {
|
|
4256
|
+
_meshes = [];
|
|
4257
|
+
_activeAmbientLight;
|
|
4258
|
+
_disposed = false;
|
|
4259
|
+
_activeCamera;
|
|
4260
|
+
/**
|
|
4261
|
+
* The camera the scene renders with; undefined until one is set or added.
|
|
4262
|
+
* Setting it assigns it to the materials of every mesh (and submesh) whose camera differs;
|
|
4263
|
+
* {@link update} also assigns it to meshes and submeshes added later.
|
|
4264
|
+
*/
|
|
4265
|
+
get camera() {
|
|
4266
|
+
return this._activeCamera;
|
|
4267
|
+
}
|
|
4268
|
+
set camera(value) {
|
|
4269
|
+
this._activeCamera = value;
|
|
4270
|
+
if (!value) {
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
4273
|
+
for (const mesh of this._meshes) {
|
|
4274
|
+
Scene._assignCameraToMesh(mesh, value);
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
/** The ambient lights registered with this scene. */
|
|
4278
|
+
ambientLights = [];
|
|
4279
|
+
/**
|
|
4280
|
+
* The constructor.
|
|
4281
|
+
* @param framework The framework.
|
|
4282
|
+
*/
|
|
4283
|
+
constructor(framework) {
|
|
4284
|
+
this._activeAmbientLight = framework.lightsFactory.createAmbientLight(void 0, this);
|
|
4285
|
+
this.__forwardLightsBuffer = framework.lightsFactory.__createLightsBuffer({
|
|
4286
|
+
ambientLight: this._activeAmbientLight,
|
|
4287
|
+
maxDirectionalLights: 3,
|
|
4288
|
+
maxPointLights: 5
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
/**
|
|
4292
|
+
* The lights buffer used for forward shading.
|
|
4293
|
+
* @internal
|
|
4294
|
+
*/
|
|
4295
|
+
__forwardLightsBuffer;
|
|
4296
|
+
/** The meshes in this scene, in the order they were added. */
|
|
4297
|
+
get meshes() {
|
|
4298
|
+
return this._meshes;
|
|
4299
|
+
}
|
|
4300
|
+
/** The directional lights in this scene. */
|
|
4301
|
+
get directionalLights() {
|
|
4302
|
+
return this.__forwardLightsBuffer.directionalLights;
|
|
4303
|
+
}
|
|
4304
|
+
/** The point lights in this scene. */
|
|
4305
|
+
get pointLights() {
|
|
4306
|
+
return this.__forwardLightsBuffer.pointLights;
|
|
4307
|
+
}
|
|
4308
|
+
/**
|
|
4309
|
+
* Gets the active ambient light.
|
|
4310
|
+
*/
|
|
4311
|
+
get activeAmbientLight() {
|
|
4312
|
+
return this._activeAmbientLight;
|
|
4313
|
+
}
|
|
4314
|
+
/** Sets the active ambient light; it is added to {@link ambientLights} if needed. */
|
|
4315
|
+
set activeAmbientLight(value) {
|
|
4316
|
+
this._addAmbientLight(value);
|
|
4317
|
+
this._activeAmbientLight = value;
|
|
4318
|
+
this.__forwardLightsBuffer.ambientLight = value;
|
|
4319
|
+
}
|
|
4320
|
+
/**
|
|
4321
|
+
* Adds a mesh, a light or a camera. Adding an item that is already in the scene does nothing.
|
|
4322
|
+
* - Mesh: drawn by {@link render}; gets the scene camera.
|
|
4323
|
+
* - Ambient light: added to {@link ambientLights} (use {@link activeAmbientLight} to make it the lit one).
|
|
4324
|
+
* - Directional / point light: added to the lights buffer.
|
|
4325
|
+
* - Camera: becomes the scene {@link camera}.
|
|
4326
|
+
* Constructors given a scene (meshes, lights, the perspective camera) call this themselves.
|
|
4327
|
+
* @param item The item to add.
|
|
4328
|
+
* @throws Error when all slots for a light kind are in use (3 directional, 5 point), or for an unsupported item.
|
|
4329
|
+
*/
|
|
4330
|
+
add(item) {
|
|
4331
|
+
if (item instanceof AmbientLight) {
|
|
4332
|
+
this._addAmbientLight(item);
|
|
4333
|
+
} else if (item instanceof DirectionalLight || item instanceof PointLight) {
|
|
4334
|
+
this.__forwardLightsBuffer.add(item);
|
|
4335
|
+
} else if (Scene._isCamera(item)) {
|
|
4336
|
+
this.camera = item;
|
|
4337
|
+
} else if (Scene._isMesh(item)) {
|
|
4338
|
+
if (!this._meshes.includes(item)) {
|
|
4339
|
+
this._meshes.push(item);
|
|
4340
|
+
if (this._activeCamera) {
|
|
4341
|
+
Scene._assignCameraToMesh(item, this._activeCamera);
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
} else {
|
|
4345
|
+
throw new Error("Scene.add: expected a mesh, an AmbientLight, DirectionalLight, PointLight or a camera.");
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
/**
|
|
4349
|
+
* Removes a mesh, a light or a camera. The mesh is not disposed.
|
|
4350
|
+
* Removing the active camera clears {@link camera}.
|
|
4351
|
+
* @param item The item to remove.
|
|
4352
|
+
* @returns True when the item was in the scene (for a camera: was the active camera).
|
|
4353
|
+
* @throws Error when removing the active ambient light (set another {@link activeAmbientLight} first), or for an unsupported item.
|
|
4354
|
+
*/
|
|
4355
|
+
remove(item) {
|
|
4356
|
+
if (item instanceof AmbientLight) {
|
|
4357
|
+
if (item === this._activeAmbientLight) {
|
|
4358
|
+
throw new Error("Scene.remove: cannot remove the active ambient light; set scene.activeAmbientLight to another light first.");
|
|
4359
|
+
}
|
|
4360
|
+
return Scene._removeFrom(this.ambientLights, item);
|
|
4361
|
+
}
|
|
4362
|
+
if (item instanceof DirectionalLight || item instanceof PointLight) {
|
|
4363
|
+
return this.__forwardLightsBuffer.remove(item);
|
|
4364
|
+
}
|
|
4365
|
+
if (Scene._isCamera(item)) {
|
|
4366
|
+
if (item !== this._activeCamera) {
|
|
4367
|
+
return false;
|
|
4368
|
+
}
|
|
4369
|
+
this._activeCamera = void 0;
|
|
4370
|
+
return true;
|
|
4371
|
+
}
|
|
4372
|
+
if (Scene._isMesh(item)) {
|
|
4373
|
+
return Scene._removeFrom(this._meshes, item);
|
|
4374
|
+
}
|
|
4375
|
+
throw new Error("Scene.remove: expected a mesh, an AmbientLight, DirectionalLight, PointLight or a camera.");
|
|
4376
|
+
}
|
|
4377
|
+
/**
|
|
4378
|
+
* Adds a directional or point light.
|
|
4379
|
+
* @deprecated Use {@link add}.
|
|
4380
|
+
* @throws Error when all slots for that kind are in use (3 directional, 5 point).
|
|
4381
|
+
*/
|
|
4382
|
+
addLight(light) {
|
|
4383
|
+
this.add(light);
|
|
4384
|
+
}
|
|
4385
|
+
/**
|
|
4386
|
+
* Removes a directional or point light.
|
|
4387
|
+
* @deprecated Use {@link remove}.
|
|
4388
|
+
* @returns True when the light was in the scene.
|
|
4389
|
+
*/
|
|
4390
|
+
removeLight(light) {
|
|
4391
|
+
return this.remove(light);
|
|
4392
|
+
}
|
|
4393
|
+
_addAmbientLight(ambientLight) {
|
|
4394
|
+
if (!this.ambientLights.includes(ambientLight)) {
|
|
4395
|
+
this.ambientLights.push(ambientLight);
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
static _removeFrom(list, item) {
|
|
4399
|
+
const index = list.indexOf(item);
|
|
4400
|
+
if (index < 0) {
|
|
4401
|
+
return false;
|
|
4402
|
+
}
|
|
4403
|
+
list.splice(index, 1);
|
|
4404
|
+
return true;
|
|
4405
|
+
}
|
|
4406
|
+
/** Cameras have the camera buffers and updateBuffers(); meshes and lights do not. */
|
|
4407
|
+
static _isCamera(item) {
|
|
4408
|
+
const camera = item;
|
|
4409
|
+
return typeof camera === "object" && camera !== null && "projectionViewBuffer" in camera && "viewMatrix" in camera && typeof camera.updateBuffers === "function";
|
|
4410
|
+
}
|
|
4411
|
+
/** Meshes have render() and a material slot or submeshes. */
|
|
4412
|
+
static _isMesh(item) {
|
|
4413
|
+
const mesh = item;
|
|
4414
|
+
return typeof mesh === "object" && mesh !== null && typeof mesh.render === "function" && ("material" in mesh || "subMeshes" in mesh);
|
|
4415
|
+
}
|
|
4416
|
+
/**
|
|
4417
|
+
* Updates the scene: assigns {@link camera} to the mesh materials that don't have it yet, and uploads changed lights.
|
|
4418
|
+
* @param _time The game time.
|
|
4419
|
+
* @public
|
|
4420
|
+
*/
|
|
4421
|
+
update(_time) {
|
|
4422
|
+
if (this._activeCamera) {
|
|
4423
|
+
for (const mesh of this._meshes) {
|
|
4424
|
+
Scene._assignCameraToMesh(mesh, this._activeCamera);
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
this.__forwardLightsBuffer.update();
|
|
4428
|
+
}
|
|
4429
|
+
static _assignCameraToMesh(mesh, camera) {
|
|
4430
|
+
Scene._assignCamera(mesh.material, camera);
|
|
4431
|
+
for (const subMesh of mesh.subMeshes ?? []) {
|
|
4432
|
+
Scene._assignCamera(subMesh.material, camera);
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
static _assignCamera(material, camera) {
|
|
4436
|
+
if (!camera.projectionViewBuffer || !camera.positionBuffer) {
|
|
4437
|
+
return;
|
|
4438
|
+
}
|
|
4439
|
+
if (material && "camera" in material && material.camera !== camera) {
|
|
4440
|
+
material.camera = camera;
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
/**
|
|
4444
|
+
* Renders the meshes.
|
|
4445
|
+
*/
|
|
4446
|
+
render() {
|
|
4447
|
+
for (const mesh of this._meshes) {
|
|
4448
|
+
mesh.render();
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
/** Disposes the meshes still in the scene and the lights buffer. Calling it twice does nothing. */
|
|
4452
|
+
dispose() {
|
|
4453
|
+
if (this._disposed) {
|
|
4454
|
+
return;
|
|
4455
|
+
}
|
|
4456
|
+
this._disposed = true;
|
|
4457
|
+
for (const mesh of this._meshes.slice()) {
|
|
4458
|
+
mesh.dispose();
|
|
4459
|
+
}
|
|
4460
|
+
this._meshes.length = 0;
|
|
4461
|
+
this.__forwardLightsBuffer.dispose();
|
|
4462
|
+
}
|
|
3109
4463
|
}
|
|
3110
4464
|
var Key = /* @__PURE__ */ ((Key2) => {
|
|
3111
4465
|
Key2[Key2["NONE"] = 0] = "NONE";
|
|
@@ -3380,16 +4734,195 @@ class CpuImageProcessor {
|
|
|
3380
4734
|
return promise;
|
|
3381
4735
|
}
|
|
3382
4736
|
}
|
|
4737
|
+
class WebGlLightsBuffer {
|
|
4738
|
+
/** Floats for the ambient light: vec3 padded to 16 bytes. */
|
|
4739
|
+
static AMBIENT_FLOAT_COUNT = 4;
|
|
4740
|
+
/** Floats per directional light: two vec3, each padded to 16 bytes. */
|
|
4741
|
+
static DIRECTIONAL_LIGHT_FLOAT_COUNT = 8;
|
|
4742
|
+
/** Floats per point light: vec3 light + float constant, vec3 position + float linear, float quadratic, padded to 48 bytes. */
|
|
4743
|
+
static POINT_LIGHT_FLOAT_COUNT = 12;
|
|
4744
|
+
_ambientLight;
|
|
4745
|
+
_buffer;
|
|
4746
|
+
_data;
|
|
4747
|
+
_uploaded;
|
|
4748
|
+
_directionalLights = [];
|
|
4749
|
+
_pointLights = [];
|
|
4750
|
+
_disposed = false;
|
|
4751
|
+
/**
|
|
4752
|
+
* The constructor.
|
|
4753
|
+
* @param framework The framework, used to create the uniform buffer.
|
|
4754
|
+
* @param descriptor The optional initial lights and directional light count.
|
|
4755
|
+
*/
|
|
4756
|
+
constructor(framework, descriptor) {
|
|
4757
|
+
this.ambientLight = descriptor?.ambientLight ?? new AmbientLight();
|
|
4758
|
+
this.maxDirectionalLights = descriptor?.maxDirectionalLights ?? DEFAULT_DIRECTIONAL_LIGHTS_COUNT;
|
|
4759
|
+
this.maxPointLights = descriptor?.maxPointLights ?? DEFAULT_POINT_LIGHTS_COUNT;
|
|
4760
|
+
for (const light of descriptor?.directionalLights ?? []) {
|
|
4761
|
+
this.add(light);
|
|
4762
|
+
}
|
|
4763
|
+
for (const light of descriptor?.pointLights ?? []) {
|
|
4764
|
+
this.add(light);
|
|
4765
|
+
}
|
|
4766
|
+
const floatCount = WebGlLightsBuffer.AMBIENT_FLOAT_COUNT + this.maxDirectionalLights * WebGlLightsBuffer.DIRECTIONAL_LIGHT_FLOAT_COUNT + this.maxPointLights * WebGlLightsBuffer.POINT_LIGHT_FLOAT_COUNT;
|
|
4767
|
+
this._data = new Array(floatCount).fill(0);
|
|
4768
|
+
this._writeData();
|
|
4769
|
+
this._uploaded = [...this._data];
|
|
4770
|
+
this._buffer = framework.bufferFactory.createUniformBuffer(this._data, BufferUsage.UNIFORM | BufferUsage.COPY_DST);
|
|
4771
|
+
}
|
|
4772
|
+
/** @inheritDoc */
|
|
4773
|
+
get ambientLight() {
|
|
4774
|
+
return this._ambientLight;
|
|
4775
|
+
}
|
|
4776
|
+
/** @inheritDoc */
|
|
4777
|
+
set ambientLight(ambientLight) {
|
|
4778
|
+
this._ambientLight = ambientLight;
|
|
4779
|
+
}
|
|
4780
|
+
/** @inheritDoc */
|
|
4781
|
+
get directionalLights() {
|
|
4782
|
+
return this._directionalLights;
|
|
4783
|
+
}
|
|
4784
|
+
/** @inheritDoc */
|
|
4785
|
+
maxDirectionalLights;
|
|
4786
|
+
/** @inheritDoc */
|
|
4787
|
+
get pointLights() {
|
|
4788
|
+
return this._pointLights;
|
|
4789
|
+
}
|
|
4790
|
+
/** @inheritDoc */
|
|
4791
|
+
add(light) {
|
|
4792
|
+
const [list, max, kind] = this._listFor(light);
|
|
4793
|
+
if (list.includes(light)) {
|
|
4794
|
+
return;
|
|
4795
|
+
}
|
|
4796
|
+
if (list.length >= max) {
|
|
4797
|
+
throw new Error(`LightsBuffer: cannot add ${kind} light ${light.id}; all ${max} ${kind} light slots are in use (max${kind === "directional" ? "Directional" : "Point"}Lights = ${max}).`);
|
|
4798
|
+
}
|
|
4799
|
+
list.push(light);
|
|
4800
|
+
}
|
|
4801
|
+
/** @inheritDoc */
|
|
4802
|
+
remove(light) {
|
|
4803
|
+
const [list] = this._listFor(light);
|
|
4804
|
+
const index = list.indexOf(light);
|
|
4805
|
+
if (index < 0) {
|
|
4806
|
+
return false;
|
|
4807
|
+
}
|
|
4808
|
+
list.splice(index, 1);
|
|
4809
|
+
return true;
|
|
4810
|
+
}
|
|
4811
|
+
/** @inheritDoc */
|
|
4812
|
+
maxPointLights;
|
|
4813
|
+
/** @inheritDoc */
|
|
4814
|
+
get buffer() {
|
|
4815
|
+
return this._buffer;
|
|
4816
|
+
}
|
|
4817
|
+
/** @inheritDoc */
|
|
4818
|
+
update() {
|
|
4819
|
+
this._writeData();
|
|
4820
|
+
if (!this._hasChanged()) {
|
|
4821
|
+
return;
|
|
4822
|
+
}
|
|
4823
|
+
this._buffer.update(this._data);
|
|
4824
|
+
for (let i2 = 0; i2 < this._data.length; i2++) {
|
|
4825
|
+
this._uploaded[i2] = this._data[i2];
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
/** @inheritDoc */
|
|
4829
|
+
dispose() {
|
|
4830
|
+
if (this._disposed) {
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
this._disposed = true;
|
|
4834
|
+
this._buffer.dispose();
|
|
4835
|
+
}
|
|
4836
|
+
_writeAmbientLight() {
|
|
4837
|
+
this._writeColor(0, this.ambientLight.color, this.ambientLight.intensity);
|
|
4838
|
+
this._data[3] = 0;
|
|
4839
|
+
}
|
|
4840
|
+
_listFor(light) {
|
|
4841
|
+
if (light instanceof DirectionalLight) {
|
|
4842
|
+
return [this._directionalLights, this.maxDirectionalLights, "directional"];
|
|
4843
|
+
}
|
|
4844
|
+
if (light instanceof PointLight) {
|
|
4845
|
+
return [this._pointLights, this.maxPointLights, "point"];
|
|
4846
|
+
}
|
|
4847
|
+
throw new Error("LightsBuffer: expected a DirectionalLight or PointLight instance.");
|
|
4848
|
+
}
|
|
4849
|
+
/** Writes color * intensity as vec3 at offset (the fourth float is left to the caller). */
|
|
4850
|
+
_writeColor(offset, color, intensity) {
|
|
4851
|
+
this._data[offset] = color ? color.r * intensity : 0;
|
|
4852
|
+
this._data[offset + 1] = color ? color.g * intensity : 0;
|
|
4853
|
+
this._data[offset + 2] = color ? color.b * intensity : 0;
|
|
4854
|
+
}
|
|
4855
|
+
_hasChanged() {
|
|
4856
|
+
for (let i2 = 0; i2 < this._data.length; i2++) {
|
|
4857
|
+
if (this._data[i2] !== this._uploaded[i2]) {
|
|
4858
|
+
return true;
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
return false;
|
|
4862
|
+
}
|
|
4863
|
+
_writeData() {
|
|
4864
|
+
this._writeAmbientLight();
|
|
4865
|
+
for (let i2 = 0; i2 < this.maxDirectionalLights; i2++) {
|
|
4866
|
+
const offset = WebGlLightsBuffer.AMBIENT_FLOAT_COUNT + i2 * WebGlLightsBuffer.DIRECTIONAL_LIGHT_FLOAT_COUNT;
|
|
4867
|
+
const light = this._directionalLights[i2];
|
|
4868
|
+
this._writeColor(offset, light?.color, light?.intensity ?? 0);
|
|
4869
|
+
this._data[offset + 3] = 0;
|
|
4870
|
+
this._data[offset + 4] = light ? light.direction[0] : 0;
|
|
4871
|
+
this._data[offset + 5] = light ? light.direction[1] : 0;
|
|
4872
|
+
this._data[offset + 6] = light ? light.direction[2] : 0;
|
|
4873
|
+
this._data[offset + 7] = 0;
|
|
4874
|
+
}
|
|
4875
|
+
const pointLightsStart = WebGlLightsBuffer.AMBIENT_FLOAT_COUNT + this.maxDirectionalLights * WebGlLightsBuffer.DIRECTIONAL_LIGHT_FLOAT_COUNT;
|
|
4876
|
+
for (let i2 = 0; i2 < this.maxPointLights; i2++) {
|
|
4877
|
+
const offset = pointLightsStart + i2 * WebGlLightsBuffer.POINT_LIGHT_FLOAT_COUNT;
|
|
4878
|
+
const light = this._pointLights[i2];
|
|
4879
|
+
this._writeColor(offset, light?.color, light?.intensity ?? 0);
|
|
4880
|
+
this._data[offset + 3] = light ? light.constant : 1;
|
|
4881
|
+
this._data[offset + 4] = light ? light.position[0] : 0;
|
|
4882
|
+
this._data[offset + 5] = light ? light.position[1] : 0;
|
|
4883
|
+
this._data[offset + 6] = light ? light.position[2] : 0;
|
|
4884
|
+
this._data[offset + 7] = light ? light.linear : 0;
|
|
4885
|
+
this._data[offset + 8] = light ? light.quadratic : 0;
|
|
4886
|
+
this._data[offset + 9] = 0;
|
|
4887
|
+
this._data[offset + 10] = 0;
|
|
4888
|
+
this._data[offset + 11] = 0;
|
|
4889
|
+
}
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
class WebGlAmbientLightFactory {
|
|
4893
|
+
/**
|
|
4894
|
+
* The constructor.
|
|
4895
|
+
* @param _framework The framework.
|
|
4896
|
+
*/
|
|
4897
|
+
constructor(_framework) {
|
|
4898
|
+
this._framework = _framework;
|
|
4899
|
+
}
|
|
4900
|
+
_framework;
|
|
4901
|
+
/** The colour used when no descriptor is given. */
|
|
4902
|
+
static DEFAULT_COLOR = new Color(0.1, 0.1, 0.1);
|
|
4903
|
+
/** @inheritDoc */
|
|
4904
|
+
createAmbientLight(desc, scene) {
|
|
4905
|
+
return new AmbientLight({ ...desc, color: desc?.color ?? WebGlAmbientLightFactory.DEFAULT_COLOR }, scene);
|
|
4906
|
+
}
|
|
4907
|
+
/** @inheritDoc */
|
|
4908
|
+
__createLightsBuffer(desc) {
|
|
4909
|
+
return new WebGlLightsBuffer(this._framework, desc);
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
3383
4912
|
class WindowManager {
|
|
3384
4913
|
_canvas;
|
|
3385
4914
|
_updateCallbacks = [];
|
|
3386
4915
|
_renderCallbacks = [];
|
|
4916
|
+
_ownsCanvas;
|
|
4917
|
+
_animationFrame = null;
|
|
4918
|
+
_disposed = false;
|
|
3387
4919
|
/**
|
|
3388
4920
|
* The constructor for the WindowManager class.
|
|
3389
4921
|
* @param canvas An optional HTMLCanvasElement to associate with the window manager. If `null`, the window manager creates its own canvas and appends it to `document.body`.
|
|
3390
4922
|
*/
|
|
3391
4923
|
constructor(canvas = null) {
|
|
3392
4924
|
this._canvas = canvas ?? document.createElement("canvas");
|
|
4925
|
+
this._ownsCanvas = !canvas;
|
|
3393
4926
|
if (!canvas) {
|
|
3394
4927
|
document.body.appendChild(this._canvas);
|
|
3395
4928
|
}
|
|
@@ -3427,18 +4960,34 @@ class WindowManager {
|
|
|
3427
4960
|
initializeForWebGl() {
|
|
3428
4961
|
throw new Error("Method not implemented.");
|
|
3429
4962
|
}
|
|
4963
|
+
/** Stops the event loop, drops all callbacks and removes the canvas when this manager created it. */
|
|
3430
4964
|
dispose() {
|
|
3431
|
-
|
|
4965
|
+
if (this._disposed) {
|
|
4966
|
+
return;
|
|
4967
|
+
}
|
|
4968
|
+
this._disposed = true;
|
|
4969
|
+
if (this._animationFrame !== null) {
|
|
4970
|
+
cancelAnimationFrame(this._animationFrame);
|
|
4971
|
+
this._animationFrame = null;
|
|
4972
|
+
}
|
|
4973
|
+
this._updateCallbacks.length = 0;
|
|
4974
|
+
this._renderCallbacks.length = 0;
|
|
4975
|
+
if (this._ownsCanvas) {
|
|
4976
|
+
this._canvas.remove();
|
|
4977
|
+
}
|
|
3432
4978
|
}
|
|
3433
4979
|
/** @inheritdoc */
|
|
3434
4980
|
runEventLoop() {
|
|
4981
|
+
if (this._disposed) {
|
|
4982
|
+
return;
|
|
4983
|
+
}
|
|
3435
4984
|
for (const callback of this._updateCallbacks) {
|
|
3436
4985
|
callback();
|
|
3437
4986
|
}
|
|
3438
4987
|
for (const callback of this._renderCallbacks) {
|
|
3439
4988
|
callback();
|
|
3440
4989
|
}
|
|
3441
|
-
requestAnimationFrame(() => this.runEventLoop());
|
|
4990
|
+
this._animationFrame = requestAnimationFrame(() => this.runEventLoop());
|
|
3442
4991
|
}
|
|
3443
4992
|
/** @inheritdoc */
|
|
3444
4993
|
get canvas() {
|
|
@@ -3465,123 +5014,6 @@ class FrameworkConfiguration {
|
|
|
3465
5014
|
/** @inheritDoc */
|
|
3466
5015
|
powerPreference = PowerPreferenceType.DEFAULT;
|
|
3467
5016
|
}
|
|
3468
|
-
const RenderConfigurationSymbol = /* @__PURE__ */ Symbol("RenderConfiguration");
|
|
3469
|
-
class BaseGeometry {
|
|
3470
|
-
/** @inheritdoc */
|
|
3471
|
-
vertexCount = 0;
|
|
3472
|
-
/** @inheritdoc */
|
|
3473
|
-
positions;
|
|
3474
|
-
/** @inheritdoc */
|
|
3475
|
-
positionsFormat = VertexFormat.FLOAT_32X3;
|
|
3476
|
-
/** @inheritdoc */
|
|
3477
|
-
colors;
|
|
3478
|
-
/** @inheritdoc */
|
|
3479
|
-
colorFormat = VertexFormat.FLOAT_32X4;
|
|
3480
|
-
/** @inheritdoc */
|
|
3481
|
-
textureCoords;
|
|
3482
|
-
/** @inheritdoc */
|
|
3483
|
-
textureFormat = VertexFormat.FLOAT_32X2;
|
|
3484
|
-
/** @inheritdoc */
|
|
3485
|
-
normals;
|
|
3486
|
-
/** @inheritdoc */
|
|
3487
|
-
normalsFormat = VertexFormat.FLOAT_32X3;
|
|
3488
|
-
/** The indices of geometry. */
|
|
3489
|
-
indices;
|
|
3490
|
-
/**
|
|
3491
|
-
* Pushes vertex data from source format to destination format.
|
|
3492
|
-
* @param index The index of the vertex to push.
|
|
3493
|
-
* @param source The source vertex data.
|
|
3494
|
-
* @param sourceFormat The vertex format of the source vertex data.
|
|
3495
|
-
* @param destination The destination array to push the vertex data to.
|
|
3496
|
-
* @param destinationFormat The vertex format of the destination vertex data.
|
|
3497
|
-
*/
|
|
3498
|
-
pushFormat(index, source, sourceFormat, destination, destinationFormat) {
|
|
3499
|
-
let _0 = 0;
|
|
3500
|
-
let _1 = 0;
|
|
3501
|
-
let _2 = 0;
|
|
3502
|
-
let _3 = 0;
|
|
3503
|
-
if (sourceFormat == VertexFormat.FLOAT_32) {
|
|
3504
|
-
_0 = source[index];
|
|
3505
|
-
} else if (sourceFormat == VertexFormat.FLOAT_32X2) {
|
|
3506
|
-
_0 = source[index * 2];
|
|
3507
|
-
_1 = source[index * 2 + 1];
|
|
3508
|
-
} else if (sourceFormat == VertexFormat.FLOAT_32X3) {
|
|
3509
|
-
_0 = source[index * 3];
|
|
3510
|
-
_1 = source[index * 3 + 1];
|
|
3511
|
-
_2 = source[index * 3 + 2];
|
|
3512
|
-
} else if (sourceFormat == VertexFormat.FLOAT_32X4) {
|
|
3513
|
-
_0 = source[index * 4];
|
|
3514
|
-
_1 = source[index * 4 + 1];
|
|
3515
|
-
_2 = source[index * 4 + 2];
|
|
3516
|
-
_3 = source[index * 4 + 3];
|
|
3517
|
-
}
|
|
3518
|
-
destination.push(_0);
|
|
3519
|
-
if (destinationFormat >= VertexFormat.FLOAT_32X2) {
|
|
3520
|
-
destination.push(_1);
|
|
3521
|
-
}
|
|
3522
|
-
if (destinationFormat >= VertexFormat.FLOAT_32X3) {
|
|
3523
|
-
destination.push(_2);
|
|
3524
|
-
}
|
|
3525
|
-
if (destinationFormat >= VertexFormat.FLOAT_32X4) {
|
|
3526
|
-
destination.push(_3);
|
|
3527
|
-
}
|
|
3528
|
-
}
|
|
3529
|
-
/**
|
|
3530
|
-
* Converts the geometry data to interleaved vertex data based on the specified geometry format.
|
|
3531
|
-
* @param format The geometry format to convert the geometry data to.
|
|
3532
|
-
* @returns The interleaved vertex data as an array of numbers.
|
|
3533
|
-
*/
|
|
3534
|
-
toInterleaved(format) {
|
|
3535
|
-
let interleavedData = [];
|
|
3536
|
-
switch (format) {
|
|
3537
|
-
case GeometryFormat.POS3_COLOR4: {
|
|
3538
|
-
let count = this.positions.length / 3;
|
|
3539
|
-
for (let i2 = 0; i2 < count; i2++) {
|
|
3540
|
-
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
3541
|
-
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
3542
|
-
}
|
|
3543
|
-
break;
|
|
3544
|
-
}
|
|
3545
|
-
case GeometryFormat.POS3_TEXTURECOORDS2: {
|
|
3546
|
-
let count = this.positions.length / 3;
|
|
3547
|
-
for (let i2 = 0; i2 < count; i2++) {
|
|
3548
|
-
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
3549
|
-
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X2);
|
|
3550
|
-
}
|
|
3551
|
-
break;
|
|
3552
|
-
}
|
|
3553
|
-
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2: {
|
|
3554
|
-
let count = this.positions.length / 3;
|
|
3555
|
-
for (let i2 = 0; i2 < count; i2++) {
|
|
3556
|
-
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
3557
|
-
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
3558
|
-
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X2);
|
|
3559
|
-
}
|
|
3560
|
-
break;
|
|
3561
|
-
}
|
|
3562
|
-
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2_NORMAL3: {
|
|
3563
|
-
let count = this.positions.length / 3;
|
|
3564
|
-
for (let i2 = 0; i2 < count; i2++) {
|
|
3565
|
-
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
3566
|
-
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
3567
|
-
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X2);
|
|
3568
|
-
this.pushFormat(i2, this.normals, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X3);
|
|
3569
|
-
}
|
|
3570
|
-
break;
|
|
3571
|
-
}
|
|
3572
|
-
case GeometryFormat.POS3_COLOR4_TEXTURECOORDS2_ALIGNED: {
|
|
3573
|
-
let count = this.positions.length / 3;
|
|
3574
|
-
for (let i2 = 0; i2 < count; i2++) {
|
|
3575
|
-
this.pushFormat(i2, this.positions, VertexFormat.FLOAT_32X3, interleavedData, VertexFormat.FLOAT_32X4);
|
|
3576
|
-
this.pushFormat(i2, this.colors, VertexFormat.FLOAT_32X4, interleavedData, VertexFormat.FLOAT_32X4);
|
|
3577
|
-
this.pushFormat(i2, this.textureCoords, VertexFormat.FLOAT_32X2, interleavedData, VertexFormat.FLOAT_32X4);
|
|
3578
|
-
}
|
|
3579
|
-
break;
|
|
3580
|
-
}
|
|
3581
|
-
}
|
|
3582
|
-
return interleavedData;
|
|
3583
|
-
}
|
|
3584
|
-
}
|
|
3585
5017
|
class GeometryBuilder {
|
|
3586
5018
|
static DEFAULT_SCALE = vec2.fromValues(1, 1);
|
|
3587
5019
|
/** @inheritdoc */
|
|
@@ -3678,6 +5110,35 @@ class GeometryBuilder {
|
|
|
3678
5110
|
];
|
|
3679
5111
|
return geometry;
|
|
3680
5112
|
}
|
|
5113
|
+
/** @inheritdoc */
|
|
5114
|
+
create(descriptor) {
|
|
5115
|
+
const vertexCount = descriptor.positions.length / 3;
|
|
5116
|
+
return new BaseGeometry(
|
|
5117
|
+
[...descriptor.positions],
|
|
5118
|
+
descriptor.indices ? [...descriptor.indices] : void 0,
|
|
5119
|
+
descriptor.normals ? [...descriptor.normals] : new Array(vertexCount * 3).fill(0),
|
|
5120
|
+
descriptor.textureCoords ? [...descriptor.textureCoords] : new Array(vertexCount * 2).fill(0),
|
|
5121
|
+
descriptor.colors ? [...descriptor.colors] : new Array(vertexCount * 4).fill(1)
|
|
5122
|
+
);
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
class ObjGeometryMapper {
|
|
5126
|
+
/**
|
|
5127
|
+
* Creates a geometry descriptor from an OBJ geometry definition.
|
|
5128
|
+
* @param definition The geometry definition, e.g. `result.geometry` from `ObjLoader.loadAsync`.
|
|
5129
|
+
* @param useMaterialColors True keeps the loader's per-vertex `Kd` colours; false (the default) uses white,
|
|
5130
|
+
* for when each submesh's material carries the colour instead.
|
|
5131
|
+
* @returns The descriptor. It references the definition's arrays; `geometryBuilder.create` copies them.
|
|
5132
|
+
*/
|
|
5133
|
+
static toDescriptor(definition, useMaterialColors = false) {
|
|
5134
|
+
return {
|
|
5135
|
+
positions: definition.positions,
|
|
5136
|
+
indices: definition.indices,
|
|
5137
|
+
normals: definition.normals,
|
|
5138
|
+
textureCoords: definition.textureCoords,
|
|
5139
|
+
colors: useMaterialColors ? definition.colors : void 0
|
|
5140
|
+
};
|
|
5141
|
+
}
|
|
3681
5142
|
}
|
|
3682
5143
|
class ContentManager {
|
|
3683
5144
|
/**
|
|
@@ -3693,6 +5154,7 @@ class ContentManager {
|
|
|
3693
5154
|
shaderModuleLoader;
|
|
3694
5155
|
_ktx2Factory = new x();
|
|
3695
5156
|
_ktx2TextureCache = {};
|
|
5157
|
+
_objLoader = new ObjLoader();
|
|
3696
5158
|
/** @inheritDoc */
|
|
3697
5159
|
async loadKtx2Async(path, contentConfig) {
|
|
3698
5160
|
let ktx2Texture = this._ktx2TextureCache[path];
|
|
@@ -3705,9 +5167,19 @@ class ContentManager {
|
|
|
3705
5167
|
}
|
|
3706
5168
|
return ktx2Texture;
|
|
3707
5169
|
}
|
|
5170
|
+
/**
|
|
5171
|
+
* Disposes the cached shader modules (their GL programs) and forgets cached KTX2 data.
|
|
5172
|
+
* Called from `Framework.dispose()`.
|
|
5173
|
+
*/
|
|
5174
|
+
dispose() {
|
|
5175
|
+
this.shaderModuleLoader.dispose?.();
|
|
5176
|
+
for (const key of Object.keys(this._ktx2TextureCache)) {
|
|
5177
|
+
delete this._ktx2TextureCache[key];
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
3708
5180
|
/** @inheritDoc */
|
|
3709
|
-
loadShaderModule(shaderModuleId) {
|
|
3710
|
-
return this.shaderModuleLoader.load(shaderModuleId);
|
|
5181
|
+
loadShaderModule(shaderModuleId, keyValueDictionary) {
|
|
5182
|
+
return this.shaderModuleLoader.load(shaderModuleId, keyValueDictionary);
|
|
3711
5183
|
}
|
|
3712
5184
|
/** @inheritDoc */
|
|
3713
5185
|
async loadTexture2DAsync(path, textureDescriptor, contentConfig) {
|
|
@@ -3733,10 +5205,152 @@ class ContentManager {
|
|
|
3733
5205
|
textureDescriptor?.generateMipmaps
|
|
3734
5206
|
);
|
|
3735
5207
|
}
|
|
3736
|
-
/** @inheritDoc */
|
|
3737
|
-
loadTexture2DFromPixels(width, height, pixels) {
|
|
3738
|
-
const view = new Uint8Array(pixels);
|
|
3739
|
-
return this._framework.textureFactory.create(width, height, view);
|
|
5208
|
+
/** @inheritDoc */
|
|
5209
|
+
loadTexture2DFromPixels(width, height, pixels) {
|
|
5210
|
+
const view = new Uint8Array(pixels);
|
|
5211
|
+
return this._framework.textureFactory.create(width, height, view);
|
|
5212
|
+
}
|
|
5213
|
+
/** @inheritDoc */
|
|
5214
|
+
async loadMeshAsync(path, scene) {
|
|
5215
|
+
const objResult = await this._loadObj(path);
|
|
5216
|
+
const geometry = this._createGeometry(objResult.geometry);
|
|
5217
|
+
const subMeshes = this._createSubMeshDescriptors(
|
|
5218
|
+
objResult,
|
|
5219
|
+
(definition) => this._createUnlitMaterial(definition, scene.camera)
|
|
5220
|
+
);
|
|
5221
|
+
return this._framework.meshFactory.createWithSubMeshes(scene, geometry, subMeshes);
|
|
5222
|
+
}
|
|
5223
|
+
/** @inheritDoc */
|
|
5224
|
+
async loadBlinnPhongMeshAsync(path, scene) {
|
|
5225
|
+
const camera = scene.camera;
|
|
5226
|
+
if (!camera) {
|
|
5227
|
+
throw new Error(`loadBlinnPhongMeshAsync("${path}"): the scene has no camera. Set scene.camera (or pass the scene to createPerspectiveCamera) before loading a Blinn-Phong mesh.`);
|
|
5228
|
+
}
|
|
5229
|
+
if (!camera.positionBuffer || !camera.projectionViewBuffer) {
|
|
5230
|
+
const what = [camera.projectionViewBuffer ? void 0 : "projectionViewBuffer", camera.positionBuffer ? void 0 : "positionBuffer"].filter((x2) => x2 !== void 0).join(" and ");
|
|
5231
|
+
throw new Error(`loadBlinnPhongMeshAsync("${path}"): the scene camera has no ${what}. Call camera.initialize() first (the camera factory does).`);
|
|
5232
|
+
}
|
|
5233
|
+
const objResult = await this._loadObj(path);
|
|
5234
|
+
const geometry = this._createGeometry(objResult.geometry);
|
|
5235
|
+
const subMeshes = this._createSubMeshDescriptors(
|
|
5236
|
+
objResult,
|
|
5237
|
+
(definition) => this._createBlinnPhongMaterial(definition, camera, scene.__forwardLightsBuffer)
|
|
5238
|
+
);
|
|
5239
|
+
return this._framework.meshFactory.createWithSubMeshes(scene, geometry, subMeshes);
|
|
5240
|
+
}
|
|
5241
|
+
/**
|
|
5242
|
+
* Loads the OBJ file and its .mtl material libraries.
|
|
5243
|
+
* @param path The path or url to the .obj file.
|
|
5244
|
+
* @returns The plain OBJ data.
|
|
5245
|
+
*/
|
|
5246
|
+
_loadObj(path) {
|
|
5247
|
+
return this._objLoader.loadAsync(path);
|
|
5248
|
+
}
|
|
5249
|
+
/**
|
|
5250
|
+
* Creates framework geometry from the OBJ geometry.
|
|
5251
|
+
* Vertex colours stay white, because each submesh's material carries the colour.
|
|
5252
|
+
* @param definition The OBJ geometry definition.
|
|
5253
|
+
* @returns The geometry.
|
|
5254
|
+
*/
|
|
5255
|
+
_createGeometry(definition) {
|
|
5256
|
+
const descriptor = ObjGeometryMapper.toDescriptor(definition);
|
|
5257
|
+
return this._framework.geometryBuilder.create(descriptor);
|
|
5258
|
+
}
|
|
5259
|
+
/**
|
|
5260
|
+
* Creates one submesh descriptor per material range.
|
|
5261
|
+
* Ranges that use the same material name share one material instance.
|
|
5262
|
+
* @param objResult The loaded OBJ data.
|
|
5263
|
+
* @param createMaterial Creates the material for a definition (undefined without `usemtl` or .mtl entry).
|
|
5264
|
+
* @returns The submesh descriptors, in file order.
|
|
5265
|
+
*/
|
|
5266
|
+
_createSubMeshDescriptors(objResult, createMaterial) {
|
|
5267
|
+
const definitionsByName = this._indexMaterialDefinitions(objResult.materials);
|
|
5268
|
+
const materialsByName = /* @__PURE__ */ new Map();
|
|
5269
|
+
const subMeshes = [];
|
|
5270
|
+
for (const range of objResult.geometry.materialRanges) {
|
|
5271
|
+
let material = materialsByName.get(range.materialName);
|
|
5272
|
+
if (material === void 0) {
|
|
5273
|
+
const definition = this._findMaterialDefinition(definitionsByName, range.materialName);
|
|
5274
|
+
material = createMaterial(definition);
|
|
5275
|
+
materialsByName.set(range.materialName, material);
|
|
5276
|
+
}
|
|
5277
|
+
subMeshes.push({
|
|
5278
|
+
material,
|
|
5279
|
+
indexStart: range.indexStart,
|
|
5280
|
+
indexCount: range.indexCount
|
|
5281
|
+
});
|
|
5282
|
+
}
|
|
5283
|
+
return subMeshes;
|
|
5284
|
+
}
|
|
5285
|
+
/**
|
|
5286
|
+
* Indexes material definitions by name.
|
|
5287
|
+
* @param definitions The material definitions from the .mtl files.
|
|
5288
|
+
* @returns A map from material name to definition.
|
|
5289
|
+
*/
|
|
5290
|
+
_indexMaterialDefinitions(definitions) {
|
|
5291
|
+
const definitionsByName = /* @__PURE__ */ new Map();
|
|
5292
|
+
for (const definition of definitions) {
|
|
5293
|
+
definitionsByName.set(definition.name, definition);
|
|
5294
|
+
}
|
|
5295
|
+
return definitionsByName;
|
|
5296
|
+
}
|
|
5297
|
+
/**
|
|
5298
|
+
* Finds the definition for a `usemtl` name.
|
|
5299
|
+
* @param definitionsByName The definitions by name.
|
|
5300
|
+
* @param materialName The material name, or undefined for faces without `usemtl`.
|
|
5301
|
+
* @returns The definition, or undefined when there is none.
|
|
5302
|
+
*/
|
|
5303
|
+
_findMaterialDefinition(definitionsByName, materialName) {
|
|
5304
|
+
if (materialName === void 0) {
|
|
5305
|
+
return void 0;
|
|
5306
|
+
}
|
|
5307
|
+
return definitionsByName.get(materialName);
|
|
5308
|
+
}
|
|
5309
|
+
/**
|
|
5310
|
+
* Creates the default unlit material for an OBJ material definition.
|
|
5311
|
+
* The diffuse colour is the definition's `Kd` with `d` as alpha; white when there is no definition.
|
|
5312
|
+
* @param definition The material definition, or undefined.
|
|
5313
|
+
* @param camera The scene's current camera, or undefined; the scene assigns it on update.
|
|
5314
|
+
* @returns The unlit material.
|
|
5315
|
+
*/
|
|
5316
|
+
_createUnlitMaterial(definition, camera) {
|
|
5317
|
+
const material = this._framework.materialFactory.createUnlitMaterial({
|
|
5318
|
+
camera
|
|
5319
|
+
});
|
|
5320
|
+
const diffuse = definition?.diffuseColor ?? [1, 1, 1];
|
|
5321
|
+
const opacity = definition?.opacity ?? 1;
|
|
5322
|
+
material.diffuseColor = new Color(diffuse[0], diffuse[1], diffuse[2], opacity);
|
|
5323
|
+
return material;
|
|
5324
|
+
}
|
|
5325
|
+
/**
|
|
5326
|
+
* Creates a Blinn-Phong material for an OBJ material definition.
|
|
5327
|
+
* `Kd` (with `d` as alpha) becomes the diffuse colour, white when missing.
|
|
5328
|
+
* `Ka` becomes the ambient colour; when missing the diffuse colour is used, so ambient light tints by the surface colour.
|
|
5329
|
+
* `Ks` and `Ns` become specular colour and shininess; when missing the material defaults stay (0.5 grey, 32).
|
|
5330
|
+
* @param definition The material definition, or undefined.
|
|
5331
|
+
* @param camera The scene's camera; its projection view and position buffers are bound.
|
|
5332
|
+
* @param lightsBuffer The optional lights buffer; the shader is compiled for its light counts.
|
|
5333
|
+
* @returns The Blinn-Phong material.
|
|
5334
|
+
*/
|
|
5335
|
+
_createBlinnPhongMaterial(definition, camera, lightsBuffer) {
|
|
5336
|
+
const material = this._framework.materialFactory.createForwardBlinnPhongMaterial({
|
|
5337
|
+
projectionViewBuffer: camera.projectionViewBuffer,
|
|
5338
|
+
cameraPositionBuffer: camera.positionBuffer,
|
|
5339
|
+
lightsBuffer
|
|
5340
|
+
});
|
|
5341
|
+
material.camera = camera;
|
|
5342
|
+
const diffuse = definition?.diffuseColor ?? [1, 1, 1];
|
|
5343
|
+
const ambient = definition?.ambientColor ?? diffuse;
|
|
5344
|
+
material.diffuseColor = new Color(diffuse[0], diffuse[1], diffuse[2], definition?.opacity ?? 1);
|
|
5345
|
+
material.ambientColor = new Color(ambient[0], ambient[1], ambient[2]);
|
|
5346
|
+
if (definition?.specularColor) {
|
|
5347
|
+
const specular = definition.specularColor;
|
|
5348
|
+
material.specularColor = new Color(specular[0], specular[1], specular[2]);
|
|
5349
|
+
}
|
|
5350
|
+
if (definition?.specularExponent !== void 0) {
|
|
5351
|
+
material.shininess = definition.specularExponent;
|
|
5352
|
+
}
|
|
5353
|
+
return material;
|
|
3740
5354
|
}
|
|
3741
5355
|
}
|
|
3742
5356
|
class ARenderer {
|
|
@@ -4445,16 +6059,15 @@ class WebGLFilterAnisotropicUtilities {
|
|
|
4445
6059
|
* The max anisotropy extension string.
|
|
4446
6060
|
*/
|
|
4447
6061
|
MAX_ANISOTROPY_EXT = "EXT_texture_filter_anisotropic";
|
|
4448
|
-
_extension;
|
|
4449
6062
|
/**
|
|
4450
6063
|
* Gets the max anisotropy.
|
|
4451
6064
|
* @param gl The WebGL2 rendering context.
|
|
4452
6065
|
* @returns The maximum supported level of anisotropic filtering. `0` if not supported.
|
|
4453
6066
|
*/
|
|
4454
6067
|
getMaxAnisotropy(gl) {
|
|
4455
|
-
|
|
4456
|
-
if (
|
|
4457
|
-
return gl.getParameter(
|
|
6068
|
+
const extension = gl.getExtension(this.MAX_ANISOTROPY_EXT);
|
|
6069
|
+
if (extension) {
|
|
6070
|
+
return gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
|
|
4458
6071
|
}
|
|
4459
6072
|
return 0;
|
|
4460
6073
|
}
|
|
@@ -4465,8 +6078,12 @@ class WebGLFilterAnisotropicUtilities {
|
|
|
4465
6078
|
* @param anisotropy The level of anisotropic filtering
|
|
4466
6079
|
*/
|
|
4467
6080
|
setAnisotropy(gl, texture, anisotropy) {
|
|
6081
|
+
const extension = gl.getExtension(this.MAX_ANISOTROPY_EXT);
|
|
6082
|
+
if (!extension) {
|
|
6083
|
+
return;
|
|
6084
|
+
}
|
|
4468
6085
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
4469
|
-
gl.texParameterf(gl.TEXTURE_2D,
|
|
6086
|
+
gl.texParameterf(gl.TEXTURE_2D, extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
|
|
4470
6087
|
}
|
|
4471
6088
|
}
|
|
4472
6089
|
class WebGlFrameBufferUtilities {
|
|
@@ -5464,25 +7081,38 @@ class WebGlTexture2D extends ATexture2D {
|
|
|
5464
7081
|
listener(this);
|
|
5465
7082
|
}
|
|
5466
7083
|
}
|
|
5467
|
-
|
|
7084
|
+
/** One default texture per framework, so a new framework (and GL context) never gets a texture of an old one. */
|
|
7085
|
+
static _defaultFilled = /* @__PURE__ */ new WeakMap();
|
|
5468
7086
|
/**
|
|
5469
7087
|
* Creates a default 1x1 white texture if it doesn't already exist and returns it.
|
|
5470
7088
|
* This can be used as a placeholder texture when a texture is expected but not available.
|
|
5471
7089
|
* @param framework The framework instance.
|
|
7090
|
+
* The texture belongs to that framework's GL context; each framework gets its own.
|
|
5472
7091
|
* @returns The default 1x1 white texture.
|
|
5473
7092
|
*/
|
|
5474
7093
|
static getOrCreateDefault(framework) {
|
|
5475
|
-
|
|
7094
|
+
let texture = WebGlTexture2D._defaultFilled.get(framework);
|
|
7095
|
+
if (!texture || texture.state === State.DISPOSED) {
|
|
5476
7096
|
const desc = new TextureDescriptor();
|
|
5477
7097
|
desc.width = 1;
|
|
5478
7098
|
desc.height = 1;
|
|
5479
7099
|
desc.data = [new Uint8Array([255, 255, 255, 255])];
|
|
5480
7100
|
desc.textureFormat = framework.renderer.preferredTextureFormat;
|
|
5481
7101
|
desc.label = "RisDefaultFilledTexture";
|
|
5482
|
-
|
|
5483
|
-
|
|
7102
|
+
texture = new WebGlTexture2D(framework, desc);
|
|
7103
|
+
texture.initialize();
|
|
7104
|
+
WebGlTexture2D._defaultFilled.set(framework, texture);
|
|
5484
7105
|
}
|
|
5485
|
-
return
|
|
7106
|
+
return texture;
|
|
7107
|
+
}
|
|
7108
|
+
/**
|
|
7109
|
+
* Disposes and forgets the default texture of a framework. Called from `Framework.dispose()`.
|
|
7110
|
+
* @param framework The framework instance.
|
|
7111
|
+
*/
|
|
7112
|
+
static disposeDefault(framework) {
|
|
7113
|
+
const texture = WebGlTexture2D._defaultFilled.get(framework);
|
|
7114
|
+
WebGlTexture2D._defaultFilled.delete(framework);
|
|
7115
|
+
texture?.dispose();
|
|
5486
7116
|
}
|
|
5487
7117
|
}
|
|
5488
7118
|
function asWebGLTexture2D(texture) {
|
|
@@ -5959,7 +7589,6 @@ class WebGlGraphicsDevice extends AGraphicsDevice {
|
|
|
5959
7589
|
/** @inheritdoc */
|
|
5960
7590
|
initialize() {
|
|
5961
7591
|
this._canvas = this._windowManager.canvas;
|
|
5962
|
-
debugger;
|
|
5963
7592
|
const contextOptions = {
|
|
5964
7593
|
antialias: false,
|
|
5965
7594
|
alpha: this._descriptor.alpha,
|
|
@@ -6080,11 +7709,13 @@ class WebGlBuffersFactory {
|
|
|
6080
7709
|
const mainRenderTargetFlipYVS = "#version 300 es\nlayout(location = 0) in vec3 a_position;\nlayout(location = 1) in vec2 a_texCoord;\nout vec2 v_texCoord;\nvoid main()\n {\n gl_Position = vec4(a_position, 1.0);\n v_texCoord = a_texCoord;\n}";
|
|
6081
7710
|
const mainRenderTargetFlipYFS = "#version 300 es\nprecision highp float;\nin vec2 v_texCoord;\nout vec4 outColor;\nuniform sampler2D u_texture;\nvoid main() \n{\n outColor = texture(u_texture, vec2(v_texCoord.x, 1.0 - v_texCoord.y));\n}";
|
|
6082
7711
|
const spriteVS = "#version 300 es\n\nlayout(location = 0) in vec3 a_position;\nlayout(location = 1) in vec4 a_color;\nlayout(location = 2) in vec2 a_texCoord;\n\nout vec4 v_color;\nout vec2 v_texCoords;\n\nlayout(std140) uniform CameraBuffer\n{\n mat4 u_projectionView;\n};\n\nvoid main()\n{\n gl_Position = u_projectionView * vec4(a_position, 1.0);\n v_color = a_color;\n v_texCoords = a_texCoord;\n}";
|
|
6083
|
-
const spriteFS = "#version 300 es\nprecision mediump float;\n\nin vec4 v_color;\nin vec2 v_texCoords;\n\nlayout(location = 0) out vec4 o_outputColor;\n\nuniform sampler2D u_texture;\n\nvoid main()\n{\n o_outputColor = texture(u_texture, v_texCoords) * v_color
|
|
7712
|
+
const spriteFS = "#version 300 es\nprecision mediump float;\n\nin vec4 v_color;\nin vec2 v_texCoords;\n\nlayout(location = 0) out vec4 o_outputColor;\n\nuniform sampler2D u_texture;\n\nvoid main()\n{\n o_outputColor = texture(u_texture, v_texCoords) * v_color;\n}";
|
|
6084
7713
|
const inspectTextureMipsVS = "#version 300 es\n\nlayout(location = 0) in vec3 a_position;\nlayout(location = 1) in vec4 a_color;\nlayout(location = 2) in vec2 a_texCoord;\n\nout vec4 v_color;\nout vec2 v_texCoords;\n\nlayout(std140) uniform CameraBuffer\n{\n mat4 projectionView;\n};\n\nlayout(std140) uniform ModelBuffer\n{\n mat4 model;\n};\n\nvoid main()\n{\n gl_Position = projectionView * model * vec4(a_position, 1.0);\n v_color = a_color;\n v_texCoords = a_texCoord;\n}";
|
|
6085
7714
|
const inspectTextureMipsFS = "#version 300 es\nprecision mediump float;\n\nin vec4 v_color;\nin vec2 v_texCoords;\n\nlayout(location = 0) out vec4 o_outputColor;\n\nlayout(std140) uniform TextureConstantsBuffer {\n float mipLevel;\n};\n\nuniform sampler2D u_texture;\n\nvoid main()\n{\n o_outputColor = textureLod(u_texture, v_texCoords, mipLevel) * v_color;\n}";
|
|
6086
7715
|
const unlitMaterialVS = "#version 300 es\n\nlayout(location = 0) in vec3 a_position;\nlayout(location = 1) in vec4 a_color;\nlayout(location = 2) in vec2 a_texCoord;\n\nout vec4 v_color;\nout vec2 v_texCoords;\n\nlayout(std140) uniform CameraBuffer\n{\n mat4 projectionView;\n};\n\nlayout(std140) uniform ModelBuffer\n{\n mat4 model;\n};\n\nvoid main()\n{\n gl_Position = projectionView * model * vec4(a_position, 1.0);\n v_color = a_color;\n v_texCoords = a_texCoord;\n}";
|
|
6087
|
-
const unlitMaterialFS = "#version 300 es\nprecision mediump float;\n\nin vec4 v_color;\nin vec2 v_texCoords;\n\nlayout(location = 0) out vec4 o_outputColor;\n\nlayout(std140) uniform MaterialBuffer\n
|
|
7716
|
+
const unlitMaterialFS = "#version 300 es\nprecision mediump float;\n\nin vec4 v_color;\nin vec2 v_texCoords;\n\nlayout(location = 0) out vec4 o_outputColor;\n\nlayout(std140) uniform MaterialBuffer\n{\n vec4 diffuseColor;\n};\n\nuniform sampler2D u_texture;\n\nvoid main()\n{\n o_outputColor = texture(u_texture, v_texCoords) * v_color * diffuseColor;\n}";
|
|
7717
|
+
const forwardBlinnPhongVS = "#version 300 es\n\nlayout(location = 0) in vec3 a_position;\nlayout(location = 1) in vec4 a_color;\nlayout(location = 2) in vec2 a_texCoord;\nlayout(location = 3) in vec3 a_normal;\n\nout vec4 v_color;\nout vec2 v_texCoords;\nout vec3 v_normal;\nout vec3 v_worldPosition;\n\nlayout(std140) uniform CameraBuffer\n{\n mat4 projectionView;\n};\n\nlayout(std140) uniform ModelBuffer\n{\n mat4 model;\n};\n\nvoid main()\n{\n vec4 worldPosition = model * vec4(a_position, 1.0);\n gl_Position = projectionView * worldPosition;\n v_color = a_color;\n v_texCoords = a_texCoord;\n // World-space normal through the normal matrix, correct for non-uniform mesh scale.\n v_normal = transpose(inverse(mat3(model))) * a_normal;\n v_worldPosition = worldPosition.xyz;\n}\n";
|
|
7718
|
+
const forwardBlinnPhongFS = "#version 300 es\nprecision mediump float;\n\n#define DIRECTIONAL_LIGHTS_COUNT ##DIRECTIONAL_LIGHTS_COUNT##\n#define POINT_LIGHTS_COUNT ##POINT_LIGHTS_COUNT##\n\nin vec4 v_color;\nin vec2 v_texCoords;\nin vec3 v_normal;\nin vec3 v_worldPosition;\n\nlayout(location = 0) out vec4 o_outputColor;\n\nlayout(std140) uniform MaterialBuffer\n{\n vec3 u_ambientColor;\n vec3 u_diffuseColor;\n vec3 u_specularColor;\n float u_shininess;\n};\n\nlayout(std140) uniform CameraPositionBuffer\n{\n vec3 u_cameraPosition;\n};\n\nstruct DirectionalLight\n{\n vec3 light;\n vec3 direction;\n};\n\nstruct PointLight\n{\n vec3 light;\n float constant;\n vec3 position;\n float linear;\n float quadratic;\n};\n\nlayout(std140) uniform LightsBuffer\n{\n vec3 u_ambientLight;\n DirectionalLight u_directionalLight[DIRECTIONAL_LIGHTS_COUNT];\n PointLight u_pointLight[POINT_LIGHTS_COUNT];\n};\n\nuniform sampler2D u_texture;\n\nvoid main()\n{\n vec3 normal = normalize(v_normal);\n\n // Ambient lighting\n vec3 ambient = u_ambientColor * u_ambientLight;\n\n vec3 viewDirection = normalize(u_cameraPosition - v_worldPosition);\n\n // Directional diffuse and Blinn-Phong specular lighting\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n\n for (int i = 0; i < DIRECTIONAL_LIGHTS_COUNT; ++i)\n {\n vec3 lightColor = u_directionalLight[i].light;\n vec3 lightDirection = normalize(-u_directionalLight[i].direction);\n\n float NdotL = max(dot(normal, lightDirection), 0.0);\n\n diffuse += u_diffuseColor * lightColor * NdotL;\n\n if (NdotL > 0.0)\n {\n vec3 halfway = normalize(lightDirection + viewDirection);\n float NdotH = max(dot(normal, halfway), 0.0);\n specular += u_specularColor * lightColor * pow(NdotH, u_shininess);\n }\n }\n\n // Point lights: same diffuse and specular, scaled by distance attenuation.\n for (int i = 0; i < POINT_LIGHTS_COUNT; ++i)\n {\n vec3 toLight = u_pointLight[i].position - v_worldPosition;\n float lightDistance = length(toLight);\n vec3 lightDirection = toLight / max(lightDistance, 0.0001);\n float attenuation = 1.0 / (u_pointLight[i].constant\n + u_pointLight[i].linear * lightDistance\n + u_pointLight[i].quadratic * lightDistance * lightDistance);\n vec3 lightColor = u_pointLight[i].light * attenuation;\n\n float NdotL = max(dot(normal, lightDirection), 0.0);\n\n diffuse += u_diffuseColor * lightColor * NdotL;\n\n if (NdotL > 0.0)\n {\n vec3 halfway = normalize(lightDirection + viewDirection);\n float NdotH = max(dot(normal, halfway), 0.0);\n specular += u_specularColor * lightColor * pow(NdotH, u_shininess);\n }\n }\n\n // Apply material lighting, texture, and vertex color\n vec4 texel = texture(u_texture, v_texCoords) * v_color;\n // Specular highlights are not tinted by the texture.\n vec3 finalColor = texel.rgb * (ambient + diffuse) + specular;\n\n o_outputColor = vec4(finalColor, texel.a);\n}";
|
|
6088
7719
|
const ShaderModuleContent = {
|
|
6089
7720
|
"main_render_target_flip_y": {
|
|
6090
7721
|
"vertex": mainRenderTargetFlipYVS,
|
|
@@ -6105,6 +7736,11 @@ const ShaderModuleContent = {
|
|
|
6105
7736
|
"vertex": unlitMaterialVS,
|
|
6106
7737
|
"fragment": unlitMaterialFS,
|
|
6107
7738
|
"reflection": {}
|
|
7739
|
+
},
|
|
7740
|
+
"forward_blinn_phong_material": {
|
|
7741
|
+
"vertex": forwardBlinnPhongVS,
|
|
7742
|
+
"fragment": forwardBlinnPhongFS,
|
|
7743
|
+
"reflection": {}
|
|
6108
7744
|
}
|
|
6109
7745
|
};
|
|
6110
7746
|
class WebGlShaderModule {
|
|
@@ -6115,11 +7751,12 @@ class WebGlShaderModule {
|
|
|
6115
7751
|
* The constructor of the WebGL shader module.
|
|
6116
7752
|
* @param framework The framework.
|
|
6117
7753
|
* @param shaderFilePath
|
|
7754
|
+
* @param @param keyValueDictionary - If present, tries to find key in shader such that it replaced ##DIRECTIONAL_LIGHT_COUNT## by given value for key DIRECTIONAL_LIGHST_COUNT.
|
|
6118
7755
|
*/
|
|
6119
|
-
constructor(framework, shaderFilePath) {
|
|
7756
|
+
constructor(framework, shaderFilePath, keyValueDictionary) {
|
|
6120
7757
|
this._graphicsDevice = framework.renderer.graphicsDevice;
|
|
6121
7758
|
this._gl = this._graphicsDevice.gl;
|
|
6122
|
-
this._program = this._loadShaders(shaderFilePath);
|
|
7759
|
+
this._program = this._loadShaders(shaderFilePath, keyValueDictionary);
|
|
6123
7760
|
}
|
|
6124
7761
|
/** @inheritDoc */
|
|
6125
7762
|
stages = [];
|
|
@@ -6147,7 +7784,7 @@ class WebGlShaderModule {
|
|
|
6147
7784
|
get program() {
|
|
6148
7785
|
return this._program;
|
|
6149
7786
|
}
|
|
6150
|
-
_loadShaders(shaderFilePath) {
|
|
7787
|
+
_loadShaders(shaderFilePath, keyValueDictionary) {
|
|
6151
7788
|
const internalShader = this._tryResolveFrameworkShader(shaderFilePath);
|
|
6152
7789
|
let vertexShaderSource = "";
|
|
6153
7790
|
let fragmentShaderSource = "";
|
|
@@ -6157,6 +7794,12 @@ class WebGlShaderModule {
|
|
|
6157
7794
|
} else {
|
|
6158
7795
|
throw new Error("Not implemented");
|
|
6159
7796
|
}
|
|
7797
|
+
if (keyValueDictionary) {
|
|
7798
|
+
for (const key in keyValueDictionary) {
|
|
7799
|
+
vertexShaderSource = vertexShaderSource.replace(`##${key}##`, String(keyValueDictionary[key]));
|
|
7800
|
+
fragmentShaderSource = fragmentShaderSource.replace(`##${key}##`, String(keyValueDictionary[key]));
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
6160
7803
|
const vertexShader = WebGlUtilities.shader.createVertexShader(this._gl, vertexShaderSource);
|
|
6161
7804
|
const fragmentShader = WebGlUtilities.shader.createFragmentShader(this._gl, fragmentShaderSource);
|
|
6162
7805
|
const program = WebGlUtilities.program.create(this._gl, vertexShader, fragmentShader);
|
|
@@ -6172,6 +7815,13 @@ class WebGlShaderModule {
|
|
|
6172
7815
|
}
|
|
6173
7816
|
}
|
|
6174
7817
|
}
|
|
7818
|
+
function shaderModuleCacheKey(id, keyValueDictionary) {
|
|
7819
|
+
if (!keyValueDictionary) {
|
|
7820
|
+
return id;
|
|
7821
|
+
}
|
|
7822
|
+
const entries = Object.keys(keyValueDictionary).sort().map((key) => `${key}=${keyValueDictionary[key]}`);
|
|
7823
|
+
return entries.length === 0 ? id : `${id}?${entries.join("&")}`;
|
|
7824
|
+
}
|
|
6175
7825
|
class WebGlShaderModuleLoader {
|
|
6176
7826
|
_shaderModules = /* @__PURE__ */ new Map();
|
|
6177
7827
|
_framework;
|
|
@@ -6182,18 +7832,23 @@ class WebGlShaderModuleLoader {
|
|
|
6182
7832
|
constructor(framework) {
|
|
6183
7833
|
this._framework = framework;
|
|
6184
7834
|
}
|
|
6185
|
-
/**
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
load(assetFilePathOrIdentifier) {
|
|
6190
|
-
let shaderModule = this._shaderModules.get(assetFilePathOrIdentifier);
|
|
7835
|
+
/** @inheritDoc */
|
|
7836
|
+
load(assetFilePathOrIdentifier, keyValueDictionary) {
|
|
7837
|
+
const cacheKey = shaderModuleCacheKey(assetFilePathOrIdentifier, keyValueDictionary);
|
|
7838
|
+
let shaderModule = this._shaderModules.get(cacheKey);
|
|
6191
7839
|
if (!shaderModule) {
|
|
6192
|
-
shaderModule = new WebGlShaderModule(this._framework, assetFilePathOrIdentifier);
|
|
6193
|
-
this._shaderModules.set(
|
|
7840
|
+
shaderModule = new WebGlShaderModule(this._framework, assetFilePathOrIdentifier, keyValueDictionary);
|
|
7841
|
+
this._shaderModules.set(cacheKey, shaderModule);
|
|
6194
7842
|
}
|
|
6195
7843
|
return shaderModule;
|
|
6196
7844
|
}
|
|
7845
|
+
/** Deletes every cached shader program and clears the cache. */
|
|
7846
|
+
dispose() {
|
|
7847
|
+
for (const shaderModule of this._shaderModules.values()) {
|
|
7848
|
+
shaderModule.dispose();
|
|
7849
|
+
}
|
|
7850
|
+
this._shaderModules.clear();
|
|
7851
|
+
}
|
|
6197
7852
|
}
|
|
6198
7853
|
class SpriteBatchMesh extends Mesh {
|
|
6199
7854
|
/**
|
|
@@ -6203,13 +7858,22 @@ class SpriteBatchMesh extends Mesh {
|
|
|
6203
7858
|
static TOTAL_FLOATS_PER_VERTEX = 9;
|
|
6204
7859
|
/** The total number of floats in a sprite. 4 for each vertex * 9 floats per vertex (xyz rgba uv). */
|
|
6205
7860
|
static TOTAL_FLOATS_IN_SPRITE = 4 * this.TOTAL_FLOATS_PER_VERTEX;
|
|
7861
|
+
_spriteMaterial;
|
|
6206
7862
|
_maxInstances = 0;
|
|
6207
7863
|
_lastIndex = -1;
|
|
7864
|
+
/** The offset of the first index to draw. Defaults to 0. */
|
|
7865
|
+
indicesOffset = 0;
|
|
7866
|
+
/** The number of indices to draw. If -1 draws all indices. */
|
|
7867
|
+
indicesCount = -1;
|
|
6208
7868
|
/**
|
|
6209
7869
|
* The constructor for the batch sprite mesh.
|
|
7870
|
+
* @param framework - The framework.
|
|
7871
|
+
* @param material - The material.
|
|
7872
|
+
* @param numberOfInstances - The number of instances.
|
|
6210
7873
|
*/
|
|
6211
|
-
constructor(framework, numberOfInstances) {
|
|
6212
|
-
super(framework);
|
|
7874
|
+
constructor(framework, material, numberOfInstances) {
|
|
7875
|
+
super(framework, null, material);
|
|
7876
|
+
this._spriteMaterial = material;
|
|
6213
7877
|
this._maxInstances = numberOfInstances;
|
|
6214
7878
|
this._vertexData = new Array(this.maxInstances * SpriteBatchMesh.TOTAL_FLOATS_IN_SPRITE);
|
|
6215
7879
|
this._setupIndices();
|
|
@@ -6227,6 +7891,12 @@ class SpriteBatchMesh extends Mesh {
|
|
|
6227
7891
|
}
|
|
6228
7892
|
this._indices = indices;
|
|
6229
7893
|
}
|
|
7894
|
+
/**
|
|
7895
|
+
* Draws the mesh with its sprite material, using {@link indicesCount} and {@link indicesOffset}.
|
|
7896
|
+
*/
|
|
7897
|
+
draw() {
|
|
7898
|
+
this._spriteMaterial.render(this._vertexBuffer, this._indexBuffer, this.indicesCount, this.indicesOffset);
|
|
7899
|
+
}
|
|
6230
7900
|
/** The maximum number of instances that can be batched. */
|
|
6231
7901
|
get maxInstances() {
|
|
6232
7902
|
return this._maxInstances;
|
|
@@ -6393,7 +8063,7 @@ class SpriteBatchDrawable {
|
|
|
6393
8063
|
_sampler;
|
|
6394
8064
|
_maxBatchSize;
|
|
6395
8065
|
_drawingMesh = null;
|
|
6396
|
-
|
|
8066
|
+
_material = null;
|
|
6397
8067
|
_needsResize = false;
|
|
6398
8068
|
_fromInstance = 0;
|
|
6399
8069
|
_toInstance = 0;
|
|
@@ -6418,10 +8088,12 @@ class SpriteBatchDrawable {
|
|
|
6418
8088
|
projectionViewBuffer;
|
|
6419
8089
|
/** Initialize the drawable */
|
|
6420
8090
|
initialize() {
|
|
6421
|
-
this.
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
8091
|
+
this._material = this._framework.materialFactory.createSpriteMaterial({
|
|
8092
|
+
projectionViewBuffer: this.projectionViewBuffer,
|
|
8093
|
+
texture: this._texture,
|
|
8094
|
+
sampler: this._sampler
|
|
8095
|
+
});
|
|
8096
|
+
this._drawingMesh = new SpriteBatchMesh(this._framework, this._material, this._maxBatchSize);
|
|
6425
8097
|
this._drawingMesh.initialize();
|
|
6426
8098
|
}
|
|
6427
8099
|
/**
|
|
@@ -6459,15 +8131,10 @@ class SpriteBatchDrawable {
|
|
|
6459
8131
|
if (this._toInstance > this._maxBatchSize) {
|
|
6460
8132
|
this._toInstance = this._maxBatchSize;
|
|
6461
8133
|
}
|
|
6462
|
-
|
|
6463
|
-
|
|
8134
|
+
this._drawingMesh.indicesOffset = this._fromInstance * 6;
|
|
8135
|
+
this._drawingMesh.indicesCount = this._toInstance * 6 - this._drawingMesh.indicesOffset;
|
|
6464
8136
|
this._drawingMesh.applyChanges();
|
|
6465
|
-
this.
|
|
6466
|
-
this._drawingMesh.vertexBuffer,
|
|
6467
|
-
this._drawingMesh.indexBuffer,
|
|
6468
|
-
indicesCount,
|
|
6469
|
-
indicesOffset
|
|
6470
|
-
);
|
|
8137
|
+
this._drawingMesh.draw();
|
|
6471
8138
|
this._fromInstance = this._toInstance;
|
|
6472
8139
|
}
|
|
6473
8140
|
/**
|
|
@@ -6482,7 +8149,7 @@ class SpriteBatchDrawable {
|
|
|
6482
8149
|
}
|
|
6483
8150
|
/** @inheritDoc */
|
|
6484
8151
|
dispose() {
|
|
6485
|
-
this.
|
|
8152
|
+
this._material.dispose();
|
|
6486
8153
|
this._drawingMesh.dispose();
|
|
6487
8154
|
}
|
|
6488
8155
|
}
|
|
@@ -6827,6 +8494,35 @@ class VertexBufferLayout {
|
|
|
6827
8494
|
]
|
|
6828
8495
|
);
|
|
6829
8496
|
}
|
|
8497
|
+
static createFloat3Float4Float2Float3Layout() {
|
|
8498
|
+
const floatSize = 4;
|
|
8499
|
+
return new VertexBufferLayout(
|
|
8500
|
+
(3 + 4 + 2 + 3) * floatSize,
|
|
8501
|
+
VertexStepMode.VERTEX,
|
|
8502
|
+
[
|
|
8503
|
+
{
|
|
8504
|
+
format: VertexFormat.FLOAT_32X3,
|
|
8505
|
+
offset: 0,
|
|
8506
|
+
shaderLocation: 0
|
|
8507
|
+
},
|
|
8508
|
+
{
|
|
8509
|
+
format: VertexFormat.FLOAT_32X4,
|
|
8510
|
+
offset: 3 * floatSize,
|
|
8511
|
+
shaderLocation: 1
|
|
8512
|
+
},
|
|
8513
|
+
{
|
|
8514
|
+
format: VertexFormat.FLOAT_32X2,
|
|
8515
|
+
offset: (3 + 4) * floatSize,
|
|
8516
|
+
shaderLocation: 2
|
|
8517
|
+
},
|
|
8518
|
+
{
|
|
8519
|
+
format: VertexFormat.FLOAT_32X3,
|
|
8520
|
+
offset: (3 + 4 + 2) * floatSize,
|
|
8521
|
+
shaderLocation: 3
|
|
8522
|
+
}
|
|
8523
|
+
]
|
|
8524
|
+
);
|
|
8525
|
+
}
|
|
6830
8526
|
}
|
|
6831
8527
|
class AWebGlRenderPipeline {
|
|
6832
8528
|
/** The framework instance. */
|
|
@@ -6920,6 +8616,8 @@ class WebGlMainRenderTargetRenderPipeline extends AWebGlRenderPipeline {
|
|
|
6920
8616
|
super(framework);
|
|
6921
8617
|
this._mainRenderTarget = mainRenderTarget;
|
|
6922
8618
|
}
|
|
8619
|
+
/** @inheritDoc */
|
|
8620
|
+
geometryFormat = GeometryFormat.POS3_COLOR4_TEXTURECOORDS2;
|
|
6923
8621
|
/** @inheritdoc */
|
|
6924
8622
|
_provideBuffers() {
|
|
6925
8623
|
return [this._vertexBuffer.buffer];
|
|
@@ -6995,6 +8693,8 @@ class WebGlSpriteRenderPipeline extends AWebGlRenderPipeline {
|
|
|
6995
8693
|
super(framework);
|
|
6996
8694
|
this._projectionViewBuffer = asWebGLUniformBuffer(projectionViewBuffer);
|
|
6997
8695
|
}
|
|
8696
|
+
/** @inheritDoc */
|
|
8697
|
+
geometryFormat = GeometryFormat.POS3_COLOR4_TEXTURECOORDS2;
|
|
6998
8698
|
/** @inheritdoc */
|
|
6999
8699
|
get projectionViewBuffer() {
|
|
7000
8700
|
return this._projectionViewBuffer;
|
|
@@ -7088,11 +8788,16 @@ class WebGlInspectTextureMipsRenderPipeline extends AWebGlRenderPipeline {
|
|
|
7088
8788
|
* @param textureConstantsBuffer The texture constants buffer.
|
|
7089
8789
|
*/
|
|
7090
8790
|
constructor(framework, projectionViewBuffer, modelBuffer, textureConstantsBuffer, primitiveState) {
|
|
8791
|
+
if (!modelBuffer) {
|
|
8792
|
+
throw new Error("WebGlInspectTextureMipsRenderPipeline: modelBuffer is required (the mesh's model buffer, std140 mat4).");
|
|
8793
|
+
}
|
|
7091
8794
|
super(framework, primitiveState);
|
|
7092
8795
|
this._projectionViewBuffer = projectionViewBuffer;
|
|
7093
8796
|
this._modelBuffer = modelBuffer;
|
|
7094
8797
|
this._textureConstantsBuffer = textureConstantsBuffer;
|
|
7095
8798
|
}
|
|
8799
|
+
/** @inheritDoc */
|
|
8800
|
+
geometryFormat = GeometryFormat.POS3_COLOR4_TEXTURECOORDS2;
|
|
7096
8801
|
/** @inheritdoc */
|
|
7097
8802
|
get projectionViewBuffer() {
|
|
7098
8803
|
return this._projectionViewBuffer;
|
|
@@ -7107,6 +8812,9 @@ class WebGlInspectTextureMipsRenderPipeline extends AWebGlRenderPipeline {
|
|
|
7107
8812
|
}
|
|
7108
8813
|
/** @inheritdoc */
|
|
7109
8814
|
set modelBuffer(value) {
|
|
8815
|
+
if (!value) {
|
|
8816
|
+
throw new Error("WebGlInspectTextureMipsRenderPipeline: modelBuffer is required (the mesh's model buffer, std140 mat4).");
|
|
8817
|
+
}
|
|
7110
8818
|
this._modelBuffer = asWebGLUniformBuffer(value);
|
|
7111
8819
|
}
|
|
7112
8820
|
/** @inheritdoc */
|
|
@@ -7223,11 +8931,16 @@ class WebGlUnlitRenderPipeline extends AWebGlRenderPipeline {
|
|
|
7223
8931
|
* @param primitiveState The primitive state.
|
|
7224
8932
|
*/
|
|
7225
8933
|
constructor(framework, projectionViewBuffer, modelBuffer, materialBuffer, primitiveState) {
|
|
8934
|
+
if (!modelBuffer) {
|
|
8935
|
+
throw new Error("WebGlUnlitRenderPipeline: modelBuffer is required (the mesh's model buffer, std140 mat4).");
|
|
8936
|
+
}
|
|
7226
8937
|
super(framework, primitiveState);
|
|
7227
8938
|
this._projectionViewBuffer = projectionViewBuffer;
|
|
7228
8939
|
this._modelBuffer = modelBuffer;
|
|
7229
8940
|
this._materialBuffer = materialBuffer;
|
|
7230
8941
|
}
|
|
8942
|
+
/** @inheritDoc */
|
|
8943
|
+
geometryFormat = GeometryFormat.POS3_COLOR4_TEXTURECOORDS2;
|
|
7231
8944
|
/** @inheritdoc */
|
|
7232
8945
|
get projectionViewBuffer() {
|
|
7233
8946
|
return this._projectionViewBuffer;
|
|
@@ -7242,6 +8955,9 @@ class WebGlUnlitRenderPipeline extends AWebGlRenderPipeline {
|
|
|
7242
8955
|
}
|
|
7243
8956
|
/** @inheritdoc */
|
|
7244
8957
|
set modelBuffer(value) {
|
|
8958
|
+
if (!value) {
|
|
8959
|
+
throw new Error("WebGlUnlitRenderPipeline: modelBuffer is required (the mesh's model buffer, std140 mat4).");
|
|
8960
|
+
}
|
|
7245
8961
|
this._modelBuffer = asWebGLUniformBuffer(value);
|
|
7246
8962
|
}
|
|
7247
8963
|
/** @inheritdoc */
|
|
@@ -7335,6 +9051,223 @@ class WebGlUnlitRenderPipeline extends AWebGlRenderPipeline {
|
|
|
7335
9051
|
this._gl.drawElements(this.primitiveState.glPrimitiveType, toIndices, type, fromIndices);
|
|
7336
9052
|
}
|
|
7337
9053
|
}
|
|
9054
|
+
class WebGlForwardBlinnPhongRenderPipeline extends AWebGlRenderPipeline {
|
|
9055
|
+
static CAMERA_BINDING_POINT = 0;
|
|
9056
|
+
static MODEL_BINDING_POINT = 1;
|
|
9057
|
+
static MATERIAL_BINDING_POINT = 2;
|
|
9058
|
+
static LIGHTS_BINDING_POINT = 3;
|
|
9059
|
+
static CAMERA_POSITION_BINDING_POINT = 4;
|
|
9060
|
+
_texture = null;
|
|
9061
|
+
_sampler;
|
|
9062
|
+
_projectionViewBuffer;
|
|
9063
|
+
_modelBuffer;
|
|
9064
|
+
_materialBuffer;
|
|
9065
|
+
_lightsBuffer;
|
|
9066
|
+
_cameraBlockIndex = -1;
|
|
9067
|
+
_modelBlockIndex = -1;
|
|
9068
|
+
_materialBlockIndex = -1;
|
|
9069
|
+
_lightsBlockIndex = -1;
|
|
9070
|
+
_cameraPositionBuffer;
|
|
9071
|
+
_cameraPositionBlockIndex = -1;
|
|
9072
|
+
_maxDirectionalLights = DEFAULT_DIRECTIONAL_LIGHTS_COUNT;
|
|
9073
|
+
_maxPointLights = DEFAULT_POINT_LIGHTS_COUNT;
|
|
9074
|
+
_initialized = false;
|
|
9075
|
+
_buffersArray = [null];
|
|
9076
|
+
_lastVertexBuffer = null;
|
|
9077
|
+
/**
|
|
9078
|
+
* The constructor.
|
|
9079
|
+
* @param framework The framework.
|
|
9080
|
+
* @param projectionViewBuffer The projection view buffer.
|
|
9081
|
+
* @param modelBuffer The model buffer.
|
|
9082
|
+
* @param materialBuffer The material buffer.
|
|
9083
|
+
* @param lightsBuffer The lights buffer (`LightsBuffer.buffer`). May be set later.
|
|
9084
|
+
* @param cameraPositionBuffer The camera position buffer (world-space eye).
|
|
9085
|
+
* @param primitiveState The primitive state.
|
|
9086
|
+
*/
|
|
9087
|
+
constructor(framework, projectionViewBuffer, modelBuffer, materialBuffer, lightsBuffer, cameraPositionBuffer, primitiveState) {
|
|
9088
|
+
if (!modelBuffer) {
|
|
9089
|
+
throw new Error("WebGlForwardBlinnPhongRenderPipeline: modelBuffer is required (the mesh's model buffer, std140 mat4).");
|
|
9090
|
+
}
|
|
9091
|
+
super(framework, primitiveState);
|
|
9092
|
+
this._projectionViewBuffer = projectionViewBuffer;
|
|
9093
|
+
this._modelBuffer = modelBuffer;
|
|
9094
|
+
this._materialBuffer = materialBuffer;
|
|
9095
|
+
this._lightsBuffer = lightsBuffer;
|
|
9096
|
+
this._cameraPositionBuffer = cameraPositionBuffer;
|
|
9097
|
+
}
|
|
9098
|
+
/** @inheritDoc */
|
|
9099
|
+
geometryFormat = GeometryFormat.POS3_COLOR4_TEXTURECOORDS2_NORMAL3;
|
|
9100
|
+
/** @inheritdoc */
|
|
9101
|
+
get projectionViewBuffer() {
|
|
9102
|
+
return this._projectionViewBuffer;
|
|
9103
|
+
}
|
|
9104
|
+
/** @inheritdoc */
|
|
9105
|
+
set projectionViewBuffer(value) {
|
|
9106
|
+
this._projectionViewBuffer = asWebGLUniformBuffer(value);
|
|
9107
|
+
}
|
|
9108
|
+
/** @inheritdoc */
|
|
9109
|
+
get modelBuffer() {
|
|
9110
|
+
return this._modelBuffer;
|
|
9111
|
+
}
|
|
9112
|
+
/** @inheritdoc */
|
|
9113
|
+
set modelBuffer(value) {
|
|
9114
|
+
if (!value) {
|
|
9115
|
+
throw new Error("WebGlForwardBlinnPhongRenderPipeline: modelBuffer is required (the mesh's model buffer, std140 mat4).");
|
|
9116
|
+
}
|
|
9117
|
+
this._modelBuffer = asWebGLUniformBuffer(value);
|
|
9118
|
+
}
|
|
9119
|
+
/** @inheritdoc */
|
|
9120
|
+
get materialBuffer() {
|
|
9121
|
+
return this._materialBuffer;
|
|
9122
|
+
}
|
|
9123
|
+
/** @inheritdoc */
|
|
9124
|
+
set materialBuffer(value) {
|
|
9125
|
+
this._materialBuffer = asWebGLUniformBuffer(value);
|
|
9126
|
+
}
|
|
9127
|
+
/** @inheritdoc */
|
|
9128
|
+
get lightsBuffer() {
|
|
9129
|
+
return this._lightsBuffer;
|
|
9130
|
+
}
|
|
9131
|
+
/** @inheritdoc */
|
|
9132
|
+
set lightsBuffer(value) {
|
|
9133
|
+
this._lightsBuffer = asWebGLUniformBuffer(value);
|
|
9134
|
+
}
|
|
9135
|
+
/** @inheritdoc */
|
|
9136
|
+
get maxDirectionalLights() {
|
|
9137
|
+
return this._maxDirectionalLights;
|
|
9138
|
+
}
|
|
9139
|
+
/** @inheritdoc */
|
|
9140
|
+
get maxPointLights() {
|
|
9141
|
+
return this._maxPointLights;
|
|
9142
|
+
}
|
|
9143
|
+
/** @inheritdoc */
|
|
9144
|
+
setLightCounts(maxDirectionalLights, maxPointLights) {
|
|
9145
|
+
if (maxDirectionalLights === this._maxDirectionalLights && maxPointLights === this._maxPointLights) {
|
|
9146
|
+
return;
|
|
9147
|
+
}
|
|
9148
|
+
this._maxDirectionalLights = maxDirectionalLights;
|
|
9149
|
+
this._maxPointLights = maxPointLights;
|
|
9150
|
+
if (this._initialized) {
|
|
9151
|
+
this._loadProgram();
|
|
9152
|
+
}
|
|
9153
|
+
}
|
|
9154
|
+
/** @inheritdoc */
|
|
9155
|
+
get cameraPositionBuffer() {
|
|
9156
|
+
return this._cameraPositionBuffer;
|
|
9157
|
+
}
|
|
9158
|
+
/** @inheritdoc */
|
|
9159
|
+
set cameraPositionBuffer(value) {
|
|
9160
|
+
this._cameraPositionBuffer = asWebGLUniformBuffer(value);
|
|
9161
|
+
}
|
|
9162
|
+
/** @inheritdoc */
|
|
9163
|
+
get diffuseTexture() {
|
|
9164
|
+
return this._texture;
|
|
9165
|
+
}
|
|
9166
|
+
/** @inheritdoc */
|
|
9167
|
+
set diffuseTexture(value) {
|
|
9168
|
+
this._texture = value;
|
|
9169
|
+
}
|
|
9170
|
+
/** @inheritdoc */
|
|
9171
|
+
get diffuseTextureSampler() {
|
|
9172
|
+
return this._sampler;
|
|
9173
|
+
}
|
|
9174
|
+
set diffuseTextureSampler(value) {
|
|
9175
|
+
this._sampler = value;
|
|
9176
|
+
}
|
|
9177
|
+
/** @inheritdoc */
|
|
9178
|
+
initialize() {
|
|
9179
|
+
this._loadProgram();
|
|
9180
|
+
this.vertexBufferLayouts = [VertexBufferLayout.createFloat3Float4Float2Float3Layout()];
|
|
9181
|
+
super.initialize();
|
|
9182
|
+
this._initialized = true;
|
|
9183
|
+
}
|
|
9184
|
+
/** Loads the shader variant for the current light counts (cached per counts by the shader loader). */
|
|
9185
|
+
_loadProgram() {
|
|
9186
|
+
const module = this._framework.content.loadShaderModule("forward_blinn_phong_material", {
|
|
9187
|
+
"DIRECTIONAL_LIGHTS_COUNT": this._maxDirectionalLights,
|
|
9188
|
+
"POINT_LIGHTS_COUNT": this._maxPointLights
|
|
9189
|
+
});
|
|
9190
|
+
this._program = module.program;
|
|
9191
|
+
this._createResources();
|
|
9192
|
+
}
|
|
9193
|
+
_createResources() {
|
|
9194
|
+
{
|
|
9195
|
+
this._texture = WebGlTexture2D.getOrCreateDefault(this._framework);
|
|
9196
|
+
this._cameraBlockIndex = this._gl.getUniformBlockIndex(this._program, "CameraBuffer");
|
|
9197
|
+
this._gl.uniformBlockBinding(this._program, this._cameraBlockIndex, WebGlForwardBlinnPhongRenderPipeline.CAMERA_BINDING_POINT);
|
|
9198
|
+
this._modelBlockIndex = this._gl.getUniformBlockIndex(this._program, "ModelBuffer");
|
|
9199
|
+
this._gl.uniformBlockBinding(this._program, this._modelBlockIndex, WebGlForwardBlinnPhongRenderPipeline.MODEL_BINDING_POINT);
|
|
9200
|
+
this._materialBlockIndex = this._gl.getUniformBlockIndex(this._program, "MaterialBuffer");
|
|
9201
|
+
this._gl.uniformBlockBinding(this._program, this._materialBlockIndex, WebGlForwardBlinnPhongRenderPipeline.MATERIAL_BINDING_POINT);
|
|
9202
|
+
this._lightsBlockIndex = this._gl.getUniformBlockIndex(this._program, "LightsBuffer");
|
|
9203
|
+
this._gl.uniformBlockBinding(this._program, this._lightsBlockIndex, WebGlForwardBlinnPhongRenderPipeline.LIGHTS_BINDING_POINT);
|
|
9204
|
+
this._cameraPositionBlockIndex = this._gl.getUniformBlockIndex(this._program, "CameraPositionBuffer");
|
|
9205
|
+
this._gl.uniformBlockBinding(this._program, this._cameraPositionBlockIndex, WebGlForwardBlinnPhongRenderPipeline.CAMERA_POSITION_BINDING_POINT);
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
/** @inheritdoc */
|
|
9209
|
+
_provideBuffers() {
|
|
9210
|
+
if (this._lastVertexBuffer === null) {
|
|
9211
|
+
return null;
|
|
9212
|
+
}
|
|
9213
|
+
this._buffersArray[0] = this._lastVertexBuffer.buffer;
|
|
9214
|
+
return this._buffersArray;
|
|
9215
|
+
}
|
|
9216
|
+
/** @inheritdoc */
|
|
9217
|
+
render(vertexBuffer, indexBuffer, indicesCount = -1, indicesOffset = 0) {
|
|
9218
|
+
if (indicesCount == 0) {
|
|
9219
|
+
return;
|
|
9220
|
+
}
|
|
9221
|
+
const webGlVertexBuffer = vertexBuffer;
|
|
9222
|
+
const webGlIndexBuffer = indexBuffer;
|
|
9223
|
+
const webGlSampler = this._sampler ?? this._defaultTextureSampler;
|
|
9224
|
+
this.primitiveState.apply(this._gl);
|
|
9225
|
+
this._blendState.apply(this._gl);
|
|
9226
|
+
if (this._lastVertexBuffer != webGlVertexBuffer) {
|
|
9227
|
+
this._lastVertexBuffer = webGlVertexBuffer;
|
|
9228
|
+
this._createVertexArrayObject();
|
|
9229
|
+
}
|
|
9230
|
+
this._gl.useProgram(this._program);
|
|
9231
|
+
this._gl.bindVertexArray(this._vertexArrayObject);
|
|
9232
|
+
this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER, webGlIndexBuffer.buffer);
|
|
9233
|
+
this._gl.bindBufferBase(
|
|
9234
|
+
this._gl.UNIFORM_BUFFER,
|
|
9235
|
+
WebGlForwardBlinnPhongRenderPipeline.CAMERA_BINDING_POINT,
|
|
9236
|
+
this._projectionViewBuffer.glBuffer
|
|
9237
|
+
);
|
|
9238
|
+
this._gl.bindBufferBase(
|
|
9239
|
+
this._gl.UNIFORM_BUFFER,
|
|
9240
|
+
WebGlForwardBlinnPhongRenderPipeline.MODEL_BINDING_POINT,
|
|
9241
|
+
this._modelBuffer.glBuffer
|
|
9242
|
+
);
|
|
9243
|
+
if (this.materialBuffer) {
|
|
9244
|
+
this._gl.bindBufferBase(
|
|
9245
|
+
this._gl.UNIFORM_BUFFER,
|
|
9246
|
+
WebGlForwardBlinnPhongRenderPipeline.MATERIAL_BINDING_POINT,
|
|
9247
|
+
this._materialBuffer.glBuffer
|
|
9248
|
+
);
|
|
9249
|
+
}
|
|
9250
|
+
if (this._lightsBuffer) {
|
|
9251
|
+
this._gl.bindBufferBase(
|
|
9252
|
+
this._gl.UNIFORM_BUFFER,
|
|
9253
|
+
WebGlForwardBlinnPhongRenderPipeline.LIGHTS_BINDING_POINT,
|
|
9254
|
+
this._lightsBuffer.glBuffer
|
|
9255
|
+
);
|
|
9256
|
+
}
|
|
9257
|
+
this._gl.bindBufferBase(
|
|
9258
|
+
this._gl.UNIFORM_BUFFER,
|
|
9259
|
+
WebGlForwardBlinnPhongRenderPipeline.CAMERA_POSITION_BINDING_POINT,
|
|
9260
|
+
this._cameraPositionBuffer.glBuffer
|
|
9261
|
+
);
|
|
9262
|
+
this._gl.activeTexture(this._gl.TEXTURE0);
|
|
9263
|
+
this._gl.bindTexture(this._gl.TEXTURE_2D, this._texture.glTexture);
|
|
9264
|
+
this._gl.bindSampler(0, webGlSampler.glSampler);
|
|
9265
|
+
const type = indexBuffer.type == IndexBufferType.UINT_16 ? this._gl.UNSIGNED_SHORT : this._gl.UNSIGNED_INT;
|
|
9266
|
+
const toIndices = indicesCount > 0 ? indicesCount : indexBuffer.indicesCount;
|
|
9267
|
+
const fromIndices = indicesOffset * indexBuffer.elementByteSize;
|
|
9268
|
+
this._gl.drawElements(this.primitiveState.glPrimitiveType, toIndices, type, fromIndices);
|
|
9269
|
+
}
|
|
9270
|
+
}
|
|
7338
9271
|
class WebGlRenderPipelineFactory {
|
|
7339
9272
|
/**
|
|
7340
9273
|
* The constructor for the WebGLRenderPipelineFactory class.
|
|
@@ -7357,6 +9290,26 @@ class WebGlRenderPipelineFactory {
|
|
|
7357
9290
|
return pipeline;
|
|
7358
9291
|
}
|
|
7359
9292
|
/** @inheritDoc */
|
|
9293
|
+
createForwardBlinnPhongRenderPipeline(descriptor) {
|
|
9294
|
+
const pipeline = new WebGlForwardBlinnPhongRenderPipeline(
|
|
9295
|
+
this._framework,
|
|
9296
|
+
descriptor.projectionViewBuffer,
|
|
9297
|
+
descriptor.modelBuffer,
|
|
9298
|
+
descriptor.materialBuffer,
|
|
9299
|
+
descriptor.lightsBuffer,
|
|
9300
|
+
descriptor.cameraPositionBuffer,
|
|
9301
|
+
descriptor.primitiveState
|
|
9302
|
+
);
|
|
9303
|
+
if (descriptor.maxDirectionalLights !== void 0 || descriptor.maxPointLights !== void 0) {
|
|
9304
|
+
pipeline.setLightCounts(
|
|
9305
|
+
descriptor.maxDirectionalLights ?? pipeline.maxDirectionalLights,
|
|
9306
|
+
descriptor.maxPointLights ?? pipeline.maxPointLights
|
|
9307
|
+
);
|
|
9308
|
+
}
|
|
9309
|
+
pipeline.initialize();
|
|
9310
|
+
return pipeline;
|
|
9311
|
+
}
|
|
9312
|
+
/** @inheritDoc */
|
|
7360
9313
|
createMainFrameBufferPipeline(mainFrameBuffer) {
|
|
7361
9314
|
const renderPipeline = new WebGlMainRenderTargetRenderPipeline(this._framework, mainFrameBuffer);
|
|
7362
9315
|
renderPipeline.initialize();
|
|
@@ -7452,6 +9405,8 @@ class InputManager {
|
|
|
7452
9405
|
_touchCapabilities;
|
|
7453
9406
|
_contacts;
|
|
7454
9407
|
_touchListening = false;
|
|
9408
|
+
/** Aborting it removes every canvas listener added in initialize(). */
|
|
9409
|
+
_listeners = new AbortController();
|
|
7455
9410
|
/** Set in update, consumed in afterUpdate. Ageing before a publish would hide the press. */
|
|
7456
9411
|
_touchPublished = false;
|
|
7457
9412
|
static _PHASE_BEGIN = 0;
|
|
@@ -7478,6 +9433,12 @@ class InputManager {
|
|
|
7478
9433
|
getTouchCapabilities() {
|
|
7479
9434
|
return this._touchCapabilities;
|
|
7480
9435
|
}
|
|
9436
|
+
/** Removes the canvas listeners. Called from `Framework.dispose()`. */
|
|
9437
|
+
dispose() {
|
|
9438
|
+
this._listeners.abort();
|
|
9439
|
+
this._listeners = new AbortController();
|
|
9440
|
+
this._touchListening = false;
|
|
9441
|
+
}
|
|
7481
9442
|
/** @inheritDoc */
|
|
7482
9443
|
initialize() {
|
|
7483
9444
|
const canvas = this._framework.windowManager.canvas;
|
|
@@ -7488,18 +9449,18 @@ class InputManager {
|
|
|
7488
9449
|
canvas.addEventListener("mousemove", (e4) => {
|
|
7489
9450
|
this._mousePosition[0] = e4.clientX;
|
|
7490
9451
|
this._mousePosition[1] = e4.clientY;
|
|
7491
|
-
});
|
|
9452
|
+
}, { signal: this._listeners.signal });
|
|
7492
9453
|
canvas.addEventListener("mousedown", (e4) => {
|
|
7493
9454
|
const idx = e4.button;
|
|
7494
9455
|
const btn = this._mouseButtonMap[idx] ?? MouseButton.NONE;
|
|
7495
9456
|
this._mouseButtonDown[btn] = true;
|
|
7496
|
-
});
|
|
9457
|
+
}, { signal: this._listeners.signal });
|
|
7497
9458
|
canvas.addEventListener("mouseup", (e4) => {
|
|
7498
9459
|
const idx = e4.button;
|
|
7499
9460
|
const btn = this._mouseButtonMap[idx] ?? MouseButton.NONE;
|
|
7500
9461
|
this._mouseButtonDown[btn] = false;
|
|
7501
9462
|
this._mouseButtonReleased[btn] = true;
|
|
7502
|
-
});
|
|
9463
|
+
}, { signal: this._listeners.signal });
|
|
7503
9464
|
canvas.addEventListener("wheel", (e4) => {
|
|
7504
9465
|
let x2 = 0;
|
|
7505
9466
|
let y2 = 0;
|
|
@@ -7515,7 +9476,7 @@ class InputManager {
|
|
|
7515
9476
|
}
|
|
7516
9477
|
vec2.set(this._scrollWheelPosition, x2, y2);
|
|
7517
9478
|
e4.preventDefault();
|
|
7518
|
-
});
|
|
9479
|
+
}, { signal: this._listeners.signal });
|
|
7519
9480
|
}
|
|
7520
9481
|
_handleTouchEvents(canvas) {
|
|
7521
9482
|
if (this._touchListening) {
|
|
@@ -7523,7 +9484,7 @@ class InputManager {
|
|
|
7523
9484
|
}
|
|
7524
9485
|
this._touchListening = true;
|
|
7525
9486
|
canvas.style.touchAction = "none";
|
|
7526
|
-
const options = InputManager._touchListenerOptions;
|
|
9487
|
+
const options = { ...InputManager._touchListenerOptions, signal: this._listeners.signal };
|
|
7527
9488
|
canvas.addEventListener("touchstart", this._onTouchStart, options);
|
|
7528
9489
|
canvas.addEventListener("touchmove", this._onTouchMove, options);
|
|
7529
9490
|
canvas.addEventListener("touchend", this._onTouchEnd, options);
|
|
@@ -7808,12 +9769,12 @@ class Framework {
|
|
|
7808
9769
|
_onInitializeListeners = [];
|
|
7809
9770
|
_onUpdateListeners = [];
|
|
7810
9771
|
_onRenderListeners = [];
|
|
7811
|
-
_container;
|
|
7812
9772
|
_textureFactory;
|
|
7813
9773
|
_buffersFactory;
|
|
7814
9774
|
_contentManager;
|
|
7815
9775
|
_geometryBuilder;
|
|
7816
9776
|
_meshFactory;
|
|
9777
|
+
_disposed = false;
|
|
7817
9778
|
/**
|
|
7818
9779
|
* The constructor for the Framework class.
|
|
7819
9780
|
* @param options The optional framework configuration. When omitted, a default `FrameworkConfiguration` is used.
|
|
@@ -7824,7 +9785,6 @@ class Framework {
|
|
|
7824
9785
|
console.warn("Canvas not provided. Creating canvas and appending it to document.body.");
|
|
7825
9786
|
}
|
|
7826
9787
|
options.backBufferSize = options.backBufferSize ?? vec2.fromValues(800, 600);
|
|
7827
|
-
this._container = container.createChildContainer();
|
|
7828
9788
|
this.windowManager = new WindowManager(options.canvas ?? null);
|
|
7829
9789
|
const rendererConfig = {
|
|
7830
9790
|
backBufferSize: options.backBufferSize,
|
|
@@ -7832,7 +9792,6 @@ class Framework {
|
|
|
7832
9792
|
alpha: options.alpha ?? false,
|
|
7833
9793
|
powerPreference: options.powerPreference ?? PowerPreferenceType.DEFAULT
|
|
7834
9794
|
};
|
|
7835
|
-
this._container.registerInstance(RenderConfigurationSymbol, rendererConfig);
|
|
7836
9795
|
this.renderer = new WebGlRenderer(this, rendererConfig);
|
|
7837
9796
|
this.renderingBackend = RenderingBackend.WEB_GL;
|
|
7838
9797
|
this._textureFactory = new WebGlTextureFactory(this);
|
|
@@ -7841,6 +9800,7 @@ class Framework {
|
|
|
7841
9800
|
this._contentManager = new ContentManager(this, new WebGlShaderModuleLoader(this));
|
|
7842
9801
|
this._meshFactory = new MeshFactory(this);
|
|
7843
9802
|
this.materialFactory = new MaterialFactory(this);
|
|
9803
|
+
this.lightsFactory = new WebGlAmbientLightFactory(this);
|
|
7844
9804
|
this._buffersFactory = new WebGlBuffersFactory(this);
|
|
7845
9805
|
this.spriteBatch = new SpriteBatch(this);
|
|
7846
9806
|
this.cameraFactory = new CameraFactory(this);
|
|
@@ -7938,6 +9898,8 @@ class Framework {
|
|
|
7938
9898
|
/** @inheritDoc */
|
|
7939
9899
|
materialFactory;
|
|
7940
9900
|
/** @inheritDoc */
|
|
9901
|
+
lightsFactory;
|
|
9902
|
+
/** @inheritDoc */
|
|
7941
9903
|
timeManager;
|
|
7942
9904
|
/** @inheritDoc */
|
|
7943
9905
|
input;
|
|
@@ -7950,10 +9912,10 @@ class Framework {
|
|
|
7950
9912
|
this.spriteBatch.initialize();
|
|
7951
9913
|
this.input.initialize();
|
|
7952
9914
|
this.timeManager.prepareStart();
|
|
7953
|
-
for (const listener of this.
|
|
9915
|
+
for (const listener of this._onInitializeListeners) {
|
|
7954
9916
|
listener();
|
|
7955
9917
|
}
|
|
7956
|
-
for (const listener of this.
|
|
9918
|
+
for (const listener of this._onLoadContentListeners) {
|
|
7957
9919
|
listener();
|
|
7958
9920
|
}
|
|
7959
9921
|
this.renderer.afterInitialize();
|
|
@@ -7975,12 +9937,31 @@ class Framework {
|
|
|
7975
9937
|
});
|
|
7976
9938
|
this.windowManager.runEventLoop();
|
|
7977
9939
|
}
|
|
7978
|
-
/**
|
|
9940
|
+
/**
|
|
9941
|
+
* Stops the event loop and disposes the framework's services and GL resources,
|
|
9942
|
+
* including per-framework caches such as the default texture and the shader modules.
|
|
9943
|
+
* A new `Framework` afterwards creates fresh resources on its own context. Calling it twice does nothing.
|
|
9944
|
+
*/
|
|
7979
9945
|
dispose() {
|
|
9946
|
+
if (this._disposed) {
|
|
9947
|
+
return;
|
|
9948
|
+
}
|
|
9949
|
+
this._disposed = true;
|
|
9950
|
+
this.windowManager.dispose();
|
|
9951
|
+
this.input.dispose?.();
|
|
9952
|
+
this._onLoadContentListeners.length = 0;
|
|
9953
|
+
this._onInitializeListeners.length = 0;
|
|
9954
|
+
this._onUpdateListeners.length = 0;
|
|
9955
|
+
this._onRenderListeners.length = 0;
|
|
9956
|
+
WebGlTexture2D.disposeDefault(this);
|
|
9957
|
+
this._contentManager.dispose?.();
|
|
9958
|
+
this.renderer.graphicsDevice?.dispose();
|
|
7980
9959
|
}
|
|
7981
9960
|
}
|
|
7982
9961
|
export {
|
|
7983
9962
|
AlignUtilities,
|
|
9963
|
+
AmbientLight,
|
|
9964
|
+
BaseGeometry,
|
|
7984
9965
|
BufferUsage,
|
|
7985
9966
|
Button,
|
|
7986
9967
|
CameraFactory,
|
|
@@ -7988,6 +9969,11 @@ export {
|
|
|
7988
9969
|
ContentConfig,
|
|
7989
9970
|
CpuImageProcessor,
|
|
7990
9971
|
CullMode,
|
|
9972
|
+
DEFAULT_DIRECTIONAL_LIGHTS_COUNT,
|
|
9973
|
+
DEFAULT_POINT_LIGHTS_COUNT,
|
|
9974
|
+
DirectionalLight,
|
|
9975
|
+
ForwardBlinnPhongMaterial,
|
|
9976
|
+
ForwardBlinnPhongMaterialDescriptor,
|
|
7991
9977
|
Framework,
|
|
7992
9978
|
FrameworkConfiguration,
|
|
7993
9979
|
FrontFace,
|
|
@@ -8012,9 +9998,12 @@ export {
|
|
|
8012
9998
|
MipMapSamplerFilter,
|
|
8013
9999
|
MouseButton,
|
|
8014
10000
|
MouseState,
|
|
10001
|
+
ObjLoader,
|
|
10002
|
+
ObjMaterialDefinition,
|
|
8015
10003
|
OrbitCamera,
|
|
8016
10004
|
OrthographicCamera,
|
|
8017
10005
|
PerspectiveCamera,
|
|
10006
|
+
PointLight,
|
|
8018
10007
|
PowerPreferenceType,
|
|
8019
10008
|
PrimitiveStateDescriptor,
|
|
8020
10009
|
PrimitiveTopology,
|
|
@@ -8028,9 +10017,12 @@ export {
|
|
|
8028
10017
|
SamplerAddressMode,
|
|
8029
10018
|
SamplerDescriptor$1 as SamplerDescriptor,
|
|
8030
10019
|
SamplerFilter,
|
|
10020
|
+
Scene,
|
|
8031
10021
|
ShaderStage,
|
|
10022
|
+
SpriteMaterial,
|
|
8032
10023
|
State,
|
|
8033
10024
|
StoreAction,
|
|
10025
|
+
SubMesh,
|
|
8034
10026
|
SurfacePresentMode,
|
|
8035
10027
|
SwapChainDescriptor,
|
|
8036
10028
|
TextureDescriptor,
|