@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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/bundle.js +284 -0
  4. package/cobalt2.jpeg +0 -0
  5. package/esbuild.js +20 -0
  6. package/examples/01-primitives/Game.js +8 -0
  7. package/examples/01-primitives/component-animation.js +8 -0
  8. package/examples/01-primitives/component-transform.js +13 -0
  9. package/examples/01-primitives/constants.js +6 -0
  10. package/examples/01-primitives/deps.js +2 -0
  11. package/examples/01-primitives/entity-sprite.js +47 -0
  12. package/examples/01-primitives/index.html +191 -0
  13. package/examples/01-primitives/system-renderer.js +37 -0
  14. package/examples/02-sprites/Game.js +8 -0
  15. package/examples/02-sprites/assets/spritesheet.json +6276 -0
  16. package/examples/02-sprites/assets/spritesheet.png +0 -0
  17. package/examples/02-sprites/assets/spritesheet_emissive.png +0 -0
  18. package/examples/02-sprites/component-animation.js +8 -0
  19. package/examples/02-sprites/component-transform.js +13 -0
  20. package/examples/02-sprites/constants.js +6 -0
  21. package/examples/02-sprites/deps.js +2 -0
  22. package/examples/02-sprites/entity-sprite.js +47 -0
  23. package/examples/02-sprites/index.html +310 -0
  24. package/examples/02-sprites/system-renderer.js +38 -0
  25. package/examples/03-tiles/Game.js +8 -0
  26. package/examples/03-tiles/assets/spelunky-tiles.png +0 -0
  27. package/examples/03-tiles/assets/spelunky0.png +0 -0
  28. package/examples/03-tiles/assets/spelunky1.png +0 -0
  29. package/examples/03-tiles/component-animation.js +8 -0
  30. package/examples/03-tiles/component-transform.js +13 -0
  31. package/examples/03-tiles/constants.js +6 -0
  32. package/examples/03-tiles/deps.js +2 -0
  33. package/examples/03-tiles/entity-sprite.js +47 -0
  34. package/examples/03-tiles/index.html +309 -0
  35. package/examples/03-tiles/system-renderer.js +38 -0
  36. package/examples/04-overlay/assets/spritesheet.json +22 -0
  37. package/examples/04-overlay/assets/spritesheet.png +0 -0
  38. package/examples/04-overlay/assets/spritesheet_emissive.png +0 -0
  39. package/examples/04-overlay/constants.js +6 -0
  40. package/examples/04-overlay/deps.js +1 -0
  41. package/examples/04-overlay/index.html +133 -0
  42. package/examples/05-bloom/Game.js +8 -0
  43. package/examples/05-bloom/assets/spritesheet.json +6276 -0
  44. package/examples/05-bloom/assets/spritesheet.png +0 -0
  45. package/examples/05-bloom/assets/spritesheet_emissive.png +0 -0
  46. package/examples/05-bloom/component-animation.js +8 -0
  47. package/examples/05-bloom/component-transform.js +13 -0
  48. package/examples/05-bloom/constants.js +6 -0
  49. package/examples/05-bloom/deps.js +2 -0
  50. package/examples/05-bloom/entity-sprite.js +47 -0
  51. package/examples/05-bloom/index.html +357 -0
  52. package/examples/05-bloom/system-renderer.js +38 -0
  53. package/examples/06-displacement/Game.js +8 -0
  54. package/examples/06-displacement/assets/displacement_map_repeat.jpg +0 -0
  55. package/examples/06-displacement/assets/spelunky-tiles.png +0 -0
  56. package/examples/06-displacement/assets/spelunky0.png +0 -0
  57. package/examples/06-displacement/assets/spelunky1.png +0 -0
  58. package/examples/06-displacement/component-animation.js +8 -0
  59. package/examples/06-displacement/component-transform.js +13 -0
  60. package/examples/06-displacement/constants.js +6 -0
  61. package/examples/06-displacement/deps.js +2 -0
  62. package/examples/06-displacement/entity-sprite.js +47 -0
  63. package/examples/06-displacement/index.html +350 -0
  64. package/examples/06-displacement/system-renderer.js +38 -0
  65. package/examples/07-sdl/assets/spritesheet.json +22 -0
  66. package/examples/07-sdl/assets/spritesheet.png +0 -0
  67. package/examples/07-sdl/assets/spritesheet_emissive.png +0 -0
  68. package/examples/07-sdl/main.js +109 -0
  69. package/examples/07-sdl/package.json +19 -0
  70. package/examples/08-light/Game.js +8 -0
  71. package/examples/08-light/assets/spelunky-tiles.png +0 -0
  72. package/examples/08-light/assets/spelunky0.png +0 -0
  73. package/examples/08-light/assets/spelunky1.png +0 -0
  74. package/examples/08-light/constants.js +6 -0
  75. package/examples/08-light/deps.js +2 -0
  76. package/examples/08-light/index.html +477 -0
  77. package/package.json +34 -0
  78. package/src/bloom/bloom.js +467 -0
  79. package/src/bloom/bloom.wgsl +176 -0
  80. package/src/cobalt.js +231 -0
  81. package/src/create-texture-from-buffer.js +39 -0
  82. package/src/create-texture-from-url.js +35 -0
  83. package/src/create-texture.js +46 -0
  84. package/src/deps.js +3 -0
  85. package/src/displacement/composition.wgsl +58 -0
  86. package/src/displacement/displacement-composition.ts +161 -0
  87. package/src/displacement/displacement-parameters-buffer.ts +44 -0
  88. package/src/displacement/displacement-texture.ts +221 -0
  89. package/src/displacement/displacement.js +160 -0
  90. package/src/displacement/displacement.wgsl +31 -0
  91. package/src/displacement/triangles-buffer.ts +95 -0
  92. package/src/fb-blit/fb-blit.js +161 -0
  93. package/src/fb-blit/fb-blit.wgsl +40 -0
  94. package/src/fb-texture/fb-texture.js +56 -0
  95. package/src/light/README.md +61 -0
  96. package/src/light/light.js +148 -0
  97. package/src/light/lights-buffer.ts +98 -0
  98. package/src/light/lights-renderer.ts +278 -0
  99. package/src/light/public-api.js +20 -0
  100. package/src/light/readme/01_illumination.webp +0 -0
  101. package/src/light/readme/02_lights_texture.webp +0 -0
  102. package/src/light/readme/03_lights_texture_decomposed.webp +0 -0
  103. package/src/light/readme/04_lights_texture_mask.webp +0 -0
  104. package/src/light/readme/05_lights_obstacle_decomposition.webp +0 -0
  105. package/src/light/readme/06_lights_hard_cast_shadows.webp +0 -0
  106. package/src/light/texture/lights-texture-initializer.ts +191 -0
  107. package/src/light/texture/lights-texture-mask.ts +286 -0
  108. package/src/light/texture/lights-texture.ts +121 -0
  109. package/src/light/types.ts +23 -0
  110. package/src/light/viewport.ts +63 -0
  111. package/src/overlay/constants.js +1 -0
  112. package/src/overlay/overlay.js +341 -0
  113. package/src/overlay/overlay.wgsl +88 -0
  114. package/src/primitives/constants.js +1 -0
  115. package/src/primitives/primitives.js +252 -0
  116. package/src/primitives/primitives.wgsl +54 -0
  117. package/src/primitives/public-api.js +325 -0
  118. package/src/scene-composite/scene-composite.js +168 -0
  119. package/src/scene-composite/scene-composite.wgsl +94 -0
  120. package/src/sprite/constants.js +1 -0
  121. package/src/sprite/create-sprite-quads.js +60 -0
  122. package/src/sprite/public-api.js +215 -0
  123. package/src/sprite/read-spritesheet.js +103 -0
  124. package/src/sprite/sorted-binary-insert.js +45 -0
  125. package/src/sprite/sprite.js +268 -0
  126. package/src/sprite/sprite.wgsl +103 -0
  127. package/src/sprite/spritesheet.js +212 -0
  128. package/src/tile/atlas.js +193 -0
  129. package/src/tile/tile.js +171 -0
  130. package/src/tile/tile.wgsl +105 -0
  131. 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
+ }