diff --git a/source/main/resources/tobj_fileformat/TObjFileFormat.cpp b/source/main/resources/tobj_fileformat/TObjFileFormat.cpp index 2a8411a614..352f96828a 100644 --- a/source/main/resources/tobj_fileformat/TObjFileFormat.cpp +++ b/source/main/resources/tobj_fileformat/TObjFileFormat.cpp @@ -72,15 +72,29 @@ void TObjParser::Prepare() bool TObjParser::ProcessLine(const char* line) { bool result = true; - if ((line != nullptr) && (line[0] != 0) && (line[0] != '/') && (line[0] != ';')) + if ((line != nullptr) && (line[0] != 0)) { - m_cur_line = line; // No trimming by design. - m_cur_line_trimmed = line; - while (m_cur_line_trimmed[0] == ' ' || m_cur_line_trimmed[0] == '\t') + bool is_comment = (line[0] == '/') || (line[0] == ';'); + if (is_comment) { - m_cur_line_trimmed++; + int text_start = 1; + while (line[text_start] == '/') + { + text_start++; + } + m_preceding_line_comments += std::string(line + text_start) + "\n"; + } + else + { + m_cur_line = line; // No trimming by design. + m_cur_line_trimmed = line; + while (m_cur_line_trimmed[0] == ' ' || m_cur_line_trimmed[0] == '\t') + { + m_cur_line_trimmed++; + } + result = this->ProcessCurrentLine(); + m_preceding_line_comments = ""; } - result = this->ProcessCurrentLine(); } m_line_number++; return result; @@ -250,6 +264,9 @@ void TObjParser::ProcessProceduralLine() else if (obj_name == "bridge_no_pillars") { point.type = RoadType::ROAD_BRIDGE; point.pillartype = 0; } else { point.type = RoadType::ROAD_AUTOMATIC; point.pillartype = 1; } + // Attach comments + point.comments = m_preceding_line_comments; + m_cur_procedural_obj->points.push_back(new ProceduralPoint(point)); } @@ -364,6 +381,9 @@ void TObjParser::ImportProceduralPoint(Ogre::Vector3 const& pos, Ogre::Vector3 c pp.type = (special == TObj::SpecialObject::ROAD) ? RoadType::ROAD_FLAT : RoadType::ROAD_AUTOMATIC; pp.width = 8; + // Attach comments + pp.comments = m_preceding_line_comments; + m_cur_procedural_obj->points.push_back(new ProceduralPoint(pp)); if (m_cur_procedural_obj_start_line == -1) { @@ -417,6 +437,10 @@ bool TObjParser::ParseObjectLine(TObjEntry& object) object = TObjEntry(pos, rot, odef.ToCStr(), special, type, name); object.rendering_distance = m_default_rendering_distance; + + // Attach comments + object.comments = m_preceding_line_comments; + return true; } @@ -510,6 +534,16 @@ void TObj::WriteToStream(TObjDocumentPtr doc, Ogre::DataStreamPtr stream) WriteTObjDelimiter(stream, "vehicles/loads/machines", doc->vehicles.size()); for (TObjVehicle& vehicle : doc->vehicles) { + // Handle preceding comments + if (vehicle.comments != "") + { + for (Ogre::String& commenttext : Ogre::StringUtil::split(vehicle.comments, "\n")) + { + std::string commentline = fmt::format("// {}\n", commenttext); + stream->write(commentline.c_str(), commentline.length()); + } + } + std::string line = fmt::format("{:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {:9f}, {} {}\n", vehicle.position.x, vehicle.position.y, vehicle.position.z, vehicle.rotation.getRoll().valueDegrees(), vehicle.rotation.getYaw().valueDegrees(), vehicle.rotation.getPitch().valueDegrees(), @@ -544,6 +578,16 @@ void TObj::WriteToStream(TObjDocumentPtr doc, Ogre::DataStreamPtr stream) case RoadType::ROAD_MONORAIL: type_str = (point->pillartype == 2) ? "monorail" : "monorail2"; break; } + // Handle preceding comments + if (point->comments != "") + { + for (Ogre::String& commenttext : Ogre::StringUtil::split(point->comments, "\n")) + { + std::string commentline = fmt::format("// {}\n", commenttext); + stream->write(commentline.c_str(), commentline.length()); + } + } + std::string line = fmt::format( "\t{:13f}, {:13f}, {:13f}, 0, {:13f}, 0, {:13f}, {:13f}, {:13f}, {}\n", point->position.x, point->position.y, point->position.z, @@ -560,6 +604,16 @@ void TObj::WriteToStream(TObjDocumentPtr doc, Ogre::DataStreamPtr stream) WriteTObjDelimiter(stream, "static objects", doc->objects.size()); for (TObjEntry& entry : doc->objects) { + // Handle preceding comments + if (entry.comments != "") + { + for (Ogre::String& commenttext : Ogre::StringUtil::split(entry.comments, "\n")) + { + std::string commentline = fmt::format("// {}\n", commenttext); + stream->write(commentline.c_str(), commentline.length()); + } + } + std::string line = fmt::format("{:8.3f}, {:8.3f}, {:8.3f}, {:9f}, {:9f}, {:9f}, {} {} {}\n", entry.position.x, entry.position.y, entry.position.z, entry.rotation.x, entry.rotation.y, entry.rotation.z, diff --git a/source/main/resources/tobj_fileformat/TObjFileFormat.h b/source/main/resources/tobj_fileformat/TObjFileFormat.h index c9066962df..b6f50ee399 100644 --- a/source/main/resources/tobj_fileformat/TObjFileFormat.h +++ b/source/main/resources/tobj_fileformat/TObjFileFormat.h @@ -132,6 +132,7 @@ struct TObjVehicle Ogre::Quaternion rotation; char name[TObj::STR_LEN]; TObj::SpecialObject type; + std::string comments; //!< Comment line(s) preceding the vehicle-line in the .TOBJ file. }; // ----------------------------------------------------------------------------- @@ -152,6 +153,7 @@ struct TObjEntry char instance_name[TObj::STR_LEN] = {}; char odef_name[TObj::STR_LEN] = {}; float rendering_distance = 0.f; // 0 means 'always rendered', see https://ogrecave.github.io/ogre/api/1.11/class_ogre_1_1_movable_object.html#afe1f2a1009e3f14f36e1bcc9b1b9557e + std::string comments; //!< Comment line(s) preceding the object-line in the .TOBJ file. }; // ----------------------------------------------------------------------------- @@ -203,6 +205,7 @@ class TObjParser const char* m_cur_line; const char* m_cur_line_trimmed; float m_default_rendering_distance; + std::string m_preceding_line_comments; // Procedural roads bool m_in_procedural_road; diff --git a/source/main/terrain/ProceduralManager.h b/source/main/terrain/ProceduralManager.h index 704ba50a7b..d9349ea02f 100644 --- a/source/main/terrain/ProceduralManager.h +++ b/source/main/terrain/ProceduralManager.h @@ -43,7 +43,8 @@ struct ProceduralPoint: public RefCountingObject width(orig.width), bwidth(orig.bwidth), bheight(orig.bheight), - pillartype(orig.pillartype) {} + pillartype(orig.pillartype), + comments(orig.comments){} virtual ~ProceduralPoint() override {}; @@ -54,6 +55,7 @@ struct ProceduralPoint: public RefCountingObject float bwidth = 0.f; float bheight = 0.f; int pillartype = 0; + std::string comments; //!< Comment line(s) preceding the point-line in the .TOBJ file. }; struct ProceduralObject: public RefCountingObject diff --git a/source/main/terrain/TerrainEditor.cpp b/source/main/terrain/TerrainEditor.cpp index 9da633ff3b..292a109abd 100644 --- a/source/main/terrain/TerrainEditor.cpp +++ b/source/main/terrain/TerrainEditor.cpp @@ -286,6 +286,7 @@ void TerrainEditor::WriteOutputFile() // TBD: reconstruct 'set_default_rendering_distance'. dst.position = src.position; dst.rotation = src.rotation; + dst.comments = src.tobj_comments; tobj->objects.push_back(dst); } diff --git a/source/main/terrain/TerrainObjectManager.cpp b/source/main/terrain/TerrainObjectManager.cpp index 96c0f5522c..9c1fbc2d7c 100644 --- a/source/main/terrain/TerrainObjectManager.cpp +++ b/source/main/terrain/TerrainObjectManager.cpp @@ -264,8 +264,13 @@ void TerrainObjectManager::LoadTObjFile(Ogre::String tobj_name) try { m_tobj_cache_active_id = (int)m_tobj_cache.size() - 1; + size_t num_editor_objects = m_editor_objects.size(); this->LoadTerrainObject(entry.odef_name, entry.position, entry.rotation, entry.instance_name, entry.type, entry.rendering_distance); m_tobj_cache_active_id = -1; + if (m_editor_objects.size() > num_editor_objects) + { + m_editor_objects.back().tobj_comments = entry.comments; + } } catch (...) { diff --git a/source/main/terrain/TerrainObjectManager.h b/source/main/terrain/TerrainObjectManager.h index 2f701a5f61..27d2c2b064 100644 --- a/source/main/terrain/TerrainObjectManager.h +++ b/source/main/terrain/TerrainObjectManager.h @@ -73,6 +73,7 @@ class TerrainObjectManager bool enable_collisions = true; int script_handler = -1; int tobj_cache_id = -1; + std::string tobj_comments; }; TerrainObjectManager(Terrain* terrainManager);