@footgun/cobalt 0.7.0 → 0.7.1

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,29 +1,27 @@
1
- import uuid from '../uuid.js'
2
1
  import { vec2, vec4 } from 'wgpu-matrix'
3
-
2
+ import uuid from '../uuid.js'
4
3
 
5
4
  // returns a unique identifier for the created sprite
6
- export function addSprite (cobalt, renderPass, name, position, scale, tint, opacity, rotation) {
7
-
5
+ export function addSprite(cobalt, renderPass, name, position, scale, tint, opacity, rotation) {
8
6
  const { idByName } = renderPass.refs.spritesheet.data
9
7
 
10
8
  renderPass.data.sprites.push({
11
9
  position: vec2.clone(position),
12
- sizeX: 1, sizeY: 1,
10
+ sizeX: 1,
11
+ sizeY: 1,
13
12
  scale: vec2.clone(scale),
14
13
  rotation,
15
14
  opacity,
16
15
  tint: vec4.clone(tint),
17
16
  spriteID: idByName.get(name),
18
17
  id: uuid(),
19
- });
18
+ })
20
19
 
21
- return renderPass.data.sprites.at(-1).id
20
+ return renderPass.data.sprites.at(-1).id
22
21
  }
23
22
 
24
-
25
- export function removeSprite (cobalt, renderPass, id) {
26
- for (let i=0; i < renderPass.data.sprites.length; i++) {
23
+ export function removeSprite(cobalt, renderPass, id) {
24
+ for (let i = 0; i < renderPass.data.sprites.length; i++) {
27
25
  if (renderPass.data.sprites[i].id === id) {
28
26
  renderPass.data.sprites.splice(i, 1)
29
27
  return
@@ -31,65 +29,52 @@ export function removeSprite (cobalt, renderPass, id) {
31
29
  }
32
30
  }
33
31
 
34
-
35
32
  // remove all sprites
36
- export function clear (cobalt, renderPass) {
37
- renderPass.data.sprites.length = 0
33
+ export function clear(cobalt, renderPass) {
34
+ renderPass.data.sprites.length = 0
38
35
  }
39
36
 
40
-
41
- export function setSpriteName (cobalt, renderPass, id, name) {
37
+ export function setSpriteName(cobalt, renderPass, id, name) {
42
38
  const sprite = renderPass.data.sprites.find((s) => s.id === id)
43
39
 
44
- if (!sprite)
45
- return
40
+ if (!sprite) return
46
41
 
47
42
  const { idByName } = renderPass.refs.spritesheet.data
48
43
 
49
44
  sprite.spriteID = idByName.get(name)
50
45
  }
51
46
 
52
-
53
- export function setSpritePosition (cobalt, renderPass, id, position) {
47
+ export function setSpritePosition(cobalt, renderPass, id, position) {
54
48
  const sprite = renderPass.data.sprites.find((s) => s.id === id)
55
- if (!sprite)
56
- return
49
+ if (!sprite) return
57
50
 
58
51
  vec2.copy(position, sprite.position)
59
52
  }
60
53
 
61
-
62
- export function setSpriteTint (cobalt, renderPass, id, tint) {
54
+ export function setSpriteTint(cobalt, renderPass, id, tint) {
63
55
  const sprite = renderPass.data.sprites.find((s) => s.id === id)
64
- if (!sprite)
65
- return
56
+ if (!sprite) return
66
57
 
67
58
  vec4.copy(tint, sprite.tint)
68
59
  }
69
60
 
70
-
71
- export function setSpriteOpacity (cobalt, renderPass, id, opacity) {
61
+ export function setSpriteOpacity(cobalt, renderPass, id, opacity) {
72
62
  const sprite = renderPass.data.sprites.find((s) => s.id === id)
73
- if (!sprite)
74
- return
63
+ if (!sprite) return
75
64
 
76
65
  sprite.opacity = opacity
77
66
  }
78
67
 
79
-
80
- export function setSpriteRotation (cobalt, renderPass, id, rotation) {
68
+ export function setSpriteRotation(cobalt, renderPass, id, rotation) {
81
69
  const sprite = renderPass.data.sprites.find((s) => s.id === id)
82
- if (!sprite)
83
- return
70
+ if (!sprite) return
84
71
 
85
72
  sprite.rotation = rotation
86
73
  }
87
74
 
88
-
89
- export function setSpriteScale (cobalt, renderPass, id, scale) {
75
+ export function setSpriteScale(cobalt, renderPass, id, scale) {
90
76
  const sprite = renderPass.data.sprites.find((s) => s.id === id)
91
- if (!sprite)
92
- return
77
+ if (!sprite) return
93
78
 
94
- vec2.copy(scale, sprite.scale)
79
+ vec2.copy(scale, sprite.scale)
95
80
  }
@@ -1,40 +1,38 @@
1
- import * as publicAPI from './public-api.js'
2
- import spriteWGSL from './sprite.wgsl'
3
- import round from 'round-half-up-symmetric'
1
+ import round from 'round-half-up-symmetric'
4
2
  import { mat4, vec3 } from 'wgpu-matrix'
5
-
3
+ import * as publicAPI from './public-api.js'
4
+ import spriteWGSL from './sprite.wgsl'
6
5
 
7
6
  // temporary variables, allocated once to avoid garbage collection
8
7
  const _tmpVec3 = vec3.create(0, 0, 0)
9
8
 
10
9
  // Packed instance layout: 48 bytes (aligned for vec4 fetch)
11
- const INSTANCE_STRIDE = 64;
10
+ const INSTANCE_STRIDE = 64
12
11
 
13
12
  // Offsets inside one instance (bytes)
14
- const OFF_POS = 0; // float32x2 (8B)
15
- const OFF_SIZE = 8; // float32x2 (8B)
16
- const OFF_SCALE = 16; // float32x2 (8B)
17
- const OFF_TINT = 24; // float32x4 (16B)
18
- const OFF_SPRITEID = 40; // uint32 (4B)
19
- const OFF_OPACITY = 44; // float32 (4B)
20
- const OFF_ROT = 48; // float32 (4B)
21
-
13
+ const OFF_POS = 0 // float32x2 (8B)
14
+ const OFF_SIZE = 8 // float32x2 (8B)
15
+ const OFF_SCALE = 16 // float32x2 (8B)
16
+ const OFF_TINT = 24 // float32x4 (16B)
17
+ const OFF_SPRITEID = 40 // uint32 (4B)
18
+ const OFF_OPACITY = 44 // float32 (4B)
19
+ const OFF_ROT = 48 // float32 (4B)
22
20
 
23
21
  export default {
24
- type: "cobalt:spriteHDR",
22
+ type: 'cobalt:spriteHDR',
25
23
  refs: [
26
- { name: "spritesheet", type: "customResource", access: "read" },
24
+ { name: 'spritesheet', type: 'customResource', access: 'read' },
27
25
  {
28
- name: "color",
29
- type: "textureView",
30
- format: "rgba16float",
31
- access: "write",
26
+ name: 'color',
27
+ type: 'textureView',
28
+ format: 'rgba16float',
29
+ access: 'write',
32
30
  },
33
31
  {
34
- name: "emissive",
35
- type: "textureView",
36
- format: "rgba16float",
37
- access: "write",
32
+ name: 'emissive',
33
+ type: 'textureView',
34
+ format: 'rgba16float',
35
+ access: 'write',
38
36
  },
39
37
  ],
40
38
 
@@ -43,32 +41,38 @@ export default {
43
41
  // @params Object cobalt renderer world object
44
42
  // @params Object options optional data passed when initing this node
45
43
  onInit: async function (cobalt, options = {}) {
46
- return init(cobalt, options);
44
+ return init(cobalt, options)
47
45
  },
48
46
 
49
47
  onRun: function (cobalt, node, webGpuCommandEncoder) {
50
48
  // do whatever you need for this node. webgpu renderpasses, etc.
51
- draw(cobalt, node, webGpuCommandEncoder);
49
+ draw(cobalt, node, webGpuCommandEncoder)
52
50
  },
53
51
 
54
52
  // Clean up GPU resources. Most WebGPU objects are GC-managed and don't
55
53
  // expose destroy(); buffers/textures/query-sets do.
56
54
  onDestroy: function (cobalt, node) {
57
55
  // Explicitly destroy GPU resources that have a destroy() method
58
- try { node.data.instanceBuf?.destroy(); } catch {}
59
- try { node.data.spriteBuf?.destroy(); } catch {}
60
- try { node.data.uniformBuffer?.destroy(); } catch {}
56
+ try {
57
+ node.data.instanceBuf?.destroy()
58
+ } catch {}
59
+ try {
60
+ node.data.spriteBuf?.destroy()
61
+ } catch {}
62
+ try {
63
+ node.data.uniformBuffer?.destroy()
64
+ } catch {}
61
65
 
62
66
  // These do not have destroy(); drop references to let GC reclaim
63
- node.data.pipeline = null; // GPURenderPipeline
64
- node.data.bindGroup = null; // GPUBindGroup
65
- node.data.bindGroupLayout = null;// GPUBindGroupLayout
67
+ node.data.pipeline = null // GPURenderPipeline
68
+ node.data.bindGroup = null // GPUBindGroup
69
+ node.data.bindGroupLayout = null // GPUBindGroupLayout
66
70
 
67
71
  // CPU-side allocations
68
- node.data.instanceStaging = null;
69
- node.data.instanceView = null;
70
- node.data.sprites.length = 0;
71
- node.data.visible.length = 0;
72
+ node.data.instanceStaging = null
73
+ node.data.instanceView = null
74
+ node.data.sprites.length = 0
75
+ node.data.visible.length = 0
72
76
  },
73
77
 
74
78
  onResize: function (cobalt, node) {
@@ -83,115 +87,114 @@ export default {
83
87
  customFunctions: {
84
88
  ...publicAPI,
85
89
  },
86
- };
90
+ }
87
91
 
88
92
  async function init(cobalt, nodeData) {
89
- const { device } = cobalt;
93
+ const { device } = cobalt
90
94
 
91
95
  const { descs, names } = nodeData.refs.spritesheet.data.spritesheet
92
96
 
93
97
  const uniformBuffer = device.createBuffer({
94
98
  size: 64 * 2, // 4x4 matrix with 4 bytes per float32, times 2 matrices (view, projection)
95
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
99
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
96
100
  })
97
101
 
98
102
  // Pack into std430-like struct (4*float*? + vec2 + vec2 → 32 bytes). We'll just write tightly as 8 floats.
99
- const BYTES_PER_DESC = 8 * 4; // 8 float32s
100
- const buf = new ArrayBuffer(BYTES_PER_DESC * descs.length);
101
- const f32 = new Float32Array(buf);
102
- for (let i=0;i<descs.length;i++){
103
- const d = descs[i];
104
- const base = i * 8;
105
- f32[base+0] = d.UvOrigin[0];
106
- f32[base+1] = d.UvOrigin[1];
107
- f32[base+2] = d.UvSpan[0];
108
- f32[base+3] = d.UvSpan[1];
109
- f32[base+4] = d.FrameSize[0];
110
- f32[base+5] = d.FrameSize[1];
111
- f32[base+6] = d.CenterOffset[0];
112
- f32[base+7] = d.CenterOffset[1];
103
+ const BYTES_PER_DESC = 8 * 4 // 8 float32s
104
+ const buf = new ArrayBuffer(BYTES_PER_DESC * descs.length)
105
+ const f32 = new Float32Array(buf)
106
+ for (let i = 0; i < descs.length; i++) {
107
+ const d = descs[i]
108
+ const base = i * 8
109
+ f32[base + 0] = d.UvOrigin[0]
110
+ f32[base + 1] = d.UvOrigin[1]
111
+ f32[base + 2] = d.UvSpan[0]
112
+ f32[base + 3] = d.UvSpan[1]
113
+ f32[base + 4] = d.FrameSize[0]
114
+ f32[base + 5] = d.FrameSize[1]
115
+ f32[base + 6] = d.CenterOffset[0]
116
+ f32[base + 7] = d.CenterOffset[1]
113
117
  }
114
118
 
115
119
  // create buffer for sprite uv lookup
116
120
  const spriteBuf = device.createBuffer({
117
- label: "spriteHDR desc table",
121
+ label: 'spriteHDR desc table',
118
122
  size: Math.max(16, buf.byteLength),
119
123
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
120
- });
124
+ })
121
125
 
122
- device.queue.writeBuffer(spriteBuf, 0, buf);
126
+ device.queue.writeBuffer(spriteBuf, 0, buf)
123
127
 
124
128
  // --- Instance buffer (growable) ---
125
- const instanceCap = 1024;
129
+ const instanceCap = 1024
126
130
  const instanceBuf = device.createBuffer({
127
- label: "spriteHDR instances",
131
+ label: 'spriteHDR instances',
128
132
  size: INSTANCE_STRIDE * instanceCap,
129
133
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
130
- });
131
- const instanceStaging = new ArrayBuffer(INSTANCE_STRIDE * instanceCap);
132
- const instanceView = new DataView(instanceStaging);
134
+ })
135
+ const instanceStaging = new ArrayBuffer(INSTANCE_STRIDE * instanceCap)
136
+ const instanceView = new DataView(instanceStaging)
133
137
 
134
138
  // --- Pipeline ---
135
- const shader = device.createShaderModule({ code: spriteWGSL });
139
+ const shader = device.createShaderModule({ code: spriteWGSL })
136
140
  const bgl = device.createBindGroupLayout({
137
141
  entries: [
138
142
  {
139
143
  binding: 0,
140
144
  visibility: GPUShaderStage.VERTEX,
141
- buffer: { type: "uniform" },
145
+ buffer: { type: 'uniform' },
142
146
  },
143
147
  {
144
148
  binding: 1,
145
149
  visibility: GPUShaderStage.FRAGMENT,
146
- sampler: { type: "filtering" },
150
+ sampler: { type: 'filtering' },
147
151
  },
148
152
  {
149
153
  binding: 2,
150
154
  visibility: GPUShaderStage.FRAGMENT,
151
- texture: { sampleType: "float" },
155
+ texture: { sampleType: 'float' },
152
156
  },
153
157
  {
154
158
  binding: 3,
155
159
  visibility: GPUShaderStage.VERTEX,
156
- buffer: { type: "read-only-storage" },
160
+ buffer: { type: 'read-only-storage' },
157
161
  },
158
162
  {
159
163
  binding: 4,
160
164
  visibility: GPUShaderStage.FRAGMENT,
161
- texture: { sampleType: "float" },
165
+ texture: { sampleType: 'float' },
162
166
  },
163
-
164
167
  ],
165
- });
168
+ })
166
169
  const pipelineLayout = device.createPipelineLayout({
167
170
  bindGroupLayouts: [bgl],
168
- });
171
+ })
169
172
 
170
173
  const instLayout = {
171
174
  arrayStride: INSTANCE_STRIDE,
172
- stepMode: "instance",
175
+ stepMode: 'instance',
173
176
  attributes: [
174
- { shaderLocation: 0, offset: OFF_POS, format: "float32x2" },
175
- { shaderLocation: 1, offset: OFF_SIZE, format: "float32x2" },
176
- { shaderLocation: 2, offset: OFF_SCALE, format: "float32x2" },
177
- { shaderLocation: 3, offset: OFF_TINT, format: "float32x4" },
178
-
179
- { shaderLocation: 4, offset: OFF_SPRITEID, format: "uint32" },
180
- { shaderLocation: 5, offset: OFF_OPACITY, format: "float32" },
181
- { shaderLocation: 6, offset: OFF_ROT, format: "float32" },
177
+ { shaderLocation: 0, offset: OFF_POS, format: 'float32x2' },
178
+ { shaderLocation: 1, offset: OFF_SIZE, format: 'float32x2' },
179
+ { shaderLocation: 2, offset: OFF_SCALE, format: 'float32x2' },
180
+ { shaderLocation: 3, offset: OFF_TINT, format: 'float32x4' },
181
+
182
+ { shaderLocation: 4, offset: OFF_SPRITEID, format: 'uint32' },
183
+ { shaderLocation: 5, offset: OFF_OPACITY, format: 'float32' },
184
+ { shaderLocation: 6, offset: OFF_ROT, format: 'float32' },
182
185
  ],
183
- };
186
+ }
184
187
 
185
188
  const pipeline = device.createRenderPipeline({
186
189
  layout: pipelineLayout,
187
190
  vertex: {
188
191
  module: shader,
189
- entryPoint: "vs_main",
192
+ entryPoint: 'vs_main',
190
193
  buffers: [instLayout],
191
194
  },
192
195
  fragment: {
193
196
  module: shader,
194
- entryPoint: "fs_main",
197
+ entryPoint: 'fs_main',
195
198
  targets: [
196
199
  // color
197
200
  {
@@ -203,23 +206,22 @@ async function init(cobalt, nodeData) {
203
206
  },
204
207
  alpha: {
205
208
  srcFactor: 'zero',
206
- dstFactor: 'one'
207
- }
208
- }
209
+ dstFactor: 'one',
210
+ },
211
+ },
209
212
  },
210
213
 
211
214
  // emissive
212
215
  {
213
216
  format: 'rgba16float',
214
- }
215
-
217
+ },
216
218
  ],
217
219
  },
218
- primitive: { topology: "triangle-strip", cullMode: "none" },
220
+ primitive: { topology: 'triangle-strip', cullMode: 'none' },
219
221
  multisample: { count: 1 },
220
- });
222
+ })
221
223
 
222
- const bindGroupLayout = bgl;
224
+ const bindGroupLayout = bgl
223
225
 
224
226
  const bindGroup = device.createBindGroup({
225
227
  layout: bgl,
@@ -232,11 +234,11 @@ async function init(cobalt, nodeData) {
232
234
  { binding: 3, resource: { buffer: spriteBuf } },
233
235
  { binding: 4, resource: nodeData.refs.spritesheet.data.emissiveTexture.view },
234
236
  ],
235
- });
237
+ })
236
238
 
237
239
  return {
238
- sprites: [ ],
239
- visible: [ ],
240
+ sprites: [],
241
+ visible: [],
240
242
  visibleCount: 0,
241
243
  viewRect: { x: 0, y: 0, w: 0, h: 0 },
242
244
 
@@ -253,36 +255,29 @@ async function init(cobalt, nodeData) {
253
255
  }
254
256
  }
255
257
 
256
-
257
- function ensureCapacity (cobalt, node, nInstances) {
258
-
258
+ function ensureCapacity(cobalt, node, nInstances) {
259
259
  const { instanceCap } = node.data
260
260
 
261
- if (nInstances <= instanceCap)
262
- return;
261
+ if (nInstances <= instanceCap) return
263
262
 
264
263
  let newCap = instanceCap
265
- if (newCap === 0)
266
- newCap = 1024;
264
+ if (newCap === 0) newCap = 1024
267
265
 
268
- while (newCap < nInstances)
269
- newCap *= 2;
266
+ while (newCap < nInstances) newCap *= 2
270
267
 
271
- node.data.instanceBuf.destroy();
268
+ node.data.instanceBuf.destroy()
272
269
  node.data.instanceBuf = cobalt.device.createBuffer({
273
270
  size: INSTANCE_STRIDE * newCap,
274
271
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
275
- });
272
+ })
276
273
 
277
- node.data.instanceStaging = new ArrayBuffer(INSTANCE_STRIDE * newCap);
278
- node.data.instanceView = new DataView(node.data.instanceStaging);
279
- node.data.instanceCap = newCap;
274
+ node.data.instanceStaging = new ArrayBuffer(INSTANCE_STRIDE * newCap)
275
+ node.data.instanceView = new DataView(node.data.instanceStaging)
276
+ node.data.instanceCap = newCap
280
277
  }
281
278
 
282
-
283
- function draw (cobalt, node, commandEncoder) {
284
-
285
- const { device, context } = cobalt;
279
+ function draw(cobalt, node, commandEncoder) {
280
+ const { device, context } = cobalt
286
281
 
287
282
  const { instanceView, instanceBuf, instanceStaging, pipeline, bindGroup } = node.data
288
283
 
@@ -294,21 +289,26 @@ function draw (cobalt, node, commandEncoder) {
294
289
  viewRect.y = cobalt.viewport.position[1]
295
290
  viewRect.w = cobalt.viewport.width
296
291
  viewRect.h = cobalt.viewport.height
297
-
292
+
298
293
  node.data.visibleCount = 0
299
294
 
300
295
  for (const s of node.data.sprites) {
301
- const d = descs[s.spriteID];
302
- if (!d)
303
- continue;
296
+ const d = descs[s.spriteID]
297
+ if (!d) continue
304
298
 
305
299
  // avoid sprite viewport culling when drawing in screenspace mode (typically ui/hud layers)
306
300
  if (!node.options.isScreenSpace) {
307
- const sx = (d.FrameSize[0] * (s.sizeX) * s.scale[0]) * 0.5;
308
- const sy = (d.FrameSize[1] * (s.sizeY) * s.scale[1]) * 0.5;
309
- const rad = Math.hypot(sx, sy);
310
- const x = s.position[0], y = s.position[1];
311
- if (x + rad < viewRect.x || x - rad > viewRect.x + viewRect.w || y + rad < viewRect.y || y - rad > viewRect.y + viewRect.h)
301
+ const sx = d.FrameSize[0] * s.sizeX * s.scale[0] * 0.5
302
+ const sy = d.FrameSize[1] * s.sizeY * s.scale[1] * 0.5
303
+ const rad = Math.hypot(sx, sy)
304
+ const x = s.position[0],
305
+ y = s.position[1]
306
+ if (
307
+ x + rad < viewRect.x ||
308
+ x - rad > viewRect.x + viewRect.w ||
309
+ y + rad < viewRect.y ||
310
+ y - rad > viewRect.y + viewRect.h
311
+ )
312
312
  continue
313
313
  }
314
314
 
@@ -319,38 +319,44 @@ function draw (cobalt, node, commandEncoder) {
319
319
  ensureCapacity(cobalt, node, node.data.visibleCount)
320
320
 
321
321
  // Pack instances into staging buffer
322
- for (let i=0; i < node.data.visibleCount; i++){
323
- const base = i * INSTANCE_STRIDE;
324
- const s = node.data.visible[i];
325
- const tint = s.tint;
326
-
327
- instanceView.setFloat32(base + OFF_POS + 0, s.position[0], true);
328
- instanceView.setFloat32(base + OFF_POS + 4, s.position[1], true);
329
-
330
- instanceView.setFloat32(base + OFF_SIZE + 0, s.sizeX, true);
331
- instanceView.setFloat32(base + OFF_SIZE + 4, s.sizeY, true);
332
-
333
- instanceView.setFloat32(base + OFF_SCALE + 0, s.scale[0], true);
334
- instanceView.setFloat32(base + OFF_SCALE + 4, s.scale[1], true);
335
-
336
- instanceView.setFloat32(base + OFF_TINT + 0, tint[0], true);
337
- instanceView.setFloat32(base + OFF_TINT + 4, tint[1], true);
338
- instanceView.setFloat32(base + OFF_TINT + 8, tint[2], true);
339
- instanceView.setFloat32(base + OFF_TINT + 12, tint[3], true);
340
-
341
- instanceView.setUint32(base + OFF_SPRITEID, s.spriteID >>> 0, true);
342
-
343
- instanceView.setFloat32(base + OFF_OPACITY, s.opacity, true);
344
-
345
- instanceView.setFloat32(base + OFF_ROT, s.rotation, true);
322
+ for (let i = 0; i < node.data.visibleCount; i++) {
323
+ const base = i * INSTANCE_STRIDE
324
+ const s = node.data.visible[i]
325
+ const tint = s.tint
326
+
327
+ instanceView.setFloat32(base + OFF_POS + 0, s.position[0], true)
328
+ instanceView.setFloat32(base + OFF_POS + 4, s.position[1], true)
329
+
330
+ instanceView.setFloat32(base + OFF_SIZE + 0, s.sizeX, true)
331
+ instanceView.setFloat32(base + OFF_SIZE + 4, s.sizeY, true)
332
+
333
+ instanceView.setFloat32(base + OFF_SCALE + 0, s.scale[0], true)
334
+ instanceView.setFloat32(base + OFF_SCALE + 4, s.scale[1], true)
335
+
336
+ instanceView.setFloat32(base + OFF_TINT + 0, tint[0], true)
337
+ instanceView.setFloat32(base + OFF_TINT + 4, tint[1], true)
338
+ instanceView.setFloat32(base + OFF_TINT + 8, tint[2], true)
339
+ instanceView.setFloat32(base + OFF_TINT + 12, tint[3], true)
340
+
341
+ instanceView.setUint32(base + OFF_SPRITEID, s.spriteID >>> 0, true)
342
+
343
+ instanceView.setFloat32(base + OFF_OPACITY, s.opacity, true)
344
+
345
+ instanceView.setFloat32(base + OFF_ROT, s.rotation, true)
346
346
  }
347
347
 
348
- device.queue.writeBuffer(instanceBuf, 0, instanceStaging, 0, node.data.visibleCount * INSTANCE_STRIDE);
348
+ device.queue.writeBuffer(
349
+ instanceBuf,
350
+ 0,
351
+ instanceStaging,
352
+ 0,
353
+ node.data.visibleCount * INSTANCE_STRIDE,
354
+ )
349
355
 
350
356
  const loadOp = node.options.loadOp || 'load'
351
357
 
352
358
  const pass = commandEncoder.beginRenderPass({
353
- label: "spriteHDR renderpass",
359
+ label: 'spriteHDR renderpass',
354
360
  colorAttachments: [
355
361
  // color
356
362
  {
@@ -365,47 +371,42 @@ function draw (cobalt, node, commandEncoder) {
365
371
  view: node.refs.emissive.data.view,
366
372
  clearValue: cobalt.clearValue,
367
373
  loadOp: 'clear',
368
- storeOp: 'store'
369
- }
370
-
374
+ storeOp: 'store',
375
+ },
371
376
  ],
372
- });
373
-
374
- pass.setPipeline(pipeline);
375
- pass.setBindGroup(0, bindGroup);
376
- pass.setVertexBuffer(0, instanceBuf);
377
- if (node.data.visibleCount)
378
- pass.draw(4, node.data.visibleCount, 0, 0); // triangle strip, 4 verts per instance
379
- pass.end();
380
- }
381
-
377
+ })
382
378
 
383
- function _writeSpriteBuffer (cobalt, node) {
379
+ pass.setPipeline(pipeline)
380
+ pass.setBindGroup(0, bindGroup)
381
+ pass.setVertexBuffer(0, instanceBuf)
382
+ if (node.data.visibleCount) pass.draw(4, node.data.visibleCount, 0, 0) // triangle strip, 4 verts per instance
383
+ pass.end()
384
+ }
384
385
 
386
+ function _writeSpriteBuffer(cobalt, node) {
385
387
  const { device, viewport } = cobalt
386
388
 
387
389
  const GAME_WIDTH = viewport.width / viewport.zoom
388
390
  const GAME_HEIGHT = viewport.height / viewport.zoom
389
391
 
390
392
  // left right bottom top near far
391
- const projection = mat4.ortho(0, GAME_WIDTH, GAME_HEIGHT, 0, -10.0, 10.0)
393
+ const projection = mat4.ortho(0, GAME_WIDTH, GAME_HEIGHT, 0, -10.0, 10.0)
392
394
 
393
395
  // set 3d camera position
394
- if (!!node.options.isScreenSpace) {
396
+ if (node.options.isScreenSpace) {
395
397
  vec3.set(0, 0, 0, _tmpVec3)
396
- }
397
- else {
398
+ } else {
398
399
  // TODO: if this doesn't introduce jitter into the crossroads render, remove this disabled code entirely.
399
400
  //
400
401
  // I'm disabling the rounding because I think it fails in cases where units are not expressed in pixels
401
- // e.g., most physics engines operate on meters, not pixels, so we don't want to round to the nearest integer as that
402
+ // e.g., most physics engines operate on meters, not pixels, so we don't want to round to the nearest integer as that
402
403
  // probably isn't high enough resolution. That would mean the camera could be snapped by up to 0.5 meters
403
404
  // in that case. I think the better solution for expressing camera position in pixels is to round before calling
404
405
  // cobalt.setViewportPosition(...)
405
406
  //
406
407
  vec3.set(-round(viewport.position[0]), -round(viewport.position[1]), 0, _tmpVec3)
407
408
  //vec3.set(-viewport.position[0], -viewport.position[1], 0, _tmpVec3)
408
- }
409
+ }
409
410
 
410
411
  const view = mat4.translation(_tmpVec3)
411
412