RigsofRods
Soft-body Physics Simulation
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 
71 int 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 
94 int 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
110 Ogre::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 
134 void 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.setNull();
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 
196 void 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 
205 void 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 
302 void 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 
318 void 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 }
RoR::SkidmarkConfig
< Skidmark config file parser and data container
Definition: Skidmark.h:32
RGN_CONFIG
#define RGN_CONFIG
Definition: Application.h:48
RoR::Skidmark::~Skidmark
virtual ~Skidmark()
Definition: Skidmark.cpp:129
RoR::Skidmark::SkidmarkSegment::groundTexture
std::vector< Ogre::String > groundTexture
Definition: Skidmark.h:77
RoR::SkidmarkConfig::SkidmarkDef::slipFrom
float slipFrom
Minimum slipping velocity.
Definition: Skidmark.h:46
ContentManager.h
RoR::wheel_t
Definition: SimData.h:420
RoR::SkidmarkConfig::SkidmarkDef::slipTo
float slipTo
Maximum slipping velocity.
Definition: Skidmark.h:47
RoR::SkidmarkConfig::getTexture
int getTexture(Ogre::String model, Ogre::String ground, float slip, Ogre::String &texture)
Definition: Skidmark.cpp:94
RoR::SkidmarkConfig::LoadDefaultSkidmarkDefs
void LoadDefaultSkidmarkDefs()
Definition: Skidmark.cpp:34
RoR::SanitizeUtf8String
std::string SanitizeUtf8String(std::string const &str_in)
Definition: Utils.cpp:117
RoR::LogFormat
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
Definition: Application.cpp:424
RoR::Skidmark::SkidmarkSegment::points
std::vector< Ogre::Vector3 > points
Definition: Skidmark.h:75
Utils.h
RoR::Skidmark::SkidmarkSegment
< Also reffered to as 'bucket'
Definition: Skidmark.h:71
Actor.h
RoR::GfxScene::GetSceneManager
Ogre::SceneManager * GetSceneManager()
Definition: GfxScene.h:64
RoR::Skidmark::m_length
int m_length
Definition: Skidmark.h:97
TOSTRING
#define TOSTRING(x)
Definition: Application.h:56
RoR::Skidmark::Skidmark
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
RoR::Skidmark::UpdatePoint
void UpdatePoint(Ogre::Vector3 contact_point, int index, float slip, Ogre::String ground_model_name)
Definition: Skidmark.cpp:205
RoR::Skidmark::SkidmarkSegment::obj
Ogre::ManualObject * obj
Definition: Skidmark.h:73
SimData.h
Core data structures for simulation; Everything affected by by either physics, network or user intera...
GfxScene.h
Application.h
Central state/object manager and communications hub.
RoR::Skidmark::SkidmarkSegment::material
Ogre::MaterialPtr material
Definition: Skidmark.h:74
RoR::Skidmark::m_instance_counter
static int m_instance_counter
Definition: Skidmark.h:90
RoR::Skidmark::SkidmarkSegment::facecounter
int facecounter
Definition: Skidmark.h:80
Skidmark.h
RoR::Skidmark::PopSegment
void PopSegment()
Definition: Skidmark.cpp:177
RoR::Skidmark::LimitObjects
void LimitObjects()
Definition: Skidmark.cpp:188
RoR::SkidmarkConfig::SkidmarkDef::texture
Ogre::String texture
Definition: Skidmark.h:45
RoR::Skidmark::m_tex_coords
static Ogre::Vector2 m_tex_coords[4]
Definition: Skidmark.h:95
RoR::Skidmark::SkidmarkSegment::lastPointAv
Ogre::Vector3 lastPointAv
Definition: Skidmark.h:78
RoR::SkidmarkConfig::SkidmarkDef
Definition: Skidmark.h:42
RoR::Skidmark::AddPoint
void AddPoint(const Ogre::Vector3 &value, Ogre::Real fsize, Ogre::String texture)
Definition: Skidmark.cpp:302
RoR::Skidmark::update
void update(Ogre::Vector3 contact_point, int index, float slip, Ogre::String ground_model_name)
Definition: Skidmark.cpp:318
RoR::Skidmark::AddObject
void AddObject(Ogre::Vector3 start, Ogre::String texture)
Definition: Skidmark.cpp:134
RoR::SkidmarkConfig::ProcessSkidmarkConfLine
int ProcessSkidmarkConfLine(Ogre::StringVector args, Ogre::String model)
Definition: Skidmark.cpp:71
RoR::Skidmark::SkidmarkSegment::faceSizes
std::vector< Ogre::Real > faceSizes
Definition: Skidmark.h:76
RoR::SkidmarkConfig::SkidmarkDef::ground
Ogre::String ground
Ground model name, see struct ground_model_t
Definition: Skidmark.h:44
RoR::Skidmark::SetPointInt
void SetPointInt(unsigned short index, const Ogre::Vector3 &value, Ogre::Real fsize, Ogre::String texture)
Definition: Skidmark.cpp:196
RoR::SkidmarkConfig::m_models
std::map< Ogre::String, std::vector< SkidmarkDef > > m_models
Definition: Skidmark.h:52
RoR::Skidmark::SkidmarkSegment::pos
int pos
Definition: Skidmark.h:79
RoR::Log
void Log(const char *msg)
The ultimate, application-wide logging function. Adds a line (any length) in 'RoR....
Definition: Application.cpp:419
RoR::App::GetGfxScene
GfxScene * GetGfxScene()
Definition: Application.cpp:276
RoR::Skidmark::reset
void reset()
Definition: Skidmark.cpp:312