@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.
- package/CHANGELOG.md +6 -0
- package/bundle.js +117 -91
- package/examples/03-tiles/index.html +3 -3
- package/examples/04-overlay/index.html +2 -102
- package/examples/06-displacement/index.html +2 -2
- package/examples/08-light/index.html +10 -70
- package/package.json +1 -1
- package/src/cobalt.js +3 -3
- package/src/create-texture.js +13 -4
- package/src/fb-texture/fb-texture.js +1 -0
- package/src/light/light.js +62 -500
- package/src/light/lights-buffer.js +59 -55
- package/src/light/lights-renderer.js +230 -0
- package/src/light/public-api.js +4 -3
- package/src/light/texture/lights-texture-initializer.js +175 -0
- package/src/light/texture/lights-texture-mask.js +167 -187
- package/src/light/texture/lights-texture.js +85 -0
- package/src/light/viewport.js +40 -0
- package/src/sprite/sprite.js +2 -2
- package/src/spritesheet/spritesheet.js +17 -14
- package/src/{tile-hdr → tile}/tile.js +1 -1
- /package/src/{tile-hdr → tile}/atlas.js +0 -0
- /package/src/{tile-hdr → tile}/tile.wgsl +0 -0
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { LightsBuffer } from "../lights-buffer.js";
|
|
2
|
+
|
|
3
|
+
class LightsTextureMask {
|
|
4
|
+
device;
|
|
5
|
+
renderPipeline;
|
|
6
|
+
renderBundleEncoderDescriptor;
|
|
7
|
+
renderBundle;
|
|
8
|
+
lightsBuffer;
|
|
9
|
+
indirectDrawing;
|
|
10
|
+
obstacles = null;
|
|
11
|
+
constructor(device, lightsBuffer, lightsTexture, uniformLightSize) {
|
|
12
|
+
this.device = device;
|
|
13
|
+
this.lightsBuffer = lightsBuffer;
|
|
14
|
+
const obstaclesAreTwoWay = true;
|
|
15
|
+
const shaderModule = device.createShaderModule({
|
|
16
|
+
label: "LightsTextureMask shader module",
|
|
17
|
+
code: `
|
|
11
18
|
struct VertexIn {
|
|
12
19
|
@builtin(instance_index) lightIndex: u32,
|
|
13
20
|
@location(0) position: vec3<f32>,
|
|
@@ -65,191 +72,164 @@ fn main_fragment(in: VertexOut) -> FragmentOut {
|
|
|
65
72
|
out.color = in.color;
|
|
66
73
|
return out;
|
|
67
74
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
stepMode: 'vertex',
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
attributes: [
|
|
91
|
-
{
|
|
92
|
-
shaderLocation: 1,
|
|
93
|
-
offset: LIGHTS_BUFFER_STRUCTS.light.radius.offset,
|
|
94
|
-
format: 'float32',
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
shaderLocation: 2,
|
|
98
|
-
offset: LIGHTS_BUFFER_STRUCTS.light.position.offset,
|
|
99
|
-
format: 'float32x2',
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
arrayStride: LIGHTS_BUFFER_STRUCTS.lightsBuffer.lights.stride,
|
|
103
|
-
stepMode: 'instance',
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
},
|
|
107
|
-
fragment: {
|
|
108
|
-
module: shaderModule,
|
|
109
|
-
entryPoint: 'main_fragment',
|
|
110
|
-
targets: [
|
|
111
|
-
{
|
|
112
|
-
format: lightsTexture.format,
|
|
113
|
-
blend: {
|
|
114
|
-
color: {
|
|
115
|
-
operation: 'min',
|
|
116
|
-
srcFactor: 'one',
|
|
117
|
-
dstFactor: 'one',
|
|
118
|
-
},
|
|
119
|
-
alpha: {
|
|
120
|
-
operation: 'min',
|
|
121
|
-
srcFactor: 'one',
|
|
122
|
-
dstFactor: 'one',
|
|
123
|
-
},
|
|
75
|
+
`,
|
|
76
|
+
});
|
|
77
|
+
this.renderPipeline = device.createRenderPipeline({
|
|
78
|
+
label: "LightsTextureMask renderpipeline",
|
|
79
|
+
layout: "auto",
|
|
80
|
+
vertex: {
|
|
81
|
+
module: shaderModule,
|
|
82
|
+
entryPoint: "main_vertex",
|
|
83
|
+
buffers: [
|
|
84
|
+
{
|
|
85
|
+
attributes: [
|
|
86
|
+
{
|
|
87
|
+
shaderLocation: 0,
|
|
88
|
+
offset: 0,
|
|
89
|
+
format: "float32x3",
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
arrayStride: 3 * Float32Array.BYTES_PER_ELEMENT,
|
|
93
|
+
stepMode: "vertex",
|
|
124
94
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
95
|
+
{
|
|
96
|
+
attributes: [
|
|
97
|
+
{
|
|
98
|
+
shaderLocation: 1,
|
|
99
|
+
offset: LightsBuffer.structs.light.radius.offset,
|
|
100
|
+
format: "float32",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
shaderLocation: 2,
|
|
104
|
+
offset: LightsBuffer.structs.light.position.offset,
|
|
105
|
+
format: "float32x2",
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
arrayStride: LightsBuffer.structs.lightsBuffer.lights.stride,
|
|
109
|
+
stepMode: "instance",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
fragment: {
|
|
114
|
+
module: shaderModule,
|
|
115
|
+
entryPoint: "main_fragment",
|
|
116
|
+
targets: [{
|
|
117
|
+
format: lightsTexture.format,
|
|
118
|
+
blend: {
|
|
119
|
+
color: {
|
|
120
|
+
operation: "min",
|
|
121
|
+
srcFactor: "one",
|
|
122
|
+
dstFactor: "one",
|
|
123
|
+
},
|
|
124
|
+
alpha: {
|
|
125
|
+
operation: "min",
|
|
126
|
+
srcFactor: "one",
|
|
127
|
+
dstFactor: "one",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
}],
|
|
131
|
+
},
|
|
132
|
+
primitive: {
|
|
133
|
+
cullMode: obstaclesAreTwoWay ? "none" : "back",
|
|
134
|
+
topology: "triangle-list",
|
|
135
|
+
},
|
|
136
|
+
multisample: {
|
|
137
|
+
count: lightsTexture.sampleCount,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
this.indirectDrawing = {
|
|
141
|
+
bufferCpu: new ArrayBuffer(20),
|
|
142
|
+
bufferGpu: device.createBuffer({
|
|
143
|
+
label: "LightsTextureMask indirect buffer",
|
|
144
|
+
size: 20,
|
|
145
|
+
usage: GPUBufferUsage.INDIRECT | GPUBufferUsage.COPY_DST,
|
|
146
|
+
}),
|
|
147
|
+
};
|
|
148
|
+
this.uploadIndirectDrawingBuffer();
|
|
149
|
+
this.renderBundleEncoderDescriptor = {
|
|
150
|
+
label: "LightsTextureMask renderbundle encoder",
|
|
151
|
+
colorFormats: [lightsTexture.format],
|
|
152
|
+
sampleCount: lightsTexture.sampleCount,
|
|
153
|
+
};
|
|
154
|
+
this.renderBundle = this.buildRenderBundle();
|
|
148
155
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
maskPipeline,
|
|
152
|
-
maskRenderBundleDescriptor,
|
|
153
|
-
maskRenderBundle: null,
|
|
154
|
-
maskIndirectBufferCpu,
|
|
155
|
-
maskIndirectBufferGpu,
|
|
156
|
-
maskObstacles: null,
|
|
157
|
-
lightsBufferGpu,
|
|
156
|
+
getRenderBundle() {
|
|
157
|
+
return this.renderBundle;
|
|
158
158
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
)
|
|
159
|
+
setObstacles(segments) {
|
|
160
|
+
const positions = [];
|
|
161
|
+
const indices = [];
|
|
162
|
+
for (const segment of segments) {
|
|
163
|
+
const firstQuadIndex = positions.length / 3;
|
|
164
|
+
positions.push(...segment[0], 0, ...segment[1], 0, ...segment[0], 1, ...segment[1], 1);
|
|
165
|
+
indices.push(firstQuadIndex + 0, firstQuadIndex + 1, firstQuadIndex + 3, firstQuadIndex + 0, firstQuadIndex + 3, firstQuadIndex + 2);
|
|
166
|
+
}
|
|
167
|
+
let gpuBuffersChanged = false;
|
|
168
|
+
const positionsArray = new Float32Array(positions);
|
|
169
|
+
let positionsBufferGpu = this.obstacles?.positionsBufferGpu;
|
|
170
|
+
if (!positionsBufferGpu || positionsBufferGpu.size < positionsArray.byteLength) {
|
|
171
|
+
positionsBufferGpu?.destroy();
|
|
172
|
+
positionsBufferGpu = this.device.createBuffer({
|
|
173
|
+
label: "LightsTextureMask positions buffer",
|
|
174
|
+
size: positionsArray.byteLength,
|
|
175
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
176
|
+
});
|
|
177
|
+
gpuBuffersChanged = true;
|
|
178
|
+
}
|
|
179
|
+
this.device.queue.writeBuffer(positionsBufferGpu, 0, positionsArray);
|
|
180
|
+
const indicesArray = new Uint16Array(indices);
|
|
181
|
+
let indexBufferGpu = this.obstacles?.indexBufferGpu;
|
|
182
|
+
if (!indexBufferGpu || indexBufferGpu.size < indicesArray.byteLength) {
|
|
183
|
+
indexBufferGpu?.destroy();
|
|
184
|
+
indexBufferGpu = this.device.createBuffer({
|
|
185
|
+
label: "LightsTextureMask index buffer",
|
|
186
|
+
size: indicesArray.byteLength,
|
|
187
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
188
|
+
});
|
|
189
|
+
gpuBuffersChanged = true;
|
|
190
|
+
}
|
|
191
|
+
this.device.queue.writeBuffer(indexBufferGpu, 0, indicesArray);
|
|
192
|
+
this.obstacles = { positionsBufferGpu, indexBufferGpu };
|
|
193
|
+
this.setIndirectIndexCount(indices.length);
|
|
194
|
+
if (gpuBuffersChanged) {
|
|
195
|
+
this.renderBundle = this.buildRenderBundle();
|
|
196
|
+
}
|
|
180
197
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const positionsArray = new Float32Array(positions)
|
|
185
|
-
let positionsBufferGpu = data.maskObstacles?.positionsBufferGpu
|
|
186
|
-
if (!positionsBufferGpu || positionsBufferGpu.size < positionsArray.byteLength) {
|
|
187
|
-
positionsBufferGpu?.destroy()
|
|
188
|
-
positionsBufferGpu = device.createBuffer({
|
|
189
|
-
label: 'LightsTextureMask positions buffer',
|
|
190
|
-
size: positionsArray.byteLength,
|
|
191
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
192
|
-
})
|
|
193
|
-
gpuBuffersChanged = true
|
|
198
|
+
setLightsCount(count) {
|
|
199
|
+
this.setIndirectInstanceCount(count);
|
|
194
200
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!indexBufferGpu || indexBufferGpu.size < indicesArray.byteLength) {
|
|
200
|
-
indexBufferGpu?.destroy()
|
|
201
|
-
indexBufferGpu = device.createBuffer({
|
|
202
|
-
label: 'LightsTextureMask index buffer',
|
|
203
|
-
size: indicesArray.byteLength,
|
|
204
|
-
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
205
|
-
})
|
|
206
|
-
gpuBuffersChanged = true
|
|
201
|
+
destroy() {
|
|
202
|
+
this.indirectDrawing.bufferGpu.destroy();
|
|
203
|
+
this.obstacles?.positionsBufferGpu.destroy();
|
|
204
|
+
this.obstacles?.indexBufferGpu.destroy();
|
|
207
205
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
params[0] = indices.length
|
|
215
|
-
uploadMaskIndirectBuffer(device, data)
|
|
206
|
+
setIndirectIndexCount(indexCount) {
|
|
207
|
+
const drawIndexedIndirectParameters = new Uint32Array(this.indirectDrawing.bufferCpu);
|
|
208
|
+
if (drawIndexedIndirectParameters[0] !== indexCount) {
|
|
209
|
+
drawIndexedIndirectParameters[0] = indexCount;
|
|
210
|
+
this.uploadIndirectDrawingBuffer();
|
|
211
|
+
}
|
|
216
212
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
213
|
+
setIndirectInstanceCount(instanceCount) {
|
|
214
|
+
const drawIndexedIndirectParameters = new Uint32Array(this.indirectDrawing.bufferCpu);
|
|
215
|
+
if (drawIndexedIndirectParameters[1] !== instanceCount) {
|
|
216
|
+
drawIndexedIndirectParameters[1] = instanceCount;
|
|
217
|
+
this.uploadIndirectDrawingBuffer();
|
|
218
|
+
}
|
|
220
219
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
220
|
+
buildRenderBundle() {
|
|
221
|
+
const renderBundleEncoder = this.device.createRenderBundleEncoder(this.renderBundleEncoderDescriptor);
|
|
222
|
+
if (this.obstacles) {
|
|
223
|
+
renderBundleEncoder.setPipeline(this.renderPipeline);
|
|
224
|
+
renderBundleEncoder.setVertexBuffer(0, this.obstacles.positionsBufferGpu);
|
|
225
|
+
renderBundleEncoder.setVertexBuffer(1, this.lightsBuffer.gpuBuffer, LightsBuffer.structs.lightsBuffer.lights.offset);
|
|
226
|
+
renderBundleEncoder.setIndexBuffer(this.obstacles.indexBufferGpu, "uint16");
|
|
227
|
+
renderBundleEncoder.drawIndexedIndirect(this.indirectDrawing.bufferGpu, 0);
|
|
228
|
+
}
|
|
229
|
+
return renderBundleEncoder.finish({ label: "LightsTextureMask renderbundle" });
|
|
228
230
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
export function destroyMask(data) {
|
|
232
|
-
data.maskIndirectBufferGpu.destroy()
|
|
233
|
-
data.maskObstacles?.positionsBufferGpu.destroy()
|
|
234
|
-
data.maskObstacles?.indexBufferGpu.destroy()
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function buildMaskRenderBundle(device, data) {
|
|
238
|
-
const renderBundleEncoder = device.createRenderBundleEncoder(data.maskRenderBundleDescriptor)
|
|
239
|
-
if (data.maskObstacles) {
|
|
240
|
-
renderBundleEncoder.setPipeline(data.maskPipeline)
|
|
241
|
-
renderBundleEncoder.setVertexBuffer(0, data.maskObstacles.positionsBufferGpu)
|
|
242
|
-
renderBundleEncoder.setVertexBuffer(
|
|
243
|
-
1,
|
|
244
|
-
data.lightsBufferGpu,
|
|
245
|
-
LIGHTS_BUFFER_STRUCTS.lightsBuffer.lights.offset,
|
|
246
|
-
)
|
|
247
|
-
renderBundleEncoder.setIndexBuffer(data.maskObstacles.indexBufferGpu, 'uint16')
|
|
248
|
-
renderBundleEncoder.drawIndexedIndirect(data.maskIndirectBufferGpu, 0)
|
|
231
|
+
uploadIndirectDrawingBuffer() {
|
|
232
|
+
this.device.queue.writeBuffer(this.indirectDrawing.bufferGpu, 0, this.indirectDrawing.bufferCpu);
|
|
249
233
|
}
|
|
250
|
-
return renderBundleEncoder.finish({ label: 'LightsTextureMask renderbundle' })
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function uploadMaskIndirectBuffer(device, data) {
|
|
254
|
-
device.queue.writeBuffer(data.maskIndirectBufferGpu, 0, data.maskIndirectBufferCpu)
|
|
255
234
|
}
|
|
235
|
+
export { LightsTextureMask };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { LightsTextureInitializer } from "./lights-texture-initializer.js";
|
|
2
|
+
import { LightsTextureMask } from "./lights-texture-mask.js";
|
|
3
|
+
|
|
4
|
+
class LightsTexture {
|
|
5
|
+
lightsBuffer;
|
|
6
|
+
texture;
|
|
7
|
+
gridSize;
|
|
8
|
+
textureMultisampled = null;
|
|
9
|
+
textureRenderpassDescriptor;
|
|
10
|
+
textureInitializer;
|
|
11
|
+
textureMask;
|
|
12
|
+
constructor(device, lightsBuffer, lightsTextureProperties) {
|
|
13
|
+
this.lightsBuffer = lightsBuffer;
|
|
14
|
+
const cellsCount = this.lightsBuffer.maxLightsCount / 4;
|
|
15
|
+
const gridSize = {
|
|
16
|
+
x: Math.ceil(Math.sqrt(cellsCount)),
|
|
17
|
+
y: 0,
|
|
18
|
+
};
|
|
19
|
+
gridSize.y = Math.ceil(cellsCount / gridSize.x);
|
|
20
|
+
this.gridSize = gridSize;
|
|
21
|
+
const lightTextureSize = {
|
|
22
|
+
width: gridSize.x * lightsTextureProperties.resolutionPerLight,
|
|
23
|
+
height: gridSize.y * lightsTextureProperties.resolutionPerLight,
|
|
24
|
+
};
|
|
25
|
+
const format = lightsTextureProperties.textureFormat;
|
|
26
|
+
this.texture = device.createTexture({
|
|
27
|
+
label: "LightsTextureMask texture",
|
|
28
|
+
size: [lightTextureSize.width, lightTextureSize.height],
|
|
29
|
+
format,
|
|
30
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
|
|
31
|
+
});
|
|
32
|
+
if (lightsTextureProperties.antialiased) {
|
|
33
|
+
this.textureMultisampled = device.createTexture({
|
|
34
|
+
label: "LightsTextureMask texture multisampled",
|
|
35
|
+
size: [lightTextureSize.width, lightTextureSize.height],
|
|
36
|
+
format,
|
|
37
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
|
|
38
|
+
sampleCount: 4,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const textureToRenderTo = this.textureMultisampled ?? this.texture;
|
|
42
|
+
const textureRenderpassColorAttachment = {
|
|
43
|
+
view: textureToRenderTo.createView(),
|
|
44
|
+
clearValue: [0, 0, 0, 1],
|
|
45
|
+
loadOp: "load",
|
|
46
|
+
storeOp: "store",
|
|
47
|
+
};
|
|
48
|
+
if (lightsTextureProperties.antialiased) {
|
|
49
|
+
textureRenderpassColorAttachment.resolveTarget = this.texture.createView();
|
|
50
|
+
}
|
|
51
|
+
this.textureRenderpassDescriptor = {
|
|
52
|
+
label: "lights-renderer render to texture renderpass",
|
|
53
|
+
colorAttachments: [textureRenderpassColorAttachment],
|
|
54
|
+
};
|
|
55
|
+
const lightsTexture = {
|
|
56
|
+
gridSize,
|
|
57
|
+
format,
|
|
58
|
+
sampleCount: this.textureMultisampled?.sampleCount ?? 1,
|
|
59
|
+
};
|
|
60
|
+
this.textureInitializer = new LightsTextureInitializer(device, lightsBuffer, lightsTexture, lightsTextureProperties.maxLightSize);
|
|
61
|
+
this.textureMask = new LightsTextureMask(device, lightsBuffer, lightsTexture, lightsTextureProperties.maxLightSize);
|
|
62
|
+
}
|
|
63
|
+
update(commandEncoder) {
|
|
64
|
+
this.textureMask.setLightsCount(this.lightsBuffer.lightsCount);
|
|
65
|
+
const renderpassEncoder = commandEncoder.beginRenderPass(this.textureRenderpassDescriptor);
|
|
66
|
+
const [textureWidth, textureHeight] = [this.texture.width, this.texture.height];
|
|
67
|
+
renderpassEncoder.setViewport(0, 0, textureWidth, textureHeight, 0, 1);
|
|
68
|
+
renderpassEncoder.setScissorRect(0, 0, textureWidth, textureHeight);
|
|
69
|
+
renderpassEncoder.executeBundles([
|
|
70
|
+
this.textureInitializer.getRenderBundle(),
|
|
71
|
+
this.textureMask.getRenderBundle(),
|
|
72
|
+
]);
|
|
73
|
+
renderpassEncoder.end();
|
|
74
|
+
}
|
|
75
|
+
setObstacles(segments) {
|
|
76
|
+
this.textureMask.setObstacles(segments);
|
|
77
|
+
}
|
|
78
|
+
destroy() {
|
|
79
|
+
this.texture.destroy();
|
|
80
|
+
this.textureMultisampled?.destroy();
|
|
81
|
+
this.textureInitializer.destroy();
|
|
82
|
+
this.textureMask.destroy();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export { LightsTexture };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as wgpuMatrix from "wgpu-matrix";
|
|
2
|
+
|
|
3
|
+
class Viewport {
|
|
4
|
+
invViewProjectionMatrix = wgpuMatrix.mat4.identity();
|
|
5
|
+
viewportSize = { width: 1, height: 1 };
|
|
6
|
+
topLeft = [0, 0];
|
|
7
|
+
zoom = 1;
|
|
8
|
+
constructor(params) {
|
|
9
|
+
this.setViewportSize(params.viewportSize.width, params.viewportSize.height);
|
|
10
|
+
const initialTopLeft = params.center ?? this.topLeft;
|
|
11
|
+
this.setTopLeft(...initialTopLeft);
|
|
12
|
+
const initialZoom = params.zoom ?? 1;
|
|
13
|
+
this.setZoom(initialZoom);
|
|
14
|
+
}
|
|
15
|
+
get invertViewProjectionMatrix() {
|
|
16
|
+
return this.invViewProjectionMatrix;
|
|
17
|
+
}
|
|
18
|
+
setViewportSize(width, height) {
|
|
19
|
+
this.viewportSize.width = width;
|
|
20
|
+
this.viewportSize.height = height;
|
|
21
|
+
this.updateMatrices();
|
|
22
|
+
}
|
|
23
|
+
setTopLeft(x, y) {
|
|
24
|
+
this.topLeft[0] = x;
|
|
25
|
+
this.topLeft[1] = y;
|
|
26
|
+
this.updateMatrices();
|
|
27
|
+
}
|
|
28
|
+
setZoom(zoom) {
|
|
29
|
+
this.zoom = zoom;
|
|
30
|
+
this.updateMatrices();
|
|
31
|
+
}
|
|
32
|
+
updateMatrices() {
|
|
33
|
+
wgpuMatrix.mat4.identity(this.invViewProjectionMatrix);
|
|
34
|
+
wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.scaling([1, -1, 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
|
|
35
|
+
wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.translation([1, 1, 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
|
|
36
|
+
wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.scaling([0.5 * this.viewportSize.width / this.zoom, 0.5 * this.viewportSize.height / this.zoom, 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
|
|
37
|
+
wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.translation([this.topLeft[0], this.topLeft[1], 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export { Viewport };
|
package/src/sprite/sprite.js
CHANGED
|
@@ -185,7 +185,7 @@ async function init(cobalt, nodeData) {
|
|
|
185
185
|
targets: [
|
|
186
186
|
// color
|
|
187
187
|
{
|
|
188
|
-
format: getPreferredFormat(cobalt),
|
|
188
|
+
format: nodeData.refs.color.data?.texture?.format || getPreferredFormat(cobalt),
|
|
189
189
|
blend: {
|
|
190
190
|
color: {
|
|
191
191
|
srcFactor: 'src-alpha',
|
|
@@ -339,7 +339,7 @@ function draw(cobalt, node, commandEncoder) {
|
|
|
339
339
|
colorAttachments: [
|
|
340
340
|
// color
|
|
341
341
|
{
|
|
342
|
-
view: node.refs.color,
|
|
342
|
+
view: node.refs.color.data?.view || node.refs.color,
|
|
343
343
|
clearValue: cobalt.clearValue,
|
|
344
344
|
loadOp: loadOp,
|
|
345
345
|
storeOp: 'store',
|
|
@@ -45,13 +45,14 @@ async function init(cobalt, node) {
|
|
|
45
45
|
node.options.colorTextureUrl,
|
|
46
46
|
format,
|
|
47
47
|
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
if (node.options.emissiveTextureUrl) {
|
|
49
|
+
emissiveTexture = await createTextureFromUrl(
|
|
50
|
+
cobalt,
|
|
51
|
+
'emissive sprite',
|
|
52
|
+
node.options.emissiveTextureUrl,
|
|
53
|
+
format,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
55
56
|
// for some reason this needs to be done _after_ creating the material, or the rendering will be blurry
|
|
56
57
|
canvas.style.imageRendering = 'pixelated'
|
|
57
58
|
} else {
|
|
@@ -64,12 +65,14 @@ async function init(cobalt, node) {
|
|
|
64
65
|
node.options.colorTexture,
|
|
65
66
|
format,
|
|
66
67
|
)
|
|
67
|
-
emissiveTexture
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
if (node.options.emissiveTexture) {
|
|
69
|
+
emissiveTexture = await createTextureFromBuffer(
|
|
70
|
+
cobalt,
|
|
71
|
+
'emissive sprite',
|
|
72
|
+
node.options.emissiveTexture,
|
|
73
|
+
format,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
// Map sprite name → ID
|
|
@@ -85,5 +88,5 @@ async function init(cobalt, node) {
|
|
|
85
88
|
|
|
86
89
|
function destroy(node) {
|
|
87
90
|
node.data.colorTexture.texture.destroy()
|
|
88
|
-
node.data.emissiveTexture.texture.destroy()
|
|
91
|
+
if (node.data.emissiveTexture) node.data.emissiveTexture.texture.destroy()
|
|
89
92
|
}
|
|
@@ -21,7 +21,7 @@ Inspired by/ported from https://blog.tojicode.com/2012/07/sprite-tile-maps-on-gp
|
|
|
21
21
|
* out (textureView, rgba16float, write) - render target
|
|
22
22
|
*/
|
|
23
23
|
export default {
|
|
24
|
-
type: 'cobalt:
|
|
24
|
+
type: 'cobalt:tile',
|
|
25
25
|
|
|
26
26
|
// @params Object cobalt renderer world object
|
|
27
27
|
// @params Object options optional data passed when initing this node
|
|
File without changes
|
|
File without changes
|