@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.
- package/CHANGELOG.md +4 -0
- package/biome.json +23 -0
- package/bundle.js +16 -14546
- package/esbuild.js +1 -1
- package/examples/01-primitives/main.js +22 -0
- package/package.json +1 -1
- package/src/bloom/bloom.js +188 -174
- package/src/cobalt.js +48 -66
- package/src/create-texture-from-buffer.js +9 -8
- package/src/create-texture-from-url.js +22 -10
- package/src/create-texture.js +15 -14
- package/src/displacement/displacement.js +42 -47
- package/src/fb-blit/fb-blit.js +34 -36
- package/src/fb-texture/fb-texture.js +30 -15
- package/src/get-preferred-format.js +3 -5
- package/src/light/light.js +30 -34
- package/src/light/public-api.js +6 -8
- package/src/primitives/constants.js +1 -1
- package/src/primitives/primitives.js +61 -64
- package/src/primitives/public-api.js +76 -73
- package/src/scene-composite/scene-composite.js +77 -78
- package/src/sprite/public-api.js +31 -46
- package/src/sprite/sprite.js +156 -154
- package/src/sprite-hdr/public-api.js +23 -38
- package/src/sprite-hdr/sprite.js +166 -165
- package/src/spritesheet/read-spritesheet.js +27 -118
- package/src/spritesheet/spritesheet.js +37 -21
- package/src/tile-hdr/atlas.js +54 -50
- package/src/tile-hdr/tile.js +40 -42
- package/src/uuid.js +2 -2
- package/src/spritesheet/create-sprite-quads.js +0 -60
|
@@ -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
|
|
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,
|
|
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
|
-
|
|
20
|
+
return renderPass.data.sprites.at(-1).id
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
37
|
-
|
|
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
|
}
|
package/src/sprite-hdr/sprite.js
CHANGED
|
@@ -1,40 +1,38 @@
|
|
|
1
|
-
import
|
|
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
|
|
15
|
-
const OFF_SIZE = 8
|
|
16
|
-
const OFF_SCALE = 16
|
|
17
|
-
const OFF_TINT = 24
|
|
18
|
-
const OFF_SPRITEID = 40
|
|
19
|
-
const OFF_OPACITY = 44
|
|
20
|
-
const OFF_ROT = 48
|
|
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:
|
|
22
|
+
type: 'cobalt:spriteHDR',
|
|
25
23
|
refs: [
|
|
26
|
-
{ name:
|
|
24
|
+
{ name: 'spritesheet', type: 'customResource', access: 'read' },
|
|
27
25
|
{
|
|
28
|
-
name:
|
|
29
|
-
type:
|
|
30
|
-
format:
|
|
31
|
-
access:
|
|
26
|
+
name: 'color',
|
|
27
|
+
type: 'textureView',
|
|
28
|
+
format: 'rgba16float',
|
|
29
|
+
access: 'write',
|
|
32
30
|
},
|
|
33
31
|
{
|
|
34
|
-
name:
|
|
35
|
-
type:
|
|
36
|
-
format:
|
|
37
|
-
access:
|
|
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 {
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
64
|
-
node.data.bindGroup = null
|
|
65
|
-
node.data.bindGroupLayout = null
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
145
|
+
buffer: { type: 'uniform' },
|
|
142
146
|
},
|
|
143
147
|
{
|
|
144
148
|
binding: 1,
|
|
145
149
|
visibility: GPUShaderStage.FRAGMENT,
|
|
146
|
-
sampler: { type:
|
|
150
|
+
sampler: { type: 'filtering' },
|
|
147
151
|
},
|
|
148
152
|
{
|
|
149
153
|
binding: 2,
|
|
150
154
|
visibility: GPUShaderStage.FRAGMENT,
|
|
151
|
-
texture: { sampleType:
|
|
155
|
+
texture: { sampleType: 'float' },
|
|
152
156
|
},
|
|
153
157
|
{
|
|
154
158
|
binding: 3,
|
|
155
159
|
visibility: GPUShaderStage.VERTEX,
|
|
156
|
-
buffer: { type:
|
|
160
|
+
buffer: { type: 'read-only-storage' },
|
|
157
161
|
},
|
|
158
162
|
{
|
|
159
163
|
binding: 4,
|
|
160
164
|
visibility: GPUShaderStage.FRAGMENT,
|
|
161
|
-
texture: { sampleType:
|
|
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:
|
|
175
|
+
stepMode: 'instance',
|
|
173
176
|
attributes: [
|
|
174
|
-
{ shaderLocation: 0, offset: OFF_POS, format:
|
|
175
|
-
{ shaderLocation: 1, offset: OFF_SIZE, format:
|
|
176
|
-
{ shaderLocation: 2, offset: OFF_SCALE, format:
|
|
177
|
-
{ shaderLocation: 3, offset: OFF_TINT, format:
|
|
178
|
-
|
|
179
|
-
{ shaderLocation: 4, offset: OFF_SPRITEID, format:
|
|
180
|
-
{ shaderLocation: 5, offset: OFF_OPACITY, format:
|
|
181
|
-
{ shaderLocation: 6, offset: OFF_ROT, format:
|
|
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:
|
|
192
|
+
entryPoint: 'vs_main',
|
|
190
193
|
buffers: [instLayout],
|
|
191
194
|
},
|
|
192
195
|
fragment: {
|
|
193
196
|
module: shader,
|
|
194
|
-
entryPoint:
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
308
|
-
const sy =
|
|
309
|
-
const rad = Math.hypot(sx, sy)
|
|
310
|
-
const x = s.position[0],
|
|
311
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
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
|
|