From 1d76808f02a4545f236d8619ac0bafaffc578668 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Wed, 5 Aug 2026 00:38:02 -0500 Subject: [PATCH] snake demo --- index.html | 2 + index.js | 80 +++++++------ snake.js | 201 +++++++++++++++++++++++++++++++++ snake.wgsl | 90 +++++++++++++++ src/Makefile | 23 +++- src/camera.cpp | 2 + src/fluid/particle.cpp | 1 - src/memory.h | 2 + src/snake/api.cpp | 48 ++++++++ src/snake/bone.cpp | 41 +++++++ src/snake/bone.h | 13 +++ src/snake/bone_configuration.h | 10 ++ src/snake/bone_system.cpp | 35 ++++++ src/snake/bone_system.h | 15 +++ src/snake/configuration.cpp | 5 + 15 files changed, 531 insertions(+), 37 deletions(-) create mode 100644 snake.js create mode 100644 snake.wgsl create mode 100644 src/snake/api.cpp create mode 100644 src/snake/bone.cpp create mode 100644 src/snake/bone.h create mode 100644 src/snake/bone_configuration.h create mode 100644 src/snake/bone_system.cpp create mode 100644 src/snake/bone_system.h create mode 100644 src/snake/configuration.cpp diff --git a/index.html b/index.html index 605c28f..909d1a6 100644 --- a/index.html +++ b/index.html @@ -129,10 +129,12 @@ --> + diff --git a/index.js b/index.js index 137fd3e..2f3516a 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,8 @@ import { getPath } from "./common.js"; import { loadGltf } from "./index2.js"; import { loadLight } from "./light.js"; import { loadFlat } from "./flat.js"; +import { loadSnake } from "./snake.js"; +import { loadFont } from "./font.js"; if (!navigator.gpu) { throw new Error("WebGPU not supported on this browser."); @@ -45,6 +47,7 @@ const module = await WebAssembly.instantiateStreaming(fetch("src/module.wasm"), env: { memory: memory, log: console.log, + logStrInt: console.log, } }); module.memory = memory; @@ -82,7 +85,7 @@ function recreateDepth(force) const matrixSize = 4 * 4 * 4; const float4Size = 4 * 4; const float3Size = 4 * 3; -const viewUniformBufferSize = (matrixSize * 2) + (float4Size * 2); +const viewUniformBufferSize = (matrixSize * 2) + (float4Size * 2) + (4 * 4); const viewUniformBuffer = device.createBuffer({ label: "view uniform buffer", @@ -99,8 +102,9 @@ module.instance.exports.camera_init(cameraStateAddress); //const gltfRenderer = await loadGltf(device, canvasFormat, viewUniformBuffer, memory, module); //const lightRenderer = await loadLight(device, canvasFormat, viewUniformBuffer); -const flatRenderer = await loadFlat(device, canvasFormat, viewUniformBuffer, module); - +//const flatRenderer = await loadFlat(device, canvasFormat, viewUniformBuffer, module); +//const snakeRenderer = await loadSnake(device, canvasFormat, viewUniformBuffer, module); +const fontRenderer = await loadFont(device, canvasFormat, viewUniformBuffer, module); const KEY = { A: 65, @@ -189,15 +193,6 @@ function updateView() device.queue.writeBuffer(viewUniformBuffer, 0, memory.buffer, viewStateAddress, viewUniformBufferSize); - - const cameraStateF32 = new Float32Array(memory.buffer, cameraStateAddress, cameraStateSize); - /* - eyeValueX.innerHTML = cameraStateF32[0].toFixed(1); - eyeValueY.innerHTML = cameraStateF32[1].toFixed(1); - eyeValueZ.innerHTML = cameraStateF32[2].toFixed(1); - yawValue.innerHTML = cameraStateF32[9].toFixed(2); - pitchValue.innerHTML = cameraStateF32[10].toFixed(2); - */ } const controlMain = document.getElementById("control-main"); @@ -207,7 +202,7 @@ function remap(low1, high1, low2, high2, value) return low2 + (value - low1) * (high2 - low2) / (high1 - low1); } -const sliderDefaults = {"test intensity":"0","mass12 mix":"0","circle thickness":"171","line thickness":"107","particle mass2":"13","particle count":"19","target density":"954","particle size":"70","pressure":"19","line length":"6","particle mass1":"1024","circle radius":"379"}; +const sliderDefaults = {"bone length":"32","velocity acc.":"385","velocity damp.":"796","bone count":"40"}; class Slider { constructor(label, low, high, max = 1024) @@ -246,6 +241,7 @@ class Slider { } else if (this.label in sliderDefaults) { this.input.value = sliderDefaults[this.label]; } + this.input.value = sliderDefaults[this.label]; this.update(); } @@ -267,7 +263,8 @@ class Slider { } } -const sliders = { +/* +const flatSliders = { particleSize: new Slider("particle size", 0.0, 0.05), particleMass1: new Slider("particle mass1", 0.0, 0.05), particleMass2: new Slider("particle mass2", 0.0, 1.0), @@ -280,27 +277,25 @@ const sliders = { lineLength: new Slider("line length", 0.0, 9.0), targetDensity: new Slider("target density", -10.0, 10.0), pressure: new Slider("pressure", 0.0, 1.0), -}; + }; +*/ const mousePosition = new Float32Array([0, 0, 0, 0]); -function updateSliders() +const snakeSliders = { + boneCount: new Slider("bone count", 0.0, 100.0, 100), + boneLength: new Slider("bone length", 0.0, 0.5), + velocityDamping: new Slider("velocity damp.", 0.0, 1.0), + velocityAcceleration: new Slider("velocity acc.", 0.0, 1.0), +}; + +function updateSliders(sliders) { - return { - mousePosition: mousePosition, - particleSize: sliders.particleSize.update(), - particleMass1: sliders.particleMass1.update(), - particleMass2: sliders.particleMass2.update(), - particleCount: sliders.particleCount.update(), - circleRadius: sliders.circleRadius.update(), - circleThickness: sliders.circleThickness.update(), - testIntensity: sliders.testIntensity.update(), - mass12Mix: sliders.mass12Mix.update(), - lineThickness: sliders.lineThickness.update(), - lineLength: sliders.lineLength.update(), - targetDensity: sliders.targetDensity.update(), - pressure: sliders.pressure.update(), - }; + const values = {}; + for (let [key, value] of Object.entries(sliders)) { + values[key] = value.update(); + } + return values; } function onClick(e) @@ -308,14 +303,24 @@ function onClick(e) mousePosition[0] = e.clientX / canvas.width; mousePosition[1] = 1.0 - (e.clientY / canvas.height); } -canvas.addEventListener("click", onClick); +//canvas.addEventListener("click", onClick); + +function onMouseMove(e) +{ + const aspect = canvas.width / canvas.height; + + mousePosition[0] = ((e.clientX / canvas.width) * 2 - 1) * aspect; + mousePosition[1] = (1.0 - (e.clientY / canvas.height)) * 2 - 1; +} +canvas.addEventListener("mousemove", onMouseMove); var canRender = false; var frameNumber = 0; function render2() { if (canRender === true) { - const configuration = updateSliders(); + //const configuration = updateSliders(flatSliders); + const configuration = updateSliders(snakeSliders); canRender = false; recreateDepth(false); @@ -341,7 +346,12 @@ function render2() //gltfRenderer.render(renderPass); //lightRenderer.render(renderPass); - flatRenderer.render(device, renderPass, frameNumber, configuration); + //flatRenderer.render(device, renderPass, frameNumber, configuration); + + //snakeRenderer.update(device, frameNumber, mousePosition, configuration); + //snakeRenderer.render(renderPass, frameNumber); + + fontRenderer.render(renderPass, frameNumber); renderPass.end(); @@ -357,7 +367,7 @@ function setCanRender() { canRender = true; } -setInterval(setCanRender, 66.67); // 15 fps +setInterval(setCanRender, 33.33); // 15 fps recreateDepth(true); requestAnimationFrame(render2); diff --git a/snake.js b/snake.js new file mode 100644 index 0000000..aa0ee30 --- /dev/null +++ b/snake.js @@ -0,0 +1,201 @@ +import { getPath } from "./common.js"; + +class SnakeRenderer { + createBuffers(device) + { + const boneCount = 100; + const boneLength = 0.1; + this.boneSystemAddress = this.module.instance.exports.bone_system__create(boneCount, boneLength); + const mem = new DataView(this.module.memory.buffer); + this.bonesAddress = mem.getUint32(this.boneSystemAddress, true); + const configurationOffset = this.module.instance.exports.bone_system__configuration_offset(); + this.configurationAddress = this.boneSystemAddress + configurationOffset; + this.bonesBufferSize = this.module.instance.exports.bone_system__bones_size(boneCount); + this.configurationBufferSize = this.module.instance.exports.bone_system__configuration_size(); + + this.frames = []; + + for (let i = 0; i < 2; i++) { + const bonesBuffer = device.createBuffer({ + label: `bone buffer ${i}`, + size: this.bonesBufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + + const configurationBuffer = device.createBuffer({ + label: `bone configuration buffer ${i}`, + size: this.configurationBufferSize, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + + const bindGroup = device.createBindGroup({ + label: `bone buffer bind group ${i}`, + layout: this.bindGroupLayout.bone, + entries: [{ + binding: 0, + resource: { buffer: bonesBuffer }, + }, { + binding: 1, + resource: { buffer: configurationBuffer }, + }] + }); + + this.frames.push({ + bonesBuffer: bonesBuffer, + configurationBuffer: configurationBuffer, + bindGroup: bindGroup, + }); + } + } + + constructor(device, canvasFormat, viewUniformBuffer, wgsl, module) + { + const label = "bone"; + + this.module = module; + + ////////////////////////////////////////////////////////////////////// + // shader module + ////////////////////////////////////////////////////////////////////// + + const shaderModule = device.createShaderModule({ + label: `${label} shader`, + code: wgsl, + }); + + ////////////////////////////////////////////////////////////////////// + // index buffer + ////////////////////////////////////////////////////////////////////// + + const array = new Uint16Array(6); + array[0] = 0; + array[1] = 1; + array[2] = 2; + array[3] = 0; + array[4] = 3; + array[5] = 1; + + this.buffer = device.createBuffer({ + label: `${label} renderer buffer`, + size: array.byteLength, + usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, + }); + + device.queue.writeBuffer(this.buffer, 0, array, 0, array.length); + + ////////////////////////////////////////////////////////////////////// + // pipeline + ////////////////////////////////////////////////////////////////////// + + this.bindGroupLayout = { + view: device.createBindGroupLayout({ + label: `${label} bind group layout view`, + entries: [{ + binding: 0, + visibility: GPUShaderStage.VERTEX, + buffer: { type: "uniform" } + }] + }), + bone: device.createBindGroupLayout({ + label: `${label} bind group layout particle`, + entries: [{ // bones + binding: 0, + visibility: GPUShaderStage.FRAGMENT, + buffer: { type: "read-only-storage" } + }, { // configuration + binding: 1, + visibility: GPUShaderStage.FRAGMENT, + buffer: { type: "uniform" } + }] + }), + }; + + const pipelineLayout = device.createPipelineLayout({ + label: `${label} pipeline layout`, + bindGroupLayouts: [ + this.bindGroupLayout.view, + this.bindGroupLayout.bone + ], + }); + + this.renderPipeline = device.createRenderPipeline({ + label: `${label} pipeline`, + layout: pipelineLayout, + vertex: { + module: shaderModule, + entryPoint: "vertexMain", + }, + fragment: { + module: shaderModule, + entryPoint: "fragmentMain", + targets: [{ + format: canvasFormat, + }] + }, + primitive: { + topology: "triangle-list", + }, + depthStencil: { + depthWriteEnabled: true, + depthCompare: "less", + format: "depth24plus", + }, + }); + + ////////////////////////////////////////////////////////////////////// + // bones + ////////////////////////////////////////////////////////////////////// + + this.createBuffers(device); + + ////////////////////////////////////////////////////////////////////// + // bind group + ////////////////////////////////////////////////////////////////////// + + this.viewBindGroup = device.createBindGroup({ + label: `${label} bind group`, + layout: this.bindGroupLayout.view, + entries: [{ + binding: 0, + resource: { buffer: viewUniformBuffer }, + }], + }); + } + + update(device, frameNumber, mousePosition, configuration) + { + this.module.instance.exports.bone_system__update_configuration(this.boneSystemAddress, + configuration.boneCount, + configuration.boneLength, + configuration.velocityDamping, + configuration.velocityAcceleration); + + this.module.instance.exports.bone_system__update(this.boneSystemAddress, + mousePosition[0], mousePosition[1], mousePosition[2]); + + device.queue.writeBuffer(this.frames[frameNumber].bonesBuffer, 0, + this.module.memory.buffer, this.bonesAddress, this.bonesBufferSize); + + device.queue.writeBuffer(this.frames[frameNumber].configurationBuffer, 0, + this.module.memory.buffer, this.configurationAddress, this.configurationBufferSize); + } + + render(renderPass, frameNumber) + { + renderPass.setPipeline(this.renderPipeline); + renderPass.setIndexBuffer(this.buffer, "uint16"); + renderPass.setBindGroup(0, this.viewBindGroup); + renderPass.setBindGroup(1, this.frames[frameNumber].bindGroup); + renderPass.drawIndexed(6); + } +} + +async function loadSnake(device, canvasFormat, viewUniformBuffer, module) +{ + const snakeWgsl = await getPath("snake.wgsl"); + + const renderer = new SnakeRenderer(device, canvasFormat, viewUniformBuffer, snakeWgsl, module); + return renderer; +} + +export { loadSnake }; diff --git a/snake.wgsl b/snake.wgsl new file mode 100644 index 0000000..912db59 --- /dev/null +++ b/snake.wgsl @@ -0,0 +1,90 @@ +struct Configuration { + viewProj: mat4x4f, + lightViewProj: mat4x4f, + lightPosition: vec4f, + eyePosition: vec4f, + aspect: f32, +}; + +struct Bone { + position: vec4f, + velocity: vec4f, +}; + +struct BoneConfiguration { + a: vec4f, +}; + +@group(0) @binding(0) var config: Configuration; +@group(1) @binding(0) var bones: array; +@group(1) @binding(1) var boneConfig: BoneConfiguration; + +struct VertexInput { + @builtin(vertex_index) vertex_index: u32, +}; + +struct VertexOutput { + @builtin(position) position: vec4f, + @location(0) texture: vec2f, +}; + +const vertices = array( + vec2f(1.0, 0.0), + vec2f(0.0, 1.0), + vec2f(0.0, 0.0), + vec2f(1.0, 1.0), +); + +@vertex +fn vertexMain(input: VertexInput) -> VertexOutput +{ + let texture = vertices[input.vertex_index]; + + let position = texture * 2.0 - 1.0; + + var output: VertexOutput; + output.position = vec4f(position, 0.0, 1.0); + output.texture = position * vec2f(config.aspect, 1); + return output; +} + +fn sdCircle(center: vec2f, sample: vec2f) -> f32 +{ + let distance = length(center - sample); + return distance; +} + +fn sdSegment(p: vec2f, a: vec2f, b: vec2f) -> f32 +{ + let ba = b - a; + let pa = p - a; + let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); + return length(pa - h * ba); +} + +@fragment +fn fragmentMain(input: VertexOutput) -> @location(0) vec4f +{ + var color = vec3f(input.texture.xy, 0); + + for (var i: u32 = 0; i < u32(boneConfig.a.x); i++) { + if (sdCircle(input.texture.xy, bones[i].position.xy) < 0.01) { + color = vec3f(1, 1, 1); + } + + /* + if (sdSegment(input.texture.xy, + bones[i].position.xy, + bones[i].position.xy + bones[i].velocity.xy * 0.05) < 0.002) { + color = vec3(0, 0, 1); + } + */ + /* + if (i == 1 && sdCircle(input.texture.xy, bones[i].velocity.xy) < 0.05) { + color = vec3(0, 0, 1); + } + */ + } + + return vec4f(color, 1); +} diff --git a/src/Makefile b/src/Makefile index 8c4872e..36abffb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,6 +9,7 @@ CFLAGS = \ -nostdlib \ -I$(MINIZ) \ -I. \ + -I../include \ -Werror \ -Wfatal-errors \ -D_XM_NO_INTRINSICS_ @@ -36,6 +37,21 @@ LDFLAGS = \ -Wl,--export=particle_system__update \ -Wl,--export=particle__stride \ -Wl,--export=particle_configuration__stride \ + -Wl,--export=bone_system__create \ + -Wl,--export=bone_system__bones_size \ + -Wl,--export=bone_system__configuration_size \ + -Wl,--export=bone_system__configuration_offset \ + -Wl,--export=bone_system__update \ + -Wl,--export=bone_system__update_configuration \ + -Wl,--export=font_layout__create \ + -Wl,--export=font_layout__texture_address \ + -Wl,--export=font_layout__glyph_buffer_size \ + -Wl,--export=font_layout__glyph_buffer_address \ + -Wl,--export=font_layout__texture_size \ + -Wl,--export=font_layout__texture_address \ + -Wl,--export=font_layout__layout_buffer_size \ + -Wl,--export=font_layout__layout_buffer_address \ + -Wl,--export=font_layout__layout_buffer_index \ -Wl,--import-undefined \ -Wl,--print-map \ -Wl,--import-memory \ @@ -56,7 +72,12 @@ PNG_OBJ = \ camera.o \ fluid/particle.o \ fluid/particle_system.o \ - fluid/api.o + fluid/api.o \ + snake/bone.o \ + snake/bone_system.o \ + snake/api.o \ + font_layout/font_layout.o \ + font_layout/api.o module.wasm: $(PNG_OBJ) clang++ $(CFLAGS) $(LDFLAGS) -o $@ $^ diff --git a/src/camera.cpp b/src/camera.cpp index 9d072ea..975ffca 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -18,6 +18,7 @@ struct ViewState { XMFLOAT4X4 lightViewProj; XMFLOAT4 lightPosition; XMFLOAT4 eyePosition; + float aspect; }; static inline XMMATRIX camera_view(CameraState const * state) @@ -150,4 +151,5 @@ void camera_view_projection(CameraState * camera_state, XMStoreFloat4(&view_state->lightPosition, light_position); XMVECTOR eye = XMLoadFloat3(&camera_state->eye); XMStoreFloat4(&view_state->eyePosition, eye); + view_state->aspect = aspect; } diff --git a/src/fluid/particle.cpp b/src/fluid/particle.cpp index 54624bc..c3400a2 100644 --- a/src/fluid/particle.cpp +++ b/src/fluid/particle.cpp @@ -28,5 +28,4 @@ namespace fluid velocity.y *= -1; } } - } diff --git a/src/memory.h b/src/memory.h index b760cd5..e1df43d 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,5 +1,7 @@ #pragma once +#include + extern "C" { size_t mem_size(); size_t mem_alloc(size_t length); diff --git a/src/snake/api.cpp b/src/snake/api.cpp new file mode 100644 index 0000000..ce4b807 --- /dev/null +++ b/src/snake/api.cpp @@ -0,0 +1,48 @@ +#include "new.h" +#include "bone_system.h" + +using namespace snake; + +extern "C" { + void logStrInt(char const * s, int a); + + BoneSystem * bone_system__create(int boneCount, float boneLength) + { + BoneSystem * system = New(); + system->init(boneCount, boneLength); + + return system; + } + + int bone_system__bones_size(int boneCount) + { + return boneCount * (sizeof (Bone)); + } + + int bone_system__configuration_size() + { + return (sizeof (BoneConfiguration)); + } + + int bone_system__configuration_offset() + { + return (offsetof (BoneSystem, configuration)); + } + + void bone_system__update(BoneSystem * bone_system, float x, float y, float z) + { + bone_system->update(x, y, z); + } + + void bone_system__update_configuration(BoneSystem * bone_system, + int boneCount, + float boneLength, + float velocityDamping, + float velocityAcceleration) + { + bone_system->configuration.boneCount = boneCount; + bone_system->configuration.boneLength = boneLength; + bone_system->configuration.velocityDamping = velocityDamping; + bone_system->configuration.velocityAcceleration = velocityAcceleration; + } +}; diff --git a/src/snake/bone.cpp b/src/snake/bone.cpp new file mode 100644 index 0000000..2fe7b7b --- /dev/null +++ b/src/snake/bone.cpp @@ -0,0 +1,41 @@ +#include "bone.h" + +namespace snake { + extern "C" void logStrInt(char const * s, float a); + + // assumes ray does intersect with sphere + static inline XMVECTOR intersectRaySphere(XMVECTOR rayPosition, XMVECTOR rayDirection, + XMVECTOR sphereCenter, float sphereRadius) + { + XMVECTOR m = rayPosition - sphereCenter; + float b = XMVectorGetX(XMVector3Dot(m, rayDirection)); + float c = XMVectorGetX(XMVector3Dot(m, m)) - sphereRadius * sphereRadius; + float discriminant = b * b - c; + float t = -b - sqrtf(discriminant); + return rayPosition + t * rayDirection; + } + + void Bone::update(Bone const * parent, BoneConfiguration const * configuration) + { + XMVECTOR parentPosition = XMLoadFloat4(&parent->position); + XMVECTOR position = XMLoadFloat4(&this->position); + XMVECTOR velocity = XMLoadFloat4(&this->velocity); + + + position += velocity; + velocity *= configuration->velocityDamping; + + + XMVECTOR direction = parentPosition - position; + XMVECTOR normal = XMVector3Normalize(direction); + + XMVECTOR newPosition = intersectRaySphere(position, normal, + parentPosition, configuration->boneLength); + + XMVECTOR force = newPosition - position; + velocity += force * configuration->velocityAcceleration; + + XMStoreFloat4(&this->position, newPosition); + XMStoreFloat4(&this->velocity, velocity); + } +} diff --git a/src/snake/bone.h b/src/snake/bone.h new file mode 100644 index 0000000..44c7f8a --- /dev/null +++ b/src/snake/bone.h @@ -0,0 +1,13 @@ +#pragma once + +#include "directxmath/DirectXMath.h" +#include "bone_configuration.h" + +namespace snake { + struct Bone { + XMFLOAT4 position; + XMFLOAT4 velocity; + + void update(Bone const * parent, BoneConfiguration const * configuration); + }; +} diff --git a/src/snake/bone_configuration.h b/src/snake/bone_configuration.h new file mode 100644 index 0000000..ba1cc9d --- /dev/null +++ b/src/snake/bone_configuration.h @@ -0,0 +1,10 @@ +#pragma once + +namespace snake { + struct BoneConfiguration { + float boneCount; + float boneLength; + float velocityDamping; + float velocityAcceleration; + }; +} diff --git a/src/snake/bone_system.cpp b/src/snake/bone_system.cpp new file mode 100644 index 0000000..b8e3eb5 --- /dev/null +++ b/src/snake/bone_system.cpp @@ -0,0 +1,35 @@ +#include "new.h" +#include "bone_system.h" + +namespace snake { + void BoneSystem::init(int maxBones, float boneLength) + { + this->maxBones = maxBones; + + configuration.boneCount = maxBones; + configuration.boneLength = boneLength; + configuration.velocityDamping = 0.0; + configuration.velocityAcceleration = 0.0; + + bones = New(maxBones); + + XMVECTOR direction = XMVectorSet(0, 1, 0, 0); + for (int i = 0; i < maxBones; i++) { + XMStoreFloat4(&bones[i].position, direction * boneLength * i); + XMStoreFloat4(&bones[i].velocity, XMVectorZero()); + } + } + + void BoneSystem::update(float x, float y, float z) + { + bones[0].position.x = x; + bones[0].position.y = y; + bones[0].position.z = z; + + for (int i = 1; i < configuration.boneCount; i++) { + Bone const * parent = &bones[i - 1]; + Bone * bone = &bones[i]; + bone->update(parent, &configuration); + } + } +} diff --git a/src/snake/bone_system.h b/src/snake/bone_system.h new file mode 100644 index 0000000..dd37f35 --- /dev/null +++ b/src/snake/bone_system.h @@ -0,0 +1,15 @@ +#pragma once + +#include "bone.h" +#include "bone_configuration.h" + +namespace snake { + struct BoneSystem { + Bone * bones; + int maxBones; + BoneConfiguration configuration; + + void init(int maxBones, float boneLength); + void update(float x, float y, float z); + }; +} diff --git a/src/snake/configuration.cpp b/src/snake/configuration.cpp new file mode 100644 index 0000000..486bc4f --- /dev/null +++ b/src/snake/configuration.cpp @@ -0,0 +1,5 @@ +namespace snake { + struct BoneConfiguration { + float boneLength; + }; +}