@multiplekex/shallot 0.1.9 → 0.1.10
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/state.ts +2 -0
- package/src/extras/arrows/index.ts +13 -8
- package/src/extras/gradient/index.ts +1050 -0
- package/src/extras/index.ts +1 -0
- package/src/extras/lines/index.ts +13 -8
- package/src/extras/text/index.ts +11 -7
- package/src/standard/compute/graph.ts +10 -12
- package/src/standard/compute/index.ts +1 -0
- package/src/standard/compute/inspect.ts +34 -52
- package/src/standard/compute/pass.ts +23 -0
- package/src/standard/render/camera.ts +7 -2
- package/src/standard/render/forward.ts +99 -89
- package/src/standard/render/index.ts +68 -44
- package/src/standard/render/mesh/index.ts +190 -19
- package/src/standard/render/pass.ts +63 -0
- package/src/standard/render/postprocess.ts +241 -57
- package/src/standard/render/scene.ts +87 -2
- package/src/standard/render/surface/compile.ts +74 -0
- package/src/standard/render/surface/index.ts +116 -0
- package/src/standard/render/surface/structs.ts +50 -0
- package/src/standard/render/transparent.ts +62 -65
- package/src/standard/render/material/index.ts +0 -92
- package/src/standard/render/opaque.ts +0 -44
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const WGSL_STRUCTS = /* wgsl */ `
|
|
2
|
+
struct VertexInput {
|
|
3
|
+
@location(0) position: vec3<f32>,
|
|
4
|
+
@location(1) normal: vec3<f32>,
|
|
5
|
+
@builtin(instance_index) instance: u32,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
struct VertexOutput {
|
|
9
|
+
@builtin(position) position: vec4<f32>,
|
|
10
|
+
@location(0) color: vec4<f32>,
|
|
11
|
+
@location(1) worldNormal: vec3<f32>,
|
|
12
|
+
@location(2) @interpolate(flat) entityId: u32,
|
|
13
|
+
@location(3) worldPos: vec3<f32>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
struct SurfaceData {
|
|
17
|
+
baseColor: vec3<f32>,
|
|
18
|
+
roughness: f32,
|
|
19
|
+
metallic: f32,
|
|
20
|
+
emission: vec3<f32>,
|
|
21
|
+
normal: vec3<f32>,
|
|
22
|
+
worldPos: vec3<f32>,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
struct FragmentOutput {
|
|
26
|
+
@location(0) color: vec4<f32>,
|
|
27
|
+
@location(1) entityId: u32,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
struct Scene {
|
|
31
|
+
viewProj: mat4x4<f32>,
|
|
32
|
+
cameraWorld: mat4x4<f32>,
|
|
33
|
+
ambientColor: vec4<f32>,
|
|
34
|
+
sunDirection: vec4<f32>,
|
|
35
|
+
sunColor: vec4<f32>,
|
|
36
|
+
cameraMode: f32,
|
|
37
|
+
cameraSize: f32,
|
|
38
|
+
viewport: vec2<f32>,
|
|
39
|
+
frustumPlanes: array<vec4<f32>, 6>,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@group(0) @binding(0) var<uniform> scene: Scene;
|
|
43
|
+
@group(0) @binding(1) var<storage, read> entityIds: array<u32>;
|
|
44
|
+
@group(0) @binding(2) var<storage, read> matrices: array<mat4x4<f32>>;
|
|
45
|
+
@group(0) @binding(3) var<storage, read> colors: array<vec4<f32>>;
|
|
46
|
+
@group(0) @binding(4) var<storage, read> sizes: array<vec4<f32>>;
|
|
47
|
+
@group(0) @binding(5) var<storage, read> pbr: array<vec4<f32>>;
|
|
48
|
+
@group(0) @binding(6) var<storage, read> emission: array<vec4<f32>>;
|
|
49
|
+
@group(0) @binding(7) var<storage, read> shapes: array<u32>;
|
|
50
|
+
`;
|
|
@@ -1,47 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { State } from "../../core";
|
|
2
2
|
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
3
3
|
import { MASK_FORMAT } from "./scene";
|
|
4
|
+
import { Pass, getDrawsByPass, type DrawContext, type SharedPassContext } from "./pass";
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
-
readonly device: GPUDevice;
|
|
7
|
-
readonly format: GPUTextureFormat;
|
|
8
|
-
readonly maskFormat: GPUTextureFormat;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface DrawContributor {
|
|
12
|
-
readonly id: string;
|
|
13
|
-
readonly order: number;
|
|
14
|
-
draw(pass: GPURenderPassEncoder, ctx: DrawContext): void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface TransparentPassState {
|
|
18
|
-
contributors: Map<string, DrawContributor>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const TransparentPass = resource<TransparentPassState>("transparent-pass");
|
|
22
|
-
|
|
23
|
-
export function registerDrawContributor(state: State, contributor: DrawContributor): void {
|
|
24
|
-
const pass = TransparentPass.from(state);
|
|
25
|
-
if (pass) {
|
|
26
|
-
pass.contributors.set(contributor.id, contributor);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function unregisterDrawContributor(state: State, id: string): void {
|
|
31
|
-
const pass = TransparentPass.from(state);
|
|
32
|
-
if (pass) {
|
|
33
|
-
pass.contributors.delete(id);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
6
|
+
export { MASK_FORMAT };
|
|
36
7
|
|
|
37
8
|
export interface TransparentNodeConfig {
|
|
38
|
-
|
|
9
|
+
state: State;
|
|
39
10
|
}
|
|
40
11
|
|
|
41
12
|
export function createTransparentNode(config: TransparentNodeConfig): ComputeNode {
|
|
42
13
|
return {
|
|
43
14
|
id: "transparent",
|
|
44
|
-
|
|
15
|
+
pass: Pass.Transparent,
|
|
45
16
|
inputs: [{ id: "depth", access: "read" }],
|
|
46
17
|
outputs: [
|
|
47
18
|
{ id: "scene", access: "write" },
|
|
@@ -49,46 +20,72 @@ export function createTransparentNode(config: TransparentNodeConfig): ComputeNod
|
|
|
49
20
|
],
|
|
50
21
|
|
|
51
22
|
execute(ctx: ExecutionContext) {
|
|
52
|
-
const
|
|
53
|
-
if (contributors.length === 0) return;
|
|
54
|
-
|
|
23
|
+
const { device, encoder, format, context } = ctx;
|
|
55
24
|
const targetView = ctx.getTextureView("scene") ?? ctx.canvasView;
|
|
56
25
|
const depthView = ctx.getTextureView("depth")!;
|
|
57
26
|
const maskView = ctx.getTextureView("mask")!;
|
|
58
|
-
|
|
59
|
-
const pass = ctx.encoder.beginRenderPass({
|
|
60
|
-
colorAttachments: [
|
|
61
|
-
{
|
|
62
|
-
view: targetView,
|
|
63
|
-
loadOp: "load" as const,
|
|
64
|
-
storeOp: "store" as const,
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
view: maskView,
|
|
68
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
69
|
-
loadOp: "clear" as const,
|
|
70
|
-
storeOp: "store" as const,
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
depthStencilAttachment: {
|
|
74
|
-
view: depthView,
|
|
75
|
-
depthLoadOp: "load" as const,
|
|
76
|
-
depthStoreOp: "store" as const,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
27
|
+
const entityIdView = ctx.getTextureView("entityId")!;
|
|
79
28
|
|
|
80
29
|
const drawCtx: DrawContext = {
|
|
81
|
-
device
|
|
82
|
-
|
|
83
|
-
|
|
30
|
+
device,
|
|
31
|
+
encoder,
|
|
32
|
+
format,
|
|
33
|
+
width: context.canvas.width,
|
|
34
|
+
height: context.canvas.height,
|
|
35
|
+
sceneView: targetView,
|
|
36
|
+
depthView,
|
|
37
|
+
entityIdView,
|
|
38
|
+
maskView,
|
|
39
|
+
canvasView: ctx.canvasView,
|
|
84
40
|
};
|
|
85
41
|
|
|
86
|
-
const
|
|
87
|
-
for (const
|
|
88
|
-
|
|
42
|
+
const beforeTransparents = getDrawsByPass(config.state, Pass.BeforeTransparent);
|
|
43
|
+
for (const draw of beforeTransparents) {
|
|
44
|
+
draw.execute(drawCtx);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const draws = getDrawsByPass(config.state, Pass.Transparent);
|
|
48
|
+
if (draws.length > 0) {
|
|
49
|
+
const pass = encoder.beginRenderPass({
|
|
50
|
+
colorAttachments: [
|
|
51
|
+
{
|
|
52
|
+
view: targetView,
|
|
53
|
+
loadOp: "load" as const,
|
|
54
|
+
storeOp: "store" as const,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
view: maskView,
|
|
58
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
59
|
+
loadOp: "clear" as const,
|
|
60
|
+
storeOp: "store" as const,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
depthStencilAttachment: {
|
|
64
|
+
view: depthView,
|
|
65
|
+
depthLoadOp: "load" as const,
|
|
66
|
+
depthStoreOp: "store" as const,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const sharedCtx: SharedPassContext = {
|
|
71
|
+
device,
|
|
72
|
+
format,
|
|
73
|
+
maskFormat: MASK_FORMAT,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
for (const draw of draws) {
|
|
77
|
+
if (draw.draw) {
|
|
78
|
+
draw.draw(pass, sharedCtx);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
pass.end();
|
|
89
83
|
}
|
|
90
84
|
|
|
91
|
-
|
|
85
|
+
const afterTransparents = getDrawsByPass(config.state, Pass.AfterTransparent);
|
|
86
|
+
for (const draw of afterTransparents) {
|
|
87
|
+
draw.execute(drawCtx);
|
|
88
|
+
}
|
|
92
89
|
},
|
|
93
90
|
};
|
|
94
91
|
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { MAX_ENTITIES } from "../../../core";
|
|
2
|
-
import { setTraits } from "../../../core/component";
|
|
3
|
-
|
|
4
|
-
export interface MaterialData {
|
|
5
|
-
roughness?: number;
|
|
6
|
-
metallic?: number;
|
|
7
|
-
emissionColor?: number;
|
|
8
|
-
emissionIntensity?: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const materials: MaterialData[] = [];
|
|
12
|
-
|
|
13
|
-
function initBuiltIns(): void {
|
|
14
|
-
if (materials.length === 0) {
|
|
15
|
-
materials.push({
|
|
16
|
-
roughness: 0.9,
|
|
17
|
-
metallic: 0.0,
|
|
18
|
-
emissionColor: 0x000000,
|
|
19
|
-
emissionIntensity: 0.0,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
initBuiltIns();
|
|
25
|
-
|
|
26
|
-
export const MaterialType = {
|
|
27
|
-
Default: 0,
|
|
28
|
-
} as const;
|
|
29
|
-
|
|
30
|
-
export function material(data: MaterialData): number {
|
|
31
|
-
const id = materials.length;
|
|
32
|
-
materials.push(data);
|
|
33
|
-
return id;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function getMaterial(id: number): MaterialData | undefined {
|
|
37
|
-
return materials[id];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function clearMaterials(): void {
|
|
41
|
-
materials.length = 0;
|
|
42
|
-
initBuiltIns();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const MaterialIds = {
|
|
46
|
-
data: new Uint32Array(MAX_ENTITIES),
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export const Material: {
|
|
50
|
-
type: number[];
|
|
51
|
-
} = {
|
|
52
|
-
type: [],
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
setTraits(Material, {
|
|
56
|
-
defaults: () => ({
|
|
57
|
-
type: MaterialType.Default,
|
|
58
|
-
}),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
function hexToRgb(hex: number): { r: number; g: number; b: number } {
|
|
62
|
-
return {
|
|
63
|
-
r: ((hex >> 16) & 0xff) / 255,
|
|
64
|
-
g: ((hex >> 8) & 0xff) / 255,
|
|
65
|
-
b: (hex & 0xff) / 255,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function packMaterials(): Float32Array {
|
|
70
|
-
const floatsPerMaterial = 8;
|
|
71
|
-
const buffer = new Float32Array(materials.length * floatsPerMaterial);
|
|
72
|
-
|
|
73
|
-
for (let i = 0; i < materials.length; i++) {
|
|
74
|
-
const mat = materials[i];
|
|
75
|
-
const offset = i * floatsPerMaterial;
|
|
76
|
-
|
|
77
|
-
const emissionColor = mat.emissionColor ?? 0x000000;
|
|
78
|
-
const emissionIntensity = mat.emissionIntensity ?? 0.0;
|
|
79
|
-
const rgb = hexToRgb(emissionColor);
|
|
80
|
-
|
|
81
|
-
buffer[offset + 0] = rgb.r * emissionIntensity;
|
|
82
|
-
buffer[offset + 1] = rgb.g * emissionIntensity;
|
|
83
|
-
buffer[offset + 2] = rgb.b * emissionIntensity;
|
|
84
|
-
buffer[offset + 3] = mat.roughness ?? 0.9;
|
|
85
|
-
buffer[offset + 4] = mat.metallic ?? 0.0;
|
|
86
|
-
buffer[offset + 5] = 0;
|
|
87
|
-
buffer[offset + 6] = 0;
|
|
88
|
-
buffer[offset + 7] = 0;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return buffer;
|
|
92
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { resource, type State } from "../../core";
|
|
2
|
-
import { DEPTH_FORMAT } from "./scene";
|
|
3
|
-
|
|
4
|
-
export interface OpaqueDrawContext {
|
|
5
|
-
readonly device: GPUDevice;
|
|
6
|
-
readonly format: GPUTextureFormat;
|
|
7
|
-
readonly depthFormat: GPUTextureFormat;
|
|
8
|
-
readonly scene: GPUBuffer;
|
|
9
|
-
readonly matrices: GPUBuffer;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface OpaqueDrawCallback {
|
|
13
|
-
readonly id: string;
|
|
14
|
-
readonly order: number;
|
|
15
|
-
draw(pass: GPURenderPassEncoder, ctx: OpaqueDrawContext): void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface OpaquePassState {
|
|
19
|
-
callbacks: Map<string, OpaqueDrawCallback>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const OpaquePass = resource<OpaquePassState>("opaque-pass");
|
|
23
|
-
|
|
24
|
-
export function registerOpaqueCallback(state: State, callback: OpaqueDrawCallback): void {
|
|
25
|
-
const pass = OpaquePass.from(state);
|
|
26
|
-
if (pass) {
|
|
27
|
-
pass.callbacks.set(callback.id, callback);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function unregisterOpaqueCallback(state: State, id: string): void {
|
|
32
|
-
const pass = OpaquePass.from(state);
|
|
33
|
-
if (pass) {
|
|
34
|
-
pass.callbacks.delete(id);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function getOpaqueCallbacks(state: State): OpaqueDrawCallback[] {
|
|
39
|
-
const pass = OpaquePass.from(state);
|
|
40
|
-
if (!pass) return [];
|
|
41
|
-
return Array.from(pass.callbacks.values());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export { DEPTH_FORMAT };
|