@codexo/exojs 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +14 -14
- package/dist/esm/core/Application.d.ts +25 -6
- package/dist/esm/core/Application.js +42 -8
- package/dist/esm/core/Application.js.map +1 -1
- package/dist/esm/core/Perf.d.ts +23 -0
- package/dist/esm/core/Perf.js +49 -0
- package/dist/esm/core/Perf.js.map +1 -0
- package/dist/esm/core/Scene.d.ts +8 -8
- package/dist/esm/core/Scene.js +7 -7
- package/dist/esm/core/Scene.js.map +1 -1
- package/dist/esm/core/SceneManager.js +2 -2
- package/dist/esm/core/SceneManager.js.map +1 -1
- package/dist/esm/core/SceneNode.d.ts +0 -3
- package/dist/esm/core/SceneNode.js +0 -9
- package/dist/esm/core/SceneNode.js.map +1 -1
- package/dist/esm/core/capabilities.d.ts +2 -0
- package/dist/esm/core/capabilities.js +15 -0
- package/dist/esm/core/capabilities.js.map +1 -1
- package/dist/esm/core/index.d.ts +1 -0
- package/dist/esm/core/types.d.ts +1 -1
- package/dist/esm/core/utils.d.ts +12 -0
- package/dist/esm/core/utils.js +18 -1
- package/dist/esm/core/utils.js.map +1 -1
- package/dist/esm/index.js +14 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/particles/ParticleSystem.d.ts +8 -5
- package/dist/esm/particles/ParticleSystem.js +9 -5
- package/dist/esm/particles/ParticleSystem.js.map +1 -1
- package/dist/esm/particles/distributions/{Gradient.d.ts → ColorGradient.d.ts} +5 -5
- package/dist/esm/particles/distributions/{Gradient.js → ColorGradient.js} +5 -5
- package/dist/esm/particles/distributions/ColorGradient.js.map +1 -0
- package/dist/esm/particles/distributions/Distribution.d.ts +2 -2
- package/dist/esm/particles/distributions/index.d.ts +2 -2
- package/dist/esm/particles/gpu/ParticleGpuState.js +1 -1
- package/dist/esm/particles/modules/ColorOverLifetime.d.ts +3 -3
- package/dist/esm/particles/modules/ColorOverLifetime.js.map +1 -1
- package/dist/esm/particles/modules/ColorOverSpeed.d.ts +3 -3
- package/dist/esm/particles/modules/ColorOverSpeed.js.map +1 -1
- package/dist/esm/particles/modules/UpdateModule.d.ts +2 -2
- package/dist/esm/particles/modules/UpdateModule.js +1 -1
- package/dist/esm/particles/modules/WgslContribution.d.ts +2 -2
- package/dist/esm/rendering/Camera.d.ts +33 -0
- package/dist/esm/rendering/Camera.js +38 -0
- package/dist/esm/rendering/Camera.js.map +1 -0
- package/dist/esm/rendering/Container.d.ts +5 -24
- package/dist/esm/rendering/Container.js +8 -71
- package/dist/esm/rendering/Container.js.map +1 -1
- package/dist/esm/rendering/Drawable.d.ts +8 -10
- package/dist/esm/rendering/Drawable.js +12 -20
- package/dist/esm/rendering/Drawable.js.map +1 -1
- package/dist/esm/rendering/RenderBackend.d.ts +18 -0
- package/dist/esm/rendering/RenderNode.d.ts +81 -8
- package/dist/esm/rendering/RenderNode.js +121 -144
- package/dist/esm/rendering/RenderNode.js.map +1 -1
- package/dist/esm/rendering/RenderTarget.d.ts +13 -0
- package/dist/esm/rendering/RenderTarget.js +13 -0
- package/dist/esm/rendering/RenderTarget.js.map +1 -1
- package/dist/esm/rendering/RenderTargetPass.js +17 -0
- package/dist/esm/rendering/RenderTargetPass.js.map +1 -1
- package/dist/esm/rendering/RenderingContext.d.ts +87 -0
- package/dist/esm/rendering/RenderingContext.js +157 -0
- package/dist/esm/rendering/RenderingContext.js.map +1 -0
- package/dist/esm/rendering/TransformBuffer.d.ts +38 -0
- package/dist/esm/rendering/TransformBuffer.js +116 -0
- package/dist/esm/rendering/TransformBuffer.js.map +1 -0
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js +5 -12
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js.map +1 -1
- package/dist/esm/rendering/geometry/Geometry.d.ts +40 -0
- package/dist/esm/rendering/geometry/Geometry.js +228 -0
- package/dist/esm/rendering/geometry/Geometry.js.map +1 -0
- package/dist/esm/rendering/geometry/GeometryAttribute.d.ts +32 -0
- package/dist/esm/rendering/geometry/QuadGeometry.d.ts +5 -0
- package/dist/esm/rendering/gradient/Gradient.d.ts +34 -0
- package/dist/esm/rendering/gradient/Gradient.js +114 -0
- package/dist/esm/rendering/gradient/Gradient.js.map +1 -0
- package/dist/esm/rendering/gradient/LinearGradient.d.ts +10 -0
- package/dist/esm/rendering/gradient/LinearGradient.js +26 -0
- package/dist/esm/rendering/gradient/LinearGradient.js.map +1 -0
- package/dist/esm/rendering/gradient/RadialGradient.d.ts +10 -0
- package/dist/esm/rendering/gradient/RadialGradient.js +25 -0
- package/dist/esm/rendering/gradient/RadialGradient.js.map +1 -0
- package/dist/esm/rendering/index.d.ts +16 -2
- package/dist/esm/rendering/material/Material.d.ts +114 -0
- package/dist/esm/rendering/material/Material.js +111 -0
- package/dist/esm/rendering/material/Material.js.map +1 -0
- package/dist/esm/rendering/material/MaterialKey.d.ts +18 -0
- package/dist/esm/rendering/material/MaterialKey.js +82 -0
- package/dist/esm/rendering/material/MaterialKey.js.map +1 -0
- package/dist/esm/rendering/material/MeshMaterial.d.ts +16 -0
- package/dist/esm/rendering/material/MeshMaterial.js +21 -0
- package/dist/esm/rendering/material/MeshMaterial.js.map +1 -0
- package/dist/esm/rendering/{mesh/MeshShader.d.ts → material/ShaderSource.d.ts} +29 -62
- package/dist/esm/rendering/{mesh/MeshShader.js → material/ShaderSource.js} +35 -62
- package/dist/esm/rendering/material/ShaderSource.js.map +1 -0
- package/dist/esm/rendering/material/SpriteMaterial.d.ts +15 -0
- package/dist/esm/rendering/material/SpriteMaterial.js +20 -0
- package/dist/esm/rendering/material/SpriteMaterial.js.map +1 -0
- package/dist/esm/rendering/mesh/Mesh.d.ts +29 -12
- package/dist/esm/rendering/mesh/Mesh.js +122 -3
- package/dist/esm/rendering/mesh/Mesh.js.map +1 -1
- package/dist/esm/rendering/pass/RenderPassCoordinator.d.ts +63 -0
- package/dist/esm/rendering/pass/RenderPassDescriptor.d.ts +48 -0
- package/dist/esm/rendering/pass/RenderPassDescriptor.js +16 -0
- package/dist/esm/rendering/pass/RenderPassDescriptor.js.map +1 -0
- package/dist/esm/rendering/plan/RenderCommand.d.ts +67 -0
- package/dist/esm/rendering/plan/RenderCommand.js +94 -0
- package/dist/esm/rendering/plan/RenderCommand.js.map +1 -0
- package/dist/esm/rendering/plan/RenderEffectExecutor.d.ts +10 -0
- package/dist/esm/rendering/plan/RenderEffectExecutor.js +159 -0
- package/dist/esm/rendering/plan/RenderEffectExecutor.js.map +1 -0
- package/dist/esm/rendering/plan/RenderPlan.d.ts +23 -0
- package/dist/esm/rendering/plan/RenderPlan.js +12 -0
- package/dist/esm/rendering/plan/RenderPlan.js.map +1 -0
- package/dist/esm/rendering/plan/RenderPlanBuilder.d.ts +31 -0
- package/dist/esm/rendering/plan/RenderPlanBuilder.js +242 -0
- package/dist/esm/rendering/plan/RenderPlanBuilder.js.map +1 -0
- package/dist/esm/rendering/plan/RenderPlanOptimizer.d.ts +10 -0
- package/dist/esm/rendering/plan/RenderPlanOptimizer.js +180 -0
- package/dist/esm/rendering/plan/RenderPlanOptimizer.js.map +1 -0
- package/dist/esm/rendering/plan/RenderPlanPlayer.d.ts +9 -0
- package/dist/esm/rendering/plan/RenderPlanPlayer.js +56 -0
- package/dist/esm/rendering/plan/RenderPlanPlayer.js.map +1 -0
- package/dist/esm/rendering/plan/RenderScope.d.ts +70 -0
- package/dist/esm/rendering/plan/RenderScope.js +16 -0
- package/dist/esm/rendering/plan/RenderScope.js.map +1 -0
- package/dist/esm/rendering/plan/playRenderTree.d.ts +4 -0
- package/dist/esm/rendering/plan/playRenderTree.js +19 -0
- package/dist/esm/rendering/plan/playRenderTree.js.map +1 -0
- package/dist/esm/rendering/sprite/Sprite.d.ts +22 -1
- package/dist/esm/rendering/sprite/Sprite.js +33 -2
- package/dist/esm/rendering/sprite/Sprite.js.map +1 -1
- package/dist/esm/rendering/sprite/spriteMaterialSources.d.ts +36 -0
- package/dist/esm/rendering/sprite/spriteMaterialSources.js +128 -0
- package/dist/esm/rendering/sprite/spriteMaterialSources.js.map +1 -0
- package/dist/esm/rendering/text/TextStyle.d.ts +1 -1
- package/dist/esm/rendering/texture/DataTexture.d.ts +5 -0
- package/dist/esm/rendering/texture/DataTexture.js +7 -0
- package/dist/esm/rendering/texture/DataTexture.js.map +1 -1
- package/dist/esm/rendering/video/Video.d.ts +3 -7
- package/dist/esm/rendering/video/Video.js +3 -8
- package/dist/esm/rendering/video/Video.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2Backend.d.ts +40 -0
- package/dist/esm/rendering/webgl2/WebGl2Backend.js +303 -22
- package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.d.ts +22 -2
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js +404 -112
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2PassCoordinator.d.ts +57 -0
- package/dist/esm/rendering/webgl2/WebGl2PassCoordinator.js +79 -0
- package/dist/esm/rendering/webgl2/WebGl2PassCoordinator.js.map +1 -0
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.d.ts +12 -0
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.js +214 -58
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2StencilClipper.d.ts +34 -0
- package/dist/esm/rendering/webgl2/WebGl2StencilClipper.js +169 -0
- package/dist/esm/rendering/webgl2/WebGl2StencilClipper.js.map +1 -0
- package/dist/esm/rendering/webgl2/WebGl2TextRenderer.js +4 -0
- package/dist/esm/rendering/webgl2/WebGl2TextRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/glsl/mesh.frag.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/mesh.vert.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/stencil-clip.frag.js +4 -0
- package/dist/esm/rendering/webgl2/glsl/stencil-clip.frag.js.map +1 -0
- package/dist/esm/rendering/webgl2/glsl/stencil-clip.vert.js +4 -0
- package/dist/esm/rendering/webgl2/glsl/stencil-clip.vert.js.map +1 -0
- package/dist/esm/rendering/webgpu/WebGpuBackend.d.ts +50 -0
- package/dist/esm/rendering/webgpu/WebGpuBackend.js +135 -19
- package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js +22 -17
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.d.ts +24 -0
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js +488 -74
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js +13 -17
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuPassCoordinator.d.ts +141 -0
- package/dist/esm/rendering/webgpu/WebGpuPassCoordinator.js +270 -0
- package/dist/esm/rendering/webgpu/WebGpuPassCoordinator.js.map +1 -0
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.d.ts +16 -0
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js +335 -26
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuStencilClipper.d.ts +57 -0
- package/dist/esm/rendering/webgpu/WebGpuStencilClipper.js +257 -0
- package/dist/esm/rendering/webgpu/WebGpuStencilClipper.js.map +1 -0
- package/dist/esm/rendering/webgpu/WebGpuStencilState.d.ts +14 -0
- package/dist/esm/rendering/webgpu/WebGpuStencilState.js +36 -0
- package/dist/esm/rendering/webgpu/WebGpuStencilState.js.map +1 -0
- package/dist/esm/rendering/webgpu/WebGpuTextRenderer.js +14 -12
- package/dist/esm/rendering/webgpu/WebGpuTextRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuTransformStorage.d.ts +16 -0
- package/dist/esm/rendering/webgpu/WebGpuTransformStorage.js +57 -0
- package/dist/esm/rendering/webgpu/WebGpuTransformStorage.js.map +1 -0
- package/dist/esm/resources/JsonStore.d.ts +18 -0
- package/dist/esm/resources/JsonStore.js +62 -0
- package/dist/esm/resources/JsonStore.js.map +1 -0
- package/dist/esm/resources/factories/ImageFactory.d.ts +14 -8
- package/dist/esm/resources/factories/ImageFactory.js +13 -6
- package/dist/esm/resources/factories/ImageFactory.js.map +1 -1
- package/dist/esm/resources/factories/TextureFactory.d.ts +4 -4
- package/dist/esm/resources/factories/TextureFactory.js +8 -4
- package/dist/esm/resources/factories/TextureFactory.js.map +1 -1
- package/dist/esm/resources/index.d.ts +1 -0
- package/dist/exo.esm.js +5424 -2205
- package/dist/exo.esm.js.map +1 -1
- package/package.json +30 -24
- package/dist/esm/particles/distributions/Gradient.js.map +0 -1
- package/dist/esm/rendering/mesh/MeshShader.js.map +0 -1
- package/dist/esm/vendor/webgl-debug.js +0 -1160
- package/dist/esm/vendor/webgl-debug.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RenderEntryKind } from '../plan/RenderCommand.js';
|
|
2
2
|
import { Shader } from '../shader/Shader.js';
|
|
3
3
|
import { RenderTexture } from '../texture/RenderTexture.js';
|
|
4
4
|
import { Texture } from '../texture/Texture.js';
|
|
5
|
+
import { BlendModes, BufferTypes, BufferUsage, RenderingPrimitives } from '../types.js';
|
|
5
6
|
import { AbstractWebGl2Renderer } from './AbstractWebGl2Renderer.js';
|
|
6
7
|
import fragmentSource from './glsl/mesh.frag.js';
|
|
7
8
|
import vertexSource from './glsl/mesh.vert.js';
|
|
@@ -17,22 +18,29 @@ const vertexStrideBytes = 20;
|
|
|
17
18
|
const vertexStrideWords = vertexStrideBytes / 4;
|
|
18
19
|
const initialVertexCapacity = 64;
|
|
19
20
|
const initialIndexCapacity = 192;
|
|
21
|
+
const initialNodeIndexCapacity = 64;
|
|
20
22
|
const defaultVertexColor = 0xffffffff; // white, full alpha
|
|
21
23
|
const maxCustomTextureSlots = 8;
|
|
24
|
+
const transformTextureUnit = 8;
|
|
22
25
|
class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
23
26
|
_defaultShader = new Shader(vertexSource, fragmentSource);
|
|
24
27
|
_customShaders = new Map();
|
|
25
|
-
|
|
28
|
+
_compatibilityCache = new Map();
|
|
26
29
|
_textureUnitScratch = new Int32Array([0]);
|
|
30
|
+
_transformUnitScratch = new Int32Array([transformTextureUnit]);
|
|
27
31
|
// Pre-built texture-unit indices used for custom-shader sampler bindings;
|
|
28
32
|
// pre-allocated so the per-frame uniform path stays allocation-free.
|
|
29
|
-
_slotScratches = Array.from({ length: maxCustomTextureSlots }, (_, i) => new Int32Array([i]));
|
|
33
|
+
_slotScratches = Array.from({ length: Math.max(transformTextureUnit + 1, maxCustomTextureSlots + 1) }, (_, i) => new Int32Array([i]));
|
|
30
34
|
_vertexCapacity = initialVertexCapacity;
|
|
31
35
|
_indexCapacity = initialIndexCapacity;
|
|
36
|
+
_nodeIndexCapacity = initialNodeIndexCapacity;
|
|
32
37
|
_vertexData = new ArrayBuffer(initialVertexCapacity * vertexStrideBytes);
|
|
33
38
|
_float32View = new Float32Array(this._vertexData);
|
|
34
39
|
_uint32View = new Uint32Array(this._vertexData);
|
|
35
40
|
_indexData = new Uint16Array(initialIndexCapacity);
|
|
41
|
+
_nodeIndexData = new Uint32Array(initialNodeIndexCapacity);
|
|
42
|
+
_pendingDraws = [];
|
|
43
|
+
_staticGeometryCache = new Map();
|
|
36
44
|
_connection = null;
|
|
37
45
|
_currentBlendMode = null;
|
|
38
46
|
render(mesh) {
|
|
@@ -40,85 +48,52 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
40
48
|
if (!connection) {
|
|
41
49
|
throw new Error('WebGl2MeshRenderer is not connected to a backend.');
|
|
42
50
|
}
|
|
43
|
-
|
|
44
|
-
if (vertexCount === 0) {
|
|
51
|
+
if (mesh.vertexCount === 0) {
|
|
45
52
|
return;
|
|
46
53
|
}
|
|
47
54
|
const backend = this.getBackend();
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// without an explicit texture get the engine's 1×1 white default. Only
|
|
74
|
-
// bind+set if the active shader actually consumes u_texture.
|
|
75
|
-
if (shader.uniforms.has('u_texture')) {
|
|
76
|
-
const meshTexture = mesh.texture ?? Texture.white;
|
|
77
|
-
shader.getUniform('u_texture').setValue(this._textureUnitScratch);
|
|
78
|
-
backend.bindTexture(meshTexture, 0);
|
|
79
|
-
}
|
|
80
|
-
if (mesh.shader?.uniforms !== undefined) {
|
|
81
|
-
this._bindCustomUniforms(shader, mesh.shader.uniforms, backend);
|
|
82
|
-
}
|
|
83
|
-
this._ensureVertexCapacity(vertexCount);
|
|
84
|
-
const positions = mesh.vertices;
|
|
85
|
-
const uvs = mesh.uvs;
|
|
86
|
-
const colors = mesh.colors;
|
|
87
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
88
|
-
const word = i * vertexStrideWords;
|
|
89
|
-
const pair = i * 2;
|
|
90
|
-
this._float32View[word] = positions[pair];
|
|
91
|
-
this._float32View[word + 1] = positions[pair + 1];
|
|
92
|
-
if (uvs !== null) {
|
|
93
|
-
this._float32View[word + 2] = uvs[pair];
|
|
94
|
-
this._float32View[word + 3] = uvs[pair + 1];
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
this._float32View[word + 2] = 0;
|
|
98
|
-
this._float32View[word + 3] = 0;
|
|
99
|
-
}
|
|
100
|
-
this._uint32View[word + 4] = colors !== null ? colors[i] : defaultVertexColor;
|
|
101
|
-
}
|
|
102
|
-
const indexCount = mesh.indexCount;
|
|
103
|
-
this._ensureIndexCapacity(indexCount);
|
|
104
|
-
if (mesh.indices !== null) {
|
|
105
|
-
this._indexData.set(mesh.indices, 0);
|
|
55
|
+
const material = mesh.material;
|
|
56
|
+
const shader = material === null ? this._defaultShader : this._getOrCreateCustomShader(material, connection.gl);
|
|
57
|
+
// The material owns its blend mode; the mesh's own blendMode overrides it
|
|
58
|
+
// when set away from the default (Normal). Default-path meshes keep their
|
|
59
|
+
// own blendMode verbatim.
|
|
60
|
+
const blendMode = material !== null && mesh.blendMode === BlendModes.Normal ? material.blendMode : mesh.blendMode;
|
|
61
|
+
const texture = mesh.texture ?? Texture.white;
|
|
62
|
+
const command = backend.activeDrawCommand;
|
|
63
|
+
const supportsInstancing = material === null ? true : this._isInstancingCompatible(shader);
|
|
64
|
+
this._pendingDraws.push({
|
|
65
|
+
mesh,
|
|
66
|
+
command,
|
|
67
|
+
material,
|
|
68
|
+
shader,
|
|
69
|
+
blendMode,
|
|
70
|
+
texture,
|
|
71
|
+
supportsInstancing,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
flush() {
|
|
75
|
+
const backend = this.getBackendOrNull();
|
|
76
|
+
const connection = this._connection;
|
|
77
|
+
if (backend === null || connection === null || this._pendingDraws.length === 0) {
|
|
78
|
+
this._pendingDraws.length = 0;
|
|
79
|
+
return;
|
|
106
80
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
81
|
+
for (let i = 0; i < this._pendingDraws.length; i++) {
|
|
82
|
+
const draw = this._pendingDraws[i];
|
|
83
|
+
if (this._canBatchStatic(draw)) {
|
|
84
|
+
let end = i + 1;
|
|
85
|
+
while (end < this._pendingDraws.length && this._isSameBatch(this._pendingDraws[end - 1], this._pendingDraws[end])) {
|
|
86
|
+
end++;
|
|
87
|
+
}
|
|
88
|
+
if (end - i >= 2) {
|
|
89
|
+
this._drawStaticBatch(this._pendingDraws.slice(i, end), backend, connection);
|
|
90
|
+
i = end - 1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
110
93
|
}
|
|
94
|
+
this._drawSingle(draw, backend, connection);
|
|
111
95
|
}
|
|
112
|
-
|
|
113
|
-
backend.bindVertexArrayObject(connection.vao);
|
|
114
|
-
connection.vertexBuffer.upload(this._float32View.subarray(0, vertexCount * vertexStrideWords));
|
|
115
|
-
connection.indexBuffer.upload(this._indexData.subarray(0, indexCount));
|
|
116
|
-
connection.vao.draw(indexCount, 0, RenderingPrimitives.Triangles);
|
|
117
|
-
backend.stats.batches++;
|
|
118
|
-
backend.stats.drawCalls++;
|
|
119
|
-
}
|
|
120
|
-
flush() {
|
|
121
|
-
// Mesh draws are immediate per-mesh; nothing to batch.
|
|
96
|
+
this._pendingDraws.length = 0;
|
|
122
97
|
}
|
|
123
98
|
destroy() {
|
|
124
99
|
this.disconnect();
|
|
@@ -127,34 +102,40 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
127
102
|
shader.destroy();
|
|
128
103
|
}
|
|
129
104
|
this._customShaders.clear();
|
|
130
|
-
this.
|
|
105
|
+
this._compatibilityCache.clear();
|
|
131
106
|
}
|
|
132
107
|
onConnect(backend) {
|
|
133
108
|
const gl = backend.context;
|
|
134
|
-
const vaoHandle = gl.createVertexArray();
|
|
135
|
-
if (vaoHandle === null) {
|
|
136
|
-
throw new Error('Could not create vertex array object.');
|
|
137
|
-
}
|
|
138
109
|
this._defaultShader.connect(createWebGl2ShaderProgram(gl));
|
|
139
|
-
const buffers = new Map();
|
|
140
|
-
const indexBuffer = new WebGl2RenderBuffer(BufferTypes.ElementArrayBuffer, this._indexData, BufferUsage.DynamicDraw).connect(this._createBufferRuntime(gl, buffers));
|
|
141
|
-
const vertexBuffer = new WebGl2RenderBuffer(BufferTypes.ArrayBuffer, this._vertexData, BufferUsage.DynamicDraw).connect(this._createBufferRuntime(gl, buffers));
|
|
142
|
-
// Force the shader's first finalize so attribute locations are
|
|
143
|
-
// available immediately (the async-compile path defers extraction
|
|
144
|
-
// to first sync(), but we need attributes here for VAO setup).
|
|
145
110
|
this._defaultShader.sync();
|
|
146
111
|
// Custom shaders compiled before connect() get connected here too.
|
|
147
112
|
for (const customShader of this._customShaders.values()) {
|
|
148
113
|
customShader.connect(createWebGl2ShaderProgram(gl));
|
|
149
114
|
customShader.sync();
|
|
150
115
|
}
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
116
|
+
const buffers = new Map();
|
|
117
|
+
const dynamicIndexBuffer = new WebGl2RenderBuffer(BufferTypes.ElementArrayBuffer, this._indexData, BufferUsage.DynamicDraw).connect(this._createBufferRuntime(gl, buffers));
|
|
118
|
+
const dynamicVertexBuffer = new WebGl2RenderBuffer(BufferTypes.ArrayBuffer, this._vertexData, BufferUsage.DynamicDraw).connect(this._createBufferRuntime(gl, buffers));
|
|
119
|
+
const dynamicNodeIndexBuffer = new WebGl2RenderBuffer(BufferTypes.ArrayBuffer, this._nodeIndexData, BufferUsage.DynamicDraw).connect(this._createBufferRuntime(gl, buffers));
|
|
120
|
+
const dynamicVaoHandle = gl.createVertexArray();
|
|
121
|
+
if (dynamicVaoHandle === null) {
|
|
122
|
+
throw new Error('Could not create vertex array object.');
|
|
123
|
+
}
|
|
124
|
+
const dynamicVao = new WebGl2VertexArrayObject()
|
|
125
|
+
.addIndex(dynamicIndexBuffer)
|
|
126
|
+
.addAttribute(dynamicVertexBuffer, this._defaultShader.getAttribute('a_position'), gl.FLOAT, false, vertexStrideBytes, 0)
|
|
127
|
+
.addAttribute(dynamicVertexBuffer, this._defaultShader.getAttribute('a_texcoord'), gl.FLOAT, false, vertexStrideBytes, 8)
|
|
128
|
+
.addAttribute(dynamicVertexBuffer, this._defaultShader.getAttribute('a_color'), gl.UNSIGNED_BYTE, true, vertexStrideBytes, 16)
|
|
129
|
+
.addAttribute(dynamicNodeIndexBuffer, this._defaultShader.getAttribute('a_nodeIndex'), gl.UNSIGNED_INT, false, Uint32Array.BYTES_PER_ELEMENT, 0, true, 1)
|
|
130
|
+
.connect(this._createVaoRuntime(gl, dynamicVaoHandle));
|
|
131
|
+
this._connection = {
|
|
132
|
+
gl,
|
|
133
|
+
buffers,
|
|
134
|
+
dynamicVao,
|
|
135
|
+
dynamicVertexBuffer,
|
|
136
|
+
dynamicIndexBuffer,
|
|
137
|
+
dynamicNodeIndexBuffer,
|
|
138
|
+
};
|
|
158
139
|
}
|
|
159
140
|
onDisconnect() {
|
|
160
141
|
const connection = this._connection;
|
|
@@ -165,11 +146,262 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
165
146
|
for (const customShader of this._customShaders.values()) {
|
|
166
147
|
customShader.disconnect();
|
|
167
148
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
149
|
+
for (const entry of this._staticGeometryCache.values()) {
|
|
150
|
+
for (const vao of entry.vaos.values()) {
|
|
151
|
+
vao.destroy();
|
|
152
|
+
}
|
|
153
|
+
entry.vaos.clear();
|
|
154
|
+
entry.indexBuffer.destroy();
|
|
155
|
+
entry.vertexBuffer.destroy();
|
|
156
|
+
}
|
|
157
|
+
this._staticGeometryCache.clear();
|
|
158
|
+
connection.dynamicNodeIndexBuffer.destroy();
|
|
159
|
+
connection.dynamicIndexBuffer.destroy();
|
|
160
|
+
connection.dynamicVertexBuffer.destroy();
|
|
161
|
+
connection.dynamicVao.destroy();
|
|
171
162
|
this._connection = null;
|
|
172
163
|
this._currentBlendMode = null;
|
|
164
|
+
this._pendingDraws.length = 0;
|
|
165
|
+
}
|
|
166
|
+
_drawSingle(draw, backend, connection) {
|
|
167
|
+
if (this._canBatchStatic(draw)) {
|
|
168
|
+
this._drawStaticBatch([draw], backend, connection);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (draw.supportsInstancing && draw.material === null) {
|
|
172
|
+
this._drawDynamicInstancedSingle(draw, backend, connection);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this._drawLegacyImmediate(draw, backend, connection);
|
|
176
|
+
}
|
|
177
|
+
_drawDynamicInstancedSingle(draw, backend, connection) {
|
|
178
|
+
this._setBlendMode(draw.blendMode, backend);
|
|
179
|
+
this._bindInstancedShaderState(draw.shader, draw.texture, draw.material, backend, draw.command?.nodeIndex ?? 0);
|
|
180
|
+
this._ensureVertexCapacity(draw.mesh.vertexCount);
|
|
181
|
+
this._ensureIndexCapacity(draw.mesh.indexCount);
|
|
182
|
+
this._ensureNodeIndexCapacity(1);
|
|
183
|
+
this._packVertices(draw.mesh, 0);
|
|
184
|
+
this._packIndices(draw.mesh, 0);
|
|
185
|
+
const nodeIndex = draw.command?.nodeIndex ?? 0;
|
|
186
|
+
if (draw.command === null) {
|
|
187
|
+
backend._prepareDrawCommand({
|
|
188
|
+
...this._createSyntheticCommand(draw.mesh, nodeIndex),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
this._nodeIndexData[0] = nodeIndex >>> 0;
|
|
192
|
+
backend.bindVertexArrayObject(connection.dynamicVao);
|
|
193
|
+
connection.dynamicVertexBuffer.upload(this._float32View.subarray(0, draw.mesh.vertexCount * vertexStrideWords));
|
|
194
|
+
connection.dynamicIndexBuffer.upload(this._indexData.subarray(0, draw.mesh.indexCount));
|
|
195
|
+
connection.dynamicNodeIndexBuffer.upload(this._nodeIndexData.subarray(0, 1));
|
|
196
|
+
connection.dynamicVao.drawInstanced(draw.mesh.indexCount, 0, 1, RenderingPrimitives.Triangles);
|
|
197
|
+
backend.stats.batches++;
|
|
198
|
+
backend.stats.drawCalls++;
|
|
199
|
+
}
|
|
200
|
+
_drawStaticBatch(batch, backend, connection) {
|
|
201
|
+
const first = batch[0];
|
|
202
|
+
const geometry = first.mesh.geometry;
|
|
203
|
+
const cacheEntry = this._getOrCreateStaticGeometryEntry(geometry, first.mesh, connection);
|
|
204
|
+
const vao = this._getOrCreateStaticGeometryVao(cacheEntry, first.shader, connection.gl, connection.dynamicNodeIndexBuffer);
|
|
205
|
+
this._setBlendMode(first.blendMode, backend);
|
|
206
|
+
let maxNodeIndex = 0;
|
|
207
|
+
this._ensureNodeIndexCapacity(batch.length);
|
|
208
|
+
for (let i = 0; i < batch.length; i++) {
|
|
209
|
+
const command = batch[i].command;
|
|
210
|
+
const nodeIndex = command.nodeIndex >>> 0;
|
|
211
|
+
this._nodeIndexData[i] = nodeIndex;
|
|
212
|
+
if (nodeIndex > maxNodeIndex) {
|
|
213
|
+
maxNodeIndex = nodeIndex;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
this._bindInstancedShaderState(first.shader, first.texture, first.material, backend, maxNodeIndex);
|
|
217
|
+
backend.bindVertexArrayObject(vao);
|
|
218
|
+
connection.dynamicNodeIndexBuffer.upload(this._nodeIndexData.subarray(0, batch.length));
|
|
219
|
+
vao.drawInstanced(cacheEntry.indexCount, 0, batch.length, RenderingPrimitives.Triangles);
|
|
220
|
+
backend.stats.batches++;
|
|
221
|
+
backend.stats.drawCalls++;
|
|
222
|
+
}
|
|
223
|
+
_drawLegacyImmediate(draw, backend, connection) {
|
|
224
|
+
const mesh = draw.mesh;
|
|
225
|
+
const shader = draw.shader;
|
|
226
|
+
this._setBlendMode(draw.blendMode, backend);
|
|
227
|
+
if (shader.uniforms.has('u_projection')) {
|
|
228
|
+
shader.getUniform('u_projection').setValue(backend.view.getTransform().toArray(false));
|
|
229
|
+
}
|
|
230
|
+
if (shader.uniforms.has('u_translation')) {
|
|
231
|
+
shader.getUniform('u_translation').setValue(mesh.getGlobalTransform().toArray(false));
|
|
232
|
+
}
|
|
233
|
+
if (shader.uniforms.has('u_tint')) {
|
|
234
|
+
shader.getUniform('u_tint').setValue(mesh.tint.toArray(false));
|
|
235
|
+
}
|
|
236
|
+
if (shader.uniforms.has('u_texture')) {
|
|
237
|
+
shader.getUniform('u_texture').setValue(this._textureUnitScratch);
|
|
238
|
+
backend.bindTexture(draw.texture, 0);
|
|
239
|
+
}
|
|
240
|
+
if (draw.material !== null) {
|
|
241
|
+
this._bindCustomUniforms(shader, draw.material, backend);
|
|
242
|
+
}
|
|
243
|
+
this._ensureVertexCapacity(mesh.vertexCount);
|
|
244
|
+
this._ensureIndexCapacity(mesh.indexCount);
|
|
245
|
+
this._packVertices(mesh, 0);
|
|
246
|
+
this._packIndices(mesh, 0);
|
|
247
|
+
shader.sync();
|
|
248
|
+
backend.bindVertexArrayObject(connection.dynamicVao);
|
|
249
|
+
connection.dynamicVertexBuffer.upload(this._float32View.subarray(0, mesh.vertexCount * vertexStrideWords));
|
|
250
|
+
connection.dynamicIndexBuffer.upload(this._indexData.subarray(0, mesh.indexCount));
|
|
251
|
+
connection.dynamicVao.draw(mesh.indexCount, 0, RenderingPrimitives.Triangles);
|
|
252
|
+
backend.stats.batches++;
|
|
253
|
+
backend.stats.drawCalls++;
|
|
254
|
+
}
|
|
255
|
+
_bindInstancedShaderState(shader, texture, material, backend, maxNodeIndex) {
|
|
256
|
+
if (shader.uniforms.has('u_projection')) {
|
|
257
|
+
shader.getUniform('u_projection').setValue(backend.view.getTransform().toArray(false));
|
|
258
|
+
}
|
|
259
|
+
if (shader.uniforms.has('u_transforms')) {
|
|
260
|
+
backend.bindTransformBufferTexture(transformTextureUnit, maxNodeIndex + 1);
|
|
261
|
+
shader.getUniform('u_transforms').setValue(this._transformUnitScratch);
|
|
262
|
+
}
|
|
263
|
+
if (shader.uniforms.has('u_texture')) {
|
|
264
|
+
shader.getUniform('u_texture').setValue(this._textureUnitScratch);
|
|
265
|
+
backend.bindTexture(texture, 0);
|
|
266
|
+
}
|
|
267
|
+
if (material !== null) {
|
|
268
|
+
this._bindCustomUniforms(shader, material, backend);
|
|
269
|
+
}
|
|
270
|
+
shader.sync();
|
|
271
|
+
}
|
|
272
|
+
_canBatchStatic(draw) {
|
|
273
|
+
if (!draw.supportsInstancing) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
if (draw.command?.groupIndex === undefined) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
const geometry = draw.mesh.geometry;
|
|
280
|
+
if (geometry?.usage !== 'static') {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
_isSameBatch(left, right) {
|
|
286
|
+
if (!this._canBatchStatic(left) || !this._canBatchStatic(right)) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
return (left.command.groupIndex === right.command.groupIndex &&
|
|
290
|
+
left.mesh.geometry === right.mesh.geometry &&
|
|
291
|
+
left.shader === right.shader &&
|
|
292
|
+
left.material === right.material &&
|
|
293
|
+
left.blendMode === right.blendMode &&
|
|
294
|
+
left.texture === right.texture &&
|
|
295
|
+
left.command.material.pipelineKey === right.command.material.pipelineKey &&
|
|
296
|
+
left.command.material.bindKey === right.command.material.bindKey);
|
|
297
|
+
}
|
|
298
|
+
_isInstancingCompatible(shader) {
|
|
299
|
+
const cached = this._compatibilityCache.get(shader);
|
|
300
|
+
if (cached !== undefined) {
|
|
301
|
+
return cached;
|
|
302
|
+
}
|
|
303
|
+
const compatible = shader.attributes.has('a_nodeIndex') && shader.uniforms.has('u_transforms') && !shader.uniforms.has('u_translation') && !shader.uniforms.has('u_tint');
|
|
304
|
+
this._compatibilityCache.set(shader, compatible);
|
|
305
|
+
return compatible;
|
|
306
|
+
}
|
|
307
|
+
_getOrCreateStaticGeometryEntry(geometry, mesh, connection) {
|
|
308
|
+
const existing = this._staticGeometryCache.get(geometry);
|
|
309
|
+
if (existing !== undefined) {
|
|
310
|
+
return existing;
|
|
311
|
+
}
|
|
312
|
+
const vertexCount = mesh.vertexCount;
|
|
313
|
+
const indexCount = mesh.indexCount;
|
|
314
|
+
const interleaved = new ArrayBuffer(vertexCount * vertexStrideBytes);
|
|
315
|
+
const floatView = new Float32Array(interleaved);
|
|
316
|
+
const uintView = new Uint32Array(interleaved);
|
|
317
|
+
this._packVertices(mesh, 0, floatView, uintView);
|
|
318
|
+
const indexData = new Uint16Array(indexCount);
|
|
319
|
+
this._packIndices(mesh, 0, indexData);
|
|
320
|
+
const vertexBuffer = new WebGl2RenderBuffer(BufferTypes.ArrayBuffer, floatView, BufferUsage.StaticDraw).connect(this._createBufferRuntime(connection.gl, connection.buffers));
|
|
321
|
+
const indexBuffer = new WebGl2RenderBuffer(BufferTypes.ElementArrayBuffer, indexData, BufferUsage.StaticDraw).connect(this._createBufferRuntime(connection.gl, connection.buffers));
|
|
322
|
+
vertexBuffer.upload(floatView);
|
|
323
|
+
indexBuffer.upload(indexData);
|
|
324
|
+
const disposeListener = () => {
|
|
325
|
+
const entry = this._staticGeometryCache.get(geometry);
|
|
326
|
+
if (entry === undefined) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
for (const vao of entry.vaos.values()) {
|
|
330
|
+
vao.destroy();
|
|
331
|
+
}
|
|
332
|
+
entry.vaos.clear();
|
|
333
|
+
entry.indexBuffer.destroy();
|
|
334
|
+
entry.vertexBuffer.destroy();
|
|
335
|
+
this._staticGeometryCache.delete(geometry);
|
|
336
|
+
};
|
|
337
|
+
geometry._onDispose(disposeListener);
|
|
338
|
+
const created = {
|
|
339
|
+
geometry,
|
|
340
|
+
vertexBuffer,
|
|
341
|
+
indexBuffer,
|
|
342
|
+
vaos: new Map(),
|
|
343
|
+
disposeListener,
|
|
344
|
+
indexCount,
|
|
345
|
+
};
|
|
346
|
+
this._staticGeometryCache.set(geometry, created);
|
|
347
|
+
return created;
|
|
348
|
+
}
|
|
349
|
+
_getOrCreateStaticGeometryVao(entry, shader, gl, nodeIndexBuffer) {
|
|
350
|
+
const existing = entry.vaos.get(shader);
|
|
351
|
+
if (existing !== undefined) {
|
|
352
|
+
return existing;
|
|
353
|
+
}
|
|
354
|
+
const vaoHandle = gl.createVertexArray();
|
|
355
|
+
if (vaoHandle === null) {
|
|
356
|
+
throw new Error('Could not create vertex array object.');
|
|
357
|
+
}
|
|
358
|
+
const nodeAttribute = shader.getAttribute('a_nodeIndex');
|
|
359
|
+
const vao = new WebGl2VertexArrayObject()
|
|
360
|
+
.addIndex(entry.indexBuffer)
|
|
361
|
+
.addAttribute(entry.vertexBuffer, shader.getAttribute('a_position'), gl.FLOAT, false, vertexStrideBytes, 0)
|
|
362
|
+
.addAttribute(entry.vertexBuffer, shader.getAttribute('a_texcoord'), gl.FLOAT, false, vertexStrideBytes, 8)
|
|
363
|
+
.addAttribute(entry.vertexBuffer, shader.getAttribute('a_color'), gl.UNSIGNED_BYTE, true, vertexStrideBytes, 16)
|
|
364
|
+
.addAttribute(nodeIndexBuffer, nodeAttribute, gl.UNSIGNED_INT, false, Uint32Array.BYTES_PER_ELEMENT, 0, true, 1)
|
|
365
|
+
.connect(this._createVaoRuntime(gl, vaoHandle));
|
|
366
|
+
entry.vaos.set(shader, vao);
|
|
367
|
+
return vao;
|
|
368
|
+
}
|
|
369
|
+
_setBlendMode(next, backend) {
|
|
370
|
+
if (this._currentBlendMode !== next) {
|
|
371
|
+
this._currentBlendMode = next;
|
|
372
|
+
backend.setBlendMode(next);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
_packVertices(mesh, vertexStart, floatView = this._float32View, uintView = this._uint32View) {
|
|
376
|
+
const positions = mesh.vertices;
|
|
377
|
+
const uvs = mesh.uvs;
|
|
378
|
+
const colors = mesh.colors;
|
|
379
|
+
const vertexCount = mesh.vertexCount;
|
|
380
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
381
|
+
const word = (vertexStart + i) * vertexStrideWords;
|
|
382
|
+
const pair = i * 2;
|
|
383
|
+
floatView[word] = positions[pair];
|
|
384
|
+
floatView[word + 1] = positions[pair + 1];
|
|
385
|
+
if (uvs !== null) {
|
|
386
|
+
floatView[word + 2] = uvs[pair];
|
|
387
|
+
floatView[word + 3] = uvs[pair + 1];
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
floatView[word + 2] = 0;
|
|
391
|
+
floatView[word + 3] = 0;
|
|
392
|
+
}
|
|
393
|
+
uintView[word + 4] = colors !== null ? colors[i] : defaultVertexColor;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
_packIndices(mesh, indexStart, target = this._indexData) {
|
|
397
|
+
const indexCount = mesh.indexCount;
|
|
398
|
+
if (mesh.indices !== null) {
|
|
399
|
+
target.set(mesh.indices, indexStart);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
for (let i = 0; i < indexCount; i++) {
|
|
403
|
+
target[indexStart + i] = i;
|
|
404
|
+
}
|
|
173
405
|
}
|
|
174
406
|
_ensureVertexCapacity(vertexCount) {
|
|
175
407
|
if (vertexCount <= this._vertexCapacity) {
|
|
@@ -191,6 +423,15 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
191
423
|
}
|
|
192
424
|
this._indexData = new Uint16Array(this._indexCapacity);
|
|
193
425
|
}
|
|
426
|
+
_ensureNodeIndexCapacity(instanceCount) {
|
|
427
|
+
if (instanceCount <= this._nodeIndexCapacity) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
while (this._nodeIndexCapacity < instanceCount) {
|
|
431
|
+
this._nodeIndexCapacity *= 2;
|
|
432
|
+
}
|
|
433
|
+
this._nodeIndexData = new Uint32Array(this._nodeIndexCapacity);
|
|
434
|
+
}
|
|
194
435
|
_createBufferRuntime(gl, buffers) {
|
|
195
436
|
const handle = gl.createBuffer();
|
|
196
437
|
if (handle === null) {
|
|
@@ -232,8 +473,14 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
232
473
|
attribute.buffer.bind();
|
|
233
474
|
lastBuffer = attribute.buffer;
|
|
234
475
|
}
|
|
235
|
-
|
|
476
|
+
if (attribute.integer) {
|
|
477
|
+
gl.vertexAttribIPointer(attribute.location, attribute.size, attribute.type, attribute.stride, attribute.start);
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
gl.vertexAttribPointer(attribute.location, attribute.size, attribute.type, attribute.normalized, attribute.stride, attribute.start);
|
|
481
|
+
}
|
|
236
482
|
gl.enableVertexAttribArray(attribute.location);
|
|
483
|
+
gl.vertexAttribDivisor(attribute.location, attribute.divisor);
|
|
237
484
|
}
|
|
238
485
|
if (vao.indexBuffer) {
|
|
239
486
|
vao.indexBuffer.bind();
|
|
@@ -252,39 +499,50 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
252
499
|
gl.drawArrays(type, start, size);
|
|
253
500
|
}
|
|
254
501
|
},
|
|
502
|
+
drawInstanced: (vao, count, start, instanceCount, type) => {
|
|
503
|
+
if (vao.indexBuffer) {
|
|
504
|
+
gl.drawElementsInstanced(type, count, gl.UNSIGNED_SHORT, start, instanceCount);
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
gl.drawArraysInstanced(type, start, count, instanceCount);
|
|
508
|
+
}
|
|
509
|
+
},
|
|
255
510
|
destroy: (vao) => {
|
|
256
511
|
gl.deleteVertexArray(vaoHandle);
|
|
257
512
|
vao.disconnect();
|
|
258
513
|
},
|
|
259
514
|
};
|
|
260
515
|
}
|
|
261
|
-
_getOrCreateCustomShader(
|
|
262
|
-
const cached = this._customShaders.get(
|
|
516
|
+
_getOrCreateCustomShader(material, gl) {
|
|
517
|
+
const cached = this._customShaders.get(material);
|
|
263
518
|
if (cached !== undefined) {
|
|
264
519
|
return cached;
|
|
265
520
|
}
|
|
266
|
-
|
|
267
|
-
|
|
521
|
+
const glsl = material.shader.glsl;
|
|
522
|
+
if (glsl === null) {
|
|
523
|
+
throw new Error('Mesh material shader has no `glsl` source; cannot render through the WebGL2 backend.');
|
|
268
524
|
}
|
|
269
|
-
const shader = new Shader(
|
|
525
|
+
const shader = new Shader(glsl.vertex, glsl.fragment);
|
|
270
526
|
shader.connect(createWebGl2ShaderProgram(gl));
|
|
271
527
|
// Force first finalize so getUniform()/uniforms.has() are usable below.
|
|
272
528
|
shader.sync();
|
|
273
|
-
this._customShaders.set(
|
|
274
|
-
// Wire
|
|
275
|
-
|
|
276
|
-
const stored = this._customShaders.get(
|
|
529
|
+
this._customShaders.set(material, shader);
|
|
530
|
+
// Wire material.destroy() through to evict + dispose the cached program.
|
|
531
|
+
material._onDispose(() => {
|
|
532
|
+
const stored = this._customShaders.get(material);
|
|
277
533
|
if (stored !== undefined) {
|
|
278
534
|
stored.destroy();
|
|
279
|
-
this._customShaders.delete(
|
|
535
|
+
this._customShaders.delete(material);
|
|
280
536
|
}
|
|
281
537
|
});
|
|
282
538
|
return shader;
|
|
283
539
|
}
|
|
284
|
-
_bindCustomUniforms(shader,
|
|
285
|
-
// Texture
|
|
286
|
-
//
|
|
540
|
+
_bindCustomUniforms(shader, material, backend) {
|
|
541
|
+
// Texture bindings take consecutive slots starting at 1 (slot 0 belongs to
|
|
542
|
+
// the mesh's own `u_texture`). Texture-valued uniforms bind first, then the
|
|
543
|
+
// entries of the material's dedicated `textures` map.
|
|
287
544
|
let textureSlot = 1;
|
|
545
|
+
const uniforms = material.uniforms;
|
|
288
546
|
for (const name in uniforms) {
|
|
289
547
|
if (!shader.uniforms.has(name)) {
|
|
290
548
|
continue;
|
|
@@ -293,7 +551,7 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
293
551
|
const uniform = shader.getUniform(name);
|
|
294
552
|
if (value instanceof Texture || value instanceof RenderTexture) {
|
|
295
553
|
if (textureSlot >= maxCustomTextureSlots) {
|
|
296
|
-
throw new Error(`Mesh
|
|
554
|
+
throw new Error(`Mesh material requested more than ${maxCustomTextureSlots - 1} texture bindings.`);
|
|
297
555
|
}
|
|
298
556
|
backend.bindTexture(value, textureSlot);
|
|
299
557
|
uniform.setValue(this._slotScratches[textureSlot]);
|
|
@@ -303,6 +561,18 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
303
561
|
uniform.setValue(this._marshalUniformValue(value));
|
|
304
562
|
}
|
|
305
563
|
}
|
|
564
|
+
const textures = material.textures;
|
|
565
|
+
for (const name in textures) {
|
|
566
|
+
if (!shader.uniforms.has(name)) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (textureSlot >= maxCustomTextureSlots) {
|
|
570
|
+
throw new Error(`Mesh material requested more than ${maxCustomTextureSlots - 1} texture bindings.`);
|
|
571
|
+
}
|
|
572
|
+
backend.bindTexture(textures[name], textureSlot);
|
|
573
|
+
shader.getUniform(name).setValue(this._slotScratches[textureSlot]);
|
|
574
|
+
textureSlot++;
|
|
575
|
+
}
|
|
306
576
|
}
|
|
307
577
|
_marshalUniformValue(value) {
|
|
308
578
|
if (value instanceof Float32Array || value instanceof Int32Array) {
|
|
@@ -314,6 +584,28 @@ class WebGl2MeshRenderer extends AbstractWebGl2Renderer {
|
|
|
314
584
|
// readonly tuple [a, b], [a, b, c], [a, b, c, d]
|
|
315
585
|
return new Float32Array(value);
|
|
316
586
|
}
|
|
587
|
+
_createSyntheticCommand(mesh, nodeIndex) {
|
|
588
|
+
return {
|
|
589
|
+
kind: RenderEntryKind.Draw,
|
|
590
|
+
drawable: mesh,
|
|
591
|
+
nodeIndex,
|
|
592
|
+
seq: 0,
|
|
593
|
+
zIndex: mesh.zIndex,
|
|
594
|
+
material: {
|
|
595
|
+
rendererId: 0,
|
|
596
|
+
blendMode: mesh.blendMode,
|
|
597
|
+
textureId: -1,
|
|
598
|
+
shaderId: -1,
|
|
599
|
+
pipelineKey: 0,
|
|
600
|
+
bindKey: 0,
|
|
601
|
+
},
|
|
602
|
+
minX: 0,
|
|
603
|
+
minY: 0,
|
|
604
|
+
maxX: 0,
|
|
605
|
+
maxY: 0,
|
|
606
|
+
groupIndex: 0,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
317
609
|
}
|
|
318
610
|
|
|
319
611
|
export { WebGl2MeshRenderer };
|