@footgun/cobalt 0.1.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/LICENSE +21 -0
- package/README.md +18 -0
- package/bundle.js +284 -0
- package/cobalt2.jpeg +0 -0
- package/esbuild.js +20 -0
- package/examples/01-primitives/Game.js +8 -0
- package/examples/01-primitives/component-animation.js +8 -0
- package/examples/01-primitives/component-transform.js +13 -0
- package/examples/01-primitives/constants.js +6 -0
- package/examples/01-primitives/deps.js +2 -0
- package/examples/01-primitives/entity-sprite.js +47 -0
- package/examples/01-primitives/index.html +191 -0
- package/examples/01-primitives/system-renderer.js +37 -0
- package/examples/02-sprites/Game.js +8 -0
- package/examples/02-sprites/assets/spritesheet.json +6276 -0
- package/examples/02-sprites/assets/spritesheet.png +0 -0
- package/examples/02-sprites/assets/spritesheet_emissive.png +0 -0
- package/examples/02-sprites/component-animation.js +8 -0
- package/examples/02-sprites/component-transform.js +13 -0
- package/examples/02-sprites/constants.js +6 -0
- package/examples/02-sprites/deps.js +2 -0
- package/examples/02-sprites/entity-sprite.js +47 -0
- package/examples/02-sprites/index.html +310 -0
- package/examples/02-sprites/system-renderer.js +38 -0
- package/examples/03-tiles/Game.js +8 -0
- package/examples/03-tiles/assets/spelunky-tiles.png +0 -0
- package/examples/03-tiles/assets/spelunky0.png +0 -0
- package/examples/03-tiles/assets/spelunky1.png +0 -0
- package/examples/03-tiles/component-animation.js +8 -0
- package/examples/03-tiles/component-transform.js +13 -0
- package/examples/03-tiles/constants.js +6 -0
- package/examples/03-tiles/deps.js +2 -0
- package/examples/03-tiles/entity-sprite.js +47 -0
- package/examples/03-tiles/index.html +309 -0
- package/examples/03-tiles/system-renderer.js +38 -0
- package/examples/04-overlay/assets/spritesheet.json +22 -0
- package/examples/04-overlay/assets/spritesheet.png +0 -0
- package/examples/04-overlay/assets/spritesheet_emissive.png +0 -0
- package/examples/04-overlay/constants.js +6 -0
- package/examples/04-overlay/deps.js +1 -0
- package/examples/04-overlay/index.html +133 -0
- package/examples/05-bloom/Game.js +8 -0
- package/examples/05-bloom/assets/spritesheet.json +6276 -0
- package/examples/05-bloom/assets/spritesheet.png +0 -0
- package/examples/05-bloom/assets/spritesheet_emissive.png +0 -0
- package/examples/05-bloom/component-animation.js +8 -0
- package/examples/05-bloom/component-transform.js +13 -0
- package/examples/05-bloom/constants.js +6 -0
- package/examples/05-bloom/deps.js +2 -0
- package/examples/05-bloom/entity-sprite.js +47 -0
- package/examples/05-bloom/index.html +357 -0
- package/examples/05-bloom/system-renderer.js +38 -0
- package/examples/06-displacement/Game.js +8 -0
- package/examples/06-displacement/assets/displacement_map_repeat.jpg +0 -0
- package/examples/06-displacement/assets/spelunky-tiles.png +0 -0
- package/examples/06-displacement/assets/spelunky0.png +0 -0
- package/examples/06-displacement/assets/spelunky1.png +0 -0
- package/examples/06-displacement/component-animation.js +8 -0
- package/examples/06-displacement/component-transform.js +13 -0
- package/examples/06-displacement/constants.js +6 -0
- package/examples/06-displacement/deps.js +2 -0
- package/examples/06-displacement/entity-sprite.js +47 -0
- package/examples/06-displacement/index.html +350 -0
- package/examples/06-displacement/system-renderer.js +38 -0
- package/examples/07-sdl/assets/spritesheet.json +22 -0
- package/examples/07-sdl/assets/spritesheet.png +0 -0
- package/examples/07-sdl/assets/spritesheet_emissive.png +0 -0
- package/examples/07-sdl/main.js +109 -0
- package/examples/07-sdl/package.json +19 -0
- package/examples/08-light/Game.js +8 -0
- package/examples/08-light/assets/spelunky-tiles.png +0 -0
- package/examples/08-light/assets/spelunky0.png +0 -0
- package/examples/08-light/assets/spelunky1.png +0 -0
- package/examples/08-light/constants.js +6 -0
- package/examples/08-light/deps.js +2 -0
- package/examples/08-light/index.html +477 -0
- package/package.json +34 -0
- package/src/bloom/bloom.js +467 -0
- package/src/bloom/bloom.wgsl +176 -0
- package/src/cobalt.js +231 -0
- package/src/create-texture-from-buffer.js +39 -0
- package/src/create-texture-from-url.js +35 -0
- package/src/create-texture.js +46 -0
- package/src/deps.js +3 -0
- package/src/displacement/composition.wgsl +58 -0
- package/src/displacement/displacement-composition.ts +161 -0
- package/src/displacement/displacement-parameters-buffer.ts +44 -0
- package/src/displacement/displacement-texture.ts +221 -0
- package/src/displacement/displacement.js +160 -0
- package/src/displacement/displacement.wgsl +31 -0
- package/src/displacement/triangles-buffer.ts +95 -0
- package/src/fb-blit/fb-blit.js +161 -0
- package/src/fb-blit/fb-blit.wgsl +40 -0
- package/src/fb-texture/fb-texture.js +56 -0
- package/src/light/README.md +61 -0
- package/src/light/light.js +148 -0
- package/src/light/lights-buffer.ts +98 -0
- package/src/light/lights-renderer.ts +278 -0
- package/src/light/public-api.js +20 -0
- package/src/light/readme/01_illumination.webp +0 -0
- package/src/light/readme/02_lights_texture.webp +0 -0
- package/src/light/readme/03_lights_texture_decomposed.webp +0 -0
- package/src/light/readme/04_lights_texture_mask.webp +0 -0
- package/src/light/readme/05_lights_obstacle_decomposition.webp +0 -0
- package/src/light/readme/06_lights_hard_cast_shadows.webp +0 -0
- package/src/light/texture/lights-texture-initializer.ts +191 -0
- package/src/light/texture/lights-texture-mask.ts +286 -0
- package/src/light/texture/lights-texture.ts +121 -0
- package/src/light/types.ts +23 -0
- package/src/light/viewport.ts +63 -0
- package/src/overlay/constants.js +1 -0
- package/src/overlay/overlay.js +341 -0
- package/src/overlay/overlay.wgsl +88 -0
- package/src/primitives/constants.js +1 -0
- package/src/primitives/primitives.js +252 -0
- package/src/primitives/primitives.wgsl +54 -0
- package/src/primitives/public-api.js +325 -0
- package/src/scene-composite/scene-composite.js +168 -0
- package/src/scene-composite/scene-composite.wgsl +94 -0
- package/src/sprite/constants.js +1 -0
- package/src/sprite/create-sprite-quads.js +60 -0
- package/src/sprite/public-api.js +215 -0
- package/src/sprite/read-spritesheet.js +103 -0
- package/src/sprite/sorted-binary-insert.js +45 -0
- package/src/sprite/sprite.js +268 -0
- package/src/sprite/sprite.wgsl +103 -0
- package/src/sprite/spritesheet.js +212 -0
- package/src/tile/atlas.js +193 -0
- package/src/tile/tile.js +171 -0
- package/src/tile/tile.wgsl +105 -0
- package/src/uuid.js +3 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title class="titleText">Web GPU prototyping</title>
|
|
6
|
+
<meta name="description" content="Web GPU 2d cobalt" />
|
|
7
|
+
<meta name="author" content="Michael Reinstein" />
|
|
8
|
+
<meta name="viewport" content="width=device-width" />
|
|
9
|
+
<meta name="viewport" content="initial-scale=1, maximum-scale=1" />
|
|
10
|
+
|
|
11
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
12
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
|
|
16
|
+
body {
|
|
17
|
+
padding: 0;
|
|
18
|
+
margin: 0;
|
|
19
|
+
overscroll-behavior: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
img {
|
|
23
|
+
image-rendering: -moz-crisp-edges;
|
|
24
|
+
image-rendering: -webkit-crisp-edges;
|
|
25
|
+
image-rendering: pixelated;
|
|
26
|
+
image-rendering: crisp-edges;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.viewport-container {
|
|
30
|
+
position: fixed;
|
|
31
|
+
top: 0;
|
|
32
|
+
left: 0;
|
|
33
|
+
bottom: 0;
|
|
34
|
+
right: 0;
|
|
35
|
+
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
canvas {
|
|
42
|
+
background-color: green;
|
|
43
|
+
border: none;
|
|
44
|
+
image-rendering: -moz-crisp-edges;
|
|
45
|
+
image-rendering: -webkit-crisp-edges;
|
|
46
|
+
image-rendering: pixelated;
|
|
47
|
+
image-rendering: crisp-edges;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
canvas::-webkit-scrollbar {
|
|
51
|
+
display: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
</style>
|
|
55
|
+
</head>
|
|
56
|
+
<body>
|
|
57
|
+
|
|
58
|
+
<div class="viewport-container">
|
|
59
|
+
<canvas id="viewport" width="480" height="270"></canvas>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<script type="module">
|
|
63
|
+
import Game from './Game.js'
|
|
64
|
+
import * as Cobalt from '../../bundle.js'
|
|
65
|
+
import constants from './constants.js'
|
|
66
|
+
import dat from 'https://cdn.skypack.dev/pin/dat.gui@v0.7.9-2wtQAdFH5SRwnJLDWGNz/mode=imports,min/optimized/dat.gui.js'
|
|
67
|
+
import debounce from 'https://cdn.skypack.dev/pin/lodash.debounce@v4.0.8-4GXU9B066R3Th6HmjZmO/lodash.debounce.js'
|
|
68
|
+
import { ECS } from './deps.js'
|
|
69
|
+
|
|
70
|
+
let mouseViewportPosition = [0, 0];
|
|
71
|
+
const mouseButtonsDown = new Map();
|
|
72
|
+
|
|
73
|
+
async function main () {
|
|
74
|
+
|
|
75
|
+
const canvas = document.querySelector('canvas')
|
|
76
|
+
|
|
77
|
+
const viewportWidth = constants.GAME_WIDTH
|
|
78
|
+
const viewportHeight = constants.GAME_HEIGHT
|
|
79
|
+
Game.renderer = await Cobalt.init(canvas, viewportWidth, viewportHeight)
|
|
80
|
+
Game.renderer.viewport.zoom = 1;
|
|
81
|
+
|
|
82
|
+
const viewportCenter = [0, 0];
|
|
83
|
+
|
|
84
|
+
// instantiate all resource nodes
|
|
85
|
+
const tileAtlasNode = await Cobalt.initNode(Game.renderer, {
|
|
86
|
+
type: 'cobalt:tileAtlas',
|
|
87
|
+
refs: { },
|
|
88
|
+
options: {
|
|
89
|
+
label: 'tile atlas',
|
|
90
|
+
tileSize: 16,
|
|
91
|
+
tileScale: 1.0,
|
|
92
|
+
textureUrl: 'assets/spelunky-tiles.png'
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const hdrTex = await Cobalt.initNode(Game.renderer, {
|
|
97
|
+
type: 'cobalt:fbTexture',
|
|
98
|
+
refs: { },
|
|
99
|
+
options: {
|
|
100
|
+
label: 'hdr color texture',
|
|
101
|
+
format: 'rgba16float',
|
|
102
|
+
mip_count: 1,
|
|
103
|
+
viewportScale: 1.0,
|
|
104
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const lightCompositeHdrTex = await Cobalt.initNode(Game.renderer, {
|
|
109
|
+
type: 'cobalt:fbTexture',
|
|
110
|
+
refs: { },
|
|
111
|
+
options: {
|
|
112
|
+
label: 'hdr + lighting/shadows color texture',
|
|
113
|
+
format: 'rgba16float',
|
|
114
|
+
mip_count: 1,
|
|
115
|
+
viewportScale: 1.0,
|
|
116
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const emissiveTex = await Cobalt.initNode(Game.renderer, {
|
|
121
|
+
type: 'cobalt:fbTexture',
|
|
122
|
+
refs: { },
|
|
123
|
+
options: {
|
|
124
|
+
label: 'hdr emissive texture',
|
|
125
|
+
format: 'rgba16float',
|
|
126
|
+
mip_count: 1,
|
|
127
|
+
viewportScale: 1.0,
|
|
128
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
const bloomTex = await Cobalt.initNode(Game.renderer, {
|
|
133
|
+
type: 'cobalt:fbTexture',
|
|
134
|
+
refs: { },
|
|
135
|
+
options: {
|
|
136
|
+
label: 'hdr bloom texture',
|
|
137
|
+
format: 'rgba16float',
|
|
138
|
+
mip_count: 7,
|
|
139
|
+
viewportScale: 0.5,
|
|
140
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const bloomNode = await Cobalt.initNode(Game.renderer, {
|
|
145
|
+
type: 'cobalt:bloom',
|
|
146
|
+
refs: {
|
|
147
|
+
// key is the var name defined in this node
|
|
148
|
+
// value is the var name in the cobalt resources dictionary
|
|
149
|
+
emissive: emissiveTex,
|
|
150
|
+
hdr: hdrTex,
|
|
151
|
+
bloom: bloomTex
|
|
152
|
+
},
|
|
153
|
+
options: {
|
|
154
|
+
// any extra options you want to pass to this node
|
|
155
|
+
bloom_intensity: 45.0,
|
|
156
|
+
bloom_combine_constant: 0.3,
|
|
157
|
+
bloom_knee: 0.2,
|
|
158
|
+
bloom_threshold: 0.1, // 1.0
|
|
159
|
+
|
|
160
|
+
// sprite instance 1
|
|
161
|
+
sprite_instances: [
|
|
162
|
+
{
|
|
163
|
+
emissive_intensity: 1.0,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
emissive_intensity: 0.5,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const tmpTex = await Cobalt.initNode(Game.renderer, {
|
|
173
|
+
type: 'cobalt:fbTexture',
|
|
174
|
+
refs: { },
|
|
175
|
+
options: {
|
|
176
|
+
label: 'bloom + hdr compositing',
|
|
177
|
+
format: 'bgra8unorm',
|
|
178
|
+
mip_count: 1,
|
|
179
|
+
viewportScale: 1.0,
|
|
180
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// instantiate all runnable nodes
|
|
185
|
+
const tileNode = await Cobalt.initNode(Game.renderer, {
|
|
186
|
+
type: 'cobalt:tile',
|
|
187
|
+
refs: {
|
|
188
|
+
// key is the var name defined in this node
|
|
189
|
+
// value is the var name in the cobalt resources dictionary
|
|
190
|
+
tileAtlas: tileAtlasNode,
|
|
191
|
+
hdr: hdrTex,
|
|
192
|
+
},
|
|
193
|
+
options: {
|
|
194
|
+
textureUrl: 'assets/spelunky1.png',
|
|
195
|
+
scrollScale: 0.6,
|
|
196
|
+
//zIndex: 0,
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
const tileNode2 = await Cobalt.initNode(Game.renderer, {
|
|
201
|
+
type: 'cobalt:tile',
|
|
202
|
+
refs: {
|
|
203
|
+
// key is the var name defined in this node
|
|
204
|
+
// value is the var name in the cobalt resources dictionary
|
|
205
|
+
tileAtlas: tileAtlasNode,
|
|
206
|
+
hdr: hdrTex,
|
|
207
|
+
},
|
|
208
|
+
options: {
|
|
209
|
+
textureUrl: 'assets/spelunky0.png',
|
|
210
|
+
scrollScale: 1.0,
|
|
211
|
+
//zIndex: 5,
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
const convertToViewportPosition = (clientPosition /* [number, number] */) /* [number, number] */ => {
|
|
217
|
+
const canvasBox = canvas.getBoundingClientRect();
|
|
218
|
+
return [
|
|
219
|
+
((clientPosition[0] - canvasBox.left) / canvasBox.width) * constants.GAME_WIDTH /Game.renderer.viewport.zoom,
|
|
220
|
+
((clientPosition[1] - canvasBox.top) / canvasBox.height) * constants.GAME_HEIGHT /Game.renderer.viewport.zoom,
|
|
221
|
+
];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const convertToWorldPosition = (viewportPosition /* [number, number] */) /* [number, number] */ => {
|
|
225
|
+
return [
|
|
226
|
+
viewportPosition[0] + viewportCenter[0] - 0.5 * constants.GAME_WIDTH / Game.renderer.viewport.zoom,
|
|
227
|
+
viewportPosition[1] + viewportCenter[1] - 0.5 * constants.GAME_HEIGHT/ Game.renderer.viewport.zoom,
|
|
228
|
+
];
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// lights and shadows node
|
|
232
|
+
const light = await Cobalt.initNode(Game.renderer, {
|
|
233
|
+
type: 'cobalt:light',
|
|
234
|
+
refs: {
|
|
235
|
+
in: hdrTex,
|
|
236
|
+
out: lightCompositeHdrTex,
|
|
237
|
+
},
|
|
238
|
+
options: {
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
light.setAmbientLight([0.3, 0.3, 0.3]);
|
|
242
|
+
|
|
243
|
+
const permanentObstacleSegments = [];
|
|
244
|
+
|
|
245
|
+
let temporaryObstacleSegment = null;
|
|
246
|
+
|
|
247
|
+
const allOccluders = () => {
|
|
248
|
+
const segments = [
|
|
249
|
+
...permanentObstacleSegments,
|
|
250
|
+
];
|
|
251
|
+
if (temporaryObstacleSegment) {
|
|
252
|
+
segments.push(temporaryObstacleSegment);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return segments;
|
|
256
|
+
|
|
257
|
+
};
|
|
258
|
+
canvas.addEventListener("mouseenter", event => {
|
|
259
|
+
mouseViewportPosition = convertToViewportPosition([event.clientX, event.clientY]);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
canvas.addEventListener("mousedown", event => {
|
|
263
|
+
mouseButtonsDown.set(event.button, true);
|
|
264
|
+
|
|
265
|
+
if (event.button === 0) {
|
|
266
|
+
temporaryObstacleSegment = [
|
|
267
|
+
convertToWorldPosition(mouseViewportPosition),
|
|
268
|
+
convertToWorldPosition(mouseViewportPosition),
|
|
269
|
+
];
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
canvas.addEventListener("mouseup", event => {
|
|
274
|
+
mouseButtonsDown.set(event.button, false);
|
|
275
|
+
|
|
276
|
+
if (temporaryObstacleSegment) {
|
|
277
|
+
permanentObstacleSegments.push(temporaryObstacleSegment);
|
|
278
|
+
temporaryObstacleSegment = null;
|
|
279
|
+
light.setOccluders(allOccluders());
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
canvas.addEventListener("mouseleave", event => {
|
|
284
|
+
for (const key of mouseButtonsDown.keys()) {
|
|
285
|
+
mouseButtonsDown.set(key, false);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
canvas.addEventListener("mousemove", event => {
|
|
290
|
+
const newMouseViewportPosition = convertToViewportPosition([event.clientX, event.clientY]);
|
|
291
|
+
const movement = [
|
|
292
|
+
newMouseViewportPosition[0] - mouseViewportPosition[0],
|
|
293
|
+
newMouseViewportPosition[1] - mouseViewportPosition[1],
|
|
294
|
+
];
|
|
295
|
+
mouseViewportPosition = newMouseViewportPosition;
|
|
296
|
+
|
|
297
|
+
if (mouseButtonsDown.get(1) === true) {
|
|
298
|
+
viewportCenter[0] -= movement[0];
|
|
299
|
+
viewportCenter[1] -= movement[1];
|
|
300
|
+
|
|
301
|
+
Cobalt.setViewportPosition(Game.renderer, viewportCenter)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (temporaryObstacleSegment) {
|
|
305
|
+
temporaryObstacleSegment[1] = convertToWorldPosition(mouseViewportPosition);
|
|
306
|
+
light.setOccluders(allOccluders());
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const mouseLight = {
|
|
311
|
+
position: convertToWorldPosition(mouseViewportPosition),
|
|
312
|
+
radius: 100,
|
|
313
|
+
color: [1, 1, 1],
|
|
314
|
+
intensity: 20,
|
|
315
|
+
attenuationLinear: 0,
|
|
316
|
+
attenuationExp: 5,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
canvas.addEventListener("wheel", event => {
|
|
320
|
+
mouseLight.intensity *= 1 + 0.001 * event.deltaY;
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const movingRedLight = {
|
|
324
|
+
position: [ 69, 42 ],
|
|
325
|
+
radius: 150,
|
|
326
|
+
color: [1, 0, 0],
|
|
327
|
+
intensity: 2,
|
|
328
|
+
attenuationLinear: 0,
|
|
329
|
+
attenuationExp: 7,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const movingGreenLight = {
|
|
333
|
+
position: [ 69, 42 ],
|
|
334
|
+
radius: 50,
|
|
335
|
+
color: [0, 1, 0],
|
|
336
|
+
intensity: 10,
|
|
337
|
+
attenuationLinear: 0,
|
|
338
|
+
attenuationExp: 3,
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const lights = [
|
|
342
|
+
mouseLight,
|
|
343
|
+
movingRedLight,
|
|
344
|
+
movingGreenLight,
|
|
345
|
+
];
|
|
346
|
+
light.setLights(lights)
|
|
347
|
+
|
|
348
|
+
const compositeNode = await Cobalt.initNode(Game.renderer, {
|
|
349
|
+
type: 'cobalt:composite',
|
|
350
|
+
refs: {
|
|
351
|
+
hdr: lightCompositeHdrTex,
|
|
352
|
+
bloom: bloomTex,
|
|
353
|
+
combined: tmpTex,
|
|
354
|
+
},
|
|
355
|
+
options: { }
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
const b = await Cobalt.initNode(Game.renderer, {
|
|
360
|
+
type: 'cobalt:fbBlit',
|
|
361
|
+
refs: {
|
|
362
|
+
in: tmpTex,
|
|
363
|
+
out: 'FRAME_TEXTURE_VIEW',
|
|
364
|
+
},
|
|
365
|
+
options: { }
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
// use resizeViewport to init values on load:
|
|
370
|
+
resizeViewport(Game.renderer, window.innerWidth, window.innerHeight)
|
|
371
|
+
|
|
372
|
+
// window resize is *expensive* - best to debounce:
|
|
373
|
+
const debouncedResize = debounce(function () {
|
|
374
|
+
resizeViewport(Game.renderer, window.innerWidth, window.innerHeight)
|
|
375
|
+
}, 50)
|
|
376
|
+
|
|
377
|
+
window.addEventListener('resize', debouncedResize, { passive: true })
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
const world = ECS.createWorld()
|
|
382
|
+
|
|
383
|
+
ECS.addSystem(world, function rendererSystem (world) {
|
|
384
|
+
const onUpdate = function (/*dt*/) {
|
|
385
|
+
|
|
386
|
+
mouseLight.position = convertToWorldPosition(mouseViewportPosition);
|
|
387
|
+
|
|
388
|
+
movingGreenLight.position = [
|
|
389
|
+
80 * Math.cos(performance.now() / 500),
|
|
390
|
+
80 * Math.sin(performance.now() / 500),
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
movingRedLight.position = [
|
|
394
|
+
40 * Math.cos(performance.now() / 500),
|
|
395
|
+
60,
|
|
396
|
+
];
|
|
397
|
+
light.setLights(lights)
|
|
398
|
+
|
|
399
|
+
Cobalt.draw(Game.renderer)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return { onUpdate }
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
Cobalt.setViewportPosition(Game.renderer, viewportCenter)
|
|
406
|
+
|
|
407
|
+
ECS.addSystem(world, function cameraLoopSystem(world) {
|
|
408
|
+
const onUpdate = function (dt) {
|
|
409
|
+
// const elapsed = performance.now()
|
|
410
|
+
// const x = (Math.sin(elapsed / 2000) * 0.5 + 0.5) * 128
|
|
411
|
+
// const y = (Math.sin(elapsed / 5000) * 0.5 + 0.5) * 170
|
|
412
|
+
|
|
413
|
+
// Cobalt.setViewportPosition(Game.renderer, [ x, y ])
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return { onUpdate }
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
Game.world = world
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
const gameLoop = function () {
|
|
423
|
+
tick(Game)
|
|
424
|
+
requestAnimationFrame(gameLoop)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
requestAnimationFrame(gameLoop)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
function tick (context) {
|
|
433
|
+
const newTime = performance.now()
|
|
434
|
+
const frameTime = newTime - context.lastFrameTime
|
|
435
|
+
context.lastFrameTime = newTime
|
|
436
|
+
ECS.update(context.world, frameTime)
|
|
437
|
+
ECS.cleanup(context.world)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
function resizeViewport (renderer, width, height) {
|
|
442
|
+
// reset mouse position
|
|
443
|
+
mouseViewportPosition[0] = 0;
|
|
444
|
+
mouseViewportPosition[1] = 0;
|
|
445
|
+
|
|
446
|
+
const { canvas, device } = renderer
|
|
447
|
+
|
|
448
|
+
// determine which screen dimension is most constrained
|
|
449
|
+
// we floor the render scale to an integer because we get weird texture artifacts when trying to render at
|
|
450
|
+
// certain float values (e.g., 3.0145833333333334)
|
|
451
|
+
const renderScale = Math.floor(Math.min(width/constants.GAME_WIDTH, height/constants.GAME_HEIGHT))
|
|
452
|
+
|
|
453
|
+
canvas.width = Math.ceil(constants.GAME_WIDTH)
|
|
454
|
+
canvas.height = Math.ceil(constants.GAME_HEIGHT)
|
|
455
|
+
|
|
456
|
+
Cobalt.setViewportDimensions(renderer, constants.GAME_WIDTH, constants.GAME_HEIGHT)
|
|
457
|
+
|
|
458
|
+
// https://www.khronos.org/webgl/wiki/HandlingHighDPI
|
|
459
|
+
// webgl display resolution size within canvas
|
|
460
|
+
const resolution = window.devicePixelRatio || 1
|
|
461
|
+
|
|
462
|
+
// center the canvas if native window doesn't match aspect ratio
|
|
463
|
+
canvas.style.width = canvas.width * renderScale + 'px'
|
|
464
|
+
canvas.style.height = canvas.height * renderScale + 'px'
|
|
465
|
+
|
|
466
|
+
canvas.style.left = Math.round((width - canvas.width * renderScale) / 2) + 'px'
|
|
467
|
+
canvas.style.top = Math.round((height - canvas.height * renderScale) / 2) + 'px'
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
main()
|
|
472
|
+
|
|
473
|
+
</script>
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
</body>
|
|
477
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@footgun/cobalt",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "bundle.js",
|
|
6
|
+
"description": "An opinionated 2D node graph based on WebGpu",
|
|
7
|
+
"homepage": "https://github.com/mreinstein/cobalt",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"webgpu",
|
|
10
|
+
"game",
|
|
11
|
+
"renderer",
|
|
12
|
+
"2d",
|
|
13
|
+
"graphics"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git@github.com:mreinstein/cobalt.git"
|
|
19
|
+
},
|
|
20
|
+
"author": "Michael Reintein",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "node esbuild.js",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@hyrious/esbuild-plugin-http": "^0.1.5",
|
|
27
|
+
"@webgpu/types": "^0.1.44",
|
|
28
|
+
"esbuild": "^0.24.0",
|
|
29
|
+
"esbuild-plugin-glsl": "^1.1.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"wgpu-matrix": "^3.0.2"
|
|
33
|
+
}
|
|
34
|
+
}
|