From 3b12aaca55b617c378d355ced40304ae0e636803 Mon Sep 17 00:00:00 2001 From: ohlidalp Date: Mon, 6 Jan 2025 21:14:02 +0100 Subject: [PATCH] :sparkles: Terrain editor: preloaded actors fully editable rotations now work --- source/main/GameContext.cpp | 2 +- source/main/physics/Actor.cpp | 10 +++- source/main/physics/Actor.h | 2 +- source/main/physics/SimData.h | 3 +- source/main/terrain/TerrainEditor.cpp | 62 +++++++++----------- source/main/terrain/TerrainObjectManager.cpp | 21 ++++--- source/main/terrain/TerrainObjectManager.h | 1 + 7 files changed, 54 insertions(+), 47 deletions(-) diff --git a/source/main/GameContext.cpp b/source/main/GameContext.cpp index 7e00c5b5b9..9403ed9e6c 100644 --- a/source/main/GameContext.cpp +++ b/source/main/GameContext.cpp @@ -417,7 +417,7 @@ void GameContext::ModifyActor(ActorModifyRequest& rq) } else if (rq.amr_type == ActorModifyRequest::Type::SOFT_RESPAWN) { - actor->softRespawn(rq.amr_softrespawn_pos); + actor->softRespawn(rq.amr_softrespawn_position, rq.amr_softrespawn_rotation); } else if (rq.amr_type == ActorModifyRequest::Type::REFRESH_VISUALS) { diff --git a/source/main/physics/Actor.cpp b/source/main/physics/Actor.cpp index 0ef246af6b..e5622db6f0 100644 --- a/source/main/physics/Actor.cpp +++ b/source/main/physics/Actor.cpp @@ -1289,7 +1289,7 @@ void Actor::resetPosition(Ogre::Vector3 translation, bool setInitPosition) calculateAveragePosition(); } -void Actor::softRespawn(Ogre::Vector3 spawnpos) +void Actor::softRespawn(Ogre::Vector3 spawnpos, Ogre::Quaternion spawnrot) { // Perform a hard reset (detach any linked actors etc...) this->SyncReset(/*reset_position:*/ false); @@ -1303,6 +1303,14 @@ void Actor::softRespawn(Ogre::Vector3 spawnpos) ar_nodes[i].Forces = Ogre::Vector3::ZERO; ar_nodes[i].Velocity = Ogre::Vector3::ZERO; } + + // Apply spawn position & spawn rotation + // (code taken as-is from `ActorManager::CreateNewActor()`) + for (NodeNum_t i = 0; i < ar_num_nodes; i++) + { + ar_nodes[i].AbsPosition = spawnpos + spawnrot * (ar_nodes[i].AbsPosition - spawnpos); + ar_nodes[i].RelPosition = ar_nodes[i].AbsPosition - ar_origin; + }; } void Actor::mouseMove(NodeNum_t node, Vector3 pos, float force) diff --git a/source/main/physics/Actor.h b/source/main/physics/Actor.h index 8966ee52ce..f0149ccc8b 100644 --- a/source/main/physics/Actor.h +++ b/source/main/physics/Actor.h @@ -100,7 +100,7 @@ class Actor : public RefCountingObject // not exported to scripting: void resetPosition(Ogre::Vector3 translation, bool setInitPosition); //!< Moves the actor to given world coords (pivot point is node 0). void resetPosition(float px, float pz, bool setInitPosition, float miny); //!< Moves the actor to given world coords (pivot point is node 0). - void softRespawn(Ogre::Vector3 spawnpos); //!< Use `MSG_SIM_MODIFY_ACTOR_REQUESTED` with type `SOFT_RESPAWN`; Resets the actor to given position as if spawned there (pivot point is `spawnpos`). + void softRespawn(Ogre::Vector3 spawnpos, Ogre::Quaternion spawnrot); //!< Use `MSG_SIM_MODIFY_ACTOR_REQUESTED` with type `SOFT_RESPAWN`; Resets the actor to given position as if spawned there (pivot point is `spawnpos`). void requestRotation(float rotation, Ogre::Vector3 center) { m_rotation_request += rotation; m_rotation_request_center = center; }; void requestAngleSnap(int division) { m_anglesnap_request = division; }; void requestTranslation(Ogre::Vector3 translation) { m_translation_request += translation; }; diff --git a/source/main/physics/SimData.h b/source/main/physics/SimData.h index ffc4a652a6..3e026c5412 100644 --- a/source/main/physics/SimData.h +++ b/source/main/physics/SimData.h @@ -873,7 +873,8 @@ struct ActorModifyRequest amr_saved_state; CacheEntryPtr amr_addonpart; //!< Primary method of specifying cache entry. std::string amr_addonpart_fname; //!< Fallback method in case CacheEntry doesn't exist anymore - that means mod was uninstalled in the meantime. Used by REMOVE_ADDONPART_AND_RELOAD. - Ogre::Vector3 amr_softrespawn_pos; //!< Position to use with `SOFT_RESPAWN`. + Ogre::Vector3 amr_softrespawn_position; //!< Position to use with `SOFT_RESPAWN`. + Ogre::Quaternion amr_softrespawn_rotation; //!< Rotation to use with `SOFT_RESPAWN`; use `TObjParser::CalcRotation()` to calculate quaternion from XYZ like in TOBJ file. }; enum class ActorLinkingRequestType diff --git a/source/main/terrain/TerrainEditor.cpp b/source/main/terrain/TerrainEditor.cpp index 6556831735..4b0e5a0101 100644 --- a/source/main/terrain/TerrainEditor.cpp +++ b/source/main/terrain/TerrainEditor.cpp @@ -378,6 +378,31 @@ Ogre::Vector3 const& TerrainEditorObject::getRotation() return rotation; } +void TerrainEditorObjectRefreshActorVisual(TerrainEditorObjectPtr obj) +{ + ROR_ASSERT(obj->actor_instance_id != ACTORINSTANCEID_INVALID); + const ActorPtr& actor = App::GetGameContext()->GetActorManager()->GetActorById(obj->actor_instance_id); + ROR_ASSERT(actor != ActorManager::ACTORPTR_NULL); + if (actor != ActorManager::ACTORPTR_NULL) + { + const bool rot_yxz = App::GetGameContext()->GetTerrain()->getObjectManager()->GetEditorObjectFlagRotYXZ(obj); + + ActorModifyRequest* req = new ActorModifyRequest(); + req->amr_type = ActorModifyRequest::Type::SOFT_RESPAWN; + req->amr_actor = actor->ar_instance_id; + req->amr_softrespawn_position = obj->position; + req->amr_softrespawn_rotation = TObjParser::CalcRotation(obj->rotation, rot_yxz); + App::GetGameContext()->PushMessage(Message(MSG_SIM_MODIFY_ACTOR_REQUESTED, (void*)req)); + req = nullptr; + + ActorModifyRequest* fxreq = new ActorModifyRequest(); + fxreq->amr_type = ActorModifyRequest::Type::REFRESH_VISUALS; + fxreq->amr_actor = actor->ar_instance_id; + App::GetGameContext()->PushMessage(Message(MSG_SIM_MODIFY_ACTOR_REQUESTED, (void*)fxreq)); + fxreq = nullptr; + } +} + void TerrainEditorObject::setPosition(Ogre::Vector3 const& pos) { position = pos; @@ -387,26 +412,7 @@ void TerrainEditorObject::setPosition(Ogre::Vector3 const& pos) } else if (special_object_type != TObjSpecialObject::NONE) { - ROR_ASSERT(actor_instance_id != ACTORINSTANCEID_INVALID); - const ActorPtr& actor = App::GetGameContext()->GetActorManager()->GetActorById(actor_instance_id); - ROR_ASSERT(actor != ActorManager::ACTORPTR_NULL); - if (actor != ActorManager::ACTORPTR_NULL) - { - actor->requestTranslation(pos - actor->getPosition()); - - ActorModifyRequest* req = new ActorModifyRequest(); - req->amr_type = ActorModifyRequest::Type::SOFT_RESPAWN; - req->amr_actor = actor->ar_instance_id; - req->amr_softrespawn_pos = pos; - App::GetGameContext()->PushMessage(Message(MSG_SIM_MODIFY_ACTOR_REQUESTED, (void*)req)); - req = nullptr; - - ActorModifyRequest* fxreq = new ActorModifyRequest(); - fxreq->amr_type = ActorModifyRequest::Type::REFRESH_VISUALS; - fxreq->amr_actor = actor->ar_instance_id; - App::GetGameContext()->PushMessage(Message(MSG_SIM_MODIFY_ACTOR_REQUESTED, (void*)fxreq)); - fxreq = nullptr; - } + TerrainEditorObjectRefreshActorVisual(this); } } @@ -420,21 +426,7 @@ void TerrainEditorObject::setRotation(Ogre::Vector3 const& rot) } else if (special_object_type != TObjSpecialObject::NONE) { - ROR_ASSERT(actor_instance_id != ACTORINSTANCEID_INVALID); - const ActorPtr& actor = App::GetGameContext()->GetActorManager()->GetActorById(actor_instance_id); - ROR_ASSERT(actor != ActorManager::ACTORPTR_NULL); - if (actor != ActorManager::ACTORPTR_NULL) - { - // TBD: only yaw can be requested at the moment. - // FIXME: the rot_xyz flag is currently ignored - Quaternion orientation = TObjParser::CalcRotation(rot, /*rot_xyz:*/false); - actor->requestRotation(orientation.getYaw(/*reprojectAxis:*/false).valueRadians() - actor->getRotation(), actor->getPosition()); - - ActorModifyRequest* req = new ActorModifyRequest(); - req->amr_type = ActorModifyRequest::Type::RESET_ON_SPOT; - req->amr_actor = actor->ar_instance_id; - App::GetGameContext()->PushMessage(Message(MSG_SIM_MODIFY_ACTOR_REQUESTED, (void*)req)); - } + TerrainEditorObjectRefreshActorVisual(this); } } diff --git a/source/main/terrain/TerrainObjectManager.cpp b/source/main/terrain/TerrainObjectManager.cpp index 3e58379ef9..fe2a717cb0 100644 --- a/source/main/terrain/TerrainObjectManager.cpp +++ b/source/main/terrain/TerrainObjectManager.cpp @@ -998,24 +998,29 @@ void TerrainObjectManager::LoadTelepoints() } } - -void TerrainObjectManager::SpawnSinglePredefinedActor(TerrainEditorObjectPtr const& object) +bool TerrainObjectManager::GetEditorObjectFlagRotYXZ(TerrainEditorObjectPtr const& object) { - // For terrain editor to work, all preloaded actors must be spawned. - // Most will spawn with terrain, however, some may be excluded for reasons. - // ----------------------------------------------------------------------- - // We need the 'rot_yxz' flag - look up the TOBJ document in cache - bool rot_yxz = false; if (object->tobj_cache_id == -1 || object->tobj_cache_id >= (int)m_tobj_cache.size()) { App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_TERRN, Console::CONSOLE_SYSTEM_WARNING, fmt::format("Assuming no 'rot_yxz' when spawning preselected actor '{}' - TOBJ document not found", object->getName())); + return false; } else { - rot_yxz = m_tobj_cache[object->tobj_cache_id]->rot_yxz; + return m_tobj_cache[object->tobj_cache_id]->rot_yxz; } +} + + +void TerrainObjectManager::SpawnSinglePredefinedActor(TerrainEditorObjectPtr const& object) +{ + // For terrain editor to work, all preloaded actors must be spawned. + // Most will spawn with terrain, however, some may be excluded for reasons. + // ----------------------------------------------------------------------- + + const bool rot_yxz = GetEditorObjectFlagRotYXZ(object); // Check if already spawned. if (object->actor_instance_id == ACTORINSTANCEID_INVALID) diff --git a/source/main/terrain/TerrainObjectManager.h b/source/main/terrain/TerrainObjectManager.h index b91b3477f8..e259e20b53 100644 --- a/source/main/terrain/TerrainObjectManager.h +++ b/source/main/terrain/TerrainObjectManager.h @@ -72,6 +72,7 @@ class TerrainObjectManager: public RefCountingObject void destroyObject(const Ogre::String& instancename); void LoadTelepoints(); void SpawnSinglePredefinedActor(TerrainEditorObjectPtr const& object); + bool GetEditorObjectFlagRotYXZ(TerrainEditorObjectPtr const& object); void LoadPredefinedActors(); bool HasPredefinedActors() { return m_has_predefined_actors; }; bool UpdateTerrainObjects(float dt);