Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ so a test written against the reference reads the same here.
| module | holds | state |
|---|---|---|
| `aephysics.math` | vectors, quaternions, transforms, 3x3 matrices, bounding boxes, segment distances, inertia helpers, the deterministic atan2/cos/sin | done, `test_math.ae` (6M checks) |
| `aephysics.core` | bit set, id pool, hash set, arrays, the stack and arena allocators | done, `test_core.ae` (100k checks) |
| `aephysics.core` | bit set, id pool, hash set, a long-to-int map, arrays, the stack and arena allocators, the block hash | done, `test_core.ae` (100k checks) |
| `aephysics.dynamic_tree` | the bounding volume hierarchy under the broad phase: SAH insertion, rotations, enlarge, sweep refit, partial rebuild in depth-first order, box / closest / ray / swept-box queries | done, `test_dynamic_tree.ae` (12k checks); [same tree as the reference, ray cast 1.9x its time](bench/RESULTS.md#dynamic_tree) |
| `aephysics.hull` | quickhull with face merging, the half-edge hull with its mass properties, box / cylinder / cone / rock hulls, clone-and-transform with mirroring, support functions, ray cast, the 2D hull | done, `test_hull.ae` (438 checks); [same hulls as the reference, 1.6-2x its time](bench/RESULTS.md#hull) |
| `aephysics.distance` | GJK with the warm-started simplex cache, the shape cast by conservative advancement, the time of impact by separating-axis root finding | done, `test_distance.ae` (1.1k checks); [same results as the reference, 1.3-1.5x its time](bench/RESULTS.md#distance) |
| `aephysics.manifold` | contact manifolds for sphere, capsule and hull in every pairing: the separating axis test with its cache, reference-face clipping, the feature pairs, reduction to four points | done, `test_manifold.ae` (43k checks, 7,000 pairs against a brute-force oracle); [same manifolds as the reference, warm cache at parity](bench/RESULTS.md#manifold) |
| `aephysics.triangle_manifold` | one mesh triangle against a sphere, capsule or hull: back-side cull with hysteresis, GJK shallow, the separating axis test deep with the triangle's edges as zero-area faces, the feature recorded for the mesh contact's ghost-collision reduction | done, `test_triangle_manifold.ae` (1.5k checks); [same manifolds as the reference, within 10% on hulls](bench/RESULTS.md#triangle_manifold) |
| `aephysics.collision` | triangle mesh (BVH), height field, shapes with mass properties, ray and shape casts | next |
| `aephysics.mesh` | the triangle mesh: a BVH by binned SAH or median split with the triangles in depth-first order, vertex welding, edge flags, any scale including mirrored; overlap, ray cast, shape cast, the mover's planes, a box query | done, `test_mesh.ae` (1.6k checks); [same trees as the reference, traversals 1.7-2x](bench/RESULTS.md#mesh) |
| `aephysics.collision` | height field, shapes with mass properties, ray and shape casts | next |
| `aephysics.dynamics` | bodies, contacts, the constraint graph, islands, the Soft Step solver, joints (spherical, revolute, prismatic, distance, motor, weld, wheel), sensors, the character mover, the world | |
| `aephysics` | the public API | |

Expand Down
123 changes: 123 additions & 0 deletions aephysics/core/module.ae
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ exports (
key_hash, shape_pair_key, SHAPE_POWER, CHILD_POWER, MAX_SHAPES, MAX_CHILD_SHAPES,
set_create, set_destroy, set_clear, set_add, set_remove, set_contains, set_contains_hashed,
set_count, set_bytes,
LongMap, map_create, map_destroy, map_clear, map_set, map_get, map_has, map_count, map_bytes,
hash_bytes,
ints_create, ints_destroy, ints_reserve, ints_push, ints_pop, ints_get, ints_set, ints_clear,
ints_remove_swap, ints_count, ints_capacity, ints_bytes,
buffer_create, buffer_destroy, buffer_reserve, buffer_push, buffer_pop, buffer_clear,
Expand Down Expand Up @@ -259,6 +261,127 @@ bitset_count(s: *BitSet) -> int {

bitset_bytes(s: *BitSet) -> int { return s.block_capacity * 8 }

// --- long -> int map ------------------------------------------------------------

// An open-addressing map from a 64-bit key to an int, on the hash set's
// pattern (a zero hash marks an empty slot): what the reference's
// verstable maps are used for -- the mesh's vertex welding and edge
// pairing, the world's hull database.
struct MapItem {
key: long
hash: int
value: int
}

struct LongMap {
items: ptr // MapItem[]
capacity: int
count: int
}

map_create(capacity: int) -> LongMap {
cap = 16
if capacity > 16 { cap = round_up_power_of_2(capacity) }
return LongMap { items: alloc(cap * sizeof(MapItem)), capacity: cap, count: 0 }
}

map_destroy(m: *LongMap) {
free_bytes(m.items, m.capacity * sizeof(MapItem))
m.items = null
m.count = 0
m.capacity = 0
}

map_clear(m: *LongMap) {
m.count = 0
memset(m.items, 0, m.capacity * sizeof(MapItem))
}

map_find_slot(m: *LongMap, key: long, hash: int) -> int {
mask = m.capacity - 1
index = hash & mask
items = m.items as MapItem[]
while items[index].hash != 0 && items[index].key != key {
index = (index + 1) & mask
}
return index
}

map_grow(m: *LongMap) {
old_capacity = m.capacity
old_items = m.items
m.count = 0
m.capacity = 2 * old_capacity
m.items = alloc(m.capacity * sizeof(MapItem))
old = old_items as MapItem[]
items = m.items as MapItem[]
i = 0
while i < old_capacity {
if old[i].hash != 0 {
index = map_find_slot(m, old[i].key, old[i].hash)
items[index] = old[i]
m.count = m.count + 1
}
i = i + 1
}
free_bytes(old_items, old_capacity * sizeof(MapItem))
}

// Set the key's value; true when the key was already there.
map_set(m: *LongMap, key: long, value: int) -> bool {
hash = key_hash(key)
index = map_find_slot(m, key, hash)
items = m.items as MapItem[]
if items[index].hash != 0 {
items[index].value = value
return true
}
if 2 * (m.count + 1) > m.capacity {
map_grow(m)
index = map_find_slot(m, key, hash)
items = m.items as MapItem[]
}
items[index] = MapItem { key: key, hash: hash, value: value }
m.count = m.count + 1
return false
}

// The key's value, or the fallback when it is not there.
map_get(m: *LongMap, key: long, fallback: int) -> int {
index = map_find_slot(m, key, key_hash(key))
items = m.items as MapItem[]
if items[index].hash == 0 { return fallback }
return items[index].value
}

map_has(m: *LongMap, key: long) -> bool {
index = map_find_slot(m, key, key_hash(key))
items = m.items as MapItem[]
return items[index].hash != 0
}

map_count(m: *LongMap) -> int { return m.count }
map_bytes(m: *LongMap) -> int { return m.capacity * sizeof(MapItem) }

// --- a hash over a block --------------------------------------------------------

// This engine's hash of a block of 8-byte words (the count rounded down);
// never zero. The reference uses rapidhash for the same purpose: identity
// of hull and mesh data.
hash_bytes(block: ptr, byte_count: int) -> long {
words = block as long[]
count = byte_count / 8
h = 0x9E3779B9 as long
i = 0
while i < count {
h = key_hash(h ^ words[i]) + (i as long)
i = i + 1
}
h = key_hash(h)
if h == (0 as long) { return 1 as long }
return h
}

// --- int array (container.h for ints) --------------------------------------

struct IntArray {
Expand Down
6 changes: 4 additions & 2 deletions aephysics/distance/module.ae
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ struct CastOutput {
point: Vec3
fraction: float
iterations: int
triangle_index: int
triangle_index: int // of a mesh or height field, or NULL_INDEX
child_index: int // of a compound, or NULL_INDEX
material_index: int // or NULL_INDEX
hit: bool
}

Expand Down Expand Up @@ -159,7 +161,7 @@ empty_cache() -> SimplexCache {

empty_cast_output() -> CastOutput {
return CastOutput { normal: math.vec3_zero(), point: math.vec3_zero(), fraction: 0.0, iterations: 0,
triangle_index: NULL_INDEX, hit: false }
triangle_index: NULL_INDEX, child_index: NULL_INDEX, material_index: NULL_INDEX, hit: false }
}

cache_index_a(c: *SimplexCache, i: int) -> int {
Expand Down
18 changes: 1 addition & 17 deletions aephysics/hull/module.ae
Original file line number Diff line number Diff line change
Expand Up @@ -1370,25 +1370,9 @@ update_hull_bulk_properties(h: *HullData) -> bool {
return mass > 0.0 && volume > 0.0 && area > 0.0 && radius > 0.0
}

// This engine's hash of the block, with the hash field itself zero;
// never zero.
hash_block(block: ptr, byte_count: int) -> long {
words = block as long[]
count = byte_count / 8
h = 0x9E3779B9 as long
i = 0
while i < count {
h = core.key_hash(h ^ words[i]) + (i as long)
i = i + 1
}
h = core.key_hash(h)
if h == (0 as long) { return 1 as long }
return h
}

stamp_hash(h: *HullData) {
h.hash = 0 as long
h.hash = hash_block(h as ptr, h.byte_count)
h.hash = core.hash_bytes(h as ptr, h.byte_count)
}

hash_hull_data(h: *HullData) -> long { return h.hash }
Expand Down
Loading
Loading