hyperstellar 0.1.72__tar.gz → 0.1.74__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.
Files changed (18) hide show
  1. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/PKG-INFO +1 -1
  2. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/math.comp +132 -173
  3. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/_native/windows-x64/stellar.pyd +0 -0
  4. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar.egg-info/PKG-INFO +1 -1
  5. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/pyproject.toml +3 -1
  6. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/LICENSE +0 -0
  7. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/README.md +0 -0
  8. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/__init__.py +0 -0
  9. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/__init__.pyi +0 -0
  10. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/quad.frag +0 -0
  11. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/quad.geom +0 -0
  12. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/_native/windows-x64/shaders/quad.vert +0 -0
  13. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar/py.typed +0 -0
  14. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar.egg-info/SOURCES.txt +0 -0
  15. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar.egg-info/dependency_links.txt +0 -0
  16. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/hyperstellar/src/hyperstellar.egg-info/top_level.txt +0 -0
  17. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/setup.cfg +0 -0
  18. {hyperstellar-0.1.72 → hyperstellar-0.1.74}/tests/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperstellar
3
- Version: 0.1.72
3
+ Version: 0.1.74
4
4
  Summary: GPU-accelerated physics simulation engine
5
5
  Author-email: Aiden Jabari <Jabariaiden15@email.com>
6
6
  License: MIT License
@@ -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
@@ -1387,72 +1387,6 @@ CollisionInfo detectCirclePolygon(vec2 circlePos, float circleRadius,
1387
1387
  return info;
1388
1388
  }
1389
1389
 
1390
- // -------------------------------------------------------------------------
1391
- // NEW: Exact AABB projection onto a SAT axis
1392
- // Meant to replace approximate polygon projection for AABB shapes
1393
- // -------------------------------------------------------------------------
1394
- vec2 projectAABB(vec2 center, vec2 halfExt, vec2 axis)
1395
- {
1396
- // Project the 4 AABB vertices efficiently using the half-extents
1397
- float centerProj = dot(center, axis);
1398
- float extent = abs(axis.x) * halfExt.x + abs(axis.y) * halfExt.y;
1399
- return vec2(centerProj - extent, centerProj + extent);
1400
- }
1401
-
1402
- // -------------------------------------------------------------------------
1403
- // NEW: AABB vs Polygon collision using a dedicated SAT that respects AABB axes
1404
- // -------------------------------------------------------------------------
1405
- CollisionInfo detectAABBPolygon(
1406
- vec2 posA, vec2 halfExtA,
1407
- vec2 posB, float radiusB, int sidesB, float rotB,
1408
- int objB)
1409
- {
1410
- CollisionInfo info;
1411
- info.hasCollision = false;
1412
- info.otherObjectID = objB;
1413
- info.penetration = 1e10;
1414
-
1415
- // AABB contributes 2 axes: X and Y
1416
- vec2 aabbAxes[2];
1417
- aabbAxes[0] = vec2(1.0, 0.0);
1418
- aabbAxes[1] = vec2(0.0, 1.0);
1419
-
1420
- // Test AABB axes
1421
- for (int i = 0; i < 2; i++)
1422
- {
1423
- vec2 axis = aabbAxes[i];
1424
- vec2 projA = projectAABB(posA, halfExtA, axis);
1425
- vec2 projB = projectPolygon(posB, radiusB, sidesB, rotB, axis);
1426
- if (projA.y < projB.x || projB.y < projA.x)
1427
- return info;
1428
- float overlap = min(projA.y, projB.y) - max(projA.x, projB.x);
1429
- if (overlap < info.penetration) {
1430
- info.penetration = overlap;
1431
- info.normal = axis;
1432
- if (dot(posB - posA, axis) < 0.0) info.normal = -axis;
1433
- }
1434
- }
1435
-
1436
- // Test polygon edge axes
1437
- for (int i = 0; i < sidesB; i++)
1438
- {
1439
- vec2 axis = getPolygonNormal(i, sidesB, rotB);
1440
- vec2 projA = projectAABB(posA, halfExtA, axis);
1441
- vec2 projB = projectPolygon(posB, radiusB, sidesB, rotB, axis);
1442
- if (projA.y < projB.x || projB.y < projA.x)
1443
- return info;
1444
- float overlap = min(projA.y, projB.y) - max(projA.x, projB.x);
1445
- if (overlap < info.penetration) {
1446
- info.penetration = overlap;
1447
- info.normal = axis;
1448
- if (dot(posB - posA, axis) < 0.0) info.normal = -axis;
1449
- }
1450
- }
1451
-
1452
- info.hasCollision = true;
1453
- return info;
1454
- }
1455
-
1456
1390
  // Master collision detection dispatcher
1457
1391
  CollisionInfo detectCollision(Object objA, Object objB, int objIndexB)
1458
1392
  {
@@ -1506,11 +1440,13 @@ CollisionInfo detectCollision(Object objA, Object objB, int objIndexB)
1506
1440
  }
1507
1441
  else if (shapeA == COLLISION_AABB && shapeB == COLLISION_CIRCLE)
1508
1442
  {
1443
+ // Swap roles and flip normal
1509
1444
  vec2 halfExtA = objA.visualData.xy * 0.5;
1510
1445
  info = detectCircleAABB(objB.position, objB.visualData.x,
1511
1446
  objA.position, halfExtA, objIndexB);
1512
1447
  info.normal = -info.normal; // Flip normal
1513
1448
  }
1449
+ // Circle vs Polygon
1514
1450
  else if (shapeA == COLLISION_CIRCLE && shapeB == COLLISION_POLYGON)
1515
1451
  {
1516
1452
  info = detectCirclePolygon(objA.position, objA.visualData.x,
@@ -1526,29 +1462,28 @@ CollisionInfo detectCollision(Object objA, Object objB, int objIndexB)
1526
1462
  objIndexB);
1527
1463
  info.normal = -info.normal; // Flip normal
1528
1464
  }
1529
- // AABB vs Polygon (corrected)
1465
+ // AABB vs Polygon (simplified - treat polygon as circle for collision)
1530
1466
  else if (shapeA == COLLISION_AABB && shapeB == COLLISION_POLYGON)
1531
1467
  {
1532
1468
  vec2 halfExtA = objA.visualData.xy * 0.5;
1533
- info = detectAABBPolygon(objA.position, halfExtA,
1534
- objB.position, objB.visualData.x,
1535
- int(objB.visualData.y), objB.visualData.z,
1536
- objIndexB);
1469
+ info = detectCircleAABB(objB.position, objB.visualData.x,
1470
+ objA.position, halfExtA, objIndexB);
1471
+ info.normal = -info.normal; // Flip normal
1537
1472
  }
1538
- // Polygon vs AABB (corrected)
1539
1473
  else if (shapeA == COLLISION_POLYGON && shapeB == COLLISION_AABB)
1540
1474
  {
1541
1475
  vec2 halfExtB = objB.visualData.xy * 0.5;
1542
- info = detectAABBPolygon(objB.position, halfExtB,
1543
- objA.position, objA.visualData.x,
1544
- int(objA.visualData.y), objA.visualData.z,
1545
- objIndexB);
1546
- info.normal = -info.normal;
1476
+ info = detectCircleAABB(objA.position, objA.visualData.x,
1477
+ objB.position, halfExtB, objIndexB);
1547
1478
  }
1548
1479
 
1549
1480
  return info;
1550
1481
  }
1551
1482
 
1483
+ // ============================================================================
1484
+ // NEW: RESEARCH-GRADE COLLISION RESOLUTION SYSTEM
1485
+ // ============================================================================
1486
+
1552
1487
  // ============================================================================
1553
1488
  // BASIC COLLISION RESPONSE (Momentum-Conserving)
1554
1489
  // ============================================================================
@@ -1807,6 +1742,33 @@ void ageContacts(int objectIndex) {
1807
1742
  }
1808
1743
  }
1809
1744
 
1745
+ // Estimate world contact point between two objects after collision detection.
1746
+ // Uses shape types to give a reasonable point for torque calculation.
1747
+ vec2 getContactPoint(Object objA, Object objB, vec2 normal, float penetration) {
1748
+ CollisionProperties propsA = collisionProps[gl_GlobalInvocationID.x];
1749
+ int shapeA = propsA.shapeType;
1750
+ float radiusA = objA.visualData.x;
1751
+ vec2 halfExtA = objA.visualData.xy * 0.5;
1752
+
1753
+ // For circle: contact point is along normal at radius distance from centre
1754
+ if (shapeA == COLLISION_CIRCLE) {
1755
+ return objA.position - normal * radiusA;
1756
+ }
1757
+ // For AABB or polygon: estimate deepest point along normal
1758
+ else if (shapeA == COLLISION_AABB || shapeA == COLLISION_POLYGON) {
1759
+ // Approximate using the other object's AABB clamp method (from user snippet)
1760
+ vec2 otherHalfExt = objB.visualData.xy * 0.5;
1761
+ vec2 otherMin = objB.position - otherHalfExt;
1762
+ vec2 otherMax = objB.position + otherHalfExt;
1763
+ // Clamp object A's centre to object B's AABB - gives a point inside B
1764
+ vec2 closest = clamp(objA.position, otherMin, otherMax);
1765
+ return closest;
1766
+ }
1767
+ // Fallback: centre of object A
1768
+ return objA.position;
1769
+ }
1770
+
1771
+
1810
1772
  // ============================================================================
1811
1773
  // MAIN COMPUTE SHADER ENTRY POINT - UPDATED WITH FIXED COLLISION RESOLUTION
1812
1774
  // ============================================================================
@@ -1931,7 +1893,7 @@ void main() {
1931
1893
  new_rotation = mod(new_rotation, 2.0 * PI); // Wrap rotation
1932
1894
 
1933
1895
  // ========================================================================
1934
- // WORLD BOUNDARIES
1896
+ // WORLD BOUNDARIES (UNCHANGED)
1935
1897
  // ========================================================================
1936
1898
  const vec2 world_min = vec2(-1000000.0, -1000000.0);
1937
1899
  const vec2 world_max = vec2(1000000.0, 1000000.0);
@@ -1965,127 +1927,124 @@ void main() {
1965
1927
  // CONSTRAINT RESOLUTION (UNCHANGED)
1966
1928
  // ========================================================================
1967
1929
  applyConstraints(new_pos, new_vel, objectIndex, originalPos);
1968
-
1969
- // ========================================================================
1970
- // NEW: COLLISION SYSTEM - FIXED FOR PARALLEL EXECUTION
1971
- // ========================================================================
1972
-
1973
- // Strategy:
1974
- // 1. Each thread only modifies its OWN object's velocity
1975
- // 2. Use impulses that conserve momentum without modifying other objects
1976
- // 3. Position correction happens AFTER all velocity updates
1977
-
1978
- vec2 collision_vel = new_vel;
1979
- bool had_collision = false;
1980
-
1981
- // Check collisions with ALL other objects (not just higher indices)
1982
- for (int i = 0; i < uNumObjects; i++) {
1983
- if (i == objectIndex) continue; // Skip self
1984
-
1985
- Object other = objectsIn[i];
1986
-
1987
- // Detect collision
1988
- CollisionInfo collision = detectCollision(p, other, i);
1989
1930
 
1990
- if (collision.hasCollision) {
1991
- had_collision = true;
1992
-
1993
- // Get collision properties
1994
- CollisionProperties propsA = collisionProps[objectIndex];
1995
- CollisionProperties propsB = collisionProps[i];
1996
-
1997
- // Calculate effective properties
1998
- float restitution = min(propsA.restitution, propsB.restitution);
1999
- float friction = sqrt(propsA.friction * propsB.friction);
1931
+ // ========================================================================
1932
+ // NEW COLLISION SYSTEM - ANTI-STICKING IMPULSE + TORQUE
1933
+ // ========================================================================
1934
+ vec2 collision_vel = new_vel;
1935
+ bool had_collision = false;
1936
+
1937
+ // Precompute inertia for torque (square plate approximation)
1938
+ float halfW = p.visualData.x * 0.5;
1939
+ float halfH = p.visualData.y * 0.5;
1940
+ float inertia = mass * (halfW * halfW + halfH * halfH) / 6.0;
1941
+ if (inertia < EPSILON) inertia = EPSILON;
1942
+ float accumulated_torque = 0.0;
1943
+
1944
+ // Check collisions with ALL other objects
1945
+ for (int i = 0; i < uNumObjects; i++) {
1946
+ if (i == objectIndex) continue; // Skip self
2000
1947
 
2001
- // Get masses
2002
- float massA = mass;
2003
- float massB = max(EPSILON, other.mass);
1948
+ Object other = objectsIn[i];
2004
1949
 
2005
- // Relative velocity
2006
- vec2 relativeVel = other.velocity - collision_vel;
2007
- float velocityAlongNormal = dot(relativeVel, collision.normal);
1950
+ // Detect collision
1951
+ CollisionInfo collision = detectCollision(p, other, i);
2008
1952
 
2009
- // Only resolve if moving toward each other
2010
- if (velocityAlongNormal < 0.0) {
2011
- // Calculate impulse (standard rigid body physics)
2012
- float e = clamp(restitution, 0.0, 1.0);
2013
- float j = -(1.0 + e) * velocityAlongNormal;
2014
- j /= (1.0 / massA + 1.0 / massB);
1953
+ if (collision.hasCollision) {
1954
+ had_collision = true;
1955
+
1956
+ // Get collision properties
1957
+ CollisionProperties propsA = collisionProps[objectIndex];
1958
+ CollisionProperties propsB = collisionProps[i];
1959
+
1960
+ // Calculate effective properties
1961
+ float restitution = min(propsA.restitution, propsB.restitution);
1962
+ float friction = sqrt(propsA.friction * propsB.friction);
1963
+
1964
+ // Get masses
1965
+ float massA = mass;
1966
+ float massB = max(EPSILON, other.mass);
2015
1967
 
2016
- // Apply impulse to THIS object only
2017
- vec2 impulse = j * collision.normal;
2018
- collision_vel -= impulse / massA;
1968
+ // Relative velocity
1969
+ vec2 relativeVel = other.velocity - collision_vel;
1970
+ float velocityAlongNormal = dot(relativeVel, collision.normal);
2019
1971
 
2020
- // Friction
2021
- if (friction > 0.0) {
2022
- vec2 tangent = relativeVel - collision.normal * dot(relativeVel, collision.normal);
2023
- float tangentLength = length(tangent);
1972
+ // Only resolve if moving toward each other
1973
+ if (velocityAlongNormal < 0.0) {
1974
+ // Analytical impulse (symmetric, conserves momentum)
1975
+ float e = clamp(restitution, 0.0, 1.0);
1976
+ float j = -(1.0 + e) * velocityAlongNormal;
1977
+ j /= (1.0 / massA + 1.0 / massB);
2024
1978
 
2025
- if (tangentLength > EPSILON) {
2026
- tangent = normalize(tangent);
2027
- float vt = dot(relativeVel, tangent);
2028
-
2029
- float jt = -vt * friction;
2030
- jt /= (1.0 / massA + 1.0 / massB);
2031
-
2032
- // Coulomb friction limit
2033
- float maxFriction = abs(j) * friction;
2034
- jt = clamp(jt, -maxFriction, maxFriction);
2035
-
2036
- vec2 frictionImpulse = jt * tangent;
2037
- collision_vel -= frictionImpulse / massA;
1979
+ // Apply impulse to THIS object only
1980
+ vec2 impulse = j * collision.normal;
1981
+ collision_vel += impulse / massA; // Note: += because we subtract from relative velocity formula
1982
+
1983
+ // Friction (Coulomb model)
1984
+ vec2 friction_impulse = vec2(0.0);
1985
+ if (friction > 0.0) {
1986
+ vec2 tangent = relativeVel - collision.normal * velocityAlongNormal;
1987
+ float tangentLength = length(tangent);
1988
+ if (tangentLength > EPSILON) {
1989
+ tangent = normalize(tangent);
1990
+ float vt = dot(relativeVel, tangent);
1991
+ float jt = -vt * friction;
1992
+ jt /= (1.0 / massA + 1.0 / massB);
1993
+ // Coulomb limit
1994
+ jt = clamp(jt, -abs(j) * friction, abs(j) * friction);
1995
+ friction_impulse = jt * tangent;
1996
+ collision_vel += friction_impulse / massA;
1997
+ }
2038
1998
  }
1999
+
2000
+ // ================================================================
2001
+ // TORQUE CALCULATION (from contact point)
2002
+ // ================================================================
2003
+ vec2 contactPoint = getContactPoint(p, other, collision.normal, collision.penetration);
2004
+ vec2 r = contactPoint - new_pos; // lever arm from centre to contact
2005
+ float torque_n = r.x * impulse.y - r.y * impulse.x; // cross(r, impulse)
2006
+ float torque_f = r.x * friction_impulse.y - r.y * friction_impulse.x;
2007
+ accumulated_torque += (torque_n + torque_f) / inertia;
2039
2008
  }
2040
- }
2041
-
2042
- // POSITION CORRECTION - only for deep penetrations
2043
- // Move THIS object away from penetration
2044
- const float slop = 0.01;
2045
- const float percent = 0.4; // Less aggressive than before
2046
-
2047
- if (collision.penetration > slop) {
2048
- float totalMass = massA + massB;
2049
- vec2 correction = collision.normal * (collision.penetration - slop) * percent;
2050
2009
 
2051
- // Only correct THIS object's position
2052
- // The correction is weighted by the inverse mass ratio
2053
- new_pos -= correction * (massB / totalMass);
2010
+ // POSITION CORRECTION - mild, to reduce sticking
2011
+ const float slop = 0.05; // larger slop = less aggressive
2012
+ const float percent = 0.2; // smaller percent = softer correction
2013
+ if (collision.penetration > slop) {
2014
+ float totalMass = massA + massB;
2015
+ vec2 correction = collision.normal * (collision.penetration - slop) * percent;
2016
+ // Only correct THIS object's position, weighted by mass ratio
2017
+ new_pos -= correction * (massB / totalMass);
2018
+ }
2054
2019
  }
2055
2020
  }
2056
- }
2057
-
2058
- // Apply collision-modified velocity
2059
- if (had_collision) {
2060
- new_vel = sanitizeVec2(collision_vel);
2061
- }
2062
-
2063
- // ========================================================================
2064
- // REST OF CODE(boundaries, final write, etc.)
2065
- // ========================================================================
2066
2021
 
2067
- // Age out old contacts for this object
2068
- if (uEnableWarmStart == 1) {
2069
- ageContacts(objectIndex);
2022
+ // Apply collision-modified velocity and torque
2023
+ if (had_collision) {
2024
+ new_vel = sanitizeVec2(collision_vel);
2025
+ new_angular_vel += accumulated_torque * uDt;
2070
2026
  }
2071
2027
 
2072
- // Apply the collision-corrected state
2073
- new_pos = sanitizeVec2(new_pos);
2074
- new_vel = sanitizeVec2(new_vel);
2028
+ // Clamp angular velocity for stability
2029
+ const float maxOmega = 100.0;
2030
+ new_angular_vel = clamp(new_angular_vel, -maxOmega, maxOmega);
2031
+ new_rotation = mod(new_rotation, 2.0 * PI);
2075
2032
 
2076
2033
  // ========================================================================
2077
2034
  // FINAL SANITY CHECKS
2078
2035
  // ========================================================================
2036
+ new_pos = sanitizeVec2(new_pos);
2037
+ new_vel = sanitizeVec2(new_vel);
2079
2038
 
2080
2039
  // Ensure velocities aren't excessive
2081
- float maxSpeed = 1000.0;
2040
+ float maxSpeed = 1000000.0;
2082
2041
  float currentSpeed = length(new_vel);
2083
2042
  if (currentSpeed > maxSpeed) {
2084
2043
  new_vel = normalize(new_vel) * maxSpeed;
2085
2044
  }
2086
2045
 
2087
2046
  // ========================================================================
2088
- // WRITE UPDATED OBJECT STATE
2047
+ // WRITE UPDATED OBJECT STATE
2089
2048
  // ========================================================================
2090
2049
  objectsOut[gid].position = new_pos;
2091
2050
  objectsOut[gid].velocity = new_vel;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperstellar
3
- Version: 0.1.72
3
+ Version: 0.1.74
4
4
  Summary: GPU-accelerated physics simulation engine
5
5
  Author-email: Aiden Jabari <Jabariaiden15@email.com>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hyperstellar"
7
- version = "0.1.72"
7
+ version = "0.1.74"
8
8
  description = "GPU-accelerated physics simulation engine"
9
9
  readme = "README.md"
10
10
  authors = [{name = "Aiden Jabari", email = "Jabariaiden15@email.com"}]
@@ -70,6 +70,8 @@ where = ["hyperstellar/src"]
70
70
 
71
71
 
72
72
 
73
+
74
+
73
75
 
74
76
 
75
77
 
File without changes
File without changes
File without changes