RigsofRods
Soft-body Physics Simulation
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 
27 using namespace Ogre;
28 using namespace RoR;
29 
30 void PointColDetector::UpdateIntraPoint(bool contactables)
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  {
36  m_collision_partners.clear();
37  m_collision_partners.push_back(m_actor->ar_instance_id);
38  m_object_list_size = contacters_size;
39  update_structures_for_contacters(contactables);
40  }
41  else
42  {
43  refresh_node_positions();
44  }
45 
46  m_kdtree[0].refid = REFELEMID_INVALID;
47  m_kdtree[0].begin = 0;
48  m_kdtree[0].end = -m_object_list_size;
49 }
50 
51 void PointColDetector::UpdateInterPoint(bool ignorestate)
52 {
53  int contacters_size = 0;
54  std::vector<ActorInstanceID_t> collision_partners;
55  for (ActorPtr& actor : App::GetGameContext()->GetActorManager()->GetActors())
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  {
67  m_actor->ar_intra_collcabrate[i].rate = 0;
68  m_actor->ar_inter_collcabrate[i].rate = 0;
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;
85  update_structures_for_contacters(false);
86  }
87  else
88  {
89  refresh_node_positions();
90  }
91 
92  m_kdtree[0].refid = REFELEMID_INVALID;
93  m_kdtree[0].begin = 0;
94  m_kdtree[0].end = -m_object_list_size;
95 }
96 
97 void PointColDetector::update_structures_for_contacters(bool ignoreinternal)
98 {
99  m_ref_list.resize(m_object_list_size);
100  hit_pointid_list.resize(m_object_list_size);
101 
102  // Insert all contacters into the list of points to consider when building the kdtree
103  int refi = 0;
104  for (ActorInstanceID_t actorid : m_collision_partners)
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 
126 void 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 
159 void 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 
223 void 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 
292 void 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 
342 void PointColDetector::refresh_node_positions()
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 
348  for (ActorPtr& actor: App::GetGameContext()->GetActorManager()->GetActors())
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 }
GameContext.h
Game state manager and message-queue provider.
RoR::node_t::nd_contacter
bool nd_contacter
Attr; User-defined.
Definition: SimData.h:311
RoR::node_t::AbsPosition
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition: SimData.h:293
RoR::Actor::ar_linked_actors
ActorPtrVec ar_linked_actors
BEWARE: Includes indirect links, see DetermineLinkedActors(); Other actors linked using 'hooks/ties/r...
Definition: Actor.h:441
RoR::Actor::ar_instance_id
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition: Actor.h:370
RoR::Actor::ar_num_nodes
int ar_num_nodes
Definition: Actor.h:277
RoR::RefelemID_t
int RefelemID_t
index to PointColDetector::m_ref_list, use RoR::REFELEMID_INVALID as empty value.
Definition: ForwardDeclarations.h:46
RoR::node_t::nd_contactable
bool nd_contactable
Attr; This node will be treated as contacter on inter truck collisions.
Definition: SimData.h:312
RefCountingObjectPtr< Actor >
ActorManager.h
Actor.h
RoR::ActorInstanceID_t
int ActorInstanceID_t
Unique sequentially generated ID of an actor in session. Use ActorManager::GetActorById()
Definition: ForwardDeclarations.h:37
RoR::NodeNum_t
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
Definition: ForwardDeclarations.h:52
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:280
PointColDetector.h
RoR::Actor::ar_nodes
node_t * ar_nodes
Definition: Actor.h:273
RoR::REFELEMID_INVALID
static const RefelemID_t REFELEMID_INVALID
Definition: ForwardDeclarations.h:47
Ogre
Definition: ExtinguishableFireAffector.cpp:35
RoR::PointColDetector::refelem_t
Definition: PointColDetector.h:54
RoR
Definition: AppContext.h:36
RoR::ActorManager::GetActorById
const ActorPtr & GetActorById(ActorInstanceID_t actor_id)
Definition: ActorManager.cpp:1146
x
float x
Definition: (ValueTypes) quaternion.h:5
RoR::GameContext::GetActorManager
ActorManager * GetActorManager()
Definition: GameContext.h:127