@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,8 @@
1
+ export default function componentAnimation (obj) {
2
+ return {
3
+ name: obj.name || '',
4
+ frame: obj.frame || 0,
5
+ accumulator: obj.accumulator || 0,
6
+ done: false,
7
+ }
8
+ }
@@ -0,0 +1,13 @@
1
+ import { vec3 } from './deps.js'
2
+
3
+
4
+ export default function componentTransform (obj) {
5
+ return {
6
+ position: obj.position || vec3.create(0, 0, 0),
7
+ rotation: obj.rotation || 0,
8
+
9
+ // if set, this means the position is relative to another transform
10
+ // rather than being in world space
11
+ relativeTo: obj.relativeTo,
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+
2
+ export default {
3
+ // virtual game screen dimensions in 16:9
4
+ GAME_WIDTH: 480,
5
+ GAME_HEIGHT: 270,
6
+ }
@@ -0,0 +1,2 @@
1
+ export { default as ECS } from 'https://cdn.skypack.dev/pin/ecs@v0.21.0-P934LlnmSF5QgmGODbyN/mode=imports/optimized/ecs.js'
2
+ export { mat4, vec2, vec3, vec4 } from 'https://wgpu-matrix.org/dist/3.x/wgpu-matrix.module.js'
@@ -0,0 +1,47 @@
1
+ import * as Cobalt from '../../bundle.js'
2
+ import animationComponent from './component-animation.js'
3
+ import transformComponent from './component-transform.js'
4
+ import { ECS, vec3, vec4 } from './deps.js'
5
+
6
+
7
+ export default function spriteEntity (world, opts) {
8
+ const ENTITY = ECS.createEntity(world)
9
+
10
+ ECS.addComponentToEntity(world, ENTITY, 'transform', transformComponent({
11
+ position: vec3.create(opts.position[0], opts.position[1], 0)
12
+ }))
13
+
14
+ ECS.addComponentToEntity(world, ENTITY, 'animation', animationComponent({
15
+ name: opts.name
16
+ }))
17
+
18
+ const tint = vec4.create(0, 0, 0, 0)
19
+
20
+ const opacity = opts.opacity ?? 255
21
+
22
+ const spriteNode = opts.spriteNode
23
+
24
+ const cobaltSpriteId = spriteNode.addSprite(opts.name,
25
+ opts.position,
26
+ vec3.create(1, 1, 1),
27
+ tint,
28
+ opacity / 255,
29
+ opts.rotation || 0,
30
+ opts.zIndex)
31
+
32
+ ECS.addComponentToEntity(world, ENTITY, 'sprite', {
33
+ name: opts.name,
34
+
35
+ layer: opts.layer,
36
+ rotation: 0, // radians
37
+ scale: vec3.create(1, 1, 1),
38
+ opacity: 1, // 0 is transparent, 1 is opaque
39
+ tint: vec4.create(0, 0, 0, 0),
40
+
41
+ // cobalt references
42
+ cobaltSpriteId,
43
+ spriteNode,
44
+ })
45
+
46
+ return ENTITY
47
+ }
@@ -0,0 +1,310 @@
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 animationSystem from './system-animation.js'
69
+ import rendererSystem from './system-renderer.js'
70
+ import spriteEntity from './entity-sprite.js'
71
+ import { ECS, vec2 } from './deps.js'
72
+
73
+
74
+ async function main () {
75
+
76
+ const canvas = document.querySelector('canvas')
77
+
78
+ const viewportWidth = constants.GAME_WIDTH
79
+ const viewportHeight = constants.GAME_HEIGHT
80
+ Game.renderer = await Cobalt.init(canvas, viewportWidth, viewportHeight)
81
+
82
+ // instantiate all resource nodes
83
+ const spritesheet = await Cobalt.initNode(Game.renderer, {
84
+ type: 'cobalt:spritesheet',
85
+ refs: { },
86
+ options: {
87
+ spriteSheetJsonUrl: './assets/spritesheet.json',
88
+ colorTextureUrl: 'assets/spritesheet.png',
89
+ emissiveTextureUrl: 'assets/spritesheet_emissive.png'
90
+ }
91
+ })
92
+
93
+ const hdrTex = await Cobalt.initNode(Game.renderer, {
94
+ type: 'cobalt:fbTexture',
95
+ refs: { },
96
+ options: {
97
+ label: 'hdr color texture',
98
+ format: 'rgba16float',
99
+ mip_count: 1,
100
+ viewportScale: 1.0,
101
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
102
+ }
103
+ })
104
+
105
+ const emissiveTex = await Cobalt.initNode(Game.renderer, {
106
+ type: 'cobalt:fbTexture',
107
+ refs: { },
108
+ options: {
109
+ label: 'hdr emissive texture',
110
+ format: 'rgba16float',
111
+ mip_count: 1,
112
+ viewportScale: 1.0,
113
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
114
+ }
115
+ })
116
+
117
+ const bloomTex = await Cobalt.initNode(Game.renderer, {
118
+ type: 'cobalt:fbTexture',
119
+ refs: { },
120
+ options: {
121
+ label: 'hdr bloom texture',
122
+ format: 'rgba16float',
123
+ mip_count: 7,
124
+ viewportScale: 0.5,
125
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
126
+ }
127
+ })
128
+
129
+ const bloomNode = await Cobalt.initNode(Game.renderer, {
130
+ type: 'cobalt:bloom',
131
+ refs: {
132
+ // key is the var name defined in this node
133
+ // value is the var name in the cobalt resources dictionary
134
+ emissive: emissiveTex,
135
+ hdr: hdrTex,
136
+ bloom: bloomTex
137
+ },
138
+ options: {
139
+ // any extra options you want to pass to this node
140
+ bloom_intensity: 0.0,
141
+ bloom_knee: 0.0,
142
+ bloom_threshold: 0.1, // 1.0
143
+ /*
144
+ // sprite instance 1
145
+ sprite_instances: [
146
+ {
147
+ emissive_intensity: 1.0,
148
+ },
149
+ {
150
+ emissive_intensity: 0.5,
151
+ },
152
+ ],
153
+ */
154
+ }
155
+ })
156
+
157
+ const tmpTex = await Cobalt.initNode(Game.renderer, {
158
+ type: 'cobalt:fbTexture',
159
+ refs: { },
160
+ options: {
161
+ label: 'bloom + hdr compositing',
162
+ format: 'bgra8unorm',
163
+ mip_count: 1,
164
+ viewportScale: 1.0,
165
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
166
+ }
167
+ })
168
+
169
+ // instantiate all runnable nodes
170
+ const spriteNode = await Cobalt.initNode(Game.renderer, {
171
+ type: 'cobalt:sprite',
172
+ refs: {
173
+ // key is the var name defined in this node
174
+ // value is the var name in the cobalt resources dictionary
175
+ spritesheet: spritesheet,
176
+ emissive: emissiveTex,
177
+ hdr: hdrTex,
178
+ },
179
+ options: {
180
+ loadOp: 'clear',
181
+ }
182
+ })
183
+
184
+
185
+ const compositeNode = await Cobalt.initNode(Game.renderer, {
186
+ type: 'cobalt:composite',
187
+ refs: {
188
+ hdr: hdrTex,
189
+ bloom: bloomTex,
190
+ combined: tmpTex,
191
+ },
192
+ options: {
193
+ bloom_combine_constant: 0.0,
194
+ },
195
+ })
196
+
197
+
198
+ const b = await Cobalt.initNode(Game.renderer, {
199
+ type: 'cobalt:fbBlit',
200
+ refs: {
201
+ in: tmpTex,
202
+ out: 'FRAME_TEXTURE_VIEW',
203
+ },
204
+ options: { }
205
+ })
206
+
207
+
208
+ // use resizeViewport to init values on load:
209
+ resizeViewport(Game.renderer, window.innerWidth, window.innerHeight)
210
+
211
+ // window resize is *expensive* - best to debounce:
212
+ const debouncedResize = debounce(function () {
213
+ resizeViewport(Game.renderer, window.innerWidth, window.innerHeight)
214
+ }, 50)
215
+
216
+ window.addEventListener('resize', debouncedResize, { passive: true })
217
+
218
+
219
+
220
+ const world = ECS.createWorld()
221
+ ECS.addSystem(world, rendererSystem(Game.renderer))
222
+
223
+ Cobalt.setViewportPosition(Game.renderer, [ 240, 135 ])
224
+
225
+ ECS.addSystem(world, function cameraLoopSystem(world) {
226
+ const onUpdate = function (dt) {
227
+ const elapsed = performance.now()
228
+ const x = (Math.sin(elapsed / 2000) * 0.5 + 0.5) * 128
229
+ const y = (Math.sin(elapsed / 5000) * 0.5 + 0.5) * 170
230
+
231
+ //const { tileScale } = Game.renderer.tile
232
+
233
+ Cobalt.setViewportPosition(Game.renderer, [ x, y ])
234
+ }
235
+
236
+ return { onUpdate }
237
+ })
238
+
239
+ Game.world = world
240
+
241
+ // test sprites
242
+ const s2 = spriteEntity(world, {
243
+ name: 'health_empty-0.png',
244
+ position: [ 50, 50 ],
245
+ zIndex: 4,
246
+ emissiveIntensity: 0.5,
247
+ spriteNode,
248
+ })
249
+
250
+ const s = spriteEntity(world, {
251
+ name: 'health-0.png',
252
+ position: [ 240, 135 ],
253
+ zIndex: 4,
254
+ emissiveIntensity: 0.5,
255
+ spriteNode,
256
+ })
257
+
258
+ const gameLoop = function () {
259
+ tick(Game)
260
+ requestAnimationFrame(gameLoop)
261
+ }
262
+
263
+
264
+ requestAnimationFrame(gameLoop)
265
+ }
266
+
267
+
268
+ function tick (context) {
269
+ const newTime = performance.now()
270
+ const frameTime = newTime - context.lastFrameTime
271
+ context.lastFrameTime = newTime
272
+ ECS.update(context.world, frameTime)
273
+ ECS.cleanup(context.world)
274
+ }
275
+
276
+
277
+ function resizeViewport (renderer, width, height) {
278
+
279
+ const { canvas, device } = renderer
280
+
281
+ // determine which screen dimension is most constrained
282
+ // we floor the render scale to an integer because we get weird texture artifacts when trying to render at
283
+ // certain float values (e.g., 3.0145833333333334)
284
+ const renderScale = Math.floor(Math.min(width/constants.GAME_WIDTH, height/constants.GAME_HEIGHT))
285
+
286
+ canvas.width = Math.ceil(constants.GAME_WIDTH)
287
+ canvas.height = Math.ceil(constants.GAME_HEIGHT)
288
+
289
+ Cobalt.setViewportDimensions(renderer, constants.GAME_WIDTH, constants.GAME_HEIGHT)
290
+
291
+ // https://www.khronos.org/webgl/wiki/HandlingHighDPI
292
+ // webgl display resolution size within canvas
293
+ const resolution = window.devicePixelRatio || 1
294
+
295
+ // center the canvas if native window doesn't match aspect ratio
296
+ canvas.style.width = canvas.width * renderScale + 'px'
297
+ canvas.style.height = canvas.height * renderScale + 'px'
298
+
299
+ canvas.style.left = Math.round((width - canvas.width * renderScale) / 2) + 'px'
300
+ canvas.style.top = Math.round((height - canvas.height * renderScale) / 2) + 'px'
301
+ }
302
+
303
+
304
+ main()
305
+
306
+ </script>
307
+
308
+
309
+ </body>
310
+ </html>
@@ -0,0 +1,38 @@
1
+ import * as Cobalt from '../../bundle.js'
2
+ import Game from './Game.js'
3
+ import { ECS } from './deps.js'
4
+
5
+
6
+ const SPRITE_QUERY = [ 'sprite' ]
7
+
8
+
9
+ // @param Object renderer Cobalt render state
10
+ export default function createRendererSystem (renderer) {
11
+
12
+ // temporary variables, allocated once to avoid garbage collection
13
+ const buf = new Float32Array(136) // tile instance data stored in a UBO
14
+ const removedEntities = {
15
+ count: 0,
16
+ entries: new Array(100)
17
+ }
18
+
19
+ return function rendererSystem (world) {
20
+
21
+ const onUpdate = function (/*dt*/) {
22
+
23
+ const device = renderer.device
24
+ const context = renderer.context
25
+
26
+ ECS.getEntities(world, SPRITE_QUERY, 'removed', removedEntities)
27
+
28
+ for (let i=0; i < removedEntities.count; i++) {
29
+ const oldSprite = removedEntities.entries[i]
30
+ oldSprite.spriteNode.removeSprite(oldSprite.sprite.cobaltSpriteId)
31
+ }
32
+
33
+ Cobalt.draw(Game.renderer)
34
+ }
35
+
36
+ return { onUpdate }
37
+ }
38
+ }
@@ -0,0 +1,8 @@
1
+ // all of the example's globals are consolidated here
2
+ export default {
3
+ renderer: undefined, // cobalt instance
4
+ world: undefined, // ECS instance
5
+
6
+ // timekeeping (in milliseconds)
7
+ lastFrameTime: 0, // local time the last frame ran
8
+ }
@@ -0,0 +1,8 @@
1
+ export default function componentAnimation (obj) {
2
+ return {
3
+ name: obj.name || '',
4
+ frame: obj.frame || 0,
5
+ accumulator: obj.accumulator || 0,
6
+ done: false,
7
+ }
8
+ }
@@ -0,0 +1,13 @@
1
+ import { vec3 } from './deps.js'
2
+
3
+
4
+ export default function componentTransform (obj) {
5
+ return {
6
+ position: obj.position || vec3.create(0, 0, 0),
7
+ rotation: obj.rotation || 0,
8
+
9
+ // if set, this means the position is relative to another transform
10
+ // rather than being in world space
11
+ relativeTo: obj.relativeTo,
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+
2
+ export default {
3
+ // virtual game screen dimensions in 16:9
4
+ GAME_WIDTH: 480,
5
+ GAME_HEIGHT: 270,
6
+ }
@@ -0,0 +1,2 @@
1
+ export { default as ECS } from 'https://cdn.skypack.dev/pin/ecs@v0.21.0-P934LlnmSF5QgmGODbyN/mode=imports/optimized/ecs.js'
2
+ export { mat4, vec2, vec3, vec4 } from 'https://wgpu-matrix.org/dist/3.x/wgpu-matrix.module.js'
@@ -0,0 +1,47 @@
1
+ import * as Cobalt from '../../bundle.js'
2
+ import animationComponent from './component-animation.js'
3
+ import transformComponent from './component-transform.js'
4
+ import { ECS, vec3, vec4 } from './deps.js'
5
+
6
+
7
+ export default function spriteEntity (world, opts) {
8
+ const ENTITY = ECS.createEntity(world)
9
+
10
+ ECS.addComponentToEntity(world, ENTITY, 'transform', transformComponent({
11
+ position: vec3.create(opts.position[0], opts.position[1], 0)
12
+ }))
13
+
14
+ ECS.addComponentToEntity(world, ENTITY, 'animation', animationComponent({
15
+ name: opts.name
16
+ }))
17
+
18
+ const tint = vec4.create(0, 0, 0, 0)
19
+
20
+ const opacity = opts.opacity ?? 255
21
+
22
+ const spriteNode = opts.spriteNode
23
+
24
+ const cobaltSpriteId = spriteNode.addSprite(opts.name,
25
+ opts.position,
26
+ vec3.create(1, 1, 1),
27
+ tint,
28
+ opacity / 255,
29
+ opts.rotation || 0,
30
+ opts.zIndex)
31
+
32
+ ECS.addComponentToEntity(world, ENTITY, 'sprite', {
33
+ name: opts.name,
34
+
35
+ layer: opts.layer,
36
+ rotation: 0, // radians
37
+ scale: vec3.create(1, 1, 1),
38
+ opacity: 1, // 0 is transparent, 1 is opaque
39
+ tint: vec4.create(0, 0, 0, 0),
40
+
41
+ // cobalt references
42
+ cobaltSpriteId,
43
+ spriteNode,
44
+ })
45
+
46
+ return ENTITY
47
+ }