@multiplekex/shallot 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/component.ts +1 -1
- package/src/core/index.ts +1 -13
- package/src/core/math.ts +186 -0
- package/src/core/state.ts +1 -1
- package/src/core/xml.ts +56 -41
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/text/index.ts +10 -65
- package/src/extras/{water.ts → water/index.ts} +59 -4
- package/src/standard/raster/batch.ts +149 -0
- package/src/standard/raster/forward.ts +832 -0
- package/src/standard/raster/index.ts +146 -472
- package/src/standard/raster/shadow.ts +408 -0
- package/src/standard/raytracing/bvh/blas.ts +335 -87
- package/src/standard/raytracing/bvh/radix.ts +225 -228
- package/src/standard/raytracing/bvh/refit.ts +711 -0
- package/src/standard/raytracing/bvh/structs.ts +0 -55
- package/src/standard/raytracing/bvh/tlas.ts +153 -141
- package/src/standard/raytracing/bvh/traverse.ts +72 -64
- package/src/standard/raytracing/index.ts +233 -204
- package/src/standard/raytracing/instance.ts +30 -18
- package/src/standard/raytracing/ray.ts +1 -1
- package/src/standard/raytracing/shaders.ts +23 -40
- package/src/standard/render/camera.ts +10 -28
- package/src/standard/render/data.ts +1 -1
- package/src/standard/render/index.ts +68 -12
- package/src/standard/render/light.ts +2 -2
- package/src/standard/render/mesh.ts +404 -0
- package/src/standard/render/overlay.ts +5 -2
- package/src/standard/render/postprocess.ts +263 -267
- package/src/standard/render/surface/index.ts +81 -12
- package/src/standard/render/surface/shaders.ts +265 -11
- package/src/standard/render/surface/structs.ts +10 -0
- package/src/standard/tween/tween.ts +44 -115
- package/src/standard/render/mesh/box.ts +0 -20
- package/src/standard/render/mesh/index.ts +0 -315
- package/src/standard/render/mesh/plane.ts +0 -11
- package/src/standard/render/mesh/sphere.ts +0 -40
- package/src/standard/render/mesh/unified.ts +0 -96
- package/src/standard/render/surface/compile.ts +0 -65
- package/src/standard/render/surface/noise.ts +0 -58
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
import { MAX_ENTITIES } from "../../../core";
|
|
2
|
-
import { setTraits, createFieldProxy, type FieldProxy } from "../../../core/component";
|
|
3
|
-
import { createEntityIdBuffer } from "../../compute";
|
|
4
|
-
import { writeIndirect } from "../indirect";
|
|
5
|
-
import { createBox } from "./box";
|
|
6
|
-
import { createSphere } from "./sphere";
|
|
7
|
-
import { createPlane } from "./plane";
|
|
8
|
-
|
|
9
|
-
export const MAX_SURFACES = 16;
|
|
10
|
-
export const MAX_BATCH_SLOTS = 256;
|
|
11
|
-
const INVALID_SHAPE = 0xffffffff;
|
|
12
|
-
|
|
13
|
-
const batchEntityIds = new Uint32Array(MAX_ENTITIES);
|
|
14
|
-
|
|
15
|
-
export interface MeshData {
|
|
16
|
-
vertices: Float32Array<ArrayBuffer>;
|
|
17
|
-
indices: Uint16Array<ArrayBuffer>;
|
|
18
|
-
vertexCount: number;
|
|
19
|
-
indexCount: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const meshes: MeshData[] = [];
|
|
23
|
-
|
|
24
|
-
function initBuiltIns(): void {
|
|
25
|
-
if (meshes.length === 0) {
|
|
26
|
-
meshes.push(createBox());
|
|
27
|
-
meshes.push(createSphere());
|
|
28
|
-
meshes.push(createPlane());
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
initBuiltIns();
|
|
33
|
-
|
|
34
|
-
export const MeshShape = {
|
|
35
|
-
Box: 0,
|
|
36
|
-
Sphere: 1,
|
|
37
|
-
Plane: 2,
|
|
38
|
-
} as const;
|
|
39
|
-
|
|
40
|
-
export function mesh(data: MeshData): number {
|
|
41
|
-
const id = meshes.length;
|
|
42
|
-
meshes.push(data);
|
|
43
|
-
return id;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function getMesh(id: number): MeshData | undefined {
|
|
47
|
-
return meshes[id];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function clearMeshes(): void {
|
|
51
|
-
meshes.length = 0;
|
|
52
|
-
initBuiltIns();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const MeshShapes = {
|
|
56
|
-
data: new Uint32Array(MAX_ENTITIES).fill(INVALID_SHAPE),
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const MeshColors = {
|
|
60
|
-
data: new Float32Array(MAX_ENTITIES * 4),
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const MeshSizes = {
|
|
64
|
-
data: new Float32Array(MAX_ENTITIES * 4),
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const MeshPBR = {
|
|
68
|
-
data: new Float32Array(MAX_ENTITIES * 4),
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const MeshEmission = {
|
|
72
|
-
data: new Float32Array(MAX_ENTITIES * 4),
|
|
73
|
-
};
|
|
74
|
-
|
|
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;
|
|
83
|
-
|
|
84
|
-
function hexColorProxy(data: Float32Array): FieldProxy {
|
|
85
|
-
function getValue(eid: number): number {
|
|
86
|
-
const offset = eid * 4;
|
|
87
|
-
const r = Math.round(data[offset] * 255);
|
|
88
|
-
const g = Math.round(data[offset + 1] * 255);
|
|
89
|
-
const b = Math.round(data[offset + 2] * 255);
|
|
90
|
-
return (r << 16) | (g << 8) | b;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function setValue(eid: number, value: number): void {
|
|
94
|
-
const offset = eid * 4;
|
|
95
|
-
data[offset] = ((value >> 16) & 0xff) / 255;
|
|
96
|
-
data[offset + 1] = ((value >> 8) & 0xff) / 255;
|
|
97
|
-
data[offset + 2] = (value & 0xff) / 255;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return new Proxy([] as unknown as FieldProxy, {
|
|
101
|
-
get(_, prop) {
|
|
102
|
-
if (prop === "get") return getValue;
|
|
103
|
-
if (prop === "set") return setValue;
|
|
104
|
-
const eid = Number(prop);
|
|
105
|
-
if (Number.isNaN(eid)) return undefined;
|
|
106
|
-
return getValue(eid);
|
|
107
|
-
},
|
|
108
|
-
set(_, prop, value) {
|
|
109
|
-
const eid = Number(prop);
|
|
110
|
-
if (Number.isNaN(eid)) return false;
|
|
111
|
-
setValue(eid, value);
|
|
112
|
-
return true;
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function roughnessProxy(): FieldProxy {
|
|
118
|
-
const data = MeshPBR.data;
|
|
119
|
-
|
|
120
|
-
function getValue(eid: number): number {
|
|
121
|
-
const val = data[eid * 4];
|
|
122
|
-
return val === 0 ? 0.9 : val;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function setValue(eid: number, value: number): void {
|
|
126
|
-
data[eid * 4] = value;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return new Proxy([] as unknown as FieldProxy, {
|
|
130
|
-
get(_, prop) {
|
|
131
|
-
if (prop === "get") return getValue;
|
|
132
|
-
if (prop === "set") return setValue;
|
|
133
|
-
const eid = Number(prop);
|
|
134
|
-
if (Number.isNaN(eid)) return undefined;
|
|
135
|
-
return getValue(eid);
|
|
136
|
-
},
|
|
137
|
-
set(_, prop, value) {
|
|
138
|
-
const eid = Number(prop);
|
|
139
|
-
if (Number.isNaN(eid)) return false;
|
|
140
|
-
setValue(eid, value);
|
|
141
|
-
return true;
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export const Mesh: {
|
|
147
|
-
shape: Uint32Array;
|
|
148
|
-
color: FieldProxy;
|
|
149
|
-
colorR: FieldProxy;
|
|
150
|
-
colorG: FieldProxy;
|
|
151
|
-
colorB: FieldProxy;
|
|
152
|
-
opacity: FieldProxy;
|
|
153
|
-
sizeX: FieldProxy;
|
|
154
|
-
sizeY: FieldProxy;
|
|
155
|
-
sizeZ: FieldProxy;
|
|
156
|
-
roughness: FieldProxy;
|
|
157
|
-
metallic: FieldProxy;
|
|
158
|
-
ior: FieldProxy;
|
|
159
|
-
emission: FieldProxy;
|
|
160
|
-
emissionIntensity: FieldProxy;
|
|
161
|
-
volume: Uint8Array;
|
|
162
|
-
} = {
|
|
163
|
-
shape: MeshShapes.data,
|
|
164
|
-
color: hexColorProxy(MeshColors.data),
|
|
165
|
-
colorR: createFieldProxy(MeshColors.data, 4, 0),
|
|
166
|
-
colorG: createFieldProxy(MeshColors.data, 4, 1),
|
|
167
|
-
colorB: createFieldProxy(MeshColors.data, 4, 2),
|
|
168
|
-
opacity: createFieldProxy(MeshColors.data, 4, 3),
|
|
169
|
-
sizeX: createFieldProxy(MeshSizes.data, 4, 0),
|
|
170
|
-
sizeY: createFieldProxy(MeshSizes.data, 4, 1),
|
|
171
|
-
sizeZ: createFieldProxy(MeshSizes.data, 4, 2),
|
|
172
|
-
roughness: roughnessProxy(),
|
|
173
|
-
metallic: createFieldProxy(MeshPBR.data, 4, 1),
|
|
174
|
-
ior: createFieldProxy(MeshPBR.data, 4, 2),
|
|
175
|
-
emission: hexColorProxy(MeshEmission.data),
|
|
176
|
-
emissionIntensity: createFieldProxy(MeshEmission.data, 4, 3),
|
|
177
|
-
volume: MeshVolumes.data,
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
setTraits(Mesh, {
|
|
181
|
-
defaults: () => ({
|
|
182
|
-
shape: MeshShape.Box,
|
|
183
|
-
color: 0xffffff,
|
|
184
|
-
opacity: 1.0,
|
|
185
|
-
sizeX: 1,
|
|
186
|
-
sizeY: 1,
|
|
187
|
-
sizeZ: 1,
|
|
188
|
-
roughness: 1.0,
|
|
189
|
-
metallic: 0.0,
|
|
190
|
-
ior: 1.0,
|
|
191
|
-
emission: 0x000000,
|
|
192
|
-
emissionIntensity: 0.0,
|
|
193
|
-
volume: Volume.Solid,
|
|
194
|
-
}),
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
export interface MeshBuffers {
|
|
198
|
-
vertex: GPUBuffer;
|
|
199
|
-
index: GPUBuffer;
|
|
200
|
-
indexCount: number;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export function createMeshBuffers(device: GPUDevice, mesh: MeshData): MeshBuffers {
|
|
204
|
-
const vertex = device.createBuffer({
|
|
205
|
-
label: "vertex",
|
|
206
|
-
size: mesh.vertices.byteLength,
|
|
207
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
208
|
-
});
|
|
209
|
-
device.queue.writeBuffer(vertex, 0, mesh.vertices);
|
|
210
|
-
|
|
211
|
-
const index = device.createBuffer({
|
|
212
|
-
label: "index",
|
|
213
|
-
size: mesh.indices.byteLength,
|
|
214
|
-
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
215
|
-
});
|
|
216
|
-
device.queue.writeBuffer(index, 0, mesh.indices);
|
|
217
|
-
|
|
218
|
-
return { vertex, index, indexCount: mesh.indexCount };
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export type BatchEntities = (number[] | null)[];
|
|
222
|
-
|
|
223
|
-
export function collectBatches(
|
|
224
|
-
entities: Iterable<number>,
|
|
225
|
-
getSurface: (eid: number) => number,
|
|
226
|
-
out: BatchEntities
|
|
227
|
-
): void {
|
|
228
|
-
for (let i = 0; i < MAX_BATCH_SLOTS; i++) {
|
|
229
|
-
if (out[i]) out[i]!.length = 0;
|
|
230
|
-
}
|
|
231
|
-
for (const eid of entities) {
|
|
232
|
-
const shape = Mesh.shape[eid];
|
|
233
|
-
const surface = getSurface(eid);
|
|
234
|
-
const batchIndex = shape * MAX_SURFACES + surface;
|
|
235
|
-
if (batchIndex >= MAX_BATCH_SLOTS) continue;
|
|
236
|
-
let entry = out[batchIndex];
|
|
237
|
-
if (!entry) {
|
|
238
|
-
entry = [];
|
|
239
|
-
out[batchIndex] = entry;
|
|
240
|
-
}
|
|
241
|
-
entry.push(eid);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export interface Batch {
|
|
246
|
-
buffers: MeshBuffers;
|
|
247
|
-
entityIds: GPUBuffer;
|
|
248
|
-
count: number;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
export interface BatchState {
|
|
252
|
-
batches: (Batch | null)[];
|
|
253
|
-
buffers: Map<number, MeshBuffers>;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export function updateBatches(
|
|
257
|
-
device: GPUDevice,
|
|
258
|
-
batchEntities: BatchEntities,
|
|
259
|
-
state: BatchState,
|
|
260
|
-
indirect: GPUBuffer
|
|
261
|
-
): void {
|
|
262
|
-
for (let batchIndex = 0; batchIndex < MAX_BATCH_SLOTS; batchIndex++) {
|
|
263
|
-
const entities = batchEntities[batchIndex];
|
|
264
|
-
if (!entities || entities.length === 0) {
|
|
265
|
-
const batch = state.batches[batchIndex];
|
|
266
|
-
if (batch) {
|
|
267
|
-
batch.count = 0;
|
|
268
|
-
writeIndirect(device, indirect, batchIndex, {
|
|
269
|
-
indexCount: 0,
|
|
270
|
-
instanceCount: 0,
|
|
271
|
-
firstIndex: 0,
|
|
272
|
-
baseVertex: 0,
|
|
273
|
-
firstInstance: 0,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
let batch = state.batches[batchIndex];
|
|
280
|
-
if (!batch) {
|
|
281
|
-
const shape = Math.floor(batchIndex / MAX_SURFACES);
|
|
282
|
-
let buffers = state.buffers.get(shape);
|
|
283
|
-
if (!buffers) {
|
|
284
|
-
const data = getMesh(shape) ?? getMesh(MeshShape.Box)!;
|
|
285
|
-
buffers = createMeshBuffers(device, data);
|
|
286
|
-
state.buffers.set(shape, buffers);
|
|
287
|
-
}
|
|
288
|
-
batch = {
|
|
289
|
-
buffers,
|
|
290
|
-
entityIds: createEntityIdBuffer(device, MAX_ENTITIES),
|
|
291
|
-
count: 0,
|
|
292
|
-
};
|
|
293
|
-
state.batches[batchIndex] = batch;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
for (let i = 0; i < entities.length; i++) {
|
|
297
|
-
batchEntityIds[i] = entities[i];
|
|
298
|
-
}
|
|
299
|
-
device.queue.writeBuffer(batch.entityIds, 0, batchEntityIds, 0, entities.length);
|
|
300
|
-
batch.count = entities.length;
|
|
301
|
-
|
|
302
|
-
writeIndirect(device, indirect, batchIndex, {
|
|
303
|
-
indexCount: batch.buffers.indexCount,
|
|
304
|
-
instanceCount: entities.length,
|
|
305
|
-
firstIndex: 0,
|
|
306
|
-
baseVertex: 0,
|
|
307
|
-
firstInstance: 0,
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export { createBox } from "./box";
|
|
313
|
-
export { createSphere } from "./sphere";
|
|
314
|
-
export { createPlane } from "./plane";
|
|
315
|
-
export { createShapeAtlas } from "./unified";
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { MeshData } from "./index";
|
|
2
|
-
|
|
3
|
-
export function createPlane(): MeshData {
|
|
4
|
-
const vertices = new Float32Array([
|
|
5
|
-
-0.5, 0, 0.5, 0, 1, 0, 0.5, 0, 0.5, 0, 1, 0, 0.5, 0, -0.5, 0, 1, 0, -0.5, 0, -0.5, 0, 1, 0,
|
|
6
|
-
]);
|
|
7
|
-
|
|
8
|
-
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
9
|
-
|
|
10
|
-
return { vertices, indices, vertexCount: 4, indexCount: 6 };
|
|
11
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { MeshData } from "./index";
|
|
2
|
-
|
|
3
|
-
export function createSphere(segments = 32, rings = 16): MeshData {
|
|
4
|
-
const vertices: number[] = [];
|
|
5
|
-
const indices: number[] = [];
|
|
6
|
-
const radius = 0.5;
|
|
7
|
-
|
|
8
|
-
for (let y = 0; y <= rings; y++) {
|
|
9
|
-
const v = y / rings;
|
|
10
|
-
const theta = v * Math.PI;
|
|
11
|
-
|
|
12
|
-
for (let x = 0; x <= segments; x++) {
|
|
13
|
-
const u = x / segments;
|
|
14
|
-
const phi = u * Math.PI * 2;
|
|
15
|
-
|
|
16
|
-
const nx = Math.sin(theta) * Math.cos(phi);
|
|
17
|
-
const ny = Math.cos(theta);
|
|
18
|
-
const nz = Math.sin(theta) * Math.sin(phi);
|
|
19
|
-
|
|
20
|
-
vertices.push(nx * radius, ny * radius, nz * radius, nx, ny, nz);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
for (let y = 0; y < rings; y++) {
|
|
25
|
-
for (let x = 0; x < segments; x++) {
|
|
26
|
-
const a = y * (segments + 1) + x;
|
|
27
|
-
const b = a + segments + 1;
|
|
28
|
-
|
|
29
|
-
indices.push(a, a + 1, b);
|
|
30
|
-
indices.push(a + 1, b + 1, b);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
vertices: new Float32Array(vertices),
|
|
36
|
-
indices: new Uint16Array(indices),
|
|
37
|
-
vertexCount: (rings + 1) * (segments + 1),
|
|
38
|
-
indexCount: rings * segments * 6,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { getMesh } from ".";
|
|
2
|
-
|
|
3
|
-
export interface ShapeMeta {
|
|
4
|
-
vertexOffset: number;
|
|
5
|
-
indexOffset: number;
|
|
6
|
-
triCount: number;
|
|
7
|
-
_pad: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ShapeAtlas {
|
|
11
|
-
vertices: GPUBuffer;
|
|
12
|
-
indices: GPUBuffer;
|
|
13
|
-
meta: GPUBuffer;
|
|
14
|
-
shapeCount: number;
|
|
15
|
-
maxTriangles: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const MAX_SHAPES = 16;
|
|
19
|
-
|
|
20
|
-
export function createShapeAtlas(device: GPUDevice): ShapeAtlas {
|
|
21
|
-
const allVertices: number[] = [];
|
|
22
|
-
const allIndices: number[] = [];
|
|
23
|
-
const shapeMetas: ShapeMeta[] = [];
|
|
24
|
-
|
|
25
|
-
let vertexOffset = 0;
|
|
26
|
-
let indexOffset = 0;
|
|
27
|
-
let maxTriangles = 0;
|
|
28
|
-
|
|
29
|
-
for (let shapeId = 0; shapeId < MAX_SHAPES; shapeId++) {
|
|
30
|
-
const mesh = getMesh(shapeId);
|
|
31
|
-
if (!mesh) {
|
|
32
|
-
shapeMetas.push({ vertexOffset: 0, indexOffset: 0, triCount: 0, _pad: 0 });
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const triCount = mesh.indexCount / 3;
|
|
37
|
-
shapeMetas.push({
|
|
38
|
-
vertexOffset,
|
|
39
|
-
indexOffset,
|
|
40
|
-
triCount,
|
|
41
|
-
_pad: 0,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
for (let i = 0; i < mesh.vertices.length; i++) {
|
|
45
|
-
allVertices.push(mesh.vertices[i]);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
for (let i = 0; i < mesh.indices.length; i++) {
|
|
49
|
-
allIndices.push(mesh.indices[i]);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
vertexOffset += mesh.vertices.length;
|
|
53
|
-
indexOffset += mesh.indices.length;
|
|
54
|
-
maxTriangles += triCount;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const verticesData = new Float32Array(allVertices);
|
|
58
|
-
const indicesData = new Uint32Array(allIndices);
|
|
59
|
-
const metaData = new Uint32Array(MAX_SHAPES * 4);
|
|
60
|
-
|
|
61
|
-
for (let i = 0; i < shapeMetas.length; i++) {
|
|
62
|
-
metaData[i * 4] = shapeMetas[i].vertexOffset;
|
|
63
|
-
metaData[i * 4 + 1] = shapeMetas[i].indexOffset;
|
|
64
|
-
metaData[i * 4 + 2] = shapeMetas[i].triCount;
|
|
65
|
-
metaData[i * 4 + 3] = 0;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const vertices = device.createBuffer({
|
|
69
|
-
label: "unified-vertices",
|
|
70
|
-
size: Math.max(verticesData.byteLength, 16),
|
|
71
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
72
|
-
});
|
|
73
|
-
device.queue.writeBuffer(vertices, 0, verticesData);
|
|
74
|
-
|
|
75
|
-
const indices = device.createBuffer({
|
|
76
|
-
label: "unified-indices",
|
|
77
|
-
size: Math.max(indicesData.byteLength, 16),
|
|
78
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
79
|
-
});
|
|
80
|
-
device.queue.writeBuffer(indices, 0, indicesData);
|
|
81
|
-
|
|
82
|
-
const meta = device.createBuffer({
|
|
83
|
-
label: "unified-meta",
|
|
84
|
-
size: metaData.byteLength,
|
|
85
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
86
|
-
});
|
|
87
|
-
device.queue.writeBuffer(meta, 0, metaData);
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
vertices,
|
|
91
|
-
indices,
|
|
92
|
-
meta,
|
|
93
|
-
shapeCount: shapeMetas.filter((m) => m.triCount > 0).length,
|
|
94
|
-
maxTriangles,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { WGSL_STRUCTS } from "./structs";
|
|
2
|
-
import type { SurfaceData } from "./index";
|
|
3
|
-
import { compileVertexBody, WGSL_LIGHTING_CALC } from "./shaders";
|
|
4
|
-
|
|
5
|
-
export function compileSurface(data: SurfaceData): string {
|
|
6
|
-
const vertexTransform = compileVertexBody(data.vertex);
|
|
7
|
-
|
|
8
|
-
const fragmentBody = data.fragment ?? "";
|
|
9
|
-
|
|
10
|
-
return /* wgsl */ `
|
|
11
|
-
${WGSL_STRUCTS}
|
|
12
|
-
|
|
13
|
-
fn userVertexTransform(worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
|
|
14
|
-
${vertexTransform}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
fn userFragment(surface: ptr<function, SurfaceData>, position: vec4<f32>) {
|
|
18
|
-
${fragmentBody}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@vertex
|
|
22
|
-
fn vs(input: VertexInput) -> VertexOutput {
|
|
23
|
-
let eid = entityIds[input.instance];
|
|
24
|
-
let world = matrices[eid];
|
|
25
|
-
let scaledPos = input.position * sizes[eid].xyz;
|
|
26
|
-
let baseWorldPos = (world * vec4<f32>(scaledPos, 1.0)).xyz;
|
|
27
|
-
let worldNormal = normalize((world * vec4<f32>(input.normal, 0.0)).xyz);
|
|
28
|
-
let finalWorldPos = userVertexTransform(baseWorldPos, worldNormal, eid);
|
|
29
|
-
|
|
30
|
-
var output: VertexOutput;
|
|
31
|
-
output.position = scene.viewProj * vec4<f32>(finalWorldPos, 1.0);
|
|
32
|
-
output.color = data[eid].baseColor;
|
|
33
|
-
output.worldNormal = worldNormal;
|
|
34
|
-
output.entityId = eid;
|
|
35
|
-
output.worldPos = finalWorldPos;
|
|
36
|
-
return output;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
@fragment
|
|
40
|
-
fn fs(input: VertexOutput) -> FragmentOutput {
|
|
41
|
-
let eid = input.entityId;
|
|
42
|
-
let d = data[eid];
|
|
43
|
-
|
|
44
|
-
var surface: SurfaceData;
|
|
45
|
-
surface.baseColor = input.color.rgb;
|
|
46
|
-
surface.roughness = d.pbr.x;
|
|
47
|
-
surface.metallic = d.pbr.y;
|
|
48
|
-
surface.emission = d.emission.rgb * d.emission.a;
|
|
49
|
-
surface.normal = normalize(input.worldNormal);
|
|
50
|
-
surface.worldPos = input.worldPos;
|
|
51
|
-
|
|
52
|
-
userFragment(&surface, input.position);
|
|
53
|
-
|
|
54
|
-
var output: FragmentOutput;
|
|
55
|
-
${
|
|
56
|
-
data.lit === false
|
|
57
|
-
? ` output.color = vec4<f32>(surface.baseColor, input.color.a);`
|
|
58
|
-
: ` ${WGSL_LIGHTING_CALC}
|
|
59
|
-
output.color = vec4<f32>(surface.baseColor * lighting + surface.emission, input.color.a);`
|
|
60
|
-
}
|
|
61
|
-
output.entityId = input.entityId;
|
|
62
|
-
return output;
|
|
63
|
-
}
|
|
64
|
-
`;
|
|
65
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
export const NOISE_WGSL = /* wgsl */ `
|
|
2
|
-
fn hash2(p: vec2<f32>) -> f32 {
|
|
3
|
-
var p3 = fract(vec3(p.x, p.y, p.x) * 0.1031);
|
|
4
|
-
p3 += dot(p3, p3.yzx + 33.33);
|
|
5
|
-
return fract((p3.x + p3.y) * p3.z);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
fn value2d(p: vec2f, seed: vec2f) -> f32 {
|
|
9
|
-
let i = floor(p);
|
|
10
|
-
let f = fract(p);
|
|
11
|
-
let u = f * f * (3.0 - 2.0 * f);
|
|
12
|
-
return mix(
|
|
13
|
-
mix(fract(sin(dot(i, seed)) * 43758.5) * 2.0 - 1.0,
|
|
14
|
-
fract(sin(dot(i + vec2(1.0, 0.0), seed)) * 43758.5) * 2.0 - 1.0, u.x),
|
|
15
|
-
mix(fract(sin(dot(i + vec2(0.0, 1.0), seed)) * 43758.5) * 2.0 - 1.0,
|
|
16
|
-
fract(sin(dot(i + vec2(1.0, 1.0), seed)) * 43758.5) * 2.0 - 1.0, u.x), u.y);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
fn simplex2(p: vec2<f32>) -> f32 {
|
|
20
|
-
let K1 = 0.366025404;
|
|
21
|
-
let K2 = 0.211324865;
|
|
22
|
-
|
|
23
|
-
let i = floor(p + (p.x + p.y) * K1);
|
|
24
|
-
let a = p - i + (i.x + i.y) * K2;
|
|
25
|
-
|
|
26
|
-
let o = select(vec2(0.0, 1.0), vec2(1.0, 0.0), a.x > a.y);
|
|
27
|
-
let b = a - o + K2;
|
|
28
|
-
let c = a - 1.0 + 2.0 * K2;
|
|
29
|
-
|
|
30
|
-
let h = max(0.5 - vec3(dot(a, a), dot(b, b), dot(c, c)), vec3(0.0));
|
|
31
|
-
let h4 = h * h * h * h;
|
|
32
|
-
|
|
33
|
-
let n = vec3(
|
|
34
|
-
dot(a, vec2(hash2(i) * 2.0 - 1.0, hash2(i + vec2(0.0, 1.0)) * 2.0 - 1.0)),
|
|
35
|
-
dot(b, vec2(hash2(i + o) * 2.0 - 1.0, hash2(i + o + vec2(0.0, 1.0)) * 2.0 - 1.0)),
|
|
36
|
-
dot(c, vec2(hash2(i + 1.0) * 2.0 - 1.0, hash2(i + vec2(1.0, 2.0)) * 2.0 - 1.0))
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
return dot(h4, n) * 70.0;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const FBM2_OCTAVES = 5;
|
|
43
|
-
|
|
44
|
-
fn fbm2(p: vec2<f32>) -> f32 {
|
|
45
|
-
var value = 0.0;
|
|
46
|
-
var amplitude = 0.5;
|
|
47
|
-
var frequency = 1.0;
|
|
48
|
-
var pos = p;
|
|
49
|
-
|
|
50
|
-
for (var i = 0; i < FBM2_OCTAVES; i++) {
|
|
51
|
-
value += amplitude * simplex2(pos * frequency);
|
|
52
|
-
amplitude *= 0.5;
|
|
53
|
-
frequency *= 2.0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return value;
|
|
57
|
-
}
|
|
58
|
-
`;
|