Skip to content

Commit c2ec1d8

Browse files
committed
Add IBL support to the renderer.
1 parent 79c9e89 commit c2ec1d8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2104
-289
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"culori": "^3.2.0",
3636
"eslint": "^8.51.0",
3737
"gl-matrix": "3.3",
38+
"hdr.js": "^0.2.0",
3839
"postcss": "^8.4.31",
3940
"prettier": "^3.0.3",
4041
"sass": "^1.69.0",

packages/global.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ declare module "*?raw" {
33
export default content;
44
}
55

6+
declare module "*?url" {
7+
const content: string;
8+
export default content;
9+
}
10+
611
declare module "*?worker" {
712
const content: new () => Worker;
813
export default content;

packages/material/material.ts

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ export function createDefaultMaterial(): Material {
3737
name: "Solid color",
3838
path: "materializer/solid-color",
3939
x: EDITOR_GRAPH_WIDTH / 2 - 64 - 128,
40-
y: EDITOR_GRAPH_HEIGHT / 2 - 64,
40+
y: EDITOR_GRAPH_HEIGHT / 2 - 64 - 128,
4141
parameters: {
42-
color: [0.15, 0.3, 1],
42+
//color: [0.15, 0.3, 1],
43+
color: [1, 1, 1],
4344
},
4445
textureSize: 1,
4546
textureFilterMethod: TextureFilterMethod.Linear,
@@ -49,12 +50,72 @@ export function createDefaultMaterial(): Material {
4950
1,
5051
createMutable({
5152
id: 1,
52-
name: "Albedo Output",
53+
name: "Base Color Output",
54+
path: "materializer/output",
55+
x: EDITOR_GRAPH_WIDTH / 2 - 64 + 128,
56+
y: EDITOR_GRAPH_HEIGHT / 2 - 64 - 128,
57+
parameters: {
58+
targetTexture: PbrTargetTextureType.BaseColor,
59+
},
60+
textureSize: 2048,
61+
textureFilterMethod: TextureFilterMethod.Linear,
62+
}),
63+
],
64+
[
65+
2,
66+
createMutable({
67+
id: 2,
68+
name: "Solid color",
69+
path: "materializer/solid-color",
70+
x: EDITOR_GRAPH_WIDTH / 2 - 64 - 128,
71+
y: EDITOR_GRAPH_HEIGHT / 2 - 64,
72+
parameters: {
73+
color: [1, 1, 1],
74+
},
75+
textureSize: 1,
76+
textureFilterMethod: TextureFilterMethod.Linear,
77+
}),
78+
],
79+
[
80+
3,
81+
createMutable({
82+
id: 3,
83+
name: "AO Output",
5384
path: "materializer/output",
5485
x: EDITOR_GRAPH_WIDTH / 2 - 64 + 128,
5586
y: EDITOR_GRAPH_HEIGHT / 2 - 64,
5687
parameters: {
57-
targetTexture: PbrTargetTextureType.Albedo,
88+
targetTexture: PbrTargetTextureType.AmbientOcclusion,
89+
},
90+
textureSize: 2048,
91+
textureFilterMethod: TextureFilterMethod.Linear,
92+
}),
93+
],
94+
[
95+
4,
96+
createMutable({
97+
id: 4,
98+
name: "Solid color",
99+
path: "materializer/solid-color",
100+
x: EDITOR_GRAPH_WIDTH / 2 - 64 - 128,
101+
y: EDITOR_GRAPH_HEIGHT / 2 - 64 + 128,
102+
parameters: {
103+
color: [0.8, 0.8, 0.8],
104+
},
105+
textureSize: 1,
106+
textureFilterMethod: TextureFilterMethod.Linear,
107+
}),
108+
],
109+
[
110+
5,
111+
createMutable({
112+
id: 5,
113+
name: "Roughness Output",
114+
path: "materializer/output",
115+
x: EDITOR_GRAPH_WIDTH / 2 - 64 + 128,
116+
y: EDITOR_GRAPH_HEIGHT / 2 - 64 + 128,
117+
parameters: {
118+
targetTexture: PbrTargetTextureType.Roughness,
58119
},
59120
textureSize: 2048,
60121
textureFilterMethod: TextureFilterMethod.Linear,
@@ -66,6 +127,14 @@ export function createDefaultMaterial(): Material {
66127
from: [0, "color"],
67128
to: [1, "color"],
68129
},
130+
{
131+
from: [2, "color"],
132+
to: [3, "color"],
133+
},
134+
{
135+
from: [4, "color"],
136+
to: [5, "color"],
137+
},
69138
],
70139
};
71140
}

packages/renderer/command-queue.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { RenderWorkerCommand, RenderWorkerResponse } from "./commands";
2+
import { RenderWorker } from "./engine";
3+
4+
type RenderWorkerResponseHandler = (response?: RenderWorkerResponse) => void;
5+
6+
type RenderCommandQueueEntry = {
7+
command: RenderWorkerCommand;
8+
transfer: Array<Transferable>;
9+
responseHandler?: RenderWorkerResponseHandler;
10+
};
11+
12+
export default function createRenderCommandQueue(worker: RenderWorker) {
13+
let queue = new Array<RenderCommandQueueEntry>();
14+
let waitingForResponse = false;
15+
16+
function processQueue() {
17+
if (waitingForResponse || queue.length === 0) {
18+
return;
19+
}
20+
21+
waitingForResponse = true;
22+
23+
const entry = queue.shift()!;
24+
if (entry.responseHandler) {
25+
worker.onmessage = (ev: MessageEvent<RenderWorkerResponse>) => {
26+
entry.responseHandler?.(ev.data);
27+
28+
waitingForResponse = false;
29+
processQueue();
30+
};
31+
32+
worker.postMessage(entry.command, entry.transfer);
33+
} else {
34+
worker.onmessage = null;
35+
worker.postMessage(entry.command, entry.transfer);
36+
37+
waitingForResponse = false;
38+
processQueue();
39+
}
40+
}
41+
42+
return {
43+
enqueue(
44+
command: RenderWorkerCommand,
45+
transfer: Array<Transferable> = [],
46+
responseHandler?: RenderWorkerResponseHandler,
47+
) {
48+
queue.push({ command, transfer, responseHandler });
49+
processQueue();
50+
},
51+
52+
enqueueAndWaitForResponse(
53+
command: RenderWorkerCommand,
54+
transfer: Array<Transferable> = [],
55+
) {
56+
return new Promise((resolve) => {
57+
this.enqueue(command, transfer, resolve);
58+
});
59+
},
60+
61+
clear() {
62+
queue = [];
63+
},
64+
};
65+
}
66+
67+
export type RenderCommandQueue = NonNullable<ReturnType<typeof createRenderCommandQueue>>;

packages/renderer/engine.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import { RenderWorkerCommand, RenderWorkerResponse } from "./commands";
1212
import { Preview3dSettings } from "./preview-3d";
1313
import { MaterialNodeSnapshot, MinimalMaterialNodeSnapshot, WebGL2RenderWorker } from "./types";
1414
import WebGL2RenderWorkerImpl from "./webgl2/worker?worker";
15+
import createRenderCommandQueue, { RenderCommandQueue } from "./command-queue";
1516

1617
// A re-type of `Worker`, because the original doesn't support specifying message type...
17-
interface RenderWorker extends Omit<Worker, "postMessage"> {
18+
export interface RenderWorker extends Omit<Worker, "postMessage"> {
1819
postMessage(command: RenderWorkerCommand): void;
1920
postMessage(command: RenderWorkerCommand, transfer: Transferable[]): void;
2021
postMessage(command: RenderWorkerCommand, options?: StructuredSerializeOptions): void;
@@ -36,18 +37,7 @@ export const [RenderEngineProvider, useRenderEngine] = createContextProvider(()
3637
const materialStore = useMaterialStore()!;
3738
const blueprintStore = useNodeBlueprintsStore()!;
3839
let worker: RenderWorker | undefined;
39-
40-
function sendCommandAndWaitForResponse(command: RenderWorkerCommand, transfer: Transferable[]) {
41-
return new Promise((resolve) => {
42-
if (worker) {
43-
worker.onmessage = (ev: MessageEvent<RenderWorkerResponse>) => {
44-
resolve(ev.data);
45-
};
46-
47-
worker.postMessage(command, transfer);
48-
}
49-
});
50-
}
40+
let commandQueue: RenderCommandQueue | undefined;
5141

5242
function createMinimalNodeSnapshot(node: MaterialNode): MinimalMaterialNodeSnapshot {
5343
return {
@@ -63,31 +53,31 @@ export const [RenderEngineProvider, useRenderEngine] = createContextProvider(()
6353
}
6454

6555
function onNodeAdded(ev: MaterialNodeEvent) {
66-
worker?.postMessage({
56+
commandQueue?.enqueue({
6757
command: "synchronizeNode",
6858
nodeId: ev.node.id,
6959
nodeSnapshot: createNodeSnapshot(ev.node),
7060
});
7161
}
7262

7363
function onNodeRemoved(ev: MaterialNodeEvent) {
74-
worker?.postMessage({
64+
commandQueue?.enqueue({
7565
command: "synchronizeNode",
7666
nodeId: ev.node.id,
7767
nodeSnapshot: null,
7868
});
7969
}
8070

8171
function onNodeChanged(ev: MaterialNodeEvent) {
82-
worker?.postMessage({
72+
commandQueue?.enqueue({
8373
command: "synchronizeNode",
8474
nodeId: ev.node.id,
8575
nodeSnapshot: createMinimalNodeSnapshot(ev.node),
8676
});
8777
}
8878

8979
function onEdgesChanged(ev: MaterialNodeEvent) {
90-
worker?.postMessage({
80+
commandQueue?.enqueue({
9181
command: "synchronizeEdges",
9282
nodeId: ev.node.id,
9383
edges: unwrap(materialStore.getMaterial().edges),
@@ -130,10 +120,12 @@ export const [RenderEngineProvider, useRenderEngine] = createContextProvider(()
130120
runScheduler: boolean,
131121
): Promise<WebGL2RenderWorker> {
132122
worker?.terminate();
123+
commandQueue?.clear();
133124

134125
worker = new WebGL2RenderWorkerImpl();
126+
commandQueue = createRenderCommandQueue(worker);
135127

136-
const response = await sendCommandAndWaitForResponse(
128+
const response = await commandQueue?.enqueueAndWaitForResponse(
137129
{
138130
command: "initialize",
139131
canvas,
@@ -160,7 +152,7 @@ export const [RenderEngineProvider, useRenderEngine] = createContextProvider(()
160152

161153
return {
162154
setEditorUITransform(x, y, scale) {
163-
worker?.postMessage({
155+
commandQueue?.enqueue({
164156
command: "setEditorUITransform",
165157
x,
166158
y,
@@ -169,7 +161,7 @@ export const [RenderEngineProvider, useRenderEngine] = createContextProvider(()
169161
},
170162

171163
setEditorUIViewportSize(width, height) {
172-
worker?.postMessage({
164+
commandQueue?.enqueue({
173165
command: "setEditorUIViewportSize",
174166
width,
175167
height,
@@ -225,8 +217,8 @@ export const [RenderEngineProvider, useRenderEngine] = createContextProvider(()
225217
*
226218
* @param canvas
227219
*/
228-
set3dPreviewCanvas(canvas: OffscreenCanvas) {
229-
worker?.postMessage(
220+
async set3dPreviewCanvas(canvas: OffscreenCanvas) {
221+
await commandQueue?.enqueueAndWaitForResponse(
230222
{
231223
command: "set3dPreviewCanvas",
232224
canvas,
@@ -235,8 +227,15 @@ export const [RenderEngineProvider, useRenderEngine] = createContextProvider(()
235227
);
236228
},
237229

238-
update3dPreviewSettings(settings: Partial<Preview3dSettings>) {
239-
worker?.postMessage({
230+
async update3dPreviewSettings(settings: Partial<Preview3dSettings>) {
231+
await commandQueue?.enqueueAndWaitForResponse({
232+
command: "set3dPreviewSettings",
233+
settings,
234+
});
235+
},
236+
237+
update3dPreviewSettingsImmediate(settings: Partial<Preview3dSettings>) {
238+
commandQueue?.enqueue({
240239
command: "set3dPreviewSettings",
241240
settings,
242241
});

packages/renderer/preview-3d.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import cubeHiRes from "../../resources/models/cube_2k.gltf?raw";
44
import planeGltf from "../../resources/models/plane.gltf?raw";
55
import plane2kGltf from "../../resources/models/plane_2k.gltf?raw";
66
import sphereGltf from "../../resources/models/sphere.gltf?raw";
7+
import meadowHdr from "../../resources/hdri/meadow_2_2k.hdr?url";
8+
import littleParisUnderTowerHdr from "../../resources/hdri/little_paris_under_tower_2k.hdr?url";
9+
import wastelandCloudsHdr from "../../resources/hdri/wasteland_clouds_puresky_2k.hdr?url";
10+
import rustigKoppieHdr from "../../resources/hdri/rustig_koppie_puresky_2k.hdr?url";
11+
712
import glm from "gl-matrix";
813

914
export enum Preview3dShape {
@@ -26,10 +31,34 @@ export function get3dPreviewShapeGltf(shape: Preview3dShape): GLTF.IGLTF {
2631
return JSON.parse(SHAPE_TO_GLTF[shape]);
2732
}
2833

34+
export enum Preview3dEnvironmentMap {
35+
// https://polyhaven.com/a/wasteland_clouds_puresky
36+
WastelandClouds,
37+
// https://polyhaven.com/a/meadow_2
38+
Meadow,
39+
// https://polyhaven.com/a/little_paris_under_tower
40+
LittleParisUnderTower,
41+
// https://polyhaven.com/a/rustig_koppie_puresky
42+
RustigKoppie,
43+
}
44+
45+
export function get3dPreviewEnvironmentMapUrl(map: Preview3dEnvironmentMap): string {
46+
const MAP: Record<Preview3dEnvironmentMap, string> = {
47+
[Preview3dEnvironmentMap.WastelandClouds]: wastelandCloudsHdr,
48+
[Preview3dEnvironmentMap.Meadow]: meadowHdr,
49+
[Preview3dEnvironmentMap.LittleParisUnderTower]: littleParisUnderTowerHdr,
50+
[Preview3dEnvironmentMap.RustigKoppie]: rustigKoppieHdr,
51+
};
52+
53+
return MAP[map];
54+
}
55+
2956
export type Preview3dSettings = {
3057
shape: GLTF.IGLTF;
58+
environmentMapUrl: string;
3159
viewportWidth: number;
3260
viewportHeight: number;
33-
cameraTransform: glm.mat4;
61+
cameraProjection: glm.mat4;
62+
cameraView: glm.mat4;
3463
cameraPosition: glm.vec3;
3564
};

0 commit comments

Comments
 (0)