@framefields/node-displacement-map 0.0.0-stage → 2.0.3
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/.turbo/turbo-build.log +21 -0
- package/CHANGELOG.md +25 -0
- package/LICENCE +202 -0
- package/dist/index.d.mts +70 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +54 -0
- package/dist/index.mjs.map +1 -0
- package/dist/renderer.d.mts +7 -0
- package/dist/renderer.d.mts.map +1 -0
- package/dist/renderer.mjs +3 -0
- package/dist/renderers-Dm9Btn8h.mjs +390 -0
- package/dist/renderers-Dm9Btn8h.mjs.map +1 -0
- package/package.json +43 -3
- package/src/index.ts +2 -0
- package/src/renderers/index.ts +6 -0
- package/src/renderers/webgpu-renderer.ts +631 -0
- package/src/shared/config.ts +67 -0
- package/src/shared/index.ts +1 -0
- package/tsconfig.json +12 -0
- package/tsdown.config.ts +13 -0
- package/README.md +0 -3
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { defineRenderer } from "@framefields/node-sdk/renderer";
|
|
2
|
+
import { signalRegistry } from "@framefields/webgpu-renderers";
|
|
3
|
+
|
|
4
|
+
//#region src/renderers/webgpu-renderer.ts
|
|
5
|
+
const STRENGTH_MIN = 0;
|
|
6
|
+
const STRENGTH_MAX = 500;
|
|
7
|
+
function clamp(value, min, max) {
|
|
8
|
+
return Math.min(max, Math.max(min, value));
|
|
9
|
+
}
|
|
10
|
+
const DISPLACEMENT_MAP_SHADER = `
|
|
11
|
+
struct DisplacementUniforms {
|
|
12
|
+
strengthX : f32,
|
|
13
|
+
strengthY : f32,
|
|
14
|
+
xChannel : f32, // 0=R, 1=G, 2=B, 3=A, 4=Luma
|
|
15
|
+
yChannel : f32, // 0=R, 1=G, 2=B, 3=A, 4=Luma
|
|
16
|
+
|
|
17
|
+
wrapMode : f32, // 0=Clamp, 1=Repeat, 2=Mirror
|
|
18
|
+
hasSignalX : f32,
|
|
19
|
+
hasSignalY : f32,
|
|
20
|
+
_pad : f32,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
@group(0) @binding(0) var<uniform> u : DisplacementUniforms;
|
|
24
|
+
@group(1) @binding(0) var srcTex : texture_2d<f32>;
|
|
25
|
+
@group(1) @binding(1) var srcSamp : sampler;
|
|
26
|
+
@group(2) @binding(0) var mapTex : texture_2d<f32>;
|
|
27
|
+
@group(2) @binding(1) var mapSamp : sampler;
|
|
28
|
+
@group(3) @binding(0) var sigXTex : texture_2d<f32>;
|
|
29
|
+
@group(3) @binding(1) var sigYTex : texture_2d<f32>;
|
|
30
|
+
@group(3) @binding(2) var sigSamp : sampler;
|
|
31
|
+
|
|
32
|
+
struct VSOut {
|
|
33
|
+
@builtin(position) pos : vec4<f32>,
|
|
34
|
+
@location(0) uv : vec2<f32>,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
@vertex fn vs(@builtin(vertex_index) vi: u32) -> VSOut {
|
|
38
|
+
var pos = array<vec2<f32>, 4>(
|
|
39
|
+
vec2<f32>(-1.0, 1.0),
|
|
40
|
+
vec2<f32>(1.0, 1.0),
|
|
41
|
+
vec2<f32>(-1.0, -1.0),
|
|
42
|
+
vec2<f32>(1.0, -1.0)
|
|
43
|
+
);
|
|
44
|
+
var uv = array<vec2<f32>, 4>(
|
|
45
|
+
vec2<f32>(0.0, 0.0),
|
|
46
|
+
vec2<f32>(1.0, 0.0),
|
|
47
|
+
vec2<f32>(0.0, 1.0),
|
|
48
|
+
vec2<f32>(1.0, 1.0)
|
|
49
|
+
);
|
|
50
|
+
return VSOut(vec4<f32>(pos[vi], 0.0, 1.0), uv[vi]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn extractChannel(color: vec4<f32>, channel: f32) -> f32 {
|
|
54
|
+
if (channel < 0.5) { return color.r; }
|
|
55
|
+
if (channel < 1.5) { return color.g; }
|
|
56
|
+
if (channel < 2.5) { return color.b; }
|
|
57
|
+
if (channel < 3.5) { return color.a; }
|
|
58
|
+
// Luminance
|
|
59
|
+
return dot(color.rgb, vec3<f32>(0.299, 0.587, 0.114));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fn applyWrap(coord: f32, mode: f32) -> f32 {
|
|
63
|
+
if (mode < 0.5) {
|
|
64
|
+
// Clamp
|
|
65
|
+
return clamp(coord, 0.0, 1.0);
|
|
66
|
+
}
|
|
67
|
+
if (mode < 1.5) {
|
|
68
|
+
// Repeat
|
|
69
|
+
return fract(coord);
|
|
70
|
+
}
|
|
71
|
+
// Mirror
|
|
72
|
+
let t = fract(coord * 0.5) * 2.0;
|
|
73
|
+
if (t > 1.0) {
|
|
74
|
+
return 2.0 - t;
|
|
75
|
+
}
|
|
76
|
+
return t;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {
|
|
80
|
+
let mapColor = textureSampleLevel(mapTex, mapSamp, in.uv, 0.0);
|
|
81
|
+
|
|
82
|
+
// Extract displacement values from chosen channels, centered around 0.5 and gated by alpha
|
|
83
|
+
// NOTE (P3, not changed here): this yields a max offset of strength/2 px at
|
|
84
|
+
// either extreme (peak-to-peak swing == strength). Confirm with design whether
|
|
85
|
+
// "strength in pixels" should mean peak amplitude instead — see review doc.
|
|
86
|
+
let dispX = (extractChannel(mapColor, u.xChannel) - 0.5) * mapColor.a;
|
|
87
|
+
let dispY = (extractChannel(mapColor, u.yChannel) - 0.5) * mapColor.a;
|
|
88
|
+
|
|
89
|
+
// Get strength, potentially from signal textures
|
|
90
|
+
var sX = u.strengthX;
|
|
91
|
+
var sY = u.strengthY;
|
|
92
|
+
if (u.hasSignalX > 0.5) {
|
|
93
|
+
sX = textureSampleLevel(sigXTex, sigSamp, in.uv, 0.0).r;
|
|
94
|
+
}
|
|
95
|
+
if (u.hasSignalY > 0.5) {
|
|
96
|
+
sY = textureSampleLevel(sigYTex, sigSamp, in.uv, 0.0).r;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Compute displacement in UV space (strength is in pixels, convert to UV)
|
|
100
|
+
let srcDims = vec2<f32>(textureDimensions(srcTex));
|
|
101
|
+
let offset = vec2<f32>(
|
|
102
|
+
dispX * sX / srcDims.x,
|
|
103
|
+
dispY * sY / srcDims.y,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
let displaced = vec2<f32>(
|
|
107
|
+
applyWrap(in.uv.x + offset.x, u.wrapMode),
|
|
108
|
+
applyWrap(in.uv.y + offset.y, u.wrapMode),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
return textureSampleLevel(srcTex, srcSamp, displaced, 0.0);
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
const deviceResourceCache = /* @__PURE__ */ new WeakMap();
|
|
115
|
+
const uniformData = new Float32Array(8);
|
|
116
|
+
function getDeviceLayouts(device) {
|
|
117
|
+
let res = deviceResourceCache.get(device);
|
|
118
|
+
if (res) return res;
|
|
119
|
+
res = {
|
|
120
|
+
uniformLayout: device.createBindGroupLayout({ entries: [{
|
|
121
|
+
binding: 0,
|
|
122
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
123
|
+
buffer: { type: "uniform" }
|
|
124
|
+
}] }),
|
|
125
|
+
srcTextureLayout: device.createBindGroupLayout({ entries: [{
|
|
126
|
+
binding: 0,
|
|
127
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
128
|
+
texture: { sampleType: "float" }
|
|
129
|
+
}, {
|
|
130
|
+
binding: 1,
|
|
131
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
132
|
+
sampler: { type: "filtering" }
|
|
133
|
+
}] }),
|
|
134
|
+
mapTextureLayout: device.createBindGroupLayout({ entries: [{
|
|
135
|
+
binding: 0,
|
|
136
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
137
|
+
texture: { sampleType: "float" }
|
|
138
|
+
}, {
|
|
139
|
+
binding: 1,
|
|
140
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
141
|
+
sampler: { type: "filtering" }
|
|
142
|
+
}] }),
|
|
143
|
+
signalTextureLayout: device.createBindGroupLayout({ entries: [
|
|
144
|
+
{
|
|
145
|
+
binding: 0,
|
|
146
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
147
|
+
texture: { sampleType: "float" }
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
binding: 1,
|
|
151
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
152
|
+
texture: { sampleType: "float" }
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
binding: 2,
|
|
156
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
157
|
+
sampler: { type: "filtering" }
|
|
158
|
+
}
|
|
159
|
+
] }),
|
|
160
|
+
pipelineCache: /* @__PURE__ */ new Map()
|
|
161
|
+
};
|
|
162
|
+
deviceResourceCache.set(device, res);
|
|
163
|
+
return res;
|
|
164
|
+
}
|
|
165
|
+
function getDisplacementResources(device, format) {
|
|
166
|
+
const layouts = getDeviceLayouts(device);
|
|
167
|
+
const cacheKey = `displacement_${format}`;
|
|
168
|
+
let pipeline = layouts.pipelineCache.get(cacheKey);
|
|
169
|
+
if (!pipeline) {
|
|
170
|
+
const module = device.createShaderModule({
|
|
171
|
+
label: `displacement_${format}.wgsl`,
|
|
172
|
+
code: DISPLACEMENT_MAP_SHADER
|
|
173
|
+
});
|
|
174
|
+
pipeline = device.createRenderPipeline({
|
|
175
|
+
label: `DisplacementMapPipeline_${format}`,
|
|
176
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [
|
|
177
|
+
layouts.uniformLayout,
|
|
178
|
+
layouts.srcTextureLayout,
|
|
179
|
+
layouts.mapTextureLayout,
|
|
180
|
+
layouts.signalTextureLayout
|
|
181
|
+
] }),
|
|
182
|
+
vertex: {
|
|
183
|
+
module,
|
|
184
|
+
entryPoint: "vs"
|
|
185
|
+
},
|
|
186
|
+
fragment: {
|
|
187
|
+
module,
|
|
188
|
+
entryPoint: "fs",
|
|
189
|
+
targets: [{ format }]
|
|
190
|
+
},
|
|
191
|
+
primitive: { topology: "triangle-strip" }
|
|
192
|
+
});
|
|
193
|
+
layouts.pipelineCache.set(cacheKey, pipeline);
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
pipeline,
|
|
197
|
+
uniformLayout: layouts.uniformLayout,
|
|
198
|
+
srcTextureLayout: layouts.srcTextureLayout,
|
|
199
|
+
mapTextureLayout: layouts.mapTextureLayout,
|
|
200
|
+
signalTextureLayout: layouts.signalTextureLayout
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function channelToFloat(ch) {
|
|
204
|
+
switch (ch) {
|
|
205
|
+
case "Green": return 1;
|
|
206
|
+
case "Blue": return 2;
|
|
207
|
+
case "Alpha": return 3;
|
|
208
|
+
case "Luminance": return 4;
|
|
209
|
+
default: return 0;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function wrapModeToFloat(mode) {
|
|
213
|
+
switch (mode) {
|
|
214
|
+
case "Repeat": return 1;
|
|
215
|
+
case "Mirror": return 2;
|
|
216
|
+
default: return 0;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function resolveSignalField(op, fieldName, defaultValue) {
|
|
220
|
+
const handleId = op[`${fieldName}HandleId`];
|
|
221
|
+
const signalInput = handleId ? op.inputs?.[handleId] : null;
|
|
222
|
+
const hasSignal = !!(signalInput?.connectionValid && signalInput.outputItem?.type === "Signal");
|
|
223
|
+
const sd = hasSignal ? signalInput?.outputItem?.data : null;
|
|
224
|
+
let value = defaultValue;
|
|
225
|
+
if (!hasSignal) if (signalInput?.connectionValid && signalInput.outputItem?.type === "Number") value = clamp(Number(signalInput.outputItem.data ?? defaultValue), STRENGTH_MIN, STRENGTH_MAX);
|
|
226
|
+
else value = Number(op[fieldName] ?? defaultValue);
|
|
227
|
+
else if (sd && typeof sd === "object" && "offset" in sd) value = Number(sd.offset ?? defaultValue);
|
|
228
|
+
return {
|
|
229
|
+
hasSignal,
|
|
230
|
+
sd,
|
|
231
|
+
value
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const DisplacementMapWebGPURenderer = async (args) => {
|
|
235
|
+
const { ctx, encoder, pass, targetView, targetTexture, targetWidth, targetHeight, props, drawChild } = args;
|
|
236
|
+
const op = props.virtualMedia?.operation;
|
|
237
|
+
if (op?.op !== "DisplacementMap" || !op) return;
|
|
238
|
+
const sourceMedia = props.virtualMedia.children?.[0];
|
|
239
|
+
const mapMedia = op.mapMedia;
|
|
240
|
+
if (!sourceMedia || !mapMedia) return;
|
|
241
|
+
pass.end();
|
|
242
|
+
const width = targetWidth;
|
|
243
|
+
const height = targetHeight;
|
|
244
|
+
const frame = props.frame ?? 0;
|
|
245
|
+
const fps = props.fps || 30;
|
|
246
|
+
const strengthXInfo = resolveSignalField(op, "strengthX", 50);
|
|
247
|
+
const strengthYInfo = resolveSignalField(op, "strengthY", 50);
|
|
248
|
+
const srcTex = ctx.renderer.getTemporaryTexture(width, height, [...props.excludeTextures || [], targetTexture]);
|
|
249
|
+
const srcView = srcTex.createView();
|
|
250
|
+
ctx.renderer.beginFrame(encoder, srcView, {
|
|
251
|
+
r: 0,
|
|
252
|
+
g: 0,
|
|
253
|
+
b: 0,
|
|
254
|
+
a: 0
|
|
255
|
+
}, width, height, "clear").end();
|
|
256
|
+
ctx.renderer.pushScissor({
|
|
257
|
+
x: 0,
|
|
258
|
+
y: 0,
|
|
259
|
+
width,
|
|
260
|
+
height
|
|
261
|
+
});
|
|
262
|
+
ctx.renderer.pushIdentity();
|
|
263
|
+
await drawChild(sourceMedia, {
|
|
264
|
+
...props,
|
|
265
|
+
virtualMedia: sourceMedia,
|
|
266
|
+
renderId: `${props.renderId}-c0`,
|
|
267
|
+
excludeTextures: [...props.excludeTextures || [], srcTex]
|
|
268
|
+
}, srcView, srcTex, width, height);
|
|
269
|
+
ctx.renderer.popTransform();
|
|
270
|
+
ctx.renderer.popScissor();
|
|
271
|
+
args.pass.end();
|
|
272
|
+
const mapTex = ctx.renderer.getTemporaryTexture(width, height, [
|
|
273
|
+
...props.excludeTextures || [],
|
|
274
|
+
targetTexture,
|
|
275
|
+
srcTex
|
|
276
|
+
]);
|
|
277
|
+
const mapView = mapTex.createView();
|
|
278
|
+
ctx.renderer.beginFrame(encoder, mapView, {
|
|
279
|
+
r: 0,
|
|
280
|
+
g: 0,
|
|
281
|
+
b: 0,
|
|
282
|
+
a: 0
|
|
283
|
+
}, width, height, "clear").end();
|
|
284
|
+
ctx.renderer.pushScissor({
|
|
285
|
+
x: 0,
|
|
286
|
+
y: 0,
|
|
287
|
+
width,
|
|
288
|
+
height
|
|
289
|
+
});
|
|
290
|
+
ctx.renderer.pushIdentity();
|
|
291
|
+
await drawChild(mapMedia, {
|
|
292
|
+
...props,
|
|
293
|
+
virtualMedia: mapMedia,
|
|
294
|
+
renderId: `${props.renderId}-c1`,
|
|
295
|
+
excludeTextures: [
|
|
296
|
+
...props.excludeTextures || [],
|
|
297
|
+
srcTex,
|
|
298
|
+
mapTex
|
|
299
|
+
]
|
|
300
|
+
}, mapView, mapTex, width, height);
|
|
301
|
+
ctx.renderer.popTransform();
|
|
302
|
+
ctx.renderer.popScissor();
|
|
303
|
+
args.pass.end();
|
|
304
|
+
const { pipeline, uniformLayout: uLayout, srcTextureLayout: srcLayout, mapTextureLayout: mapLayout, signalTextureLayout: sigLayout } = getDisplacementResources(ctx.device, ctx.renderer.format);
|
|
305
|
+
const sampler = ctx.renderer.samplerCache.getSampler(ctx.device);
|
|
306
|
+
let sigXView;
|
|
307
|
+
let sigYView;
|
|
308
|
+
const elapsedSeconds = props.elapsedMs !== void 0 ? props.elapsedMs / 1e3 : frame / fps;
|
|
309
|
+
const durationSeconds = props.virtualMedia?.metadata?.durationMs ? props.virtualMedia.metadata.durationMs / 1e3 : props.durationMs !== void 0 ? props.durationMs / 1e3 : 0;
|
|
310
|
+
if (strengthXInfo.hasSignal && strengthXInfo.sd) sigXView = signalRegistry.getOrCreate2DTextureView(ctx.device, encoder, strengthXInfo.sd.nodeId ?? "displace_sx_sig", elapsedSeconds, durationSeconds, strengthXInfo.sd, width, height, props.renderId, frame, fps);
|
|
311
|
+
else sigXView = signalRegistry.getDummy1x1TextureView(ctx.device);
|
|
312
|
+
if (strengthYInfo.hasSignal && strengthYInfo.sd) sigYView = signalRegistry.getOrCreate2DTextureView(ctx.device, encoder, strengthYInfo.sd.nodeId ?? "displace_sy_sig", elapsedSeconds, durationSeconds, strengthYInfo.sd, width, height, props.renderId, frame, fps);
|
|
313
|
+
else sigYView = signalRegistry.getDummy1x1TextureView(ctx.device);
|
|
314
|
+
uniformData[0] = strengthXInfo.value;
|
|
315
|
+
uniformData[1] = strengthYInfo.value;
|
|
316
|
+
uniformData[2] = channelToFloat(op.xChannel);
|
|
317
|
+
uniformData[3] = channelToFloat(op.yChannel);
|
|
318
|
+
uniformData[4] = wrapModeToFloat(op.wrapMode);
|
|
319
|
+
uniformData[5] = strengthXInfo.hasSignal ? 1 : 0;
|
|
320
|
+
uniformData[6] = strengthYInfo.hasSignal ? 1 : 0;
|
|
321
|
+
uniformData[7] = 0;
|
|
322
|
+
const uniformBuffer = ctx.renderer.getTemporaryBuffer(uniformData);
|
|
323
|
+
const outTex = ctx.renderer.getTemporaryTexture(width, height, [
|
|
324
|
+
srcTex,
|
|
325
|
+
mapTex,
|
|
326
|
+
targetTexture,
|
|
327
|
+
...props.excludeTextures || []
|
|
328
|
+
]);
|
|
329
|
+
const renderPass = encoder.beginRenderPass({ colorAttachments: [{
|
|
330
|
+
view: outTex.createView(),
|
|
331
|
+
loadOp: "clear",
|
|
332
|
+
storeOp: "store",
|
|
333
|
+
clearValue: {
|
|
334
|
+
r: 0,
|
|
335
|
+
g: 0,
|
|
336
|
+
b: 0,
|
|
337
|
+
a: 0
|
|
338
|
+
}
|
|
339
|
+
}] });
|
|
340
|
+
renderPass.setPipeline(pipeline);
|
|
341
|
+
renderPass.setBindGroup(0, ctx.device.createBindGroup({
|
|
342
|
+
layout: uLayout,
|
|
343
|
+
entries: [{
|
|
344
|
+
binding: 0,
|
|
345
|
+
resource: { buffer: uniformBuffer }
|
|
346
|
+
}]
|
|
347
|
+
}));
|
|
348
|
+
renderPass.setBindGroup(1, ctx.renderer.bindGroupCache.getBindGroup(ctx.device, srcLayout, srcTex, sampler));
|
|
349
|
+
renderPass.setBindGroup(2, ctx.renderer.bindGroupCache.getBindGroup(ctx.device, mapLayout, mapTex, sampler));
|
|
350
|
+
renderPass.setBindGroup(3, ctx.device.createBindGroup({
|
|
351
|
+
layout: sigLayout,
|
|
352
|
+
entries: [
|
|
353
|
+
{
|
|
354
|
+
binding: 0,
|
|
355
|
+
resource: sigXView
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
binding: 1,
|
|
359
|
+
resource: sigYView
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
binding: 2,
|
|
363
|
+
resource: sampler
|
|
364
|
+
}
|
|
365
|
+
]
|
|
366
|
+
}));
|
|
367
|
+
renderPass.draw(4);
|
|
368
|
+
renderPass.end();
|
|
369
|
+
const finalPass = ctx.renderer.beginFrame(encoder, targetView, {
|
|
370
|
+
r: 0,
|
|
371
|
+
g: 0,
|
|
372
|
+
b: 0,
|
|
373
|
+
a: 0
|
|
374
|
+
}, targetWidth, targetHeight, "load");
|
|
375
|
+
ctx.renderer.drawTexture(finalPass, outTex, {
|
|
376
|
+
x: 0,
|
|
377
|
+
y: 0,
|
|
378
|
+
width: targetWidth,
|
|
379
|
+
height: targetHeight
|
|
380
|
+
}, { opacity: op.opacity ?? 1 });
|
|
381
|
+
args.pass = finalPass;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
//#endregion
|
|
385
|
+
//#region src/renderers/index.ts
|
|
386
|
+
var renderers_default = defineRenderer({ WebGPURenderer: DisplacementMapWebGPURenderer });
|
|
387
|
+
|
|
388
|
+
//#endregion
|
|
389
|
+
export { renderers_default as t };
|
|
390
|
+
//# sourceMappingURL=renderers-Dm9Btn8h.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderers-Dm9Btn8h.mjs","names":[],"sources":["../src/renderers/webgpu-renderer.ts","../src/renderers/index.ts"],"sourcesContent":["/// <reference types=\"webgpu\" />\nimport type { SignalData, VirtualMediaData } from \"@framefields/core\";\nimport type { WebGPUNodeRenderer } from \"@framefields/node-sdk\";\nimport { signalRegistry } from \"@framefields/webgpu-renderers\";\n\n// FIX (P1): `mapMedia` is now a first-class, typed field instead of being\n// reached via `(operation as any).mapMedia`. Replace `unknown` with the\n// SDK's actual virtual-media child type if one is exported.\ninterface DisplacementMapOp {\n\top: \"DisplacementMap\";\n\tstrengthX?: number;\n\tstrengthY?: number;\n\txChannel?: \"Red\" | \"Green\" | \"Blue\" | \"Alpha\" | \"Luminance\";\n\tyChannel?: \"Red\" | \"Green\" | \"Blue\" | \"Alpha\" | \"Luminance\";\n\twrapMode?: \"Clamp\" | \"Repeat\" | \"Mirror\";\n\topacity?: number;\n\tmapMedia?: VirtualMediaData | null;\n\n\tstrengthXHandleId?: string | null;\n\tstrengthYHandleId?: string | null;\n\n\tinputs?: Record<\n\t\tstring,\n\t\t{\n\t\t\tconnectionValid: boolean;\n\t\t\toutputItem: {\n\t\t\t\ttype: string;\n\t\t\t\tdata: unknown;\n\t\t\t} | null;\n\t\t}\n\t>;\n}\n\ntype DisplacementSignalData = SignalData & { nodeId?: string };\n\n// Schema bounds mirrored from displacementMapConfig.ts (`z.number().min(0).max(500)`).\n// FIX (P2): kept local rather than imported to avoid pulling zod/config-builder\n// into the render path; ideally these are exported as shared constants from\n// displacementMapConfig.ts so the two can't drift.\nconst STRENGTH_MIN = 0;\nconst STRENGTH_MAX = 500;\n\nfunction clamp(value: number, min: number, max: number): number {\n\treturn Math.min(max, Math.max(min, value));\n}\n\nconst DISPLACEMENT_MAP_SHADER = `\nstruct DisplacementUniforms {\n strengthX : f32,\n strengthY : f32,\n xChannel : f32, // 0=R, 1=G, 2=B, 3=A, 4=Luma\n yChannel : f32, // 0=R, 1=G, 2=B, 3=A, 4=Luma\n\n wrapMode : f32, // 0=Clamp, 1=Repeat, 2=Mirror\n hasSignalX : f32,\n hasSignalY : f32,\n _pad : f32,\n};\n\n@group(0) @binding(0) var<uniform> u : DisplacementUniforms;\n@group(1) @binding(0) var srcTex : texture_2d<f32>;\n@group(1) @binding(1) var srcSamp : sampler;\n@group(2) @binding(0) var mapTex : texture_2d<f32>;\n@group(2) @binding(1) var mapSamp : sampler;\n@group(3) @binding(0) var sigXTex : texture_2d<f32>;\n@group(3) @binding(1) var sigYTex : texture_2d<f32>;\n@group(3) @binding(2) var sigSamp : sampler;\n\nstruct VSOut {\n @builtin(position) pos : vec4<f32>,\n @location(0) uv : vec2<f32>,\n};\n\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VSOut {\n var pos = array<vec2<f32>, 4>(\n vec2<f32>(-1.0, 1.0),\n vec2<f32>(1.0, 1.0),\n vec2<f32>(-1.0, -1.0),\n vec2<f32>(1.0, -1.0)\n );\n var uv = array<vec2<f32>, 4>(\n vec2<f32>(0.0, 0.0),\n vec2<f32>(1.0, 0.0),\n vec2<f32>(0.0, 1.0),\n vec2<f32>(1.0, 1.0)\n );\n return VSOut(vec4<f32>(pos[vi], 0.0, 1.0), uv[vi]);\n}\n\nfn extractChannel(color: vec4<f32>, channel: f32) -> f32 {\n if (channel < 0.5) { return color.r; }\n if (channel < 1.5) { return color.g; }\n if (channel < 2.5) { return color.b; }\n if (channel < 3.5) { return color.a; }\n // Luminance\n return dot(color.rgb, vec3<f32>(0.299, 0.587, 0.114));\n}\n\nfn applyWrap(coord: f32, mode: f32) -> f32 {\n if (mode < 0.5) {\n // Clamp\n return clamp(coord, 0.0, 1.0);\n }\n if (mode < 1.5) {\n // Repeat\n return fract(coord);\n }\n // Mirror\n let t = fract(coord * 0.5) * 2.0;\n if (t > 1.0) {\n return 2.0 - t;\n }\n return t;\n}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n let mapColor = textureSampleLevel(mapTex, mapSamp, in.uv, 0.0);\n\n // Extract displacement values from chosen channels, centered around 0.5 and gated by alpha\n // NOTE (P3, not changed here): this yields a max offset of strength/2 px at\n // either extreme (peak-to-peak swing == strength). Confirm with design whether\n // \"strength in pixels\" should mean peak amplitude instead — see review doc.\n let dispX = (extractChannel(mapColor, u.xChannel) - 0.5) * mapColor.a;\n let dispY = (extractChannel(mapColor, u.yChannel) - 0.5) * mapColor.a;\n\n // Get strength, potentially from signal textures\n var sX = u.strengthX;\n var sY = u.strengthY;\n if (u.hasSignalX > 0.5) {\n sX = textureSampleLevel(sigXTex, sigSamp, in.uv, 0.0).r;\n }\n if (u.hasSignalY > 0.5) {\n sY = textureSampleLevel(sigYTex, sigSamp, in.uv, 0.0).r;\n }\n\n // Compute displacement in UV space (strength is in pixels, convert to UV)\n let srcDims = vec2<f32>(textureDimensions(srcTex));\n let offset = vec2<f32>(\n dispX * sX / srcDims.x,\n dispY * sY / srcDims.y,\n );\n\n let displaced = vec2<f32>(\n applyWrap(in.uv.x + offset.x, u.wrapMode),\n applyWrap(in.uv.y + offset.y, u.wrapMode),\n );\n\n return textureSampleLevel(srcTex, srcSamp, displaced, 0.0);\n}\n`;\n\ninterface DeviceDisplacementResources {\n\tuniformLayout: GPUBindGroupLayout;\n\tsrcTextureLayout: GPUBindGroupLayout;\n\tmapTextureLayout: GPUBindGroupLayout;\n\tsignalTextureLayout: GPUBindGroupLayout;\n\tpipelineCache: Map<string, GPURenderPipeline>;\n}\n\nconst deviceResourceCache = new WeakMap<\n\tGPUDevice,\n\tDeviceDisplacementResources\n>();\n// NOTE (P3, not changed): module-scoped and reused across every invocation\n// (including recursive/nested displacement nodes). Currently safe because\n// there is no `await` between filling this array and the synchronous\n// getTemporaryBuffer() call that consumes it — if a future refactor adds an\n// await in between, this becomes a cross-call data race. Consider making\n// this function-scoped if that invariant ever changes.\nconst uniformData = new Float32Array(8);\n\nfunction getDeviceLayouts(device: GPUDevice): DeviceDisplacementResources {\n\tlet res = deviceResourceCache.get(device);\n\tif (res) return res;\n\n\tconst uniformLayout = device.createBindGroupLayout({\n\t\tentries: [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tbuffer: { type: \"uniform\" },\n\t\t\t},\n\t\t],\n\t});\n\n\tconst srcTextureLayout = device.createBindGroupLayout({\n\t\tentries: [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\ttexture: { sampleType: \"float\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 1,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tsampler: { type: \"filtering\" },\n\t\t\t},\n\t\t],\n\t});\n\n\tconst mapTextureLayout = device.createBindGroupLayout({\n\t\tentries: [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\ttexture: { sampleType: \"float\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 1,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tsampler: { type: \"filtering\" },\n\t\t\t},\n\t\t],\n\t});\n\n\tconst signalTextureLayout = device.createBindGroupLayout({\n\t\tentries: [\n\t\t\t{\n\t\t\t\tbinding: 0,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\ttexture: { sampleType: \"float\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 1,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\ttexture: { sampleType: \"float\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tbinding: 2,\n\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\tsampler: { type: \"filtering\" },\n\t\t\t},\n\t\t],\n\t});\n\n\tres = {\n\t\tuniformLayout,\n\t\tsrcTextureLayout,\n\t\tmapTextureLayout,\n\t\tsignalTextureLayout,\n\t\tpipelineCache: new Map(),\n\t};\n\tdeviceResourceCache.set(device, res);\n\treturn res;\n}\n\nfunction getDisplacementResources(device: GPUDevice, format: GPUTextureFormat) {\n\tconst layouts = getDeviceLayouts(device);\n\n\tconst cacheKey = `displacement_${format}`;\n\tlet pipeline = layouts.pipelineCache.get(cacheKey);\n\tif (!pipeline) {\n\t\tconst module = device.createShaderModule({\n\t\t\tlabel: `displacement_${format}.wgsl`,\n\t\t\tcode: DISPLACEMENT_MAP_SHADER,\n\t\t});\n\n\t\tpipeline = device.createRenderPipeline({\n\t\t\tlabel: `DisplacementMapPipeline_${format}`,\n\t\t\tlayout: device.createPipelineLayout({\n\t\t\t\tbindGroupLayouts: [\n\t\t\t\t\tlayouts.uniformLayout,\n\t\t\t\t\tlayouts.srcTextureLayout,\n\t\t\t\t\tlayouts.mapTextureLayout,\n\t\t\t\t\tlayouts.signalTextureLayout,\n\t\t\t\t],\n\t\t\t}),\n\t\t\tvertex: { module, entryPoint: \"vs\" },\n\t\t\tfragment: {\n\t\t\t\tmodule,\n\t\t\t\tentryPoint: \"fs\",\n\t\t\t\ttargets: [{ format }],\n\t\t\t},\n\t\t\tprimitive: { topology: \"triangle-strip\" },\n\t\t});\n\t\tlayouts.pipelineCache.set(cacheKey, pipeline);\n\t}\n\n\treturn {\n\t\tpipeline,\n\t\tuniformLayout: layouts.uniformLayout,\n\t\tsrcTextureLayout: layouts.srcTextureLayout,\n\t\tmapTextureLayout: layouts.mapTextureLayout,\n\t\tsignalTextureLayout: layouts.signalTextureLayout,\n\t};\n}\n\nfunction channelToFloat(\n\tch: \"Red\" | \"Green\" | \"Blue\" | \"Alpha\" | \"Luminance\" | undefined,\n): number {\n\tswitch (ch) {\n\t\tcase \"Green\":\n\t\t\treturn 1;\n\t\tcase \"Blue\":\n\t\t\treturn 2;\n\t\tcase \"Alpha\":\n\t\t\treturn 3;\n\t\tcase \"Luminance\":\n\t\t\treturn 4;\n\t\tdefault:\n\t\t\treturn 0; // Red\n\t}\n}\n\nfunction wrapModeToFloat(\n\tmode: \"Clamp\" | \"Repeat\" | \"Mirror\" | undefined,\n): number {\n\tswitch (mode) {\n\t\tcase \"Repeat\":\n\t\t\treturn 1;\n\t\tcase \"Mirror\":\n\t\t\treturn 2;\n\t\tdefault:\n\t\t\treturn 0; // Clamp\n\t}\n}\n\nfunction resolveSignalField(\n\top: DisplacementMapOp,\n\tfieldName: \"strengthX\" | \"strengthY\",\n\tdefaultValue: number,\n): { hasSignal: boolean; sd: DisplacementSignalData | null; value: number } {\n\tconst handleIdKey = `${fieldName}HandleId` as keyof DisplacementMapOp;\n\tconst handleId = op[handleIdKey] as string | null | undefined;\n\tconst signalInput = handleId ? op.inputs?.[handleId] : null;\n\n\t// FIX (P2): the config's dataTypes are exactly [\"Number\", \"Signal\"] — there\n\t// is no \"Numeric\" type. Checking for it here made this branch either dead\n\t// code or a landmine for any output item that happened to use that string.\n\t// Only \"Signal\" should route through the per-pixel signal-texture path.\n\tconst hasSignal = !!(\n\t\tsignalInput?.connectionValid && signalInput.outputItem?.type === \"Signal\"\n\t);\n\tconst sd = hasSignal\n\t\t? (signalInput?.outputItem?.data as DisplacementSignalData | null)\n\t\t: null;\n\n\tlet value = defaultValue;\n\tif (!hasSignal) {\n\t\tif (\n\t\t\tsignalInput?.connectionValid &&\n\t\t\tsignalInput.outputItem?.type === \"Number\"\n\t\t) {\n\t\t\t// FIX (P2): clamp static Number-connection values to the same bounds\n\t\t\t// the field enforces when unbound, so binding a value behaves\n\t\t\t// consistently with typing one in directly.\n\t\t\tvalue = clamp(\n\t\t\t\tNumber(signalInput.outputItem.data ?? defaultValue),\n\t\t\t\tSTRENGTH_MIN,\n\t\t\t\tSTRENGTH_MAX,\n\t\t\t);\n\t\t} else {\n\t\t\tvalue = Number(op[fieldName] ?? defaultValue);\n\t\t}\n\t} else if (sd && typeof sd === \"object\" && \"offset\" in sd) {\n\t\tvalue = Number(sd.offset ?? defaultValue);\n\t}\n\n\treturn { hasSignal, sd, value };\n}\n\nexport const DisplacementMapWebGPURenderer: WebGPUNodeRenderer = async (\n\targs,\n) => {\n\tconst {\n\t\tctx,\n\t\tencoder,\n\t\tpass,\n\t\ttargetView,\n\t\ttargetTexture,\n\t\ttargetWidth,\n\t\ttargetHeight,\n\t\tprops,\n\t\tdrawChild,\n\t} = args;\n\n\tconst op = props.virtualMedia?.operation as DisplacementMapOp | undefined;\n\tif (op?.op !== \"DisplacementMap\" || !op) return;\n\n\tconst sourceMedia = props.virtualMedia.children?.[0];\n\t// FIX (P1): typed field access instead of `(operation as any)?.mapMedia`.\n\tconst mapMedia = op.mapMedia;\n\tif (!sourceMedia || !mapMedia) return;\n\n\t// End previous pass\n\tpass.end();\n\n\tconst width = targetWidth;\n\tconst height = targetHeight;\n\tconst frame = props.frame ?? 0;\n\tconst fps = props.fps || 30;\n\n\t// Resolve signal fields\n\tconst strengthXInfo = resolveSignalField(op, \"strengthX\", 50);\n\tconst strengthYInfo = resolveSignalField(op, \"strengthY\", 50);\n\n\t// --- Draw source child into srcTex ---\n\tconst srcTex = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t...(props.excludeTextures || []),\n\t\ttargetTexture,\n\t]);\n\tconst srcView = srcTex.createView();\n\n\tconst srcClearPass = ctx.renderer.beginFrame(\n\t\tencoder,\n\t\tsrcView,\n\t\t{ r: 0, g: 0, b: 0, a: 0 },\n\t\twidth,\n\t\theight,\n\t\t\"clear\",\n\t);\n\tsrcClearPass.end();\n\n\tctx.renderer.pushScissor({ x: 0, y: 0, width, height });\n\tctx.renderer.pushIdentity();\n\tawait drawChild(\n\t\tsourceMedia,\n\t\t{\n\t\t\t...props,\n\t\t\tvirtualMedia: sourceMedia,\n\t\t\trenderId: `${props.renderId}-c0`,\n\t\t\texcludeTextures: [...(props.excludeTextures || []), srcTex],\n\t\t},\n\t\tsrcView,\n\t\tsrcTex,\n\t\twidth,\n\t\theight,\n\t);\n\tctx.renderer.popTransform();\n\tctx.renderer.popScissor();\n\n\t// FIX (P0 - CRITICAL): drawChild leaves its own last-active pass open on\n\t// `args.pass` (targeting srcView), matching the same \"caller closes it\"\n\t// convention this function itself uses on exit (`args.pass = finalPass`\n\t// below, with no `.end()`). Without this line, the next `beginFrame` call\n\t// (for mapClearPass) opens a second render pass on the same encoder while\n\t// this one is still open, which is invalid WebGPU encoder usage — it will\n\t// throw or push the encoder into an error state that silently drops\n\t// subsequent commands. This reproduces whenever `sourceMedia` renders\n\t// through the GPU, i.e. essentially always.\n\targs.pass.end();\n\n\t// --- Draw map child into mapTex ---\n\tconst mapTex = ctx.renderer.getTemporaryTexture(width, height, [\n\t\t...(props.excludeTextures || []),\n\t\ttargetTexture,\n\t\tsrcTex,\n\t]);\n\tconst mapView = mapTex.createView();\n\n\tconst mapClearPass = ctx.renderer.beginFrame(\n\t\tencoder,\n\t\tmapView,\n\t\t{ r: 0, g: 0, b: 0, a: 0 },\n\t\twidth,\n\t\theight,\n\t\t\"clear\",\n\t);\n\tmapClearPass.end();\n\n\tctx.renderer.pushScissor({ x: 0, y: 0, width, height });\n\tctx.renderer.pushIdentity();\n\tawait drawChild(\n\t\tmapMedia,\n\t\t{\n\t\t\t...props,\n\t\t\tvirtualMedia: mapMedia,\n\t\t\trenderId: `${props.renderId}-c1`,\n\t\t\texcludeTextures: [...(props.excludeTextures || []), srcTex, mapTex],\n\t\t},\n\t\tmapView,\n\t\tmapTex,\n\t\twidth,\n\t\theight,\n\t);\n\tctx.renderer.popTransform();\n\tctx.renderer.popScissor();\n\n\t// End active pass from child drawing\n\targs.pass.end();\n\n\t// --- Fetch pipeline ---\n\tconst {\n\t\tpipeline,\n\t\tuniformLayout: uLayout,\n\t\tsrcTextureLayout: srcLayout,\n\t\tmapTextureLayout: mapLayout,\n\t\tsignalTextureLayout: sigLayout,\n\t} = getDisplacementResources(ctx.device, ctx.renderer.format);\n\n\tconst sampler = ctx.renderer.samplerCache.getSampler(ctx.device);\n\n\t// --- Signal textures ---\n\tlet sigXView: GPUTextureView;\n\tlet sigYView: GPUTextureView;\n\n\tconst elapsedSeconds =\n\t\tprops.elapsedMs !== undefined ? props.elapsedMs / 1000 : frame / fps;\n\tconst durationSeconds = props.virtualMedia?.metadata?.durationMs\n\t\t? props.virtualMedia.metadata.durationMs / 1000\n\t\t: props.durationMs !== undefined\n\t\t\t? props.durationMs / 1000\n\t\t\t: 0;\n\n\tif (strengthXInfo.hasSignal && strengthXInfo.sd) {\n\t\tsigXView = signalRegistry.getOrCreate2DTextureView(\n\t\t\tctx.device,\n\t\t\tencoder,\n\t\t\tstrengthXInfo.sd.nodeId ?? \"displace_sx_sig\",\n\t\t\telapsedSeconds,\n\t\t\tdurationSeconds,\n\t\t\tstrengthXInfo.sd,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tprops.renderId,\n\t\t\tframe,\n\t\t\tfps,\n\t\t);\n\t} else {\n\t\tsigXView = signalRegistry.getDummy1x1TextureView(ctx.device);\n\t}\n\n\tif (strengthYInfo.hasSignal && strengthYInfo.sd) {\n\t\tsigYView = signalRegistry.getOrCreate2DTextureView(\n\t\t\tctx.device,\n\t\t\tencoder,\n\t\t\tstrengthYInfo.sd.nodeId ?? \"displace_sy_sig\",\n\t\t\telapsedSeconds,\n\t\t\tdurationSeconds,\n\t\t\tstrengthYInfo.sd,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tprops.renderId,\n\t\t\tframe,\n\t\t\tfps,\n\t\t);\n\t} else {\n\t\tsigYView = signalRegistry.getDummy1x1TextureView(ctx.device);\n\t}\n\n\t// --- Fill uniforms ---\n\tuniformData[0] = strengthXInfo.value;\n\tuniformData[1] = strengthYInfo.value;\n\tuniformData[2] = channelToFloat(op.xChannel);\n\tuniformData[3] = channelToFloat(op.yChannel);\n\tuniformData[4] = wrapModeToFloat(op.wrapMode);\n\tuniformData[5] = strengthXInfo.hasSignal ? 1.0 : 0.0;\n\tuniformData[6] = strengthYInfo.hasSignal ? 1.0 : 0.0;\n\tuniformData[7] = 0.0; // padding\n\n\tconst uniformBuffer = ctx.renderer.getTemporaryBuffer(uniformData);\n\n\t// --- Output texture ---\n\tconst outTex = ctx.renderer.getTemporaryTexture(width, height, [\n\t\tsrcTex,\n\t\tmapTex,\n\t\ttargetTexture,\n\t\t...(props.excludeTextures || []),\n\t]);\n\n\t// --- Render displacement pass ---\n\tconst renderPass = encoder.beginRenderPass({\n\t\tcolorAttachments: [\n\t\t\t{\n\t\t\t\tview: outTex.createView(),\n\t\t\t\tloadOp: \"clear\",\n\t\t\t\tstoreOp: \"store\",\n\t\t\t\tclearValue: { r: 0, g: 0, b: 0, a: 0 },\n\t\t\t},\n\t\t],\n\t});\n\n\trenderPass.setPipeline(pipeline);\n\trenderPass.setBindGroup(\n\t\t0,\n\t\tctx.device.createBindGroup({\n\t\t\tlayout: uLayout,\n\t\t\tentries: [{ binding: 0, resource: { buffer: uniformBuffer } }],\n\t\t}),\n\t);\n\trenderPass.setBindGroup(\n\t\t1,\n\t\tctx.renderer.bindGroupCache.getBindGroup(\n\t\t\tctx.device,\n\t\t\tsrcLayout,\n\t\t\tsrcTex,\n\t\t\tsampler,\n\t\t),\n\t);\n\trenderPass.setBindGroup(\n\t\t2,\n\t\tctx.renderer.bindGroupCache.getBindGroup(\n\t\t\tctx.device,\n\t\t\tmapLayout,\n\t\t\tmapTex,\n\t\t\tsampler,\n\t\t),\n\t);\n\trenderPass.setBindGroup(\n\t\t3,\n\t\tctx.device.createBindGroup({\n\t\t\tlayout: sigLayout,\n\t\t\tentries: [\n\t\t\t\t{ binding: 0, resource: sigXView },\n\t\t\t\t{ binding: 1, resource: sigYView },\n\t\t\t\t{ binding: 2, resource: sampler },\n\t\t\t],\n\t\t}),\n\t);\n\n\trenderPass.draw(4);\n\trenderPass.end();\n\n\t// --- Final pass: draw result to targetView ---\n\tconst finalPass = ctx.renderer.beginFrame(\n\t\tencoder,\n\t\ttargetView,\n\t\t{ r: 0, g: 0, b: 0, a: 0 },\n\t\ttargetWidth,\n\t\ttargetHeight,\n\t\t\"load\",\n\t);\n\tctx.renderer.drawTexture(\n\t\tfinalPass,\n\t\toutTex,\n\t\t{ x: 0, y: 0, width: targetWidth, height: targetHeight },\n\t\t{ opacity: op.opacity ?? 1 },\n\t);\n\n\targs.pass = finalPass;\n};\n","import { defineRenderer } from \"@framefields/node-sdk/renderer\";\nimport { DisplacementMapWebGPURenderer } from \"./webgpu-renderer.js\";\n\nexport default defineRenderer({\n\tWebGPURenderer: DisplacementMapWebGPURenderer,\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@framefields/node-displacement-map",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "2.0.3",
|
|
4
|
+
"private": false,
|
|
5
|
+
"framefields": {
|
|
6
|
+
"type": "DisplacementMap",
|
|
7
|
+
"kind": "effect"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.mts",
|
|
14
|
+
"development": "./src/index.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"default": "./dist/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./renderer": {
|
|
19
|
+
"types": "./dist/renderer.d.mts",
|
|
20
|
+
"development": "./src/renderers/index.ts",
|
|
21
|
+
"import": "./dist/renderer.mjs",
|
|
22
|
+
"default": "./dist/renderer.mjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"zod": "^4.3.6",
|
|
27
|
+
"@framefields/core": "2.0.3",
|
|
28
|
+
"@framefields/webgpu-renderers": "2.0.3",
|
|
29
|
+
"@framefields/node-sdk": "2.0.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.1.0",
|
|
33
|
+
"tsdown": "^0.19.0",
|
|
34
|
+
"typescript": "^5.8.3",
|
|
35
|
+
"webgpu": "^0.4.0",
|
|
36
|
+
"@framefields/tsconfig": "2.0.3"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsdown",
|
|
43
|
+
"dev": "tsdown --watch",
|
|
44
|
+
"clean": "rm -rf dist .turbo"
|
|
45
|
+
}
|
|
6
46
|
}
|
package/src/index.ts
ADDED