@footgun/cobalt 0.10.0 → 0.11.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.
@@ -1,18 +1,8 @@
1
- import * as wgpuMatrix from 'wgpu-matrix'
2
1
  import getPreferredFormat from '../get-preferred-format.js'
3
- import {
4
- createLightsBuffer,
5
- destroyLightsBuffer,
6
- LIGHTS_STRUCT_DEFINITION,
7
- writeLightsBuffer,
8
- } from './lights-buffer.js'
2
+ import { LightsBuffer } from './lights-buffer.js'
3
+ import { LightsRenderer } from './lights-renderer.js'
9
4
  import * as publicAPI from './public-api.js'
10
- import {
11
- createLightsTextureMask,
12
- destroyMask,
13
- setMaskLightsCount,
14
- setMaskObstacles,
15
- } from './texture/lights-texture-mask.js'
5
+ import { Viewport } from './viewport.js'
16
6
 
17
7
  /**
18
8
  * 2D lighting and Shadows
@@ -24,34 +14,38 @@ import {
24
14
  export default {
25
15
  type: 'cobalt:light',
26
16
 
27
- onInit: async function (cobalt, node) {
28
- return init(cobalt, node)
17
+ // cobalt event handling functions
18
+
19
+ // @params Object cobalt renderer world object
20
+ // @params Object options optional data passed when initing this node
21
+ onInit: async function (cobalt, options = {}) {
22
+ return init(cobalt, options)
29
23
  },
30
24
 
31
25
  onRun: function (cobalt, node, webGpuCommandEncoder) {
26
+ // do whatever you need for this node. webgpu renderpasses, etc.
32
27
  draw(cobalt, node, webGpuCommandEncoder)
33
28
  },
34
29
 
35
30
  onDestroy: function (cobalt, node) {
31
+ // any cleanup for your node should go here (releasing textures, etc.)
36
32
  destroy(node)
37
33
  },
38
34
 
39
35
  onResize: function (cobalt, node) {
36
+ // runs when the viewport size changes (handle resizing textures, etc.)
40
37
  resize(cobalt, node)
41
38
  },
42
39
 
43
40
  onViewportPosition: function (cobalt, node) {
44
- const { viewport } = cobalt
45
- node.data.viewportTopLeft = [...viewport.position]
46
- node.data.invViewProjectionMatrix = computeInvertViewProjectionMatrix(
47
- node.data.viewportWidth,
48
- node.data.viewportHeight,
49
- node.data.viewportZoom,
50
- node.data.viewportTopLeft,
51
- )
41
+ // runs when the viewport position changes
42
+ node.data.viewport.setTopLeft(...cobalt.viewport.position)
52
43
  },
53
44
 
54
- customFunctions: { ...publicAPI },
45
+ // optional
46
+ customFunctions: {
47
+ ...publicAPI,
48
+ },
55
49
  }
56
50
 
57
51
  async function init(cobalt, node) {
@@ -60,427 +54,61 @@ async function init(cobalt, node) {
60
54
  // a 2048x2048 light texture with 4 channels (rgba) with each light lighting a 256x256 region can hold 256 lights
61
55
  const MAX_LIGHT_COUNT = 256
62
56
  const MAX_LIGHT_SIZE = 256
63
- const textureFormat = getPreferredFormat(cobalt)
64
- const resolutionPerLight = MAX_LIGHT_SIZE
65
- const filtering = 'nearest'
66
-
67
- // --- lights storage buffer ---
68
- const lightsBufferData = createLightsBuffer(device, MAX_LIGHT_COUNT)
69
-
70
- // --- lights atlas texture ---
71
- const cellsCount = MAX_LIGHT_COUNT / 4
72
- const gridSize = {
73
- x: Math.ceil(Math.sqrt(cellsCount)),
74
- y: 0,
75
- }
76
- gridSize.y = Math.ceil(cellsCount / gridSize.x)
77
-
78
- const lightTextureSize = {
79
- width: gridSize.x * resolutionPerLight,
80
- height: gridSize.y * resolutionPerLight,
81
- }
82
-
83
- const lightsTexture = device.createTexture({
84
- label: 'LightsTexture texture',
85
- size: [lightTextureSize.width, lightTextureSize.height],
86
- format: textureFormat,
87
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
88
- })
89
-
90
- const lightsTextureRenderpassDescriptor = {
91
- label: 'lights-renderer render to texture renderpass',
92
- colorAttachments: [
93
- {
94
- view: lightsTexture.createView(),
95
- clearValue: [0, 0, 0, 1],
96
- loadOp: 'load',
97
- storeOp: 'store',
98
- },
99
- ],
100
- }
101
-
102
- // descriptor object passed to initializer + mask (metadata only, no GPU resources)
103
- const lightsTextureDescriptor = {
104
- gridSize,
105
- format: textureFormat,
106
- sampleCount: 1,
107
- }
108
-
109
- // --- initializer sub-pass (radial attenuation pre-pass, baked render bundle) ---
110
- const initializerShaderModule = device.createShaderModule({
111
- label: 'LightsTextureInitializer shader module',
112
- code: `
113
- ${LIGHTS_STRUCT_DEFINITION}
114
-
115
- @group(0) @binding(0) var<storage,read> lightsBuffer: LightsBuffer;
116
-
117
- struct VertexIn {
118
- @builtin(vertex_index) vertexIndex: u32,
119
- };
120
-
121
- struct VertexOut {
122
- @builtin(position) position: vec4<f32>,
123
- @location(0) uv: vec2<f32>,
124
- };
125
-
126
- const cellsGridSizeU = vec2<u32>(${gridSize.x}, ${gridSize.y});
127
- const cellsGridSizeF = vec2<f32>(${gridSize.x}, ${gridSize.y});
128
-
129
- @vertex
130
- fn main_vertex(in: VertexIn) -> VertexOut {
131
- const corners = array<vec2<f32>, 4>(
132
- vec2<f32>(-1, -1),
133
- vec2<f32>(1, -1),
134
- vec2<f32>(-1, 1),
135
- vec2<f32>(1, 1),
136
- );
137
- let screenPosition = corners[in.vertexIndex];
138
-
139
- var out: VertexOut;
140
- out.position = vec4<f32>(screenPosition, 0.0, 1.0);
141
- out.uv = (0.5 + 0.5 * screenPosition) * cellsGridSizeF;
142
- return out;
143
- }
144
-
145
- struct FragmentOut {
146
- @location(0) color: vec4<f32>,
147
- };
148
-
149
- struct LightProperties {
150
- radius: f32,
151
- intensity: f32,
152
- attenuationLinear: f32,
153
- attenuationExp: f32,
154
- };
155
-
156
- fn get_light_properties(lightId: u32) -> LightProperties {
157
- var p: LightProperties;
158
- if (lightId < lightsBuffer.count) {
159
- let light = lightsBuffer.lights[lightId];
160
- p.radius = light.radius;
161
- p.intensity = 1.0;
162
- p.attenuationLinear = light.attenuationLinear;
163
- p.attenuationExp = light.attenuationExp;
164
- }
165
- return p;
166
- }
167
-
168
- @fragment
169
- fn main_fragment(in: VertexOut) -> FragmentOut {
170
- let cellId = vec2<u32>(in.uv);
171
- let lightIdFrom = 4u * (cellId.x + cellId.y * cellsGridSizeU.x);
172
- let p = array<LightProperties, 4>(
173
- get_light_properties(lightIdFrom + 0u),
174
- get_light_properties(lightIdFrom + 1u),
175
- get_light_properties(lightIdFrom + 2u),
176
- get_light_properties(lightIdFrom + 3u),
177
- );
178
-
179
- let localUv = fract(in.uv);
180
- let fromCenter = 2.0 * localUv - 1.0;
181
- let uvDist = distance(vec2<f32>(0.0, 0.0), fromCenter);
182
- let sizes = vec4<f32>(p[0].radius, p[1].radius, p[2].radius, p[3].radius);
183
- let d = vec4<f32>(uvDist / sizes * f32(${MAX_LIGHT_SIZE}));
184
-
185
- let intensities = vec4<f32>(p[0].intensity, p[1].intensity, p[2].intensity, p[3].intensity)
186
- * (1.0 + step(uvDist, 0.01));
187
- let attenuationL = vec4<f32>(p[0].attenuationLinear, p[1].attenuationLinear, p[2].attenuationLinear, p[3].attenuationLinear);
188
- let attenuationE = vec4<f32>(p[0].attenuationExp, p[1].attenuationExp, p[2].attenuationExp, p[3].attenuationExp);
189
-
190
- var lightIntensities = intensities / (1.0 + d * (attenuationL + d * attenuationE));
191
- lightIntensities *= cos(d * ${Math.PI / 2});
192
- lightIntensities *= step(d, vec4<f32>(1.0));
193
-
194
- var out: FragmentOut;
195
- out.color = lightIntensities;
196
- return out;
197
- }
198
- `,
199
- })
57
+ const lightsBuffer = new LightsBuffer(device, MAX_LIGHT_COUNT)
200
58
 
201
- const initializerPipeline = device.createRenderPipeline({
202
- label: 'LightsTextureInitializer renderpipeline',
203
- layout: 'auto',
204
- vertex: { module: initializerShaderModule, entryPoint: 'main_vertex' },
205
- fragment: {
206
- module: initializerShaderModule,
207
- entryPoint: 'main_fragment',
208
- targets: [{ format: textureFormat }],
59
+ const viewport = new Viewport({
60
+ viewportSize: {
61
+ width: cobalt.viewport.width,
62
+ height: cobalt.viewport.height,
209
63
  },
210
- primitive: { cullMode: 'none', topology: 'triangle-strip' },
64
+ center: cobalt.viewport.position,
65
+ zoom: cobalt.viewport.zoom,
211
66
  })
212
67
 
213
- const initializerBindgroup = device.createBindGroup({
214
- label: 'LightsTextureInitializer bindgroup',
215
- layout: initializerPipeline.getBindGroupLayout(0),
216
- entries: [{ binding: 0, resource: { buffer: lightsBufferData.lightsBufferGpu } }],
217
- })
218
-
219
- const initializerBundleEncoder = device.createRenderBundleEncoder({
220
- label: 'LightsTextureInitializer renderbundle encoder',
221
- colorFormats: [textureFormat],
222
- })
223
- initializerBundleEncoder.setPipeline(initializerPipeline)
224
- initializerBundleEncoder.setBindGroup(0, initializerBindgroup)
225
- initializerBundleEncoder.draw(4)
226
- const initializerRenderBundle = initializerBundleEncoder.finish({
227
- label: 'LightsTextureInitializer renderbundle',
228
- })
229
-
230
- // --- mask sub-pass (shadow/occlusion geometry) ---
231
- const maskData = createLightsTextureMask(
68
+ const lightsRenderer = new LightsRenderer({
232
69
  device,
233
- lightsBufferData.lightsBufferGpu,
234
- lightsTextureDescriptor,
235
- MAX_LIGHT_SIZE,
236
- )
237
-
238
- // --- final composite render pipeline ---
239
- const rendererTargetFormat = node.refs.out.data.texture.format
240
-
241
- const rendererUniformsBufferGpu = device.createBuffer({
242
- label: 'LightsRenderer uniforms buffer',
243
- size: 80,
244
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
245
- })
246
-
247
- const rendererShaderModule = device.createShaderModule({
248
- label: 'LightsRenderer shader module',
249
- code: `
250
- struct Uniforms { // align(16) size(80)
251
- invertViewMatrix: mat4x4<f32>, // offset(0) align(16) size(64)
252
- ambientLight: vec3<f32>, // offset(64) align(16) size(12)
253
- };
254
-
255
- ${LIGHTS_STRUCT_DEFINITION}
256
-
257
- @group(0) @binding(0) var<uniform> uniforms: Uniforms;
258
- @group(0) @binding(1) var<storage,read> lightsBuffer: LightsBuffer;
259
- @group(0) @binding(2) var lightsTexture: texture_2d<f32>;
260
- @group(0) @binding(3) var lightsTextureSampler: sampler;
261
-
262
- @group(1) @binding(0) var albedoTexture: texture_2d<f32>;
263
- @group(1) @binding(1) var albedoSampler: sampler;
264
-
265
- struct VertexIn {
266
- @builtin(vertex_index) vertexIndex: u32,
267
- };
268
-
269
- struct VertexOut {
270
- @builtin(position) position: vec4<f32>,
271
- @location(0) worldPosition: vec2<f32>,
272
- @location(1) uv: vec2<f32>,
273
- };
274
-
275
- @vertex
276
- fn main_vertex(in: VertexIn) -> VertexOut {
277
- const corners = array<vec2<f32>, 4>(
278
- vec2<f32>(-1, -1),
279
- vec2<f32>(1, -1),
280
- vec2<f32>(-1, 1),
281
- vec2<f32>(1, 1),
282
- );
283
- let screenPosition = corners[in.vertexIndex];
284
-
285
- var out: VertexOut;
286
- out.position = vec4<f32>(screenPosition, 0.0, 1.0);
287
- out.worldPosition = (uniforms.invertViewMatrix * out.position).xy;
288
- out.uv = 0.5 + 0.5 * screenPosition * vec2<f32>(1.0, -1.0);
289
- return out;
290
- }
291
-
292
- struct FragmentOut {
293
- @location(0) color: vec4<f32>,
294
- };
295
-
296
- const cellsGridSizeU = vec2<u32>(${gridSize.x}, ${gridSize.y});
297
- const cellsGridSizeF = vec2<f32>(${gridSize.x}, ${gridSize.y});
298
-
299
- fn sampleLightBaseIntensity(lightId: u32, localUv: vec2<f32>) -> f32 {
300
- let cellIndex = lightId / 4u;
301
- let indexInCell = lightId % 4u;
302
-
303
- let cellIdU = vec2<u32>(
304
- cellIndex % cellsGridSizeU.x,
305
- cellIndex / cellsGridSizeU.x,
306
- );
307
- let cellIdF = vec2<f32>(cellIdU);
308
- let uv = (cellIdF + localUv) / cellsGridSizeF;
309
- let uvYInverted = vec2<f32>(uv.x, 1.0 - uv.y);
310
- let sample = textureSampleLevel(lightsTexture, lightsTextureSampler, uvYInverted, 0.0);
311
- let channel = vec4<f32>(
312
- vec4<u32>(indexInCell) == vec4<u32>(0u, 1u, 2u, 3u),
313
- );
314
- return dot(sample, channel);
315
- }
316
-
317
- fn compute_lights(worldPosition: vec2<f32>) -> vec3<f32> {
318
- var color = vec3<f32>(uniforms.ambientLight);
319
-
320
- const maxUvDistance = f32(${1 - 2 / resolutionPerLight});
321
-
322
- let lightsCount = lightsBuffer.count;
323
- for (var iLight = 0u; iLight < lightsCount; iLight++) {
324
- let light = lightsBuffer.lights[iLight];
325
- let lightSize = f32(${resolutionPerLight});
326
- let relativePosition = (worldPosition - light.position) / lightSize;
327
- if (max(abs(relativePosition.x), abs(relativePosition.y)) < maxUvDistance) {
328
- let localUv = 0.5 + 0.5 * relativePosition;
329
- let lightIntensity = light.intensity * sampleLightBaseIntensity(iLight, localUv);
330
- color += lightIntensity * light.color;
331
- }
332
- }
333
-
334
- return color;
335
- }
336
-
337
- @fragment
338
- fn main_fragment(in: VertexOut) -> FragmentOut {
339
- let light = clamp(compute_lights(in.worldPosition), vec3<f32>(0.0), vec3<f32>(1.0));
340
- let albedo = textureSample(albedoTexture, albedoSampler, in.uv);
341
- let color = albedo.rgb * light;
342
-
343
- var out: FragmentOut;
344
- out.color = vec4<f32>(color, 1.0);
345
- return out;
346
- }
347
- `,
348
- })
349
-
350
- const rendererPipeline = device.createRenderPipeline({
351
- label: 'LightsRenderer renderpipeline',
352
- layout: 'auto',
353
- vertex: {
354
- module: rendererShaderModule,
355
- entryPoint: 'main_vertex',
70
+ albedo: {
71
+ view: node.refs.in.data.view,
72
+ sampler: node.refs.in.data.sampler,
356
73
  },
357
- fragment: {
358
- module: rendererShaderModule,
359
- entryPoint: 'main_fragment',
360
- targets: [{ format: rendererTargetFormat }],
361
- },
362
- primitive: {
363
- cullMode: 'none',
364
- topology: 'triangle-strip',
74
+ targetTexture: node.refs.out.data.texture,
75
+ lightsBuffer,
76
+ lightsTextureProperties: {
77
+ resolutionPerLight: MAX_LIGHT_SIZE,
78
+ maxLightSize: MAX_LIGHT_SIZE,
79
+ antialiased: false,
80
+ filtering: 'nearest',
81
+ textureFormat: getPreferredFormat(cobalt),
365
82
  },
366
83
  })
367
84
 
368
- const rendererBindgroup0 = device.createBindGroup({
369
- label: 'LightsRenderer bindgroup 0',
370
- layout: rendererPipeline.getBindGroupLayout(0),
371
- entries: [
372
- {
373
- binding: 0,
374
- resource: { buffer: rendererUniformsBufferGpu },
375
- },
376
- {
377
- binding: 1,
378
- resource: { buffer: lightsBufferData.lightsBufferGpu },
379
- },
380
- {
381
- binding: 2,
382
- resource: lightsTexture.createView({ label: 'LightsRenderer lightsTexture view' }),
383
- },
384
- {
385
- binding: 3,
386
- resource: device.createSampler({
387
- label: 'LightsRenderer sampler',
388
- addressModeU: 'clamp-to-edge',
389
- addressModeV: 'clamp-to-edge',
390
- magFilter: filtering,
391
- minFilter: filtering,
392
- }),
393
- },
394
- ],
395
- })
396
-
397
- const rendererBindgroup1 = buildRendererBindgroup1(device, rendererPipeline, {
398
- view: node.refs.in.data.view,
399
- sampler: node.refs.in.data.sampler,
400
- })
401
-
402
- // viewport
403
- const viewportTopLeft = [...cobalt.viewport.position]
404
- const invViewProjectionMatrix = computeInvertViewProjectionMatrix(
405
- cobalt.viewport.width,
406
- cobalt.viewport.height,
407
- cobalt.viewport.zoom,
408
- viewportTopLeft,
409
- )
410
-
411
- const data = {
412
- // lights storage buffer
413
- ...lightsBufferData,
414
-
415
- // lights atlas texture
416
- lightsTexture,
417
- lightsTextureRenderpassDescriptor,
418
- gridSize,
419
-
420
- // initializer sub-pass
421
- initializerPipeline,
422
- initializerBindgroup,
423
- initializerRenderBundle,
424
-
425
- // mask sub-pass
426
- ...maskData,
85
+ return {
86
+ lightsBuffer,
87
+ lightsBufferNeedsUpdate: true,
427
88
 
428
- // final composite pass
429
- rendererTargetFormat,
430
- rendererPipeline,
431
- rendererUniformsBufferGpu,
432
- rendererBindgroup0,
433
- rendererBindgroup1,
434
- rendererRenderBundle: null, // set below after data is fully constructed
89
+ lightsTextureNeedsUpdate: true,
90
+ lightsRenderer,
435
91
 
436
- // viewport (flat, no class)
437
- invViewProjectionMatrix,
438
- viewportWidth: cobalt.viewport.width,
439
- viewportHeight: cobalt.viewport.height,
440
- viewportZoom: cobalt.viewport.zoom,
441
- viewportTopLeft,
92
+ viewport,
442
93
 
443
- // logical state
444
94
  lights: [],
445
- ambientLight: [0.2, 0.2, 0.2],
446
- lightsBufferNeedsUpdate: true,
447
- lightsTextureNeedsUpdate: true,
448
95
  }
449
-
450
- data.rendererRenderBundle = buildRendererRenderBundle(device, data)
451
-
452
- return data
453
96
  }
454
97
 
455
98
  function draw(cobalt, node, commandEncoder) {
456
- const d = node.data
457
- const { device } = cobalt
458
-
459
- if (d.lightsBufferNeedsUpdate) {
460
- writeLightsBuffer(device, d, d.lights)
461
- d.lightsBufferNeedsUpdate = false
462
- d.lightsTextureNeedsUpdate = true
99
+ if (node.data.lightsBufferNeedsUpdate) {
100
+ const lightsBuffer = node.data.lightsBuffer
101
+ lightsBuffer.setLights(node.data.lights)
102
+ node.data.lightsBufferNeedsUpdate = false
103
+ node.data.lightsTextureNeedsUpdate = true
463
104
  }
464
105
 
465
- if (d.lightsTextureNeedsUpdate) {
466
- computeLightsTexture(device, d, commandEncoder)
467
- d.lightsTextureNeedsUpdate = false
468
- }
106
+ const lightsRenderer = node.data.lightsRenderer
469
107
 
470
- // update zoom and recompute invert VP matrix
471
- d.viewportZoom = cobalt.viewport.zoom
472
- d.invViewProjectionMatrix = computeInvertViewProjectionMatrix(
473
- d.viewportWidth,
474
- d.viewportHeight,
475
- d.viewportZoom,
476
- d.viewportTopLeft,
477
- )
478
-
479
- // write uniforms: invertViewMatrix (64 bytes) + ambientLight (12 bytes)
480
- const uniformsCpu = new ArrayBuffer(80)
481
- new Float32Array(uniformsCpu, 0, 16).set(d.invViewProjectionMatrix)
482
- new Float32Array(uniformsCpu, 64, 3).set(d.ambientLight)
483
- device.queue.writeBuffer(d.rendererUniformsBufferGpu, 0, uniformsCpu)
108
+ if (node.data.lightsTextureNeedsUpdate) {
109
+ lightsRenderer.computeLightsTexture(commandEncoder)
110
+ node.data.lightsTextureNeedsUpdate = false
111
+ }
484
112
 
485
113
  const renderpass = commandEncoder.beginRenderPass({
486
114
  label: 'light',
@@ -493,90 +121,24 @@ function draw(cobalt, node, commandEncoder) {
493
121
  },
494
122
  ],
495
123
  })
496
- renderpass.executeBundles([d.rendererRenderBundle])
124
+
125
+ node.data.viewport.setZoom(cobalt.viewport.zoom)
126
+ const invertVpMatrix = node.data.viewport.invertViewProjectionMatrix
127
+ lightsRenderer.render(renderpass, invertVpMatrix)
128
+
497
129
  renderpass.end()
498
130
  }
499
131
 
500
132
  function destroy(node) {
501
- const d = node.data
502
- destroyLightsBuffer(d)
503
- d.lightsTexture.destroy()
504
- d.rendererUniformsBufferGpu.destroy()
505
- destroyMask(d)
133
+ node.data.lightsBuffer.destroy()
134
+ node.data.lightsRenderer.destroy()
506
135
  }
507
136
 
508
137
  function resize(cobalt, node) {
509
- const d = node.data
510
- const { device } = cobalt
511
-
512
- d.viewportWidth = cobalt.viewport.width
513
- d.viewportHeight = cobalt.viewport.height
514
- d.invViewProjectionMatrix = computeInvertViewProjectionMatrix(
515
- d.viewportWidth,
516
- d.viewportHeight,
517
- d.viewportZoom,
518
- d.viewportTopLeft,
519
- )
520
-
521
- // rebuild bindgroup1 with the new albedo texture view/sampler from the resized input node
522
- d.rendererBindgroup1 = buildRendererBindgroup1(device, d.rendererPipeline, {
138
+ node.data.lightsRenderer.setAlbedo({
523
139
  view: node.refs.in.data.view,
524
140
  sampler: node.refs.in.data.sampler,
525
141
  })
526
- d.rendererRenderBundle = buildRendererRenderBundle(device, d)
527
- }
528
-
529
- // ---- helpers ----
530
-
531
- function computeLightsTexture(device, data, commandEncoder) {
532
- setMaskLightsCount(device, data, data.lightsCount)
533
- const renderpassEncoder = commandEncoder.beginRenderPass(data.lightsTextureRenderpassDescriptor)
534
- const w = data.lightsTexture.width
535
- const h = data.lightsTexture.height
536
- renderpassEncoder.setViewport(0, 0, w, h, 0, 1)
537
- renderpassEncoder.setScissorRect(0, 0, w, h)
538
- renderpassEncoder.executeBundles([data.initializerRenderBundle, data.maskRenderBundle])
539
- renderpassEncoder.end()
540
- }
541
142
 
542
- function buildRendererBindgroup1(device, pipeline, albedo) {
543
- return device.createBindGroup({
544
- label: 'LightsRenderer bindgroup 1',
545
- layout: pipeline.getBindGroupLayout(1),
546
- entries: [
547
- {
548
- binding: 0,
549
- resource: albedo.view,
550
- },
551
- {
552
- binding: 1,
553
- resource: albedo.sampler,
554
- },
555
- ],
556
- })
557
- }
558
-
559
- function computeInvertViewProjectionMatrix(width, height, zoom, topLeft) {
560
- const m = wgpuMatrix.mat4.identity()
561
- wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.scaling([1, -1, 0]), m, m)
562
- wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.translation([1, 1, 0]), m, m)
563
- wgpuMatrix.mat4.multiply(
564
- wgpuMatrix.mat4.scaling([(0.5 * width) / zoom, (0.5 * height) / zoom, 0]),
565
- m,
566
- m,
567
- )
568
- wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.translation([topLeft[0], topLeft[1], 0]), m, m)
569
- return m
570
- }
571
-
572
- function buildRendererRenderBundle(device, data) {
573
- const renderBundleEncoder = device.createRenderBundleEncoder({
574
- label: 'LightsRenderer renderbundle encoder',
575
- colorFormats: [data.rendererTargetFormat],
576
- })
577
- renderBundleEncoder.setPipeline(data.rendererPipeline)
578
- renderBundleEncoder.setBindGroup(0, data.rendererBindgroup0)
579
- renderBundleEncoder.setBindGroup(1, data.rendererBindgroup1)
580
- renderBundleEncoder.draw(4)
581
- return renderBundleEncoder.finish({ label: 'LightsRenderer renderbundle' })
143
+ node.data.viewport.setViewportSize(cobalt.viewport.width, cobalt.viewport.height)
582
144
  }