|
| 1 | +@startuml collision-algorithm-broadphase |
| 2 | + |
| 3 | +skinparam backgroundColor #FAFAFA |
| 4 | +skinparam defaultFontName Sans |
| 5 | +skinparam defaultFontSize 11 |
| 6 | + |
| 7 | +skinparam class { |
| 8 | + BackgroundColor #EEF2FB |
| 9 | + BorderColor #5577AA |
| 10 | + HeaderBackgroundColor #7AA3CC |
| 11 | + FontColor #222222 |
| 12 | + HeaderFontColor #FFFFFF |
| 13 | + HeaderFontStyle bold |
| 14 | + ArrowColor #2E3436 |
| 15 | + AttributeFontSize 10 |
| 16 | +} |
| 17 | + |
| 18 | +skinparam class<<abstract>> { |
| 19 | + HeaderBackgroundColor #4E9A06 |
| 20 | +} |
| 21 | + |
| 22 | +skinparam class<<SOFA>> { |
| 23 | + HeaderBackgroundColor #888888 |
| 24 | +} |
| 25 | + |
| 26 | +skinparam note { |
| 27 | + BackgroundColor #FFFBD5 |
| 28 | + BorderColor #C8A000 |
| 29 | +} |
| 30 | + |
| 31 | +' ───────────────────────────────────────────── |
| 32 | +' SOFA root (external) |
| 33 | +' ───────────────────────────────────────────── |
| 34 | +class BaseObject <<SOFA>> { |
| 35 | +} |
| 36 | + |
| 37 | +' ───────────────────────────────────────────── |
| 38 | +' Stubs from other diagrams |
| 39 | +' ───────────────────────────────────────────── |
| 40 | +class BaseGeometry |
| 41 | +class BaseElement |
| 42 | + |
| 43 | +' ───────────────────────────────────────────── |
| 44 | +' BroadPhase interface (nested in BaseGeometry) |
| 45 | +' ───────────────────────────────────────────── |
| 46 | +abstract class "BaseGeometry::BroadPhase" as BroadPhase <<abstract>> { |
| 47 | + + l_geometry : SingleLink<BaseGeometry> |
| 48 | + + init() |
| 49 | + + {abstract} getNbox() : Vec3i |
| 50 | + + {abstract} getBoxCoord(P : Vec3) : Vec3i |
| 51 | + + {abstract} getElementSet(i, j, k) : set<BaseElement> |
| 52 | + + getTypeInfo() : type_info& |
| 53 | + + {abstract} initBroadPhase() |
| 54 | + + {abstract} updateBroadPhase() |
| 55 | +} |
| 56 | + |
| 57 | +note right of BroadPhase |
| 58 | + Nested class inside BaseGeometry. |
| 59 | + Registered as a slave object — |
| 60 | + BaseGeometry::setBroadPhase() |
| 61 | + calls initBroadPhase() on attachment. |
| 62 | +end note |
| 63 | + |
| 64 | +BaseObject <|-- BroadPhase |
| 65 | +BroadPhase ..> BaseGeometry : l_geometry > |
| 66 | +BroadPhase ..> BaseElement : getElementSet() returns set of > |
| 67 | + |
| 68 | +' ───────────────────────────────────────────── |
| 69 | +' AABB base |
| 70 | +' ───────────────────────────────────────────── |
| 71 | +abstract class BaseAABBBroadPhase <<abstract>> { |
| 72 | + + d_nbox : Data<Vec3i> ' default (8,8,8) |
| 73 | + + d_static : Data<bool> ' skip update if true |
| 74 | + + d_method : Data<int> ' 0=project, 1=SAT, 2=bbox |
| 75 | + + d_thread : Data<int> ' thread count for method 2 |
| 76 | + + getBBox() : BoundingBox |
| 77 | + + getMin() : Vec3 |
| 78 | + + getMax() : Vec3 |
| 79 | + + getCellSize() : Vec3 |
| 80 | + + getBoxCoord(P) : Vec3i |
| 81 | + + getNbox() : Vec3i |
| 82 | + + initBroadPhase() |
| 83 | + + updateBroadPhase() |
| 84 | + + doUpdate() |
| 85 | + + updateElemInBoxes() |
| 86 | + + projectElemOnBoxes() ' method 0 |
| 87 | + + boxTriangleSAT() ' method 1 |
| 88 | + + bboxIntersection() ' method 2 — multithreaded |
| 89 | + + {abstract} newContainer() |
| 90 | + + {abstract} addElement(i, j, k, elmt) |
| 91 | + + {abstract} updateData() |
| 92 | + # m_Bmin : Vec3 |
| 93 | + # m_Bmax : Vec3 |
| 94 | + # m_cellSize : Vec3 |
| 95 | + # m_nbox : Vec3i |
| 96 | + # m_data : vector<vector<ELMT_THREAD>> |
| 97 | +} |
| 98 | + |
| 99 | +note right of BaseAABBBroadPhase |
| 100 | + Three strategies to map elements to cells, |
| 101 | + selected at runtime via d_method: |
| 102 | + 0 — project element centre, check residual |
| 103 | + 1 — SAT-based triangle-box overlap test |
| 104 | + 2 — pure bounding-box intersection (threaded) |
| 105 | +end note |
| 106 | + |
| 107 | +BroadPhase <|-- BaseAABBBroadPhase |
| 108 | + |
| 109 | +' ───────────────────────────────────────────── |
| 110 | +' Concrete implementations |
| 111 | +' ───────────────────────────────────────────── |
| 112 | +class AABBBroadPhase { |
| 113 | + + updateData() |
| 114 | + + getElementSet(i, j, k) : set<BaseElement> |
| 115 | + + newContainer() |
| 116 | + + addElement(i, j, k, elmt) |
| 117 | + + getKey(i, j, k) : Index |
| 118 | + + getIKey(key) : unsigned |
| 119 | + + getJKey(key) : unsigned |
| 120 | + + getKKey(key) : unsigned |
| 121 | + # m_indexedElement : map<unsigned, set<BaseElement>> |
| 122 | + # m_offset : Vec<2, size_t> |
| 123 | +} |
| 124 | + |
| 125 | +class FullAABBBroadPhase { |
| 126 | + + updateData() ' no-op — no offset needed |
| 127 | + + getElementSet(i, j, k) : set<BaseElement> |
| 128 | + + newContainer() |
| 129 | + + addElement(i, j, k, elmt) |
| 130 | + # m_indexedElement : vector<vector<vector<set<BaseElement>>>> |
| 131 | + # m_offset : Vec<2, size_t> |
| 132 | +} |
| 133 | + |
| 134 | +note bottom of AABBBroadPhase |
| 135 | + Sparse storage: hash map keyed by |
| 136 | + a linearised (i,j,k) index. |
| 137 | + Good for scenes where most cells are empty. |
| 138 | +end note |
| 139 | + |
| 140 | +note bottom of FullAABBBroadPhase |
| 141 | + Dense storage: pre-allocated 3D vector. |
| 142 | + O(1) lookup at the cost of memory for |
| 143 | + all nbox[0]×nbox[1]×nbox[2] cells. |
| 144 | +end note |
| 145 | + |
| 146 | +BaseAABBBroadPhase <|-- AABBBroadPhase |
| 147 | +BaseAABBBroadPhase <|-- FullAABBBroadPhase |
| 148 | + |
| 149 | +@enduml |
0 commit comments