Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
PointColDetector.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3
4 Copyright 2009 Lefteris Stamatogiannakis
5
6 For more information, see http://www.rigsofrods.org/
7
8 Rigs of Rods is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 3, as
10 published by the Free Software Foundation.
11
12 Rigs of Rods is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "PointColDetector.h"
22
23#include "Actor.h"
24#include "ActorManager.h"
25#include "GameContext.h"
26
27using namespace Ogre;
28using namespace RoR;
29
31{
32 int contacters_size = contactables ? m_actor->ar_num_contactable_nodes : m_actor->ar_num_contacters;
33
34 if (contacters_size != m_object_list_size)
35 {
38 m_object_list_size = contacters_size;
40 }
41 else
42 {
44 }
45
46 m_kdtree[0].refid = REFELEMID_INVALID;
47 m_kdtree[0].begin = 0;
49}
50
52{
53 int contacters_size = 0;
54 std::vector<ActorInstanceID_t> collision_partners;
56 {
57 if (actor != m_actor && (ignorestate || actor->ar_update_physics) &&
58 m_actor->ar_bounding_box.intersects(actor->ar_bounding_box))
59 {
60 collision_partners.push_back(actor->ar_instance_id);
61 bool is_linked = std::find(m_actor->ar_linked_actors.begin(), m_actor->ar_linked_actors.end(), actor) != m_actor->ar_linked_actors.end();
62 contacters_size += is_linked ? actor->ar_num_contacters : actor->ar_num_contactable_nodes;
63 if (m_actor->ar_nodes[0].Velocity.squaredDistance(actor->ar_nodes[0].Velocity) > 16)
64 {
65 for (int i = 0; i < m_actor->ar_num_collcabs; i++)
66 {
69 }
70 for (int i = 0; i < actor->ar_num_collcabs; i++)
71 {
72 actor->ar_intra_collcabrate[i].rate = 0;
73 actor->ar_inter_collcabrate[i].rate = 0;
74 }
75 }
76 }
77 }
78
79 m_actor->ar_collision_relevant = (contacters_size > 0);
80
81 if (collision_partners != m_collision_partners || contacters_size != m_object_list_size)
82 {
83 m_collision_partners = collision_partners;
84 m_object_list_size = contacters_size;
86 }
87 else
88 {
90 }
91
92 m_kdtree[0].refid = REFELEMID_INVALID;
93 m_kdtree[0].begin = 0;
95}
96
98{
101
102 // Insert all contacters into the list of points to consider when building the kdtree
103 int refi = 0;
105 {
106 const ActorPtr& actor = App::GetGameContext()->GetActorManager()->GetActorById(actorid);
107
108 bool is_linked = std::find(m_actor->ar_linked_actors.begin(), m_actor->ar_linked_actors.end(), actor) != m_actor->ar_linked_actors.end();
109 bool internal_collision = !ignoreinternal && ((actorid == m_actor->ar_instance_id) || is_linked);
110 for (int i = 0; i < actor->ar_num_nodes; i++)
111 {
112 if (actor->ar_nodes[i].nd_contacter || (!internal_collision && actor->ar_nodes[i].nd_contactable))
113 {
114 hit_pointid_list[refi].actorid = actor->ar_instance_id;
115 hit_pointid_list[refi].nodenum = static_cast<NodeNum_t>(i);
116 m_ref_list[refi].pidrefid = refi;
117 m_ref_list[refi].setPoint(actor->ar_nodes[i].AbsPosition);
118 refi++;
119 }
120 }
121 }
122
123 m_kdtree.resize(std::max(1.0, std::pow(2, std::ceil(std::log2(m_object_list_size)) + 1)));
124}
125
126void PointColDetector::query(const Vector3 &vec1, const Vector3 &vec2, const Vector3 &vec3, float enlargeBB)
127{
128 m_bbmin = vec1;
129
130 m_bbmin.x = std::min(vec2.x, m_bbmin.x);
131 m_bbmin.x = std::min(vec3.x, m_bbmin.x);
132
133 m_bbmin.y = std::min(vec2.y, m_bbmin.y);
134 m_bbmin.y = std::min(vec3.y, m_bbmin.y);
135
136 m_bbmin.z = std::min(vec2.z, m_bbmin.z);
137 m_bbmin.z = std::min(vec3.z, m_bbmin.z);
138
139 m_bbmin -= enlargeBB;
140
141 m_bbmax = vec1;
142
143 m_bbmax.x = std::max(m_bbmax.x, vec2.x);
144 m_bbmax.x = std::max(m_bbmax.x, vec3.x);
145
146 m_bbmax.y = std::max(m_bbmax.y, vec2.y);
147 m_bbmax.y = std::max(m_bbmax.y, vec3.y);
148
149 m_bbmax.z = std::max(m_bbmax.z, vec2.z);
150 m_bbmax.z = std::max(m_bbmax.z, vec3.z);
151
152 m_bbmax += enlargeBB;
153
154 hit_list.clear();
155 hit_list_actorset.clear();
156 queryrec(0, 0);
157}
158
159void PointColDetector::queryrec(int kdindex, int axis)
160{
161 for (;;)
162 {
163 if (m_kdtree[kdindex].end < 0)
164 {
165 build_kdtree_incr(axis, kdindex);
166 }
167
168 if (m_kdtree[kdindex].refid != REFELEMID_INVALID)
169 {
170 const std::array<float, 3> point = m_ref_list[m_kdtree[kdindex].refid].point;
171 if (point[0] >= m_bbmin.x && point[0] <= m_bbmax.x &&
172 point[1] >= m_bbmin.y && point[1] <= m_bbmax.y &&
173 point[2] >= m_bbmin.z && point[2] <= m_bbmax.z)
174 {
175 hit_list.push_back(m_ref_list[m_kdtree[kdindex].refid].pidrefid);
176 hit_list_actorset.insert(hit_pointid_list[m_ref_list[m_kdtree[kdindex].refid].pidrefid].actorid);
177 }
178 return;
179 }
180
181 if (m_bbmax[axis] >= m_kdtree[kdindex].middle)
182 {
183 if (m_bbmin[axis] > m_kdtree[kdindex].max)
184 {
185 return;
186 }
187
188 int newaxis = axis + 1;
189
190 if (newaxis >= 3)
191 {
192 newaxis = 0;
193 }
194
195 int newindex = kdindex + kdindex + 1;
196
197 if (m_bbmin[axis] <= m_kdtree[kdindex].middle)
198 {
199 queryrec(newindex, newaxis);
200 }
201
202 kdindex = newindex + 1;
203 axis = newaxis;
204 }
205 else
206 {
207 if (m_bbmax[axis] < m_kdtree[kdindex].min)
208 {
209 return;
210 }
211
212 kdindex = 2 * kdindex + 1;
213 axis++;
214
215 if (axis >= 3)
216 {
217 axis = 0;
218 }
219 }
220 }
221}
222
223void PointColDetector::build_kdtree_incr(int axis, int index)
224{
225 int end = -m_kdtree[index].end;
226 m_kdtree[index].end = end;
227 RefelemID_t begin = m_kdtree[index].begin;
228 RefelemID_t median;
229 int slice_size = end - begin;
230 if (slice_size != 1)
231 {
232 int newindex=index+index+1;
233 if (slice_size == 2)
234 {
235 median = begin+1;
236 if (m_ref_list[begin].point[axis] > m_ref_list[median].point[axis])
237 {
238 std::swap(m_ref_list[begin], m_ref_list[median]);
239 }
240
241 m_kdtree[index].min = m_ref_list[begin].point[axis];
242 m_kdtree[index].max = m_ref_list[median].point[axis];
243 m_kdtree[index].middle = m_kdtree[index].max;
244 m_kdtree[index].refid = REFELEMID_INVALID;
245
246 axis++;
247 if (axis >= 3)
248 {
249 axis = 0;
250 }
251
252 m_kdtree[newindex].refid = begin;
253 m_kdtree[newindex].middle = m_ref_list[m_kdtree[newindex].refid].point[axis];
254 m_kdtree[newindex].min = m_kdtree[newindex].middle;
255 m_kdtree[newindex].max = m_kdtree[newindex].middle;
256 m_kdtree[newindex].end = median;
257 newindex++;
258
259 m_kdtree[newindex].refid = median;
260 m_kdtree[newindex].middle = m_ref_list[m_kdtree[newindex].refid].point[axis];
261 m_kdtree[newindex].min = m_kdtree[newindex].middle;
262 m_kdtree[newindex].max = m_kdtree[newindex].middle;
263 m_kdtree[newindex].end = end;
264 return;
265 }
266 else
267 {
268 median = begin + (slice_size / 2);
269 partintwo(begin, median, end, axis, m_kdtree[index].min, m_kdtree[index].max);
270 }
271
272 m_kdtree[index].middle = m_ref_list[median].point[axis];
273 m_kdtree[index].refid = REFELEMID_INVALID;
274
275 m_kdtree[newindex].begin = begin;
276 m_kdtree[newindex].end = -median;
277
278 newindex++;
279 m_kdtree[newindex].begin = median;
280 m_kdtree[newindex].end = -end;
281
282 }
283 else
284 {
285 m_kdtree[index].refid = begin;
286 m_kdtree[index].middle = m_ref_list[m_kdtree[index].refid].point[axis];
287 m_kdtree[index].min = m_kdtree[index].middle;
288 m_kdtree[index].max = m_kdtree[index].middle;
289 }
290}
291
292void PointColDetector::partintwo(const int start, const int median, const int end, const int axis, float &minex, float &maxex)
293{
294 int i, j, l, m;
295 int k = median;
296 l = start;
297 m = end - 1;
298
299 float x = m_ref_list[k].point[axis];
300 while (l < m)
301 {
302 i = l;
303 j = m;
304 while (!(j < k || k < i))
305 {
306 while (m_ref_list[i].point[axis] < x)
307 {
308 i++;
309 }
310 while (x < m_ref_list[j].point[axis])
311 {
312 j--;
313 }
314
315 std::swap(m_ref_list[i], m_ref_list[j]);
316 i++;
317 j--;
318 }
319 if (j < k)
320 {
321 l = i;
322 }
323 if (k < i)
324 {
325 m = j;
326 }
327 x = m_ref_list[k].point[axis];
328 }
329
330 minex = x;
331 maxex = x;
332 for (int i = start; i < median; ++i)
333 {
334 minex = std::min(m_ref_list[i].point[axis], minex);
335 }
336 for (int i = median+1; i < end; ++i)
337 {
338 maxex = std::max(maxex, m_ref_list[i].point[axis]);
339 }
340}
341
343{
344 // Because the reflist contains cached node positions, we must update it on each tick.
345 // To avoid repeated lookup in actormanager, we loop actors first.
346 // ----------------------------------------------------------------------------------
347
349 {
350 for (refelem_t& refelem: m_ref_list)
351 {
352 if (hit_pointid_list[refelem.pidrefid].actorid == actor->ar_instance_id)
353 {
354 refelem.setPoint(actor->ar_nodes[hit_pointid_list[refelem.pidrefid].nodenum].AbsPosition);
355 }
356 }
357 }
358}
Game state manager and message-queue provider.
Ogre::AxisAlignedBox ar_bounding_box
standard bounding box (surrounds all nodes of an actor)
Definition Actor.h:370
collcab_rate_t ar_intra_collcabrate[MAX_CABS]
Definition Actor.h:398
node_t * ar_nodes
Definition Actor.h:330
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition Actor.h:429
ActorPtrVec ar_linked_actors
BEWARE: Includes indirect links, see DetermineLinkedActors(); Other actors linked using 'hooks/ties/r...
Definition Actor.h:519
collcab_rate_t ar_inter_collcabrate[MAX_CABS]
Definition Actor.h:397
int ar_num_collcabs
Definition Actor.h:399
int ar_num_nodes
Definition Actor.h:345
int ar_num_contacters
Total number of nodes which can selfcontact cabs.
Definition Actor.h:383
bool ar_collision_relevant
Physics state;.
Definition Actor.h:552
int ar_num_contactable_nodes
Total number of nodes which can contact ground or cabs.
Definition Actor.h:382
ActorPtrVec & GetActors()
const ActorPtr & GetActorById(ActorInstanceID_t actor_id)
ActorManager * GetActorManager()
void update_structures_for_contacters(bool ignoreinternal)
std::vector< kdnode_t > m_kdtree
std::vector< refelem_t > m_ref_list
void partintwo(const int start, const int median, const int end, const int axis, float &minex, float &maxex)
void queryrec(int kdindex, int axis)
void query(const Ogre::Vector3 &vec1, const Ogre::Vector3 &vec2, const Ogre::Vector3 &vec3, const float enlargeBB)
void UpdateIntraPoint(bool contactables=false)
std::vector< PointidID_t > hit_list
std::vector< ActorInstanceID_t > m_collision_partners
IntraPoint: always just owning actor; InterPoint: all colliding actors.
std::vector< pointid_t > hit_pointid_list
std::unordered_set< ActorInstanceID_t > hit_list_actorset
void UpdateInterPoint(bool ignorestate=false)
void build_kdtree_incr(int axis, int index)
GameContext * GetGameContext()
int ActorInstanceID_t
Unique sequentially generated ID of an actor in session. Use ActorManager::GetActorById()
int RefelemID_t
index to PointColDetector::m_ref_list, use RoR::REFELEMID_INVALID as empty value.
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
static const RefelemID_t REFELEMID_INVALID
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition SimData.h:267
Ogre::Vector3 Velocity
Definition SimData.h:268
bool nd_contacter
Attr; User-defined.
Definition SimData.h:285
bool nd_contactable
Attr; This node will be treated as contacter on inter truck collisions.
Definition SimData.h:286