#include "undamped.h" #include "../../../shared_cpp/Renderer2d.h" #include "../../../shared_cpp/types.h" #include "../../../shared_cpp/WebglContext.h" #include "../../../shared_cpp/mathlib.h" #include "../../../shared_cpp/MainLoop.h" #include #include #include #include #include #include #include namespace Undamped { struct SpringWeight { Mesh2d shape; float32 radius = 2.f; float32 mass = 1.f; void load(Renderer2d* renderer, float32 inMass, Vector4 startColor, Vector4 endColor); void update(float32 dtSeconds); void render(Renderer2d* renderer); void unload(); }; struct Spring { SpringWeight* weight; // -- Rendering Mesh2d shape; Vertex2d* vertices = NULL; int32 numSegments = 0; int32 numVertices = 0; // -- Spring initial variables float32 k = 4; // Spring Constant, in N / m // -- Spring runtime variables float32 force = 0.f; float32 velocity = 0.f; float32 position = 0.f; void load(Renderer2d* renderer, SpringWeight* inWieight, float32 length, float32 inInitialDisplacement, float32 inK, float32 loopRadius); void update(float32 dtSeconds); void render(Renderer2d* renderer); void unload(); }; UndampedInitVariables initVariables; void setInitVariables(UndampedInitVariables newVariables) { initVariables = newVariables; } UndampedInitVariables getInitVariables() { return initVariables; } WebglContext* context; Renderer2d renderer; MainLoop mainLoop; SpringWeight weight; Spring spring; EM_BOOL onUndampedPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); EM_BOOL onUndampedStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); void load(); void update(float32 deltaTimeSeconds, void* userData); void unload(); void init(WebglContext* inContext) { context = inContext; emscripten_set_click_callback("#gl_canvas_play_undamped", NULL, false, onUndampedPlayClicked); emscripten_set_click_callback("#gl_canvas_stop_undamped", NULL, false, onUndampedStopClicked); } void load() { context->init("#gl_canvas_undamped"); renderer.load(context); weight.load(&renderer, initVariables.mass, Vector4 { 55.f, 235.f, 35.f, 255.f }, Vector4 { 235.f, 5.f, 235.f, 255.f }); spring.load(&renderer, &weight, initVariables.springLength, initVariables.initialDisplacement, initVariables.k, 64.f); mainLoop.run(update); } void update(float32 deltaTimeSeconds, void* userData) { // -- Update spring.update(deltaTimeSeconds); weight.update(deltaTimeSeconds); // -- Render renderer.render(); weight.render(&renderer); spring.render(&renderer); } void unload() { mainLoop.stop(); renderer.unload(); weight.unload(); spring.unload(); context->destroy(); } void SpringWeight::load(Renderer2d* renderer, float32 inMass, Vector4 startColor, Vector4 endColor) { mass = inMass; radius = mass * 16.f; if (radius > 42) radius = 42.f; const int32 numSegments = 96; const float32 radiansPerSegment = (2.f * PI) / static_cast(numSegments); const int32 numVertices = numSegments * 3; float32 t = 0.f; float32 tIncrement = 1.f / (numSegments / 2.f); startColor = startColor.toNormalizedColor(); endColor = endColor.toNormalizedColor(); Vertex2d vertices[numVertices]; for (int idx = 0; idx < numSegments; idx++) { int vIdx = idx * 3; Vector4 color = lerp(startColor, endColor, t); if (idx >= numSegments / 2) { t -= tIncrement; } else { t += tIncrement; } vertices[vIdx].color = color; vertices[vIdx].position = Vector2 { radius * cosf(radiansPerSegment * idx), radius * sinf(radiansPerSegment * idx) }; vertices[vIdx + 1].color = color; vertices[vIdx + 1].position = Vector2 { 0.f, 0.f }; vertices[vIdx + 2].color = color; vertices[vIdx + 2].position = Vector2 { radius * cosf(radiansPerSegment * (idx + 1)), radius * sinf(radiansPerSegment * (idx + 1)) }; } shape.load(vertices, numVertices, renderer); } void SpringWeight::update(float32 dtSeconds) { } void SpringWeight::render(Renderer2d* renderer) { shape.render(renderer); } void SpringWeight::unload() { shape.unload(); } void Spring::load(Renderer2d* renderer, SpringWeight* inWeight, float32 length, float32 initialDisplacement, float32 inK, float32 loopRadius) { weight = inWeight; position = initialDisplacement; k = inK; force = -k * initialDisplacement; velocity = sqrtf(k / weight->mass); const int32 verticesPerSegment = 6; numSegments = 256; numVertices = numSegments * verticesPerSegment; vertices = new Vertex2d[numVertices]; float32 lengthIncrement = length / static_cast(numSegments); const float32 frequency = 0.25f; const float32 loopWidth = 20.f; const float32 offset = 0.25f; int32 vidx = 0; float32 dx = position; for (int pidx = 0; pidx < numSegments; pidx++) { float32 y1 = lengthIncrement * pidx; float32 x1 = loopWidth * sinf(frequency * y1 + offset); float32 y2 = y1 + lengthIncrement; float32 x2 = loopWidth * sinf(frequency * y2 + offset); float32 y1Offset = dx * (1.f - pidx / static_cast(numSegments)); float32 y2Offset = dx * (1.f - (pidx + 1) / static_cast(numSegments)); y1 += y1Offset; y2 += y2Offset; vertices[vidx++].position = Vector2(x1, y1); vertices[vidx++].position = Vector2(x1, y2); vertices[vidx++].position = Vector2(x2, y1); vertices[vidx++].position = Vector2(x2, y1); vertices[vidx++].position = Vector2(x1, y2); vertices[vidx++].position = Vector2(x2, y2); } shape.load(vertices, numVertices, renderer, GL_DYNAMIC_DRAW); shape.model = Mat4x4().translateByVec2(Vector2(400, 300)); weight->shape.model = shape.model.translateByVec2(Vector2(0, -weight->radius + dx)); } void Spring::update(float32 dtSeconds) { float32 lastPosition = position; force = -k * position; velocity = velocity + (force * dtSeconds) / weight->mass; position = position + velocity * dtSeconds; float32 dx = position - lastPosition; int32 vidx = 0; for (int pidx = 0; pidx < numSegments; pidx++) { float32 y1Offset = dx * (1.f - pidx / static_cast(numSegments)); float32 y2Offset = dx * (1.f - (pidx + 1) / static_cast(numSegments)); vertices[vidx++].position.y += y1Offset; vertices[vidx++].position.y += y2Offset; vertices[vidx++].position.y += y1Offset; vertices[vidx++].position.y += y1Offset; vertices[vidx++].position.y += y2Offset; vertices[vidx++].position.y += y2Offset; } weight->shape.model = weight->shape.model.translateByVec2(Vector2(0, dx)); } void Spring::render(Renderer2d* renderer) { glBindBuffer(GL_ARRAY_BUFFER, shape.vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2d), &vertices[0]); shape.render(renderer); } void Spring::unload() { shape.unload(); delete[] vertices; } EM_BOOL onUndampedPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { printf("Play clicked\n"); load(); return true; } EM_BOOL onUndampedStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { printf("Stop clicked\n"); unload(); return true; } }