From 2046c336068ffd0e024c6b5710fe2113c14cf559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Ohl=C3=ADdal?= Date: Sun, 9 Apr 2017 18:25:27 +0200 Subject: [PATCH] :wrench: Created standalone 'ODef' fileformat parser --- source/main/CMakeLists.txt | 2 + .../odef_fileformat/ODefFileFormat.cpp | 302 ++++++++ .../odef_fileformat/ODefFileFormat.h | 218 ++++++ source/main/terrain/TerrainObjectManager.cpp | 688 ++++++------------ 4 files changed, 741 insertions(+), 469 deletions(-) create mode 100644 source/main/resources/odef_fileformat/ODefFileFormat.cpp create mode 100644 source/main/resources/odef_fileformat/ODefFileFormat.h diff --git a/source/main/CMakeLists.txt b/source/main/CMakeLists.txt index b97df45990..eb4f44874e 100644 --- a/source/main/CMakeLists.txt +++ b/source/main/CMakeLists.txt @@ -214,6 +214,7 @@ set(SOURCE_FILES resources/CacheSystem.{h,cpp} resources/ContentManager.{h,cpp} resources/otc_fileformat/OTCFileformat.{h,cpp} + resources/odef_fileformat/ODefFileFormat.{h,cpp} resources/rig_def_fileformat/RigDef_File.{h,cpp} resources/rig_def_fileformat/RigDef_Node.{h,cpp} resources/rig_def_fileformat/RigDef_Parser.{h,cpp} @@ -344,6 +345,7 @@ target_include_directories(${BINNAME} PRIVATE physics/water plugins/pagedgeometry/include resources + resources/odef_fileformat/ resources/otc_fileformat/ resources/rig_def_fileformat resources/terrn2_fileformat diff --git a/source/main/resources/odef_fileformat/ODefFileFormat.cpp b/source/main/resources/odef_fileformat/ODefFileFormat.cpp new file mode 100644 index 0000000000..73cebaba72 --- /dev/null +++ b/source/main/resources/odef_fileformat/ODefFileFormat.cpp @@ -0,0 +1,302 @@ +/* + This source file is part of Rigs of Rods + Copyright 2016-2017 Petr Ohlidal + + For more information, see http://www.rigsofrods.org/ + + Rigs of Rods is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 3, as + published by the Free Software Foundation. + + Rigs of Rods is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Rigs of Rods. If not, see . +*/ + +#include "ODefFileFormat.h" + +#include "Utils.h" + +using namespace RoR; +using namespace Ogre; + +static int s_unique_id_source; + +void ODefParser::ProcessOgreStream(Ogre::DataStream* stream) +{ + char raw_line_buf[ODef::LINE_BUF_LEN]; + bool keep_reading = true; + while (keep_reading && !stream->eof()) + { + stream->readLine(raw_line_buf, ODef::LINE_BUF_LEN); + keep_reading = this->ProcessLine(raw_line_buf); + } +} + +bool ODefParser::ProcessLine(const char* line) +{ + bool result = true; + if ((line != nullptr) && (line[0] != 0)) + { + m_cur_line = line; // No trimming by design. + result = this->ProcessCurrentLine(); + } + m_line_number++; + return result; +} + +void ODefParser::Prepare() +{ + m_ctx.header_done = false; + m_ctx.header_scale = Ogre::Vector3::ZERO; + this->ResetCBoxContext(); + m_def = std::make_shared(); +} + +std::shared_ptr ODefParser::Finalize() +{ + std::shared_ptr def = m_def; // Pass ownership + m_def.reset(); + return def; +} + +inline bool StartsWith(std::string const & line, const char* test) +{ + return line.compare(0, strlen(test), test) == 0; +} + +// retval true = continue processing (false = stop) +bool ODefParser::ProcessCurrentLine() +{ + if (!m_ctx.header_done) + { + if (strcmp(m_cur_line, "LOD") == 0) // Clone of old parser logic. + { + return true; // 'LOD line' = obsolete + } + + if (m_ctx.header_mesh_name.empty()) + { + m_ctx.header_mesh_name = m_cur_line; // No trimming by design + return true; + } + + sscanf(m_cur_line, "%f, %f, %f", &m_ctx.header_scale.x, &m_ctx.header_scale.y, &m_ctx.header_scale.z); + char ent_name[100]; + sprintf(ent_name, "object_%04x", s_unique_id_source); + s_unique_id_source++; + + m_def->header.entity_name = ent_name; + m_def->header.mesh_name = m_ctx.header_mesh_name; + m_def->header.scale = m_ctx.header_scale; + m_ctx.header_done = true; + return true; + } + + std::string line_str = m_cur_line; + Ogre::StringUtil::trim(line_str); + line_str = RoR::Utils::SanitizeUtf8String(line_str); + if ((line_str[0] == 0) || (line_str[0] == '/') || (line_str[0] == ';')) + { + return true; + } + + if (line_str == "end") + { + return false; + } + + if (line_str == "movable") + { + // Unused keyword + } + else if (line_str == "standard") + { + m_def->mode_standard = true; + } + else if (StartsWith(line_str, "localizer-")) + { + if (line_str.compare(10, 3, "vor")) { m_def->localizers.push_back(ODef::Localizer::VOR ); return true; } + if (line_str.compare(10, 3, "ndb")) { m_def->localizers.push_back(ODef::Localizer::NDB ); return true; } + if (line_str.compare(10, 1, "v" )) { m_def->localizers.push_back(ODef::Localizer::VERTICAL ); return true; } + if (line_str.compare(10, 1, "h" )) { m_def->localizers.push_back(ODef::Localizer::HORIZONTAL); return true; } + + LOG("[RoR|ODef] Invalid line: " + line_str); + } + else if (StartsWith(line_str, "sound")) + { + char tmp[255]=""; + sscanf(line_str.c_str(), "sound %s", tmp); + m_def->sounds.push_back(tmp); + } + else if (StartsWith(line_str, "particleSystem")) + { + ODefParticleSys psys; + int res = sscanf(line_str.c_str(), "particleSystem %f, %f, %f, %f, %s %s", + &psys.scale, &psys.pos.x, &psys.pos.y, &psys.pos.z, psys.instance_name, psys.template_name); + + if (res == 6) + { + m_def->particle_systems.push_back(psys); + } + } + else if (StartsWith(line_str, "setMeshMaterial") && line_str.length() > 16) + { + m_def->mat_name = line_str.substr(16); // Format: "setMeshMaterial %s" + } + else if (StartsWith(line_str, "generateMaterialShaders") && line_str.length() > 24) + { + m_def->mat_name_generate = line_str.substr(24); // Format: "generateMaterialShaders %s" + } + else if (StartsWith(line_str, "playanimation")) + { + ODefAnimation anim; + sscanf(line_str.c_str(), "playanimation %f, %f, %s", &anim.speed_min, &anim.speed_max, anim.name); + if (strnlen(anim.name, ODef::STR_LEN) > 0) + { + m_def->animations.push_back(anim); + } + } + else if (StartsWith(line_str, "drawTextOnMeshTexture")) + { + ODefTexPrint tp; + int res = sscanf(line_str.c_str(), + "drawTextOnMeshTexture %f, %f, %f, %f, %f, %f, %f, %f, %c, %i, %i, %s %s", + &tp.x, &tp.y, &tp.w, &tp.h, &tp.r, &tp.g, &tp.b, &tp.a, + &tp.option, &tp.font_size, &tp.font_dpi, tp.font_name, tp.text); + + if (res == 13) + m_def->texture_prints.push_back(tp); + else + LOG("[RoR|ODef] Warning: invalid 'drawTextOnMeshTexture' line."); + } + else if (StartsWith(line_str, "spotlight")) + { + ODefSpotlight sl; + int res = sscanf(line_str.c_str(), "spotlight %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f", + &sl.pos.x, &sl.pos.y, &sl.pos.z, &sl.dir.x, &sl.dir.y, &sl.dir.z, + &sl.color.r, &sl.color.g, &sl.color.b, &sl.range, &sl.angle_inner, &sl.angle_outer); + if (res == 12) + { + snprintf(sl.name, ODef::STR_LEN, "spotlight_%04x", s_unique_id_source++); + m_def->spotlights.push_back(sl); + } + else + { + LOG("[RoR|ODef] Warning: invalid 'spotlight' line."); + } + } + else if (StartsWith(line_str, "pointlight")) + { + ODefPointLight pl; + int res = sscanf(line_str.c_str(), "pointlight %f, %f, %f, %f, %f, %f, %f, %f, %f, %f", + &pl.pos.x, &pl.pos.y, &pl.pos.z, &pl.dir.x, &pl.dir.y, &pl.dir.z, + &pl.color.r, &pl.color.g, &pl.color.b, &pl.range); + if (res == 10) + { + snprintf(pl.name, ODef::STR_LEN, "pointlight_%04x", s_unique_id_source++); + m_def->point_lights.push_back(pl); + } + else + { + LOG("[RoR|ODef] Warning: invalid 'pointlight' line."); + } + } + + // Collision boxes or meshes + else if ((line_str == "beginbox") || (line_str == "beginmesh")) + { + this->ResetCBoxContext(); + } + else if (StartsWith(line_str, "boxcoords")) + { + sscanf(line_str.c_str(), "boxcoords %f, %f, %f, %f, %f, %f", + &m_ctx.cbox_aabb_min.x, &m_ctx.cbox_aabb_max.x, + &m_ctx.cbox_aabb_min.y, &m_ctx.cbox_aabb_max.y, + &m_ctx.cbox_aabb_min.z, &m_ctx.cbox_aabb_max.z); + } + else if (StartsWith(line_str, "mesh")) + { + char tmp[200] = ""; + sscanf(line_str.c_str(), "mesh %s", tmp); + m_ctx.cbox_mesh_name = tmp; + } + else if (StartsWith(line_str, "rotate")) + { + sscanf(line_str.c_str(), "rotate %f, %f, %f", + &m_ctx.cbox_rotation.x, &m_ctx.cbox_rotation.y, &m_ctx.cbox_rotation.z); + m_ctx.cbox_is_rotating = true; + } + else if (StartsWith(line_str, "forcecamera")) + { + sscanf(line_str.c_str(), "forcecamera %f, %f, %f", + &m_ctx.cbox_cam_pos.x, &m_ctx.cbox_cam_pos.y, &m_ctx.cbox_cam_pos.z); + m_ctx.cbox_force_cam = true; + } + else if (StartsWith(line_str, "direction")) + { + sscanf(line_str.c_str(), "direction %f, %f, %f", + &m_ctx.cbox_direction.x, &m_ctx.cbox_direction.y, &m_ctx.cbox_direction.z); + } + else if (StartsWith(line_str, "frictionconfig") && line_str.length() > 15) + { + m_def->groundmodel_files.push_back(line_str.substr(15)); + } + else if ((StartsWith(line_str, "stdfriction") || StartsWith(line_str, "usefriction")) && line_str.length() > 12) + { + m_ctx.cbox_groundmodel_name = line_str.substr(12); + } + else if (line_str == "virtual") + { + m_ctx.cbox_is_virtual = true; + } + else if (StartsWith(line_str, "event")) + { + char ev_name[300] = ""; + char ev_type[300] = ""; + sscanf(line_str.c_str(), "event %s %s", ev_name, ev_type); + m_ctx.cbox_event_name = ev_name; + + if (!strncmp(ev_type, "avatar", 6)) { m_ctx.cbox_event_filter = ODef::EventType::AVATAR; } + else if (!strncmp(ev_type, "truck", 5)) { m_ctx.cbox_event_filter = ODef::EventType::TRUCK; } + else if (!strncmp(ev_type, "airplane", 8)) { m_ctx.cbox_event_filter = ODef::EventType::AIRPLANE; } + else if (!strncmp(ev_type, "boat", 4)) { m_ctx.cbox_event_filter = ODef::EventType::BOAT; } + else if (!strncmp(ev_type, "delete", 6)) { m_ctx.cbox_event_filter = ODef::EventType::DELETE; } + else { m_ctx.cbox_event_filter = ODef::EventType::NONE; } + + // hack to avoid fps drops near spawnzones + if (!strncmp(ev_name, "spawnzone", 9)) { m_ctx.cbox_event_filter = ODef::EventType::AVATAR; } + } + else if (line_str == "endbox") + { + m_def->collision_boxes.emplace_back( + m_ctx.cbox_aabb_min, m_ctx.cbox_aabb_max, + m_ctx.cbox_rotation, m_ctx.cbox_cam_pos, + m_ctx.cbox_direction, m_ctx.header_scale, + m_ctx.cbox_event_name, static_cast(m_ctx.cbox_event_filter), + m_ctx.cbox_is_rotating, m_ctx.cbox_is_virtual, m_ctx.cbox_force_cam); + } + else if (line_str == "endmesh") + { + m_def->collision_meshes.emplace_back( + m_ctx.cbox_mesh_name, m_ctx.header_scale, m_ctx.cbox_groundmodel_name); + } + return true; +} + +void ODefParser::ResetCBoxContext() +{ + m_ctx.cbox_direction = Ogre::Vector3::ZERO; + m_ctx.cbox_is_rotating = false; + m_ctx.cbox_is_virtual = false; + m_ctx.cbox_force_cam = false; + m_ctx.cbox_event_filter = ODef::EventType::NONE; + m_ctx.cbox_event_name.clear(); + m_ctx.cbox_mesh_name.clear(); + m_ctx.cbox_groundmodel_name = "concrete"; +} + diff --git a/source/main/resources/odef_fileformat/ODefFileFormat.h b/source/main/resources/odef_fileformat/ODefFileFormat.h new file mode 100644 index 0000000000..6df6229a51 --- /dev/null +++ b/source/main/resources/odef_fileformat/ODefFileFormat.h @@ -0,0 +1,218 @@ +/* + This source file is part of Rigs of Rods + Copyright 2016-2017 Petr Ohlidal + + For more information, see http://www.rigsofrods.org/ + + Rigs of Rods is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 3, as + published by the Free Software Foundation. + + Rigs of Rods is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Rigs of Rods. If not, see . +*/ + +#pragma once + +#include +#include +#include + +#include + +#ifdef DELETE // Windows hack: The `EventType::DELETE` name collides with `DELETE` from which *MAY* be included at this point. +# undef DELETE +#endif + +namespace RoR { +namespace ODef { + + const int STR_LEN = 300; + const int LINE_BUF_LEN = 4000; + + enum class Localizer + { + NONE, + HORIZONTAL, + VERTICAL, + NDB, + VOR, + }; + + enum class EventType + { + NONE, + AVATAR, + TRUCK, + AIRPLANE, + BOAT, + DELETE, + }; + +} // namespace ODef + +struct ODefCollisionBox +{ + ODefCollisionBox(Ogre::Vector3 min, Ogre::Vector3 max, Ogre::Vector3 rot, + Ogre::Vector3 campos, Ogre::Vector3 dir, Ogre::Vector3 scal, + std::string ev_name, int ev_filter, bool is_rot, bool is_virt, bool forcecam): + + aabb_min(min), aabb_max(max), box_rot(rot), cam_pos(campos), direction(dir), scale(scal), + event_name(ev_name), event_filter(ev_filter), + is_rotating(is_rot), is_virtual(is_virt), force_cam_pos(forcecam) + {} + + Ogre::Vector3 aabb_min; + Ogre::Vector3 aabb_max; + Ogre::Vector3 box_rot; + Ogre::Vector3 cam_pos; + Ogre::Vector3 direction; + Ogre::Vector3 scale; + std::string event_name; + int event_filter; + + // Flags + bool is_rotating:1; + bool is_virtual:1; + bool force_cam_pos:1; +}; + +struct ODefCollisionMesh +{ + ODefCollisionMesh(std::string meshname, Ogre::Vector3 scal, std::string gmodelname): + mesh_name(meshname), scale(scal), groundmodel_name(gmodelname) + {} + + std::string mesh_name; + Ogre::Vector3 scale; + std::string groundmodel_name; +}; + +struct ODefParticleSys +{ + ODefParticleSys() { memset(this, 0, sizeof(ODefParticleSys)); } + + char instance_name[ODef::STR_LEN]; + char template_name[ODef::STR_LEN]; + Ogre::Vector3 pos; + float scale; +}; + +struct ODefAnimation +{ + ODefAnimation() { memset(this, 0, sizeof(ODefAnimation)); } + + float speed_min, speed_max; + char name[ODef::STR_LEN]; +}; + +struct ODefTexPrint +{ + ODefTexPrint() { memset(this, 0, sizeof(ODefTexPrint)); } + + char font_name[ODef::STR_LEN]; + int font_size; + int font_dpi; + char text[ODef::STR_LEN]; + char option; + + float x, y, w, h; + float a, r, g, b; +}; + +struct ODefSpotlight +{ + ODefSpotlight() { memset(this, 0, sizeof(ODefSpotlight)); } + + Ogre::Vector3 pos; + Ogre::Vector3 dir; + float range; + float angle_inner; ///< Degrees + float angle_outer; ///< Degrees + Ogre::ColourValue color; + char name[ODef::STR_LEN]; +}; + +struct ODefPointLight +{ + ODefPointLight() { memset(this, 0, sizeof(ODefPointLight)); } + + Ogre::Vector3 pos; + Ogre::Vector3 dir; + float range; + Ogre::ColourValue color; + char name[ODef::STR_LEN]; +}; + +struct ODefFile +{ + ODefFile(): + mode_standard(false) + {} + + struct ODefFileHeader + { + std::string mesh_name; + std::string entity_name; + Ogre::Vector3 scale; + } header; + + bool mode_standard; + std::vector localizers; + std::list sounds; + std::list groundmodel_files; + std::list collision_boxes; + std::list collision_meshes; + std::list particle_systems; + std::list animations; + std::list texture_prints; ///< Section 'drawTextOnMeshTexture' + std::list spotlights; + std::list point_lights; + std::string mat_name; ///< Section 'setMeshMaterial' + std::string mat_name_generate; ///< Section 'generateMaterialShaders' +}; + +// ----------------------------------------------------------------------------- +class ODefParser +{ +public: + void Prepare(); + bool ProcessLine(const char* line); + void ProcessOgreStream(Ogre::DataStream* stream); + std::shared_ptr Finalize(); ///< Passes ownership + +private: + bool ProcessCurrentLine(); + void ResetCBoxContext(); + + struct ODefParserContext + { + bool header_done; + std::string header_mesh_name; + Ogre::Vector3 header_scale; + // Collision boxes or meshes + Ogre::Vector3 cbox_direction; + bool cbox_is_rotating; + bool cbox_is_virtual; + bool cbox_force_cam; + Ogre::Vector3 cbox_rotation; + Ogre::Vector3 cbox_cam_pos; ///< Section 'forcecamera' + ODef::EventType cbox_event_filter; + std::string cbox_event_name; + std::string cbox_mesh_name; + std::string cbox_groundmodel_name; + Ogre::Vector3 cbox_aabb_min; + Ogre::Vector3 cbox_aabb_max; + } m_ctx; ///< Parser context + + std::shared_ptr m_def; + int m_line_number; + const char* m_cur_line; +}; + +} // namespace RoR diff --git a/source/main/terrain/TerrainObjectManager.cpp b/source/main/terrain/TerrainObjectManager.cpp index f0534f82e4..15fb66589a 100644 --- a/source/main/terrain/TerrainObjectManager.cpp +++ b/source/main/terrain/TerrainObjectManager.cpp @@ -31,6 +31,7 @@ #include "GUIManager.h" #include "GUI_LoadingWindow.h" #include "MeshObject.h" +#include "ODefFileformat.h" #include "PlatformUtils.h" #include "ProceduralManager.h" #include "Road2.h" @@ -521,57 +522,36 @@ void TerrainObjectManager::LoadTerrainObject(const Ogre::String& name, const Ogr if (name.empty()) return; - char mesh[1024] = {}; - char line[1024] = {}; - char collmesh[1024] = {}; - Vector3 l(Vector3::ZERO); - Vector3 h(Vector3::ZERO); - Vector3 dr(Vector3::ZERO); - Vector3 fc(Vector3::ZERO); - Vector3 sc(Vector3::ZERO); - Vector3 sr(Vector3::ZERO); - bool forcecam = false; - bool ismovable = false; - - int event_filter = EVENT_ALL; - Quaternion rotation = Quaternion(Degree(rot.x), Vector3::UNIT_X) * Quaternion(Degree(rot.y), Vector3::UNIT_Y) * Quaternion(Degree(rot.z), Vector3::UNIT_Z); String odefgroup = ""; String odefname = name + ".odef"; if (!RoR::App::GetCacheSystem()->CheckResourceLoaded(odefname, odefgroup)) + { { LOG("Error while loading Terrain: could not find required .odef file: " + odefname + ". Ignoring entry."); return; } + } DataStreamPtr ds = ResourceGroupManager::getSingleton().openResource(odefname, odefgroup); - ds->readLine(mesh, 1023); - if (String(mesh) == "LOD") - { - // LOD line is obsolete - ds->readLine(mesh, 1023); - } - - //scale - ds->readLine(line, 1023); - sscanf(line, "%f, %f, %f", &sc.x, &sc.y, &sc.z); + ODefParser parser; + parser.Prepare(); + parser.ProcessOgreStream(ds.get()); + std::shared_ptr odef = parser.Finalize(); static int objcounter = 0; - String entity_name = "object" + TOSTRING(objcounter) + "(" + name + ")"; - RoR::Utils::SanitizeUtf8String(entity_name); - objcounter++; SceneNode* tenode = App::GetGfxScene()->GetSceneManager()->getRootSceneNode()->createChildSceneNode(); MeshObject* mo = nullptr; - if (String(mesh) != "none") + if (odef->header.mesh_name != "none") { - mo = new MeshObject(mesh, Ogre::RGN_AUTODETECT, entity_name, tenode); + mo = new MeshObject(odef->header.mesh_name, Ogre::RGN_AUTODETECT, odef->header.entity_name, tenode); m_mesh_objects.push_back(mo); } - tenode->setScale(sc); + tenode->setScale(odef->header.scale); tenode->setPosition(pos); tenode->rotate(rotation); tenode->pitch(Degree(-90)); @@ -606,496 +586,266 @@ void TerrainObjectManager::LoadTerrainObject(const Ogre::String& name, const Ogr } } - //collision box(es) - bool virt = false; - bool rotating = false; - bool classic_ref = true; - // everything is of concrete by default - ground_model_t* gm = terrainManager->GetCollisions()->getGroundModelByString("concrete"); - char eventname[256] = {}; - while (!ds->eof()) + for (ODef::Localizer loc_type : odef->localizers) { - size_t ll = ds->readLine(line, 1023); - - // little workaround to trim it - String line_str = String(line); - Ogre::StringUtil::trim(line_str); - RoR::Utils::SanitizeUtf8String(line_str); - - const char* ptline = line_str.c_str(); - if (ll == 0 || line[0] == '/' || line[0] == ';') - continue; - - if (!strcmp("end", ptline)) - break; - if (!strcmp("movable", ptline)) - { - ismovable = true; - continue; - }; - if (!strcmp("localizer-h", ptline)) - { - localizer_t loc; - loc.position = Vector3(pos.x, pos.y, pos.z); - loc.rotation = rotation; - loc.type = Autopilot::LOCALIZER_HORIZONTAL; - localizers.push_back(loc); - continue; - } - if (!strcmp("localizer-v", ptline)) - { - localizer_t loc; - loc.position = Vector3(pos.x, pos.y, pos.z); - loc.rotation = rotation; - loc.type = Autopilot::LOCALIZER_VERTICAL; - localizers.push_back(loc); - continue; - } - if (!strcmp("localizer-ndb", ptline)) - { - localizer_t loc; - loc.position = Vector3(pos.x, pos.y, pos.z); - loc.rotation = rotation; - loc.type = Autopilot::LOCALIZER_NDB; - localizers.push_back(loc); - continue; - } - if (!strcmp("localizer-vor", ptline)) + int type; + switch (loc_type) { - localizer_t loc; - loc.position = Vector3(pos.x, pos.y, pos.z); - loc.rotation = rotation; - loc.type = Autopilot::LOCALIZER_VOR; - localizers.push_back(loc); - continue; + case ODef::Localizer::HORIZONTAL: type = Autopilot::LOCALIZER_HORIZONTAL; break; + case ODef::Localizer::VERTICAL: type = Autopilot::LOCALIZER_VERTICAL; break; + case ODef::Localizer::NDB: type = Autopilot::LOCALIZER_NDB; break; + case ODef::Localizer::VOR: type = Autopilot::LOCALIZER_VOR; break; + default: continue; // Invalid - skip this } - if (!strcmp("standard", ptline)) - { - classic_ref = false; - tenode->pitch(Degree(90)); - continue; - }; - if (!strncmp("sound", ptline, 5)) - { + localizer_t loc; + loc.position = Vector3(pos.x, pos.y, pos.z); + loc.rotation = rotation; + loc.type = type; + localizers.push_back(loc); + } + + if (odef->mode_standard) + { + tenode->pitch(Degree(90)); + } + #ifdef USE_OPENAL - if (!App::GetSoundScriptManager()->isDisabled()) - { - char tmp[255] = ""; - sscanf(ptline, "sound %s", tmp); - SoundScriptInstance* sound = App::GetSoundScriptManager()->createInstance(tmp, MAX_ACTORS + 1, tenode); - sound->setPosition(tenode->getPosition(), Vector3::ZERO); - sound->start(); - } -#endif //USE_OPENAL - continue; - } - if (!strcmp("beginbox", ptline) || !strcmp("beginmesh", ptline)) - { - dr = Vector3::ZERO; - rotating = false; - virt = false; - forcecam = false; - event_filter = EVENT_NONE; - eventname[0] = 0; - collmesh[0] = 0; - gm = terrainManager->GetCollisions()->getGroundModelByString("concrete"); - continue; - }; - if (!strncmp("boxcoords", ptline, 9)) - { - sscanf(ptline, "boxcoords %f, %f, %f, %f, %f, %f", &l.x, &h.x, &l.y, &h.y, &l.z, &h.z); - continue; - } - if (!strncmp("mesh", ptline, 4)) - { - sscanf(ptline, "mesh %s", collmesh); - continue; - } - if (!strncmp("rotate", ptline, 6)) - { - sscanf(ptline, "rotate %f, %f, %f", &sr.x, &sr.y, &sr.z); - rotating = true; - continue; - } - if (!strncmp("forcecamera", ptline, 11)) - { - sscanf(ptline, "forcecamera %f, %f, %f", &fc.x, &fc.y, &fc.z); - forcecam = true; - continue; - } - if (!strncmp("direction", ptline, 9)) - { - sscanf(ptline, "direction %f, %f, %f", &dr.x, &dr.y, &dr.z); - continue; - } - if (!strncmp("frictionconfig", ptline, 14) && strlen(ptline) > 15) - { - // load a custom friction config - terrainManager->GetCollisions()->loadGroundModelsConfigFile(String(ptline + 15)); - continue; - } - if ((!strncmp("stdfriction", ptline, 11) || !strncmp("usefriction", ptline, 11)) && strlen(ptline) > 12) + if (!App::GetSoundScriptManager()->isDisabled()) + { + for (std::string& snd_name : odef->sounds) { - String modelName = String(ptline + 12); - gm = terrainManager->GetCollisions()->getGroundModelByString(modelName); - continue; + SoundScriptInstance* sound = App::GetSoundScriptManager()->createInstance(snd_name, MAX_ACTORS+1, tenode); + sound->setPosition(tenode->getPosition(), Vector3::ZERO); + sound->start(); } - if (!strcmp("virtual", ptline)) - { - virt = true; - continue; - }; - if (!strncmp("event", ptline, 5)) - { - char ts[256]; - ts[0] = 0; - sscanf(ptline, "event %s %s", eventname, ts); - if (!strncmp(ts, "avatar", 6)) - event_filter = EVENT_AVATAR; - else if (!strncmp(ts, "truck", 5)) - event_filter = EVENT_TRUCK; - else if (!strncmp(ts, "airplane", 8)) - event_filter = EVENT_AIRPLANE; - else if (!strncmp(ts, "boat", 8)) - event_filter = EVENT_BOAT; - else if (!strncmp(ts, "delete", 8)) - event_filter = EVENT_DELETE; - - // fallback - if (strlen(ts) == 0) - event_filter = EVENT_ALL; - - // hack to avoid fps drops near spawnzones - if (!strncmp(eventname, "spawnzone", 9)) - event_filter = EVENT_AVATAR; + } +#endif //USE_OPENAL - continue; - } - if (!strcmp("endbox", ptline)) - { - bool box_is_valid = (l.x <= h.x && l.y <= h.y && l.z <= h.z); // Check that size is never negative - if (!box_is_valid) - { - RoR::LogFormat("[ODEF] Invalid collision box (file: '%s', event: '%s', instance: '%s')," - " at least one axis has negative size. Ignoring the box.", - name.c_str(), eventname, instancename.c_str()); - continue; - } + for (std::string& gmodel_file: odef->groundmodel_files) + { + terrainManager->GetCollisions()->loadGroundModelsConfigFile(gmodel_file); + } - bool race_event = !instancename.compare(0, 10, "checkpoint") || !instancename.compare(0, 4, "race"); - if (enable_collisions && (App::sim_races_enabled->GetBool() || !race_event)) - { - int boxnum = terrainManager->GetCollisions()->addCollisionBox(tenode, rotating, virt, pos, rot, l, h, sr, eventname, instancename, forcecam, fc, sc, dr, event_filter, scripthandler); - obj->collBoxes.push_back((boxnum)); + for (ODefCollisionBox& cbox : odef->collision_boxes) + { + int boxnum = terrainManager->GetCollisions()->addCollisionBox( + tenode, cbox.is_rotating, cbox.is_virtual, pos, rot, + cbox.aabb_min, cbox.aabb_max, cbox.box_rot, cbox.event_name, + instancename, cbox.force_cam_pos, cbox.cam_pos, + cbox.scale, cbox.direction, cbox.event_filter, scripthandler); - if (race_event) - { - String type = "checkpoint"; - auto res = StringUtil::split(instancename, "|"); - if ((res.size() == 4 && res[2] == "0") || !instancename.compare(0, 4, "race")) - { - type = "racestart"; - } - int race_id = res.size() > 1 ? StringConverter::parseInt(res[1], -1) : -1; - m_map_entities.push_back({type, "", pos, rot.y, race_id}); - } - else if (!type.empty()) - { - String caption = ""; - if (type == "station" || type == "hotel" || type == "village" || - type == "observatory" || type == "farm" || type == "ship") - { - caption = instancename + " " + type; - } - m_map_entities.push_back({type, caption, pos, rot.y, -1}); - } - } - continue; - } - if (!strcmp("endmesh", ptline)) - { - if(strcmp("", collmesh) == 0) - RoR::LogFormat("[ODEF] Collision mesh not found in file %s", odefname.c_str()); - else - terrainManager->GetCollisions()->addCollisionMesh(collmesh, Vector3(pos.x, pos.y, pos.z), tenode->getOrientation(), sc, gm, &(obj->collTris)); - continue; - } + obj->collBoxes.push_back(boxnum); + } - if (!strncmp("particleSystem", ptline, 14) && tenode) - { - float x = 0, y = 0, z = 0, scale = 0; - char pname[255] = "", sname[255] = ""; - int res = sscanf(ptline, "particleSystem %f, %f, %f, %f, %s %s", &scale, &x, &y, &z, pname, sname); - if (res != 6) - continue; + for (ODefCollisionMesh& cmesh : odef->collision_meshes) + { + auto gm = terrainManager->GetCollisions()->getGroundModelByString(cmesh.groundmodel_name); + terrainManager->GetCollisions()->addCollisionMesh( + cmesh.mesh_name, pos, tenode->getOrientation(), + cmesh.scale, gm, &(obj->collTris)); + } + + for (ODefParticleSys& psys : odef->particle_systems) + { - // hacky: prevent duplicates - String paname = String(pname); - while (App::GetGfxScene()->GetSceneManager()->hasParticleSystem(paname)) - paname += "_"; + // hacky: prevent duplicates + String paname = String(psys.instance_name); + while (App::GetGfxScene()->GetSceneManager()->hasParticleSystem(paname)) + paname += "_"; - // create particle system - ParticleSystem* pParticleSys = App::GetGfxScene()->GetSceneManager()->createParticleSystem(paname, String(sname)); - pParticleSys->setCastShadows(false); - pParticleSys->setVisibilityFlags(DEPTHMAP_DISABLED); // disable particles in depthmap + // create particle system + ParticleSystem* pParticleSys = App::GetGfxScene()->GetSceneManager()->createParticleSystem(paname, String(psys.template_name)); + pParticleSys->setCastShadows(false); + pParticleSys->setVisibilityFlags(DEPTHMAP_DISABLED); // disable particles in depthmap - // Some affectors may need its instance name (e.g. for script feedback purposes) + // Some affectors may need its instance name (e.g. for script feedback purposes) #ifdef USE_ANGELSCRIPT - unsigned short affCount = pParticleSys->getNumAffectors(); - ParticleAffector* pAff; - for (unsigned short i = 0; i < affCount; ++i) + unsigned short affCount = pParticleSys->getNumAffectors(); + ParticleAffector* pAff; + for (unsigned short i = 0; i < affCount; ++i) + { + pAff = pParticleSys->getAffector(i); + if (pAff->getType() == "ExtinguishableFire") { - pAff = pParticleSys->getAffector(i); - if (pAff->getType() == "ExtinguishableFire") - { - ((ExtinguishableFireAffector*)pAff)->setInstanceName(obj->instanceName); - } + ((ExtinguishableFireAffector*)pAff)->setInstanceName(obj->instanceName); } + } #endif // USE_ANGELSCRIPT - SceneNode* sn = tenode->createChildSceneNode(); - sn->attachObject(pParticleSys); - sn->pitch(Degree(90)); - continue; + SceneNode* sn = tenode->createChildSceneNode(); + sn->attachObject(pParticleSys); + sn->pitch(Degree(90)); + } + + if (!odef->mat_name.empty()) + { + if (mo->getEntity()) + { + mo->getEntity()->setMaterialName(odef->mat_name); } + } - if (!strncmp("setMeshMaterial", ptline, 15)) + if (odef->mat_name_generate != "") + { + Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(odef->mat_name_generate,"generatedMaterialShaders"); + Ogre::RTShader::ShaderGenerator::getSingleton().createShaderBasedTechnique(*mat, Ogre::MaterialManager::DEFAULT_SCHEME_NAME, Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); + Ogre::RTShader::ShaderGenerator::getSingleton().invalidateMaterial(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, String(odef->mat_name_generate)); + } + + for (ODefAnimation& anim : odef->animations) + { + if (tenode && mo->getEntity()) { - char mat[256] = ""; - sscanf(ptline, "setMeshMaterial %s", mat); - if (mo->getEntity() && strnlen(mat, 250) > 0) + AnimationStateSet *s = mo->getEntity()->getAllAnimationStates(); + String anim_name_str(anim.name); + if (!s->hasAnimationState(anim_name_str)) { - mo->getEntity()->setMaterialName(String(mat)); + LOG("[ODEF] animation '" + anim_name_str + "' for mesh: '" + odef->header.mesh_name + "' in odef file '" + name + ".odef' not found!"); + //continue; } - continue; - } - if (!strncmp("generateMaterialShaders", ptline, 23)) - { - char matn[256] = ""; - sscanf(ptline, "generateMaterialShaders %s", matn); - if (RoR::App::gfx_enable_rtshaders->GetBool()) + AnimatedObject ao; + ao.node = tenode; + ao.ent = mo->getEntity(); + ao.speedfactor = anim.speed_min; + if (anim.speed_min != anim.speed_max) + ao.speedfactor = Math::RangeRandom(anim.speed_min, anim.speed_max); + ao.anim = 0; + try + { + ao.anim = mo->getEntity()->getAnimationState(anim_name_str); + } catch (...) + { + ao.anim = 0; + } + if (!ao.anim) { - MaterialPtr mat = MaterialManager::getSingleton().create(matn,"generatedMaterialShaders"); - Ogre::RTShader::ShaderGenerator::getSingleton().createShaderBasedTechnique(*mat, Ogre::MaterialManager::DEFAULT_SCHEME_NAME, Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); - Ogre::RTShader::ShaderGenerator::getSingleton().invalidateMaterial(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, matn); + LOG("[ODEF] animation '" + anim_name_str + "' for mesh: '" + odef->header.mesh_name + "' in odef file '" + name + ".odef' not found!"); + continue; } + ao.anim->setEnabled(true); + m_animated_objects.push_back(ao); + } + } + for (ODefTexPrint& tex_print : odef->texture_prints) + { + if (!mo->getEntity()) + continue; + String matName = mo->getEntity()->getSubEntity(0)->getMaterialName(); + MaterialPtr m = MaterialManager::getSingleton().getByName(matName); + if (m.getPointer() == 0) + { + LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh material not found: "+odefname+" : "+matName); continue; } - if (!strncmp("playanimation", ptline, 13) && mo) + String texName = m->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureName(); + Texture* background = (Texture *)TextureManager::getSingleton().getByName(texName).getPointer(); + if (!background) { - char animname[256] = ""; - float speedfactorMin = 0, speedfactorMax = 0; - sscanf(ptline, "playanimation %f, %f, %s", &speedfactorMin, &speedfactorMax, animname); - if (tenode && mo->getEntity() && strnlen(animname, 250) > 0) - { - AnimationStateSet* s = mo->getEntity()->getAllAnimationStates(); - if (!s->hasAnimationState(String(animname))) - { - LOG("[ODEF] animation '" + String(animname) + "' for mesh: '" + String(mesh) + "' in odef file '" + name + ".odef' not found!"); - continue; - } - AnimatedObject ao; - ao.node = tenode; - ao.ent = mo->getEntity(); - ao.speedfactor = speedfactorMin; - if (speedfactorMin != speedfactorMax) - ao.speedfactor = Math::RangeRandom(speedfactorMin, speedfactorMax); - ao.anim = 0; - try - { - ao.anim = mo->getEntity()->getAnimationState(String(animname)); - } - catch (...) - { - ao.anim = 0; - } - if (!ao.anim) - { - LOG("[ODEF] animation '" + String(animname) + "' for mesh: '" + String(mesh) + "' in odef file '" + name + ".odef' not found!"); - continue; - } - ao.anim->setEnabled(true); - m_animated_objects.push_back(ao); - } + LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh texture not found: "+odefname+" : "+texName); continue; } - if (!strncmp("drawTextOnMeshTexture", ptline, 21) && mo) + + static int textureNumber = 0; + textureNumber++; + char tmpTextName[256] = "", tmpMatName[256] = ""; + sprintf(tmpTextName, "TextOnTexture_%d_Texture", textureNumber); + sprintf(tmpMatName, "TextOnTexture_%d_Material", textureNumber); // Make sure the texture is not WRITE_ONLY, we need to read the buffer to do the blending with the font (get the alpha for example) + TexturePtr texture = TextureManager::getSingleton().createManual(tmpTextName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, (Ogre::uint)background->getWidth(), (Ogre::uint)background->getHeight(), MIP_UNLIMITED, PF_X8R8G8B8, Ogre::TU_STATIC | Ogre::TU_AUTOMIPMAP); + if (texture.getPointer() == 0) { - if (!mo->getEntity()) - continue; - String matName = mo->getEntity()->getSubEntity(0)->getMaterialName(); - MaterialPtr m = MaterialManager::getSingleton().getByName(matName); - if (m.getPointer() == 0) - { - LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh material not found: "+odefname+" : "+String(ptline)); - continue; - } - String texName = m->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureName(); - Texture* background = (Texture *)TextureManager::getSingleton().getByName(texName).getPointer(); - if (!background) - { - LOG("[ODEF] problem with drawTextOnMeshTexture command: mesh texture not found: "+odefname+" : "+String(ptline)); - continue; - } + LOG("[ODEF] problem with drawTextOnMeshTexture command: could not create texture: "+odefname+" : "+tmpTextName); + continue; + } - static int textureNumber = 0; - textureNumber++; - char tmpTextName[256] = "", tmpMatName[256] = ""; - sprintf(tmpTextName, "TextOnTexture_%d_Texture", textureNumber); - sprintf(tmpMatName, "TextOnTexture_%d_Material", textureNumber); // Make sure the texture is not WRITE_ONLY, we need to read the buffer to do the blending with the font (get the alpha for example) - TexturePtr texture = TextureManager::getSingleton().createManual(tmpTextName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, (Ogre::uint)background->getWidth(), (Ogre::uint)background->getHeight(), MIP_UNLIMITED, PF_X8R8G8B8, Ogre::TU_STATIC | Ogre::TU_AUTOMIPMAP); - if (texture.getPointer() == 0) - { - LOG("[ODEF] problem with drawTextOnMeshTexture command: could not create texture: "+odefname+" : "+String(ptline)); - continue; - } + char text[ODef::STR_LEN]; + strcpy(text, tex_print.text); - float x = 0, y = 0, w = 0, h = 0; - float a = 0, r = 0, g = 0, b = 0; - int fs = 40, fdpi = 144; - char fontname[256] = ""; - char text[256] = ""; - char option = 'l'; - int res = sscanf(ptline, "drawTextOnMeshTexture %f, %f, %f, %f, %f, %f, %f, %f, %c, %i, %i, %s %s", &x, &y, &w, &h, &r, &g, &b, &a, &option, &fs, &fdpi, fontname, text); - if (res < 13) - { - LOG("[ODEF] problem with drawTextOnMeshTexture command: "+odefname+" : "+String(ptline)); - continue; - } + // check if we got a template argument + if (!strncmp(text, "{{argument1}}", 13)) + strncpy(text, instancename.c_str(), 250); - // check if we got a template argument - if (!strncmp(text, "{{argument1}}", 13)) - strncpy(text, instancename.c_str(), 250); + // replace '_' with ' ' + char *text_pointer = text; + while (*text_pointer!=0) {if (*text_pointer=='_') *text_pointer=' ';text_pointer++;}; - // replace '_' with ' ' - char* text_pointer = text; - while (*text_pointer != 0) - { - if (*text_pointer == '_') - *text_pointer = ' '; - text_pointer++; - }; + String font_name_str(tex_print.font_name); + Font* font = (Font *)FontManager::getSingleton().getByName(font_name_str).getPointer(); + if (!font) + { + LOG("[ODEF] problem with drawTextOnMeshTexture command: font not found: "+odefname+" : "+font_name_str); + continue; + } - Ogre::Font* font = (Ogre::Font *)FontManager::getSingleton().getByName(String(fontname)).getPointer(); - if (!font) - { - LOG("[ODEF] problem with drawTextOnMeshTexture command: font not found: "+odefname+" : "+String(ptline)); - continue; - } + //Draw the background to the new texture + texture->getBuffer()->blit(background->getBuffer()); - //Draw the background to the new texture - texture->getBuffer()->blit(background->getBuffer()); + float x = background->getWidth() * tex_print.x; + float y = background->getHeight() * tex_print.y; + float w = background->getWidth() * tex_print.w; + float h = background->getHeight() * tex_print.h; - x = background->getWidth() * x; - y = background->getHeight() * y; - w = background->getWidth() * w; - h = background->getHeight() * h; + ColourValue color(tex_print.r, tex_print.g, tex_print.b, tex_print.a); + Ogre::Box box = Ogre::Box((size_t)x, (size_t)y, (size_t)(x+w), (size_t)(y+h)); + WriteToTexture(String(text), texture, box, font, color, tex_print.font_size, tex_print.font_dpi, tex_print.option); - Box box = Box((size_t)x, (size_t)y, (size_t)(x + w), (size_t)(y + h)); - WriteToTexture(String(text), texture, box, font, ColourValue(r, g, b, a), fs, fdpi, option); + // we can save it to disc for debug purposes: + //SaveImage(texture, "test.png"); - // we can save it to disc for debug purposes: - //SaveImage(texture, "test.png"); + m->clone(tmpMatName); + MaterialPtr mNew = MaterialManager::getSingleton().getByName(tmpMatName); + mNew->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(tmpTextName); - m->clone(tmpMatName); - MaterialPtr mNew = MaterialManager::getSingleton().getByName(tmpMatName); - mNew->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(tmpTextName); + mo->getEntity()->setMaterialName(String(tmpMatName)); + } - mo->getEntity()->setMaterialName(String(tmpMatName)); - continue; - } + for (ODefSpotlight& spotl: odef->spotlights) + { + Light* spotLight = App::GetGfxScene()->GetSceneManager()->createLight(spotl.name); - if (!strncmp("spotlight", ptline, 9)) - { - Vector3 lpos, ldir; - float lrange = 10, innerAngle = 45, outerAngle = 45; - ColourValue lcol; + spotLight->setType(Light::LT_SPOTLIGHT); + spotLight->setPosition(spotl.pos); + spotLight->setDirection(spotl.dir); + spotLight->setAttenuation(spotl.range, 1.0, 0.3, 0.0); + spotLight->setDiffuseColour(spotl.color); + spotLight->setSpecularColour(spotl.color); + spotLight->setSpotlightRange(Degree(spotl.angle_inner), Degree(spotl.angle_outer)); - int res = sscanf(ptline, "spotlight %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f", - &lpos.x, &lpos.y, &lpos.z, &ldir.x, &ldir.y, &ldir.z, &lcol.r, &lcol.g, &lcol.b, &lrange, &innerAngle, &outerAngle); - if (res < 12) - { - LOG("ODEF: problem with light command: " + odefname + " : " + String(ptline)); - continue; - } + BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); + lflare->createBillboard(spotl.pos, spotl.color); + lflare->setMaterialName("tracks/flare"); + lflare->setVisibilityFlags(DEPTHMAP_DISABLED); - static unsigned int counter = 0; - char name[50]; - snprintf(name, 50, "terrn2/spotlight-%u", counter); - ++counter; - Light* spotLight = App::GetGfxScene()->GetSceneManager()->createLight(name); - - spotLight->setType(Light::LT_SPOTLIGHT); - spotLight->setPosition(lpos); - spotLight->setDirection(ldir); - spotLight->setAttenuation(lrange, 1.0, 0.3, 0.0); - spotLight->setDiffuseColour(lcol); - spotLight->setSpecularColour(lcol); - spotLight->setSpotlightRange(Degree(innerAngle), Degree(outerAngle)); - - BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); - lflare->createBillboard(lpos, lcol); - lflare->setMaterialName("tracks/flare"); - lflare->setVisibilityFlags(DEPTHMAP_DISABLED); - - float fsize = Math::Clamp(lrange / 10, 0.2f, 2.0f); - lflare->setDefaultDimensions(fsize, fsize); - - SceneNode* sn = tenode->createChildSceneNode(); - sn->attachObject(spotLight); - sn->attachObject(lflare); - continue; - } + float fsize = Math::Clamp(spotl.range / 10, 0.2f, 2.0f); + lflare->setDefaultDimensions(fsize, fsize); - if (!strncmp("pointlight", ptline, 10)) - { - Vector3 lpos, ldir; - float lrange = 10; - ColourValue lcol; + SceneNode *sn = tenode->createChildSceneNode(); + sn->attachObject(spotLight); + sn->attachObject(lflare); + } - int res = sscanf(ptline, "pointlight %f, %f, %f, %f, %f, %f, %f, %f, %f, %f", - &lpos.x, &lpos.y, &lpos.z, &ldir.x, &ldir.y, &ldir.z, &lcol.r, &lcol.g, &lcol.b, &lrange); - if (res < 10) - { - LOG("ODEF: problem with light command: " + odefname + " : " + String(ptline)); - continue; - } + for (ODefPointLight& plight : odef->point_lights) + { + Light* pointlight = App::GetGfxScene()->GetSceneManager()->createLight(plight.name); - static unsigned int counter = 0; - char name[50]; - snprintf(name, 50, "terrn2/pointlight-%x", counter); - ++counter; - Light* pointlight = App::GetGfxScene()->GetSceneManager()->createLight(name); - - pointlight->setType(Light::LT_POINT); - pointlight->setPosition(lpos); - pointlight->setDirection(ldir); - pointlight->setAttenuation(lrange, 1.0, 0.3, 0.0); - pointlight->setDiffuseColour(lcol); - pointlight->setSpecularColour(lcol); - - BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); - lflare->createBillboard(lpos, lcol); - lflare->setMaterialName("tracks/flare"); - lflare->setVisibilityFlags(DEPTHMAP_DISABLED); - - float fsize = Math::Clamp(lrange / 10, 0.2f, 2.0f); - lflare->setDefaultDimensions(fsize, fsize); - - SceneNode* sn = tenode->createChildSceneNode(); - sn->attachObject(pointlight); - sn->attachObject(lflare); - continue; - } + pointlight->setType(Light::LT_POINT); + pointlight->setPosition(plight.pos); + pointlight->setDirection(plight.dir); + pointlight->setAttenuation(plight.range, 1.0, 0.3, 0.0); + pointlight->setDiffuseColour(plight.color); + pointlight->setSpecularColour(plight.color); - if (!strncmp("nocast", ptline, 6)) - { - mo->getEntity()->setCastShadows(false); - continue; - } + BillboardSet* lflare = App::GetGfxScene()->GetSceneManager()->createBillboardSet(1); + lflare->createBillboard(plight.pos, plight.color); + lflare->setMaterialName("tracks/flare"); + lflare->setVisibilityFlags(DEPTHMAP_DISABLED); + + float fsize = Math::Clamp(plight.range / 10, 0.2f, 2.0f); + lflare->setDefaultDimensions(fsize, fsize); - LOG("ODEF: unknown command in "+odefname+" : "+String(ptline)); + SceneNode *sn = tenode->createChildSceneNode(); + sn->attachObject(pointlight); + sn->attachObject(lflare); } }