Skip to content

Commit 1d496a0

Browse files
authored
Keyboard input support (#145)
Fixes #144
1 parent 154831b commit 1d496a0

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

src/components/Help.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ defineExpose({
7272
<li><code>sign(mouze.z)</code>: button is down</br></li>
7373
<li><code>sign(mouze.w)</code>: button is clicked</br></li>
7474
</ul>
75+
<h4 class="doc-header"><code>[playground::KEY("KeyA")]</code></h4>
76+
Sets a scalar uniform to <code>1</code> if the specified key
77+
is pressed, <code>0</code> otherwise. Key name comes from either javascript <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code"><code>event.code</code></a> or <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key"><code>event.key</code></a>.
7578
<h4 class="doc-header"><code>[playground::SLIDER(0.3, 0.0, 1.0)]</code></h4>
7679
Control a <code>float</code> uniform with a provided default, minimum, and maximum.
7780
<h4 class="doc-header"><code>[playground::COLOR_PICK(0.5, 0.5, 0.5)]</code></h4>

src/components/RenderCanvas.vue

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ let canvasCurrentMousePos = { x: 0, y: 0 };
3131
let canvasIsMouseDown = false;
3232
let canvasMouseClicked = false;
3333
34+
const pressedKeys = new Set<string>();
35+
3436
const canvas = useTemplateRef("canvas");
3537
const frameTime = ref(0);
3638
@@ -63,8 +65,21 @@ onMounted(() => {
6365
// therefore, we have to set the resolution same as the container size.
6466
const observer = new ResizeObserver((entries) => { resizeCanvasHandler(entries); });
6567
observer.observe(canvas.value);
68+
69+
window.addEventListener('keydown', handleKeyDown);
70+
window.addEventListener('keyup', handleKeyUp);
6671
})
6772
73+
function handleKeyDown(event: KeyboardEvent) {
74+
pressedKeys.add(event.key);
75+
pressedKeys.add(event.code);
76+
}
77+
78+
function handleKeyUp(event: KeyboardEvent) {
79+
pressedKeys.delete(event.key);
80+
pressedKeys.delete(event.code);
81+
}
82+
6883
function resizeCanvas(entries: ResizeObserverEntry[]) {
6984
const canvas = entries[0].target;
7085
@@ -318,6 +333,28 @@ async function execFrame(timeMS: number, currentDisplayMode: ShaderType, playgro
318333
uniformBufferView.setFloat32(offset + 4, canvasCurrentMousePos.y, true);
319334
uniformBufferView.setFloat32(offset + 8, canvasLastMouseDownPos.x * (canvasIsMouseDown ? -1 : 1), true);
320335
uniformBufferView.setFloat32(offset + 12, canvasLastMouseDownPos.y * (canvasMouseClicked ? -1 : 1), true);
336+
} else if (uniformComponent.type == "KEY") {
337+
// Set 1 or 0 depending on key state, using correct type
338+
const isPressed = pressedKeys.has(uniformComponent.key);
339+
if (uniformComponent.scalarType == "float32") {
340+
uniformBufferView.setFloat32(offset, isPressed ? 1.0 : 0.0, true);
341+
} else if (uniformComponent.scalarType == "float64") {
342+
uniformBufferView.setFloat64(offset, isPressed ? 1.0 : 0.0, true);
343+
} else if (uniformComponent.scalarType == "int8") {
344+
uniformBufferView.setInt8(offset, isPressed ? 1 : 0);
345+
} else if (uniformComponent.scalarType == "int16") {
346+
uniformBufferView.setInt16(offset, isPressed ? 1 : 0, true);
347+
} else if (uniformComponent.scalarType == "int32") {
348+
uniformBufferView.setInt32(offset, isPressed ? 1 : 0, true);
349+
} else if (uniformComponent.scalarType == "uint8") {
350+
uniformBufferView.setUint8(offset, isPressed ? 1 : 0);
351+
} else if (uniformComponent.scalarType == "uint16") {
352+
uniformBufferView.setUint16(offset, isPressed ? 1 : 0, true);
353+
} else if (uniformComponent.scalarType == "uint32") {
354+
uniformBufferView.setUint32(offset, isPressed ? 1 : 0, true);
355+
} else {
356+
throw new Error("KEY_INPUT only scalar type not supported");
357+
}
321358
} else {
322359
let _: never = uniformComponent;
323360
throw new Error("Invalid state");
@@ -749,6 +786,12 @@ async function processResourceCommands(resourceBindings: Bindings, resourceComma
749786
// Initialize the buffer with zeros.
750787
let bufferDefault: BufferSource = new Float32Array([0, 0, 0, 0]);
751788
device.queue.writeBuffer(buffer, parsedCommand.offset, bufferDefault);
789+
} else if (parsedCommand.type == "KEY") {
790+
const buffer = allocatedResources.get("uniformInput") as GPUBuffer
791+
792+
// Initialize the buffer with zeros.
793+
let bufferDefault: BufferSource = new Float32Array([0]);
794+
device.queue.writeBuffer(buffer, parsedCommand.offset, bufferDefault);
752795
} else {
753796
// exhaustiveness check
754797
let x: never = parsedCommand;

src/slang/playground.slang

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ public struct playground_MOUSE_POSITIONAttribute
140140
{
141141
};
142142

143+
// Checks if a given key is down. `1.0` if key is down, `0.0` otherwise. Key name comes from either javascript `event.code` or `event.key`.
144+
[__AttributeUsage(_AttributeTargets.Var)]
145+
public struct playground_KEYAttribute
146+
{
147+
string keyName;
148+
};
149+
143150
// Control a \`float\` uniform with a provided default, minimum, and maximum.
144151
[__AttributeUsage(_AttributeTargets.Var)]
145152
public struct playground_SLIDERAttribute

src/util.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ export type ParsedCommand = {
257257
} | {
258258
"type": "MOUSE_POSITION",
259259
"offset": number,
260+
} | {
261+
"type": "KEY",
262+
key: string,
263+
offset: number,
264+
scalarType: ScalarType,
260265
} | {
261266
"type": "SAMPLER"
262267
};
@@ -394,6 +399,20 @@ export function getResourceCommandsFromAttributes(reflection: ReflectionJSON): R
394399
elementSize: parseInt(parameter.type.elementType.scalarType.slice(5)) / 8,
395400
offset: parameter.binding.offset,
396401
};
402+
} else if (playground_attribute_name == "KEY") {
403+
// Only allow on scalar uniforms (float or int)
404+
if (parameter.type.kind != "scalar" || parameter.binding.kind != "uniform") {
405+
throw new Error(`${playground_attribute_name} attribute can only be applied to scalar uniforms`);
406+
}
407+
if (!attribute.arguments || attribute.arguments.length !== 1 || typeof attribute.arguments[0] !== "string") {
408+
throw new Error(`${playground_attribute_name} attribute requires a single string argument (the key name)`);
409+
}
410+
command = {
411+
type: playground_attribute_name,
412+
key: attribute.arguments[0] as string,
413+
offset: parameter.binding.offset,
414+
scalarType: parameter.type.scalarType,
415+
};
397416
}
398417

399418
if (command != null) {
@@ -433,6 +452,10 @@ export type UniformController = { buffer_offset: number } & ({
433452
type: "TIME",
434453
} | {
435454
type: "MOUSE_POSITION",
455+
} | {
456+
type: "KEY",
457+
key: string,
458+
scalarType: ScalarType,
436459
})
437460

438461
export function isControllerRendered(controller: UniformController) {
@@ -468,6 +491,13 @@ export function getUniformControllers(resourceCommands: ResourceCommand[]): Unif
468491
type: resourceCommand.parsedCommand.type,
469492
buffer_offset: resourceCommand.parsedCommand.offset,
470493
})
494+
} else if (resourceCommand.parsedCommand.type == 'KEY') {
495+
controllers.push({
496+
type: resourceCommand.parsedCommand.type,
497+
buffer_offset: resourceCommand.parsedCommand.offset,
498+
key: resourceCommand.parsedCommand.key,
499+
scalarType: resourceCommand.parsedCommand.scalarType,
500+
})
471501
}
472502
}
473503
return controllers;

0 commit comments

Comments
 (0)