hyperstellar 1.5.6__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hyperstellar/__init__.py +299 -0
- hyperstellar/__init__.pyi +1019 -0
- hyperstellar/_native/linux-x64/shaders/axis.frag +8 -0
- hyperstellar/_native/linux-x64/shaders/axis.vert +13 -0
- hyperstellar/_native/linux-x64/shaders/fragShader.frag +27 -0
- hyperstellar/_native/linux-x64/shaders/geoShader.geom +69 -0
- hyperstellar/_native/linux-x64/shaders/math.comp +2177 -0
- hyperstellar/_native/linux-x64/shaders/paint.comp +1214 -0
- hyperstellar/_native/linux-x64/shaders/quad.frag +26 -0
- hyperstellar/_native/linux-x64/shaders/quad.geom +149 -0
- hyperstellar/_native/linux-x64/shaders/quad.vert +39 -0
- hyperstellar/_native/linux-x64/shaders/vertexShader.vert +13 -0
- hyperstellar/_native/linux-x64/stellar.so +0 -0
- hyperstellar/_native/windows-x64/shaders/axis.frag +8 -0
- hyperstellar/_native/windows-x64/shaders/axis.vert +13 -0
- hyperstellar/_native/windows-x64/shaders/fragShader.frag +27 -0
- hyperstellar/_native/windows-x64/shaders/geoShader.geom +69 -0
- hyperstellar/_native/windows-x64/shaders/math.comp +2177 -0
- hyperstellar/_native/windows-x64/shaders/paint.comp +1214 -0
- hyperstellar/_native/windows-x64/shaders/quad.frag +26 -0
- hyperstellar/_native/windows-x64/shaders/quad.geom +149 -0
- hyperstellar/_native/windows-x64/shaders/quad.vert +39 -0
- hyperstellar/_native/windows-x64/shaders/vertexShader.vert +13 -0
- hyperstellar/_native/windows-x64/stellar.pyd +0 -0
- hyperstellar/debug.py +169 -0
- hyperstellar/jit.py +2010 -0
- hyperstellar/jit_linter.py +3 -0
- hyperstellar/py.typed +0 -0
- hyperstellar/shader.py +1356 -0
- hyperstellar/typed.py +152 -0
- hyperstellar-1.5.6.dist-info/METADATA +470 -0
- hyperstellar-1.5.6.dist-info/RECORD +35 -0
- hyperstellar-1.5.6.dist-info/WHEEL +5 -0
- hyperstellar-1.5.6.dist-info/licenses/LICENSE +24 -0
- hyperstellar-1.5.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2177 @@
|
|
|
1
|
+
#version 430 core
|
|
2
|
+
// Welcome to the EVILNATOR shader! Or formarly known as:
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// HYPERSTELLAR PHYSICS ENGINE – COMPUTE SHADER
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// This shader implements a full 2D physics engine with a DSL for equations.
|
|
7
|
+
//
|
|
8
|
+
// FEATURES:
|
|
9
|
+
// • Complex numbers (i, real, imag, conj, arg)
|
|
10
|
+
// • Derivatives D(expr, var, order) for any variable (x, y, vx, vy, theta, omega, mass, charge)
|
|
11
|
+
// • Tensor literals [a,b,c] (vectors of length 2–4)
|
|
12
|
+
// • dot(v1, v2), cross(v1, v2), norm(v)
|
|
13
|
+
// • select(condition, a, b) – condition can use <, <=, >, >=, ==, !=
|
|
14
|
+
// • sum_neighbors(weight, body) – loops over all other objects
|
|
15
|
+
// • Smooth Perlin noise(x,y), rand()
|
|
16
|
+
// • Object references: p[id].x, p[id].y, p[id].vx, ..., p[id].theta, p[id].radius, etc.
|
|
17
|
+
// • Collisions: circle, AABB, convex polygon (SAT)
|
|
18
|
+
// • Constraints: distance, boundary, angle
|
|
19
|
+
// • Warm starting for contacts
|
|
20
|
+
// • Symplectic Euler integration
|
|
21
|
+
//
|
|
22
|
+
// Author: Aiden Jabari
|
|
23
|
+
// Wrote this shader so that no one has to write a thousand case stetements for a DSL
|
|
24
|
+
// You can thank me later. Or not. I don't care. I just want to finish this project and move on to the next one.
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
|
|
28
|
+
|
|
29
|
+
// ----------------------------------------------------------------------------
|
|
30
|
+
// DATA STRUCTURES (should be same as CPU side)
|
|
31
|
+
// ----------------------------------------------------------------------------
|
|
32
|
+
struct Object {
|
|
33
|
+
vec2 position;
|
|
34
|
+
vec2 velocity;
|
|
35
|
+
float mass;
|
|
36
|
+
float charge;
|
|
37
|
+
int visualSkinType;
|
|
38
|
+
int collisionShapeType;
|
|
39
|
+
vec4 visualData; // x = radius/width, y = height, z = theta, w = omega
|
|
40
|
+
vec4 collisionData; // x = previous ax, y = previous ay
|
|
41
|
+
vec4 color;
|
|
42
|
+
int equationID;
|
|
43
|
+
int _pad1;
|
|
44
|
+
int _padEnd[2];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
struct EquationMapping {
|
|
48
|
+
int tokenOffset_ax; int tokenCount_ax; int constantOffset_ax; int _pad1;
|
|
49
|
+
int tokenOffset_ay; int tokenCount_ay; int constantOffset_ay; int _pad2;
|
|
50
|
+
int tokenOffset_angular; int tokenCount_angular; int constantOffset_angular; int _pad3;
|
|
51
|
+
int tokenOffset_r; int tokenCount_r; int constantOffset_r; int _pad4;
|
|
52
|
+
int tokenOffset_g; int tokenCount_g; int constantOffset_g; int _pad5;
|
|
53
|
+
int tokenOffset_b; int tokenCount_b; int constantOffset_b; int _pad6;
|
|
54
|
+
int tokenOffset_a; int tokenCount_a; int constantOffset_a; int _pad7;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
struct Constraint {
|
|
58
|
+
int type; // 0 = distance, 1 = boundary, 2 = angle
|
|
59
|
+
int targetObjectID;
|
|
60
|
+
float param1;
|
|
61
|
+
float param2;
|
|
62
|
+
float param3;
|
|
63
|
+
float param4;
|
|
64
|
+
int _pad1;
|
|
65
|
+
int _pad2;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
struct ObjectConstraints {
|
|
69
|
+
int objectID;
|
|
70
|
+
int numConstraints;
|
|
71
|
+
int constraintOffset;
|
|
72
|
+
int _pad;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
struct CollisionProperties {
|
|
76
|
+
int enabled;
|
|
77
|
+
int shapeType; // 0 = none, 1 = circle, 2 = AABB, 3 = polygon
|
|
78
|
+
float restitution;
|
|
79
|
+
float friction;
|
|
80
|
+
float mass_factor;
|
|
81
|
+
int _pad1;
|
|
82
|
+
int _pad2;
|
|
83
|
+
int _pad3;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
struct CollisionInfo {
|
|
87
|
+
bool hasCollision;
|
|
88
|
+
vec2 normal;
|
|
89
|
+
float penetration;
|
|
90
|
+
int otherObjectID;
|
|
91
|
+
vec2 contactPoint;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
struct ContactPoint {
|
|
95
|
+
vec2 normal;
|
|
96
|
+
vec2 position;
|
|
97
|
+
float penetration;
|
|
98
|
+
float accumulatedNormalImpulse;
|
|
99
|
+
float accumulatedTangentImpulse;
|
|
100
|
+
int frameCount;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// ----------------------------------------------------------------------------
|
|
104
|
+
// BUFFERS AND UNIFORMS
|
|
105
|
+
// ----------------------------------------------------------------------------
|
|
106
|
+
layout(std430, binding = 0) readonly buffer ObjectsIn { Object objectsIn[]; };
|
|
107
|
+
layout(std430, binding = 1) writeonly buffer ObjectsOut { Object objectsOut[]; };
|
|
108
|
+
layout(std430, binding = 2) readonly buffer AllEquationTokens { int allTokens[]; };
|
|
109
|
+
layout(std430, binding = 3) readonly buffer AllEquationConstants { float allConstants[]; };
|
|
110
|
+
layout(std430, binding = 4) readonly buffer EquationMappings { EquationMapping mappings[256]; };
|
|
111
|
+
layout(std430, binding = 5) readonly buffer Constraints { Constraint constraints[]; };
|
|
112
|
+
layout(std430, binding = 6) readonly buffer ObjectConstraintMappings { ObjectConstraints objectConstraints[]; };
|
|
113
|
+
layout(std430, binding = 7) readonly buffer CollisionProps { CollisionProperties collisionProps[]; };
|
|
114
|
+
layout(std430, binding = 8) buffer ContactBuffer { ContactPoint contacts[]; };
|
|
115
|
+
|
|
116
|
+
uniform float uDt; // time step
|
|
117
|
+
uniform float uTime; // global time
|
|
118
|
+
uniform float k; // spring constant
|
|
119
|
+
uniform float b; // damping coefficient
|
|
120
|
+
uniform float g; // gravity strength
|
|
121
|
+
uniform vec2 uGravityDir; // gravity direction
|
|
122
|
+
uniform float uRestitution; // default restitution
|
|
123
|
+
uniform float uCoupling; // coupling constant
|
|
124
|
+
uniform vec2 uExternalForce; // constant external force
|
|
125
|
+
uniform float uDriveFreq; // driving frequency
|
|
126
|
+
uniform float uDriveAmp; // driving amplitude
|
|
127
|
+
uniform int uEquationMode; // 0 = use equations, else default
|
|
128
|
+
uniform int uNumObjects; // number of objects
|
|
129
|
+
uniform int uEnableWarmStart; // 1 = enable warm starting
|
|
130
|
+
uniform int uMaxContactIterations; // maximum contact iterations
|
|
131
|
+
uniform float uDerivativeEpsilon; // step size for numerical derivatives
|
|
132
|
+
|
|
133
|
+
// ----------------------------------------------------------------------------
|
|
134
|
+
// TOKEN CODES (must match CPU serializer)
|
|
135
|
+
// ----------------------------------------------------------------------------
|
|
136
|
+
const int TOKEN_NUMBER = 0;
|
|
137
|
+
const int TOKEN_VARIABLE = 1;
|
|
138
|
+
const int TOKEN_OBJECT_REF = 2;
|
|
139
|
+
const int TOKEN_ADD = 3;
|
|
140
|
+
const int TOKEN_SUB = 4;
|
|
141
|
+
const int TOKEN_MUL = 5;
|
|
142
|
+
const int TOKEN_DIV = 6;
|
|
143
|
+
const int TOKEN_NEG = 7;
|
|
144
|
+
const int TOKEN_POW = 8;
|
|
145
|
+
const int TOKEN_SIN = 9;
|
|
146
|
+
const int TOKEN_COS = 10;
|
|
147
|
+
const int TOKEN_TAN = 11;
|
|
148
|
+
const int TOKEN_SQRT = 12;
|
|
149
|
+
const int TOKEN_LOG = 13;
|
|
150
|
+
const int TOKEN_EXP = 14;
|
|
151
|
+
const int TOKEN_ABS = 15;
|
|
152
|
+
const int TOKEN_MIN = 16;
|
|
153
|
+
const int TOKEN_MAX = 17;
|
|
154
|
+
const int TOKEN_CLAMP = 18;
|
|
155
|
+
const int TOKEN_FLOOR = 19;
|
|
156
|
+
const int TOKEN_CEIL = 20;
|
|
157
|
+
const int TOKEN_FRAC = 21;
|
|
158
|
+
const int TOKEN_MOD = 22;
|
|
159
|
+
const int TOKEN_ATAN2 = 23;
|
|
160
|
+
const int TOKEN_REAL = 24;
|
|
161
|
+
const int TOKEN_IMAG = 25;
|
|
162
|
+
const int TOKEN_CONJ = 26;
|
|
163
|
+
const int TOKEN_ARG = 27;
|
|
164
|
+
const int TOKEN_SIGN = 28;
|
|
165
|
+
const int TOKEN_STEP = 29;
|
|
166
|
+
const int TOKEN_OPEN_PAREN = 30;
|
|
167
|
+
const int TOKEN_CLOSE_PAREN = 31;
|
|
168
|
+
const int TOKEN_COMMA = 32;
|
|
169
|
+
const int TOKEN_DERIVATIVE = 33;
|
|
170
|
+
const int TOKEN_DOT = 34;
|
|
171
|
+
const int TOKEN_CROSS = 35;
|
|
172
|
+
const int TOKEN_NORM = 36;
|
|
173
|
+
const int TOKEN_SELECT = 37;
|
|
174
|
+
const int TOKEN_NOISE = 38;
|
|
175
|
+
const int TOKEN_RAND = 39;
|
|
176
|
+
const int TOKEN_SUM_NEIGHBORS = 40;
|
|
177
|
+
const int TOKEN_NEIGHBOR_INDEX= 41;
|
|
178
|
+
const int TOKEN_TENSOR_LIT = 42;
|
|
179
|
+
// Comparison operators (return 1.0 if true, else 0.0)
|
|
180
|
+
const int TOKEN_LT = 43; // <
|
|
181
|
+
const int TOKEN_LE = 44; // <=
|
|
182
|
+
const int TOKEN_GT = 45; // >
|
|
183
|
+
const int TOKEN_GE = 46; // >=
|
|
184
|
+
const int TOKEN_EQ = 47; // ==
|
|
185
|
+
const int TOKEN_NE = 48; // !=
|
|
186
|
+
// gets a single component from a vector
|
|
187
|
+
const int TOKEN_COMP = 49;
|
|
188
|
+
|
|
189
|
+
// Variable hashes
|
|
190
|
+
const int VAR_HASH_X = 1;
|
|
191
|
+
const int VAR_HASH_Y = 2;
|
|
192
|
+
const int VAR_HASH_VX = 3;
|
|
193
|
+
const int VAR_HASH_VY = 4;
|
|
194
|
+
const int VAR_HASH_AX = 5;
|
|
195
|
+
const int VAR_HASH_AY = 6;
|
|
196
|
+
const int VAR_HASH_T = 7;
|
|
197
|
+
const int VAR_HASH_THETA = 8;
|
|
198
|
+
const int VAR_HASH_OMEGA = 27;
|
|
199
|
+
const int VAR_HASH_MASS = 22;
|
|
200
|
+
const int VAR_HASH_CHARGE = 23;
|
|
201
|
+
const int VAR_HASH_R = 9;
|
|
202
|
+
const int VAR_HASH_G = 10;
|
|
203
|
+
const int VAR_HASH_B = 11;
|
|
204
|
+
const int VAR_HASH_A = 12;
|
|
205
|
+
const int VAR_HASH_VIS_X = 100;
|
|
206
|
+
const int VAR_HASH_VIS_Y = 101;
|
|
207
|
+
const int VAR_HASH_RADIUS = 102;
|
|
208
|
+
const int VAR_HASH_WIDTH = 103;
|
|
209
|
+
const int VAR_HASH_HEIGHT = 104;
|
|
210
|
+
const int VAR_HASH_PI = 17;
|
|
211
|
+
const int VAR_HASH_E = 18;
|
|
212
|
+
const int VAR_HASH_I = 16;
|
|
213
|
+
const int VAR_HASH_K = 19;
|
|
214
|
+
const int VAR_HASH_B_DAMP = 20;
|
|
215
|
+
const int VAR_HASH_G_GRAV = 21;
|
|
216
|
+
const int VAR_HASH_COUPLING = 24;
|
|
217
|
+
const int VAR_HASH_FREQ = 25;
|
|
218
|
+
const int VAR_HASH_AMP = 26;
|
|
219
|
+
|
|
220
|
+
// Limits and constants
|
|
221
|
+
const int MAX_RPN_STACK_SIZE = 128;
|
|
222
|
+
const int MAX_TOKEN_BUFFER_SIZE = 10000;
|
|
223
|
+
const int MAX_CONSTANT_BUFFER_SIZE = 10000;
|
|
224
|
+
const int MAX_EQUATION_COUNT = 256;
|
|
225
|
+
const float PI = 3.14159265359;
|
|
226
|
+
const float E = 2.71828182846;
|
|
227
|
+
const float EPSILON = 1e-6;
|
|
228
|
+
const float SAFE_MIN_VALUE = 1e-6;
|
|
229
|
+
const float SAFE_MAX_EXP = 50.0;
|
|
230
|
+
const int MAX_DERIV_EXPR_SIZE = 500;
|
|
231
|
+
const int CONSTRAINT_DISTANCE = 0;
|
|
232
|
+
const int CONSTRAINT_BOUNDARY = 1;
|
|
233
|
+
const int CONSTRAINT_ANGLE = 2;
|
|
234
|
+
const float CONSTRAINT_STIFFNESS = 1.0;
|
|
235
|
+
const int MAX_CONSTRAINT_ITERATIONS = 3;
|
|
236
|
+
|
|
237
|
+
const int COLLISION_NONE = 0;
|
|
238
|
+
const int COLLISION_CIRCLE = 1;
|
|
239
|
+
const int COLLISION_AABB = 2;
|
|
240
|
+
const int COLLISION_POLYGON = 3;
|
|
241
|
+
const int MAX_CONTACTS_PER_OBJECT = 4;
|
|
242
|
+
const int MAX_CONTACT_FRAMES = 5;
|
|
243
|
+
|
|
244
|
+
// ----------------------------------------------------------------------------
|
|
245
|
+
// UTILITY FUNCTIONS
|
|
246
|
+
// ----------------------------------------------------------------------------
|
|
247
|
+
//safety first! Avoids Nans and Infs. Those are nasty in shaders
|
|
248
|
+
float safeDivide(float n, float d) {
|
|
249
|
+
return (abs(d) < EPSILON) ? 0.0 : n / d;
|
|
250
|
+
}
|
|
251
|
+
float safePow(float base, float exp) {
|
|
252
|
+
// For negative base with integer exponent, compute correctly
|
|
253
|
+
if (base < 0.0) {
|
|
254
|
+
float intPart;
|
|
255
|
+
if (abs(modf(exp, intPart)) < EPSILON) {
|
|
256
|
+
// Exponent is an integer
|
|
257
|
+
float result = pow(-base, exp);
|
|
258
|
+
if (int(intPart) % 2 == 1) result = -result;
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Otherwise clamp base to non‑negative
|
|
263
|
+
return pow(max(0.0, base), exp);
|
|
264
|
+
}
|
|
265
|
+
float safeLog(float v) {
|
|
266
|
+
return log(max(SAFE_MIN_VALUE, v));
|
|
267
|
+
}
|
|
268
|
+
float safeExp(float v) {
|
|
269
|
+
return exp(clamp(v, -SAFE_MAX_EXP, SAFE_MAX_EXP));
|
|
270
|
+
}
|
|
271
|
+
bool isInvalid(float v) {
|
|
272
|
+
return isinf(v) || isnan(v);
|
|
273
|
+
}
|
|
274
|
+
vec2 sanitizeVec2(vec2 v) {
|
|
275
|
+
if (isInvalid(v.x)) v.x = 0.0;
|
|
276
|
+
if (isInvalid(v.y)) v.y = 0.0;
|
|
277
|
+
return v;
|
|
278
|
+
}
|
|
279
|
+
vec4 sanitizeVec4(vec4 v) {
|
|
280
|
+
for (int i = 0; i < 4; ++i)
|
|
281
|
+
if (isInvalid(v[i])) v[i] = 0.0;
|
|
282
|
+
return v;
|
|
283
|
+
}
|
|
284
|
+
float signFunc(float x) { return (x > 0.0) ? 1.0 : ((x < 0.0) ? -1.0 : 0.0); }
|
|
285
|
+
float stepFunc(float x) { return (x >= 0.0) ? 1.0 : 0.0; }
|
|
286
|
+
|
|
287
|
+
// ----------------------------------------------------------------------------
|
|
288
|
+
// COMPLEX NUMBERS
|
|
289
|
+
// ----------------------------------------------------------------------------
|
|
290
|
+
vec2 cAdd(vec2 a, vec2 b) { return a + b; }
|
|
291
|
+
vec2 cSub(vec2 a, vec2 b) { return a - b; }
|
|
292
|
+
vec2 cMul(vec2 a, vec2 b) {
|
|
293
|
+
return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x);
|
|
294
|
+
}
|
|
295
|
+
vec2 cDiv(vec2 a, vec2 b) {
|
|
296
|
+
float d = b.x*b.x + b.y*b.y;
|
|
297
|
+
if (abs(d) < EPSILON) return vec2(0.0);
|
|
298
|
+
return vec2((a.x*b.x + a.y*b.y) / d, (a.y*b.x - a.x*b.y) / d);
|
|
299
|
+
}
|
|
300
|
+
vec2 cLog(vec2 z) {
|
|
301
|
+
return vec2(safeLog(length(z)), atan(z.y, z.x));
|
|
302
|
+
}
|
|
303
|
+
vec2 cExp(vec2 z) {
|
|
304
|
+
float ea = safeExp(z.x);
|
|
305
|
+
return vec2(ea * cos(z.y), ea * sin(z.y));
|
|
306
|
+
}
|
|
307
|
+
vec2 cPow(vec2 b, vec2 e) {
|
|
308
|
+
if (length(b) < EPSILON) return vec2(0.0);
|
|
309
|
+
return cExp(cMul(e, cLog(b)));
|
|
310
|
+
}
|
|
311
|
+
vec2 cSin(vec2 z) { return vec2(sin(z.x)*cosh(z.y), cos(z.x)*sinh(z.y)); }
|
|
312
|
+
vec2 cCos(vec2 z) { return vec2(cos(z.x)*cosh(z.y), -sin(z.x)*sinh(z.y)); }
|
|
313
|
+
vec2 cTan(vec2 z) { return cDiv(cSin(z), cCos(z)); }
|
|
314
|
+
|
|
315
|
+
// ----------------------------------------------------------------------------
|
|
316
|
+
// RANDOM AND SMOOTH NOISE
|
|
317
|
+
// ----------------------------------------------------------------------------
|
|
318
|
+
float rand(vec2 seed) {
|
|
319
|
+
return fract(sin(dot(seed, vec2(12.9898, 78.233))) * 43758.5453);
|
|
320
|
+
}
|
|
321
|
+
float smoothNoise(vec2 p) {
|
|
322
|
+
vec2 i = floor(p);
|
|
323
|
+
vec2 f = fract(p);
|
|
324
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
325
|
+
float a = rand(i);
|
|
326
|
+
float b = rand(i + vec2(1.0, 0.0));
|
|
327
|
+
float c = rand(i + vec2(0.0, 1.0));
|
|
328
|
+
float d = rand(i + vec2(1.0, 1.0));
|
|
329
|
+
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
|
330
|
+
}
|
|
331
|
+
float noise(vec2 p) {
|
|
332
|
+
float sum = 0.0;
|
|
333
|
+
float amp = 0.5;
|
|
334
|
+
float freq = 4.0;
|
|
335
|
+
for (int i = 0; i < 3; ++i) {
|
|
336
|
+
sum += amp * smoothNoise(p * freq);
|
|
337
|
+
amp *= 0.5;
|
|
338
|
+
freq *= 2.0;
|
|
339
|
+
}
|
|
340
|
+
return sum;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ----------------------------------------------------------------------------
|
|
344
|
+
// OBJECT PROPERTY GETTER
|
|
345
|
+
// ----------------------------------------------------------------------------
|
|
346
|
+
float getObjectProperty(int idx, int hash, int self) {
|
|
347
|
+
if (idx < 0 || idx >= uNumObjects || idx == self) return 0.0;
|
|
348
|
+
Object o = objectsIn[idx];
|
|
349
|
+
switch (hash) {
|
|
350
|
+
case VAR_HASH_X: return o.position.x;
|
|
351
|
+
case VAR_HASH_Y: return o.position.y;
|
|
352
|
+
case VAR_HASH_VX: return o.velocity.x;
|
|
353
|
+
case VAR_HASH_VY: return o.velocity.y;
|
|
354
|
+
case VAR_HASH_AX: return o.collisionData.x;
|
|
355
|
+
case VAR_HASH_AY: return o.collisionData.y;
|
|
356
|
+
case VAR_HASH_MASS: return o.mass;
|
|
357
|
+
case VAR_HASH_CHARGE: return o.charge;
|
|
358
|
+
case VAR_HASH_THETA: return o.visualData.z;
|
|
359
|
+
case VAR_HASH_OMEGA: return o.visualData.w;
|
|
360
|
+
case VAR_HASH_R: return o.color.r;
|
|
361
|
+
case VAR_HASH_G: return o.color.g;
|
|
362
|
+
case VAR_HASH_B: return o.color.b;
|
|
363
|
+
case VAR_HASH_A: return o.color.a;
|
|
364
|
+
case VAR_HASH_VIS_X: return o.visualData.x;
|
|
365
|
+
case VAR_HASH_VIS_Y: return o.visualData.y;
|
|
366
|
+
case VAR_HASH_RADIUS: return o.visualData.x;
|
|
367
|
+
case VAR_HASH_WIDTH: return o.visualData.x;
|
|
368
|
+
case VAR_HASH_HEIGHT: return o.visualData.y;
|
|
369
|
+
default: return 0.0;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ----------------------------------------------------------------------------
|
|
374
|
+
// GET VALUE OF A VARIABLE BY HASH
|
|
375
|
+
// ----------------------------------------------------------------------------
|
|
376
|
+
float getVariableValue(int hash,
|
|
377
|
+
float x, float y, float vx, float vy, float ax, float ay,
|
|
378
|
+
float theta, float omega, vec4 color,
|
|
379
|
+
float mass, float charge) {
|
|
380
|
+
switch (hash) {
|
|
381
|
+
case VAR_HASH_X: return x;
|
|
382
|
+
case VAR_HASH_Y: return y;
|
|
383
|
+
case VAR_HASH_VX: return vx;
|
|
384
|
+
case VAR_HASH_VY: return vy;
|
|
385
|
+
case VAR_HASH_AX: return ax;
|
|
386
|
+
case VAR_HASH_AY: return ay;
|
|
387
|
+
case VAR_HASH_T: return uTime;
|
|
388
|
+
case VAR_HASH_THETA: return theta;
|
|
389
|
+
case VAR_HASH_OMEGA: return omega;
|
|
390
|
+
case VAR_HASH_R: return color.r;
|
|
391
|
+
case VAR_HASH_G: return color.g;
|
|
392
|
+
case VAR_HASH_B: return color.b;
|
|
393
|
+
case VAR_HASH_A: return color.a;
|
|
394
|
+
case VAR_HASH_PI: return PI;
|
|
395
|
+
case VAR_HASH_E: return E;
|
|
396
|
+
case VAR_HASH_K: return k;
|
|
397
|
+
case VAR_HASH_B_DAMP: return b;
|
|
398
|
+
case VAR_HASH_G_GRAV: return g;
|
|
399
|
+
case VAR_HASH_MASS: return mass;
|
|
400
|
+
case VAR_HASH_CHARGE: return charge;
|
|
401
|
+
case VAR_HASH_COUPLING:return uCoupling;
|
|
402
|
+
case VAR_HASH_FREQ: return uDriveFreq;
|
|
403
|
+
case VAR_HASH_AMP: return uDriveAmp;
|
|
404
|
+
default: return 0.0;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ----------------------------------------------------------------------------
|
|
409
|
+
// FORWARD DECLARATIONS (no recursion – derivatives are handled only in the main evaluator)
|
|
410
|
+
// ----------------------------------------------------------------------------
|
|
411
|
+
float evaluateScalarTokenList(float x, float y, float vx, float vy, float ax, float ay,
|
|
412
|
+
float theta, float omega, vec4 color,
|
|
413
|
+
float mass, float charge, int self,
|
|
414
|
+
int offset, int count, int constOffset);
|
|
415
|
+
float computeDerivative(float x, float y, float vx, float vy, float ax, float ay,
|
|
416
|
+
float theta, float omega, vec4 color,
|
|
417
|
+
float mass, float charge, int self,
|
|
418
|
+
int wrtHash, int order,
|
|
419
|
+
int exprOffset, int exprCount, int constOffset);
|
|
420
|
+
float evaluateRPNComponent(float x, float y, float vx, float vy, float ax, float ay,
|
|
421
|
+
float theta, float omega, vec4 color,
|
|
422
|
+
float mass, float charge, int self,
|
|
423
|
+
int componentType,
|
|
424
|
+
int tokenOffset, int tokenCount, int constantOffset);
|
|
425
|
+
|
|
426
|
+
// ----------------------------------------------------------------------------
|
|
427
|
+
// SCALAR TOKEN LIST EVALUATOR (no derivative handling – used inside derivatives)
|
|
428
|
+
// ----------------------------------------------------------------------------
|
|
429
|
+
float evaluateScalarTokenList(float x, float y, float vx, float vy, float ax, float ay,
|
|
430
|
+
float theta, float omega, vec4 color,
|
|
431
|
+
float mass, float charge, int self,
|
|
432
|
+
int offset, int count, int constOffset) {
|
|
433
|
+
float stack[32];
|
|
434
|
+
int sp = 0;
|
|
435
|
+
int idx = offset;
|
|
436
|
+
int end = offset + count;
|
|
437
|
+
while (idx < end) {
|
|
438
|
+
int token = allTokens[idx++];
|
|
439
|
+
if (token == TOKEN_NUMBER) {
|
|
440
|
+
int cidx = allTokens[idx++];
|
|
441
|
+
stack[sp++] = allConstants[constOffset + cidx];
|
|
442
|
+
}
|
|
443
|
+
else if (token == TOKEN_VARIABLE) {
|
|
444
|
+
int h = allTokens[idx++];
|
|
445
|
+
if (h == VAR_HASH_I) {
|
|
446
|
+
stack[sp++] = 0.0;
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
stack[sp++] = getVariableValue(h, x, y, vx, vy, ax, ay, theta, omega, color, mass, charge);
|
|
450
|
+
}
|
|
451
|
+
else if (token == TOKEN_OBJECT_REF) {
|
|
452
|
+
int obj = allTokens[idx++];
|
|
453
|
+
int prop = allTokens[idx++];
|
|
454
|
+
stack[sp++] = getObjectProperty(obj, prop, self);
|
|
455
|
+
}
|
|
456
|
+
else if (token == TOKEN_DERIVATIVE) {
|
|
457
|
+
// Nested derivatives are disabled: skip and return 0. Gotta HATE recursion, am I right?
|
|
458
|
+
int dummy1 = allTokens[idx++];
|
|
459
|
+
int dummy2 = allTokens[idx++];
|
|
460
|
+
int dummy3 = allTokens[idx++];
|
|
461
|
+
int subc = allTokens[idx++];
|
|
462
|
+
idx += subc;
|
|
463
|
+
stack[sp++] = 0.0;
|
|
464
|
+
}
|
|
465
|
+
// Binary arithmetic and comparisons
|
|
466
|
+
else if (token == TOKEN_ADD || token == TOKEN_SUB || token == TOKEN_MUL ||
|
|
467
|
+
token == TOKEN_DIV || token == TOKEN_POW ||
|
|
468
|
+
token == TOKEN_LT || token == TOKEN_LE || token == TOKEN_GT ||
|
|
469
|
+
token == TOKEN_GE || token == TOKEN_EQ || token == TOKEN_NE) {
|
|
470
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
471
|
+
float b = stack[--sp];
|
|
472
|
+
float a = stack[--sp];
|
|
473
|
+
if (token == TOKEN_ADD) stack[sp++] = a + b;
|
|
474
|
+
else if (token == TOKEN_SUB) stack[sp++] = a - b;
|
|
475
|
+
else if (token == TOKEN_MUL) stack[sp++] = a * b;
|
|
476
|
+
else if (token == TOKEN_DIV) stack[sp++] = safeDivide(a, b);
|
|
477
|
+
else if (token == TOKEN_POW) stack[sp++] = safePow(a, b);
|
|
478
|
+
else if (token == TOKEN_LT) stack[sp++] = (a < b) ? 1.0 : 0.0;
|
|
479
|
+
else if (token == TOKEN_LE) stack[sp++] = (a <= b) ? 1.0 : 0.0;
|
|
480
|
+
else if (token == TOKEN_GT) stack[sp++] = (a > b) ? 1.0 : 0.0;
|
|
481
|
+
else if (token == TOKEN_GE) stack[sp++] = (a >= b) ? 1.0 : 0.0;
|
|
482
|
+
else if (token == TOKEN_EQ) stack[sp++] = (abs(a - b) < EPSILON) ? 1.0 : 0.0;
|
|
483
|
+
else stack[sp++] = (abs(a - b) >= EPSILON) ? 1.0 : 0.0; // NE
|
|
484
|
+
}
|
|
485
|
+
// Unary operators
|
|
486
|
+
else if (token == TOKEN_NEG || token == TOKEN_SIN || token == TOKEN_COS || token == TOKEN_TAN ||
|
|
487
|
+
token == TOKEN_EXP || token == TOKEN_LOG || token == TOKEN_SQRT || token == TOKEN_ABS ||
|
|
488
|
+
token == TOKEN_FLOOR || token == TOKEN_CEIL || token == TOKEN_FRAC ||
|
|
489
|
+
token == TOKEN_SIGN || token == TOKEN_STEP) {
|
|
490
|
+
if (sp < 1) { stack[0] = 0.0; sp = 1; continue; }
|
|
491
|
+
float a = stack[--sp];
|
|
492
|
+
if (token == TOKEN_NEG) stack[sp++] = -a;
|
|
493
|
+
else if (token == TOKEN_SIN) stack[sp++] = sin(a);
|
|
494
|
+
else if (token == TOKEN_COS) stack[sp++] = cos(a);
|
|
495
|
+
else if (token == TOKEN_TAN) stack[sp++] = tan(a);
|
|
496
|
+
else if (token == TOKEN_EXP) stack[sp++] = safeExp(a);
|
|
497
|
+
else if (token == TOKEN_LOG) stack[sp++] = safeLog(a);
|
|
498
|
+
else if (token == TOKEN_SQRT)stack[sp++] = sqrt(max(0.0, a));
|
|
499
|
+
else if (token == TOKEN_ABS) stack[sp++] = abs(a);
|
|
500
|
+
else if (token == TOKEN_FLOOR)stack[sp++] = floor(a);
|
|
501
|
+
else if (token == TOKEN_CEIL)stack[sp++] = ceil(a);
|
|
502
|
+
else if (token == TOKEN_FRAC)stack[sp++] = fract(a);
|
|
503
|
+
else if (token == TOKEN_SIGN)stack[sp++] = signFunc(a);
|
|
504
|
+
else stack[sp++] = stepFunc(a);
|
|
505
|
+
}
|
|
506
|
+
else if (token == TOKEN_MOD) {
|
|
507
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
508
|
+
float b = stack[--sp];
|
|
509
|
+
float a = stack[--sp];
|
|
510
|
+
stack[sp++] = (abs(b) < EPSILON) ? 0.0 : mod(a, b);
|
|
511
|
+
}
|
|
512
|
+
else if (token == TOKEN_MIN || token == TOKEN_MAX) {
|
|
513
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
514
|
+
float b = stack[--sp];
|
|
515
|
+
float a = stack[--sp];
|
|
516
|
+
stack[sp++] = (token == TOKEN_MIN) ? min(a, b) : max(a, b);
|
|
517
|
+
}
|
|
518
|
+
else if (token == TOKEN_ATAN2) {
|
|
519
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
520
|
+
float xv = stack[--sp];
|
|
521
|
+
float yv = stack[--sp];
|
|
522
|
+
stack[sp++] = atan(yv, xv);
|
|
523
|
+
}
|
|
524
|
+
else if (token == TOKEN_CLAMP) {
|
|
525
|
+
if (sp < 3) { stack[0] = 0.0; sp = 1; continue; }
|
|
526
|
+
float maxv = stack[--sp];
|
|
527
|
+
float minv = stack[--sp];
|
|
528
|
+
float val = stack[--sp];
|
|
529
|
+
stack[sp++] = clamp(val, minv, maxv);
|
|
530
|
+
}
|
|
531
|
+
else if (token == TOKEN_NOISE) {
|
|
532
|
+
if (sp < 2) { stack[sp++] = 0.0; continue; }
|
|
533
|
+
float yv = stack[--sp];
|
|
534
|
+
float xv = stack[--sp];
|
|
535
|
+
stack[sp++] = noise(vec2(xv, yv));
|
|
536
|
+
}
|
|
537
|
+
else if (token == TOKEN_RAND) {
|
|
538
|
+
stack[sp++] = rand(vec2(uTime, float(self)));
|
|
539
|
+
}
|
|
540
|
+
// Other tokens (SELECT, SUM_NEIGHBORS, TENSOR_LIT, DOT, CROSS, NORM) are not used here. Too lazy too add them.
|
|
541
|
+
}
|
|
542
|
+
return (sp > 0) ? stack[0] : 0.0; // Return top of stack or 0 if empty
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ----------------------------------------------------------------------------
|
|
546
|
+
// NUMERICAL DERIVATIVE (orders 1-4, any variable)
|
|
547
|
+
// ----------------------------------------------------------------------------
|
|
548
|
+
float computeDerivative(float x, float y, float vx, float vy, float ax, float ay,
|
|
549
|
+
float theta, float omega, vec4 color,
|
|
550
|
+
float mass, float charge, int self,
|
|
551
|
+
int wrtHash, int order,
|
|
552
|
+
int exprOffset, int exprCount, int constOffset) {
|
|
553
|
+
if (order <= 0) {
|
|
554
|
+
return evaluateScalarTokenList(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
555
|
+
mass, charge, self,
|
|
556
|
+
exprOffset, exprCount, constOffset);
|
|
557
|
+
}
|
|
558
|
+
if (order > 4) return 0.0;
|
|
559
|
+
|
|
560
|
+
float eps = uDerivativeEpsilon;
|
|
561
|
+
float h = eps;
|
|
562
|
+
float orig = getVariableValue(wrtHash, x, y, vx, vy, ax, ay, theta, omega, color, mass, charge);
|
|
563
|
+
if (abs(orig) > EPSILON) h = eps * abs(orig);
|
|
564
|
+
|
|
565
|
+
// Helper macro to create a perturbed state
|
|
566
|
+
#define PERTURB(delta, xo, yo, vxo, vyo, axo, ayo, tho, omo, colo, masso, chargeo) \
|
|
567
|
+
xo = x; yo = y; vxo = vx; vyo = vy; axo = ax; ayo = ay; \
|
|
568
|
+
tho = theta; omo = omega; colo = color; masso = mass; chargeo = charge; \
|
|
569
|
+
switch (wrtHash) { \
|
|
570
|
+
case VAR_HASH_X: xo = x + delta; break; \
|
|
571
|
+
case VAR_HASH_Y: yo = y + delta; break; \
|
|
572
|
+
case VAR_HASH_VX: vxo = vx + delta; break; \
|
|
573
|
+
case VAR_HASH_VY: vyo = vy + delta; break; \
|
|
574
|
+
case VAR_HASH_AX: axo = ax + delta; break; \
|
|
575
|
+
case VAR_HASH_AY: ayo = ay + delta; break; \
|
|
576
|
+
case VAR_HASH_THETA: tho = theta + delta; break; \
|
|
577
|
+
case VAR_HASH_OMEGA: omo = omega + delta; break; \
|
|
578
|
+
case VAR_HASH_MASS: masso = mass + delta; break; \
|
|
579
|
+
case VAR_HASH_CHARGE: chargeo = charge + delta; break; \
|
|
580
|
+
default: break; \
|
|
581
|
+
}
|
|
582
|
+
// Compute derivative using central difference formulas
|
|
583
|
+
//I love my life :)
|
|
584
|
+
if (order == 1) {
|
|
585
|
+
float xp, yp, vxp, vyp, axp, ayp, thp, omop; vec4 colp; float massp, chargep;
|
|
586
|
+
float xm, ym, vxm, vym, axm, aym, thm, omom; vec4 colm; float massm, chargem;
|
|
587
|
+
PERTURB( h, xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep);
|
|
588
|
+
PERTURB(-h, xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem);
|
|
589
|
+
float fp = evaluateScalarTokenList(xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep,self,
|
|
590
|
+
exprOffset, exprCount, constOffset);
|
|
591
|
+
float fm = evaluateScalarTokenList(xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem,self,
|
|
592
|
+
exprOffset, exprCount, constOffset);
|
|
593
|
+
return (fp - fm) / (2.0 * h);
|
|
594
|
+
}
|
|
595
|
+
else if (order == 2) {
|
|
596
|
+
float x0,y0,vx0,vy0,ax0,ay0,th0,om0; vec4 col0; float mass0,charge0;
|
|
597
|
+
float xp,yp,vxp,vyp,axp,ayp,thp,omop; vec4 colp; float massp,chargep;
|
|
598
|
+
float xm,ym,vxm,vym,axm,aym,thm,omom; vec4 colm; float massm,chargem;
|
|
599
|
+
PERTURB(0.0, x0,y0,vx0,vy0,ax0,ay0,th0,om0,col0,mass0,charge0);
|
|
600
|
+
PERTURB( h, xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep);
|
|
601
|
+
PERTURB(-h, xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem);
|
|
602
|
+
float f0 = evaluateScalarTokenList(x0,y0,vx0,vy0,ax0,ay0,th0,om0,col0,mass0,charge0,self,
|
|
603
|
+
exprOffset, exprCount, constOffset);
|
|
604
|
+
float fp = evaluateScalarTokenList(xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep,self,
|
|
605
|
+
exprOffset, exprCount, constOffset);
|
|
606
|
+
float fm = evaluateScalarTokenList(xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem,self,
|
|
607
|
+
exprOffset, exprCount, constOffset);
|
|
608
|
+
return (fp - 2.0*f0 + fm) / (h * h);
|
|
609
|
+
}
|
|
610
|
+
else if (order == 3) {
|
|
611
|
+
float x2p,y2p,vx2p,vy2p,ax2p,ay2p,th2p,om2p; vec4 col2p; float mass2p,charge2p;
|
|
612
|
+
float xp,yp,vxp,vyp,axp,ayp,thp,omop; vec4 colp; float massp,chargep;
|
|
613
|
+
float xm,ym,vxm,vym,axm,aym,thm,omom; vec4 colm; float massm,chargem;
|
|
614
|
+
float x2m,y2m,vx2m,vy2m,ax2m,ay2m,th2m,om2m; vec4 col2m; float mass2m,charge2m;
|
|
615
|
+
PERTURB( 2.0*h, x2p,y2p,vx2p,vy2p,ax2p,ay2p,th2p,om2p,col2p,mass2p,charge2p);
|
|
616
|
+
PERTURB( h, xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep);
|
|
617
|
+
PERTURB(-h, xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem);
|
|
618
|
+
PERTURB(-2.0*h, x2m,y2m,vx2m,vy2m,ax2m,ay2m,th2m,om2m,col2m,mass2m,charge2m);
|
|
619
|
+
float f2p = evaluateScalarTokenList(x2p,y2p,vx2p,vy2p,ax2p,ay2p,th2p,om2p,col2p,mass2p,charge2p,self,
|
|
620
|
+
exprOffset, exprCount, constOffset);
|
|
621
|
+
float fp = evaluateScalarTokenList(xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep,self,
|
|
622
|
+
exprOffset, exprCount, constOffset);
|
|
623
|
+
float fm = evaluateScalarTokenList(xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem,self,
|
|
624
|
+
exprOffset, exprCount, constOffset);
|
|
625
|
+
float f2m = evaluateScalarTokenList(x2m,y2m,vx2m,vy2m,ax2m,ay2m,th2m,om2m,col2m,mass2m,charge2m,self,
|
|
626
|
+
exprOffset, exprCount, constOffset);
|
|
627
|
+
return (f2p - 2.0*fp + 2.0*fm - f2m) / (2.0 * h * h * h);
|
|
628
|
+
}
|
|
629
|
+
else { // order == 4
|
|
630
|
+
float x2p,y2p,vx2p,vy2p,ax2p,ay2p,th2p,om2p; vec4 col2p; float mass2p,charge2p;
|
|
631
|
+
float xp,yp,vxp,vyp,axp,ayp,thp,omop; vec4 colp; float massp,chargep;
|
|
632
|
+
float x0,y0,vx0,vy0,ax0,ay0,th0,om0; vec4 col0; float mass0,charge0;
|
|
633
|
+
float xm,ym,vxm,vym,axm,aym,thm,omom; vec4 colm; float massm,chargem;
|
|
634
|
+
float x2m,y2m,vx2m,vy2m,ax2m,ay2m,th2m,om2m; vec4 col2m; float mass2m,charge2m;
|
|
635
|
+
PERTURB( 2.0*h, x2p,y2p,vx2p,vy2p,ax2p,ay2p,th2p,om2p,col2p,mass2p,charge2p);
|
|
636
|
+
PERTURB( h, xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep);
|
|
637
|
+
PERTURB(0.0, x0,y0,vx0,vy0,ax0,ay0,th0,om0,col0,mass0,charge0);
|
|
638
|
+
PERTURB(-h, xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem);
|
|
639
|
+
PERTURB(-2.0*h, x2m,y2m,vx2m,vy2m,ax2m,ay2m,th2m,om2m,col2m,mass2m,charge2m);
|
|
640
|
+
float f2p = evaluateScalarTokenList(x2p,y2p,vx2p,vy2p,ax2p,ay2p,th2p,om2p,col2p,mass2p,charge2p,self,
|
|
641
|
+
exprOffset, exprCount, constOffset);
|
|
642
|
+
float fp = evaluateScalarTokenList(xp,yp,vxp,vyp,axp,ayp,thp,omop,colp,massp,chargep,self,
|
|
643
|
+
exprOffset, exprCount, constOffset);
|
|
644
|
+
float f0 = evaluateScalarTokenList(x0,y0,vx0,vy0,ax0,ay0,th0,om0,col0,mass0,charge0,self,
|
|
645
|
+
exprOffset, exprCount, constOffset);
|
|
646
|
+
float fm = evaluateScalarTokenList(xm,ym,vxm,vym,axm,aym,thm,omom,colm,massm,chargem,self,
|
|
647
|
+
exprOffset, exprCount, constOffset);
|
|
648
|
+
float f2m = evaluateScalarTokenList(x2m,y2m,vx2m,vy2m,ax2m,ay2m,th2m,om2m,col2m,mass2m,charge2m,self,
|
|
649
|
+
exprOffset, exprCount, constOffset);
|
|
650
|
+
return (f2p - 4.0*fp + 6.0*f0 - 4.0*fm + f2m) / (h * h * h * h);
|
|
651
|
+
}
|
|
652
|
+
#undef PERTURB
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// ----------------------------------------------------------------------------
|
|
656
|
+
// SCALAR SUB‑EXPRESSION EVALUATOR (for select, sum_neighbors, tensor components)
|
|
657
|
+
// ----------------------------------------------------------------------------
|
|
658
|
+
float evalScalarSubExpr(float x, float y, float vx, float vy, float ax, float ay,
|
|
659
|
+
float theta, float omega, vec4 color,
|
|
660
|
+
float mass, float charge, int self,
|
|
661
|
+
int offset, int count, int constOffset, int neighbor) {
|
|
662
|
+
if (count <= 0) return 0.0;
|
|
663
|
+
float stack[32];
|
|
664
|
+
int sp = 0;
|
|
665
|
+
int idx = offset;
|
|
666
|
+
int end = offset + count;
|
|
667
|
+
while (idx < end) {
|
|
668
|
+
int token = allTokens[idx++];
|
|
669
|
+
if (token == TOKEN_NUMBER) {
|
|
670
|
+
int cidx = allTokens[idx++];
|
|
671
|
+
stack[sp++] = allConstants[constOffset + cidx];
|
|
672
|
+
}
|
|
673
|
+
else if (token == TOKEN_VARIABLE) {
|
|
674
|
+
int h = allTokens[idx++];
|
|
675
|
+
if (h == VAR_HASH_I) {
|
|
676
|
+
stack[sp++] = 0.0;
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
stack[sp++] = getVariableValue(h, x, y, vx, vy, ax, ay, theta, omega, color, mass, charge);
|
|
680
|
+
}
|
|
681
|
+
else if (token == TOKEN_OBJECT_REF) {
|
|
682
|
+
int obj = allTokens[idx++];
|
|
683
|
+
int prop = allTokens[idx++];
|
|
684
|
+
stack[sp++] = getObjectProperty(obj, prop, self);
|
|
685
|
+
}
|
|
686
|
+
else if (token == TOKEN_NEIGHBOR_INDEX) {
|
|
687
|
+
stack[sp++] = float(neighbor);
|
|
688
|
+
}
|
|
689
|
+
else if (token == TOKEN_DERIVATIVE) {
|
|
690
|
+
// Derivatives are not allowed inside select / sum_neighbors / tensors: return 0. Too lazy to implement
|
|
691
|
+
int dummy1 = allTokens[idx++];
|
|
692
|
+
int dummy2 = allTokens[idx++];
|
|
693
|
+
int dummy3 = allTokens[idx++];
|
|
694
|
+
int subc = allTokens[idx++];
|
|
695
|
+
idx += subc;
|
|
696
|
+
stack[sp++] = 0.0;
|
|
697
|
+
}
|
|
698
|
+
else if (token == TOKEN_COMP) {
|
|
699
|
+
int index = allTokens[idx++];
|
|
700
|
+
if (sp < 1) { stack[0] = 0.0; sp = 1; continue; }
|
|
701
|
+
float result = 0.0;
|
|
702
|
+
// We should not encounter TOKEN_COMP here. We'll just push 0.
|
|
703
|
+
stack[sp++] = 0.0;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Binary arithmetic and comparisons
|
|
707
|
+
else if (token == TOKEN_ADD || token == TOKEN_SUB || token == TOKEN_MUL ||
|
|
708
|
+
token == TOKEN_DIV || token == TOKEN_POW ||
|
|
709
|
+
token == TOKEN_LT || token == TOKEN_LE || token == TOKEN_GT ||
|
|
710
|
+
token == TOKEN_GE || token == TOKEN_EQ || token == TOKEN_NE) {
|
|
711
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
712
|
+
float b = stack[--sp];
|
|
713
|
+
float a = stack[--sp];
|
|
714
|
+
if (token == TOKEN_ADD) stack[sp++] = a + b;
|
|
715
|
+
else if (token == TOKEN_SUB) stack[sp++] = a - b;
|
|
716
|
+
else if (token == TOKEN_MUL) stack[sp++] = a * b;
|
|
717
|
+
else if (token == TOKEN_DIV) stack[sp++] = safeDivide(a, b);
|
|
718
|
+
else if (token == TOKEN_POW) stack[sp++] = safePow(a, b);
|
|
719
|
+
else if (token == TOKEN_LT) stack[sp++] = (a < b) ? 1.0 : 0.0;
|
|
720
|
+
else if (token == TOKEN_LE) stack[sp++] = (a <= b) ? 1.0 : 0.0;
|
|
721
|
+
else if (token == TOKEN_GT) stack[sp++] = (a > b) ? 1.0 : 0.0;
|
|
722
|
+
else if (token == TOKEN_GE) stack[sp++] = (a >= b) ? 1.0 : 0.0;
|
|
723
|
+
else if (token == TOKEN_EQ) stack[sp++] = (abs(a - b) < EPSILON) ? 1.0 : 0.0;
|
|
724
|
+
else stack[sp++] = (abs(a - b) >= EPSILON) ? 1.0 : 0.0;
|
|
725
|
+
}
|
|
726
|
+
// Unary operators
|
|
727
|
+
else if (token == TOKEN_NEG || token == TOKEN_SIN || token == TOKEN_COS || token == TOKEN_TAN ||
|
|
728
|
+
token == TOKEN_EXP || token == TOKEN_LOG || token == TOKEN_SQRT || token == TOKEN_ABS ||
|
|
729
|
+
token == TOKEN_FLOOR || token == TOKEN_CEIL || token == TOKEN_FRAC ||
|
|
730
|
+
token == TOKEN_SIGN || token == TOKEN_STEP) {
|
|
731
|
+
if (sp < 1) { stack[0] = 0.0; sp = 1; continue; }
|
|
732
|
+
float a = stack[--sp];
|
|
733
|
+
if (token == TOKEN_NEG) stack[sp++] = -a;
|
|
734
|
+
else if (token == TOKEN_SIN) stack[sp++] = sin(a);
|
|
735
|
+
else if (token == TOKEN_COS) stack[sp++] = cos(a);
|
|
736
|
+
else if (token == TOKEN_TAN) stack[sp++] = tan(a);
|
|
737
|
+
else if (token == TOKEN_EXP) stack[sp++] = safeExp(a);
|
|
738
|
+
else if (token == TOKEN_LOG) stack[sp++] = safeLog(a);
|
|
739
|
+
else if (token == TOKEN_SQRT)stack[sp++] = sqrt(max(0.0, a));
|
|
740
|
+
else if (token == TOKEN_ABS) stack[sp++] = abs(a);
|
|
741
|
+
else if (token == TOKEN_FLOOR)stack[sp++] = floor(a);
|
|
742
|
+
else if (token == TOKEN_CEIL)stack[sp++] = ceil(a);
|
|
743
|
+
else if (token == TOKEN_FRAC)stack[sp++] = fract(a);
|
|
744
|
+
else if (token == TOKEN_SIGN)stack[sp++] = signFunc(a);
|
|
745
|
+
else stack[sp++] = stepFunc(a);
|
|
746
|
+
}
|
|
747
|
+
else if (token == TOKEN_MOD) {
|
|
748
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
749
|
+
float b = stack[--sp];
|
|
750
|
+
float a = stack[--sp];
|
|
751
|
+
stack[sp++] = (abs(b) < EPSILON) ? 0.0 : mod(a, b);
|
|
752
|
+
}
|
|
753
|
+
else if (token == TOKEN_MIN || token == TOKEN_MAX) {
|
|
754
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
755
|
+
float b = stack[--sp];
|
|
756
|
+
float a = stack[--sp];
|
|
757
|
+
stack[sp++] = (token == TOKEN_MIN) ? min(a, b) : max(a, b);
|
|
758
|
+
}
|
|
759
|
+
else if (token == TOKEN_ATAN2) {
|
|
760
|
+
if (sp < 2) { stack[0] = 0.0; sp = 1; continue; }
|
|
761
|
+
float xv = stack[--sp];
|
|
762
|
+
float yv = stack[--sp];
|
|
763
|
+
stack[sp++] = atan(yv, xv);
|
|
764
|
+
}
|
|
765
|
+
else if (token == TOKEN_CLAMP) {
|
|
766
|
+
if (sp < 3) { stack[0] = 0.0; sp = 1; continue; }
|
|
767
|
+
float maxv = stack[--sp];
|
|
768
|
+
float minv = stack[--sp];
|
|
769
|
+
float val = stack[--sp];
|
|
770
|
+
stack[sp++] = clamp(val, minv, maxv);
|
|
771
|
+
}
|
|
772
|
+
else if (token == TOKEN_NOISE) {
|
|
773
|
+
if (sp < 2) { stack[sp++] = 0.0; continue; }
|
|
774
|
+
float yv = stack[--sp];
|
|
775
|
+
float xv = stack[--sp];
|
|
776
|
+
stack[sp++] = noise(vec2(xv, yv));
|
|
777
|
+
}
|
|
778
|
+
else if (token == TOKEN_RAND) {
|
|
779
|
+
stack[sp++] = rand(vec2(uTime, float(self)));
|
|
780
|
+
}
|
|
781
|
+
// Other tokens (DOT, CROSS, NORM, SELECT, SUM_NEIGHBORS, TENSOR_LIT) are not used here.
|
|
782
|
+
}
|
|
783
|
+
return (sp > 0) ? stack[0] : 0.0;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// ----------------------------------------------------------------------------
|
|
787
|
+
// MAIN TYPED EVALUATOR (handles all tokens, including derivatives and comparisons)
|
|
788
|
+
// ----------------------------------------------------------------------------
|
|
789
|
+
float evaluateRPNComponent(float x, float y, float vx, float vy, float ax, float ay,
|
|
790
|
+
float theta, float omega, vec4 color,
|
|
791
|
+
float mass, float charge, int self,
|
|
792
|
+
int componentType,
|
|
793
|
+
int tokenOffset, int tokenCount, int constantOffset) {
|
|
794
|
+
if (tokenCount <= 0) {
|
|
795
|
+
if (componentType == 2) return 0.0; // angular acceleration default
|
|
796
|
+
if (componentType >= 3 && componentType <= 6) return 1.0; // color default
|
|
797
|
+
return 0.0;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Typed stack: each entry is a scalar, vector (vec2/vec3/vec4), or complex
|
|
801
|
+
int stack_type[128]; // 0=scalar, 1=vec2, 2=vec3, 3=vec4
|
|
802
|
+
float stack_data[128][4];
|
|
803
|
+
int stack_rank[128];
|
|
804
|
+
int stack_shape[128][4];
|
|
805
|
+
int stack_size[128];
|
|
806
|
+
bool stack_isComplex[128];
|
|
807
|
+
int sp = 0;
|
|
808
|
+
|
|
809
|
+
int idx = tokenOffset;
|
|
810
|
+
int end = tokenOffset + tokenCount;
|
|
811
|
+
while (idx < end) {
|
|
812
|
+
int token = allTokens[idx++];
|
|
813
|
+
|
|
814
|
+
// ----- DERIVATIVE (handled only here – no recursion) -----
|
|
815
|
+
if (token == TOKEN_DERIVATIVE) {
|
|
816
|
+
int wrtHash = allTokens[idx++];
|
|
817
|
+
int order = allTokens[idx++];
|
|
818
|
+
int method = allTokens[idx++];
|
|
819
|
+
int subCount = allTokens[idx++];
|
|
820
|
+
int subOffset = idx;
|
|
821
|
+
idx += subCount;
|
|
822
|
+
float deriv = computeDerivative(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
823
|
+
mass, charge, self,
|
|
824
|
+
wrtHash, order, subOffset, subCount, constantOffset);
|
|
825
|
+
stack_type[sp] = 0;
|
|
826
|
+
stack_data[sp][0] = deriv;
|
|
827
|
+
stack_isComplex[sp] = false;
|
|
828
|
+
stack_rank[sp] = 0;
|
|
829
|
+
stack_size[sp] = 1;
|
|
830
|
+
sp++;
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// ----- TENSOR LITERAL -----
|
|
835
|
+
if (token == TOKEN_TENSOR_LIT) {
|
|
836
|
+
int rank = allTokens[idx++];
|
|
837
|
+
int shape[4] = {0,0,0,0};
|
|
838
|
+
int total = 1;
|
|
839
|
+
for (int d = 0; d < rank; ++d) {
|
|
840
|
+
shape[d] = allTokens[idx++];
|
|
841
|
+
total *= shape[d];
|
|
842
|
+
}
|
|
843
|
+
if (total > 4) {
|
|
844
|
+
// Skip unsupported high‑rank tensors
|
|
845
|
+
for (int c = 0; c < total; ++c) {
|
|
846
|
+
int compCount = allTokens[idx++];
|
|
847
|
+
idx += compCount;
|
|
848
|
+
}
|
|
849
|
+
stack_type[sp] = 0;
|
|
850
|
+
stack_data[sp][0] = 0.0;
|
|
851
|
+
stack_isComplex[sp] = false;
|
|
852
|
+
stack_rank[sp] = 0;
|
|
853
|
+
stack_size[sp] = 1;
|
|
854
|
+
sp++;
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
// rank‑1 vector (length 2,3,4)
|
|
858
|
+
if (rank == 1 && total >= 2 && total <= 4) {
|
|
859
|
+
float comps[4];
|
|
860
|
+
for (int c = 0; c < total; ++c) {
|
|
861
|
+
int compCount = allTokens[idx++];
|
|
862
|
+
int compOffset = idx;
|
|
863
|
+
idx += compCount;
|
|
864
|
+
comps[c] = evalScalarSubExpr(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
865
|
+
mass, charge, self,
|
|
866
|
+
compOffset, compCount, constantOffset, -1);
|
|
867
|
+
}
|
|
868
|
+
int vecType = (total == 2) ? 1 : (total == 3) ? 2 : 3;
|
|
869
|
+
stack_type[sp] = vecType;
|
|
870
|
+
for (int c = 0; c < total; ++c) stack_data[sp][c] = comps[c];
|
|
871
|
+
stack_rank[sp] = 1;
|
|
872
|
+
stack_shape[sp][0] = total;
|
|
873
|
+
stack_size[sp] = total;
|
|
874
|
+
stack_isComplex[sp] = false;
|
|
875
|
+
sp++;
|
|
876
|
+
} else {
|
|
877
|
+
// Higher rank: fallback to first component as scalar
|
|
878
|
+
int firstCount = allTokens[idx++];
|
|
879
|
+
int firstOffset = idx;
|
|
880
|
+
idx += firstCount;
|
|
881
|
+
float firstVal = evalScalarSubExpr(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
882
|
+
mass, charge, self,
|
|
883
|
+
firstOffset, firstCount, constantOffset, -1);
|
|
884
|
+
for (int c = 1; c < total; ++c) {
|
|
885
|
+
int compCount = allTokens[idx++];
|
|
886
|
+
idx += compCount;
|
|
887
|
+
}
|
|
888
|
+
stack_type[sp] = 0;
|
|
889
|
+
stack_data[sp][0] = firstVal;
|
|
890
|
+
stack_isComplex[sp] = false;
|
|
891
|
+
stack_rank[sp] = 0;
|
|
892
|
+
stack_size[sp] = 1;
|
|
893
|
+
sp++;
|
|
894
|
+
}
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// ----- SELECT -----
|
|
899
|
+
if (token == TOKEN_SELECT) {
|
|
900
|
+
int condCount = allTokens[idx++];
|
|
901
|
+
int condOffset = idx; idx += condCount;
|
|
902
|
+
int aCount = allTokens[idx++];
|
|
903
|
+
int aOffset = idx; idx += aCount;
|
|
904
|
+
int bCount = allTokens[idx++];
|
|
905
|
+
int bOffset = idx; idx += bCount;
|
|
906
|
+
float cond = evalScalarSubExpr(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
907
|
+
mass, charge, self,
|
|
908
|
+
condOffset, condCount, constantOffset, -1);
|
|
909
|
+
if (cond > 0.0) {
|
|
910
|
+
float aVal = evalScalarSubExpr(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
911
|
+
mass, charge, self,
|
|
912
|
+
aOffset, aCount, constantOffset, -1);
|
|
913
|
+
stack_type[sp] = 0;
|
|
914
|
+
stack_data[sp][0] = aVal;
|
|
915
|
+
stack_isComplex[sp] = false;
|
|
916
|
+
stack_rank[sp] = 0;
|
|
917
|
+
stack_size[sp] = 1;
|
|
918
|
+
} else {
|
|
919
|
+
float bVal = evalScalarSubExpr(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
920
|
+
mass, charge, self,
|
|
921
|
+
bOffset, bCount, constantOffset, -1);
|
|
922
|
+
stack_type[sp] = 0;
|
|
923
|
+
stack_data[sp][0] = bVal;
|
|
924
|
+
stack_isComplex[sp] = false;
|
|
925
|
+
stack_rank[sp] = 0;
|
|
926
|
+
stack_size[sp] = 1;
|
|
927
|
+
}
|
|
928
|
+
sp++;
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// ----- SUM_NEIGHBORS -----
|
|
933
|
+
if (token == TOKEN_SUM_NEIGHBORS) {
|
|
934
|
+
int weightCount = allTokens[idx++];
|
|
935
|
+
int weightOffset = idx; idx += weightCount;
|
|
936
|
+
int bodyCount = allTokens[idx++];
|
|
937
|
+
int bodyOffset = idx; idx += bodyCount;
|
|
938
|
+
float sum = 0.0;
|
|
939
|
+
for (int n = 0; n < uNumObjects; ++n) {
|
|
940
|
+
if (n == self) continue;
|
|
941
|
+
float w = evalScalarSubExpr(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
942
|
+
mass, charge, self,
|
|
943
|
+
weightOffset, weightCount, constantOffset, n);
|
|
944
|
+
if (abs(w) < EPSILON) continue;
|
|
945
|
+
float v = evalScalarSubExpr(x, y, vx, vy, ax, ay, theta, omega, color,
|
|
946
|
+
mass, charge, self,
|
|
947
|
+
bodyOffset, bodyCount, constantOffset, n);
|
|
948
|
+
sum += w * v;
|
|
949
|
+
}
|
|
950
|
+
stack_type[sp] = 0;
|
|
951
|
+
stack_data[sp][0] = sum;
|
|
952
|
+
stack_isComplex[sp] = false;
|
|
953
|
+
stack_rank[sp] = 0;
|
|
954
|
+
stack_size[sp] = 1;
|
|
955
|
+
sp++;
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// ----- NUMBERS -----
|
|
960
|
+
if (token == TOKEN_NUMBER) {
|
|
961
|
+
int constIdx = allTokens[idx++];
|
|
962
|
+
float val = allConstants[constantOffset + constIdx];
|
|
963
|
+
stack_type[sp] = 0;
|
|
964
|
+
stack_data[sp][0] = val;
|
|
965
|
+
stack_isComplex[sp] = false;
|
|
966
|
+
stack_rank[sp] = 0;
|
|
967
|
+
stack_size[sp] = 1;
|
|
968
|
+
sp++;
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// ----- VARIABLES (including imaginary unit i) -----
|
|
973
|
+
if (token == TOKEN_VARIABLE) {
|
|
974
|
+
int h = allTokens[idx++];
|
|
975
|
+
if (h == VAR_HASH_I) {
|
|
976
|
+
// Imaginary unit
|
|
977
|
+
stack_type[sp] = 0;
|
|
978
|
+
stack_data[sp][0] = 0.0;
|
|
979
|
+
stack_data[sp][1] = 1.0;
|
|
980
|
+
stack_isComplex[sp] = true;
|
|
981
|
+
stack_rank[sp] = 0;
|
|
982
|
+
stack_size[sp] = 1;
|
|
983
|
+
sp++;
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
float val = getVariableValue(h, x, y, vx, vy, ax, ay, theta, omega, color, mass, charge);
|
|
987
|
+
stack_type[sp] = 0;
|
|
988
|
+
stack_data[sp][0] = val;
|
|
989
|
+
stack_isComplex[sp] = false;
|
|
990
|
+
stack_rank[sp] = 0;
|
|
991
|
+
stack_size[sp] = 1;
|
|
992
|
+
sp++;
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// ----- OBJECT REFERENCE -----
|
|
997
|
+
if (token == TOKEN_OBJECT_REF) {
|
|
998
|
+
int obj = allTokens[idx++];
|
|
999
|
+
int prop = allTokens[idx++];
|
|
1000
|
+
float val = getObjectProperty(obj, prop, self);
|
|
1001
|
+
stack_type[sp] = 0;
|
|
1002
|
+
stack_data[sp][0] = val;
|
|
1003
|
+
stack_isComplex[sp] = false;
|
|
1004
|
+
stack_rank[sp] = 0;
|
|
1005
|
+
stack_size[sp] = 1;
|
|
1006
|
+
sp++;
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// ----- NEIGHBOR INDEX -----
|
|
1011
|
+
if (token == TOKEN_NEIGHBOR_INDEX) {
|
|
1012
|
+
stack_type[sp] = 0;
|
|
1013
|
+
stack_data[sp][0] = -1.0;
|
|
1014
|
+
stack_isComplex[sp] = false;
|
|
1015
|
+
stack_rank[sp] = 0;
|
|
1016
|
+
stack_size[sp] = 1;
|
|
1017
|
+
sp++;
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// ----- NOISE -----
|
|
1022
|
+
if (token == TOKEN_NOISE) {
|
|
1023
|
+
if (sp < 2) {
|
|
1024
|
+
stack_type[sp] = 0;
|
|
1025
|
+
stack_data[sp][0] = 0.0;
|
|
1026
|
+
stack_rank[sp] = 0;
|
|
1027
|
+
stack_size[sp] = 1;
|
|
1028
|
+
sp++;
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
float yv = stack_data[sp-1][0]; sp--;
|
|
1032
|
+
float xv = stack_data[sp-1][0]; sp--;
|
|
1033
|
+
stack_type[sp] = 0;
|
|
1034
|
+
stack_data[sp][0] = noise(vec2(xv, yv));
|
|
1035
|
+
stack_isComplex[sp] = false;
|
|
1036
|
+
stack_rank[sp] = 0;
|
|
1037
|
+
stack_size[sp] = 1;
|
|
1038
|
+
sp++;
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// ----- RAND -----
|
|
1043
|
+
if (token == TOKEN_RAND) {
|
|
1044
|
+
stack_type[sp] = 0;
|
|
1045
|
+
stack_data[sp][0] = rand(vec2(uTime, float(self)));
|
|
1046
|
+
stack_isComplex[sp] = false;
|
|
1047
|
+
stack_rank[sp] = 0;
|
|
1048
|
+
stack_size[sp] = 1;
|
|
1049
|
+
sp++;
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// ----- DOT PRODUCT -----
|
|
1054
|
+
if (token == TOKEN_DOT) {
|
|
1055
|
+
if (sp < 2) {
|
|
1056
|
+
stack_type[sp] = 0;
|
|
1057
|
+
stack_data[sp][0] = 0.0;
|
|
1058
|
+
stack_rank[sp] = 0;
|
|
1059
|
+
stack_size[sp] = 1;
|
|
1060
|
+
sp++;
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
sp--; int b = sp;
|
|
1064
|
+
sp--; int a = sp;
|
|
1065
|
+
int sz = min(stack_size[a], stack_size[b]);
|
|
1066
|
+
float result = 0.0;
|
|
1067
|
+
for (int c = 0; c < sz; ++c) {
|
|
1068
|
+
float av = stack_isComplex[a] ? stack_data[a][0] : stack_data[a][c];
|
|
1069
|
+
float bv = stack_isComplex[b] ? stack_data[b][0] : stack_data[b][c];
|
|
1070
|
+
result += av * bv;
|
|
1071
|
+
}
|
|
1072
|
+
stack_type[sp] = 0;
|
|
1073
|
+
stack_data[sp][0] = result;
|
|
1074
|
+
stack_isComplex[sp] = false;
|
|
1075
|
+
stack_rank[sp] = 0;
|
|
1076
|
+
stack_size[sp] = 1;
|
|
1077
|
+
sp++;
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// ----- CROSS PRODUCT (pads to 3D with zeroes) -----
|
|
1082
|
+
if (token == TOKEN_CROSS) {
|
|
1083
|
+
if (sp < 2) {
|
|
1084
|
+
stack_type[sp] = 0;
|
|
1085
|
+
stack_data[sp][0] = 0.0;
|
|
1086
|
+
stack_rank[sp] = 0;
|
|
1087
|
+
stack_size[sp] = 1;
|
|
1088
|
+
sp++;
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
sp--; int b = sp;
|
|
1092
|
+
sp--; int a = sp;
|
|
1093
|
+
vec3 va = vec3(0.0), vb = vec3(0.0);
|
|
1094
|
+
for (int c = 0; c < min(stack_size[a], 3); ++c) va[c] = stack_data[a][c];
|
|
1095
|
+
for (int c = 0; c < min(stack_size[b], 3); ++c) vb[c] = stack_data[b][c];
|
|
1096
|
+
vec3 cr = cross(va, vb);
|
|
1097
|
+
stack_type[sp] = 2; // vec3
|
|
1098
|
+
stack_data[sp][0] = cr.x;
|
|
1099
|
+
stack_data[sp][1] = cr.y;
|
|
1100
|
+
stack_data[sp][2] = cr.z;
|
|
1101
|
+
stack_isComplex[sp] = false;
|
|
1102
|
+
stack_rank[sp] = 1;
|
|
1103
|
+
stack_shape[sp][0] = 3;
|
|
1104
|
+
stack_size[sp] = 3;
|
|
1105
|
+
sp++;
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// ----- NORM (Euclidean length) -----
|
|
1110
|
+
if (token == TOKEN_NORM) {
|
|
1111
|
+
if (sp < 1) {
|
|
1112
|
+
stack_type[sp] = 0;
|
|
1113
|
+
stack_data[sp][0] = 0.0;
|
|
1114
|
+
stack_rank[sp] = 0;
|
|
1115
|
+
stack_size[sp] = 1;
|
|
1116
|
+
sp++;
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
sp--; int a = sp;
|
|
1120
|
+
float lenSq = 0.0;
|
|
1121
|
+
for (int c = 0; c < stack_size[a]; ++c) {
|
|
1122
|
+
float v = stack_isComplex[a] ? stack_data[a][0] : stack_data[a][c];
|
|
1123
|
+
lenSq += v * v;
|
|
1124
|
+
}
|
|
1125
|
+
stack_type[sp] = 0;
|
|
1126
|
+
stack_data[sp][0] = sqrt(lenSq);
|
|
1127
|
+
stack_isComplex[sp] = false;
|
|
1128
|
+
stack_rank[sp] = 0;
|
|
1129
|
+
stack_size[sp] = 1;
|
|
1130
|
+
sp++;
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// ----- COMP (extract component from a vector/tensor) -----
|
|
1135
|
+
if (token == TOKEN_COMP) {
|
|
1136
|
+
// The index is the next token in the stream
|
|
1137
|
+
int index = allTokens[idx++]; // 0‑based index
|
|
1138
|
+
if (sp < 1) {
|
|
1139
|
+
stack_type[sp] = 0;
|
|
1140
|
+
stack_data[sp][0] = 0.0;
|
|
1141
|
+
stack_rank[sp] = 0;
|
|
1142
|
+
stack_size[sp] = 1;
|
|
1143
|
+
sp++;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
sp--; // pop the tensor/vector
|
|
1147
|
+
int a_idx = sp;
|
|
1148
|
+
float result = 0.0;
|
|
1149
|
+
if (index >= 0 && index < stack_size[a_idx]) {
|
|
1150
|
+
result = stack_data[a_idx][index];
|
|
1151
|
+
}
|
|
1152
|
+
stack_type[sp] = 0;
|
|
1153
|
+
stack_data[sp][0] = result;
|
|
1154
|
+
stack_isComplex[sp] = false;
|
|
1155
|
+
stack_rank[sp] = 0;
|
|
1156
|
+
stack_size[sp] = 1;
|
|
1157
|
+
sp++;
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
// ----- POWER OPERATOR (supports scalar and non-complex) ----- a lazy solution that bypasses the other pow down the line
|
|
1161
|
+
else if (token == TOKEN_POW) {
|
|
1162
|
+
if (sp < 2) {
|
|
1163
|
+
stack_type[sp] = 0;
|
|
1164
|
+
stack_data[sp][0] = 0.0;
|
|
1165
|
+
stack_rank[sp] = 0;
|
|
1166
|
+
stack_size[sp] = 1;
|
|
1167
|
+
stack_isComplex[sp] = false;
|
|
1168
|
+
sp++;
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
// Pop exponent first (top of stack)
|
|
1172
|
+
sp--;
|
|
1173
|
+
int b_idx = sp;
|
|
1174
|
+
sp--;
|
|
1175
|
+
int a_idx = sp;
|
|
1176
|
+
bool use_complex = stack_isComplex[a_idx] || stack_isComplex[b_idx];
|
|
1177
|
+
if (!use_complex) {
|
|
1178
|
+
// Scalar power (real base, real exponent)
|
|
1179
|
+
float base_val = stack_data[a_idx][0];
|
|
1180
|
+
float exp_val = stack_data[b_idx][0];
|
|
1181
|
+
float result = safePow(base_val, exp_val);
|
|
1182
|
+
stack_type[sp] = 0;
|
|
1183
|
+
stack_data[sp][0] = result;
|
|
1184
|
+
stack_rank[sp] = 0;
|
|
1185
|
+
stack_size[sp] = 1;
|
|
1186
|
+
stack_isComplex[sp] = false;
|
|
1187
|
+
} else {
|
|
1188
|
+
// Complex power
|
|
1189
|
+
vec2 base_c = vec2(stack_data[a_idx][0], stack_data[a_idx][1]);
|
|
1190
|
+
vec2 exp_c = vec2(stack_data[b_idx][0], stack_data[b_idx][1]);
|
|
1191
|
+
vec2 result = cPow(base_c, exp_c);
|
|
1192
|
+
stack_type[sp] = 0;
|
|
1193
|
+
stack_data[sp][0] = result.x;
|
|
1194
|
+
stack_data[sp][1] = result.y;
|
|
1195
|
+
stack_rank[sp] = 0;
|
|
1196
|
+
stack_size[sp] = 1;
|
|
1197
|
+
stack_isComplex[sp] = true;
|
|
1198
|
+
}
|
|
1199
|
+
sp++;
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
// ----- BINARY OPERATORS (arithmetic + comparisons) -----
|
|
1203
|
+
if (token == TOKEN_ADD || token == TOKEN_SUB || token == TOKEN_MUL ||
|
|
1204
|
+
token == TOKEN_DIV || token == TOKEN_POW ||
|
|
1205
|
+
token == TOKEN_LT || token == TOKEN_LE || token == TOKEN_GT ||
|
|
1206
|
+
token == TOKEN_GE || token == TOKEN_EQ || token == TOKEN_NE) {
|
|
1207
|
+
|
|
1208
|
+
if (sp < 2) {
|
|
1209
|
+
stack_type[sp] = 0;
|
|
1210
|
+
stack_data[sp][0] = 0.0;
|
|
1211
|
+
stack_rank[sp] = 0;
|
|
1212
|
+
stack_size[sp] = 1;
|
|
1213
|
+
sp++;
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
sp--; int b = sp;
|
|
1217
|
+
sp--; int a = sp;
|
|
1218
|
+
|
|
1219
|
+
// Complex arithmetic for ADD, SUB, MUL, DIV, POW
|
|
1220
|
+
if ((token == TOKEN_ADD || token == TOKEN_SUB || token == TOKEN_MUL ||
|
|
1221
|
+
token == TOKEN_DIV || token == TOKEN_POW) &&
|
|
1222
|
+
(stack_isComplex[a] || stack_isComplex[b])) {
|
|
1223
|
+
vec2 ca = stack_isComplex[a] ? vec2(stack_data[a][0], stack_data[a][1])
|
|
1224
|
+
: vec2(stack_data[a][0], 0.0);
|
|
1225
|
+
vec2 cb = stack_isComplex[b] ? vec2(stack_data[b][0], stack_data[b][1])
|
|
1226
|
+
: vec2(stack_data[b][0], 0.0);
|
|
1227
|
+
vec2 cr;
|
|
1228
|
+
if (token == TOKEN_ADD) cr = ca + cb;
|
|
1229
|
+
else if (token == TOKEN_SUB) cr = ca - cb;
|
|
1230
|
+
else if (token == TOKEN_MUL) cr = cMul(ca, cb);
|
|
1231
|
+
else if (token == TOKEN_DIV) cr = cDiv(ca, cb);
|
|
1232
|
+
else cr = cPow(ca, cb);
|
|
1233
|
+
stack_type[sp] = 0;
|
|
1234
|
+
stack_data[sp][0] = cr.x;
|
|
1235
|
+
stack_data[sp][1] = cr.y;
|
|
1236
|
+
stack_isComplex[sp] = true;
|
|
1237
|
+
stack_rank[sp] = 0;
|
|
1238
|
+
stack_size[sp] = 1;
|
|
1239
|
+
sp++;
|
|
1240
|
+
continue;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// Scalar * vector (broadcast)
|
|
1244
|
+
if ((token == TOKEN_ADD || token == TOKEN_SUB || token == TOKEN_MUL ||
|
|
1245
|
+
token == TOKEN_DIV || token == TOKEN_POW) &&
|
|
1246
|
+
stack_type[a] == 0 && !stack_isComplex[a] && stack_type[b] != 0) {
|
|
1247
|
+
float s = stack_data[a][0];
|
|
1248
|
+
for (int c = 0; c < stack_size[b]; ++c) {
|
|
1249
|
+
if (token == TOKEN_ADD) stack_data[b][c] += s;
|
|
1250
|
+
else if (token == TOKEN_SUB) stack_data[b][c] -= s;
|
|
1251
|
+
else if (token == TOKEN_MUL) stack_data[b][c] *= s;
|
|
1252
|
+
else if (token == TOKEN_DIV) stack_data[b][c] = safeDivide(stack_data[b][c], s);
|
|
1253
|
+
else stack_data[b][c] = safePow(stack_data[b][c], s);
|
|
1254
|
+
}
|
|
1255
|
+
stack_type[sp] = stack_type[b];
|
|
1256
|
+
stack_rank[sp] = stack_rank[b];
|
|
1257
|
+
for (int d = 0; d < 4; ++d) stack_shape[sp][d] = stack_shape[b][d];
|
|
1258
|
+
stack_size[sp] = stack_size[b];
|
|
1259
|
+
stack_isComplex[sp] = false;
|
|
1260
|
+
sp++;
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
if ((token == TOKEN_ADD || token == TOKEN_SUB || token == TOKEN_MUL ||
|
|
1264
|
+
token == TOKEN_DIV || token == TOKEN_POW) &&
|
|
1265
|
+
stack_type[b] == 0 && !stack_isComplex[b] && stack_type[a] != 0) {
|
|
1266
|
+
float s = stack_data[b][0];
|
|
1267
|
+
for (int c = 0; c < stack_size[a]; ++c) {
|
|
1268
|
+
if (token == TOKEN_ADD) stack_data[a][c] += s;
|
|
1269
|
+
else if (token == TOKEN_SUB) stack_data[a][c] -= s;
|
|
1270
|
+
else if (token == TOKEN_MUL) stack_data[a][c] *= s;
|
|
1271
|
+
else if (token == TOKEN_DIV) stack_data[a][c] = safeDivide(stack_data[a][c], s);
|
|
1272
|
+
else stack_data[a][c] = safePow(stack_data[a][c], s);
|
|
1273
|
+
}
|
|
1274
|
+
stack_type[sp] = stack_type[a];
|
|
1275
|
+
stack_rank[sp] = stack_rank[a];
|
|
1276
|
+
for (int d = 0; d < 4; ++d) stack_shape[sp][d] = stack_shape[a][d];
|
|
1277
|
+
stack_size[sp] = stack_size[a];
|
|
1278
|
+
stack_isComplex[sp] = false;
|
|
1279
|
+
sp++;
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// Component‑wise for vectors; comparisons always produce scalar.
|
|
1284
|
+
int sz = max(stack_size[a], stack_size[b]);
|
|
1285
|
+
|
|
1286
|
+
// If both operands are scalars, compute directly and push a scalar result.
|
|
1287
|
+
if (stack_size[a] == 1 && stack_size[b] == 1 && !stack_isComplex[a] && !stack_isComplex[b]) {
|
|
1288
|
+
float av = stack_data[a][0];
|
|
1289
|
+
float bv = stack_data[b][0];
|
|
1290
|
+
float res;
|
|
1291
|
+
if (token == TOKEN_ADD) res = av + bv;
|
|
1292
|
+
else if (token == TOKEN_SUB) res = av - bv;
|
|
1293
|
+
else if (token == TOKEN_MUL) res = av * bv;
|
|
1294
|
+
else if (token == TOKEN_DIV) res = safeDivide(av, bv);
|
|
1295
|
+
else res = safePow(av, bv); // for completeness
|
|
1296
|
+
stack_type[sp] = 0;
|
|
1297
|
+
stack_data[sp][0] = res;
|
|
1298
|
+
stack_rank[sp] = 0;
|
|
1299
|
+
stack_size[sp] = 1;
|
|
1300
|
+
stack_isComplex[sp] = false;
|
|
1301
|
+
sp++;
|
|
1302
|
+
continue;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
stack_type[sp] = (token >= TOKEN_LT) ? 0 : stack_type[a];
|
|
1306
|
+
stack_rank[sp] = (token >= TOKEN_LT) ? 0 : stack_rank[a];
|
|
1307
|
+
for (int d = 0; d < 4; ++d) stack_shape[sp][d] = (token >= TOKEN_LT) ? 0 : stack_shape[a][d];
|
|
1308
|
+
stack_size[sp] = (token >= TOKEN_LT) ? 1 : sz;
|
|
1309
|
+
for (int c = 0; c < sz && c < 4; ++c) {
|
|
1310
|
+
float av = (c < stack_size[a]) ? stack_data[a][c] : 0.0;
|
|
1311
|
+
float bv = (c < stack_size[b]) ? stack_data[b][c] : 0.0;
|
|
1312
|
+
if (token == TOKEN_ADD) stack_data[sp][c] = av + bv;
|
|
1313
|
+
else if (token == TOKEN_SUB) stack_data[sp][c] = av - bv;
|
|
1314
|
+
else if (token == TOKEN_MUL) stack_data[sp][c] = av * bv;
|
|
1315
|
+
else if (token == TOKEN_DIV) stack_data[sp][c] = safeDivide(av, bv);
|
|
1316
|
+
else if (token == TOKEN_POW) stack_data[sp][c] = safePow(av, bv);
|
|
1317
|
+
else if (token == TOKEN_LT) stack_data[sp][c] = (av < bv) ? 1.0 : 0.0;
|
|
1318
|
+
else if (token == TOKEN_LE) stack_data[sp][c] = (av <= bv) ? 1.0 : 0.0;
|
|
1319
|
+
else if (token == TOKEN_GT) stack_data[sp][c] = (av > bv) ? 1.0 : 0.0;
|
|
1320
|
+
else if (token == TOKEN_GE) stack_data[sp][c] = (av >= bv) ? 1.0 : 0.0;
|
|
1321
|
+
else if (token == TOKEN_EQ) stack_data[sp][c] = (abs(av - bv) < EPSILON) ? 1.0 : 0.0;
|
|
1322
|
+
else stack_data[sp][c] = (abs(av - bv) >= EPSILON) ? 1.0 : 0.0;
|
|
1323
|
+
}
|
|
1324
|
+
stack_isComplex[sp] = false;
|
|
1325
|
+
sp++;
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// ----- UNARY OPERATORS -----
|
|
1330
|
+
if (token == TOKEN_NEG || token == TOKEN_SIN || token == TOKEN_COS ||
|
|
1331
|
+
token == TOKEN_TAN || token == TOKEN_EXP || token == TOKEN_LOG ||
|
|
1332
|
+
token == TOKEN_SQRT || token == TOKEN_ABS || token == TOKEN_FLOOR ||
|
|
1333
|
+
token == TOKEN_CEIL || token == TOKEN_FRAC || token == TOKEN_SIGN ||
|
|
1334
|
+
token == TOKEN_STEP) {
|
|
1335
|
+
if (sp < 1) continue;
|
|
1336
|
+
sp--; int a = sp;
|
|
1337
|
+
|
|
1338
|
+
if (stack_isComplex[a]) {
|
|
1339
|
+
vec2 z = vec2(stack_data[a][0], stack_data[a][1]);
|
|
1340
|
+
vec2 r;
|
|
1341
|
+
if (token == TOKEN_NEG) r = -z;
|
|
1342
|
+
else if (token == TOKEN_SIN) r = cSin(z);
|
|
1343
|
+
else if (token == TOKEN_COS) r = cCos(z);
|
|
1344
|
+
else if (token == TOKEN_TAN) r = cTan(z);
|
|
1345
|
+
else if (token == TOKEN_EXP) r = cExp(z);
|
|
1346
|
+
else if (token == TOKEN_LOG) r = cLog(z);
|
|
1347
|
+
else if (token == TOKEN_SQRT)r = cPow(z, vec2(0.5, 0.0));
|
|
1348
|
+
else if (token == TOKEN_ABS) {
|
|
1349
|
+
stack_type[sp] = 0;
|
|
1350
|
+
stack_data[sp][0] = length(z);
|
|
1351
|
+
stack_isComplex[sp] = false;
|
|
1352
|
+
stack_rank[sp] = 0;
|
|
1353
|
+
stack_size[sp] = 1;
|
|
1354
|
+
sp++;
|
|
1355
|
+
continue;
|
|
1356
|
+
} else {
|
|
1357
|
+
r = z;
|
|
1358
|
+
}
|
|
1359
|
+
stack_type[sp] = 0;
|
|
1360
|
+
stack_data[sp][0] = r.x;
|
|
1361
|
+
stack_data[sp][1] = r.y;
|
|
1362
|
+
stack_isComplex[sp] = true;
|
|
1363
|
+
stack_rank[sp] = 0;
|
|
1364
|
+
stack_size[sp] = 1;
|
|
1365
|
+
sp++;
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
// Real case: elementwise
|
|
1370
|
+
for (int c = 0; c < stack_size[a]; ++c) {
|
|
1371
|
+
float v = stack_data[a][c];
|
|
1372
|
+
if (token == TOKEN_NEG) stack_data[a][c] = -v;
|
|
1373
|
+
else if (token == TOKEN_SIN) stack_data[a][c] = sin(v);
|
|
1374
|
+
else if (token == TOKEN_COS) stack_data[a][c] = cos(v);
|
|
1375
|
+
else if (token == TOKEN_TAN) stack_data[a][c] = tan(v);
|
|
1376
|
+
else if (token == TOKEN_EXP) stack_data[a][c] = safeExp(v);
|
|
1377
|
+
else if (token == TOKEN_LOG) stack_data[a][c] = safeLog(v);
|
|
1378
|
+
else if (token == TOKEN_SQRT)stack_data[a][c] = sqrt(max(0.0, v));
|
|
1379
|
+
else if (token == TOKEN_ABS) stack_data[a][c] = abs(v);
|
|
1380
|
+
else if (token == TOKEN_FLOOR)stack_data[a][c] = floor(v);
|
|
1381
|
+
else if (token == TOKEN_CEIL)stack_data[a][c] = ceil(v);
|
|
1382
|
+
else if (token == TOKEN_FRAC)stack_data[a][c] = fract(v);
|
|
1383
|
+
else if (token == TOKEN_SIGN)stack_data[a][c] = signFunc(v);
|
|
1384
|
+
else stack_data[a][c] = stepFunc(v);
|
|
1385
|
+
}
|
|
1386
|
+
stack_type[sp] = stack_type[a];
|
|
1387
|
+
stack_rank[sp] = stack_rank[a];
|
|
1388
|
+
for (int d = 0; d < 4; ++d) stack_shape[sp][d] = stack_shape[a][d];
|
|
1389
|
+
stack_size[sp] = stack_size[a];
|
|
1390
|
+
stack_isComplex[sp] = false;
|
|
1391
|
+
sp++;
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// ----- COMPLEX OPERATIONS (REAL, IMAG, CONJ, ARG) -----
|
|
1396
|
+
if (token == TOKEN_REAL) {
|
|
1397
|
+
if (sp < 1) continue;
|
|
1398
|
+
sp--; int a = sp;
|
|
1399
|
+
if (stack_isComplex[a]) {
|
|
1400
|
+
stack_type[sp] = 0;
|
|
1401
|
+
stack_data[sp][0] = stack_data[a][0];
|
|
1402
|
+
stack_isComplex[sp] = false;
|
|
1403
|
+
stack_rank[sp] = 0;
|
|
1404
|
+
stack_size[sp] = 1;
|
|
1405
|
+
} else {
|
|
1406
|
+
stack_type[sp] = stack_type[a];
|
|
1407
|
+
stack_rank[sp] = stack_rank[a];
|
|
1408
|
+
for (int d = 0; d < 4; ++d) stack_shape[sp][d] = stack_shape[a][d];
|
|
1409
|
+
stack_size[sp] = stack_size[a];
|
|
1410
|
+
stack_isComplex[sp] = false;
|
|
1411
|
+
}
|
|
1412
|
+
sp++;
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
if (token == TOKEN_IMAG) {
|
|
1416
|
+
if (sp < 1) continue;
|
|
1417
|
+
sp--; int a = sp;
|
|
1418
|
+
if (stack_isComplex[a]) {
|
|
1419
|
+
stack_type[sp] = 0;
|
|
1420
|
+
stack_data[sp][0] = stack_data[a][1];
|
|
1421
|
+
stack_isComplex[sp] = false;
|
|
1422
|
+
stack_rank[sp] = 0;
|
|
1423
|
+
stack_size[sp] = 1;
|
|
1424
|
+
} else {
|
|
1425
|
+
stack_type[sp] = 0;
|
|
1426
|
+
stack_data[sp][0] = 0.0;
|
|
1427
|
+
stack_isComplex[sp] = false;
|
|
1428
|
+
stack_rank[sp] = 0;
|
|
1429
|
+
stack_size[sp] = 1;
|
|
1430
|
+
}
|
|
1431
|
+
sp++;
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
if (token == TOKEN_CONJ) {
|
|
1435
|
+
if (sp < 1) continue;
|
|
1436
|
+
sp--; int a = sp;
|
|
1437
|
+
if (stack_isComplex[a]) {
|
|
1438
|
+
stack_type[sp] = 0;
|
|
1439
|
+
stack_data[sp][0] = stack_data[a][0];
|
|
1440
|
+
stack_data[sp][1] = -stack_data[a][1];
|
|
1441
|
+
stack_isComplex[sp] = true;
|
|
1442
|
+
stack_rank[sp] = 0;
|
|
1443
|
+
stack_size[sp] = 1;
|
|
1444
|
+
} else {
|
|
1445
|
+
stack_type[sp] = stack_type[a];
|
|
1446
|
+
stack_rank[sp] = stack_rank[a];
|
|
1447
|
+
for (int d = 0; d < 4; ++d) stack_shape[sp][d] = stack_shape[a][d];
|
|
1448
|
+
stack_size[sp] = stack_size[a];
|
|
1449
|
+
stack_isComplex[sp] = false;
|
|
1450
|
+
}
|
|
1451
|
+
sp++;
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
if (token == TOKEN_ARG) {
|
|
1455
|
+
if (sp < 1) continue;
|
|
1456
|
+
sp--; int a = sp;
|
|
1457
|
+
if (stack_isComplex[a]) {
|
|
1458
|
+
float arg = atan(stack_data[a][1], stack_data[a][0]);
|
|
1459
|
+
stack_type[sp] = 0;
|
|
1460
|
+
stack_data[sp][0] = arg;
|
|
1461
|
+
stack_isComplex[sp] = false;
|
|
1462
|
+
stack_rank[sp] = 0;
|
|
1463
|
+
stack_size[sp] = 1;
|
|
1464
|
+
} else {
|
|
1465
|
+
stack_type[sp] = 0;
|
|
1466
|
+
stack_data[sp][0] = (stack_data[a][0] >= 0.0) ? 0.0 : PI;
|
|
1467
|
+
stack_isComplex[sp] = false;
|
|
1468
|
+
stack_rank[sp] = 0;
|
|
1469
|
+
stack_size[sp] = 1;
|
|
1470
|
+
}
|
|
1471
|
+
sp++;
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// ----- MOD, MIN, MAX, ATAN2, CLAMP -----
|
|
1476
|
+
if (token == TOKEN_MOD) {
|
|
1477
|
+
if (sp < 2) continue;
|
|
1478
|
+
float bv = stack_data[--sp][0];
|
|
1479
|
+
float av = stack_data[--sp][0];
|
|
1480
|
+
stack_type[sp] = 0;
|
|
1481
|
+
stack_data[sp][0] = (abs(bv) < EPSILON) ? 0.0 : mod(av, bv);
|
|
1482
|
+
stack_isComplex[sp] = false;
|
|
1483
|
+
stack_rank[sp] = 0;
|
|
1484
|
+
stack_size[sp] = 1;
|
|
1485
|
+
sp++;
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
if (token == TOKEN_MIN || token == TOKEN_MAX) {
|
|
1489
|
+
if (sp < 2) continue;
|
|
1490
|
+
float bv = stack_data[--sp][0];
|
|
1491
|
+
float av = stack_data[--sp][0];
|
|
1492
|
+
stack_type[sp] = 0;
|
|
1493
|
+
stack_data[sp][0] = (token == TOKEN_MIN) ? min(av, bv) : max(av, bv);
|
|
1494
|
+
stack_isComplex[sp] = false;
|
|
1495
|
+
stack_rank[sp] = 0;
|
|
1496
|
+
stack_size[sp] = 1;
|
|
1497
|
+
sp++;
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
if (token == TOKEN_ATAN2) {
|
|
1501
|
+
if (sp < 2) continue;
|
|
1502
|
+
float xv = stack_data[--sp][0];
|
|
1503
|
+
float yv = stack_data[--sp][0];
|
|
1504
|
+
stack_type[sp] = 0;
|
|
1505
|
+
stack_data[sp][0] = atan(yv, xv);
|
|
1506
|
+
stack_isComplex[sp] = false;
|
|
1507
|
+
stack_rank[sp] = 0;
|
|
1508
|
+
stack_size[sp] = 1;
|
|
1509
|
+
sp++;
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
if (token == TOKEN_CLAMP) {
|
|
1513
|
+
if (sp < 3) continue;
|
|
1514
|
+
float maxv = stack_data[--sp][0];
|
|
1515
|
+
float minv = stack_data[--sp][0];
|
|
1516
|
+
float val = stack_data[--sp][0];
|
|
1517
|
+
stack_type[sp] = 0;
|
|
1518
|
+
stack_data[sp][0] = clamp(val, minv, maxv);
|
|
1519
|
+
stack_isComplex[sp] = false;
|
|
1520
|
+
stack_rank[sp] = 0;
|
|
1521
|
+
stack_size[sp] = 1;
|
|
1522
|
+
sp++;
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// Extract the final scalar result
|
|
1528
|
+
if (sp == 0) return 0.0;
|
|
1529
|
+
float result = 0.0;
|
|
1530
|
+
if (stack_isComplex[0]) result = stack_data[0][0];
|
|
1531
|
+
else if (stack_type[0] == 0) result = stack_data[0][0];
|
|
1532
|
+
else if (stack_size[0] > 0) result = stack_data[0][0];
|
|
1533
|
+
if (isInvalid(result)) {
|
|
1534
|
+
if (componentType == 2) return 0.0;
|
|
1535
|
+
if (componentType >= 3 && componentType <= 6) return 1.0;
|
|
1536
|
+
return 0.0;
|
|
1537
|
+
}
|
|
1538
|
+
if (componentType >= 3 && componentType <= 6) result = clamp(result, 0.0, 1.0);
|
|
1539
|
+
return result;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// ----------------------------------------------------------------------------
|
|
1543
|
+
// DEFAULT PHYSICS (fallback)
|
|
1544
|
+
// ----------------------------------------------------------------------------
|
|
1545
|
+
vec2 calculateDefaultPhysics() { return vec2(0.0); }
|
|
1546
|
+
|
|
1547
|
+
// ----------------------------------------------------------------------------
|
|
1548
|
+
// CONSTRAINT SOLVERS(too lazy to implement full LCP solver, so just do a few iterations of position/velocity projection)
|
|
1549
|
+
// ----------------------------------------------------------------------------
|
|
1550
|
+
void solveDistanceConstraint(inout vec2 pos, inout vec2 vel, Constraint c, int self) {
|
|
1551
|
+
if (c.targetObjectID < 0 || c.targetObjectID >= uNumObjects || c.targetObjectID == self) return;
|
|
1552
|
+
|
|
1553
|
+
Object target = objectsIn[c.targetObjectID];
|
|
1554
|
+
vec2 offset = pos - target.position;
|
|
1555
|
+
float dist = length(offset);
|
|
1556
|
+
if (dist < EPSILON) return;
|
|
1557
|
+
|
|
1558
|
+
vec2 normal = offset / dist;
|
|
1559
|
+
|
|
1560
|
+
// 1. Fix Position Drift
|
|
1561
|
+
vec2 desired = normal * c.param1;
|
|
1562
|
+
pos -= (offset - desired) * CONSTRAINT_STIFFNESS;
|
|
1563
|
+
|
|
1564
|
+
// 2. Project Velocity to Tangent (Conserves Momentum!)
|
|
1565
|
+
vec2 relVel = vel - target.velocity;
|
|
1566
|
+
vel -= dot(relVel, normal) * normal;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
void solveBoundaryConstraint(inout vec2 pos, inout vec2 vel, Constraint c) {
|
|
1570
|
+
float x1 = c.param1, x2 = c.param2, y1 = c.param3, y2 = c.param4;
|
|
1571
|
+
float minX = min(x1, x2), maxX = max(x1, x2);
|
|
1572
|
+
float minY = min(y1, y2), maxY = max(y1, y2);
|
|
1573
|
+
const float elasticity = 0.7, friction = 0.95;
|
|
1574
|
+
if (pos.x < minX) { pos.x = minX; vel.x = abs(vel.x) * elasticity; vel.y *= friction; }
|
|
1575
|
+
else if (pos.x > maxX) { pos.x = maxX; vel.x = -abs(vel.x) * elasticity; vel.y *= friction; }
|
|
1576
|
+
if (pos.y < minY) { pos.y = minY; vel.y = abs(vel.y) * elasticity; vel.x *= friction; }
|
|
1577
|
+
else if (pos.y > maxY) { pos.y = maxY; vel.y = -abs(vel.y) * elasticity; vel.x *= friction; }
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
//Too lazy to expose yet to API.
|
|
1581
|
+
void solveAngleConstraint(inout vec2 pos, inout vec2 vel, Constraint c, vec2 originalPos) {
|
|
1582
|
+
vec2 dir = pos - originalPos;
|
|
1583
|
+
float radius = length(dir);
|
|
1584
|
+
if (radius < EPSILON) return;
|
|
1585
|
+
float current = atan(dir.y, dir.x);
|
|
1586
|
+
current = mod(current + 2.0*PI, 2.0*PI);
|
|
1587
|
+
float minA = mod(c.param1 + 2.0*PI, 2.0*PI);
|
|
1588
|
+
float maxA = mod(c.param2 + 2.0*PI, 2.0*PI);
|
|
1589
|
+
bool outOfBounds = false;
|
|
1590
|
+
float corrected = current;
|
|
1591
|
+
if (minA <= maxA) {
|
|
1592
|
+
if (current < minA || current > maxA) {
|
|
1593
|
+
outOfBounds = true;
|
|
1594
|
+
corrected = (abs(current - minA) < abs(current - maxA)) ? minA : maxA;
|
|
1595
|
+
}
|
|
1596
|
+
} else {
|
|
1597
|
+
if (current < minA && current > maxA) {
|
|
1598
|
+
outOfBounds = true;
|
|
1599
|
+
corrected = (abs(current - minA) < abs(current - maxA)) ? minA : maxA;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
if (outOfBounds) {
|
|
1603
|
+
pos = originalPos + vec2(cos(corrected), sin(corrected)) * radius;
|
|
1604
|
+
vec2 radial = normalize(pos - originalPos);
|
|
1605
|
+
vec2 tangent = vec2(-radial.y, radial.x);
|
|
1606
|
+
vel = tangent * dot(vel, tangent) * 0.9;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
void applyConstraints(inout vec2 pos, inout vec2 vel, int self, vec2 originalPos) {
|
|
1610
|
+
ObjectConstraints pc = objectConstraints[self];
|
|
1611
|
+
if (pc.numConstraints <= 0) return;
|
|
1612
|
+
for (int iter = 0; iter < MAX_CONSTRAINT_ITERATIONS; ++iter) {
|
|
1613
|
+
for (int i = 0; i < pc.numConstraints; ++i) {
|
|
1614
|
+
Constraint c = constraints[pc.constraintOffset + i];
|
|
1615
|
+
if (c.type == CONSTRAINT_DISTANCE) solveDistanceConstraint(pos, vel, c, self);
|
|
1616
|
+
else if (c.type == CONSTRAINT_BOUNDARY) solveBoundaryConstraint(pos, vel, c);
|
|
1617
|
+
else if (c.type == CONSTRAINT_ANGLE) solveAngleConstraint(pos, vel, c, originalPos);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// ----------------------------------------------------------------------------
|
|
1623
|
+
// ROTATION HELPERS
|
|
1624
|
+
// ----------------------------------------------------------------------------
|
|
1625
|
+
vec2 rotatePoint(vec2 p, float a) {
|
|
1626
|
+
float ca = cos(a), sa = sin(a);
|
|
1627
|
+
return vec2(p.x*ca - p.y*sa, p.x*sa + p.y*ca);
|
|
1628
|
+
}
|
|
1629
|
+
vec2 worldToLocal(vec2 wp, vec2 op, float r) {
|
|
1630
|
+
return rotatePoint(wp - op, -r);
|
|
1631
|
+
}
|
|
1632
|
+
vec2 localToWorld(vec2 lp, vec2 op, float r) {
|
|
1633
|
+
return op + rotatePoint(lp, r);
|
|
1634
|
+
}
|
|
1635
|
+
vec2 getSupportPoint(vec2 pos, vec2 halfExt, float rot, vec2 dir) {
|
|
1636
|
+
float cr = cos(rot), sr = sin(rot);
|
|
1637
|
+
vec2 localDir = vec2(dot(dir, vec2(cr, sr)), dot(dir, vec2(-sr, cr)));
|
|
1638
|
+
vec2 localSupport = vec2(sign(localDir.x) * halfExt.x, sign(localDir.y) * halfExt.y);
|
|
1639
|
+
return pos + vec2(localSupport.x*cr - localSupport.y*sr, localSupport.x*sr + localSupport.y*cr);
|
|
1640
|
+
}
|
|
1641
|
+
float getMomentOfInertia(float mass, int shapeType, float w, float h, float r) {
|
|
1642
|
+
if (shapeType == COLLISION_CIRCLE) return 0.5 * mass * r * r;
|
|
1643
|
+
if (shapeType == COLLISION_AABB) return (1.0/12.0) * mass * (w*w + h*h);
|
|
1644
|
+
return 0.5 * mass * r * r;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// ----------------------------------------------------------------------------
|
|
1648
|
+
// COLLISION DETECTION (no broad phase – works correctly for all shapes)
|
|
1649
|
+
// ----------------------------------------------------------------------------
|
|
1650
|
+
// Normal ass collision system, nothing special
|
|
1651
|
+
CollisionInfo detectCircleCircle(vec2 pa, float ra, vec2 pb, float rb, int ob) {
|
|
1652
|
+
CollisionInfo info;
|
|
1653
|
+
info.hasCollision = false;
|
|
1654
|
+
info.otherObjectID = ob;
|
|
1655
|
+
vec2 d = pb - pa;
|
|
1656
|
+
float dsq = dot(d, d);
|
|
1657
|
+
float rs = ra + rb;
|
|
1658
|
+
if (dsq < rs*rs && dsq > EPSILON) {
|
|
1659
|
+
float dist = sqrt(dsq);
|
|
1660
|
+
info.hasCollision = true;
|
|
1661
|
+
info.normal = d / dist;
|
|
1662
|
+
info.penetration = rs - dist;
|
|
1663
|
+
info.contactPoint = pa + d * (ra / rs);
|
|
1664
|
+
}
|
|
1665
|
+
return info;
|
|
1666
|
+
}
|
|
1667
|
+
CollisionInfo detectAABBAABB(vec2 pa, vec2 ha, float ra, vec2 pb, vec2 hb, float rb, int ob) {
|
|
1668
|
+
CollisionInfo info;
|
|
1669
|
+
info.hasCollision = false;
|
|
1670
|
+
info.otherObjectID = ob;
|
|
1671
|
+
info.penetration = 1e10;
|
|
1672
|
+
vec2 axes[4];
|
|
1673
|
+
axes[0] = vec2(cos(ra), sin(ra));
|
|
1674
|
+
axes[1] = vec2(-sin(ra), cos(ra));
|
|
1675
|
+
axes[2] = vec2(cos(rb), sin(rb));
|
|
1676
|
+
axes[3] = vec2(-sin(rb), cos(rb));
|
|
1677
|
+
vec2 d = pb - pa;
|
|
1678
|
+
for (int i = 0; i < 4; ++i) {
|
|
1679
|
+
vec2 axis = axes[i];
|
|
1680
|
+
float projA_rad = abs(dot(axes[0], axis)) * ha.x + abs(dot(axes[1], axis)) * ha.y;
|
|
1681
|
+
float projB_rad = abs(dot(axes[2], axis)) * hb.x + abs(dot(axes[3], axis)) * hb.y;
|
|
1682
|
+
float projA_center = 0.0;
|
|
1683
|
+
float projB_center = dot(d, axis);
|
|
1684
|
+
float sep = abs(projB_center - projA_center) - (projA_rad + projB_rad);
|
|
1685
|
+
if (sep > 1e-4) return info;
|
|
1686
|
+
float overlap = -sep;
|
|
1687
|
+
if (overlap < info.penetration) {
|
|
1688
|
+
info.penetration = overlap;
|
|
1689
|
+
info.normal = axis;
|
|
1690
|
+
if (dot(d, axis) < 0.0) info.normal = -axis;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
info.hasCollision = true;
|
|
1694
|
+
info.contactPoint = getSupportPoint(pa, ha, ra, info.normal);
|
|
1695
|
+
return info;
|
|
1696
|
+
}
|
|
1697
|
+
vec2 projectPolygon(vec2 center, float radius, int sides, float rot, vec2 axis) {
|
|
1698
|
+
float minP = 1e10, maxP = -1e10;
|
|
1699
|
+
float step = 2.0 * PI / float(sides);
|
|
1700
|
+
for (int i = 0; i < sides; ++i) {
|
|
1701
|
+
float a = rot + float(i) * step;
|
|
1702
|
+
vec2 v = center + radius * vec2(cos(a), sin(a));
|
|
1703
|
+
float p = dot(v, axis);
|
|
1704
|
+
minP = min(minP, p);
|
|
1705
|
+
maxP = max(maxP, p);
|
|
1706
|
+
}
|
|
1707
|
+
return vec2(minP, maxP);
|
|
1708
|
+
}
|
|
1709
|
+
vec2 getPolygonNormal(int side, int total, float rot) {
|
|
1710
|
+
float step = 2.0 * PI / float(total);
|
|
1711
|
+
float a = rot + float(side) * step;
|
|
1712
|
+
vec2 edge = vec2(cos(a + step), sin(a + step)) - vec2(cos(a), sin(a));
|
|
1713
|
+
return normalize(vec2(-edge.y, edge.x));
|
|
1714
|
+
}
|
|
1715
|
+
CollisionInfo detectPolygonPolygon(vec2 pa, float ra, int sa, float rta, vec2 pb, float rb, int sb, float rtb, int ob) {
|
|
1716
|
+
CollisionInfo info;
|
|
1717
|
+
info.hasCollision = false;
|
|
1718
|
+
info.otherObjectID = ob;
|
|
1719
|
+
info.penetration = 1e10;
|
|
1720
|
+
int total = sa + sb;
|
|
1721
|
+
for (int i = 0; i < total; ++i) {
|
|
1722
|
+
vec2 axis = (i < sa) ? getPolygonNormal(i, sa, rta) : getPolygonNormal(i - sa, sb, rtb);
|
|
1723
|
+
vec2 projA = projectPolygon(pa, ra, sa, rta, axis);
|
|
1724
|
+
vec2 projB = projectPolygon(pb, rb, sb, rtb, axis);
|
|
1725
|
+
if (projA.y < projB.x || projB.y < projA.x) return info;
|
|
1726
|
+
float overlap = min(projA.y, projB.y) - max(projA.x, projB.x);
|
|
1727
|
+
if (overlap < info.penetration) {
|
|
1728
|
+
info.penetration = overlap;
|
|
1729
|
+
info.normal = axis;
|
|
1730
|
+
if (dot(pb - pa, axis) < 0.0) info.normal = -axis;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
info.hasCollision = true;
|
|
1734
|
+
return info;
|
|
1735
|
+
}
|
|
1736
|
+
CollisionInfo detectCircleAABB(vec2 cp, float cr, vec2 bp, vec2 he, float brot, int ob) {
|
|
1737
|
+
CollisionInfo info;
|
|
1738
|
+
info.hasCollision = false;
|
|
1739
|
+
info.otherObjectID = ob;
|
|
1740
|
+
vec2 local = worldToLocal(cp, bp, brot);
|
|
1741
|
+
vec2 closest = clamp(local, -he, he);
|
|
1742
|
+
vec2 d = local - closest;
|
|
1743
|
+
float dsq = dot(d, d);
|
|
1744
|
+
if (dsq < cr*cr) {
|
|
1745
|
+
float dist = sqrt(dsq);
|
|
1746
|
+
info.hasCollision = true;
|
|
1747
|
+
if (dist > EPSILON) {
|
|
1748
|
+
vec2 localNormal = -(d / dist);
|
|
1749
|
+
info.normal = rotatePoint(localNormal, brot);
|
|
1750
|
+
info.penetration = cr - dist;
|
|
1751
|
+
info.contactPoint = cp - info.normal * (cr - info.penetration * 0.5);
|
|
1752
|
+
} else {
|
|
1753
|
+
vec2 toEdge = -local;
|
|
1754
|
+
vec2 absToEdge = abs(toEdge);
|
|
1755
|
+
vec2 edgeDist = he - absToEdge;
|
|
1756
|
+
vec2 localNormal;
|
|
1757
|
+
if (edgeDist.x < edgeDist.y) {
|
|
1758
|
+
localNormal = vec2(sign(toEdge.x), 0.0);
|
|
1759
|
+
info.penetration = cr + edgeDist.x;
|
|
1760
|
+
} else {
|
|
1761
|
+
localNormal = vec2(0.0, sign(toEdge.y));
|
|
1762
|
+
info.penetration = cr + edgeDist.y;
|
|
1763
|
+
}
|
|
1764
|
+
info.normal = rotatePoint(localNormal, brot);
|
|
1765
|
+
info.contactPoint = cp - info.normal * cr;
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
return info;
|
|
1769
|
+
}
|
|
1770
|
+
CollisionInfo detectCirclePolygon(vec2 cp, float cr, vec2 pp, float pr, int ps, float prot, int ob) {
|
|
1771
|
+
CollisionInfo info;
|
|
1772
|
+
info.hasCollision = false;
|
|
1773
|
+
info.otherObjectID = ob;
|
|
1774
|
+
info.penetration = 1e10;
|
|
1775
|
+
float step = 2.0 * PI / float(ps);
|
|
1776
|
+
for (int i = 0; i < ps; ++i) {
|
|
1777
|
+
vec2 axis = getPolygonNormal(i, ps, prot);
|
|
1778
|
+
float cproj = dot(cp, axis);
|
|
1779
|
+
vec2 crange = vec2(cproj - cr, cproj + cr);
|
|
1780
|
+
vec2 prange = projectPolygon(pp, pr, ps, prot, axis);
|
|
1781
|
+
if (crange.y < prange.x || prange.y < crange.x) return info;
|
|
1782
|
+
float overlap = min(crange.y, prange.y) - max(crange.x, prange.x);
|
|
1783
|
+
if (overlap < info.penetration) {
|
|
1784
|
+
info.penetration = overlap;
|
|
1785
|
+
info.normal = axis;
|
|
1786
|
+
if (dot(pp - cp, axis) < 0.0) info.normal = -axis;
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
vec2 closest = pp;
|
|
1790
|
+
float closestDsq = 1e10;
|
|
1791
|
+
for (int i = 0; i < ps; ++i) {
|
|
1792
|
+
float a = prot + float(i) * step;
|
|
1793
|
+
vec2 v = pp + pr * vec2(cos(a), sin(a));
|
|
1794
|
+
float dsq = dot(v - cp, v - cp);
|
|
1795
|
+
if (dsq < closestDsq) { closestDsq = dsq; closest = v; }
|
|
1796
|
+
}
|
|
1797
|
+
vec2 axis = normalize(cp - closest);
|
|
1798
|
+
float cproj = dot(cp, axis);
|
|
1799
|
+
vec2 crange = vec2(cproj - cr, cproj + cr);
|
|
1800
|
+
vec2 prange = projectPolygon(pp, pr, ps, prot, axis);
|
|
1801
|
+
if (!(crange.y < prange.x || prange.y < crange.x)) {
|
|
1802
|
+
float overlap = min(crange.y, prange.y) - max(crange.x, prange.x);
|
|
1803
|
+
if (overlap < info.penetration) {
|
|
1804
|
+
info.penetration = overlap;
|
|
1805
|
+
info.normal = axis;
|
|
1806
|
+
if (dot(pp - cp, axis) < 0.0) info.normal = -axis;
|
|
1807
|
+
}
|
|
1808
|
+
info.hasCollision = true;
|
|
1809
|
+
}
|
|
1810
|
+
if (info.hasCollision) info.contactPoint = cp - info.normal * cr;
|
|
1811
|
+
return info;
|
|
1812
|
+
}
|
|
1813
|
+
CollisionInfo detectPolygonAABB(vec2 pp, float pr, int ps, float prot, vec2 bp, vec2 he, float brot, int ob) {
|
|
1814
|
+
CollisionInfo info;
|
|
1815
|
+
info.hasCollision = false;
|
|
1816
|
+
info.otherObjectID = ob;
|
|
1817
|
+
info.penetration = 1e10;
|
|
1818
|
+
vec2 verts[32];
|
|
1819
|
+
float step = 2.0 * PI / float(ps);
|
|
1820
|
+
for (int i = 0; i < ps && i < 32; ++i) {
|
|
1821
|
+
float a = prot + float(i) * step;
|
|
1822
|
+
verts[i] = pp + pr * vec2(cos(a), sin(a));
|
|
1823
|
+
}
|
|
1824
|
+
vec2 bax[2];
|
|
1825
|
+
bax[0] = vec2(cos(brot), sin(brot));
|
|
1826
|
+
bax[1] = vec2(-sin(brot), cos(brot));
|
|
1827
|
+
vec2 bc[4] = vec2[](
|
|
1828
|
+
vec2(-he.x, -he.y), vec2( he.x, -he.y),
|
|
1829
|
+
vec2( he.x, he.y), vec2(-he.x, he.y)
|
|
1830
|
+
);
|
|
1831
|
+
vec2 bv[4];
|
|
1832
|
+
for (int i = 0; i < 4; ++i) bv[i] = bp + bc[i].x * bax[0] + bc[i].y * bax[1];
|
|
1833
|
+
vec2 axes[34];
|
|
1834
|
+
int ac = 0;
|
|
1835
|
+
for (int i = 0; i < ps; ++i) {
|
|
1836
|
+
int n = (i + 1) % ps;
|
|
1837
|
+
vec2 e = verts[n] - verts[i];
|
|
1838
|
+
axes[ac++] = normalize(vec2(-e.y, e.x));
|
|
1839
|
+
}
|
|
1840
|
+
axes[ac++] = bax[0];
|
|
1841
|
+
axes[ac++] = bax[1];
|
|
1842
|
+
for (int i = 0; i < ac; ++i) {
|
|
1843
|
+
vec2 axis = axes[i];
|
|
1844
|
+
float pmin = dot(verts[0], axis), pmax = pmin;
|
|
1845
|
+
for (int j = 1; j < ps; ++j) {
|
|
1846
|
+
float p = dot(verts[j], axis);
|
|
1847
|
+
pmin = min(pmin, p);
|
|
1848
|
+
pmax = max(pmax, p);
|
|
1849
|
+
}
|
|
1850
|
+
float bmin = dot(bv[0], axis), bmax = bmin;
|
|
1851
|
+
for (int j = 1; j < 4; ++j) {
|
|
1852
|
+
float p = dot(bv[j], axis);
|
|
1853
|
+
bmin = min(bmin, p);
|
|
1854
|
+
bmax = max(bmax, p);
|
|
1855
|
+
}
|
|
1856
|
+
if (pmax < bmin - 1e-4 || bmax < pmin - 1e-4) return info;
|
|
1857
|
+
float overlap = min(pmax, bmax) - max(pmin, bmin);
|
|
1858
|
+
if (overlap < info.penetration) {
|
|
1859
|
+
info.penetration = overlap;
|
|
1860
|
+
info.normal = axis;
|
|
1861
|
+
vec2 centerDiff = bp - pp;
|
|
1862
|
+
if (dot(centerDiff, axis) < 0.0) info.normal = -axis;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
info.hasCollision = true;
|
|
1866
|
+
info.contactPoint = (pp + bp) * 0.5;
|
|
1867
|
+
return info;
|
|
1868
|
+
}
|
|
1869
|
+
CollisionInfo detectCollision(Object a, Object b, int ib) {
|
|
1870
|
+
CollisionInfo info;
|
|
1871
|
+
info.hasCollision = false;
|
|
1872
|
+
CollisionProperties pa = collisionProps[gl_GlobalInvocationID.x];
|
|
1873
|
+
CollisionProperties pb = collisionProps[ib];
|
|
1874
|
+
if (pa.enabled == 0 || pb.enabled == 0) return info;
|
|
1875
|
+
int sa = pa.shapeType, sb = pb.shapeType;
|
|
1876
|
+
if (sa == COLLISION_NONE || sb == COLLISION_NONE) return info;
|
|
1877
|
+
if (sa == COLLISION_CIRCLE && sb == COLLISION_CIRCLE)
|
|
1878
|
+
return detectCircleCircle(a.position, a.visualData.x, b.position, b.visualData.x, ib);
|
|
1879
|
+
if (sa == COLLISION_AABB && sb == COLLISION_AABB) {
|
|
1880
|
+
vec2 ha = a.visualData.xy * 0.5, hb = b.visualData.xy * 0.5;
|
|
1881
|
+
return detectAABBAABB(a.position, ha, a.visualData.z, b.position, hb, b.visualData.z, ib);
|
|
1882
|
+
}
|
|
1883
|
+
if (sa == COLLISION_POLYGON && sb == COLLISION_POLYGON)
|
|
1884
|
+
return detectPolygonPolygon(a.position, a.visualData.x, int(a.visualData.y), a.visualData.z,
|
|
1885
|
+
b.position, b.visualData.x, int(b.visualData.y), b.visualData.z, ib);
|
|
1886
|
+
if (sa == COLLISION_CIRCLE && sb == COLLISION_AABB) {
|
|
1887
|
+
vec2 hb = b.visualData.xy * 0.5;
|
|
1888
|
+
return detectCircleAABB(a.position, a.visualData.x, b.position, hb, b.visualData.z, ib);
|
|
1889
|
+
}
|
|
1890
|
+
if (sa == COLLISION_AABB && sb == COLLISION_CIRCLE) {
|
|
1891
|
+
vec2 ha = a.visualData.xy * 0.5;
|
|
1892
|
+
CollisionInfo ci = detectCircleAABB(b.position, b.visualData.x, a.position, ha, a.visualData.z, ib);
|
|
1893
|
+
ci.normal = -ci.normal;
|
|
1894
|
+
return ci;
|
|
1895
|
+
}
|
|
1896
|
+
if (sa == COLLISION_CIRCLE && sb == COLLISION_POLYGON)
|
|
1897
|
+
return detectCirclePolygon(a.position, a.visualData.x, b.position, b.visualData.x, int(b.visualData.y), b.visualData.z, ib);
|
|
1898
|
+
if (sa == COLLISION_POLYGON && sb == COLLISION_CIRCLE) {
|
|
1899
|
+
CollisionInfo ci = detectCirclePolygon(b.position, b.visualData.x, a.position, a.visualData.x, int(a.visualData.y), a.visualData.z, ib);
|
|
1900
|
+
ci.normal = -ci.normal;
|
|
1901
|
+
return ci;
|
|
1902
|
+
}
|
|
1903
|
+
if (sa == COLLISION_POLYGON && sb == COLLISION_AABB) {
|
|
1904
|
+
vec2 hb = b.visualData.xy * 0.5;
|
|
1905
|
+
return detectPolygonAABB(a.position, a.visualData.x, int(a.visualData.y), a.visualData.z,
|
|
1906
|
+
b.position, hb, b.visualData.z, ib);
|
|
1907
|
+
}
|
|
1908
|
+
if (sa == COLLISION_AABB && sb == COLLISION_POLYGON) {
|
|
1909
|
+
vec2 ha = a.visualData.xy * 0.5;
|
|
1910
|
+
CollisionInfo ci = detectPolygonAABB(b.position, b.visualData.x, int(b.visualData.y), b.visualData.z,
|
|
1911
|
+
a.position, ha, a.visualData.z, ib);
|
|
1912
|
+
ci.normal = -ci.normal;
|
|
1913
|
+
return ci;
|
|
1914
|
+
}
|
|
1915
|
+
return info;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// ----------------------------------------------------------------------------
|
|
1919
|
+
// COLLISION RESPONSE (with torque)
|
|
1920
|
+
// ----------------------------------------------------------------------------
|
|
1921
|
+
void resolveCollisionWithTorque(inout vec2 pa, inout vec2 va, inout float wa, float ma, float ia,
|
|
1922
|
+
inout vec2 pb, inout vec2 vb, inout float wb, float mb, float ib,
|
|
1923
|
+
vec2 n, vec2 cp, float pen, float rest, float fric) {
|
|
1924
|
+
const float posCorr = 0.8, slop = 0.001;
|
|
1925
|
+
if (pen > slop) {
|
|
1926
|
+
float tot = ma + mb;
|
|
1927
|
+
float invTot = 1.0 / tot;
|
|
1928
|
+
vec2 corr = (pen - slop) * posCorr * n;
|
|
1929
|
+
pa -= corr * (mb * invTot);
|
|
1930
|
+
pb += corr * (ma * invTot);
|
|
1931
|
+
}
|
|
1932
|
+
vec2 ra = cp - pa;
|
|
1933
|
+
vec2 rb = cp - pb;
|
|
1934
|
+
vec2 vac = va + vec2(-wa * ra.y, wa * ra.x);
|
|
1935
|
+
vec2 vbc = vb + vec2(-wb * rb.y, wb * rb.x);
|
|
1936
|
+
vec2 rel = vbc - vac;
|
|
1937
|
+
float vn = dot(rel, n);
|
|
1938
|
+
if (vn > 0.0) return;
|
|
1939
|
+
float e = clamp(rest, 0.0, 1.0);
|
|
1940
|
+
float num = -(1.0 + e) * vn;
|
|
1941
|
+
float rap = dot(ra, n), rbp = dot(rb, n);
|
|
1942
|
+
float den = (1.0/ma) + (1.0/mb) + (rap*rap)/ia + (rbp*rbp)/ib;
|
|
1943
|
+
if (abs(den) < EPSILON) return;
|
|
1944
|
+
float j = num / den;
|
|
1945
|
+
vec2 imp = j * n;
|
|
1946
|
+
va -= imp / ma;
|
|
1947
|
+
vb += imp / mb;
|
|
1948
|
+
wa -= (ra.x * imp.y - ra.y * imp.x) / ia;
|
|
1949
|
+
wb += (rb.x * imp.y - rb.y * imp.x) / ib;
|
|
1950
|
+
if (fric > 0.0) {
|
|
1951
|
+
vac = va + vec2(-wa * ra.y, wa * ra.x);
|
|
1952
|
+
vbc = vb + vec2(-wb * rb.y, wb * rb.x);
|
|
1953
|
+
rel = vbc - vac;
|
|
1954
|
+
vec2 tan = rel - n * dot(rel, n);
|
|
1955
|
+
float tl = length(tan);
|
|
1956
|
+
if (tl > EPSILON) {
|
|
1957
|
+
tan /= tl;
|
|
1958
|
+
float vt = dot(rel, tan);
|
|
1959
|
+
float jt = -vt * fric;
|
|
1960
|
+
float dent = (1.0/ma) + (1.0/mb) + (rap*rap)/ia + (rbp*rbp)/ib;
|
|
1961
|
+
if (abs(dent) > EPSILON) {
|
|
1962
|
+
jt /= dent;
|
|
1963
|
+
float maxJt = abs(j) * fric;
|
|
1964
|
+
jt = clamp(jt, -maxJt, maxJt);
|
|
1965
|
+
vec2 fimp = jt * tan;
|
|
1966
|
+
va -= fimp / ma;
|
|
1967
|
+
vb += fimp / mb;
|
|
1968
|
+
wa -= (ra.x * fimp.y - ra.y * fimp.x) / ia;
|
|
1969
|
+
wb += (rb.x * fimp.y - rb.y * fimp.x) / ib;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
// ----------------------------------------------------------------------------
|
|
1976
|
+
// CONTACT PERSISTENCE (warm starting)
|
|
1977
|
+
// ----------------------------------------------------------------------------
|
|
1978
|
+
int findPersistentContact(int a, int b, vec2 n) {
|
|
1979
|
+
int base = a * MAX_CONTACTS_PER_OBJECT;
|
|
1980
|
+
for (int i = 0; i < MAX_CONTACTS_PER_OBJECT; ++i) {
|
|
1981
|
+
int ci = base + i;
|
|
1982
|
+
if (ci >= contacts.length()) break;
|
|
1983
|
+
ContactPoint c = contacts[ci];
|
|
1984
|
+
if (c.frameCount > 0 && dot(c.normal, n) > 0.9) return ci;
|
|
1985
|
+
}
|
|
1986
|
+
return -1;
|
|
1987
|
+
}
|
|
1988
|
+
void updateContactPoint(int a, int b, vec2 n, vec2 pos, float pen) {
|
|
1989
|
+
int base = a * MAX_CONTACTS_PER_OBJECT;
|
|
1990
|
+
int oldest = base, oldestFrames = 9999;
|
|
1991
|
+
for (int i = 0; i < MAX_CONTACTS_PER_OBJECT; ++i) {
|
|
1992
|
+
int ci = base + i;
|
|
1993
|
+
if (ci >= contacts.length()) break;
|
|
1994
|
+
ContactPoint c = contacts[ci];
|
|
1995
|
+
if (c.frameCount == 0 || dot(c.normal, n) > 0.9) {
|
|
1996
|
+
contacts[ci].normal = n;
|
|
1997
|
+
contacts[ci].position = pos;
|
|
1998
|
+
contacts[ci].penetration = pen;
|
|
1999
|
+
contacts[ci].frameCount = min(c.frameCount + 1, MAX_CONTACT_FRAMES);
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
if (c.frameCount < oldestFrames) {
|
|
2003
|
+
oldestFrames = c.frameCount;
|
|
2004
|
+
oldest = ci;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
if (oldest >= 0 && oldest < contacts.length()) {
|
|
2008
|
+
contacts[oldest].normal = n;
|
|
2009
|
+
contacts[oldest].position = pos;
|
|
2010
|
+
contacts[oldest].penetration = pen;
|
|
2011
|
+
contacts[oldest].frameCount = 1;
|
|
2012
|
+
contacts[oldest].accumulatedNormalImpulse = 0.0;
|
|
2013
|
+
contacts[oldest].accumulatedTangentImpulse = 0.0;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
void ageContacts(int idx) {
|
|
2017
|
+
int base = idx * MAX_CONTACTS_PER_OBJECT;
|
|
2018
|
+
for (int i = 0; i < MAX_CONTACTS_PER_OBJECT; ++i) {
|
|
2019
|
+
int ci = base + i;
|
|
2020
|
+
if (ci >= contacts.length()) break;
|
|
2021
|
+
if (contacts[ci].frameCount > 0) {
|
|
2022
|
+
contacts[ci].frameCount--;
|
|
2023
|
+
if (contacts[ci].frameCount == 0) {
|
|
2024
|
+
contacts[ci].accumulatedNormalImpulse = 0.0;
|
|
2025
|
+
contacts[ci].accumulatedTangentImpulse = 0.0;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// ----------------------------------------------------------------------------
|
|
2032
|
+
// MAIN ENTRY POINT
|
|
2033
|
+
// ----------------------------------------------------------------------------
|
|
2034
|
+
//It all comes together here. This is where we calculate the new position, velocity, and color of each object based on the physics and equations defined above.
|
|
2035
|
+
void main() {
|
|
2036
|
+
uint gid = gl_GlobalInvocationID.x;
|
|
2037
|
+
if (int(gid) >= uNumObjects) return;
|
|
2038
|
+
|
|
2039
|
+
Object p = objectsIn[gid];
|
|
2040
|
+
vec2 pos = p.position;
|
|
2041
|
+
vec2 vel = p.velocity;
|
|
2042
|
+
float mass = max(EPSILON, p.mass);
|
|
2043
|
+
float charge = p.charge;
|
|
2044
|
+
float theta = p.visualData.z;
|
|
2045
|
+
float omega = p.visualData.w;
|
|
2046
|
+
vec4 color = p.color;
|
|
2047
|
+
int self = int(gid);
|
|
2048
|
+
vec2 originalPos = pos;
|
|
2049
|
+
|
|
2050
|
+
vec2 acc = vec2(0.0);
|
|
2051
|
+
float angAcc = 0.0;
|
|
2052
|
+
vec4 newColor = color;
|
|
2053
|
+
|
|
2054
|
+
if (uEquationMode == 0) {
|
|
2055
|
+
int eq = p.equationID;
|
|
2056
|
+
bool valid = (eq >= 0 && eq < MAX_EQUATION_COUNT) &&
|
|
2057
|
+
(mappings[eq].tokenCount_ax > 0 || mappings[eq].tokenCount_ay > 0 ||
|
|
2058
|
+
mappings[eq].tokenCount_angular > 0 || mappings[eq].tokenCount_r > 0 ||
|
|
2059
|
+
mappings[eq].tokenCount_g > 0 || mappings[eq].tokenCount_b > 0 ||
|
|
2060
|
+
mappings[eq].tokenCount_a > 0);
|
|
2061
|
+
if (!valid) {
|
|
2062
|
+
acc = calculateDefaultPhysics();
|
|
2063
|
+
} else {
|
|
2064
|
+
EquationMapping m = mappings[eq];
|
|
2065
|
+
float ax = 0.0, ay = 0.0;
|
|
2066
|
+
if (m.tokenCount_ax > 0) {
|
|
2067
|
+
ax = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
2068
|
+
theta, omega, color, mass, charge, self,
|
|
2069
|
+
0, m.tokenOffset_ax, m.tokenCount_ax, m.constantOffset_ax);
|
|
2070
|
+
}
|
|
2071
|
+
if (m.tokenCount_ay > 0) {
|
|
2072
|
+
ay = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
2073
|
+
theta, omega, color, mass, charge, self,
|
|
2074
|
+
1, m.tokenOffset_ay, m.tokenCount_ay, m.constantOffset_ay);
|
|
2075
|
+
}
|
|
2076
|
+
acc = sanitizeVec2(vec2(ax, ay));
|
|
2077
|
+
if (m.tokenCount_angular > 0) {
|
|
2078
|
+
angAcc = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
2079
|
+
theta, omega, color, mass, charge, self,
|
|
2080
|
+
2, m.tokenOffset_angular, m.tokenCount_angular, m.constantOffset_angular);
|
|
2081
|
+
angAcc = isInvalid(angAcc) ? 0.0 : angAcc;
|
|
2082
|
+
}
|
|
2083
|
+
if (m.tokenCount_r > 0)
|
|
2084
|
+
newColor.r = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
2085
|
+
theta, omega, color, mass, charge, self,
|
|
2086
|
+
3, m.tokenOffset_r, m.tokenCount_r, m.constantOffset_r);
|
|
2087
|
+
if (m.tokenCount_g > 0)
|
|
2088
|
+
newColor.g = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
2089
|
+
theta, omega, color, mass, charge, self,
|
|
2090
|
+
4, m.tokenOffset_g, m.tokenCount_g, m.constantOffset_g);
|
|
2091
|
+
if (m.tokenCount_b > 0)
|
|
2092
|
+
newColor.b = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
2093
|
+
theta, omega, color, mass, charge, self,
|
|
2094
|
+
5, m.tokenOffset_b, m.tokenCount_b, m.constantOffset_b);
|
|
2095
|
+
if (m.tokenCount_a > 0)
|
|
2096
|
+
newColor.a = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
2097
|
+
theta, omega, color, mass, charge, self,
|
|
2098
|
+
6, m.tokenOffset_a, m.tokenCount_a, m.constantOffset_a);
|
|
2099
|
+
newColor = sanitizeVec4(newColor);
|
|
2100
|
+
}
|
|
2101
|
+
} else {
|
|
2102
|
+
acc = calculateDefaultPhysics();
|
|
2103
|
+
}
|
|
2104
|
+
acc = sanitizeVec2(acc);
|
|
2105
|
+
|
|
2106
|
+
// Symplectic Euler integration
|
|
2107
|
+
vec2 newVel = vel + acc * uDt;
|
|
2108
|
+
newVel = sanitizeVec2(newVel);
|
|
2109
|
+
vec2 newPos = pos + newVel * uDt;
|
|
2110
|
+
newPos = sanitizeVec2(newPos);
|
|
2111
|
+
float newOmega = omega + angAcc * uDt;
|
|
2112
|
+
float newTheta = theta + newOmega * uDt;
|
|
2113
|
+
newTheta = mod(newTheta, 2.0 * PI);
|
|
2114
|
+
if (newTheta < 0.0) newTheta += 2.0 * PI;
|
|
2115
|
+
|
|
2116
|
+
applyConstraints(newPos, newVel, self, originalPos);
|
|
2117
|
+
|
|
2118
|
+
// Collision detection (narrow phase only – accurate for all shapes)
|
|
2119
|
+
bool hadCollision = false;
|
|
2120
|
+
for (int i = 0; i < uNumObjects; ++i) {
|
|
2121
|
+
if (i == self) continue;
|
|
2122
|
+
Object other = objectsIn[i];
|
|
2123
|
+
CollisionProperties propA = collisionProps[self];
|
|
2124
|
+
CollisionProperties propB = collisionProps[i];
|
|
2125
|
+
if (propA.enabled == 0 || propB.enabled == 0) continue;
|
|
2126
|
+
if (propA.shapeType == COLLISION_NONE || propB.shapeType == COLLISION_NONE) continue;
|
|
2127
|
+
|
|
2128
|
+
CollisionInfo col = detectCollision(p, other, i);
|
|
2129
|
+
if (col.hasCollision) {
|
|
2130
|
+
hadCollision = true;
|
|
2131
|
+
float rest = min(propA.restitution, propB.restitution);
|
|
2132
|
+
float fric = sqrt(propA.friction * propB.friction);
|
|
2133
|
+
float mA = mass, mB = max(EPSILON, other.mass);
|
|
2134
|
+
float wA = newOmega, wB = other.visualData.w;
|
|
2135
|
+
float iA = getMomentOfInertia(mA, propA.shapeType, p.visualData.x, p.visualData.y, p.visualData.x * 0.5);
|
|
2136
|
+
float iB = getMomentOfInertia(mB, propB.shapeType, other.visualData.x, other.visualData.y, other.visualData.x * 0.5);
|
|
2137
|
+
vec2 tempPos = other.position;
|
|
2138
|
+
vec2 tempVel = other.velocity;
|
|
2139
|
+
float tempOmega = wB;
|
|
2140
|
+
resolveCollisionWithTorque(newPos, newVel, newOmega, mA, iA,
|
|
2141
|
+
tempPos, tempVel, tempOmega, mB, iB,
|
|
2142
|
+
col.normal, col.contactPoint, col.penetration,
|
|
2143
|
+
rest, fric);
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
if (hadCollision) {
|
|
2147
|
+
newVel = sanitizeVec2(newVel);
|
|
2148
|
+
newOmega = clamp(newOmega, -100.0, 100.0);
|
|
2149
|
+
}
|
|
2150
|
+
if (uEnableWarmStart == 1) ageContacts(self);
|
|
2151
|
+
|
|
2152
|
+
newPos = sanitizeVec2(newPos);
|
|
2153
|
+
newVel = sanitizeVec2(newVel);
|
|
2154
|
+
float speed = length(newVel);
|
|
2155
|
+
if (speed > 1000.0) newVel = normalize(newVel) * 1000.0;
|
|
2156
|
+
|
|
2157
|
+
// Write output to buffer
|
|
2158
|
+
objectsOut[self].position = newPos;
|
|
2159
|
+
objectsOut[self].velocity = newVel;
|
|
2160
|
+
objectsOut[self].mass = mass;
|
|
2161
|
+
objectsOut[self].charge = charge;
|
|
2162
|
+
objectsOut[self].visualSkinType = p.visualSkinType;
|
|
2163
|
+
objectsOut[self].collisionShapeType = p.collisionShapeType;
|
|
2164
|
+
objectsOut[self].visualData.x = p.visualData.x;
|
|
2165
|
+
objectsOut[self].visualData.y = p.visualData.y;
|
|
2166
|
+
objectsOut[self].visualData.z = newTheta;
|
|
2167
|
+
objectsOut[self].visualData.w = newOmega;
|
|
2168
|
+
objectsOut[self].collisionData.x = acc.x;
|
|
2169
|
+
objectsOut[self].collisionData.y = acc.y;
|
|
2170
|
+
objectsOut[self].collisionData.z = p.collisionData.z;
|
|
2171
|
+
objectsOut[self].collisionData.w = p.collisionData.w;
|
|
2172
|
+
objectsOut[self].color = newColor;
|
|
2173
|
+
objectsOut[self].equationID = p.equationID;
|
|
2174
|
+
objectsOut[self]._pad1 = 0;
|
|
2175
|
+
objectsOut[self]._padEnd[0] = 0;
|
|
2176
|
+
objectsOut[self]._padEnd[1] = 0;
|
|
2177
|
+
}
|