diff --git a/source/main/gfx/GfxActor.cpp b/source/main/gfx/GfxActor.cpp index e491dc7fbf..fdcf14873a 100644 --- a/source/main/gfx/GfxActor.cpp +++ b/source/main/gfx/GfxActor.cpp @@ -2998,8 +2998,13 @@ void RoR::GfxActor::UpdateFlexbodies() for (FlexBody* fb: m_flexbodies) { - const int camera_mode = fb->getCameraMode(); - if ((camera_mode == -2) || (camera_mode == m_simbuf.simbuf_cur_cinecam)) + // Update visibility + fb->setVisible( + fb->getCameraMode() == CAMERA_MODE_ALWAYS_VISIBLE + || fb->getCameraMode() == m_simbuf.simbuf_cur_cinecam); + + // Update visible on background thread + if (fb->isVisible()) { auto func = std::function([fb]() { @@ -3008,10 +3013,6 @@ void RoR::GfxActor::UpdateFlexbodies() auto task_handle = App::GetThreadPool()->RunTask(func); m_flexbody_tasks.push_back(task_handle); } - else - { - fb->setVisible(false); - } } } @@ -3039,7 +3040,10 @@ void RoR::GfxActor::FinishFlexbodyTasks() } for (FlexBody* fb: m_flexbodies) { - fb->updateFlexbodyVertexBuffers(); + if (fb->isVisible()) + { + fb->updateFlexbodyVertexBuffers(); + } } } diff --git a/source/main/gui/panels/GUI_TopMenubar.cpp b/source/main/gui/panels/GUI_TopMenubar.cpp index 858ad8b3ad..53a42cce90 100644 --- a/source/main/gui/panels/GUI_TopMenubar.cpp +++ b/source/main/gui/panels/GUI_TopMenubar.cpp @@ -1652,7 +1652,7 @@ void TopMenubar::Draw(float dt) if (p.pp_beacon_type == 'L' || p.pp_beacon_type == 'R' || p.pp_beacon_type == 'w') { ImGui::SameLine(); - ImGui::TextDisabled("(!)"); + ImGui::TextDisabled("(special!)"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -1663,7 +1663,7 @@ void TopMenubar::Draw(float dt) else if (p.pp_wheel_mesh_obj) { ImGui::SameLine(); - ImGui::TextDisabled("(!)"); + ImGui::TextDisabled("(special!)"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); diff --git a/source/main/physics/ActorSpawner.cpp b/source/main/physics/ActorSpawner.cpp index 3f209a8181..7037923e3b 100644 --- a/source/main/physics/ActorSpawner.cpp +++ b/source/main/physics/ActorSpawner.cpp @@ -1496,7 +1496,7 @@ void ActorSpawner::ProcessFlexbody(RigDef::Flexbody& def) if (TuneupUtil::isFlexbodyRemoved(m_actor, flexbody_id)) { // Create placeholder - m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::TUNING_PLACEHOLDER)); + m_actor->m_gfx_actor->m_flexbodies.emplace_back(new FlexBody(FlexBody::TUNING_PLACEHOLDER, flexbody_id, def.mesh_name)); return; } @@ -1531,8 +1531,10 @@ void ActorSpawner::ProcessFlexbody(RigDef::Flexbody& def) this->GetNodeIndexOrThrow(def.reference_node), this->GetNodeIndexOrThrow(def.x_axis_node), this->GetNodeIndexOrThrow(def.y_axis_node), - TuneupUtil::getTweakedFlexbodyOffset(m_actor->getUsedTuneupEntry(), flexbody_id, def.offset), - TuneupUtil::getTweakedFlexbodyRotation(m_actor->getUsedTuneupEntry(), flexbody_id, def.rotation), + //TuneupUtil::getTweakedFlexbodyOffset(m_actor->getUsedTuneupEntry(), flexbody_id, def.offset), + //TuneupUtil::getTweakedFlexbodyRotation(m_actor->getUsedTuneupEntry(), flexbody_id, def.rotation), + def.offset, + def.rotation, node_indices, TuneupUtil::getTweakedFlexbodyMedia(m_actor->getUsedTuneupEntry(), flexbody_id, 0, def.mesh_name), TuneupUtil::getTweakedFlexbodyMediaRG(m_actor, flexbody_id, 0) diff --git a/source/main/physics/ActorSpawner.h b/source/main/physics/ActorSpawner.h index 517786dce7..f47733e40d 100644 --- a/source/main/physics/ActorSpawner.h +++ b/source/main/physics/ActorSpawner.h @@ -475,7 +475,7 @@ class ActorSpawner int m_first_wing_index; std::vector m_oldstyle_cab_texcoords; std::vector m_oldstyle_cab_submeshes; - RigDef::Keyword m_current_keyword; //!< For error reports + RigDef::Keyword m_current_keyword = RigDef::Keyword::INVALID; //!< For error reports std::shared_ptr m_current_module; //!< For resolving addonparts std::map m_named_nodes; /// @} diff --git a/source/main/physics/flex/FlexBody.cpp b/source/main/physics/flex/FlexBody.cpp index a9802d1b92..9a62b6f85f 100644 --- a/source/main/physics/flex/FlexBody.cpp +++ b/source/main/physics/flex/FlexBody.cpp @@ -519,9 +519,11 @@ FlexBody::FlexBody( } } -FlexBody::FlexBody(FlexBodyPlaceholder_t ticket) +FlexBody::FlexBody(FlexBodyPlaceholder_t ticket, FlexbodyID_t id, const std::string& orig_meshname) { m_camera_mode = CAMERA_MODE_ALWAYS_HIDDEN; + m_id = id; + m_orig_mesh_name = orig_meshname; } FlexBody::~FlexBody() @@ -554,6 +556,15 @@ FlexBody::~FlexBody() m_scene_entity = nullptr; } +bool FlexBody::isVisible() const +{ + // Scene node is NULL if disabled via addonpart/tuneup. + return m_scene_node + && m_scene_node->isInSceneGraph() + && m_scene_node->getAttachedObject(0)->isVisible(); + +} + void FlexBody::setVisible(bool visible) { if (m_scene_node) @@ -608,6 +619,9 @@ void FlexBody::computeFlexbody() void FlexBody::updateFlexbodyVertexBuffers() { + if (!m_scene_node) // Disabled via addonpart/tuneup + return; + Vector3 *ppt = m_dst_pos; Vector3 *npt = m_dst_normals; if (m_uses_shared_vertex_data) @@ -679,19 +693,6 @@ void FlexBody::updateBlend() //so easy! } } -std::string FlexBody::getOrigMeshName() -{ - std::string meshname = m_scene_entity->getMesh()->getName(); - // Cut off the generated '_FlexBody_#@Actor_#' part - // TODO: fix the resource management to create resource group for each actor (currently done for each repo ZIP) - then we won't need those unique tokens anymore. - size_t pos = meshname.find("_FlexBody_"); - if (pos != std::string::npos) - { - meshname = meshname.substr(0, pos); - } - return meshname; -} - int evalNodeDistance(NodeNum_t a, NodeNum_t b) { if (a > b) diff --git a/source/main/physics/flex/FlexBody.h b/source/main/physics/flex/FlexBody.h index 3f20508a0a..2e147c750d 100644 --- a/source/main/physics/flex/FlexBody.h +++ b/source/main/physics/flex/FlexBody.h @@ -61,7 +61,7 @@ class FlexBody typedef int FlexBodyPlaceholder_t; static const FlexBodyPlaceholder_t TUNING_PLACEHOLDER = -11; - FlexBody(FlexBodyPlaceholder_t); + FlexBody(FlexBodyPlaceholder_t, FlexbodyID_t id, const std::string& orig_meshname); ~FlexBody(); void reset(); @@ -76,6 +76,7 @@ class FlexBody void computeFlexbody(); //!< Updates mesh deformation; works on CPU using local copy of vertex data. void updateFlexbodyVertexBuffers(); + bool isVisible() const; void setVisible(bool visible); void setFlexbodyCastShadow(bool val); @@ -84,7 +85,7 @@ class FlexBody Locator_t& getVertexLocator(int vert) { ROR_ASSERT((size_t)vert < m_vertex_count); return m_locators[vert]; } Ogre::Vector3 getVertexPos(int vert) { ROR_ASSERT((size_t)vert < m_vertex_count); return m_dst_pos[vert] + m_flexit_center; } Ogre::Entity* getEntity() { return m_scene_entity; } - std::string getOrigMeshName(); + const std::string& getOrigMeshName() const { return m_orig_mesh_name; } std::vector& getForsetNodes() { return m_forset_nodes; }; std::string getOrigMeshInfo() { return m_orig_mesh_info; } std::string getLiveMeshInfo() { return RoR::PrintMeshInfo("Live", m_scene_entity->getMesh()); } @@ -137,6 +138,7 @@ class FlexBody // Diagnostic data, not used for calculations std::vector m_forset_nodes; std::string m_orig_mesh_info; + std::string m_orig_mesh_name; }; /// @} // addtogroup Flex diff --git a/source/main/physics/flex/FlexFactory.cpp b/source/main/physics/flex/FlexFactory.cpp index e75764f090..19eecc5fb9 100644 --- a/source/main/physics/flex/FlexFactory.cpp +++ b/source/main/physics/flex/FlexFactory.cpp @@ -107,6 +107,7 @@ FlexBody* FlexFactory::CreateFlexBody( m_flexbody_cache.AddItemToSave(new_flexbody); } new_flexbody->m_id = flexbody_id; + new_flexbody->m_orig_mesh_name = common_mesh->getName(); return new_flexbody; } diff --git a/source/main/resources/CacheSystem.cpp b/source/main/resources/CacheSystem.cpp index 59171ecdbf..c5028d8212 100644 --- a/source/main/resources/CacheSystem.cpp +++ b/source/main/resources/CacheSystem.cpp @@ -1740,6 +1740,9 @@ void CacheSystem::ModifyProject(ModifyProjectRequest* request) tuneup_entry->tuneup_def->use_addonparts.clear(); tuneup_entry->tuneup_def->wheel_tweaks.clear(); tuneup_entry->tuneup_def->node_tweaks.clear(); + tuneup_entry->tuneup_def->protected_flexbodies.clear(); + tuneup_entry->tuneup_def->protected_props.clear(); + tuneup_entry->tuneup_def->protected_wheels.clear(); break; default: diff --git a/source/main/resources/addonpart_fileformat/AddonPartFileFormat.cpp b/source/main/resources/addonpart_fileformat/AddonPartFileFormat.cpp index 2bdfee7d81..61de085449 100644 --- a/source/main/resources/addonpart_fileformat/AddonPartFileFormat.cpp +++ b/source/main/resources/addonpart_fileformat/AddonPartFileFormat.cpp @@ -137,6 +137,10 @@ void AddonPartUtility::ResolveUnwantedAndTweakedElements(TuneupDefPtr& tuneup, C this->ProcessTweakWheel(); else if (m_context->getTokKeyword() == "addonpart_tweak_node") this->ProcessTweakNode(); + else if (m_context->getTokKeyword() == "addonpart_tweak_prop") + this->ProcessTweakProp(); + else if (m_context->getTokKeyword() == "addonpart_tweak_flexbody") + this->ProcessTweakFlexbody(); } m_context->seekNextLine(); @@ -332,7 +336,6 @@ void AddonPartUtility::ProcessTweakWheel() { ROR_ASSERT(m_context->getTokKeyword() == "addonpart_tweak_wheel"); // also asserts !EOF and TokenType::KEYWORD - // 'addonpart_tweak_wheel ' if (m_context->isTokFloat(1) && m_context->isTokString(2)) { const int wheel_id = (int)m_context->getTokFloat(1); @@ -380,7 +383,6 @@ void AddonPartUtility::ProcessTweakNode() { ROR_ASSERT(m_context->getTokKeyword() == "addonpart_tweak_node"); // also asserts !EOF and TokenType::KEYWORD - // Data of 'addonpart_tweak_node ' if (m_context->isTokFloat(1) && m_context->isTokFloat(1) && m_context->isTokFloat(2) && m_context->isTokFloat(3)) { NodeNum_t nodenum = (NodeNum_t)m_context->getTokFloat(1); @@ -421,3 +423,114 @@ void AddonPartUtility::ProcessTweakNode() LOG(fmt::format("[RoR|Addonpart] WARNING: file '{}', element '{}': bad arguments", m_addonpart_entry->fname, m_context->getTokKeyword())); } } + +void AddonPartUtility::ProcessTweakFlexbody() +{ + ROR_ASSERT(m_context->getTokKeyword() == "addonpart_tweak_flexbody"); // also asserts !EOF and TokenType::KEYWORD + + // TBD: add `null` token type to GenericDocument, so these params can be made optional + if (m_context->isTokFloat(1) && // ID + m_context->isTokFloat(2) && m_context->isTokFloat(3) && m_context->isTokFloat(4) && // offset + m_context->isTokFloat(5) && m_context->isTokFloat(6) && m_context->isTokFloat(7) && // rotation + m_context->isTokString(8)) // media + { + const int flexbody_id = (int)m_context->getTokFloat(1); + if (!m_tuneup->isFlexbodyProtected(flexbody_id)) + { + if (m_tuneup->flexbody_tweaks.find(flexbody_id) == m_tuneup->flexbody_tweaks.end()) + { + TuneupFlexbodyTweak data; + data.tft_origin = m_addonpart_entry->fname; + data.tft_flexbody_id = flexbody_id; + data.tft_offset.x = m_context->getTokFloat(2); + data.tft_offset.y = m_context->getTokFloat(3); + data.tft_offset.z = m_context->getTokFloat(4); + data.tft_rotation.x = m_context->getTokFloat(5); + data.tft_rotation.y = m_context->getTokFloat(6); + data.tft_rotation.z = m_context->getTokFloat(7); + data.tft_media = m_context->getTokString(8); + m_tuneup->flexbody_tweaks.insert(std::make_pair(flexbody_id, data)); + + LOG(fmt::format("[RoR|Addonpart] INFO: file '{}', element '{}': tweaking flexbody {}" + " with params {{ offsetX={}, offsetY={}, offsetZ={}, rotX={}, rotY={}, rotZ={}, media={} }}", + m_addonpart_entry->fname, m_context->getTokKeyword(), flexbody_id, + data.tft_offset.x, data.tft_offset.y, data.tft_offset.z, + data.tft_rotation.x, data.tft_rotation.y, data.tft_rotation.z, data.tft_media[0])); + } + else if (m_tuneup->flexbody_tweaks[flexbody_id].tft_origin != m_addonpart_entry->fname) + { + m_tuneup->flexbody_tweaks.erase(flexbody_id); + + LOG(fmt::format("[RoR|Addonpart] INFO: file '{}', element '{}': Conflict of tweaks at flexbody '{}', addon parts '{}' and '{}'", + m_addonpart_entry->fname, m_context->getTokKeyword(), flexbody_id, + m_tuneup->flexbody_tweaks[flexbody_id].tft_origin, m_addonpart_entry->fname)); + } + } + else + { + LOG(fmt::format("[RoR|Addonpart] INFO: file '{}', element '{}': skipping flexbody '{}' because it's marked PROTECTED", + m_addonpart_entry->fname, m_context->getTokKeyword(), (int)m_context->getTokFloat(1))); + } + } + else + { + LOG(fmt::format("[RoR|Addonpart] WARNING: file '{}', element '{}': bad arguments", m_addonpart_entry->fname, m_context->getTokKeyword())); + } +} + +void AddonPartUtility::ProcessTweakProp() +{ + ROR_ASSERT(m_context->getTokKeyword() == "addonpart_tweak_prop"); // also asserts !EOF and TokenType::KEYWORD + + // TBD: add `null` token type to GenericDocument, so these params can be made optional + if (m_context->isTokFloat(1) && // ID + m_context->isTokFloat(2) && m_context->isTokFloat(3) && m_context->isTokFloat(4) && // offset + m_context->isTokFloat(5) && m_context->isTokFloat(6) && m_context->isTokFloat(7) && // rotation + m_context->isTokString(8)) // media + { + const int prop_id = (int)m_context->getTokFloat(1); + if (!m_tuneup->isFlexbodyProtected(prop_id)) + { + if (m_tuneup->prop_tweaks.find(prop_id) == m_tuneup->prop_tweaks.end()) + { + TuneupPropTweak data; + data.tpt_origin = m_addonpart_entry->fname; + data.tpt_prop_id = prop_id; + + data.tpt_offset.x = m_context->getTokFloat(2); + data.tpt_offset.y = m_context->getTokFloat(3); + data.tpt_offset.z = m_context->getTokFloat(4); + data.tpt_rotation.x = m_context->getTokFloat(5); + data.tpt_rotation.y = m_context->getTokFloat(6); + data.tpt_rotation.z = m_context->getTokFloat(7); + data.tpt_media[0] = m_context->getTokString(8); + if (m_context->isTokString(9)) data.tpt_media[1] = m_context->getTokString(9); // <== Optional Media2 is specific for prop + m_tuneup->prop_tweaks.insert(std::make_pair(prop_id, data)); + + LOG(fmt::format("[RoR|Addonpart] INFO: file '{}', element '{}': tweaking prop {}" + " with params {{ media1={}, offsetX={}, offsetY={}, offsetZ={}, rotX={}, rotY={}, rotZ={}, media2={} }}", + m_addonpart_entry->fname, m_context->getTokKeyword(), prop_id, data.tpt_media[0], + data.tpt_offset.x, data.tpt_offset.y, data.tpt_offset.z, + data.tpt_rotation.x, data.tpt_rotation.y, data.tpt_rotation.z, + data.tpt_media[1])); + } + else if (m_tuneup->prop_tweaks[prop_id].tpt_origin != m_addonpart_entry->fname) + { + m_tuneup->prop_tweaks.erase(prop_id); + + LOG(fmt::format("[RoR|Addonpart] INFO: file '{}', element '{}': Conflict of tweaks at prop '{}', addon parts '{}' and '{}'", + m_addonpart_entry->fname, m_context->getTokKeyword(), prop_id, + m_tuneup->prop_tweaks[prop_id].tpt_origin, m_addonpart_entry->fname)); + } + } + else + { + LOG(fmt::format("[RoR|Addonpart] INFO: file '{}', element '{}': skipping prop '{}' because it's marked PROTECTED", + m_addonpart_entry->fname, m_context->getTokKeyword(), (int)m_context->getTokFloat(1))); + } + } + else + { + LOG(fmt::format("[RoR|Addonpart] WARNING: file '{}', element '{}': bad arguments", m_addonpart_entry->fname, m_context->getTokKeyword())); + } +} diff --git a/source/main/resources/addonpart_fileformat/AddonPartFileFormat.h b/source/main/resources/addonpart_fileformat/AddonPartFileFormat.h index bd44c6bd8c..317c6d4cc1 100644 --- a/source/main/resources/addonpart_fileformat/AddonPartFileFormat.h +++ b/source/main/resources/addonpart_fileformat/AddonPartFileFormat.h @@ -57,12 +57,14 @@ class AddonPartUtility void ProcessManagedMaterial(); void ProcessProp(); void ProcessFlexbody(); + void ProcessTweakWheel(); + void ProcessTweakNode(); + void ProcessTweakFlexbody(); + void ProcessTweakProp(); // Helpers of `ResolveUnwantedAndTweakedElements()`, they expect `m_context` to be in position: void ProcessUnwantedProp(); void ProcessUnwantedFlexbody(); - void ProcessTweakWheel(); - void ProcessTweakNode(); // Shared state: GenericDocumentPtr m_document; diff --git a/source/main/resources/tuneup_fileformat/TuneupFileFormat.cpp b/source/main/resources/tuneup_fileformat/TuneupFileFormat.cpp index 884dd93f79..f3e77b71e8 100644 --- a/source/main/resources/tuneup_fileformat/TuneupFileFormat.cpp +++ b/source/main/resources/tuneup_fileformat/TuneupFileFormat.cpp @@ -187,7 +187,7 @@ Ogre::Vector3 RoR::TuneupUtil::getTweakedPropRotation(CacheEntryPtr& tuneup_entr if (itor == tuneup_entry->tuneup_def->prop_tweaks.end()) return orig_val; - Ogre::Vector3 retval = itor->second.tpt_rot; + Ogre::Vector3 retval = itor->second.tpt_rotation; ROR_ASSERT(!isnan(retval.x)); ROR_ASSERT(!isnan(retval.y)); ROR_ASSERT(!isnan(retval.z)); @@ -268,7 +268,7 @@ Ogre::Vector3 RoR::TuneupUtil::getTweakedFlexbodyRotation(CacheEntryPtr& tuneup_ if (itor == tuneup_entry->tuneup_def->flexbody_tweaks.end()) return orig_val; - Ogre::Vector3 retval = itor->second.tft_rot; + Ogre::Vector3 retval = itor->second.tft_rotation; ROR_ASSERT(!isnan(retval.x)); ROR_ASSERT(!isnan(retval.y)); ROR_ASSERT(!isnan(retval.z)); diff --git a/source/main/resources/tuneup_fileformat/TuneupFileFormat.h b/source/main/resources/tuneup_fileformat/TuneupFileFormat.h index 93437733fd..b0cbe1f909 100644 --- a/source/main/resources/tuneup_fileformat/TuneupFileFormat.h +++ b/source/main/resources/tuneup_fileformat/TuneupFileFormat.h @@ -59,7 +59,7 @@ struct TuneupPropTweak //!< Data of 'addonpart_tweak_prop FlexbodyID_t tft_flexbody_id = FLEXBODYID_INVALID; std::string tft_media; Ogre::Vector3 tft_offset = Ogre::Vector3::ZERO; - Ogre::Vector3 tft_rot = Ogre::Vector3::ZERO; + Ogre::Vector3 tft_rotation = Ogre::Vector3::ZERO; std::string tft_origin; //!< Addonpart filename };