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
ODefFileFormat.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2016-2017 Petr Ohlidal
4
5 For more information, see http://www.rigsofrods.org/
6
7 Rigs of Rods is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 3, as
9 published by the Free Software Foundation.
10
11 Rigs of Rods is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "ODefFileFormat.h"
21
22#include "Actor.h"
23#include "Utils.h"
24
25using namespace RoR;
26using namespace Ogre;
27
28const int ODEF_LINE_BUF_LEN = 4000;
29
30void ODefParser::ProcessOgreStream(Ogre::DataStream* stream)
31{
32 char raw_line_buf[ODEF_LINE_BUF_LEN];
33 bool keep_reading = true;
34 while (keep_reading && !stream->eof())
35 {
36 stream->readLine(raw_line_buf, ODEF_LINE_BUF_LEN);
37 keep_reading = this->ProcessLine(raw_line_buf);
38 }
39}
40
41bool ODefParser::ProcessLine(const char* line)
42{
43 bool result = true;
44 if ((line != nullptr) && (line[0] != 0))
45 {
46 m_cur_line = line; // No trimming by design.
47 result = this->ProcessCurrentLine();
48 }
50 return result;
51}
52
54{
55 m_ctx.header_done = false;
56 m_ctx.header_scale = Ogre::Vector3::ZERO;
57 this->ResetCBoxContext();
58 m_def = std::make_shared<ODefDocument>();
59}
60
61std::shared_ptr<ODefDocument> ODefParser::Finalize()
62{
63 std::shared_ptr<ODefDocument> def = m_def; // Pass ownership
64 m_def.reset();
65 return def;
66}
67
68inline bool StartsWith(std::string const & line, const char* test)
69{
70 return line.compare(0, strlen(test), test) == 0;
71}
72
73// retval true = continue processing (false = stop)
75{
76 if (!m_ctx.header_done)
77 {
78 if (strcmp(m_cur_line, "LOD") == 0) // Clone of old parser logic.
79 {
80 return true; // 'LOD line' = obsolete
81 }
82
83 if (m_ctx.header_mesh_name.empty())
84 {
85 m_ctx.header_mesh_name = m_cur_line; // No trimming by design
86 return true;
87 }
88
89 sscanf(m_cur_line, "%f, %f, %f", &m_ctx.header_scale.x, &m_ctx.header_scale.y, &m_ctx.header_scale.z);
90
91 m_def->header.mesh_name = m_ctx.header_mesh_name;
92 m_def->header.scale = m_ctx.header_scale;
93 m_ctx.header_done = true;
94 return true;
95 }
96
97 std::string line_str = m_cur_line;
98 Ogre::StringUtil::trim(line_str);
99 line_str = SanitizeUtf8String(line_str);
100 if ((line_str[0] == 0) || (line_str[0] == '/') || (line_str[0] == ';'))
101 {
102 return true;
103 }
104
105 if (line_str == "end")
106 {
107 return false;
108 }
109
110 if (line_str == "movable")
111 {
112 // Unused keyword
113 }
114 else if (line_str == "standard")
115 {
116 m_def->mode_standard = true;
117 }
118 else if (StartsWith(line_str, "localizer-"))
119 {
120 if (line_str.compare(10, 3, "vor") == 0) { m_def->localizers.push_back(LOCALIZER_VOR ); return true; }
121 if (line_str.compare(10, 3, "ndb") == 0) { m_def->localizers.push_back(LOCALIZER_NDB ); return true; }
122 if (line_str.compare(10, 1, "v" ) == 0) { m_def->localizers.push_back(LOCALIZER_VERTICAL ); return true; }
123 if (line_str.compare(10, 1, "h" ) == 0) { m_def->localizers.push_back(LOCALIZER_HORIZONTAL); return true; }
124
125 LOG("[RoR|ODef] Invalid line: " + line_str);
126 }
127 else if (StartsWith(line_str, "sound"))
128 {
129 char tmp[201] = "";
130 sscanf(line_str.c_str(), "sound %200s", tmp);
131 m_def->sounds.push_back(tmp);
132 }
133 else if (StartsWith(line_str, "particleSystem"))
134 {
135 ODefParticleSys psys;
136 char instance_name[201] = "";
137 char template_name[201] = "";
138 int res = sscanf(line_str.c_str(), "particleSystem %f, %f, %f, %f, %200s %200s",
139 &psys.scale, &psys.pos.x, &psys.pos.y, &psys.pos.z, instance_name, template_name);
140
141 if (res == 6)
142 {
143 psys.instance_name = instance_name;
144 psys.template_name = template_name;
145 m_def->particle_systems.push_back(psys);
146 }
147 }
148 else if (StartsWith(line_str, "setMeshMaterial") && line_str.length() > 16)
149 {
150 m_def->mat_name = line_str.substr(16); // Format: "setMeshMaterial %s"
151 }
152 else if (StartsWith(line_str, "generateMaterialShaders") && line_str.length() > 24)
153 {
154 m_def->mat_name_generate = line_str.substr(24); // Format: "generateMaterialShaders %s"
155 }
156 else if (StartsWith(line_str, "playanimation"))
157 {
158 ODefAnimation anim;
159 char anim_name[201] = "";
160 sscanf(line_str.c_str(), "playanimation %f, %f, %200s", &anim.speed_min, &anim.speed_max, anim_name);
161 anim.name = anim_name;
162 if (anim.name != "")
163 {
164 m_def->animations.push_back(anim);
165 }
166 }
167 else if (StartsWith(line_str, "drawTextOnMeshTexture"))
168 {
169 ODefTexPrint tp;
170 char font_name[201] = "";
171 char text[501] = "";
172 int res = sscanf(line_str.c_str(),
173 "drawTextOnMeshTexture %f, %f, %f, %f, %f, %f, %f, %f, %c, %i, %i, %200s %500s",
174 &tp.x, &tp.y, &tp.w, &tp.h, &tp.r, &tp.g, &tp.b, &tp.a,
175 &tp.option, &tp.font_size, &tp.font_dpi, font_name, text);
176
177 if (res == 13)
178 {
179 tp.font_name = font_name;
180 tp.text = text;
181 m_def->texture_prints.push_back(tp);
182 }
183 else
184 LOG("[RoR|ODef] Warning: invalid 'drawTextOnMeshTexture' line.");
185 }
186 else if (StartsWith(line_str, "spotlight"))
187 {
188 ODefSpotlight sl;
189 int res = sscanf(line_str.c_str(), "spotlight %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f",
190 &sl.pos.x, &sl.pos.y, &sl.pos.z, &sl.dir.x, &sl.dir.y, &sl.dir.z,
191 &sl.color.r, &sl.color.g, &sl.color.b, &sl.range, &sl.angle_inner, &sl.angle_outer);
192 if (res == 12)
193 {
194 m_def->spotlights.push_back(sl);
195 }
196 else
197 {
198 LOG("[RoR|ODef] Warning: invalid 'spotlight' line.");
199 }
200 }
201 else if (StartsWith(line_str, "pointlight"))
202 {
204 int res = sscanf(line_str.c_str(), "pointlight %f, %f, %f, %f, %f, %f, %f, %f, %f, %f",
205 &pl.pos.x, &pl.pos.y, &pl.pos.z, &pl.dir.x, &pl.dir.y, &pl.dir.z,
206 &pl.color.r, &pl.color.g, &pl.color.b, &pl.range);
207 if (res == 10)
208 {
209 m_def->point_lights.push_back(pl);
210 }
211 else
212 {
213 LOG("[RoR|ODef] Warning: invalid 'pointlight' line.");
214 }
215 }
216
217 // Collision boxes or meshes
218 else if ((line_str == "beginbox") || (line_str == "beginmesh"))
219 {
220 this->ResetCBoxContext();
221 }
222 else if (StartsWith(line_str, "boxcoords"))
223 {
224 sscanf(line_str.c_str(), "boxcoords %f, %f, %f, %f, %f, %f",
228 }
229 else if (StartsWith(line_str, "mesh"))
230 {
231 char tmp[200] = "";
232 sscanf(line_str.c_str(), "mesh %200s", tmp);
233 m_ctx.cbox_mesh_name = tmp;
234 }
235 else if (StartsWith(line_str, "rotate"))
236 {
237 sscanf(line_str.c_str(), "rotate %f, %f, %f",
239 m_ctx.cbox_is_rotating = true;
240 }
241 else if (StartsWith(line_str, "forcecamera"))
242 {
243 sscanf(line_str.c_str(), "forcecamera %f, %f, %f",
245 m_ctx.cbox_force_cam = true;
246 }
247 else if (StartsWith(line_str, "direction"))
248 {
249 sscanf(line_str.c_str(), "direction %f, %f, %f",
251 }
252 else if (StartsWith(line_str, "frictionconfig") && line_str.length() > 15)
253 {
254 m_def->groundmodel_files.push_back(line_str.substr(15));
255 }
256 else if ((StartsWith(line_str, "stdfriction") || StartsWith(line_str, "usefriction")) && line_str.length() > 12)
257 {
258 m_ctx.cbox_groundmodel_name = line_str.substr(12);
259 }
260 else if (line_str == "virtual")
261 {
262 m_ctx.cbox_is_virtual = true;
263 }
264 else if (StartsWith(line_str, "event"))
265 {
266 char ev_name[301] = "";
267 char ev_type[301] = "";
268 sscanf(line_str.c_str(), "event %300s %300s", ev_name, ev_type);
269 m_ctx.cbox_event_name = ev_name;
270
271 if (!strncmp(ev_type, "avatar", 6)) { m_ctx.cbox_event_filter = EVENT_AVATAR; }
272 else if (!strncmp(ev_type, "truck_wheels", 12)) { m_ctx.cbox_event_filter = EVENT_TRUCK_WHEELS; }
273 else if (!strncmp(ev_type, "truck", 5)) { m_ctx.cbox_event_filter = EVENT_TRUCK; }
274 else if (!strncmp(ev_type, "airplane", 8)) { m_ctx.cbox_event_filter = EVENT_AIRPLANE; }
275 else if (!strncmp(ev_type, "boat", 4)) { m_ctx.cbox_event_filter = EVENT_BOAT; }
277
278 // hack to avoid fps drops near spawnzones
279 if (!strncmp(ev_name, "spawnzone", 9)) { m_ctx.cbox_event_filter = EVENT_AVATAR; }
280 }
281 else if (StartsWith(line_str, "reverb_preset"))
282 {
283 char tmp[200] = "";
284 sscanf(line_str.c_str(), "reverb_preset %199s", tmp);
286 }
287 else if (line_str == "endbox")
288 {
289 m_def->collision_boxes.emplace_back(
296 }
297 else if (line_str == "endmesh")
298 {
299 m_def->collision_meshes.emplace_back(
301 }
302 else if (line_str == "nocast")
303 {
304 m_def->header.cast_shadows = false;
305 }
306
307 return true;
308}
309
311{
312 m_ctx.cbox_direction = Ogre::Vector3::ZERO;
313 m_ctx.cbox_is_rotating = false;
314 m_ctx.cbox_is_virtual = false;
315 m_ctx.cbox_force_cam = false;
317 m_ctx.cbox_event_name.clear();
318 m_ctx.cbox_mesh_name.clear();
320 m_ctx.cbox_groundmodel_name = "concrete";
321}
322
void LOG(const char *msg)
Legacy alias - formerly a macro.
const int ODEF_LINE_BUF_LEN
bool StartsWith(std::string const &line, const char *test)
const char * m_cur_line
struct RoR::ODefParser::ODefParserContext m_ctx
Parser context.
std::shared_ptr< ODefDocument > Finalize()
Passes ownership.
bool ProcessLine(const char *line)
void ProcessOgreStream(Ogre::DataStream *stream)
std::shared_ptr< ODefDocument > m_def
@ LOCALIZER_NDB
Definition SimData.h:228
@ LOCALIZER_HORIZONTAL
Definition SimData.h:227
@ LOCALIZER_VERTICAL
Definition SimData.h:226
@ LOCALIZER_VOR
Definition SimData.h:229
@ EVENT_ALL
(default) ~ Triggered by any node on any vehicle
Definition SimData.h:49
@ EVENT_TRUCK_WHEELS
'truck_wheels' ~ Triggered only by wheel nodes of land vehicle (ActorType::TRUCK)
Definition SimData.h:52
@ EVENT_AIRPLANE
'airplane' ~ Triggered by any node of airplane (ActorType::AIRPLANE)
Definition SimData.h:53
@ EVENT_AVATAR
'avatar' ~ Triggered by the character only
Definition SimData.h:50
@ EVENT_TRUCK
'truck' ~ Triggered by any node of land vehicle (ActorType::TRUCK)
Definition SimData.h:51
@ EVENT_NONE
Invalid value.
Definition SimData.h:48
@ EVENT_BOAT
'boat' ~ Triggered by any node of boats (ActorType::BOAT)
Definition SimData.h:54
std::string SanitizeUtf8String(std::string const &str_in)
Definition Utils.cpp:120
Ogre::Vector3 cbox_cam_pos
Section 'forcecamera'.
CollisionEventFilter cbox_event_filter
std::string instance_name
std::string template_name
Ogre::ColourValue color
float angle_outer
Degrees.
float angle_inner
Degrees.
Ogre::ColourValue color
std::string font_name