Rigs of Rods 2023.09
Soft-body Physics Simulation
Loading...
Searching...
No Matches
TObjFileFormat.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 "TObjFileFormat.h"
21
22#include "Actor.h"
23#include "ProceduralRoad.h"
24
25#define LOGSTREAM Ogre::LogManager::getSingleton().stream() << "[RoR|TObj fileformat] "
26
27using namespace RoR;
28using namespace Ogre;
29
30// --------------------------------
31// TObjEntry
32
33TObjEntry::TObjEntry(Ogre::Vector3 pos, Ogre::Vector3 rot, const char* odef, TObjSpecialObject spc, const char* ty, const char* nam):
34 position(pos),
35 rotation(rot),
36 special(spc)
37{
38 strcpy(type, ty);
39 strcpy(instance_name, nam);
40 strcpy(odef_name, odef);
41}
42
47
54
55// --------------------------------
56// Parser
57
71
72bool TObjParser::ProcessLine(const char* line)
73{
74 bool result = true;
75 if ((line != nullptr) && (line[0] != 0))
76 {
77 bool is_comment = (line[0] == '/') || (line[0] == ';');
78 if (is_comment)
79 {
80 int text_start = 1;
81 while (line[text_start] == '/')
82 {
83 text_start++;
84 }
85 m_preceding_line_comments += std::string(line + text_start) + "\n";
86 }
87 else
88 {
89 m_cur_line = line; // No trimming by design.
90 m_cur_line_trimmed = line;
91 while (m_cur_line_trimmed[0] == ' ' || m_cur_line_trimmed[0] == '\t')
92 {
94 }
95 result = this->ProcessCurrentLine();
97 }
98 }
100 return result;
101}
102
103// retval true = continue processing (false = stop)
105{
106 // ** Process keywords
107
108 if (!strcmp(m_cur_line, "end"))
109 {
110 return false;
111 }
112 if (strncmp(m_cur_line, "collision-tris", 14) == 0)
113 {
114 return true; // Obsolete - ignore it.
115 }
116 if (strncmp(m_cur_line, "grid", 4) == 0)
117 {
118 this->ProcessGridLine();
119 return true;
120 }
121 if (strncmp(m_cur_line, "trees", 5) == 0)
122 {
123 this->ProcessTreesLine();
124 return true;
125 }
126 if (strncmp(m_cur_line, "grass", 5) == 0)
127 {
128 this->ProcessGrassLine();
129 return true;
130 }
131 if (strncmp(m_cur_line, "set_default_rendering_distance", 30) == 0)
132 {
133 const int result = sscanf(m_cur_line, "set_default_rendering_distance %f", &m_default_rendering_distance);
134 if (result != 1)
135 {
136 LOG(fmt::format("too few parameters on line: '{}' ({}, line {})", m_cur_line, m_filename, m_line_number));
137 }
138 return true;
139 }
140 if (strncmp(m_cur_line, "rot_yxz", 7) == 0)
141 {
142 m_rot_yxz = true;
143 return true;
144 }
145 if (strncmp("begin_procedural_roads", m_cur_line, 22) == 0)
146 {
147 m_cur_procedural_obj = new ProceduralObject(); // Hard reset, discarding last "non-procedural" road strip. For backwards compatibility. ~ Petr Ohlidal, 08/2020
150 return true;
151 }
152 if (strncmp("end_procedural_roads", m_cur_line, 20) == 0)
153 {
155 {
156 this->FlushProceduralObject();
157 }
158 m_in_procedural_road = false;
159 return true;
160 }
161 if (strncmp("smoothing_num_splits", m_cur_line_trimmed, 20) == 0)
162 {
164 {
165 int result = sscanf(m_cur_line_trimmed, "smoothing_num_splits %d", &m_cur_procedural_obj->smoothing_num_splits);
166 if (result != 1)
167 {
168 LOG(fmt::format("[RoR|TObj] not enough parameters at line '{}' ({}, line {})", m_cur_line, m_filename, m_line_number));
169 }
170 }
171 return true;
172 }
173 if (strncmp("collision_enabled", m_cur_line_trimmed, 17) == 0)
174 {
176 {
177 char valuebuf[100] = {};
178 int result = sscanf(m_cur_line_trimmed, "collision_enabled %s", &valuebuf);
179 if (result != 1)
180 {
181 LOG(fmt::format("[RoR|TObj] not enough parameters at line '{}' ({}, line {})", m_cur_line, m_filename, m_line_number));
182 }
183 m_cur_procedural_obj->collision_enabled = Ogre::StringConverter::parseBool(valuebuf, false);
184 }
185 return true;
186 }
187
188 // ** Process entries (ODEF or special objects)
189
191 {
192 this->ProcessProceduralLine();
193 }
194 else
195 {
196 TObjEntry object;
197 if (this->ParseObjectLine(object))
198 {
199 if (object.IsActor())
200 {
201 this->ProcessActorObject(object);
202 }
203 else if (object.IsRoad())
204 {
205 this->ProcessRoadObject(object);
206 }
207 else
208 {
209 m_def->objects.push_back(object);
210 }
211 }
212 }
213 return true;
214}
215
217{
218 // finish the last road
219 if (m_road2_num_blocks > 0)
220 {
221 Vector3 pp_pos = m_road2_last_pos + m_road2_last_rot * Vector3(10.0f, 0.0f, 0.9f);
223
224 this->FlushProceduralObject();
225 }
226
227 m_def->document_name = m_filename;
228 m_def->rot_yxz = m_rot_yxz;
229 m_filename = "";
230
231 TObjDocumentPtr tmp_def = m_def;
232 m_def.reset();
233 return tmp_def; // Pass ownership
234}
235
236void TObjParser::ProcessOgreStream(Ogre::DataStream* stream)
237{
238 m_filename = stream->getName();
239 char raw_line_buf[TObj::LINE_BUF_LEN];
240 bool keep_reading = true;
241 while (keep_reading && !stream->eof())
242 {
243 stream->readLine(raw_line_buf, TObj::LINE_BUF_LEN);
244 keep_reading = this->ProcessLine(raw_line_buf);
245 }
246}
247
248// --------------------------------
249// Processing
250
252{
253 ProceduralPoint point;
254 Str<300> obj_name;
255 Ogre::Vector3 rot = Ogre::Vector3::ZERO;
256 sscanf(m_cur_line, "%f, %f, %f, %f, %f, %f, %f, %f, %f, %s",
257 &point.position.x, &point.position.y, &point.position.z,
258 &rot.x, &rot.y, &rot.z,
259 &point.width, &point.bwidth, &point.bheight, obj_name.GetBuffer());
260
261 point.rotation = this->CalcRotation(rot, m_rot_yxz);
262
263 if (obj_name == "flat" ) { point.type = RoadType::ROAD_FLAT; }
264 else if (obj_name == "left" ) { point.type = RoadType::ROAD_LEFT; }
265 else if (obj_name == "right" ) { point.type = RoadType::ROAD_RIGHT; }
266 else if (obj_name == "both" ) { point.type = RoadType::ROAD_BOTH; }
267 else if (obj_name == "bridge" ) { point.type = RoadType::ROAD_BRIDGE; point.pillartype = 1; }
268 else if (obj_name == "monorail" ) { point.type = RoadType::ROAD_MONORAIL; point.pillartype = 2; }
269 else if (obj_name == "monorail2" ) { point.type = RoadType::ROAD_MONORAIL; point.pillartype = 0; }
270 else if (obj_name == "bridge_no_pillars") { point.type = RoadType::ROAD_BRIDGE; point.pillartype = 0; }
271 else { point.type = RoadType::ROAD_AUTOMATIC; point.pillartype = 1; }
272
273 // Attach comments
275
276 m_cur_procedural_obj->points.push_back(new ProceduralPoint(point));
277}
278
280{
281 Ogre::Vector3 & pos = m_def->grid_position;
282 sscanf(m_cur_line, "grid %f, %f, %f", &pos.x, &pos.y, &pos.z); // No error check by design
283 m_def->grid_enabled = true;
284}
285
287{
288 TObjTree tree;
289 sscanf(m_cur_line, "trees %f, %f, %f, %f, %f, %f, %f, %s %s %s %f %s",
290 &tree.yaw_from, &tree.yaw_to,
291 &tree.scale_from, &tree.scale_to,
292 &tree.high_density,
293 &tree.min_distance, &tree.max_distance,
294 tree.tree_mesh, tree.color_map, tree.density_map,
295 &tree.grid_spacing, tree.collision_mesh);
296
297 m_def->trees.push_back(tree);
298}
299
301{
302 TObjGrass grass;
303 if (strncmp(m_cur_line, "grass2", 6) == 0)
304 {
305 sscanf(m_cur_line, "grass2 %d, %f, %f, %f, %f, %f, %f, %f, %f, %d, %f, %f, %d, %s %s %s",
306 &grass.range,
307 &grass.sway_speed, &grass.sway_length, &grass.sway_distrib, &grass.density,
308 &grass.min_x, &grass.min_y, &grass.max_x, &grass.max_y,
309 &grass.grow_techniq, &grass.min_h, &grass.max_h, &grass.technique,
310 grass.material_name,
311 grass.color_map_filename,
313 }
314 else
315 {
316 // Same as 'grass2', except without 'technique' parameter
317 sscanf(m_cur_line, "grass %d, %f, %f, %f, %f, %f, %f, %f, %f, %d, %f, %f, %s %s %s",
318 &grass.range,
319 &grass.sway_speed, &grass.sway_length, &grass.sway_distrib, &grass.density,
320 &grass.min_x, &grass.min_y, &grass.max_x, &grass.max_y,
321 &grass.grow_techniq, &grass.min_h, &grass.max_h,
322 grass.material_name,
323 grass.color_map_filename,
325 }
326
327 // 0: GRASSTECH_QUAD; // Grass constructed of randomly placed and rotated quads
328 // 1: GRASSTECH_CROSSQUADS; // Grass constructed of two quads forming a "X" cross shape
329 // 2: GRASSTECH_SPRITE; // Grass constructed of camera-facing billboard quads
330 if ((grass.technique < 0) || (grass.technique > 2))
331 {
332 LOGSTREAM << "Invalid parameter 'technique': '" << grass.technique << "', falling back to default '1: GRASSTECH_CROSSQUADS'";
333 grass.technique = 1;
334 }
335
336 m_def->grass.push_back(grass);
337}
338
340{
341 TObjVehicle v;
342 v.position = object.position;
343 v.tobj_rotation = object.rotation;
344 v.rotation = this->CalcRotation(object.rotation, m_rot_yxz);
345 v.type = object.special;
346 strcpy(v.name, object.type);
347
348 m_def->vehicles.push_back(v);
349}
350
352{
353 // ** Import road objects as procedural road
354
355 if (object.position.distance(m_road2_last_pos) > 20.0f)
356 {
357 // break the road
358 if (m_road2_num_blocks > 0)
359 {
360 Vector3 pp_pos = m_road2_last_pos + this->CalcRotation(m_road2_last_rot, m_rot_yxz) * Vector3(10.0f, 0.0f, 0.9f);
361 this->ImportProceduralPoint(pp_pos, m_road2_last_rot, object.special);
362 this->FlushProceduralObject();
363 }
365
366 // beginning of new
367 this->ImportProceduralPoint(object.position, object.rotation, object.special);
368 }
369 else
370 {
371 this->ImportProceduralPoint(object.position, object.rotation, object.special);
372 }
373 m_road2_last_pos=object.position;
374 m_road2_last_rot=object.rotation;
375}
376
377// --------------------------------
378// Helpers
379
380void TObjParser::ImportProceduralPoint(Ogre::Vector3 const& pos, Ogre::Vector3 const& rot, TObjSpecialObject special)
381{
383 pp.bheight = 0.2;
384 pp.bwidth = 1.4;
386 pp.position = pos;
389 pp.width = 8;
390
391 // Attach comments
393
394 m_cur_procedural_obj->points.push_back(new ProceduralPoint(pp));
396 {
398 }
399}
400
401Ogre::Quaternion TObjParser::CalcRotation(Ogre::Vector3 const& rot, bool rot_yxz)
402{
403 if (rot_yxz)
404 {
405 return Quaternion(Degree(rot.y), Vector3::UNIT_Y) * // y global
406 Quaternion(Degree(rot.x), Vector3::UNIT_X) * // x local
407 Quaternion(Degree(rot.z), Vector3::UNIT_Z); // z local
408 }
409 else
410 {
411 return Quaternion(Degree(rot.x), Vector3::UNIT_X) *
412 Quaternion(Degree(rot.y), Vector3::UNIT_Y) *
413 Quaternion(Degree(rot.z), Vector3::UNIT_Z);
414 }
415}
416
418{
419 Str<TObj::STR_LEN> odef("generic");
420 Str<TObj::STR_LEN> type("");
421 Str<TObj::STR_LEN> instance_name("");
422 Ogre::Vector3 pos(Ogre::Vector3::ZERO);
423 Ogre::Vector3 rot(Ogre::Vector3::ZERO);
424 int r = sscanf(m_cur_line, "%f, %f, %f, %f, %f, %f, %s %s %s",
425 &pos.x, &pos.y, &pos.z, &rot.x, &rot.y, &rot.z, odef.GetBuffer(), type.GetBuffer(), instance_name.GetBuffer());
426 if (r < 6)
427 {
428 return false;
429 }
430
432 if (odef == "truck" ) { special = TObjSpecialObject::TRUCK ; }
433 else if (odef == "load" ) { special = TObjSpecialObject::LOAD ; }
434 else if (odef == "machine" ) { special = TObjSpecialObject::MACHINE ; }
435 else if (odef == "boat" ) { special = TObjSpecialObject::BOAT ; }
436 else if (odef == "truck2" ) { special = TObjSpecialObject::TRUCK2 ; }
437 else if (odef == "grid" ) { special = TObjSpecialObject::GRID ; }
438 else if (odef == "road" ) { special = TObjSpecialObject::ROAD ; }
439 else if (odef == "roadborderleft" ) { special = TObjSpecialObject::ROAD_BORDER_LEFT ; }
440 else if (odef == "roadborderright" ) { special = TObjSpecialObject::ROAD_BORDER_RIGHT ; }
441 else if (odef == "roadborderboth" ) { special = TObjSpecialObject::ROAD_BORDER_BOTH ; }
442 else if (odef == "roadbridgenopillar") { special = TObjSpecialObject::ROAD_BRIDGE_NO_PILLARS; }
443 else if (odef == "roadbridge" ) { special = TObjSpecialObject::ROAD_BRIDGE ; }
444
445 // If no instance name given, generate one, so that scripts can use `game.destroyObject(instanceName)` even for pre-placed objects.
446 // Don't use spaces because TOBJ parser doesn't support "" yet (game tries to strip the 'auto^' instancenames on save, but someone could export via custom script).
447 if (instance_name == "")
448 {
449 instance_name << "auto^" << m_filename << "(line:" << m_line_number << ")";
450 }
451
452 object = TObjEntry(pos, rot, odef.ToCStr(), special, type, instance_name);
453 object.rendering_distance = m_default_rendering_distance;
454
455 // Attach comments
456 object.comments = m_preceding_line_comments;
457
458 return true;
459}
460
462{
463 // finish it and start new object
465 m_def->proc_objects.push_back(m_cur_procedural_obj);
469}
470
471void WriteTObjDelimiter(Ogre::DataStreamPtr& stream, const std::string& title, size_t count)
472{
473 if (count > 0)
474 {
475 std::string line = fmt::format("\n\n// ~~~~~~~~~~ {} ({}) ~~~~~~~~~~\n\n", title, count);
476 stream->write(line.c_str(), line.length());
477 }
478}
479
480void TObj::WriteToStream(TObjDocumentPtr doc, Ogre::DataStreamPtr stream)
481{
482 // assert on Debug, play safe on Release
483 ROR_ASSERT(doc);
484 ROR_ASSERT(stream);
485 if (!doc || !stream)
486 {
487 return;
488 }
489
490 // 'grid'
491 WriteTObjDelimiter(stream, "grid", (int)doc->grid_enabled);
492 if (doc->grid_enabled)
493 {
494 std::string line = fmt::format("grid {}, {}, {}\n");
495 stream->write(line.c_str(), line.length());
496 }
497
498 // 'trees'
499 WriteTObjDelimiter(stream, "trees", doc->trees.size());
500 for (TObjTree& tree : doc->trees)
501 {
502 std::string line = fmt::format("trees {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {} {} {} {:9f} {}\n",
503 tree.yaw_from, tree.yaw_to,
504 tree.scale_from, tree.scale_to,
505 tree.high_density,
506 tree.min_distance, tree.max_distance,
507 tree.tree_mesh, tree.color_map, tree.density_map,
508 tree.grid_spacing, tree.collision_mesh);
509 stream->write(line.c_str(), line.length());
510 }
511
512 // 'grass2' (incudes 'grass' elements)
513 WriteTObjDelimiter(stream, "grass", doc->grass.size());
514 for (TObjGrass& grass : doc->grass)
515 {
516 std::string line = fmt::format("grass2 {}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {}, {:9f}, {:9f}, {}, {} {} {}\n",
517 grass.range,
518 grass.sway_speed, grass.sway_length, grass.sway_distrib, grass.density,
519 grass.min_x, grass.min_y, grass.max_x, grass.max_y,
520 grass.grow_techniq, grass.min_h, grass.max_h, grass.technique,
521 grass.material_name,
522 grass.color_map_filename,
523 grass.density_map_filename);
524 stream->write(line.c_str(), line.length());
525 }
526
527 // vehicles ('truck', 'truck2', 'boat', 'load', 'machine')
528 WriteTObjDelimiter(stream, "vehicles/loads/machines", doc->vehicles.size());
529 for (TObjVehicle& vehicle : doc->vehicles)
530 {
531 // Handle preceding comments
532 if (vehicle.comments != "")
533 {
534 for (Ogre::String& commenttext : Ogre::StringUtil::split(vehicle.comments, "\n"))
535 {
536 std::string commentline = fmt::format("// {}\n", commenttext);
537 stream->write(commentline.c_str(), commentline.length());
538 }
539 }
540
541 std::string line = fmt::format("{:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {} {}\n",
542 vehicle.position.x, vehicle.position.y, vehicle.position.z,
543 vehicle.tobj_rotation.x, vehicle.tobj_rotation.y, vehicle.tobj_rotation.z,
544 TObjSpecialObjectToString(vehicle.type), (const char*)vehicle.name);
545 stream->write(line.c_str(), line.length());
546 }
547
548 // procedural objects
549 WriteTObjDelimiter(stream, "roads", doc->proc_objects.size());
550 for (ProceduralObjectPtr& procobj : doc->proc_objects)
551 {
552 std::string bline = "begin_procedural_roads\n";
553 stream->write(bline.c_str(), bline.length());
554
555 std::string sline = fmt::format(" smoothing_num_splits {}\n", procobj->smoothing_num_splits);
556 stream->write(sline.c_str(), sline.length());
557
558 std::string cline = fmt::format(" collision_enabled {}\n", procobj->collision_enabled);
559 stream->write(cline.c_str(), cline.length());
560
561 for (ProceduralPointPtr& point : procobj->points)
562 {
563 std::string type_str;
564 switch (point->type)
565 {
566 case RoadType::ROAD_AUTOMATIC: type_str = "auto"; break;
567 case RoadType::ROAD_FLAT: type_str = "flat"; break;
568 case RoadType::ROAD_LEFT: type_str = "left"; break;
569 case RoadType::ROAD_RIGHT: type_str = "right"; break;
570 case RoadType::ROAD_BOTH: type_str = "both"; break;
571 case RoadType::ROAD_BRIDGE: type_str = (point->pillartype == 1) ? "bridge" : "bridge_no_pillars"; break;
572 case RoadType::ROAD_MONORAIL: type_str = (point->pillartype == 2) ? "monorail" : "monorail2"; break;
573 }
574
575 // Handle preceding comments
576 if (point->comments != "")
577 {
578 for (Ogre::String& commenttext : Ogre::StringUtil::split(point->comments, "\n"))
579 {
580 std::string commentline = fmt::format("// {}\n", commenttext);
581 stream->write(commentline.c_str(), commentline.length());
582 }
583 }
584
585 Ogre::Matrix3 point_rot_matrix;
586 point->rotation.ToRotationMatrix(point_rot_matrix);
587 Ogre::Radian yaw, pitch, roll;
588 point_rot_matrix.ToEulerAnglesYXZ(yaw, pitch, roll);
589
590 std::string line = fmt::format(
591 "\t{:13f}, {:13f}, {:13f}, 0, {:13f}, 0, {:13f}, {:13f}, {:13f}, {}\n",
592 point->position.x, point->position.y, point->position.z,
593 yaw.valueDegrees(),
594 point->width, point->bwidth, point->bheight, type_str);
595 stream->write(line.c_str(), line.length());
596 }
597
598 std::string eline = "end_procedural_roads\n";
599 stream->write(eline.c_str(), eline.length());
600 }
601
602 // static objects
603 WriteTObjDelimiter(stream, "static objects", doc->objects.size());
604 for (TObjEntry& entry : doc->objects)
605 {
606 // Handle preceding comments
607 if (entry.comments != "")
608 {
609 for (Ogre::String& commenttext : Ogre::StringUtil::split(entry.comments, "\n"))
610 {
611 std::string commentline = fmt::format("// {}\n", commenttext);
612 stream->write(commentline.c_str(), commentline.length());
613 }
614 }
615
616 // Don't save autogenerated instance names
617 std::string valid_instance_name;
618 if (strncmp(entry.instance_name, "auto^", 5) != 0)
619 {
620 valid_instance_name = entry.instance_name;
621 }
622
623 std::string line = fmt::format("{:8.3f}, {:8.3f}, {:8.3f}, {:9f}, {:9f}, {:9f}, {} {} {}\n",
624 entry.position.x, entry.position.y, entry.position.z,
625 entry.rotation.x, entry.rotation.y, entry.rotation.z,
626 entry.odef_name, entry.type, valid_instance_name);
627 stream->write(line.c_str(), line.length());
628 }
629}
630
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
void LOG(const char *msg)
Legacy alias - formerly a macro.
#define LOGSTREAM
void WriteTObjDelimiter(Ogre::DataStreamPtr &stream, const std::string &title, size_t count)
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition Str.h:36
const char * ToCStr() const
Definition Str.h:46
char * GetBuffer()
Definition Str.h:48
TObjDocumentPtr Finalize()
Passes ownership.
Ogre::Vector3 m_road2_last_rot
ProceduralObjectPtr m_cur_procedural_obj
bool ProcessLine(const char *line)
float m_default_rendering_distance
std::string m_filename
TObjDocumentPtr m_def
void ProcessRoadObject(const TObjEntry &object)
std::string m_preceding_line_comments
bool ParseObjectLine(TObjEntry &object)
void ProcessActorObject(const TObjEntry &object)
const char * m_cur_line_trimmed
void ImportProceduralPoint(Ogre::Vector3 const &pos, Ogre::Vector3 const &rot, TObjSpecialObject special)
static Ogre::Quaternion CalcRotation(Ogre::Vector3 const &rot, bool rot_yxz)
const char * m_cur_line
int m_cur_procedural_obj_start_line
Ogre::Vector3 m_road2_last_pos
void ProcessOgreStream(Ogre::DataStream *stream)
const int LINE_BUF_LEN
void WriteToStream(TObjDocumentPtr doc, Ogre::DataStreamPtr stream)
std::shared_ptr< TObjDocument > TObjDocumentPtr
const char * TObjSpecialObjectToString(TObjSpecialObject val)
TObjSpecialObject
@ TRUCK2
Free position (not auto-adjusted to fit terrain or water surface)
bool collision_enabled
Generate collision triangles?
std::vector< ProceduralPointPtr > points
std::string comments
Comment line(s) preceding the point-line in the .TOBJ file.
Ogre::Quaternion rotation
char instance_name[TObj::STR_LEN]
bool IsRoad() const
bool IsActor() const
char type[TObj::STR_LEN]
char odef_name[TObj::STR_LEN]
TObjSpecialObject special
TObjEntry()
Unified 'grass' and 'grass2'.
char density_map_filename[TObj::STR_LEN]
char material_name[TObj::STR_LEN]
char color_map_filename[TObj::STR_LEN]
char collision_mesh[TObj::STR_LEN]
char density_map[TObj::STR_LEN]
char tree_mesh[TObj::STR_LEN]
char color_map[TObj::STR_LEN]
Ogre::Quaternion rotation
Ogre::Vector3 position
Ogre::Vector3 tobj_rotation
Original rotation specified in .TOBJ file.
char name[TObj::STR_LEN]
TObjSpecialObject type