@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
package/cobalt2.jpeg
ADDED
|
Binary file
|
package/esbuild.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import esbuild from 'esbuild'
|
|
2
|
+
import { http } from '@hyrious/esbuild-plugin-http'
|
|
3
|
+
import { glsl } from 'esbuild-plugin-glsl'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
esbuild.build({
|
|
7
|
+
entryPoints: [ 'src/cobalt.js' ],
|
|
8
|
+
allowOverwrite: true,
|
|
9
|
+
bundle: true,
|
|
10
|
+
format: 'esm',
|
|
11
|
+
target: 'es2022',
|
|
12
|
+
plugins: [
|
|
13
|
+
// minifying used to break the code for some reason, but I think it's fixed
|
|
14
|
+
// so I'll re-enable this and see how it goes!
|
|
15
|
+
glsl({ minify: true }),
|
|
16
|
+
http()
|
|
17
|
+
],
|
|
18
|
+
outfile: 'bundle.js',
|
|
19
|
+
minify: true,
|
|
20
|
+
})
|
|
@@ -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,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,191 @@
|
|
|
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
|
+
|
|
84
|
+
const pNode = await Cobalt.initNode(Game.renderer, {
|
|
85
|
+
type: 'cobalt:primitives',
|
|
86
|
+
refs: {
|
|
87
|
+
// key is the var name defined in this node
|
|
88
|
+
// value is the var name in the cobalt resources dictionary
|
|
89
|
+
color: 'FRAME_TEXTURE_VIEW',
|
|
90
|
+
},
|
|
91
|
+
options: {
|
|
92
|
+
//zIndex: 5,
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// use resizeViewport to init values on load:
|
|
97
|
+
resizeViewport(Game.renderer, window.innerWidth, window.innerHeight)
|
|
98
|
+
|
|
99
|
+
// window resize is *expensive* - best to debounce:
|
|
100
|
+
const debouncedResize = debounce(function () {
|
|
101
|
+
resizeViewport(Game.renderer, window.innerWidth, window.innerHeight)
|
|
102
|
+
}, 50)
|
|
103
|
+
|
|
104
|
+
window.addEventListener('resize', debouncedResize, { passive: true })
|
|
105
|
+
|
|
106
|
+
const world = ECS.createWorld()
|
|
107
|
+
ECS.addSystem(world, rendererSystem(Game.renderer))
|
|
108
|
+
|
|
109
|
+
Cobalt.setViewportPosition(Game.renderer, [ 240, 135 ])
|
|
110
|
+
|
|
111
|
+
Game.world = world
|
|
112
|
+
|
|
113
|
+
const gameLoop = function () {
|
|
114
|
+
const newTime = performance.now()
|
|
115
|
+
const frameTime = newTime - Game.lastFrameTime
|
|
116
|
+
Game.lastFrameTime = newTime
|
|
117
|
+
ECS.update(world, frameTime)
|
|
118
|
+
ECS.cleanup(world)
|
|
119
|
+
|
|
120
|
+
render(Game)
|
|
121
|
+
|
|
122
|
+
requestAnimationFrame(gameLoop)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let box1Angle = 0
|
|
126
|
+
let box2Angle = 0
|
|
127
|
+
|
|
128
|
+
const render = function (context) {
|
|
129
|
+
const pNode = context.renderer.nodes.find((n) => n.type === 'cobalt:primitives')
|
|
130
|
+
|
|
131
|
+
pNode.clear()
|
|
132
|
+
|
|
133
|
+
// start end color width
|
|
134
|
+
pNode.line([ 10, 10 ], [ 150, 100 ], [ 1, 0, 0, 1 ], 5)
|
|
135
|
+
pNode.line([ 100, 10 ], [ 100, 100 ], [ 1, 1, 0, 1 ], 1)
|
|
136
|
+
pNode.line([ 450, 10 ], [ 100, 100 ], [ 0, 1, 1, 1 ], 1)
|
|
137
|
+
|
|
138
|
+
pNode.filledEllipse([ 180, 80 ], 20, 40, 20, [ 1, 0, 1, 0.6 ])
|
|
139
|
+
|
|
140
|
+
pNode.filledEllipse([ 192, 140 ], 8, 8, 20, [ 1, 1, 1, 0.9 ])
|
|
141
|
+
|
|
142
|
+
pNode.ellipse([ 240, 140 ], 8, 18, 20, [ 0, 0, 1, 0.9 ])
|
|
143
|
+
|
|
144
|
+
// pos w h c ϴ
|
|
145
|
+
pNode.filledBox([ 50, 170 ], 42, 23, [ 0, 1, 0, 1 ], box1Angle)
|
|
146
|
+
|
|
147
|
+
// pos w h c ϴ lineW
|
|
148
|
+
pNode.box([ 120, 130 ], 42, 23, [ 1, 0, 1, 1 ], box2Angle, 1)
|
|
149
|
+
|
|
150
|
+
box1Angle -= 0.05
|
|
151
|
+
box2Angle += 0.05
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
requestAnimationFrame(gameLoop)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
function resizeViewport (renderer, width, height) {
|
|
159
|
+
|
|
160
|
+
const { canvas, device } = renderer
|
|
161
|
+
|
|
162
|
+
// determine which screen dimension is most constrained
|
|
163
|
+
// we floor the render scale to an integer because we get weird texture artifacts when trying to render at
|
|
164
|
+
// certain float values (e.g., 3.0145833333333334)
|
|
165
|
+
const renderScale = Math.floor(Math.min(width/constants.GAME_WIDTH, height/constants.GAME_HEIGHT))
|
|
166
|
+
|
|
167
|
+
canvas.width = Math.ceil(constants.GAME_WIDTH)
|
|
168
|
+
canvas.height = Math.ceil(constants.GAME_HEIGHT)
|
|
169
|
+
|
|
170
|
+
Cobalt.setViewportDimensions(renderer, constants.GAME_WIDTH, constants.GAME_HEIGHT)
|
|
171
|
+
|
|
172
|
+
// https://www.khronos.org/webgl/wiki/HandlingHighDPI
|
|
173
|
+
// webgl display resolution size within canvas
|
|
174
|
+
const resolution = window.devicePixelRatio || 1
|
|
175
|
+
|
|
176
|
+
// center the canvas if native window doesn't match aspect ratio
|
|
177
|
+
canvas.style.width = canvas.width * renderScale + 'px'
|
|
178
|
+
canvas.style.height = canvas.height * renderScale + 'px'
|
|
179
|
+
|
|
180
|
+
canvas.style.left = Math.round((width - canvas.width * renderScale) / 2) + 'px'
|
|
181
|
+
canvas.style.top = Math.round((height - canvas.height * renderScale) / 2) + 'px'
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
main()
|
|
186
|
+
|
|
187
|
+
</script>
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
</body>
|
|
191
|
+
</html>
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
const onUpdate = function (/*dt*/) {
|
|
21
|
+
|
|
22
|
+
const device = renderer.device
|
|
23
|
+
const context = renderer.context
|
|
24
|
+
|
|
25
|
+
ECS.getEntities(world, SPRITE_QUERY, 'removed', removedEntities)
|
|
26
|
+
|
|
27
|
+
for (let i=0; i < removedEntities.count; i++) {
|
|
28
|
+
const oldSprite = removedEntities.entries[i]
|
|
29
|
+
oldSprite.spriteNode.removeSprite(oldSprite.sprite.cobaltSpriteId)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Cobalt.draw(Game.renderer)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { onUpdate }
|
|
36
|
+
}
|
|
37
|
+
}
|