hyperstellar 0.1.63__tar.gz → 0.1.64__tar.gz
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-0.1.63 → hyperstellar-0.1.64}/PKG-INFO +1 -1
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/math.comp +102 -291
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/_native/windows-x64/stellar.pyd +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar.egg-info/PKG-INFO +1 -1
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/pyproject.toml +2 -1
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/LICENSE +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/README.md +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/__init__.py +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/__init__.pyi +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/quad.frag +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/quad.geom +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/quad.vert +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar/py.typed +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar.egg-info/SOURCES.txt +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar.egg-info/dependency_links.txt +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar.egg-info/top_level.txt +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/setup.cfg +0 -0
- {hyperstellar-0.1.63 → hyperstellar-0.1.64}/tests/test.py +0 -0
|
@@ -1110,7 +1110,7 @@ void applyConstraints(inout vec2 pos, inout vec2 vel, int objectIndex, vec2 orig
|
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
1112
1112
|
// ============================================================================
|
|
1113
|
-
// COLLISION DETECTION FUNCTIONS
|
|
1113
|
+
// COLLISION DETECTION FUNCTIONS
|
|
1114
1114
|
// ============================================================================
|
|
1115
1115
|
|
|
1116
1116
|
// Circle-Circle collision detection
|
|
@@ -1120,52 +1120,54 @@ CollisionInfo detectCircleCircle(vec2 posA, float radiusA, vec2 posB, float radi
|
|
|
1120
1120
|
info.hasCollision = false;
|
|
1121
1121
|
info.otherObjectID = objB;
|
|
1122
1122
|
|
|
1123
|
-
// Calculate distance between centers
|
|
1124
1123
|
vec2 delta = posB - posA;
|
|
1125
1124
|
float distSq = dot(delta, delta);
|
|
1126
1125
|
float radiusSum = radiusA + radiusB;
|
|
1127
1126
|
|
|
1128
|
-
// Check if circles overlap
|
|
1129
1127
|
if (distSq < radiusSum * radiusSum && distSq > EPSILON)
|
|
1130
1128
|
{
|
|
1131
1129
|
float dist = sqrt(distSq);
|
|
1132
1130
|
info.hasCollision = true;
|
|
1133
|
-
info.normal = delta / dist;
|
|
1134
|
-
info.penetration = radiusSum - dist;
|
|
1131
|
+
info.normal = delta / dist;
|
|
1132
|
+
info.penetration = radiusSum - dist;
|
|
1135
1133
|
}
|
|
1136
1134
|
|
|
1137
1135
|
return info;
|
|
1138
1136
|
}
|
|
1139
1137
|
|
|
1140
|
-
// Axis-Aligned Bounding Box collision detection
|
|
1141
|
-
|
|
1138
|
+
// Axis-Aligned Bounding Box collision detection with fix for "sticky" behavior.
|
|
1139
|
+
// The fix: Use old positions only for determining the sign of the normal,
|
|
1140
|
+
// while penetration depth still uses the current (predicted) positions.
|
|
1141
|
+
// This prevents the normal from flipping when A leaps past B in one timestep.
|
|
1142
|
+
CollisionInfo detectAABBAABB(vec2 posA, vec2 halfExtA, vec2 posB, vec2 halfExtB,
|
|
1143
|
+
vec2 oldPosA, vec2 oldPosB, int objB)
|
|
1142
1144
|
{
|
|
1143
1145
|
CollisionInfo info;
|
|
1144
1146
|
info.hasCollision = false;
|
|
1145
1147
|
info.otherObjectID = objB;
|
|
1146
|
-
|
|
1147
|
-
//
|
|
1148
|
-
vec2 delta = posB - posA;
|
|
1148
|
+
|
|
1149
|
+
vec2 delta = posB - posA; // current (possibly overshot)
|
|
1149
1150
|
vec2 overlap = halfExtA + halfExtB - abs(delta);
|
|
1150
|
-
|
|
1151
|
-
// Check for overlap on both axes
|
|
1151
|
+
|
|
1152
1152
|
if (overlap.x > 0.0 && overlap.y > 0.0)
|
|
1153
1153
|
{
|
|
1154
1154
|
info.hasCollision = true;
|
|
1155
|
-
|
|
1156
|
-
//
|
|
1155
|
+
|
|
1156
|
+
// Use the OLD relative position to decide the normal direction.
|
|
1157
|
+
// This preserves physical sign even after a large velocity causes overshoot.
|
|
1158
|
+
vec2 oldDelta = oldPosB - oldPosA;
|
|
1159
|
+
|
|
1157
1160
|
if (overlap.x < overlap.y)
|
|
1158
1161
|
{
|
|
1159
|
-
info.normal = vec2(sign(
|
|
1162
|
+
info.normal = vec2(sign(oldDelta.x), 0.0);
|
|
1160
1163
|
info.penetration = overlap.x;
|
|
1161
1164
|
}
|
|
1162
1165
|
else
|
|
1163
1166
|
{
|
|
1164
|
-
info.normal = vec2(0.0, sign(
|
|
1167
|
+
info.normal = vec2(0.0, sign(oldDelta.y));
|
|
1165
1168
|
info.penetration = overlap.y;
|
|
1166
1169
|
}
|
|
1167
1170
|
}
|
|
1168
|
-
|
|
1169
1171
|
return info;
|
|
1170
1172
|
}
|
|
1171
1173
|
|
|
@@ -1177,7 +1179,6 @@ vec2 projectPolygon(vec2 center, float radius, int sides, float rotation, vec2 a
|
|
|
1177
1179
|
|
|
1178
1180
|
float angleStep = 2.0 * PI / float(sides);
|
|
1179
1181
|
|
|
1180
|
-
// Project each vertex onto axis
|
|
1181
1182
|
for (int i = 0; i < sides; i++)
|
|
1182
1183
|
{
|
|
1183
1184
|
float angle = rotation + float(i) * angleStep;
|
|
@@ -1196,11 +1197,9 @@ vec2 getPolygonNormal(int side, int totalSides, float rotation)
|
|
|
1196
1197
|
float angleStep = 2.0 * PI / float(totalSides);
|
|
1197
1198
|
float angle = rotation + float(side) * angleStep;
|
|
1198
1199
|
|
|
1199
|
-
// Calculate edge vector
|
|
1200
1200
|
vec2 edge = vec2(cos(angle + angleStep), sin(angle + angleStep)) -
|
|
1201
1201
|
vec2(cos(angle), sin(angle));
|
|
1202
1202
|
|
|
1203
|
-
// Return perpendicular (normal) to edge
|
|
1204
1203
|
return normalize(vec2(-edge.y, edge.x));
|
|
1205
1204
|
}
|
|
1206
1205
|
|
|
@@ -1215,7 +1214,6 @@ CollisionInfo detectPolygonPolygon(
|
|
|
1215
1214
|
info.otherObjectID = objB;
|
|
1216
1215
|
info.penetration = 1e10;
|
|
1217
1216
|
|
|
1218
|
-
// Test all edge normals from both polygons
|
|
1219
1217
|
int totalTests = sidesA + sidesB;
|
|
1220
1218
|
|
|
1221
1219
|
for (int i = 0; i < totalTests; i++)
|
|
@@ -1227,24 +1225,18 @@ CollisionInfo detectPolygonPolygon(
|
|
|
1227
1225
|
else
|
|
1228
1226
|
axis = getPolygonNormal(i - sidesA, sidesB, rotB);
|
|
1229
1227
|
|
|
1230
|
-
// Project both polygons onto axis
|
|
1231
1228
|
vec2 projA = projectPolygon(posA, radiusA, sidesA, rotA, axis);
|
|
1232
1229
|
vec2 projB = projectPolygon(posB, radiusB, sidesB, rotB, axis);
|
|
1233
1230
|
|
|
1234
|
-
// Check for separating axis
|
|
1235
1231
|
if (projA.y < projB.x || projB.y < projA.x)
|
|
1236
|
-
return info;
|
|
1232
|
+
return info;
|
|
1237
1233
|
|
|
1238
|
-
// Calculate overlap
|
|
1239
1234
|
float overlap = min(projA.y, projB.y) - max(projA.x, projB.x);
|
|
1240
1235
|
|
|
1241
|
-
// Track minimum overlap (minimum translation vector)
|
|
1242
1236
|
if (overlap < info.penetration)
|
|
1243
1237
|
{
|
|
1244
1238
|
info.penetration = overlap;
|
|
1245
1239
|
info.normal = axis;
|
|
1246
|
-
|
|
1247
|
-
// Ensure normal points from A to B
|
|
1248
1240
|
if (dot(posB - posA, axis) < 0.0)
|
|
1249
1241
|
info.normal = -axis;
|
|
1250
1242
|
}
|
|
@@ -1262,7 +1254,6 @@ CollisionInfo detectCircleAABB(vec2 circlePos, float radius,
|
|
|
1262
1254
|
info.hasCollision = false;
|
|
1263
1255
|
info.otherObjectID = objB;
|
|
1264
1256
|
|
|
1265
|
-
// Find closest point on AABB to circle center
|
|
1266
1257
|
vec2 closest = clamp(circlePos, boxPos - halfExt, boxPos + halfExt);
|
|
1267
1258
|
vec2 delta = circlePos - closest;
|
|
1268
1259
|
float distSq = dot(delta, delta);
|
|
@@ -1274,13 +1265,11 @@ CollisionInfo detectCircleAABB(vec2 circlePos, float radius,
|
|
|
1274
1265
|
|
|
1275
1266
|
if (dist > EPSILON)
|
|
1276
1267
|
{
|
|
1277
|
-
// Circle center outside AABB
|
|
1278
1268
|
info.normal = -(delta / dist);
|
|
1279
1269
|
info.penetration = radius - dist;
|
|
1280
1270
|
}
|
|
1281
1271
|
else
|
|
1282
1272
|
{
|
|
1283
|
-
// Circle center inside AABB - push out nearest edge
|
|
1284
1273
|
vec2 toEdge = boxPos - circlePos;
|
|
1285
1274
|
vec2 absToEdge = abs(toEdge);
|
|
1286
1275
|
vec2 edgeDist = halfExt - absToEdge;
|
|
@@ -1311,30 +1300,24 @@ CollisionInfo detectCirclePolygon(vec2 circlePos, float circleRadius,
|
|
|
1311
1300
|
info.otherObjectID = objB;
|
|
1312
1301
|
info.penetration = 1e10;
|
|
1313
1302
|
|
|
1314
|
-
// Test polygon edge normals
|
|
1315
1303
|
float angleStep = 2.0 * PI / float(polySides);
|
|
1316
1304
|
|
|
1317
1305
|
for (int i = 0; i < polySides; i++)
|
|
1318
1306
|
{
|
|
1319
1307
|
vec2 axis = getPolygonNormal(i, polySides, polyRot);
|
|
1320
1308
|
|
|
1321
|
-
// Project circle onto axis (capsule projection)
|
|
1322
1309
|
float circleProj = dot(circlePos, axis);
|
|
1323
1310
|
vec2 circleProjRange = vec2(circleProj - circleRadius,
|
|
1324
1311
|
circleProj + circleRadius);
|
|
1325
1312
|
|
|
1326
|
-
// Project polygon onto axis
|
|
1327
1313
|
vec2 polyProj = projectPolygon(polyPos, polyRadius, polySides, polyRot, axis);
|
|
1328
1314
|
|
|
1329
|
-
// Check for separation
|
|
1330
1315
|
if (circleProjRange.y < polyProj.x || polyProj.y < circleProjRange.x)
|
|
1331
1316
|
return info;
|
|
1332
1317
|
|
|
1333
|
-
// Calculate overlap
|
|
1334
1318
|
float overlap = min(circleProjRange.y, polyProj.y) -
|
|
1335
1319
|
max(circleProjRange.x, polyProj.x);
|
|
1336
1320
|
|
|
1337
|
-
// Track minimum overlap
|
|
1338
1321
|
if (overlap < info.penetration)
|
|
1339
1322
|
{
|
|
1340
1323
|
info.penetration = overlap;
|
|
@@ -1344,11 +1327,9 @@ CollisionInfo detectCirclePolygon(vec2 circlePos, float circleRadius,
|
|
|
1344
1327
|
}
|
|
1345
1328
|
}
|
|
1346
1329
|
|
|
1347
|
-
// Test axis from circle center to closest polygon vertex
|
|
1348
1330
|
vec2 closestVertex = polyPos;
|
|
1349
1331
|
float closestDistSq = 1e10;
|
|
1350
1332
|
|
|
1351
|
-
// Find closest polygon vertex to circle center
|
|
1352
1333
|
for (int i = 0; i < polySides; i++)
|
|
1353
1334
|
{
|
|
1354
1335
|
float angle = polyRot + float(i) * angleStep;
|
|
@@ -1362,13 +1343,11 @@ CollisionInfo detectCirclePolygon(vec2 circlePos, float circleRadius,
|
|
|
1362
1343
|
}
|
|
1363
1344
|
}
|
|
1364
1345
|
|
|
1365
|
-
// Test axis from circle center to closest vertex
|
|
1366
1346
|
vec2 axis = normalize(circlePos - closestVertex);
|
|
1367
1347
|
float circleProj = dot(circlePos, axis);
|
|
1368
1348
|
vec2 circleProjRange = vec2(circleProj - circleRadius, circleProj + circleRadius);
|
|
1369
1349
|
vec2 polyProj = projectPolygon(polyPos, polyRadius, polySides, polyRot, axis);
|
|
1370
1350
|
|
|
1371
|
-
// Final separation check
|
|
1372
1351
|
if (circleProjRange.y < polyProj.x || polyProj.y < circleProjRange.x)
|
|
1373
1352
|
return info;
|
|
1374
1353
|
|
|
@@ -1387,43 +1366,38 @@ CollisionInfo detectCirclePolygon(vec2 circlePos, float circleRadius,
|
|
|
1387
1366
|
return info;
|
|
1388
1367
|
}
|
|
1389
1368
|
|
|
1390
|
-
// Master collision detection dispatcher
|
|
1391
|
-
CollisionInfo detectCollision(Object objA, Object objB, int objIndexB
|
|
1369
|
+
// Master collision detection dispatcher (now with old positions for AABB fix)
|
|
1370
|
+
CollisionInfo detectCollision(Object objA, Object objB, int objIndexB,
|
|
1371
|
+
vec2 oldPosA, vec2 oldPosB)
|
|
1392
1372
|
{
|
|
1393
1373
|
CollisionInfo info;
|
|
1394
1374
|
info.hasCollision = false;
|
|
1395
1375
|
|
|
1396
|
-
// Get collision properties for both objects
|
|
1397
1376
|
CollisionProperties propsA = collisionProps[gl_GlobalInvocationID.x];
|
|
1398
1377
|
CollisionProperties propsB = collisionProps[objIndexB];
|
|
1399
1378
|
|
|
1400
|
-
// Check if collisions are enabled for both objects
|
|
1401
1379
|
if (propsA.enabled == 0 || propsB.enabled == 0)
|
|
1402
1380
|
return info;
|
|
1403
1381
|
|
|
1404
1382
|
int shapeA = propsA.shapeType;
|
|
1405
1383
|
int shapeB = propsB.shapeType;
|
|
1406
1384
|
|
|
1407
|
-
// Skip if either object has no collision shape
|
|
1408
1385
|
if (shapeA == COLLISION_NONE || shapeB == COLLISION_NONE)
|
|
1409
1386
|
return info;
|
|
1410
1387
|
|
|
1411
|
-
// Dispatch to appropriate collision detection function based on shape types
|
|
1412
1388
|
if (shapeA == COLLISION_CIRCLE && shapeB == COLLISION_CIRCLE)
|
|
1413
1389
|
{
|
|
1414
|
-
// Circle vs Circle
|
|
1415
1390
|
info = detectCircleCircle(objA.position, objA.visualData.x,
|
|
1416
1391
|
objB.position, objB.visualData.x, objIndexB);
|
|
1417
1392
|
}
|
|
1418
|
-
// AABB vs AABB
|
|
1419
1393
|
else if (shapeA == COLLISION_AABB && shapeB == COLLISION_AABB)
|
|
1420
1394
|
{
|
|
1421
1395
|
vec2 halfExtA = objA.visualData.xy * 0.5;
|
|
1422
1396
|
vec2 halfExtB = objB.visualData.xy * 0.5;
|
|
1423
|
-
info = detectAABBAABB(objA.position, halfExtA,
|
|
1424
|
-
|
|
1397
|
+
info = detectAABBAABB(objA.position, halfExtA,
|
|
1398
|
+
objB.position, halfExtB,
|
|
1399
|
+
oldPosA, oldPosB, objIndexB);
|
|
1425
1400
|
}
|
|
1426
|
-
// Polygon vs Polygon
|
|
1427
1401
|
else if (shapeA == COLLISION_POLYGON && shapeB == COLLISION_POLYGON)
|
|
1428
1402
|
{
|
|
1429
1403
|
info = detectPolygonPolygon(
|
|
@@ -1431,7 +1405,6 @@ CollisionInfo detectCollision(Object objA, Object objB, int objIndexB)
|
|
|
1431
1405
|
objB.position, objB.visualData.x, int(objB.visualData.y), objB.visualData.z,
|
|
1432
1406
|
objIndexB);
|
|
1433
1407
|
}
|
|
1434
|
-
// Circle vs AABB
|
|
1435
1408
|
else if (shapeA == COLLISION_CIRCLE && shapeB == COLLISION_AABB)
|
|
1436
1409
|
{
|
|
1437
1410
|
vec2 halfExtB = objB.visualData.xy * 0.5;
|
|
@@ -1440,13 +1413,11 @@ CollisionInfo detectCollision(Object objA, Object objB, int objIndexB)
|
|
|
1440
1413
|
}
|
|
1441
1414
|
else if (shapeA == COLLISION_AABB && shapeB == COLLISION_CIRCLE)
|
|
1442
1415
|
{
|
|
1443
|
-
// Swap roles and flip normal
|
|
1444
1416
|
vec2 halfExtA = objA.visualData.xy * 0.5;
|
|
1445
1417
|
info = detectCircleAABB(objB.position, objB.visualData.x,
|
|
1446
1418
|
objA.position, halfExtA, objIndexB);
|
|
1447
|
-
info.normal = -info.normal;
|
|
1419
|
+
info.normal = -info.normal;
|
|
1448
1420
|
}
|
|
1449
|
-
// Circle vs Polygon
|
|
1450
1421
|
else if (shapeA == COLLISION_CIRCLE && shapeB == COLLISION_POLYGON)
|
|
1451
1422
|
{
|
|
1452
1423
|
info = detectCirclePolygon(objA.position, objA.visualData.x,
|
|
@@ -1460,15 +1431,14 @@ CollisionInfo detectCollision(Object objA, Object objB, int objIndexB)
|
|
|
1460
1431
|
objA.position, objA.visualData.x,
|
|
1461
1432
|
int(objA.visualData.y), objA.visualData.z,
|
|
1462
1433
|
objIndexB);
|
|
1463
|
-
info.normal = -info.normal;
|
|
1434
|
+
info.normal = -info.normal;
|
|
1464
1435
|
}
|
|
1465
|
-
// AABB vs Polygon (simplified - treat polygon as circle for collision)
|
|
1466
1436
|
else if (shapeA == COLLISION_AABB && shapeB == COLLISION_POLYGON)
|
|
1467
1437
|
{
|
|
1468
1438
|
vec2 halfExtA = objA.visualData.xy * 0.5;
|
|
1469
1439
|
info = detectCircleAABB(objB.position, objB.visualData.x,
|
|
1470
1440
|
objA.position, halfExtA, objIndexB);
|
|
1471
|
-
info.normal = -info.normal;
|
|
1441
|
+
info.normal = -info.normal;
|
|
1472
1442
|
}
|
|
1473
1443
|
else if (shapeA == COLLISION_POLYGON && shapeB == COLLISION_AABB)
|
|
1474
1444
|
{
|
|
@@ -1484,10 +1454,6 @@ CollisionInfo detectCollision(Object objA, Object objB, int objIndexB)
|
|
|
1484
1454
|
// NEW: RESEARCH-GRADE COLLISION RESOLUTION SYSTEM
|
|
1485
1455
|
// ============================================================================
|
|
1486
1456
|
|
|
1487
|
-
// ============================================================================
|
|
1488
|
-
// BASIC COLLISION RESPONSE (Momentum-Conserving)
|
|
1489
|
-
// ============================================================================
|
|
1490
|
-
|
|
1491
1457
|
// Apply collision response to two objects (conserves momentum and energy)
|
|
1492
1458
|
void applyCollisionResponse(
|
|
1493
1459
|
inout vec2 posA, inout vec2 velA, float massA,
|
|
@@ -1495,73 +1461,51 @@ void applyCollisionResponse(
|
|
|
1495
1461
|
vec2 normal, float penetration,
|
|
1496
1462
|
float restitution, float friction
|
|
1497
1463
|
) {
|
|
1498
|
-
// Ensure valid masses
|
|
1499
1464
|
if (massA < EPSILON || massB < EPSILON) return;
|
|
1500
1465
|
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
// ========================================================================
|
|
1504
|
-
const float positionCorrectionFactor = 0.8; // Baumgarte stabilization
|
|
1505
|
-
const float penetrationSlop = 0.001; // Allow small penetration
|
|
1466
|
+
const float positionCorrectionFactor = 0.8;
|
|
1467
|
+
const float penetrationSlop = 0.001;
|
|
1506
1468
|
|
|
1507
1469
|
if (penetration > penetrationSlop) {
|
|
1508
1470
|
float totalMass = massA + massB;
|
|
1509
1471
|
float invTotalMass = 1.0 / totalMass;
|
|
1510
1472
|
|
|
1511
|
-
// Calculate correction vector
|
|
1512
1473
|
vec2 correction = (penetration - penetrationSlop) * positionCorrectionFactor * normal;
|
|
1513
1474
|
|
|
1514
|
-
// Apply position correction based on mass ratio
|
|
1515
1475
|
posA -= correction * (massB * invTotalMass);
|
|
1516
1476
|
posB += correction * (massA * invTotalMass);
|
|
1517
1477
|
}
|
|
1518
1478
|
|
|
1519
|
-
// ========================================================================
|
|
1520
|
-
// 2. VELOCITY CORRECTION (Impulse-based, conserves momentum)
|
|
1521
|
-
// ========================================================================
|
|
1522
|
-
// Relative velocity
|
|
1523
1479
|
vec2 relativeVel = velB - velA;
|
|
1524
1480
|
float velocityAlongNormal = dot(relativeVel, normal);
|
|
1525
1481
|
|
|
1526
|
-
// Only resolve if objects are moving toward each other
|
|
1527
1482
|
if (velocityAlongNormal > 0.0) return;
|
|
1528
1483
|
|
|
1529
|
-
|
|
1530
|
-
float e = min(max(restitution, 0.0), 1.0); // Clamp restitution
|
|
1484
|
+
float e = min(max(restitution, 0.0), 1.0);
|
|
1531
1485
|
float impulseScalar = -(1.0 + e) * velocityAlongNormal;
|
|
1532
1486
|
impulseScalar /= (1.0 / massA + 1.0 / massB);
|
|
1533
1487
|
|
|
1534
|
-
// Apply impulse to both velocities (equal and opposite)
|
|
1535
1488
|
vec2 impulse = impulseScalar * normal;
|
|
1536
1489
|
velA -= impulse / massA;
|
|
1537
1490
|
velB += impulse / massB;
|
|
1538
1491
|
|
|
1539
|
-
// ========================================================================
|
|
1540
|
-
// 3. FRICTION (Coulomb model)
|
|
1541
|
-
// ========================================================================
|
|
1542
1492
|
if (friction > 0.0) {
|
|
1543
|
-
// Recompute relative velocity after normal impulse
|
|
1544
1493
|
relativeVel = velB - velA;
|
|
1545
1494
|
|
|
1546
|
-
// Tangent vector (perpendicular to normal)
|
|
1547
1495
|
vec2 tangent = relativeVel - normal * dot(relativeVel, normal);
|
|
1548
1496
|
float tangentLength = length(tangent);
|
|
1549
1497
|
|
|
1550
1498
|
if (tangentLength > EPSILON) {
|
|
1551
1499
|
tangent = normalize(tangent);
|
|
1552
1500
|
|
|
1553
|
-
// Relative velocity in tangent direction
|
|
1554
1501
|
float relVelTangent = dot(relativeVel, tangent);
|
|
1555
1502
|
|
|
1556
|
-
// Friction impulse scalar
|
|
1557
1503
|
float frictionImpulse = -relVelTangent * friction;
|
|
1558
1504
|
frictionImpulse /= (1.0 / massA + 1.0 / massB);
|
|
1559
1505
|
|
|
1560
|
-
// Coulomb's law: friction impulse cannot exceed normal impulse
|
|
1561
1506
|
float maxFriction = abs(impulseScalar) * friction;
|
|
1562
1507
|
frictionImpulse = clamp(frictionImpulse, -maxFriction, maxFriction);
|
|
1563
1508
|
|
|
1564
|
-
// Apply friction impulse
|
|
1565
1509
|
vec2 frictionVec = frictionImpulse * tangent;
|
|
1566
1510
|
velA -= frictionVec / massA;
|
|
1567
1511
|
velB += frictionVec / massB;
|
|
@@ -1569,12 +1513,7 @@ void applyCollisionResponse(
|
|
|
1569
1513
|
}
|
|
1570
1514
|
}
|
|
1571
1515
|
|
|
1572
|
-
//
|
|
1573
|
-
// SIMPLIFIED COLLISION RESOLUTION FOR PARALLEL EXECUTION
|
|
1574
|
-
// ============================================================================
|
|
1575
|
-
|
|
1576
|
-
// This version handles collisions in a thread-safe way by only modifying
|
|
1577
|
-
// the current object and letting the other object handle its own updates
|
|
1516
|
+
// Simplified collision resolution for parallel execution
|
|
1578
1517
|
void resolveCollisionSimple(
|
|
1579
1518
|
inout vec2 posA, inout vec2 velA, float massA,
|
|
1580
1519
|
Object objB, int objBIndex,
|
|
@@ -1582,26 +1521,21 @@ void resolveCollisionSimple(
|
|
|
1582
1521
|
) {
|
|
1583
1522
|
if (!collision.hasCollision) return;
|
|
1584
1523
|
|
|
1585
|
-
// Get collision properties
|
|
1586
1524
|
CollisionProperties propsA = collisionProps[gl_GlobalInvocationID.x];
|
|
1587
1525
|
CollisionProperties propsB = collisionProps[objBIndex];
|
|
1588
1526
|
|
|
1589
|
-
// Effective restitution and friction
|
|
1590
1527
|
float restitution = min(propsA.restitution, propsB.restitution);
|
|
1591
1528
|
float friction = sqrt(propsA.friction * propsB.friction);
|
|
1592
1529
|
|
|
1593
|
-
// Other object's state
|
|
1594
1530
|
vec2 posB = objB.position;
|
|
1595
1531
|
vec2 velB = objB.velocity;
|
|
1596
1532
|
float massB = max(EPSILON, objB.mass);
|
|
1597
1533
|
|
|
1598
|
-
// Copy state for local processing
|
|
1599
1534
|
vec2 tempPosA = posA;
|
|
1600
1535
|
vec2 tempVelA = velA;
|
|
1601
1536
|
vec2 tempPosB = posB;
|
|
1602
1537
|
vec2 tempVelB = velB;
|
|
1603
1538
|
|
|
1604
|
-
// Apply collision response
|
|
1605
1539
|
applyCollisionResponse(
|
|
1606
1540
|
tempPosA, tempVelA, massA,
|
|
1607
1541
|
tempPosB, tempVelB, massB,
|
|
@@ -1609,25 +1543,16 @@ void resolveCollisionSimple(
|
|
|
1609
1543
|
restitution, friction
|
|
1610
1544
|
);
|
|
1611
1545
|
|
|
1612
|
-
// Update this object's state
|
|
1613
1546
|
posA = tempPosA;
|
|
1614
1547
|
velA = tempVelA;
|
|
1615
|
-
|
|
1616
|
-
// Note: The other object (objB) will handle its own update in its thread
|
|
1617
|
-
// This ensures momentum is conserved across the entire system
|
|
1618
1548
|
}
|
|
1619
1549
|
|
|
1620
|
-
//
|
|
1621
|
-
// ANALYTICAL ELASTIC COLLISION FORMULA (For perfect conservation)
|
|
1622
|
-
// ============================================================================
|
|
1623
|
-
|
|
1624
|
-
// Perfectly elastic collision for circles (conserves both momentum and energy)
|
|
1550
|
+
// Elastic collision formula (for perfect conservation)
|
|
1625
1551
|
void resolveElasticCollision(
|
|
1626
1552
|
inout vec2 posA, inout vec2 velA, float massA,
|
|
1627
1553
|
inout vec2 posB, inout vec2 velB, float massB,
|
|
1628
1554
|
vec2 normal, float penetration
|
|
1629
1555
|
) {
|
|
1630
|
-
// Position correction
|
|
1631
1556
|
const float positionPush = 0.5;
|
|
1632
1557
|
if (penetration > 0.0) {
|
|
1633
1558
|
float totalMass = massA + massB;
|
|
@@ -1635,21 +1560,17 @@ void resolveElasticCollision(
|
|
|
1635
1560
|
posB += normal * penetration * positionPush * (massA / totalMass);
|
|
1636
1561
|
}
|
|
1637
1562
|
|
|
1638
|
-
// Relative velocity along collision normal
|
|
1639
1563
|
vec2 relativeVel = velB - velA;
|
|
1640
1564
|
float velocityAlongNormal = dot(relativeVel, normal);
|
|
1641
1565
|
|
|
1642
|
-
// If objects are separating, no impulse needed
|
|
1643
1566
|
if (velocityAlongNormal > 0.0) return;
|
|
1644
1567
|
|
|
1645
|
-
// Perfectly elastic collision formulas (conserves both momentum and energy)
|
|
1646
1568
|
float totalMass = massA + massB;
|
|
1647
1569
|
float massRatioA = (massA - massB) / totalMass;
|
|
1648
1570
|
float massRatioB = (2.0 * massB) / totalMass;
|
|
1649
1571
|
float massRatioC = (2.0 * massA) / totalMass;
|
|
1650
1572
|
float massRatioD = (massB - massA) / totalMass;
|
|
1651
1573
|
|
|
1652
|
-
// Apply velocity changes
|
|
1653
1574
|
vec2 impulseA = normal * velocityAlongNormal;
|
|
1654
1575
|
vec2 impulseB = normal * velocityAlongNormal;
|
|
1655
1576
|
|
|
@@ -1657,11 +1578,7 @@ void resolveElasticCollision(
|
|
|
1657
1578
|
velB = velB - massRatioC * impulseB;
|
|
1658
1579
|
}
|
|
1659
1580
|
|
|
1660
|
-
//
|
|
1661
|
-
// CONTACT PERSISTENCE SYSTEM (Optional, for stability)
|
|
1662
|
-
// ============================================================================
|
|
1663
|
-
|
|
1664
|
-
// Find a persistent contact in the contact buffer
|
|
1581
|
+
// Contact persistence system
|
|
1665
1582
|
int findPersistentContact(int objectA, int objectB, vec2 normal) {
|
|
1666
1583
|
int baseIndex = objectA * MAX_CONTACTS_PER_OBJECT;
|
|
1667
1584
|
|
|
@@ -1671,32 +1588,26 @@ int findPersistentContact(int objectA, int objectB, vec2 normal) {
|
|
|
1671
1588
|
|
|
1672
1589
|
ContactPoint contact = contacts[contactIndex];
|
|
1673
1590
|
|
|
1674
|
-
// Check if this contact is between the same objects
|
|
1675
|
-
// and has a similar normal direction
|
|
1676
1591
|
if (contact.frameCount > 0 && dot(contact.normal, normal) > 0.9) {
|
|
1677
1592
|
return contactIndex;
|
|
1678
1593
|
}
|
|
1679
1594
|
}
|
|
1680
1595
|
|
|
1681
|
-
return -1;
|
|
1596
|
+
return -1;
|
|
1682
1597
|
}
|
|
1683
1598
|
|
|
1684
|
-
// Update or create a contact point
|
|
1685
1599
|
void updateContactPoint(int objectA, int objectB, vec2 normal, vec2 position, float penetration) {
|
|
1686
1600
|
int baseIndex = objectA * MAX_CONTACTS_PER_OBJECT;
|
|
1687
1601
|
int oldestIndex = baseIndex;
|
|
1688
1602
|
int oldestFrameCount = 9999;
|
|
1689
1603
|
|
|
1690
|
-
// Find slot for this contact
|
|
1691
1604
|
for (int i = 0; i < MAX_CONTACTS_PER_OBJECT; i++) {
|
|
1692
1605
|
int contactIndex = baseIndex + i;
|
|
1693
1606
|
if (contactIndex >= contacts.length()) break;
|
|
1694
1607
|
|
|
1695
1608
|
ContactPoint contact = contacts[contactIndex];
|
|
1696
1609
|
|
|
1697
|
-
// If this slot is empty or matches our contact, use it
|
|
1698
1610
|
if (contact.frameCount == 0 || dot(contact.normal, normal) > 0.9) {
|
|
1699
|
-
// Update existing contact
|
|
1700
1611
|
contacts[contactIndex].normal = normal;
|
|
1701
1612
|
contacts[contactIndex].position = position;
|
|
1702
1613
|
contacts[contactIndex].penetration = penetration;
|
|
@@ -1704,14 +1615,12 @@ void updateContactPoint(int objectA, int objectB, vec2 normal, vec2 position, fl
|
|
|
1704
1615
|
return;
|
|
1705
1616
|
}
|
|
1706
1617
|
|
|
1707
|
-
// Track the oldest contact for replacement
|
|
1708
1618
|
if (contact.frameCount < oldestFrameCount) {
|
|
1709
1619
|
oldestFrameCount = contact.frameCount;
|
|
1710
1620
|
oldestIndex = contactIndex;
|
|
1711
1621
|
}
|
|
1712
1622
|
}
|
|
1713
1623
|
|
|
1714
|
-
// Replace the oldest contact
|
|
1715
1624
|
if (oldestIndex >= 0 && oldestIndex < contacts.length()) {
|
|
1716
1625
|
contacts[oldestIndex].normal = normal;
|
|
1717
1626
|
contacts[oldestIndex].position = position;
|
|
@@ -1722,7 +1631,6 @@ void updateContactPoint(int objectA, int objectB, vec2 normal, vec2 position, fl
|
|
|
1722
1631
|
}
|
|
1723
1632
|
}
|
|
1724
1633
|
|
|
1725
|
-
// Age out old contacts
|
|
1726
1634
|
void ageContacts(int objectIndex) {
|
|
1727
1635
|
int baseIndex = objectIndex * MAX_CONTACTS_PER_OBJECT;
|
|
1728
1636
|
|
|
@@ -1730,11 +1638,9 @@ void ageContacts(int objectIndex) {
|
|
|
1730
1638
|
int contactIndex = baseIndex + i;
|
|
1731
1639
|
if (contactIndex >= contacts.length()) break;
|
|
1732
1640
|
|
|
1733
|
-
// Decrement frame count, remove if too old
|
|
1734
1641
|
if (contacts[contactIndex].frameCount > 0) {
|
|
1735
1642
|
contacts[contactIndex].frameCount--;
|
|
1736
1643
|
if (contacts[contactIndex].frameCount == 0) {
|
|
1737
|
-
// Reset contact
|
|
1738
1644
|
contacts[contactIndex].accumulatedNormalImpulse = 0.0;
|
|
1739
1645
|
contacts[contactIndex].accumulatedTangentImpulse = 0.0;
|
|
1740
1646
|
}
|
|
@@ -1743,16 +1649,14 @@ void ageContacts(int objectIndex) {
|
|
|
1743
1649
|
}
|
|
1744
1650
|
|
|
1745
1651
|
// ============================================================================
|
|
1746
|
-
// MAIN COMPUTE SHADER ENTRY POINT -
|
|
1652
|
+
// MAIN COMPUTE SHADER ENTRY POINT - WITH AABB COLLISION FIX
|
|
1747
1653
|
// ============================================================================
|
|
1748
1654
|
|
|
1749
1655
|
void main() {
|
|
1750
1656
|
uint gid = gl_GlobalInvocationID.x;
|
|
1751
1657
|
|
|
1752
|
-
// Early exit if beyond active object count
|
|
1753
1658
|
if (int(gid) >= uNumObjects) return;
|
|
1754
1659
|
|
|
1755
|
-
// Read current object state
|
|
1756
1660
|
Object p = objectsIn[gid];
|
|
1757
1661
|
vec2 pos = p.position;
|
|
1758
1662
|
vec2 vel = p.velocity;
|
|
@@ -1764,16 +1668,12 @@ void main() {
|
|
|
1764
1668
|
int objectIndex = int(gid);
|
|
1765
1669
|
vec2 originalPos = pos;
|
|
1766
1670
|
|
|
1767
|
-
// Initialize physics variables
|
|
1768
1671
|
vec2 acceleration = vec2(0.0);
|
|
1769
1672
|
float angular_accel = 0.0;
|
|
1770
1673
|
vec4 new_color = color;
|
|
1771
1674
|
|
|
1772
|
-
//
|
|
1773
|
-
// PHYSICS EQUATION EVALUATION (UNCHANGED)
|
|
1774
|
-
// ========================================================================
|
|
1675
|
+
// Physics equation evaluation (unchanged)
|
|
1775
1676
|
if (uEquationMode == 0) {
|
|
1776
|
-
// Custom equation mode
|
|
1777
1677
|
int eqID = p.equationID;
|
|
1778
1678
|
bool isValidEquation = (eqID >= 0 && eqID < MAX_EQUATION_COUNT) &&
|
|
1779
1679
|
(mappings[eqID].tokenCount_ax > 0 ||
|
|
@@ -1785,22 +1685,18 @@ void main() {
|
|
|
1785
1685
|
mappings[eqID].tokenCount_a > 0);
|
|
1786
1686
|
|
|
1787
1687
|
if (!isValidEquation) {
|
|
1788
|
-
// Use default physics if equation is invalid
|
|
1789
1688
|
acceleration = calculateDefaultPhysics(pos, vel, mass);
|
|
1790
1689
|
} else {
|
|
1791
|
-
// Evaluate custom physics equation components
|
|
1792
1690
|
EquationMapping mapping = mappings[eqID];
|
|
1793
1691
|
float ax = 0.0;
|
|
1794
1692
|
float ay = 0.0;
|
|
1795
1693
|
|
|
1796
|
-
// Evaluate X acceleration component
|
|
1797
1694
|
if (mapping.tokenCount_ax > 0) {
|
|
1798
1695
|
ax = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
1799
1696
|
rotation, angular_vel, color, mass, charge, objectIndex,
|
|
1800
1697
|
0, mapping.tokenOffset_ax, mapping.tokenCount_ax, mapping.constantOffset_ax);
|
|
1801
1698
|
}
|
|
1802
1699
|
|
|
1803
|
-
// Evaluate Y acceleration component
|
|
1804
1700
|
if (mapping.tokenCount_ay > 0) {
|
|
1805
1701
|
ay = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
1806
1702
|
rotation, angular_vel, color, mass, charge, objectIndex,
|
|
@@ -1809,7 +1705,6 @@ void main() {
|
|
|
1809
1705
|
|
|
1810
1706
|
acceleration = sanitizeVec2(vec2(ax, ay));
|
|
1811
1707
|
|
|
1812
|
-
// Evaluate angular acceleration
|
|
1813
1708
|
if (mapping.tokenCount_angular > 0) {
|
|
1814
1709
|
angular_accel = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
1815
1710
|
rotation, angular_vel, color, mass, charge, objectIndex,
|
|
@@ -1817,7 +1712,6 @@ void main() {
|
|
|
1817
1712
|
angular_accel = isInvalidFloat(angular_accel) ? 0.0 : angular_accel;
|
|
1818
1713
|
}
|
|
1819
1714
|
|
|
1820
|
-
// Evaluate color components (dynamic coloring)
|
|
1821
1715
|
if (mapping.tokenCount_r > 0) {
|
|
1822
1716
|
new_color.r = evaluateRPNComponent(pos.x, pos.y, vel.x, vel.y, p.collisionData.x, p.collisionData.y,
|
|
1823
1717
|
rotation, angular_vel, color, mass, charge, objectIndex,
|
|
@@ -1842,192 +1736,113 @@ void main() {
|
|
|
1842
1736
|
new_color = sanitizeVec4(new_color);
|
|
1843
1737
|
}
|
|
1844
1738
|
} else {
|
|
1845
|
-
// Default physics mode (simple spring-mass-damper)
|
|
1846
1739
|
acceleration = calculateDefaultPhysics(pos, vel, mass);
|
|
1847
1740
|
}
|
|
1848
1741
|
|
|
1849
1742
|
acceleration = sanitizeVec2(acceleration);
|
|
1850
1743
|
|
|
1851
|
-
//
|
|
1852
|
-
// STABLE INTEGRATION (Symplectic Euler - Energy Conserving)
|
|
1853
|
-
// ========================================================================
|
|
1854
|
-
|
|
1855
|
-
// 1. Update VELOCITY first using current acceleration
|
|
1744
|
+
// Symplectic Euler integration
|
|
1856
1745
|
vec2 new_vel = vel + acceleration * uDt;
|
|
1857
1746
|
new_vel = sanitizeVec2(new_vel);
|
|
1858
1747
|
|
|
1859
|
-
// 2. Use NEW VELOCITY to update POSITION (energy-conserving)
|
|
1860
1748
|
vec2 new_pos = pos + new_vel * uDt;
|
|
1861
1749
|
new_pos = sanitizeVec2(new_pos);
|
|
1862
1750
|
|
|
1863
|
-
Object selfNew = p;
|
|
1864
|
-
selfNew.position = new_pos;
|
|
1865
|
-
|
|
1866
|
-
// 3. Same for angular motion
|
|
1751
|
+
Object selfNew = p;
|
|
1752
|
+
selfNew.position = new_pos;
|
|
1753
|
+
|
|
1867
1754
|
float new_angular_vel = angular_vel + angular_accel * uDt;
|
|
1868
1755
|
float new_rotation = rotation + new_angular_vel * uDt;
|
|
1869
|
-
new_rotation = mod(new_rotation, 2.0 * PI);
|
|
1756
|
+
new_rotation = mod(new_rotation, 2.0 * PI);
|
|
1870
1757
|
|
|
1871
|
-
|
|
1872
|
-
// WORLD BOUNDARIES
|
|
1873
|
-
// ========================================================================
|
|
1874
|
-
const vec2 world_min = vec2(-1000000.0, -1000000.0);
|
|
1875
|
-
const vec2 world_max = vec2(1000000.0, 1000000.0);
|
|
1876
|
-
const float boundary_friction = 0.95;
|
|
1877
|
-
|
|
1878
|
-
// X boundaries
|
|
1879
|
-
if (new_pos.x < world_min.x) {
|
|
1880
|
-
new_pos.x = world_min.x;
|
|
1881
|
-
new_vel.x = abs(new_vel.x) * uRestitution;
|
|
1882
|
-
new_vel.y *= boundary_friction;
|
|
1883
|
-
}
|
|
1884
|
-
else if (new_pos.x > world_max.x) {
|
|
1885
|
-
new_pos.x = world_max.x;
|
|
1886
|
-
new_vel.x = -abs(new_vel.x) * uRestitution;
|
|
1887
|
-
new_vel.y *= boundary_friction;
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
// Y boundaries
|
|
1891
|
-
if (new_pos.y < world_min.y) {
|
|
1892
|
-
new_pos.y = world_min.y;
|
|
1893
|
-
new_vel.y = abs(new_vel.y) * uRestitution;
|
|
1894
|
-
new_vel.x *= boundary_friction;
|
|
1895
|
-
}
|
|
1896
|
-
else if (new_pos.y > world_max.y) {
|
|
1897
|
-
new_pos.y = world_max.y;
|
|
1898
|
-
new_vel.y = -abs(new_vel.y) * uRestitution;
|
|
1899
|
-
new_vel.x *= boundary_friction;
|
|
1900
|
-
}
|
|
1758
|
+
applyConstraints(new_pos, new_vel, objectIndex, originalPos);
|
|
1901
1759
|
|
|
1902
1760
|
// ========================================================================
|
|
1903
|
-
//
|
|
1761
|
+
// COLLISION HANDLING - using old positions for normal sign in AABB case
|
|
1904
1762
|
// ========================================================================
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
// ========================================================================
|
|
1908
|
-
// NEW: COLLISION SYSTEM - FIXED FOR PARALLEL EXECUTION
|
|
1909
|
-
// ========================================================================
|
|
1910
|
-
|
|
1911
|
-
// Strategy:
|
|
1912
|
-
// 1. Each thread only modifies its OWN object's velocity
|
|
1913
|
-
// 2. Use impulses that conserve momentum without modifying other objects
|
|
1914
|
-
// 3. Position correction happens AFTER all velocity updates
|
|
1915
|
-
|
|
1916
|
-
vec2 collision_vel = new_vel;
|
|
1917
|
-
bool had_collision = false;
|
|
1918
|
-
|
|
1919
|
-
// Check collisions with ALL other objects (not just higher indices)
|
|
1920
|
-
for (int i = 0; i < uNumObjects; i++) {
|
|
1921
|
-
if (i == objectIndex) continue; // Skip self
|
|
1763
|
+
vec2 collision_vel = new_vel;
|
|
1764
|
+
bool had_collision = false;
|
|
1922
1765
|
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
// Detect collision
|
|
1926
|
-
CollisionInfo collision = detectCollision(selfNew, other, i);
|
|
1927
|
-
|
|
1928
|
-
if (collision.hasCollision) {
|
|
1929
|
-
had_collision = true;
|
|
1930
|
-
|
|
1931
|
-
// Get collision properties
|
|
1932
|
-
CollisionProperties propsA = collisionProps[objectIndex];
|
|
1933
|
-
CollisionProperties propsB = collisionProps[i];
|
|
1766
|
+
for (int i = 0; i < uNumObjects; i++) {
|
|
1767
|
+
if (i == objectIndex) continue;
|
|
1934
1768
|
|
|
1935
|
-
|
|
1936
|
-
float restitution = min(propsA.restitution, propsB.restitution);
|
|
1937
|
-
float friction = sqrt(propsA.friction * propsB.friction);
|
|
1769
|
+
Object other = objectsIn[i];
|
|
1938
1770
|
|
|
1939
|
-
//
|
|
1940
|
-
|
|
1941
|
-
|
|
1771
|
+
// Pass current positions for overlap detection, but also
|
|
1772
|
+
// original (pre-update) positions for correct normal sign.
|
|
1773
|
+
CollisionInfo collision = detectCollision(selfNew, other, i, p.position, other.position);
|
|
1942
1774
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
if (velocityAlongNormal < 0.0) {
|
|
1949
|
-
// Calculate impulse (standard rigid body physics)
|
|
1950
|
-
float e = clamp(restitution, 0.0, 1.0);
|
|
1951
|
-
float j = -(1.0 + e) * velocityAlongNormal;
|
|
1952
|
-
j /= (1.0 / massA + 1.0 / massB);
|
|
1775
|
+
if (collision.hasCollision) {
|
|
1776
|
+
had_collision = true;
|
|
1777
|
+
|
|
1778
|
+
CollisionProperties propsA = collisionProps[objectIndex];
|
|
1779
|
+
CollisionProperties propsB = collisionProps[i];
|
|
1953
1780
|
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
collision_vel -= impulse / massA;
|
|
1781
|
+
float restitution = min(propsA.restitution, propsB.restitution);
|
|
1782
|
+
float friction = sqrt(propsA.friction * propsB.friction);
|
|
1957
1783
|
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1784
|
+
float massA = mass;
|
|
1785
|
+
float massB = max(EPSILON, other.mass);
|
|
1786
|
+
|
|
1787
|
+
vec2 relativeVel = other.velocity - collision_vel;
|
|
1788
|
+
float velocityAlongNormal = dot(relativeVel, collision.normal);
|
|
1789
|
+
|
|
1790
|
+
if (velocityAlongNormal < 0.0) {
|
|
1791
|
+
float e = clamp(restitution, 0.0, 1.0);
|
|
1792
|
+
float j = -(1.0 + e) * velocityAlongNormal;
|
|
1793
|
+
j /= (1.0 / massA + 1.0 / massB);
|
|
1962
1794
|
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
// Coulomb friction limit
|
|
1971
|
-
float maxFriction = abs(j) * friction;
|
|
1972
|
-
jt = clamp(jt, -maxFriction, maxFriction);
|
|
1795
|
+
vec2 impulse = j * collision.normal;
|
|
1796
|
+
collision_vel -= impulse / massA;
|
|
1797
|
+
|
|
1798
|
+
if (friction > 0.0) {
|
|
1799
|
+
vec2 tangent = relativeVel - collision.normal * dot(relativeVel, collision.normal);
|
|
1800
|
+
float tangentLength = length(tangent);
|
|
1973
1801
|
|
|
1974
|
-
|
|
1975
|
-
|
|
1802
|
+
if (tangentLength > EPSILON) {
|
|
1803
|
+
tangent = normalize(tangent);
|
|
1804
|
+
float vt = dot(relativeVel, tangent);
|
|
1805
|
+
|
|
1806
|
+
float jt = -vt * friction;
|
|
1807
|
+
jt /= (1.0 / massA + 1.0 / massB);
|
|
1808
|
+
|
|
1809
|
+
float maxFriction = abs(j) * friction;
|
|
1810
|
+
jt = clamp(jt, -maxFriction, maxFriction);
|
|
1811
|
+
|
|
1812
|
+
vec2 frictionImpulse = jt * tangent;
|
|
1813
|
+
collision_vel -= frictionImpulse / massA;
|
|
1814
|
+
}
|
|
1976
1815
|
}
|
|
1977
1816
|
}
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1980
|
-
// POSITION CORRECTION - only for deep penetrations
|
|
1981
|
-
// Move THIS object away from penetration
|
|
1982
|
-
const float slop = 0.01;
|
|
1983
|
-
const float percent = 0.4;
|
|
1984
|
-
|
|
1985
|
-
if (collision.penetration > slop) {
|
|
1986
|
-
float totalMass = massA + massB;
|
|
1987
|
-
vec2 correction = collision.normal * (collision.penetration - slop) * percent;
|
|
1988
1817
|
|
|
1989
|
-
|
|
1990
|
-
|
|
1818
|
+
const float slop = 0.01;
|
|
1819
|
+
const float percent = 0.4;
|
|
1820
|
+
if (collision.penetration > slop) {
|
|
1821
|
+
float totalMass = massA + massB;
|
|
1822
|
+
vec2 correction = collision.normal * (collision.penetration - slop) * percent;
|
|
1823
|
+
new_pos -= correction * (massB / totalMass);
|
|
1824
|
+
}
|
|
1991
1825
|
}
|
|
1992
1826
|
}
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
// Apply collision-modified velocity
|
|
1996
|
-
if (had_collision) {
|
|
1997
|
-
new_vel = sanitizeVec2(collision_vel);
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
// ========================================================================
|
|
2001
|
-
// REST OF CODE(boundaries, final write, etc.)
|
|
2002
|
-
// ========================================================================
|
|
2003
1827
|
|
|
2004
|
-
|
|
1828
|
+
if (had_collision) {
|
|
1829
|
+
new_vel = sanitizeVec2(collision_vel);
|
|
1830
|
+
}
|
|
1831
|
+
|
|
2005
1832
|
if (uEnableWarmStart == 1) {
|
|
2006
1833
|
ageContacts(objectIndex);
|
|
2007
1834
|
}
|
|
2008
1835
|
|
|
2009
|
-
// REMOVED: The second collision loop that was causing double-resolution
|
|
2010
|
-
// We no longer need to check i < objectIndex because all pairs are
|
|
2011
|
-
// processed exactly once in the loop above
|
|
2012
|
-
|
|
2013
|
-
// Apply the collision-corrected state
|
|
2014
1836
|
new_pos = sanitizeVec2(new_pos);
|
|
2015
1837
|
new_vel = sanitizeVec2(new_vel);
|
|
2016
1838
|
|
|
2017
|
-
// ========================================================================
|
|
2018
|
-
// FINAL SANITY CHECKS
|
|
2019
|
-
// ========================================================================
|
|
2020
|
-
|
|
2021
|
-
// Ensure velocities aren't excessive
|
|
2022
1839
|
float maxSpeed = 1000.0;
|
|
2023
1840
|
float currentSpeed = length(new_vel);
|
|
2024
1841
|
if (currentSpeed > maxSpeed) {
|
|
2025
1842
|
new_vel = normalize(new_vel) * maxSpeed;
|
|
2026
1843
|
}
|
|
2027
1844
|
|
|
2028
|
-
//
|
|
2029
|
-
// WRITE UPDATED OBJECT STATE
|
|
2030
|
-
// ========================================================================
|
|
1845
|
+
// Write updated object state to output buffer
|
|
2031
1846
|
objectsOut[gid].position = new_pos;
|
|
2032
1847
|
objectsOut[gid].velocity = new_vel;
|
|
2033
1848
|
objectsOut[gid].mass = mass;
|
|
@@ -2047,8 +1862,4 @@ if (had_collision) {
|
|
|
2047
1862
|
objectsOut[gid]._pad1 = 0;
|
|
2048
1863
|
objectsOut[gid]._padEnd[0] = 0;
|
|
2049
1864
|
objectsOut[gid]._padEnd[1] = 0;
|
|
2050
|
-
}
|
|
2051
|
-
|
|
2052
|
-
// ============================================================================
|
|
2053
|
-
// END OF RESEARCH-GRADE COLLISION SYSTEM
|
|
2054
|
-
// ============================================================================
|
|
1865
|
+
}
|
|
Binary file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hyperstellar"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.64"
|
|
8
8
|
description = "GPU-accelerated physics simulation engine"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{name = "Aiden Jabari", email = "Jabariaiden15@email.com"}]
|
|
@@ -62,5 +62,6 @@ where = ["hyperstellar/src"]
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
|
|
65
66
|
|
|
66
67
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{hyperstellar-0.1.63 → hyperstellar-0.1.64}/hyperstellar/src/hyperstellar.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|