#include "damped.h" #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 Damped { struct DampedSpringWeight { Mesh2d shape; float32 radius; float32 mass = 1.f; void load(Renderer2d* renderer, float32 inRadius, Vector4 startColor, Vector4 endColor); void update(float32 dtSeconds); void render(Renderer2d* renderer); void unload(); }; struct DampedSpring { DampedSpringWeight* weight; Mesh2d shape; Vertex2d* vertices = NULL; int32 numSegments = 0; int32 numVertices = 0; // Initialization variables float32 k = 4; // DampedSpring Constant, in N / m (Hooke's Law) float32 c = 1.f; // Viscous damping coefficient (Damping force) float32 R = 2.f; float32 gamma = 6.2; // Constants calculated at load time float32 discriminant = 0.f; float32 omega1 = 0.f; // Update variables float32 displacement = 0.f; float32 timeElapsed = 0.f; void load(Renderer2d* renderer, DampedSpringWeight* inWieight, float32 length, float32 loopRadius); void update(float32 dtSeconds); void render(Renderer2d* renderer); void unload(); }; DampedInitVariables initVariables; void setInitVariables(DampedInitVariables newVariables) { initVariables = newVariables; } DampedInitVariables getInitVariables() { return initVariables; } WebglContext* context; Renderer2d renderer; MainLoop mainLoop; DampedSpringWeight weight; DampedSpring spring; EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); EM_BOOL onStopClicked(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_damped", NULL, false, onPlayClicked); emscripten_set_click_callback("#gl_canvas_stop_damped", NULL, false, onStopClicked); } void load() { context->init("#gl_canvas_damped"); renderer.load(context); weight.load(&renderer, 32.f, Vector4 { 55.f, 235.f, 35.f, 255.f }, Vector4 { 235.f, 5.f, 235.f, 255.f }); spring.load(&renderer, &weight, 250.f, 16.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 DampedSpringWeight::load(Renderer2d* renderer, float32 inRadius, Vector4 startColor, Vector4 endColor) { radius = inRadius; 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 DampedSpringWeight::update(float32 dtSeconds) { } void DampedSpringWeight::render(Renderer2d* renderer) { shape.render(renderer); } void DampedSpringWeight::unload() { shape.unload(); } const float32 epsilon = 0.0001f; void DampedSpring::load(Renderer2d* renderer, DampedSpringWeight* inWeight, float32 length, float32 loopRadius) { weight = inWeight; discriminant = c * c - (4 * weight->mass * k); if (discriminant < epsilon && discriminant > -epsilon) { // Real repeated root: Overdamped motion } else if (discriminant > 0) { // Two real roots: Critically damped motion } else { // Complex pair (less than zero): Underdamped motion omega1 = sqrtf(-discriminant) / (2.f * weight->mass); // Get the real part of the number } timeElapsed = 0.f; 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; 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); 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)); } void DampedSpring::update(float32 dtSeconds) { timeElapsed += dtSeconds; if (discriminant < epsilon && discriminant > -epsilon) { // Real repeated root: Overdamped motion } else if (discriminant > 0) { // Two real roots: Critically damped motion } else { // Complex pair (less than zero): Underdamped motion float32 exponent = (-c / (2 * weight->mass)) * timeElapsed; displacement = R * pow(E, exponent) * (cosf(omega1 * timeElapsed - gamma)); } int32 vidx = 0; for (int pidx = 0; pidx < numSegments; pidx++) { float32 y1Offset = displacement * (1.f - pidx / static_cast(numSegments)); float32 y2Offset = displacement * (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, displacement)); } void DampedSpring::render(Renderer2d* renderer) { glBindBuffer(GL_ARRAY_BUFFER, shape.vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2d), &vertices[0]); shape.render(renderer); } void DampedSpring::unload() { shape.unload(); delete[] vertices; } EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { printf("Play clicked\n"); load(); return true; } EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { printf("Stop clicked\n"); unload(); return true; } }