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
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
33using namespace Ogre;
34using namespace RoR;
35
37
49static 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
88static 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
97void 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
118void 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
210void 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}
Central state/object manager and communications hub.
float sign(const float x)
Definition ApproxMath.h:140
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...
static bool InsideTriangleTest(const CartesianToTriangleTransform::TriangleCoord &local, const float margin)
Test if a point given in triangle local coordinates lies within the triangle itself.
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.
Game state manager and message-queue provider.
Core data structures for simulation; Everything affected by by either physics, network or user intera...
Defines a linear transformation from cartesian coordinates to local (barycentric) coordinates of a sp...
node_t * ar_nodes
Definition Actor.h:330
ActorState ar_state
Definition Actor.h:518
std::vector< std::vector< int > > ar_node_to_node_connections
Definition Actor.h:375
const ActorPtr & GetActorById(ActorInstanceID_t actor_id)
ActorManager * GetActorManager()
void query(const Ogre::Vector3 &vec1, const Ogre::Vector3 &vec2, const Ogre::Vector3 &vec3, const float enlargeBB)
std::vector< PointidID_t > hit_list
std::vector< pointid_t > hit_pointid_list
std::unordered_set< ActorInstanceID_t > hit_list_actorset
Represents a triangle in three-dimensional space.
Definition Triangle.h:36
Ogre::Vector3 normal() const
Return normal vector of the triangle.
Definition Triangle.h:48
Ogre::Vector3 primitiveCollision(node_t *node, Ogre::Vector3 velocity, float mass, Ogre::Vector3 normal, float dt, ground_model_t *gm, float penetration=0)
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)
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)
GameContext * GetGameContext()
int ActorInstanceID_t
Unique sequentially generated ID of an actor in session. Use ActorManager::GetActorById()
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
int PointidID_t
index to PointColDetector::hit_pointid_list, use RoR::POINTIDID_INVALID as empty value.
Return type of CartesianToTriangleTransform transformation.
const struct CartesianToTriangleTransform::TriangleCoord::@9 barycentric
const Ogre::Real distance
Shortest (signed) distance to triangle plane.
Surface friction properties.
Definition SimData.h:704
Physics: A vertex in the softbody structure.
Definition SimData.h:260
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition SimData.h:267
Ogre::Real mass
Definition SimData.h:271
Ogre::Vector3 Velocity
Definition SimData.h:268
bool nd_has_mesh_contact
Physics state.
Definition SimData.h:288
Ogre::Vector3 Forces
Definition SimData.h:269
ground_model_t * nd_last_collision_gm
Physics state; last collision 'ground model' (surface definition)
Definition SimData.h:300
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:284