summaryrefslogtreecommitdiff
path: root/2d/softbody/softbody_2/SpringRectangle.h
blob: 848db231639f2678e2d295fd5840f2af10a7dbd7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include "../../../shared_cpp/Renderer2d.h"
#include "../../../shared_cpp/types.h"
#include "../../../shared_cpp/mathlib.h"

struct SoftbodyUpdateVertexData {
	Vector2 position;  // Position is in world coordinates, for math's sake.
    Vector2 displacement;
	Vector2 initialVelocity;
	Vector2 initialDisplacement;
	bool isHovered = false;

	SoftbodyUpdateVertexData* left;
	SoftbodyUpdateVertexData* right;
	SoftbodyUpdateVertexData* top;
	SoftbodyUpdateVertexData* bottom;
};

EM_BOOL onMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
EM_BOOL onMouseDown(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
EM_BOOL onMouseUp(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);

struct SoftbodyRectangle {
	float32 width = 200;
	float32 height = 200;
	Vector2 position;
	int32 springDensity = 10;
	float32 springConstant = 4.f; // in N /m
	float32 jointMassKg = 10;
	float32 angularVelocity = 0.f;

	// Runtime data
	float32 totalTimeSeconds = 0.f;
	SoftbodyUpdateVertexData* updateData = NULL;
	bool hasLastPositionChanged = false;
	Vector2 lastMousePosition;
    SoftbodyUpdateVertexData* draggedVertex = NULL;

	// Render data
	Mesh2d mesh;
	Mesh2d pointsMesh;
	Vertex2d* vertices = NULL;
	Vertex2d* pointsVertices = NULL;

	void load(Renderer2d* renderer) {
		position = Vector2(800 / 2 - width / 2, 600 / 2 - height / 2);
		angularVelocity = sqrtf(springConstant / jointMassKg);
	    int32 numVertices = springDensity * springDensity; // Each subdivision is a square.
		int32 numIndices = 6 * ((springDensity - 1) * (springDensity - 1));
	    vertices = new Vertex2d[numVertices];
		updateData = new SoftbodyUpdateVertexData[numVertices];
		auto indices = new GLuint[numIndices];

		// Load a square with the desired density
		int32 vIdx = 0;
		int32 iIdx = 0;
		float32 inverseDensity = 1.f / springDensity;
		float32 halfInv = inverseDensity / 2.f;
		for (int32 y = 0; y < springDensity; y++) {  // Rows
			for (int32 x = 0; x < springDensity; x++) { // Columns
			    Vector2 vpos = Vector2(x * inverseDensity - halfInv, y * inverseDensity- halfInv);
				vpos.x = vpos.x * width + position.x;
				vpos.y = vpos.y * height + position.y;
				vertices[vIdx] = { vpos, Vector4(1, 0, 0, 1) };
			    updateData[vIdx].position = vpos;
				updateData[vIdx].initialVelocity = Vector2(randomFloatBetween(0, 1.0) * 0.1, randomFloatBetween(0, 1.0) * 0.1);
				updateData[vIdx].initialDisplacement = Vector2(randomFloatBetween(0, 1.0) * 0.1, randomFloatBetween(0, 1.0) * 0.1);

				if (x != 0) updateData[vIdx].left = &updateData[vIdx - 1];
				if (x != springDensity - 1) updateData[vIdx].right = &updateData[vIdx + 1];
				if (y != 0) updateData[vIdx].top = &updateData[vIdx - springDensity];
				if (y != springDensity - 1) updateData[vIdx].bottom = &updateData[vIdx + springDensity];

				if (y != springDensity - 1 && x != springDensity - 1) {
					indices[iIdx++] = vIdx;
					indices[iIdx++] = vIdx + 1;
					indices[iIdx++] = vIdx + springDensity;
					indices[iIdx++] = vIdx + springDensity;
					indices[iIdx++] = vIdx + springDensity + 1;
					indices[iIdx++] = vIdx + 1;
				}

				vIdx++;
			}
		}

		mesh.load(vertices, numVertices, indices, numIndices, renderer, GL_DYNAMIC_DRAW);
		// mesh.model = Mat4x4().scale(Vector3(width, height, 0)).translateByVec2(position);

	    pointsVertices = new Vertex2d[numVertices];
		for (int32 v = 0; v < numVertices; v++) {
			pointsVertices[v].position = vertices[v].position;
			pointsVertices[v].color = Vector4(0, 0, 0, 1);
		}
		//pointsMesh.model = mesh.model;
		pointsMesh.load(pointsVertices, numVertices, renderer, GL_DYNAMIC_DRAW);
		
		delete [] indices;

		// Setup callbacks
		emscripten_set_mousemove_callback("#gl_canvas", this, false, onMouseMove);
		emscripten_set_mousedown_callback("#gl_canvas", this, false, onMouseDown);
		emscripten_set_mouseup_callback("#gl_canvas", this, false, onMouseUp);
	}

	void update(float32 dtSeconds) {
		totalTimeSeconds += dtSeconds;

		for (int32 v = 0; v < pointsMesh.numVertices; v++) {
			auto springWeight = &updateData[v];
			
			//springWeight->displacement.x = springWeight->initialDisplacement.x * cosf(angularVelocity * totalTimeSeconds - springWeight->initialVelocity.x);
			//springWeight->displacement.y = springWeight->initialDisplacement.y * cosf(angularVelocity * totalTimeSeconds - springWeight->initialVelocity.y);

			//Vector2 nextPosition = springWeight->position + springWeight->displacement;
			//vertices[v].position = nextPosition;
			//pointsVertices[v].position = nextPosition;

			if (springWeight == draggedVertex && hasLastPositionChanged) {
				hasLastPositionChanged = false;
				vertices[v].position = lastMousePosition;
				pointsVertices[v].position = lastMousePosition;
			}

			if (!draggedVertex && hasLastPositionChanged) {
			    if ((springWeight->position - lastMousePosition).length() < 10.f) {
					pointsVertices[v].color = Vector4(1, 1, 0, 1);
					springWeight->isHovered = true;
				}
				else {
					pointsVertices[v].color = Vector4(0, 0, 0, 1);
				    springWeight->isHovered = false;
				}
			}
		}

		if (hasLastPositionChanged) hasLastPositionChanged = false;

		mesh.updateVertices(vertices);
		pointsMesh.updateVertices(pointsVertices);
	}

	void render(Renderer2d* renderer) {
		mesh.render(renderer);
		pointsMesh.render(renderer, GL_POINTS);
	}

	void unload() {
		mesh.unload();
		pointsMesh.unload();
		delete [] vertices;
		delete [] pointsVertices;
	}
};

EM_BOOL onMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
	SoftbodyRectangle* rectangle = (SoftbodyRectangle*)userData;
	rectangle->hasLastPositionChanged = true;
	rectangle->lastMousePosition = Vector2(static_cast<float32>(mouseEvent->targetX), static_cast<float32>(600 - mouseEvent->targetY));
	return true;
}

EM_BOOL onMouseDown(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
	SoftbodyRectangle* rectangle = (SoftbodyRectangle*)userData;

    for (int32 v = 0; v < rectangle->pointsMesh.numVertices; v++) {
		auto springWeight = &rectangle->updateData[v];
		if (springWeight->isHovered) {
			rectangle->draggedVertex = springWeight;
			break;
		}
	}

	return true;
}

EM_BOOL onMouseUp(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
	SoftbodyRectangle* rectangle = (SoftbodyRectangle*)userData;
	rectangle->draggedVertex = NULL;
	return true;
}