Spaces:
Running
Running
implement game ticks
Browse files- index.js +62 -33
- wgpu-config.js +2 -1
index.js
CHANGED
@@ -17,16 +17,31 @@ async function main() {
|
|
17 |
if (!device) return;
|
18 |
|
19 |
state.device = device;
|
|
|
|
|
20 |
const shaderCode = await fetchShaderCode('shaders.wgsl');
|
21 |
const vertexSize = config.floatsPerVertex * 4;
|
22 |
|
|
|
23 |
state.pipeline = await createPipeline(state.device, presentationFormat, vertexSize, shaderCode);
|
24 |
|
|
|
25 |
const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
|
26 |
document.body.appendChild(glyphCanvas);
|
27 |
glyphCanvas.style.backgroundColor = '#222';
|
28 |
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
state.vertexBuffer = state.device.createBuffer({
|
31 |
label: 'vertices',
|
32 |
size: vertexBufferSize,
|
@@ -39,25 +54,31 @@ async function main() {
|
|
39 |
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
40 |
});
|
41 |
|
42 |
-
const indices =
|
|
|
|
|
|
|
|
|
|
|
43 |
const ndx = Math.floor(i / 6) * 4;
|
44 |
return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
|
45 |
});
|
46 |
-
|
47 |
|
|
|
48 |
const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, config, glyphCanvas);
|
49 |
state.device.queue.writeBuffer(state.vertexBuffer, 0, vertexData);
|
50 |
|
51 |
state.texture = createTextureFromSource(state.device, glyphCanvas, { mips: true });
|
52 |
state.sampler = state.device.createSampler({
|
53 |
minFilter: 'linear',
|
54 |
-
magFilter: 'linear'
|
55 |
});
|
56 |
|
57 |
state.uniformBuffer = state.device.createBuffer({
|
58 |
label: 'uniforms for quad',
|
59 |
size: config.uniformBufferSize,
|
60 |
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
61 |
});
|
62 |
|
63 |
state.matrix = state.uniformValues.subarray(0, 16);
|
@@ -74,42 +95,50 @@ async function main() {
|
|
74 |
state.numGlyphs = numGlyphs;
|
75 |
state.width = width;
|
76 |
state.height = height;
|
77 |
-
|
78 |
-
gameLoop(context);
|
79 |
}
|
80 |
|
81 |
function gameLoop(context) {
|
82 |
let lastTime = performance.now();
|
|
|
83 |
|
84 |
-
function
|
85 |
const currentTime = performance.now();
|
86 |
-
const deltaTime = (currentTime - lastTime) / 1000;
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = context.getCurrentTexture().createView();
|
95 |
-
const encoder = state.device.createCommandEncoder();
|
96 |
-
const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
|
97 |
-
pass.setPipeline(state.pipeline);
|
98 |
-
mat4.rotateY(viewProjectionMatrix, state.time, state.matrix);
|
99 |
-
mat4.translate(state.matrix, [-state.width / 2, -state.height / 2, 0], state.matrix);
|
100 |
-
state.device.queue.writeBuffer(state.uniformBuffer, 0, state.uniformValues);
|
101 |
-
pass.setBindGroup(0, state.bindGroup);
|
102 |
-
pass.setVertexBuffer(0, state.vertexBuffer);
|
103 |
-
pass.setIndexBuffer(state.indexBuffer, 'uint32');
|
104 |
-
pass.drawIndexed(state.numGlyphs * 6);
|
105 |
-
pass.end();
|
106 |
-
state.device.queue.submit([encoder.finish()]);
|
107 |
-
|
108 |
-
lastTime = currentTime;
|
109 |
-
setTimeout(update, 16); // Approximate 60 FPS
|
110 |
}
|
111 |
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
}
|
114 |
|
115 |
main();
|
|
|
17 |
if (!device) return;
|
18 |
|
19 |
state.device = device;
|
20 |
+
|
21 |
+
// Load shader code
|
22 |
const shaderCode = await fetchShaderCode('shaders.wgsl');
|
23 |
const vertexSize = config.floatsPerVertex * 4;
|
24 |
|
25 |
+
// Create pipeline
|
26 |
state.pipeline = await createPipeline(state.device, presentationFormat, vertexSize, shaderCode);
|
27 |
|
28 |
+
// Generate and display glyph texture atlas
|
29 |
const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
|
30 |
document.body.appendChild(glyphCanvas);
|
31 |
glyphCanvas.style.backgroundColor = '#222';
|
32 |
|
33 |
+
// Create vertex and index buffers
|
34 |
+
createBuffers();
|
35 |
+
|
36 |
+
// Generate vertex buffer data and texture
|
37 |
+
generateVertexDataAndTexture(glyphCanvas);
|
38 |
+
|
39 |
+
// Start the game loop
|
40 |
+
gameLoop(context);
|
41 |
+
}
|
42 |
+
|
43 |
+
function createBuffers() {
|
44 |
+
const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * config.floatsPerVertex * 4;
|
45 |
state.vertexBuffer = state.device.createBuffer({
|
46 |
label: 'vertices',
|
47 |
size: vertexBufferSize,
|
|
|
54 |
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
55 |
});
|
56 |
|
57 |
+
const indices = generateIndices(config.maxGlyphs);
|
58 |
+
state.device.queue.writeBuffer(state.indexBuffer, 0, new Uint32Array(indices));
|
59 |
+
}
|
60 |
+
|
61 |
+
function generateIndices(maxGlyphs) {
|
62 |
+
return Array.from({ length: maxGlyphs * 6 }, (_, i) => {
|
63 |
const ndx = Math.floor(i / 6) * 4;
|
64 |
return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
|
65 |
});
|
66 |
+
}
|
67 |
|
68 |
+
function generateVertexDataAndTexture(glyphCanvas) {
|
69 |
const { vertexData, numGlyphs, width, height } = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, config, glyphCanvas);
|
70 |
state.device.queue.writeBuffer(state.vertexBuffer, 0, vertexData);
|
71 |
|
72 |
state.texture = createTextureFromSource(state.device, glyphCanvas, { mips: true });
|
73 |
state.sampler = state.device.createSampler({
|
74 |
minFilter: 'linear',
|
75 |
+
magFilter: 'linear',
|
76 |
});
|
77 |
|
78 |
state.uniformBuffer = state.device.createBuffer({
|
79 |
label: 'uniforms for quad',
|
80 |
size: config.uniformBufferSize,
|
81 |
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
82 |
});
|
83 |
|
84 |
state.matrix = state.uniformValues.subarray(0, 16);
|
|
|
95 |
state.numGlyphs = numGlyphs;
|
96 |
state.width = width;
|
97 |
state.height = height;
|
|
|
|
|
98 |
}
|
99 |
|
100 |
function gameLoop(context) {
|
101 |
let lastTime = performance.now();
|
102 |
+
const frameInterval = 1000 / config.maxFPS;
|
103 |
|
104 |
+
function tick() {
|
105 |
const currentTime = performance.now();
|
106 |
+
const deltaTime = (currentTime - lastTime) / 1000;
|
107 |
+
|
108 |
+
if (currentTime - lastTime >= frameInterval) {
|
109 |
+
update(deltaTime, context);
|
110 |
+
lastTime = currentTime;
|
111 |
+
}
|
112 |
+
|
113 |
+
setTimeout(tick, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
}
|
115 |
|
116 |
+
tick();
|
117 |
+
}
|
118 |
+
|
119 |
+
function update(deltaTime, context) {
|
120 |
+
state.time += deltaTime;
|
121 |
+
|
122 |
+
// Set up projection and view matrices
|
123 |
+
const fov = 60 * Math.PI / 180;
|
124 |
+
const aspect = canvas.clientWidth / canvas.clientHeight;
|
125 |
+
const projectionMatrix = mat4.perspective(fov, aspect, config.render.zNear, config.render.zFar);
|
126 |
+
const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
|
127 |
+
const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
|
128 |
+
RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = context.getCurrentTexture().createView();
|
129 |
+
|
130 |
+
const encoder = state.device.createCommandEncoder();
|
131 |
+
const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
|
132 |
+
pass.setPipeline(state.pipeline);
|
133 |
+
mat4.rotateY(viewProjectionMatrix, state.time, state.matrix);
|
134 |
+
mat4.translate(state.matrix, [-state.width / 2, -state.height / 2, 0], state.matrix);
|
135 |
+
state.device.queue.writeBuffer(state.uniformBuffer, 0, state.uniformValues);
|
136 |
+
pass.setBindGroup(0, state.bindGroup);
|
137 |
+
pass.setVertexBuffer(0, state.vertexBuffer);
|
138 |
+
pass.setIndexBuffer(state.indexBuffer, 'uint32');
|
139 |
+
pass.drawIndexed(state.numGlyphs * 6);
|
140 |
+
pass.end();
|
141 |
+
state.device.queue.submit([encoder.finish()]);
|
142 |
}
|
143 |
|
144 |
main();
|
wgpu-config.js
CHANGED
@@ -21,5 +21,6 @@ export const config = {
|
|
21 |
render: {
|
22 |
zNear: 0.001,
|
23 |
zFar: 50
|
24 |
-
}
|
|
|
25 |
};
|
|
|
21 |
render: {
|
22 |
zNear: 0.001,
|
23 |
zFar: 50
|
24 |
+
},
|
25 |
+
maxFPS: 60
|
26 |
};
|