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