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
Skidmark.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
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 "Skidmark.h"
22
23#include "Actor.h"
24#include "Application.h"
25#include "SimData.h"
26#include "ContentManager.h" // RGN_CONFIG
27#include "GfxScene.h"
28#include "Utils.h"
29
30#include <Ogre.h>
31
33
35{
36 LOG("[RoR] Loading skidmarks.cfg...");
37 try
38 {
39 Ogre::DataStreamPtr ds = Ogre::ResourceGroupManager::getSingleton().openResource("skidmarks.cfg", RGN_CONFIG);
40 Ogre::String currentModel = "";
41
42 while (!ds->eof())
43 {
44 Ogre::String line = SanitizeUtf8String(ds->getLine());
45 Ogre::StringUtil::trim(line);
46
47 if (line.empty() || line[0] == ';')
48 continue;
49
50 Ogre::StringVector args = Ogre::StringUtil::split(line, ",");
51
52 if (args.size() == 1)
53 {
54 currentModel = line;
55 continue;
56 }
57
58 // process the line if we got a model
59 if (currentModel != "")
60 this->ProcessSkidmarkConfLine(args, currentModel);
61 }
62 RoR::Log("[RoR] skidmarks.cfg loaded OK");
63 }
64 catch (Ogre::Exception& e)
65 {
66 RoR::LogFormat("[RoR] Error loading skidmarks.cfg (%s)", e.getFullDescription().c_str());
67 m_models.clear(); // Delete anything we might have loaded
68 }
69}
70
71int RoR::SkidmarkConfig::ProcessSkidmarkConfLine(Ogre::StringVector args, Ogre::String modelName)
72{
73 // we only accept 4 arguments
74 if (args.size() != 4)
75 return 1;
76
77 // parse the data
78 SkidmarkDef cfg;
79 cfg.ground = args[0];
80 Ogre::StringUtil::trim(cfg.ground);
81 cfg.texture = args[1];
82 Ogre::StringUtil::trim(cfg.texture);
83
84 cfg.slipFrom = Ogre::StringConverter::parseReal(args[2]);
85 cfg.slipTo = Ogre::StringConverter::parseReal(args[3]);
86
87 if (!m_models.size() || m_models.find(modelName) == m_models.end())
88 m_models[modelName] = std::vector<SkidmarkDef>();
89
90 m_models[modelName].push_back(cfg);
91 return 0;
92}
93
94int RoR::SkidmarkConfig::getTexture(Ogre::String model, Ogre::String ground, float slip, Ogre::String& texture)
95{
96 if (m_models.find(model) == m_models.end())
97 return 1;
98 for (std::vector<SkidmarkDef>::iterator it = m_models[model].begin(); it != m_models[model].end(); it++)
99 {
100 if (it->ground == ground && it->slipFrom <= slip && it->slipTo > slip)
101 {
102 texture = it->texture.c_str();
103 return 0;
104 }
105 }
106 return 2;
107}
108
109// this is a hardcoded array which we use to map ground types to a certain texture with UV/ coords
110Ogre::Vector2 RoR::Skidmark::m_tex_coords[4] = {Ogre::Vector2(0, 0), Ogre::Vector2(0, 1), Ogre::Vector2(1, 0), Ogre::Vector2(1, 1)};
111
113 Ogre::SceneNode* snode, int m_length /* = 500 */, int m_bucket_count /* = 20 */)
114 : m_scene_node(snode)
115 , m_is_dirty(true)
116 , m_length(m_length)
117 , m_bucket_count(m_bucket_count)
118 , m_wheel(m_wheel)
119 , m_min_distance(0.25f)
120 , m_max_distance(std::max(0.5f, m_wheel->wh_width * 1.1f))
121 , m_config(config)
122{
123 if (m_length % 2)
124 {
125 m_length--;
126 }
127}
128
130{
131 this->reset();
132}
133
134void RoR::Skidmark::AddObject(Ogre::Vector3 start, Ogre::String texture)
135{
136 SkidmarkSegment skid;
137 skid.pos = 0;
138 skid.lastPointAv = start;
139 skid.facecounter = 0;
140
141 // new material
142 char bname[256] = "";
143 sprintf(bname, "mat-skidmark-%d", m_instance_counter);
144 skid.material = Ogre::MaterialManager::getSingleton().create(bname, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
145 Ogre::Pass* p = skid.material->getTechnique(0)->getPass(0);
146
147 p->createTextureUnitState(texture);
148 p->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
149 p->setLightingEnabled(false);
150 p->setDepthWriteEnabled(false);
151 p->setDepthBias(3, 3);
152 p->setCullingMode(Ogre::CULL_NONE);
153
154 skid.points.resize(m_length);
155 skid.faceSizes.resize(m_length);
156 skid.groundTexture.resize(m_length);
157 skid.obj = App::GetGfxScene()->GetSceneManager()->createManualObject("skidmark" + TOSTRING(m_instance_counter++));
158 skid.obj->setDynamic(true);
159 skid.obj->setRenderingDistance(800); // 800m view distance
160 skid.obj->begin(bname, Ogre::RenderOperation::OT_TRIANGLE_STRIP);
161 for (int i = 0; i < m_length; i++)
162 {
163 skid.points[i] = start;
164 skid.faceSizes[i] = 0;
165 skid.groundTexture[i] = "0.png";
166 skid.obj->position(start);
167 skid.obj->textureCoord(0, 0);
168 }
169 skid.obj->end();
170 m_scene_node->attachObject(skid.obj);
171
172 m_objects.push(skid);
173
174 this->LimitObjects();
175}
176
178{
179 SkidmarkSegment& skid = m_objects.front();
180 skid.points.clear();
181 skid.faceSizes.clear();
182 Ogre::MaterialManager::getSingleton().remove(skid.material->getName());
183 skid.material.reset();
184 App::GetGfxScene()->GetSceneManager()->destroyManualObject(skid.obj);
185 m_objects.pop();
186}
187
189{
190 if ((int)m_objects.size() > m_bucket_count)
191 {
192 this->PopSegment();
193 }
194}
195
196void RoR::Skidmark::SetPointInt(unsigned short index, const Ogre::Vector3& value, Ogre::Real fsize, Ogre::String texture)
197{
198 m_objects.back().points[index] = value;
199 m_objects.back().faceSizes[index] = fsize;
200 m_objects.back().groundTexture[index] = texture;
201
202 m_is_dirty = true;
203}
204
205void RoR::Skidmark::UpdatePoint(Ogre::Vector3 contact_point, int index, float slip, Ogre::String ground_model_name)
206{
207 Ogre::Vector3 thisPoint = contact_point;
208 Ogre::Vector3 axis = m_wheel->wh_axis_node_1->RelPosition - m_wheel->wh_axis_node_0->RelPosition;
209 if (index % 2)
210 {
211 axis = -axis;
212 }
213 Ogre::Vector3 thisPointAV = thisPoint + axis * 0.5f;
214 Ogre::Real distance = 0;
215 Ogre::Real maxDist = m_max_distance;
216 Ogre::String texture = "none";
217 m_config->getTexture("default", ground_model_name, slip, texture);
218
219 // dont add points with no texture
220 if (texture == "none")
221 return;
222
223 if (m_wheel->wh_speed > 1)
224 maxDist *= m_wheel->wh_speed;
225
226 if (!m_objects.size())
227 {
228 // add first bucket
229 this->AddObject(thisPoint, texture);
230 }
231 else
232 {
233 // check existing buckets
234 SkidmarkSegment skid = m_objects.back();
235
236 distance = skid.lastPointAv.distance(thisPointAV);
237 // too near to update?
238 if (distance < m_min_distance)
239 {
240 //LOG("E: too near for update");
241 return;
242 }
243
244 // change ground texture if required
245 if (skid.pos > 0 && skid.groundTexture[0] != texture)
246 {
247 // new object with new texture
248 if (distance > maxDist)
249 {
250 // to far away for connection
251 this->AddObject(thisPoint, texture);
252 }
253 else
254 {
255 // add new bucket with connection to last bucket
256 Ogre::Vector3 lp1 = m_objects.back().points[m_objects.back().pos - 1];
257 Ogre::Vector3 lp2 = m_objects.back().points[m_objects.back().pos - 2];
258 this->AddObject(lp1, texture);
259 this->AddPoint(lp2, distance, texture);
260 this->AddPoint(lp1, distance, texture);
261 }
262 }
263 else
264 {
265 // no texture change required :D
266
267 // far enough for new bucket?
268 if (skid.pos >= (int)skid.points.size())
269 {
270 if (distance > maxDist)
271 {
272 // to far away for connection
273 this->AddObject(thisPoint, texture);
274 }
275 else
276 {
277 // add new bucket with connection to last bucket
278 Ogre::Vector3 lp1 = m_objects.back().points[m_objects.back().pos - 1];
279 Ogre::Vector3 lp2 = m_objects.back().points[m_objects.back().pos - 2];
280 this->AddObject(lp1, texture);
281 this->AddPoint(lp2, distance, texture);
282 this->AddPoint(lp1, distance, texture);
283 }
284 }
285 else if (distance > m_max_distance)
286 {
287 // just new bucket, no connection to last bucket
288 this->AddObject(thisPoint, texture);
289 }
290 }
291 }
292
293 const float overaxis = 0.2f;
294
295 this->AddPoint(contact_point - (axis * overaxis), distance, texture);
296 this->AddPoint(contact_point + axis + (axis * overaxis), distance, texture);
297
298 // save as last point (in the middle of the m_wheel)
299 m_objects.back().lastPointAv = thisPointAV;
300}
301
302void RoR::Skidmark::AddPoint(const Ogre::Vector3& value, Ogre::Real fsize, Ogre::String texture)
303{
304 if (m_objects.back().pos >= m_length)
305 {
306 return;
307 }
308 this->SetPointInt(m_objects.back().pos, value, fsize, texture);
309 m_objects.back().pos++;
310}
311
313{
314 while (m_objects.size() != 0) // Remove all skid segments
315 this->PopSegment();
316}
317
318void RoR::Skidmark::update(Ogre::Vector3 contact_point, int index, float slip, Ogre::String ground_model_name)
319{
320 this->UpdatePoint(contact_point, index, slip, ground_model_name);
321 if (!m_is_dirty)
322 return;
323 if (!m_objects.size())
324 return;
325 SkidmarkSegment skid = m_objects.back();
326 Ogre::Vector3 vaabMin = skid.points[0];
327 Ogre::Vector3 vaabMax = skid.points[0];
328 skid.obj->beginUpdate(0);
329 bool behindEnd = false;
330 Ogre::Vector3 lastValid = Ogre::Vector3::ZERO;
331 int to_counter = 0;
332 float tcox_counter = 0;
333
334 for (int i = 0; i < m_length; i++ , to_counter++)
335 {
336 if (i >= skid.pos)
337 behindEnd = true;
338
339 if (to_counter > 3)
340 to_counter = 0;
341
342 if (!behindEnd)
343 tcox_counter += skid.faceSizes[i] / m_min_distance;
344
345 while (tcox_counter > 1)
346 tcox_counter--;
347
348 if (behindEnd)
349 {
350 skid.obj->position(lastValid);
351 skid.obj->textureCoord(0, 0);
352 }
353 else
354 {
355 skid.obj->position(skid.points[i]);
356
357 Ogre::Vector2 tco = m_tex_coords[to_counter];
358 tco.x *= skid.faceSizes[i] / m_min_distance; // scale texture according face size
359 skid.obj->textureCoord(tco);
360
361 lastValid = skid.points[i];
362 }
363
364 if (skid.points[i].x < vaabMin.x)
365 vaabMin.x = skid.points[i].x;
366 if (skid.points[i].y < vaabMin.y)
367 vaabMin.y = skid.points[i].y;
368 if (skid.points[i].z < vaabMin.z)
369 vaabMin.z = skid.points[i].z;
370 if (skid.points[i].x > vaabMax.x)
371 vaabMax.x = skid.points[i].x;
372 if (skid.points[i].y > vaabMax.y)
373 vaabMax.y = skid.points[i].y;
374 if (skid.points[i].z > vaabMax.z)
375 vaabMax.z = skid.points[i].z;
376 }
377 skid.obj->end();
378
379 skid.obj->setBoundingBox(Ogre::AxisAlignedBox(vaabMin, vaabMax));
380
381 m_is_dirty = false;
382}
Central state/object manager and communications hub.
#define RGN_CONFIG
Definition Application.h:49
#define TOSTRING(x)
Definition Application.h:57
void LOG(const char *msg)
Legacy alias - formerly a macro.
Core data structures for simulation; Everything affected by by either physics, network or user intera...
Ogre::SceneManager * GetSceneManager()
Definition GfxScene.h:83
< Skidmark config file parser and data container
Definition Skidmark.h:33
int ProcessSkidmarkConfLine(Ogre::StringVector args, Ogre::String model)
Definition Skidmark.cpp:71
void LoadDefaultSkidmarkDefs()
Definition Skidmark.cpp:34
std::map< Ogre::String, std::vector< SkidmarkDef > > m_models
Definition Skidmark.h:52
int getTexture(Ogre::String model, Ogre::String ground, float slip, Ogre::String &texture)
Definition Skidmark.cpp:94
void AddPoint(const Ogre::Vector3 &value, Ogre::Real fsize, Ogre::String texture)
Definition Skidmark.cpp:302
void AddObject(Ogre::Vector3 start, Ogre::String texture)
Definition Skidmark.cpp:134
static Ogre::Vector2 m_tex_coords[4]
Definition Skidmark.h:110
void SetPointInt(unsigned short index, const Ogre::Vector3 &value, Ogre::Real fsize, Ogre::String texture)
Definition Skidmark.cpp:196
static int m_instance_counter
Definition Skidmark.h:90
virtual ~Skidmark()
Definition Skidmark.cpp:129
Skidmark(SkidmarkConfig *config, wheel_t *m_wheel, Ogre::SceneNode *snode, int m_length=500, int m_bucket_count=20)
Constructor - see setOperationType() for description of argument.
Definition Skidmark.cpp:112
void UpdatePoint(Ogre::Vector3 contact_point, int index, float slip, Ogre::String ground_model_name)
Definition Skidmark.cpp:205
void LimitObjects()
Definition Skidmark.cpp:188
void update(Ogre::Vector3 contact_point, int index, float slip, Ogre::String ground_model_name)
Definition Skidmark.cpp:318
void PopSegment()
Definition Skidmark.cpp:177
GfxScene * GetGfxScene()
void Log(const char *msg)
The ultimate, application-wide logging function. Adds a line (any length) in 'RoR....
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
std::string SanitizeUtf8String(std::string const &str_in)
Definition Utils.cpp:120
< Also reffered to as 'bucket'
Definition Skidmark.h:72
std::vector< Ogre::String > groundTexture
Definition Skidmark.h:77
Ogre::MaterialPtr material
Definition Skidmark.h:74
std::vector< Ogre::Real > faceSizes
Definition Skidmark.h:76
std::vector< Ogre::Vector3 > points
Definition Skidmark.h:75
Ogre::ManualObject * obj
Definition Skidmark.h:73
float slipTo
Maximum slipping velocity.
Definition Skidmark.h:47
Ogre::String ground
Ground model name, see struct ground_model_t
Definition Skidmark.h:44
float slipFrom
Minimum slipping velocity.
Definition Skidmark.h:46