@multiplekex/shallot 0.1.12 → 0.2.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/package.json +3 -4
- package/src/core/builder.ts +71 -32
- package/src/core/component.ts +25 -11
- package/src/core/index.ts +14 -13
- package/src/core/math.ts +135 -0
- package/src/core/runtime.ts +0 -1
- package/src/core/state.ts +9 -68
- package/src/core/xml.ts +381 -265
- package/src/editor/format.ts +5 -0
- package/src/editor/index.ts +101 -0
- package/src/extras/arrows/index.ts +28 -69
- package/src/extras/gradient/index.ts +36 -52
- package/src/extras/lines/index.ts +51 -122
- package/src/extras/orbit/index.ts +40 -15
- package/src/extras/text/font.ts +546 -0
- package/src/extras/text/index.ts +158 -204
- package/src/extras/text/sdf.ts +429 -0
- package/src/standard/activity/index.ts +172 -0
- package/src/standard/compute/graph.ts +23 -23
- package/src/standard/compute/index.ts +76 -61
- package/src/standard/defaults.ts +8 -5
- package/src/standard/index.ts +1 -0
- package/src/standard/input/index.ts +30 -19
- package/src/standard/loading/index.ts +18 -13
- package/src/standard/render/bvh/blas.ts +752 -0
- package/src/standard/render/bvh/radix.ts +476 -0
- package/src/standard/render/bvh/structs.ts +167 -0
- package/src/standard/render/bvh/tlas.ts +886 -0
- package/src/standard/render/bvh/traverse.ts +467 -0
- package/src/standard/render/camera.ts +302 -27
- package/src/standard/render/data.ts +93 -0
- package/src/standard/render/depth.ts +117 -0
- package/src/standard/render/forward/index.ts +259 -0
- package/src/standard/render/forward/raster.ts +228 -0
- package/src/standard/render/index.ts +443 -70
- package/src/standard/render/indirect.ts +40 -0
- package/src/standard/render/instance.ts +214 -0
- package/src/standard/render/intersection.ts +72 -0
- package/src/standard/render/light.ts +16 -16
- package/src/standard/render/mesh/index.ts +67 -75
- package/src/standard/render/mesh/unified.ts +96 -0
- package/src/standard/render/{transparent.ts → overlay.ts} +14 -15
- package/src/standard/render/pass.ts +10 -4
- package/src/standard/render/postprocess.ts +142 -64
- package/src/standard/render/ray.ts +61 -0
- package/src/standard/render/scene.ts +38 -164
- package/src/standard/render/shaders.ts +484 -0
- package/src/standard/render/surface/compile.ts +3 -10
- package/src/standard/render/surface/index.ts +60 -30
- package/src/standard/render/surface/noise.ts +45 -0
- package/src/standard/render/surface/structs.ts +60 -19
- package/src/standard/render/surface/wgsl.ts +573 -0
- package/src/standard/render/triangle.ts +84 -0
- package/src/standard/transforms/index.ts +4 -6
- package/src/standard/tween/index.ts +10 -1
- package/src/standard/tween/sequence.ts +24 -16
- package/src/standard/tween/tween.ts +67 -16
- package/src/core/types.ts +0 -37
- package/src/standard/compute/inspect.ts +0 -201
- package/src/standard/compute/pass.ts +0 -23
- package/src/standard/compute/timing.ts +0 -139
- package/src/standard/render/forward.ts +0 -273
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
2
|
+
|
|
3
|
+
const WORKGROUP_SIZE = 64;
|
|
4
|
+
|
|
5
|
+
const shader = /* wgsl */ `
|
|
6
|
+
struct ShapeAABB {
|
|
7
|
+
minX: f32,
|
|
8
|
+
minY: f32,
|
|
9
|
+
minZ: f32,
|
|
10
|
+
_pad0: u32,
|
|
11
|
+
maxX: f32,
|
|
12
|
+
maxY: f32,
|
|
13
|
+
maxZ: f32,
|
|
14
|
+
_pad1: u32,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
struct InstanceAABB {
|
|
18
|
+
minX: f32,
|
|
19
|
+
minY: f32,
|
|
20
|
+
minZ: f32,
|
|
21
|
+
_pad0: u32,
|
|
22
|
+
maxX: f32,
|
|
23
|
+
maxY: f32,
|
|
24
|
+
maxZ: f32,
|
|
25
|
+
_pad1: u32,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@group(0) @binding(0) var<storage, read> matrices: array<mat4x4<f32>>;
|
|
29
|
+
@group(0) @binding(1) var<storage, read> sizes: array<vec4<f32>>;
|
|
30
|
+
@group(0) @binding(2) var<storage, read> shapes: array<u32>;
|
|
31
|
+
@group(0) @binding(3) var<storage, read> shapeAABBs: array<ShapeAABB>;
|
|
32
|
+
@group(0) @binding(4) var<storage, read> entityCount: array<u32>;
|
|
33
|
+
@group(0) @binding(5) var<storage, read_write> instanceAABBs: array<InstanceAABB>;
|
|
34
|
+
@group(0) @binding(6) var<storage, read_write> instanceInverses: array<mat4x4<f32>>;
|
|
35
|
+
|
|
36
|
+
fn scaleColumns(m: mat4x4<f32>, s: vec3<f32>) -> mat4x4<f32> {
|
|
37
|
+
return mat4x4<f32>(
|
|
38
|
+
m[0] * s.x,
|
|
39
|
+
m[1] * s.y,
|
|
40
|
+
m[2] * s.z,
|
|
41
|
+
m[3]
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn transformPoint(p: vec3<f32>, m: mat4x4<f32>) -> vec3<f32> {
|
|
46
|
+
return (m * vec4<f32>(p, 1.0)).xyz;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fn transformAABB(aabbMin: vec3<f32>, aabbMax: vec3<f32>, m: mat4x4<f32>) -> array<vec3<f32>, 2> {
|
|
50
|
+
let corners = array<vec3<f32>, 8>(
|
|
51
|
+
vec3<f32>(aabbMin.x, aabbMin.y, aabbMin.z),
|
|
52
|
+
vec3<f32>(aabbMin.x, aabbMin.y, aabbMax.z),
|
|
53
|
+
vec3<f32>(aabbMin.x, aabbMax.y, aabbMin.z),
|
|
54
|
+
vec3<f32>(aabbMin.x, aabbMax.y, aabbMax.z),
|
|
55
|
+
vec3<f32>(aabbMax.x, aabbMin.y, aabbMin.z),
|
|
56
|
+
vec3<f32>(aabbMax.x, aabbMin.y, aabbMax.z),
|
|
57
|
+
vec3<f32>(aabbMax.x, aabbMax.y, aabbMin.z),
|
|
58
|
+
vec3<f32>(aabbMax.x, aabbMax.y, aabbMax.z)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
var newMin = vec3<f32>(1e30, 1e30, 1e30);
|
|
62
|
+
var newMax = vec3<f32>(-1e30, -1e30, -1e30);
|
|
63
|
+
|
|
64
|
+
for (var i = 0u; i < 8u; i++) {
|
|
65
|
+
let t = transformPoint(corners[i], m);
|
|
66
|
+
newMin = min(newMin, t);
|
|
67
|
+
newMax = max(newMax, t);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return array<vec3<f32>, 2>(newMin, newMax);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fn inverse4x4(m: mat4x4<f32>) -> mat4x4<f32> {
|
|
74
|
+
let m00 = m[0][0]; let m10 = m[0][1]; let m20 = m[0][2]; let m30 = m[0][3];
|
|
75
|
+
let m01 = m[1][0]; let m11 = m[1][1]; let m21 = m[1][2]; let m31 = m[1][3];
|
|
76
|
+
let m02 = m[2][0]; let m12 = m[2][1]; let m22 = m[2][2]; let m32 = m[2][3];
|
|
77
|
+
let m03 = m[3][0]; let m13 = m[3][1]; let m23 = m[3][2]; let m33 = m[3][3];
|
|
78
|
+
|
|
79
|
+
let c00 = m11 * (m22 * m33 - m32 * m23) - m21 * (m12 * m33 - m32 * m13) + m31 * (m12 * m23 - m22 * m13);
|
|
80
|
+
let c01 = -(m01 * (m22 * m33 - m32 * m23) - m21 * (m02 * m33 - m32 * m03) + m31 * (m02 * m23 - m22 * m03));
|
|
81
|
+
let c02 = m01 * (m12 * m33 - m32 * m13) - m11 * (m02 * m33 - m32 * m03) + m31 * (m02 * m13 - m12 * m03);
|
|
82
|
+
let c03 = -(m01 * (m12 * m23 - m22 * m13) - m11 * (m02 * m23 - m22 * m03) + m21 * (m02 * m13 - m12 * m03));
|
|
83
|
+
|
|
84
|
+
let c10 = -(m10 * (m22 * m33 - m32 * m23) - m20 * (m12 * m33 - m32 * m13) + m30 * (m12 * m23 - m22 * m13));
|
|
85
|
+
let c11 = m00 * (m22 * m33 - m32 * m23) - m20 * (m02 * m33 - m32 * m03) + m30 * (m02 * m23 - m22 * m03);
|
|
86
|
+
let c12 = -(m00 * (m12 * m33 - m32 * m13) - m10 * (m02 * m33 - m32 * m03) + m30 * (m02 * m13 - m12 * m03));
|
|
87
|
+
let c13 = m00 * (m12 * m23 - m22 * m13) - m10 * (m02 * m23 - m22 * m03) + m20 * (m02 * m13 - m12 * m03);
|
|
88
|
+
|
|
89
|
+
let c20 = m10 * (m21 * m33 - m31 * m23) - m20 * (m11 * m33 - m31 * m13) + m30 * (m11 * m23 - m21 * m13);
|
|
90
|
+
let c21 = -(m00 * (m21 * m33 - m31 * m23) - m20 * (m01 * m33 - m31 * m03) + m30 * (m01 * m23 - m21 * m03));
|
|
91
|
+
let c22 = m00 * (m11 * m33 - m31 * m13) - m10 * (m01 * m33 - m31 * m03) + m30 * (m01 * m13 - m11 * m03);
|
|
92
|
+
let c23 = -(m00 * (m11 * m23 - m21 * m13) - m10 * (m01 * m23 - m21 * m03) + m20 * (m01 * m13 - m11 * m03));
|
|
93
|
+
|
|
94
|
+
let c30 = -(m10 * (m21 * m32 - m31 * m22) - m20 * (m11 * m32 - m31 * m12) + m30 * (m11 * m22 - m21 * m12));
|
|
95
|
+
let c31 = m00 * (m21 * m32 - m31 * m22) - m20 * (m01 * m32 - m31 * m02) + m30 * (m01 * m22 - m21 * m02);
|
|
96
|
+
let c32 = -(m00 * (m11 * m32 - m31 * m12) - m10 * (m01 * m32 - m31 * m02) + m30 * (m01 * m12 - m11 * m02));
|
|
97
|
+
let c33 = m00 * (m11 * m22 - m21 * m12) - m10 * (m01 * m22 - m21 * m02) + m20 * (m01 * m12 - m11 * m02);
|
|
98
|
+
|
|
99
|
+
let det = m00 * c00 + m01 * c10 + m02 * c20 + m03 * c30;
|
|
100
|
+
let invDet = select(0.0, 1.0 / det, abs(det) > 1e-10);
|
|
101
|
+
|
|
102
|
+
return mat4x4<f32>(
|
|
103
|
+
vec4<f32>(c00, c10, c20, c30) * invDet,
|
|
104
|
+
vec4<f32>(c01, c11, c21, c31) * invDet,
|
|
105
|
+
vec4<f32>(c02, c12, c22, c32) * invDet,
|
|
106
|
+
vec4<f32>(c03, c13, c23, c33) * invDet
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@compute @workgroup_size(${WORKGROUP_SIZE})
|
|
111
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
112
|
+
let eid = gid.x;
|
|
113
|
+
let count = entityCount[0];
|
|
114
|
+
if (eid >= count) { return; }
|
|
115
|
+
|
|
116
|
+
let shapeId = shapes[eid];
|
|
117
|
+
let shapeAABB = shapeAABBs[shapeId];
|
|
118
|
+
let matrix = matrices[eid];
|
|
119
|
+
let size = sizes[eid].xyz;
|
|
120
|
+
|
|
121
|
+
let hasZeroScale = size.x == 0.0 || size.y == 0.0 || size.z == 0.0;
|
|
122
|
+
|
|
123
|
+
if (hasZeroScale) {
|
|
124
|
+
var zeroAABB: InstanceAABB;
|
|
125
|
+
zeroAABB.minX = 0.0;
|
|
126
|
+
zeroAABB.minY = 0.0;
|
|
127
|
+
zeroAABB.minZ = 0.0;
|
|
128
|
+
zeroAABB._pad0 = 0u;
|
|
129
|
+
zeroAABB.maxX = 0.0;
|
|
130
|
+
zeroAABB.maxY = 0.0;
|
|
131
|
+
zeroAABB.maxZ = 0.0;
|
|
132
|
+
zeroAABB._pad1 = 0u;
|
|
133
|
+
instanceAABBs[eid] = zeroAABB;
|
|
134
|
+
instanceInverses[eid] = mat4x4<f32>();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let scaledMatrix = scaleColumns(matrix, size);
|
|
139
|
+
|
|
140
|
+
let aabbMin = vec3<f32>(shapeAABB.minX, shapeAABB.minY, shapeAABB.minZ);
|
|
141
|
+
let aabbMax = vec3<f32>(shapeAABB.maxX, shapeAABB.maxY, shapeAABB.maxZ);
|
|
142
|
+
let worldAABB = transformAABB(aabbMin, aabbMax, scaledMatrix);
|
|
143
|
+
|
|
144
|
+
var outAABB: InstanceAABB;
|
|
145
|
+
outAABB.minX = worldAABB[0].x;
|
|
146
|
+
outAABB.minY = worldAABB[0].y;
|
|
147
|
+
outAABB.minZ = worldAABB[0].z;
|
|
148
|
+
outAABB._pad0 = 0u;
|
|
149
|
+
outAABB.maxX = worldAABB[1].x;
|
|
150
|
+
outAABB.maxY = worldAABB[1].y;
|
|
151
|
+
outAABB.maxZ = worldAABB[1].z;
|
|
152
|
+
outAABB._pad1 = 0u;
|
|
153
|
+
instanceAABBs[eid] = outAABB;
|
|
154
|
+
|
|
155
|
+
instanceInverses[eid] = inverse4x4(scaledMatrix);
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
export interface InstanceConfig {
|
|
160
|
+
matrices: GPUBuffer;
|
|
161
|
+
sizes: GPUBuffer;
|
|
162
|
+
shapes: GPUBuffer;
|
|
163
|
+
shapeAABBs: GPUBuffer;
|
|
164
|
+
entityCount: GPUBuffer;
|
|
165
|
+
instanceAABBs: GPUBuffer;
|
|
166
|
+
instanceInverses: GPUBuffer;
|
|
167
|
+
getEntityCount: () => number;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function createInstanceNode(config: InstanceConfig): ComputeNode {
|
|
171
|
+
let pipeline: GPUComputePipeline | null = null;
|
|
172
|
+
let bindGroup: GPUBindGroup | null = null;
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
id: "instance",
|
|
176
|
+
inputs: [],
|
|
177
|
+
outputs: [
|
|
178
|
+
{ id: "instance-aabbs", access: "write" },
|
|
179
|
+
{ id: "instance-inverses", access: "write" },
|
|
180
|
+
],
|
|
181
|
+
|
|
182
|
+
async prepare(device: GPUDevice) {
|
|
183
|
+
const module = await device.createShaderModule({ code: shader });
|
|
184
|
+
|
|
185
|
+
pipeline = await device.createComputePipelineAsync({
|
|
186
|
+
layout: "auto",
|
|
187
|
+
compute: { module, entryPoint: "main" },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
bindGroup = device.createBindGroup({
|
|
191
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
192
|
+
entries: [
|
|
193
|
+
{ binding: 0, resource: { buffer: config.matrices } },
|
|
194
|
+
{ binding: 1, resource: { buffer: config.sizes } },
|
|
195
|
+
{ binding: 2, resource: { buffer: config.shapes } },
|
|
196
|
+
{ binding: 3, resource: { buffer: config.shapeAABBs } },
|
|
197
|
+
{ binding: 4, resource: { buffer: config.entityCount } },
|
|
198
|
+
{ binding: 5, resource: { buffer: config.instanceAABBs } },
|
|
199
|
+
{ binding: 6, resource: { buffer: config.instanceInverses } },
|
|
200
|
+
],
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
execute(ctx: ExecutionContext) {
|
|
205
|
+
const workgroups = Math.ceil(config.getEntityCount() / WORKGROUP_SIZE);
|
|
206
|
+
|
|
207
|
+
const pass = ctx.encoder.beginComputePass();
|
|
208
|
+
pass.setPipeline(pipeline!);
|
|
209
|
+
pass.setBindGroup(0, bindGroup!);
|
|
210
|
+
pass.dispatchWorkgroups(workgroups);
|
|
211
|
+
pass.end();
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const EPSILON = 1e-7;
|
|
2
|
+
|
|
3
|
+
export interface RayTriangleResult {
|
|
4
|
+
hit: boolean;
|
|
5
|
+
t: number;
|
|
6
|
+
u: number;
|
|
7
|
+
v: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function intersectRayTriangle(
|
|
11
|
+
originX: number,
|
|
12
|
+
originY: number,
|
|
13
|
+
originZ: number,
|
|
14
|
+
directionX: number,
|
|
15
|
+
directionY: number,
|
|
16
|
+
directionZ: number,
|
|
17
|
+
v0x: number,
|
|
18
|
+
v0y: number,
|
|
19
|
+
v0z: number,
|
|
20
|
+
v1x: number,
|
|
21
|
+
v1y: number,
|
|
22
|
+
v1z: number,
|
|
23
|
+
v2x: number,
|
|
24
|
+
v2y: number,
|
|
25
|
+
v2z: number
|
|
26
|
+
): RayTriangleResult {
|
|
27
|
+
const e1x = v1x - v0x;
|
|
28
|
+
const e1y = v1y - v0y;
|
|
29
|
+
const e1z = v1z - v0z;
|
|
30
|
+
|
|
31
|
+
const e2x = v2x - v0x;
|
|
32
|
+
const e2y = v2y - v0y;
|
|
33
|
+
const e2z = v2z - v0z;
|
|
34
|
+
|
|
35
|
+
const hx = directionY * e2z - directionZ * e2y;
|
|
36
|
+
const hy = directionZ * e2x - directionX * e2z;
|
|
37
|
+
const hz = directionX * e2y - directionY * e2x;
|
|
38
|
+
|
|
39
|
+
const a = e1x * hx + e1y * hy + e1z * hz;
|
|
40
|
+
|
|
41
|
+
if (a > -EPSILON && a < EPSILON) {
|
|
42
|
+
return { hit: false, t: 0, u: 0, v: 0 };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const f = 1 / a;
|
|
46
|
+
|
|
47
|
+
const sx = originX - v0x;
|
|
48
|
+
const sy = originY - v0y;
|
|
49
|
+
const sz = originZ - v0z;
|
|
50
|
+
|
|
51
|
+
const u = f * (sx * hx + sy * hy + sz * hz);
|
|
52
|
+
if (u < 0 || u > 1) {
|
|
53
|
+
return { hit: false, t: 0, u: 0, v: 0 };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const qx = sy * e1z - sz * e1y;
|
|
57
|
+
const qy = sz * e1x - sx * e1z;
|
|
58
|
+
const qz = sx * e1y - sy * e1x;
|
|
59
|
+
|
|
60
|
+
const v = f * (directionX * qx + directionY * qy + directionZ * qz);
|
|
61
|
+
if (v < 0 || u + v > 1) {
|
|
62
|
+
return { hit: false, t: 0, u: 0, v: 0 };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const t = f * (e2x * qx + e2y * qy + e2z * qz);
|
|
66
|
+
|
|
67
|
+
if (t > EPSILON) {
|
|
68
|
+
return { hit: true, t, u, v };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { hit: false, t: 0, u: 0, v: 0 };
|
|
72
|
+
}
|
|
@@ -21,7 +21,7 @@ export const DirectionalLight = {
|
|
|
21
21
|
setTraits(DirectionalLight, {
|
|
22
22
|
defaults: () => ({
|
|
23
23
|
color: 0xffffff,
|
|
24
|
-
intensity:
|
|
24
|
+
intensity: 0.8,
|
|
25
25
|
directionX: -0.6,
|
|
26
26
|
directionY: -1.0,
|
|
27
27
|
directionZ: -0.8,
|
|
@@ -49,33 +49,33 @@ export interface DirectionalLightData {
|
|
|
49
49
|
directionZ: number;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
const lightData = new Float32Array(12);
|
|
53
|
+
|
|
52
54
|
export function packLightUniforms(
|
|
53
55
|
ambient: AmbientLightData,
|
|
54
56
|
directional: DirectionalLightData
|
|
55
57
|
): Float32Array {
|
|
56
|
-
const data = new Float32Array(12);
|
|
57
|
-
|
|
58
58
|
const ambientRgb = unpackColor(ambient.color);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
lightData[0] = ambientRgb.r;
|
|
60
|
+
lightData[1] = ambientRgb.g;
|
|
61
|
+
lightData[2] = ambientRgb.b;
|
|
62
|
+
lightData[3] = ambient.intensity;
|
|
63
63
|
|
|
64
64
|
const [dx, dy, dz] = normalizeDirection(
|
|
65
65
|
directional.directionX,
|
|
66
66
|
directional.directionY,
|
|
67
67
|
directional.directionZ
|
|
68
68
|
);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
lightData[4] = dx;
|
|
70
|
+
lightData[5] = dy;
|
|
71
|
+
lightData[6] = dz;
|
|
72
|
+
lightData[7] = 0;
|
|
73
73
|
|
|
74
74
|
const sunRgb = unpackColor(directional.color);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
lightData[8] = sunRgb.r * directional.intensity;
|
|
76
|
+
lightData[9] = sunRgb.g * directional.intensity;
|
|
77
|
+
lightData[10] = sunRgb.b * directional.intensity;
|
|
78
|
+
lightData[11] = 0;
|
|
79
79
|
|
|
80
|
-
return
|
|
80
|
+
return lightData;
|
|
81
81
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { MAX_ENTITIES } from "../../../core";
|
|
2
2
|
import { setTraits, type FieldAccessor } from "../../../core/component";
|
|
3
3
|
import { createEntityIdBuffer } from "../../compute";
|
|
4
|
-
import { writeIndirect } from "../
|
|
4
|
+
import { writeIndirect } from "../indirect";
|
|
5
5
|
import { createBox } from "./box";
|
|
6
6
|
import { createSphere } from "./sphere";
|
|
7
7
|
import { createPlane } from "./plane";
|
|
8
8
|
|
|
9
9
|
export const MAX_SURFACES = 16;
|
|
10
10
|
export const MAX_BATCH_SLOTS = 64;
|
|
11
|
-
|
|
11
|
+
const INVALID_SHAPE = 0xffffffff;
|
|
12
|
+
|
|
13
|
+
const batchEntityIds = new Uint32Array(MAX_ENTITIES);
|
|
12
14
|
|
|
13
15
|
export interface MeshData {
|
|
14
16
|
vertices: Float32Array<ArrayBuffer>;
|
|
@@ -70,12 +72,14 @@ export const MeshEmission = {
|
|
|
70
72
|
data: new Float32Array(MAX_ENTITIES * 4),
|
|
71
73
|
};
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
export const MeshVolumes = {
|
|
76
|
+
data: new Uint8Array(MAX_ENTITIES),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Volume = {
|
|
80
|
+
Solid: 0,
|
|
81
|
+
HalfSpace: 1,
|
|
82
|
+
} as const;
|
|
79
83
|
|
|
80
84
|
interface ColorProxy extends Array<number>, FieldAccessor {}
|
|
81
85
|
|
|
@@ -95,7 +99,6 @@ function colorProxy(): ColorProxy {
|
|
|
95
99
|
data[offset] = ((value >> 16) & 0xff) / 255;
|
|
96
100
|
data[offset + 1] = ((value >> 8) & 0xff) / 255;
|
|
97
101
|
data[offset + 2] = (value & 0xff) / 255;
|
|
98
|
-
data[offset + 3] = 1;
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
return new Proxy([] as unknown as ColorProxy, {
|
|
@@ -277,53 +280,49 @@ export const Mesh: {
|
|
|
277
280
|
colorR: ColorChannelProxy;
|
|
278
281
|
colorG: ColorChannelProxy;
|
|
279
282
|
colorB: ColorChannelProxy;
|
|
283
|
+
opacity: ColorChannelProxy;
|
|
280
284
|
sizeX: SizeProxy;
|
|
281
285
|
sizeY: SizeProxy;
|
|
282
286
|
sizeZ: SizeProxy;
|
|
283
287
|
roughness: PBRProxy;
|
|
284
288
|
metallic: PBRProxy;
|
|
289
|
+
ior: PBRProxy;
|
|
285
290
|
emission: EmissionProxy;
|
|
286
291
|
emissionIntensity: PBRProxy;
|
|
292
|
+
volume: Uint8Array;
|
|
287
293
|
} = {
|
|
288
294
|
shape: MeshShapes.data,
|
|
289
295
|
color: colorProxy(),
|
|
290
296
|
colorR: colorChannelProxy(0),
|
|
291
297
|
colorG: colorChannelProxy(1),
|
|
292
298
|
colorB: colorChannelProxy(2),
|
|
299
|
+
opacity: colorChannelProxy(3),
|
|
293
300
|
sizeX: sizeProxy(0),
|
|
294
301
|
sizeY: sizeProxy(1),
|
|
295
302
|
sizeZ: sizeProxy(2),
|
|
296
303
|
roughness: pbrProxy(0, 0.9),
|
|
297
304
|
metallic: pbrProxy(1, 0.0),
|
|
305
|
+
ior: pbrProxy(2, 1.0),
|
|
298
306
|
emission: emissionProxy(),
|
|
299
307
|
emissionIntensity: emissionIntensityProxy(),
|
|
308
|
+
volume: MeshVolumes.data,
|
|
300
309
|
};
|
|
301
310
|
|
|
302
311
|
setTraits(Mesh, {
|
|
303
312
|
defaults: () => ({
|
|
304
313
|
shape: MeshShape.Box,
|
|
305
314
|
color: 0xffffff,
|
|
315
|
+
opacity: 1.0,
|
|
306
316
|
sizeX: 1,
|
|
307
317
|
sizeY: 1,
|
|
308
318
|
sizeZ: 1,
|
|
309
|
-
roughness: 0
|
|
319
|
+
roughness: 1.0,
|
|
310
320
|
metallic: 0.0,
|
|
321
|
+
ior: 1.0,
|
|
311
322
|
emission: 0x000000,
|
|
312
323
|
emissionIntensity: 0.0,
|
|
324
|
+
volume: Volume.Solid,
|
|
313
325
|
}),
|
|
314
|
-
accessors: {
|
|
315
|
-
color: Mesh.color,
|
|
316
|
-
colorR: Mesh.colorR,
|
|
317
|
-
colorG: Mesh.colorG,
|
|
318
|
-
colorB: Mesh.colorB,
|
|
319
|
-
sizeX: Mesh.sizeX,
|
|
320
|
-
sizeY: Mesh.sizeY,
|
|
321
|
-
sizeZ: Mesh.sizeZ,
|
|
322
|
-
roughness: Mesh.roughness,
|
|
323
|
-
metallic: Mesh.metallic,
|
|
324
|
-
emission: Mesh.emission,
|
|
325
|
-
emissionIntensity: Mesh.emissionIntensity,
|
|
326
|
-
},
|
|
327
326
|
});
|
|
328
327
|
|
|
329
328
|
export interface MeshBuffers {
|
|
@@ -350,90 +349,88 @@ export function createMeshBuffers(device: GPUDevice, mesh: MeshData): MeshBuffer
|
|
|
350
349
|
return { vertex, index, indexCount: mesh.indexCount };
|
|
351
350
|
}
|
|
352
351
|
|
|
353
|
-
export
|
|
354
|
-
shape: number;
|
|
355
|
-
surface: number;
|
|
356
|
-
}
|
|
352
|
+
export type BatchEntities = (number[] | null)[];
|
|
357
353
|
|
|
358
|
-
export function
|
|
359
|
-
return `${key.shape}:${key.surface}`;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export function collectByShapeAndSurface(
|
|
354
|
+
export function collectBatches(
|
|
363
355
|
entities: Iterable<number>,
|
|
364
|
-
getSurface: (eid: number) => number
|
|
365
|
-
|
|
366
|
-
|
|
356
|
+
getSurface: (eid: number) => number,
|
|
357
|
+
out: BatchEntities
|
|
358
|
+
): void {
|
|
359
|
+
for (let i = 0; i < MAX_BATCH_SLOTS; i++) {
|
|
360
|
+
if (out[i]) out[i]!.length = 0;
|
|
361
|
+
}
|
|
367
362
|
for (const eid of entities) {
|
|
368
363
|
const shape = Mesh.shape[eid];
|
|
369
364
|
const surface = getSurface(eid);
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
let entry =
|
|
365
|
+
const batchIndex = shape * MAX_SURFACES + surface;
|
|
366
|
+
if (batchIndex >= MAX_BATCH_SLOTS) continue;
|
|
367
|
+
let entry = out[batchIndex];
|
|
373
368
|
if (!entry) {
|
|
374
|
-
entry =
|
|
375
|
-
|
|
369
|
+
entry = [];
|
|
370
|
+
out[batchIndex] = entry;
|
|
376
371
|
}
|
|
377
|
-
entry.
|
|
372
|
+
entry.push(eid);
|
|
378
373
|
}
|
|
379
|
-
return batches;
|
|
380
374
|
}
|
|
381
375
|
|
|
382
|
-
export interface
|
|
383
|
-
index: number;
|
|
384
|
-
shape: number;
|
|
385
|
-
surface: number;
|
|
376
|
+
export interface Batch {
|
|
386
377
|
buffers: MeshBuffers;
|
|
387
378
|
entityIds: GPUBuffer;
|
|
388
379
|
count: number;
|
|
389
380
|
}
|
|
390
381
|
|
|
391
382
|
export interface BatchState {
|
|
392
|
-
batches:
|
|
383
|
+
batches: (Batch | null)[];
|
|
393
384
|
buffers: Map<number, MeshBuffers>;
|
|
394
385
|
}
|
|
395
386
|
|
|
396
387
|
export function updateBatches(
|
|
397
388
|
device: GPUDevice,
|
|
398
|
-
|
|
389
|
+
batchEntities: BatchEntities,
|
|
399
390
|
state: BatchState,
|
|
400
391
|
indirect: GPUBuffer
|
|
401
392
|
): void {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
393
|
+
for (let batchIndex = 0; batchIndex < MAX_BATCH_SLOTS; batchIndex++) {
|
|
394
|
+
const entities = batchEntities[batchIndex];
|
|
395
|
+
if (!entities || entities.length === 0) {
|
|
396
|
+
const batch = state.batches[batchIndex];
|
|
397
|
+
if (batch) {
|
|
398
|
+
batch.count = 0;
|
|
399
|
+
writeIndirect(device, indirect, batchIndex, {
|
|
400
|
+
indexCount: 0,
|
|
401
|
+
instanceCount: 0,
|
|
402
|
+
firstIndex: 0,
|
|
403
|
+
baseVertex: 0,
|
|
404
|
+
firstInstance: 0,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
406
409
|
|
|
407
|
-
let batch = state.batches
|
|
410
|
+
let batch = state.batches[batchIndex];
|
|
408
411
|
if (!batch) {
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
console.warn(
|
|
412
|
-
`Batch index ${batchIndex} exceeds limit (${MAX_BATCH_SLOTS}), skipping batch`
|
|
413
|
-
);
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
let buffers = state.buffers.get(key.shape);
|
|
412
|
+
const shape = Math.floor(batchIndex / MAX_SURFACES);
|
|
413
|
+
let buffers = state.buffers.get(shape);
|
|
417
414
|
if (!buffers) {
|
|
418
|
-
const data = getMesh(
|
|
415
|
+
const data = getMesh(shape) ?? getMesh(MeshShape.Box)!;
|
|
419
416
|
buffers = createMeshBuffers(device, data);
|
|
420
|
-
state.buffers.set(
|
|
417
|
+
state.buffers.set(shape, buffers);
|
|
421
418
|
}
|
|
422
419
|
batch = {
|
|
423
|
-
index: batchIndex,
|
|
424
|
-
shape: key.shape,
|
|
425
|
-
surface: key.surface,
|
|
426
420
|
buffers,
|
|
427
421
|
entityIds: createEntityIdBuffer(device, MAX_ENTITIES),
|
|
428
422
|
count: 0,
|
|
429
423
|
};
|
|
430
|
-
state.batches
|
|
424
|
+
state.batches[batchIndex] = batch;
|
|
431
425
|
}
|
|
432
426
|
|
|
433
|
-
|
|
427
|
+
for (let i = 0; i < entities.length; i++) {
|
|
428
|
+
batchEntityIds[i] = entities[i];
|
|
429
|
+
}
|
|
430
|
+
device.queue.writeBuffer(batch.entityIds, 0, batchEntityIds, 0, entities.length);
|
|
434
431
|
batch.count = entities.length;
|
|
435
432
|
|
|
436
|
-
writeIndirect(device, indirect,
|
|
433
|
+
writeIndirect(device, indirect, batchIndex, {
|
|
437
434
|
indexCount: batch.buffers.indexCount,
|
|
438
435
|
instanceCount: entities.length,
|
|
439
436
|
firstIndex: 0,
|
|
@@ -441,14 +438,9 @@ export function updateBatches(
|
|
|
441
438
|
firstInstance: 0,
|
|
442
439
|
});
|
|
443
440
|
}
|
|
444
|
-
|
|
445
|
-
for (const keyStr of state.batches.keys()) {
|
|
446
|
-
if (!activeBatches.has(keyStr)) {
|
|
447
|
-
state.batches.delete(keyStr);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
441
|
}
|
|
451
442
|
|
|
452
443
|
export { createBox } from "./box";
|
|
453
444
|
export { createSphere } from "./sphere";
|
|
454
445
|
export { createPlane } from "./plane";
|
|
446
|
+
export { createShapeAtlas } from "./unified";
|