RigsofRods
Soft-body Physics Simulation
DynamicCollisions.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2005-2012 Pierre-Michel Ricordel
4  Copyright 2007-2012 Thomas Fischer
5  Copyright 2016 Fabian Killus
6 
7  For more information, see http://www.rigsofrods.org/
8 
9  Rigs of Rods is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License version 3, as
11  published by the Free Software Foundation.
12 
13  Rigs of Rods is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "DynamicCollisions.h"
23 
24 #include "Application.h"
25 #include "Actor.h"
26 #include "SimData.h"
28 #include "Collisions.h"
29 #include "GameContext.h"
30 #include "PointColDetector.h"
31 #include "Triangle.h"
32 
33 using namespace Ogre;
34 using namespace RoR;
35 
37 
49 static bool BackfaceCollisionTest(const float distance,
50  const Vector3 &normal,
51  const node_t &surface_point,
52  const std::vector<int> &neighbour_node_ids,
53  const node_t nodes[])
54 {
55  auto sign = [](float x){ return (x >= 0) ? 1 : -1; };
56 
57  // Summarize over the collision node and its connected neighbour nodes to infer on which side
58  // of the collision plane most of these nodes are located.
59  // Nodes in front contribute positively, nodes on the backface contribute negatively.
60 
61  // the contribution of the collision node itself has triple weight in this heuristic
62  const int weight = 3;
63  int face_indicator = weight * sign(distance);
64 
65  // calculate the contribution of neighbouring nodes (if it can still change the final outcome)
66  if (neighbour_node_ids.size() > weight) {
67  for (auto id : neighbour_node_ids) {
68  const auto neighbour_distance = normal.dotProduct(nodes[id].AbsPosition - surface_point.AbsPosition);
69  face_indicator += sign(neighbour_distance);
70  }
71  }
72 
73  // a negative sum indicates that the collision is occuring on the backface
74  return (face_indicator < 0);
75 }
76 
77 
79 
88 static bool InsideTriangleTest(const CartesianToTriangleTransform::TriangleCoord &local, const float margin)
89 {
90  const auto coord = local.barycentric;
91  const auto distance = local.distance;
92  return (coord.alpha >= 0) && (coord.beta >= 0) && (coord.gamma >= 0) && (std::abs(distance) <= margin);
93 }
94 
95 
97 void ResolveCollisionForces(const float penetration_depth,
98  node_t &hitnode, node_t &na, node_t &nb, node_t &no,
99  const float alpha, const float beta, const float gamma,
100  const Vector3 &normal,
101  const float dt,
102  const bool remote,
103  ground_model_t &submesh_ground_model)
104 {
105  const auto velocity = hitnode.Velocity - (na.Velocity * alpha + nb.Velocity * beta + no.Velocity * gamma);
106  const float tr_mass = na.mass * alpha + nb.mass * beta + no.mass * gamma;
107  const float mass = remote ? hitnode.mass : (hitnode.mass * tr_mass) / (hitnode.mass + tr_mass);
108 
109  auto forcevec = primitiveCollision(&hitnode, velocity, mass, normal, dt, &submesh_ground_model, penetration_depth);
110 
111  hitnode.Forces += forcevec;
112  na.Forces -= forcevec * alpha;
113  nb.Forces -= forcevec * beta;
114  no.Forces -= forcevec * gamma;
115 }
116 
117 
118 void RoR::ResolveInterActorCollisions(const float dt, PointColDetector &interPointCD,
119  const int free_collcab, int collcabs[], int cabs[],
120  collcab_rate_t inter_collcabrate[], node_t nodes[],
121  const float collrange,
122  ground_model_t &submesh_ground_model)
123 {
124  for (int i=0; i<free_collcab; i++)
125  {
126  if (inter_collcabrate[i].rate > 0)
127  {
128  inter_collcabrate[i].distance++;
129  inter_collcabrate[i].rate--;
130  continue;
131  }
132  inter_collcabrate[i].rate = std::min(inter_collcabrate[i].distance, 12);
133  inter_collcabrate[i].distance = 0;
134 
135  int tmpv = collcabs[i]*3;
136  const auto no = &nodes[cabs[tmpv]];
137  const auto na = &nodes[cabs[tmpv+1]];
138  const auto nb = &nodes[cabs[tmpv+2]];
139 
140  interPointCD.query(no->AbsPosition
141  , na->AbsPosition
142  , nb->AbsPosition, collrange);
143 
144  if (!interPointCD.hit_list.empty())
145  {
146  // setup transformation of points to triangle local coordinates
147  const Triangle triangle(na->AbsPosition, nb->AbsPosition, no->AbsPosition);
148  const CartesianToTriangleTransform transform(triangle);
149 
150  // To avoid repeated lookups in ActorManager, we loop actors first and skip non-matching hits.
151  for (ActorInstanceID_t actorid: interPointCD.hit_list_actorset)
152  {
153  const ActorPtr& hit_actor = App::GetGameContext()->GetActorManager()->GetActorById(actorid);
154  for (PointidID_t h : interPointCD.hit_list)
155  {
156  // skip hits from other actors
157  if (interPointCD.hit_pointid_list[h].actorid != actorid)
158  continue;
159 
160  NodeNum_t hitnode_num = interPointCD.hit_pointid_list[h].nodenum;
161  node_t& hitnode = hit_actor->ar_nodes[hitnode_num];
162 
163  // transform point to triangle local coordinates
164  const auto local_point = transform(hitnode.AbsPosition);
165 
166  // collision test
167  const bool is_colliding = InsideTriangleTest(local_point, collrange);
168  if (is_colliding)
169  {
170  inter_collcabrate[i].rate = 0;
171 
172  const auto coord = local_point.barycentric;
173  auto distance = local_point.distance;
174  auto normal = triangle.normal();
175 
176  // adapt in case the collision is occuring on the backface of the triangle
177  const auto neighbour_node_ids = hit_actor->ar_node_to_node_connections[hitnode_num];
178  const bool is_backface = BackfaceCollisionTest(distance, normal, *no, neighbour_node_ids, hit_actor->ar_nodes);
179  if (is_backface)
180  {
181  // flip surface normal and distance to triangle plane
182  normal = -normal;
183  distance = -distance;
184  }
185 
186  const auto penetration_depth = collrange - distance;
187 
188  const bool remote = (hit_actor->ar_state == ActorState::NETWORKED_OK);
189 
190  ResolveCollisionForces(penetration_depth, hitnode, *na, *nb, *no, coord.alpha,
191  coord.beta, coord.gamma, normal, dt, remote, submesh_ground_model);
192 
193  hitnode.nd_last_collision_gm = &submesh_ground_model;
194  hitnode.nd_has_mesh_contact = true;
195  na->nd_has_mesh_contact = true;
196  nb->nd_has_mesh_contact = true;
197  no->nd_has_mesh_contact = true;
198  }
199  }
200  }
201  }
202  else
203  {
204  inter_collcabrate[i].rate++;
205  }
206  }
207 }
208 
209 
210 void RoR::ResolveIntraActorCollisions(const float dt, PointColDetector &intraPointCD,
211  const int free_collcab, int collcabs[], int cabs[],
212  collcab_rate_t intra_collcabrate[], node_t nodes[],
213  const float collrange,
214  ground_model_t &submesh_ground_model)
215 {
216  for (int i=0; i<free_collcab; i++)
217  {
218  if (intra_collcabrate[i].rate > 0)
219  {
220  intra_collcabrate[i].distance++;
221  intra_collcabrate[i].rate--;
222  continue;
223  }
224  if (intra_collcabrate[i].distance > 0)
225  {
226  intra_collcabrate[i].rate = std::min(intra_collcabrate[i].distance, 12);
227  intra_collcabrate[i].distance = 0;
228  }
229 
230  int tmpv = collcabs[i]*3;
231  const auto no = &nodes[cabs[tmpv]];
232  const auto na = &nodes[cabs[tmpv+1]];
233  const auto nb = &nodes[cabs[tmpv+2]];
234 
235  intraPointCD.query(no->AbsPosition
236  , na->AbsPosition
237  , nb->AbsPosition, collrange);
238 
239  bool collision = false;
240 
241  if (!intraPointCD.hit_list.empty())
242  {
243  // setup transformation of points to triangle local coordinates
244  const Triangle triangle(na->AbsPosition, nb->AbsPosition, no->AbsPosition);
245  const CartesianToTriangleTransform transform(triangle);
246 
247  for (PointidID_t h : intraPointCD.hit_list)
248  {
249  NodeNum_t hitnode_num = intraPointCD.hit_pointid_list[h].nodenum;
250  node_t& hitnode = nodes[hitnode_num];
251 
252  //ignore wheel/chassis self contact
253  if (hitnode.nd_tyre_node) continue;
254  if (no == &hitnode || na == &hitnode || nb == &hitnode) continue;
255 
256  // transform point to triangle local coordinates
257  const auto local_point = transform(hitnode.AbsPosition);
258 
259  // collision test
260  const bool is_colliding = InsideTriangleTest(local_point, collrange);
261  if (is_colliding)
262  {
263  collision = true;
264 
265  const auto coord = local_point.barycentric;
266  auto distance = local_point.distance;
267  auto normal = triangle.normal();
268 
269  // adapt in case the collision is occuring on the backface of the triangle
270  if (distance < 0)
271  {
272  // flip surface normal and distance to triangle plane
273  normal = -normal;
274  distance = -distance;
275  }
276 
277  const auto penetration_depth = collrange - distance;
278 
279  ResolveCollisionForces(penetration_depth, hitnode, *na, *nb, *no, coord.alpha,
280  coord.beta, coord.gamma, normal, dt, false, submesh_ground_model);
281  }
282  }
283  }
284 
285  if (collision)
286  {
287  intra_collcabrate[i].rate = -20000;
288  }
289  else
290  {
291  intra_collcabrate[i].rate++;
292  }
293  }
294 }
GameContext.h
Game state manager and message-queue provider.
RoR::collcab_rate_t::rate
int rate
Definition: SimData.h:405
Triangle
Represents a triangle in three-dimensional space.
Definition: Triangle.h:35
Triangle.h
RoR::PointColDetector::hit_pointid_list
std::vector< pointid_t > hit_pointid_list
Definition: PointColDetector.h:44
RoR::primitiveCollision
Ogre::Vector3 primitiveCollision(node_t *node, Ogre::Vector3 velocity, float mass, Ogre::Vector3 normal, float dt, ground_model_t *gm, float penetration=0)
Definition: Collisions.cpp:1228
RoR::node_t::Velocity
Ogre::Vector3 Velocity
Definition: SimData.h:295
ResolveCollisionForces
void ResolveCollisionForces(const float penetration_depth, node_t &hitnode, node_t &na, node_t &nb, node_t &no, const float alpha, const float beta, const float gamma, const Vector3 &normal, const float dt, const bool remote, ground_model_t &submesh_ground_model)
Calculate collision forces and apply them to the collision node and the three vertex nodes of the col...
Definition: DynamicCollisions.cpp:97
RoR::ResolveIntraActorCollisions
void ResolveIntraActorCollisions(const float dt, PointColDetector &intraPointCD, const int free_collcab, int collcabs[], int cabs[], collcab_rate_t intra_collcabrate[], node_t nodes[], const float collrange, ground_model_t &submesh_ground_model)
Definition: DynamicCollisions.cpp:210
sign
float sign(const float x)
Definition: ApproxMath.h:140
RoR::node_t::nd_last_collision_gm
ground_model_t * nd_last_collision_gm
Physics state; last collision 'ground model' (surface definition)
Definition: SimData.h:326
CartesianToTriangleTransform::TriangleCoord::distance
const Ogre::Real distance
Shortest (signed) distance to triangle plane.
Definition: CartesianToTriangleTransform.h:62
RoR::node_t::AbsPosition
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition: SimData.h:294
RoR::PointidID_t
int PointidID_t
index to PointColDetector::hit_pointid_list, use RoR::POINTIDID_INVALID as empty value.
Definition: ForwardDeclarations.h:43
DynamicCollisions.h
Triangle::normal
Ogre::Vector3 normal() const
Return normal vector of the triangle.
Definition: Triangle.h:48
RefCountingObjectPtr< Actor >
InsideTriangleTest
static bool InsideTriangleTest(const CartesianToTriangleTransform::TriangleCoord &local, const float margin)
Test if a point given in triangle local coordinates lies within the triangle itself.
Definition: DynamicCollisions.cpp:88
Actor.h
RoR::collcab_rate_t
Definition: SimData.h:403
RoR::collcab_rate_t::distance
int distance
Definition: SimData.h:406
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
SimData.h
Core data structures for simulation; Everything affected by by either physics, network or user intera...
CartesianToTriangleTransform
Defines a linear transformation from cartesian coordinates to local (barycentric) coordinates of a sp...
Definition: CartesianToTriangleTransform.h:47
RoR::node_t::nd_tyre_node
bool nd_tyre_node
Attr; This node is part of a tyre (note some wheel types don't use rim nodes at all)
Definition: SimData.h:311
Application.h
Central state/object manager and communications hub.
RoR::PointColDetector
Definition: PointColDetector.h:32
RoR::node_t
Physics: A vertex in the softbody structure.
Definition: SimData.h:286
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:280
CartesianToTriangleTransform.h
RoR::PointColDetector::hit_list
std::vector< PointidID_t > hit_list
Definition: PointColDetector.h:42
PointColDetector.h
RoR::Actor::ar_node_to_node_connections
std::vector< std::vector< int > > ar_node_to_node_connections
Definition: Actor.h:314
RoR::node_t::nd_has_mesh_contact
bool nd_has_mesh_contact
Physics state.
Definition: SimData.h:315
RoR::ResolveInterActorCollisions
void ResolveInterActorCollisions(const float dt, PointColDetector &interPointCD, const int free_collcab, int collcabs[], int cabs[], collcab_rate_t inter_collcabrate[], node_t nodes[], const float collrange, ground_model_t &submesh_ground_model)
Definition: DynamicCollisions.cpp:118
RoR::Actor::ar_nodes
node_t * ar_nodes
Definition: Actor.h:279
BackfaceCollisionTest
static bool BackfaceCollisionTest(const float distance, const Vector3 &normal, const node_t &surface_point, const std::vector< int > &neighbour_node_ids, const node_t nodes[])
Determine on which side of a triangle an occuring collision takes place.
Definition: DynamicCollisions.cpp:49
nodes
or anywhere else will not be considered a but parsed as regular data ! Each line is treated as values separated by separators Possible i e animators Multiline description Single does not affect it Directive usualy set global attributes or change behavior of the parsing Directive may appear in any block section Modularity The elements can be grouped into modules Each module must belong to one or more configurations Directives sectionconfig specify truck configurations the user can choose from Exactly one must be selected If the first defined is used lettercase matches original docs(parsing is insensitive). NAME TYPE NOTES advdrag BLOCK add_animation DIRECTIVE Special syntax airbrakes BLOCK animators BLOCK Special syntax IF(values[0]=="") bad trailing chars are silently ignored no space at the end Items delimited On each side of there is max item Empty invalid string parses as node num items Acceptable item the node is the others When a node range has more than nodes
Definition: ReadMe.txt:302
RoR::ground_model_t
Surface friction properties.
Definition: SimData.h:739
RoR::PointColDetector::hit_list_actorset
std::unordered_set< ActorInstanceID_t > hit_list_actorset
Definition: PointColDetector.h:43
Ogre
Definition: ExtinguishableFireAffector.cpp:35
CartesianToTriangleTransform::TriangleCoord::barycentric
const struct CartesianToTriangleTransform::TriangleCoord::@9 barycentric
Collisions.h
RoR::node_t::mass
Ogre::Real mass
Definition: SimData.h:298
RoR::Actor::ar_state
ActorState ar_state
Definition: Actor.h:446
RoR
Definition: AppContext.h:36
RoR::ActorManager::GetActorById
const ActorPtr & GetActorById(ActorInstanceID_t actor_id)
Definition: ActorManager.cpp:1142
x
float x
Definition: (ValueTypes) quaternion.h:5
RoR::GameContext::GetActorManager
ActorManager * GetActorManager()
Definition: GameContext.h:127
CartesianToTriangleTransform::TriangleCoord
Return type of CartesianToTriangleTransform transformation.
Definition: CartesianToTriangleTransform.h:56
RoR::PointColDetector::query
void query(const Ogre::Vector3 &vec1, const Ogre::Vector3 &vec2, const Ogre::Vector3 &vec3, const float enlargeBB)
Definition: PointColDetector.cpp:126
RoR::node_t::Forces
Ogre::Vector3 Forces
Definition: SimData.h:296