diff options
Diffstat (limited to '2d/softbody/softbody_1/damped.cpp')
-rw-r--r-- | 2d/softbody/softbody_1/damped.cpp | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/2d/softbody/softbody_1/damped.cpp b/2d/softbody/softbody_1/damped.cpp new file mode 100644 index 0000000..010963b --- /dev/null +++ b/2d/softbody/softbody_1/damped.cpp @@ -0,0 +1,255 @@ +#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 <cstdio> +#include <cmath> +#include <emscripten/html5.h> +#include <unistd.h> +#include <pthread.h> +#include <cmath> +#include <cfloat> + +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(); + }; + + 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<float>(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<float32>(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<float32>(numSegments)); + float32 y2Offset = displacement * (1.f - (pidx + 1) / static_cast<float32>(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; + } +} |