From 740440ca7868d3d2e2a93fef1b92d7088f4c62f0 Mon Sep 17 00:00:00 2001 From: Vsevolod Kremianskii Date: Fri, 14 May 2021 23:31:05 +0700 Subject: [PATCH] Major refactoring of the scene management system --- CMakeLists.txt | 21 +- src/engine/game/actionexecutor.cpp | 1 - src/engine/game/camera/animatedcamera.cpp | 14 +- src/engine/game/camera/animatedcamera.h | 4 +- src/engine/game/camera/dialogcamera.cpp | 6 +- src/engine/game/camera/dialogcamera.h | 2 +- src/engine/game/camera/firstperson.cpp | 9 +- src/engine/game/camera/firstperson.h | 2 +- src/engine/game/camera/staticcamera.cpp | 11 +- src/engine/game/camera/staticcamera.h | 2 +- src/engine/game/camera/thirdperson.cpp | 7 +- src/engine/game/camera/thirdperson.h | 2 +- src/engine/game/combat/combat_projectiles.cpp | 31 +- src/engine/game/console.cpp | 6 +- src/engine/game/game.cpp | 2 +- src/engine/game/gui/chargen/chargen.cpp | 6 +- src/engine/game/gui/chargen/classselect.cpp | 6 +- .../game/gui/chargen/portraitselect.cpp | 12 +- src/engine/game/gui/conversation.h | 2 +- src/engine/game/gui/dialog.cpp | 20 +- src/engine/game/gui/ingame/character.cpp | 4 +- src/engine/game/gui/mainmenu.cpp | 5 +- src/engine/game/object/area.cpp | 41 +- src/engine/game/object/area_collision.cpp | 8 +- src/engine/game/object/creature.cpp | 68 +--- src/engine/game/object/creature.h | 6 +- .../game/object/creature_appearance.cpp | 56 +-- src/engine/game/object/door.cpp | 14 +- src/engine/game/object/placeable.cpp | 2 +- src/engine/game/object/spatial.cpp | 19 +- src/engine/game/object/spatial.h | 5 +- src/engine/game/objectselect.cpp | 2 +- .../lip/{lipanimation.cpp => animation.cpp} | 2 +- .../lip/{lipanimation.h => animation.h} | 0 src/engine/graphics/lip/lipreader.h | 2 +- src/engine/graphics/lip/lips.h | 2 +- src/engine/graphics/lip/lipwriter.h | 2 +- src/engine/graphics/model/animation.cpp | 13 +- src/engine/graphics/model/animation.h | 6 +- src/engine/graphics/model/mdlreader.cpp | 9 +- src/engine/graphics/model/model.cpp | 56 ++- src/engine/graphics/model/model.h | 7 +- src/engine/graphics/model/modelnode.h | 28 +- src/engine/graphics/pixelutil.cpp | 4 - src/engine/graphics/texture/texture.cpp | 7 - src/engine/graphics/types.h | 9 +- src/engine/gui/scenebuilder.cpp | 11 +- src/engine/scene/animation/channel.cpp | 238 ----------- src/engine/scene/animation/channel.h | 107 ----- .../scene/animation/scenenodeanimator.cpp | 328 --------------- .../scene/animation/scenenodeanimator.h | 107 ----- .../eventlistener.h => animeventlistener.h} | 0 .../properties.h => animproperties.h} | 0 src/engine/scene/node/cameranode.cpp | 30 +- src/engine/scene/node/cameranode.h | 16 +- .../{grasscluster.h => node/dummynode.h} | 11 +- src/engine/scene/node/emitternode.cpp | 94 +++-- src/engine/scene/node/emitternode.h | 57 ++- .../scene/node/emitternode_particle.cpp | 93 +++++ src/engine/scene/node/grassnode.cpp | 9 +- src/engine/scene/node/grassnode.h | 20 +- src/engine/scene/node/lightnode.cpp | 23 +- src/engine/scene/node/lightnode.h | 48 +-- src/engine/scene/node/meshnode.cpp | 155 ++++---- src/engine/scene/node/meshnode.h | 33 +- src/engine/scene/node/modelnode.cpp | 376 ++++++------------ src/engine/scene/node/modelnode.h | 147 ++++--- src/engine/scene/node/modelnode_animation.cpp | 336 ++++++++++++++++ .../modelnodescenenode.cpp} | 29 +- .../{particle.h => node/modelnodescenenode.h} | 38 +- src/engine/scene/node/scenenode.cpp | 69 ++-- src/engine/scene/node/scenenode.h | 82 ++-- src/engine/scene/particle.cpp | 110 ----- src/engine/scene/scenegraph.cpp | 62 ++- src/engine/scene/scenegraph.h | 20 +- src/engine/scene/types.h | 11 +- 76 files changed, 1314 insertions(+), 1889 deletions(-) rename src/engine/graphics/lip/{lipanimation.cpp => animation.cpp} (98%) rename src/engine/graphics/lip/{lipanimation.h => animation.h} (100%) delete mode 100644 src/engine/scene/animation/channel.cpp delete mode 100644 src/engine/scene/animation/channel.h delete mode 100644 src/engine/scene/animation/scenenodeanimator.cpp delete mode 100644 src/engine/scene/animation/scenenodeanimator.h rename src/engine/scene/{animation/eventlistener.h => animeventlistener.h} (100%) rename src/engine/scene/{animation/properties.h => animproperties.h} (100%) rename src/engine/scene/{grasscluster.h => node/dummynode.h} (75%) create mode 100644 src/engine/scene/node/emitternode_particle.cpp create mode 100644 src/engine/scene/node/modelnode_animation.cpp rename src/engine/scene/{animation/scenenodestate.h => node/modelnodescenenode.cpp} (66%) rename src/engine/scene/{particle.h => node/modelnodescenenode.h} (50%) delete mode 100644 src/engine/scene/particle.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d75d7b74..c6c5fd0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,7 +163,7 @@ set(GRAPHICS_HEADERS src/engine/graphics/font.h src/engine/graphics/fonts.h src/engine/graphics/framebuffer.h - src/engine/graphics/lip/lipanimation.h + src/engine/graphics/lip/animation.h src/engine/graphics/lip/lipreader.h src/engine/graphics/lip/lips.h src/engine/graphics/lip/lipwriter.h @@ -205,7 +205,7 @@ set(GRAPHICS_SOURCES src/engine/graphics/font.cpp src/engine/graphics/fonts.cpp src/engine/graphics/framebuffer.cpp - src/engine/graphics/lip/lipanimation.cpp + src/engine/graphics/lip/animation.cpp src/engine/graphics/lip/lipreader.cpp src/engine/graphics/lip/lips.cpp src/engine/graphics/lip/lipwriter.cpp @@ -289,36 +289,33 @@ set_target_properties(libvideo PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINAR ## libscene static library set(SCENE_HEADERS - src/engine/scene/animation/eventlistener.h - src/engine/scene/animation/channel.h - src/engine/scene/animation/properties.h - src/engine/scene/animation/scenenodeanimator.h - src/engine/scene/animation/scenenodestate.h - src/engine/scene/grasscluster.h + src/engine/scene/animeventlistener.h + src/engine/scene/animproperties.h src/engine/scene/node/cameranode.h + src/engine/scene/node/dummynode.h src/engine/scene/node/emitternode.h src/engine/scene/node/grassnode.h src/engine/scene/node/lightnode.h src/engine/scene/node/meshnode.h src/engine/scene/node/modelnode.h + src/engine/scene/node/modelnodescenenode.h src/engine/scene/node/scenenode.h - src/engine/scene/particle.h src/engine/scene/pipeline/control.h src/engine/scene/pipeline/world.h src/engine/scene/scenegraph.h src/engine/scene/types.h) set(SCENE_SOURCES - src/engine/scene/animation/channel.cpp - src/engine/scene/animation/scenenodeanimator.cpp src/engine/scene/node/cameranode.cpp src/engine/scene/node/emitternode.cpp + src/engine/scene/node/emitternode_particle.cpp src/engine/scene/node/grassnode.cpp src/engine/scene/node/lightnode.cpp src/engine/scene/node/meshnode.cpp src/engine/scene/node/modelnode.cpp + src/engine/scene/node/modelnode_animation.cpp + src/engine/scene/node/modelnodescenenode.cpp src/engine/scene/node/scenenode.cpp - src/engine/scene/particle.cpp src/engine/scene/pipeline/control.cpp src/engine/scene/pipeline/world.cpp src/engine/scene/scenegraph.cpp) diff --git a/src/engine/game/actionexecutor.cpp b/src/engine/game/actionexecutor.cpp index 4fc45e91..07fdbf2c 100644 --- a/src/engine/game/actionexecutor.cpp +++ b/src/engine/game/actionexecutor.cpp @@ -380,7 +380,6 @@ void ActionExecutor::executeJumpToLocation(const shared_ptr &actor, Loca void ActionExecutor::executePlayAnimation(const shared_ptr &actor, const shared_ptr &action, float dt) { AnimationProperties properties; - properties.flags = AnimationFlags::propagateHead; properties.speed = action->speed(); auto spatial = static_pointer_cast(actor); diff --git a/src/engine/game/camera/animatedcamera.cpp b/src/engine/game/camera/animatedcamera.cpp index febab82f..60eefd51 100644 --- a/src/engine/game/camera/animatedcamera.cpp +++ b/src/engine/game/camera/animatedcamera.cpp @@ -21,8 +21,10 @@ #include +#include "../../graphics/types.h" #include "../../graphics/model/models.h" #include "../../resource/resources.h" +#include "../../scene/node/cameranode.h" using namespace std; @@ -33,14 +35,14 @@ namespace reone { namespace game { -AnimatedCamera::AnimatedCamera(SceneGraph *sceneGraph, float aspect) : _sceneGraph(sceneGraph), _aspect(aspect) { +AnimatedCamera::AnimatedCamera(float aspect, SceneGraph *sceneGraph) : _sceneGraph(sceneGraph), _aspect(aspect) { _sceneGraph = sceneGraph; - _sceneNode = make_shared(_sceneGraph, glm::mat4(1.0f), _aspect, _zNear, _zFar); + _sceneNode = make_shared("", glm::mat4(1.0f), _sceneGraph); updateProjection(); } void AnimatedCamera::updateProjection() { - glm::mat4 projection(glm::perspective(glm::radians(_fovy), _aspect, _zNear, _zFar)); + glm::mat4 projection(glm::perspective(glm::radians(_fovy), _aspect, kDefaultClipPlaneNear, kDefaultClipPlaneFar)); _sceneNode->setProjection(projection); } @@ -64,12 +66,12 @@ static const string &getAnimationName(int animNumber) { void AnimatedCamera::playAnimation(int animNumber) { if (_model) { - _model->animator().playAnimation(getAnimationName(animNumber)); + _model->playAnimation(getAnimationName(animNumber)); } } bool AnimatedCamera::isAnimationFinished() const { - return _model ? _model->animator().isAnimationFinished() : false; + return _model ? _model->isAnimationFinished() : false; } void AnimatedCamera::setModel(const shared_ptr &model) { @@ -77,7 +79,7 @@ void AnimatedCamera::setModel(const shared_ptr &model) { (!_model && !model)) return; if (model) { - _model = make_unique(ModelUsage::Other, model, _sceneGraph); + _model = make_unique(model, ModelUsage::Camera, _sceneGraph); _model->attach("camerahook", _sceneNode); } else { _model.reset(); diff --git a/src/engine/game/camera/animatedcamera.h b/src/engine/game/camera/animatedcamera.h index bd7b461a..8fa672d1 100644 --- a/src/engine/game/camera/animatedcamera.h +++ b/src/engine/game/camera/animatedcamera.h @@ -34,7 +34,7 @@ const float kDefaultAnimCamFOV = 55.0f; class AnimatedCamera : public Camera { public: - AnimatedCamera(scene::SceneGraph *sceneGraph, float aspect); + AnimatedCamera(float aspect, scene::SceneGraph *sceneGraph); void update(float dt) override; @@ -50,8 +50,6 @@ private: float _aspect { 1.0f }; std::unique_ptr _model; float _fovy { kDefaultAnimCamFOV }; - float _zNear { 0.1f }; - float _zFar { 10000.0f }; void updateProjection(); }; diff --git a/src/engine/game/camera/dialogcamera.cpp b/src/engine/game/camera/dialogcamera.cpp index 596ea88b..f829f97a 100644 --- a/src/engine/game/camera/dialogcamera.cpp +++ b/src/engine/game/camera/dialogcamera.cpp @@ -28,9 +28,9 @@ namespace reone { namespace game { -DialogCamera::DialogCamera(SceneGraph *sceneGraph, const CameraStyle &style, float aspect, float zNear, float zFar) { - glm::mat4 projection(glm::perspective(glm::radians(style.viewAngle), aspect, zNear, zFar)); - _sceneNode = make_shared(sceneGraph, projection, aspect, zNear, zFar); +DialogCamera::DialogCamera(float aspect, const CameraStyle &style, SceneGraph *sceneGraph) { + glm::mat4 projection(glm::perspective(glm::radians(style.viewAngle), aspect, kDefaultClipPlaneNear, kDefaultClipPlaneFar)); + _sceneNode = make_shared("", move(projection), sceneGraph); } void DialogCamera::setSpeakerPosition(const glm::vec3 &position) { diff --git a/src/engine/game/camera/dialogcamera.h b/src/engine/game/camera/dialogcamera.h index fd7891e3..5ac0117e 100644 --- a/src/engine/game/camera/dialogcamera.h +++ b/src/engine/game/camera/dialogcamera.h @@ -40,7 +40,7 @@ public: ListenerFar }; - DialogCamera(scene::SceneGraph *sceneGraph, const CameraStyle &style, float aspect, float zNear = 0.1f, float zFar = 10000.0f); + DialogCamera(float aspect, const CameraStyle &style, scene::SceneGraph *sceneGraph); void setSpeakerPosition(const glm::vec3 &position); void setListenerPosition(const glm::vec3 &position); diff --git a/src/engine/game/camera/firstperson.cpp b/src/engine/game/camera/firstperson.cpp index 99e5c4a8..b92b6c52 100644 --- a/src/engine/game/camera/firstperson.cpp +++ b/src/engine/game/camera/firstperson.cpp @@ -19,8 +19,11 @@ #include "glm/ext.hpp" +#include "../../graphics/types.h" + using namespace std; +using namespace reone::graphics; using namespace reone::scene; namespace reone { @@ -30,9 +33,9 @@ namespace game { static constexpr float kMovementSpeed = 4.0f; static constexpr float kMouseMultiplier = glm::pi() / 4000.0f; -FirstPersonCamera::FirstPersonCamera(SceneGraph *sceneGraph, float aspect, float fovy, float zNear, float zFar) { - glm::mat4 projection(glm::perspective(fovy, aspect, zNear, zFar)); - _sceneNode = make_unique(sceneGraph, projection, aspect, zNear, zFar); +FirstPersonCamera::FirstPersonCamera(float aspect, float fovy, SceneGraph *sceneGraph) { + glm::mat4 projection(glm::perspective(fovy, aspect, kDefaultClipPlaneNear, kDefaultClipPlaneFar)); + _sceneNode = make_unique("", move(projection), sceneGraph); } bool FirstPersonCamera::handle(const SDL_Event &event) { diff --git a/src/engine/game/camera/firstperson.h b/src/engine/game/camera/firstperson.h index 57b83b8e..ee427550 100644 --- a/src/engine/game/camera/firstperson.h +++ b/src/engine/game/camera/firstperson.h @@ -25,7 +25,7 @@ namespace game { class FirstPersonCamera : public Camera { public: - FirstPersonCamera(scene::SceneGraph *sceneGraph, float aspect, float fovy, float zNear = 0.1f, float zFar = 10000.0f); + FirstPersonCamera(float aspect, float fovy, scene::SceneGraph *sceneGraph); bool handle(const SDL_Event &event) override; void update(float dt) override; diff --git a/src/engine/game/camera/staticcamera.cpp b/src/engine/game/camera/staticcamera.cpp index a5203f00..b3372ac7 100644 --- a/src/engine/game/camera/staticcamera.cpp +++ b/src/engine/game/camera/staticcamera.cpp @@ -23,22 +23,19 @@ using namespace std; +using namespace reone::graphics; using namespace reone::scene; namespace reone { namespace game { -static constexpr float kNearPlane = 0.1f; -static constexpr float kFarPlane = 10000.0f; - -StaticCamera::StaticCamera(SceneGraph *sceneGraph, float aspect) : _aspect(aspect) { - _sceneNode = make_unique(sceneGraph, glm::mat4(1.0f), aspect, kNearPlane, kFarPlane); +StaticCamera::StaticCamera(float aspect, SceneGraph *sceneGraph) : _aspect(aspect) { + _sceneNode = make_unique("", glm::mat4(1.0f), sceneGraph); } void StaticCamera::setObject(const PlaceableCamera &object) { - glm::mat4 projection(glm::perspective(glm::radians(object.fieldOfView()), _aspect, kNearPlane, kFarPlane)); - + glm::mat4 projection(glm::perspective(glm::radians(object.fieldOfView()), _aspect, kDefaultClipPlaneNear, kDefaultClipPlaneFar)); _sceneNode->setLocalTransform(object.transform()); _sceneNode->setProjection(projection); } diff --git a/src/engine/game/camera/staticcamera.h b/src/engine/game/camera/staticcamera.h index bcd4ff45..c3fd668d 100644 --- a/src/engine/game/camera/staticcamera.h +++ b/src/engine/game/camera/staticcamera.h @@ -27,7 +27,7 @@ class PlaceableCamera; class StaticCamera : public Camera { public: - StaticCamera(scene::SceneGraph *sceneGraph, float aspect); + StaticCamera(float aspect, scene::SceneGraph *sceneGraph); void setObject(const PlaceableCamera &object); diff --git a/src/engine/game/camera/thirdperson.cpp b/src/engine/game/camera/thirdperson.cpp index d6b923a8..433f4d3a 100644 --- a/src/engine/game/camera/thirdperson.cpp +++ b/src/engine/game/camera/thirdperson.cpp @@ -23,6 +23,7 @@ using namespace std; +using namespace reone::graphics; using namespace reone::scene; namespace reone { @@ -34,9 +35,9 @@ static constexpr float kMaxRotationSpeed = 2.5f; static constexpr float kRotationAcceleration = 1.0f; static constexpr float kMouseRotationSpeed = 0.001f; -ThirdPersonCamera::ThirdPersonCamera(Game *game, SceneGraph *sceneGraph, float aspect, const CameraStyle &style, float zNear, float zFar) : _game(game) { - glm::mat4 projection(glm::perspective(glm::radians(style.viewAngle), aspect, zNear, zFar)); - _sceneNode = make_unique(sceneGraph, projection, aspect, zNear, zFar); +ThirdPersonCamera::ThirdPersonCamera(float aspect, const CameraStyle &style, Game *game, SceneGraph *sceneGraph) : _game(game) { + glm::mat4 projection(glm::perspective(glm::radians(style.viewAngle), aspect, kDefaultClipPlaneNear, kDefaultClipPlaneFar)); + _sceneNode = make_unique("", move(projection), sceneGraph); _style = style; } diff --git a/src/engine/game/camera/thirdperson.h b/src/engine/game/camera/thirdperson.h index 71fbcced..7cc17fa8 100644 --- a/src/engine/game/camera/thirdperson.h +++ b/src/engine/game/camera/thirdperson.h @@ -32,7 +32,7 @@ class Game; class ThirdPersonCamera : public Camera { public: - ThirdPersonCamera(Game *game, scene::SceneGraph *sceneGraph, float aspect, const CameraStyle &style, float zNear = 0.1f, float zFar = 10000.0f); + ThirdPersonCamera(float aspect, const CameraStyle &style, Game *game, scene::SceneGraph *sceneGraph); bool handle(const SDL_Event &event) override; void update(float dt) override; diff --git a/src/engine/game/combat/combat_projectiles.cpp b/src/engine/game/combat/combat_projectiles.cpp index 2ce0ff14..812ff8d4 100644 --- a/src/engine/game/combat/combat_projectiles.cpp +++ b/src/engine/game/combat/combat_projectiles.cpp @@ -22,6 +22,7 @@ #include "combat.h" #include "glm/gtx/euler_angles.hpp" +#include "glm/gtx/transform.hpp" #include "../game.h" @@ -38,38 +39,42 @@ static constexpr char kModelEventDetonate[] = "detonate"; static constexpr float kProjectileSpeed = 16.0f; void Combat::fireProjectile(const shared_ptr &attacker, const shared_ptr &target, Round &round) { + auto attackerModel = static_pointer_cast(attacker->sceneNode()); + auto targetModel = static_pointer_cast(target->sceneNode()); + if (!attackerModel || !targetModel) return; + shared_ptr weapon(attacker->getEquippedItem(InventorySlot::rightWeapon)); if (!weapon) return; shared_ptr ammunitionType(weapon->ammunitionType()); if (!ammunitionType) return; - shared_ptr weaponModel(attacker->getModelSceneNode()->getAttachedModel("rhand")); + auto weaponModel = static_pointer_cast(attackerModel->getAttachment("rhand")); if (!weaponModel) return; // Determine projectile position - glm::vec3 projectilePos, bulletHookPos; - if (weaponModel->getNodeAbsolutePosition("bullethook", bulletHookPos)) { - projectilePos = weaponModel->absoluteTransform() * glm::vec4(bulletHookPos, 1.0f); + glm::vec3 projectilePos; + shared_ptr bulletHook(weaponModel->model()->getNodeByName("bullethook")); + if (bulletHook) { + projectilePos = weaponModel->absoluteTransform() * glm::vec4(bulletHook->restPosition(), 1.0f); } else { - projectilePos = weaponModel->absoluteTransform()[3]; + projectilePos = weaponModel->getOrigin(); } // Determine projectile direction - shared_ptr targetModel(target->getModelSceneNode()); - glm::vec3 projectileTarget, impactPos; - if (targetModel->getNodeAbsolutePosition("impact", impactPos)) { - projectileTarget = targetModel->absoluteTransform() * glm::vec4(impactPos, 1.0f); + glm::vec3 projectileTarget; + shared_ptr impact(targetModel->model()->getNodeByName("impact")); + if (impact) { + projectileTarget = targetModel->absoluteTransform() * glm::vec4(impact->restPosition(), 1.0f); } else { - projectileTarget = targetModel->absoluteTransform()[3]; + projectileTarget = targetModel->getOrigin(); } round.projectileDir = glm::normalize(projectileTarget - projectilePos); // Create and add a projectile to the scene graph - round.projectile = make_shared(ModelUsage::Projectile, ammunitionType->model, &_game->sceneGraph()); + round.projectile = make_shared(ammunitionType->model, ModelUsage::Projectile, &_game->sceneGraph()); round.projectile->signalEvent(kModelEventDetonate); - round.projectile->setPosition(projectilePos); - round.projectile->setProjectileSpeed(kProjectileSpeed); + round.projectile->setLocalTransform(glm::translate(projectilePos)); _game->sceneGraph().addRoot(round.projectile); // Play shot sound, if any diff --git a/src/engine/game/console.cpp b/src/engine/game/console.cpp index 0746c8d5..4ca775ed 100644 --- a/src/engine/game/console.cpp +++ b/src/engine/game/console.cpp @@ -122,7 +122,8 @@ void Console::cmdListAnim(vector tokens) { substr = tokens[1]; } - vector anims(object->getModelSceneNode()->model()->getAnimationNames()); + auto model = static_pointer_cast(object->sceneNode()); + vector anims(model->model()->getAnimationNames()); sort(anims.begin(), anims.end()); for (auto &anim : anims) { @@ -146,7 +147,8 @@ void Console::cmdPlayAnim(vector tokens) { return; } } - object->getModelSceneNode()->animator().playAnimation(tokens[1], AnimationProperties::fromFlags(AnimationFlags::loop)); + auto model = static_pointer_cast(object->sceneNode()); + model->playAnimation(tokens[1], AnimationProperties::fromFlags(AnimationFlags::loop)); } void Console::cmdKill(vector tokens) { diff --git a/src/engine/game/game.cpp b/src/engine/game/game.cpp index b27c2f02..2b231722 100644 --- a/src/engine/game/game.cpp +++ b/src/engine/game/game.cpp @@ -729,7 +729,7 @@ void Game::updateSceneGraph(float dt) { shared_ptr lightingRefNode; shared_ptr partyLeader(_party.getLeader()); if (partyLeader && _cameraType == CameraType::ThirdPerson) { - lightingRefNode = partyLeader->getModelSceneNode(); + lightingRefNode = partyLeader->sceneNode(); } else { lightingRefNode = camera->sceneNode(); } diff --git a/src/engine/game/gui/chargen/chargen.cpp b/src/engine/game/gui/chargen/chargen.cpp index 2bacb176..3eb154a9 100644 --- a/src/engine/game/gui/chargen/chargen.cpp +++ b/src/engine/game/gui/chargen/chargen.cpp @@ -377,11 +377,11 @@ shared_ptr CharacterGeneration::getCharacterModel(SceneGraph &sc creature->setAppearance(_character.appearance); creature->equip("g_a_clothes01"); creature->loadAppearance(); - creature->getModelSceneNode()->setCullable(false); + creature->sceneNode()->setCullable(false); creature->updateModelAnimation(); - auto model = make_shared(ModelUsage::GUI, Models::instance().get("cgbody_light"), &sceneGraph); - model->attach("cgbody_light", creature->getModelSceneNode()); + auto model = make_shared(Models::instance().get("cgbody_light"), ModelUsage::GUI, &sceneGraph); + model->attach("cgbody_light", creature->sceneNode()); return move(model); } diff --git a/src/engine/game/gui/chargen/classselect.cpp b/src/engine/game/gui/chargen/classselect.cpp index 10bbb374..85af6fd1 100644 --- a/src/engine/game/gui/chargen/classselect.cpp +++ b/src/engine/game/gui/chargen/classselect.cpp @@ -148,11 +148,11 @@ shared_ptr ClassSelection::getCharacterModel(int appearance, Sce character->setAppearance(appearance); character->equip("g_a_clothes01"); character->loadAppearance(); - character->getModelSceneNode()->setCullable(false); + character->sceneNode()->setCullable(false); character->updateModelAnimation(); - auto model = make_shared(ModelUsage::GUI, Models::instance().get("cgbody_light"), &sceneGraph); - model->attach("cgbody_light", character->getModelSceneNode()); + auto model = make_shared(Models::instance().get("cgbody_light"), ModelUsage::GUI, &sceneGraph); + model->attach("cgbody_light", character->sceneNode()); return move(model); } diff --git a/src/engine/game/gui/chargen/portraitselect.cpp b/src/engine/game/gui/chargen/portraitselect.cpp index 5e0a20f3..6c75b6af 100644 --- a/src/engine/game/gui/chargen/portraitselect.cpp +++ b/src/engine/game/gui/chargen/portraitselect.cpp @@ -106,17 +106,17 @@ shared_ptr PortraitSelection::getCharacterModel(SceneGraph &scen creature->setAppearance(getAppearanceFromCurrentPortrait()); creature->equip("g_a_clothes01"); creature->loadAppearance(); - creature->getModelSceneNode()->setCullable(false); + creature->sceneNode()->setCullable(false); creature->updateModelAnimation(); // Attach creature model to the root scene node - shared_ptr creatureModel(creature->getModelSceneNode()); - glm::vec3 headPosition; - if (creatureModel->getNodeAbsolutePosition("camerahook", headPosition)) { - creature->setPosition(glm::vec3(0.0f, 0.0f, -headPosition.z)); + auto creatureModel = static_pointer_cast(creature->sceneNode()); + shared_ptr cameraHook(creatureModel->model()->getNodeByName("camerahook")); + if (cameraHook) { + creature->setPosition(glm::vec3(0.0f, 0.0f, -cameraHook->restPosition().z)); } - auto model = make_shared(ModelUsage::GUI, Models::instance().get("cghead_light"), &sceneGraph); + auto model = make_shared(Models::instance().get("cghead_light"), ModelUsage::GUI, &sceneGraph); model->attach("cghead_light", creatureModel); diff --git a/src/engine/game/gui/conversation.h b/src/engine/game/gui/conversation.h index 572f3820..ed84a269 100644 --- a/src/engine/game/gui/conversation.h +++ b/src/engine/game/gui/conversation.h @@ -19,7 +19,7 @@ #include "../../audio/soundhandle.h" #include "../../common/timer.h" -#include "../../graphics/lip/lipanimation.h" +#include "../../graphics/lip/animation.h" #include "../../graphics/model/model.h" #include "../dialog.h" diff --git a/src/engine/game/gui/dialog.cpp b/src/engine/game/gui/dialog.cpp index 9fb1343c..24905c80 100644 --- a/src/engine/game/gui/dialog.cpp +++ b/src/engine/game/gui/dialog.cpp @@ -250,16 +250,15 @@ void DialogGUI::updateCamera() { } glm::vec3 DialogGUI::getTalkPosition(const SpatialObject &object) const { - shared_ptr model(object.getModelSceneNode()); - if (model) { - glm::vec3 hookPosition(0.0f); - if (model->getNodeAbsolutePosition("talkdummy", hookPosition)) { - return object.position() + hookPosition; - } - return model->getWorldCenterAABB(); - } + auto model = static_pointer_cast(object.sceneNode()); + if (!model) return object.position(); - return object.position(); + + shared_ptr talkDummy(model->model()->getNodeByName("talkdummy")); + + return talkDummy ? + (object.transform() * talkDummy->absoluteTransform())[3] : + model->getWorldCenterOfAABB(); } DialogCamera::Variant DialogGUI::getRandomCameraVariant() const { @@ -287,7 +286,6 @@ void DialogGUI::updateParticipantAnimations() { shared_ptr animation(participant.model->getAnimation(animName)); if (animation) { AnimationProperties properties; - properties.flags = AnimationFlags::propagateHead; properties.scale = 1.0f; participant.creature->playAnimation(animation, move(properties)); } @@ -304,7 +302,7 @@ void DialogGUI::updateParticipantAnimations() { } AnimationType animType = getStuntAnimationType(anim.animation); if (animType != AnimationType::Invalid) { - participant->playAnimation(animType, AnimationProperties::fromFlags(AnimationFlags::propagateHead)); + participant->playAnimation(animType); } } } diff --git a/src/engine/game/gui/ingame/character.cpp b/src/engine/game/gui/ingame/character.cpp index 95f84b8d..3919c3e1 100644 --- a/src/engine/game/gui/ingame/character.cpp +++ b/src/engine/game/gui/ingame/character.cpp @@ -180,8 +180,8 @@ shared_ptr CharacterMenu::getSceneModel(SceneGraph &sceneGraph) character->loadAppearance(); character->updateModelAnimation(); - auto sceneModel = make_shared(ModelUsage::GUI, Models::instance().get("charmain_light"), &sceneGraph); - sceneModel->attach("charmain_light", character->getModelSceneNode()); + auto sceneModel = make_shared(Models::instance().get("charmain_light"), ModelUsage::GUI, &sceneGraph); + sceneModel->attach("charmain_light", character->sceneNode()); return move(sceneModel); } diff --git a/src/engine/game/gui/mainmenu.cpp b/src/engine/game/gui/mainmenu.cpp index 67499f38..e5ebdf03 100644 --- a/src/engine/game/gui/mainmenu.cpp +++ b/src/engine/game/gui/mainmenu.cpp @@ -127,9 +127,8 @@ void MainMenu::setup3DView() { } shared_ptr MainMenu::getKotorModel(SceneGraph &sceneGraph) { - auto model = make_shared(ModelUsage::GUI, Models::instance().get("mainmenu"), &sceneGraph); - model->animator().playAnimation("default", AnimationProperties::fromFlags(AnimationFlags::loop)); - + auto model = make_shared(Models::instance().get("mainmenu"), ModelUsage::GUI, &sceneGraph); + model->playAnimation("default", AnimationProperties::fromFlags(AnimationFlags::loop)); return move(model); } diff --git a/src/engine/game/object/area.cpp b/src/engine/game/object/area.cpp index cedb1948..71354797 100644 --- a/src/engine/game/object/area.cpp +++ b/src/engine/game/object/area.cpp @@ -117,17 +117,17 @@ void Area::loadLYT() { if (!model) continue; glm::vec3 position(lytRoom.position.x, lytRoom.position.y, lytRoom.position.z); - shared_ptr walkmesh(Walkmeshes::instance().get(lytRoom.name, ResourceType::Wok)); - auto sceneNode = make_shared(ModelUsage::Room, model, &_game->sceneGraph()); - sceneNode->setWalkmesh(walkmesh); + auto sceneNode = make_shared(model, ModelUsage::Room, &_game->sceneGraph()); sceneNode->setLocalTransform(glm::translate(glm::mat4(1.0f), position)); for (auto &anim : model->getAnimationNames()) { if (boost::starts_with(anim, "animloop")) { - sceneNode->animator().playAnimation(anim, AnimationProperties::fromFlags(AnimationFlags::loopOverlay)); + sceneNode->playAnimation(anim, AnimationProperties::fromFlags(AnimationFlags::loopOverlay)); } } + shared_ptr walkmesh(Walkmeshes::instance().get(lytRoom.name, ResourceType::Wok)); + auto room = make_unique(lytRoom.name, position, sceneNode, walkmesh); _rooms.insert(make_pair(room->name(), move(room))); } @@ -181,20 +181,20 @@ void Area::initCameras(const glm::vec3 &entryPosition, float entryFacing) { SceneGraph *sceneGraph = &_game->sceneGraph(); - _firstPersonCamera = make_unique(sceneGraph, _cameraAspect, glm::radians(kDefaultFieldOfView)); + _firstPersonCamera = make_unique(_cameraAspect, glm::radians(kDefaultFieldOfView), sceneGraph); _firstPersonCamera->setPosition(position); _firstPersonCamera->setFacing(entryFacing); - _thirdPersonCamera = make_unique(_game, sceneGraph, _cameraAspect, _camStyleDefault); + _thirdPersonCamera = make_unique(_cameraAspect, _camStyleDefault, _game, sceneGraph); _thirdPersonCamera->setFindObstacle(bind(&Area::getCameraObstacle, this, _1, _2, _3)); _thirdPersonCamera->setTargetPosition(position); _thirdPersonCamera->setFacing(entryFacing); - _dialogCamera = make_unique(sceneGraph, _camStyleDefault, _cameraAspect); + _dialogCamera = make_unique(_cameraAspect, _camStyleDefault, sceneGraph); _dialogCamera->setFindObstacle(bind(&Area::getCameraObstacle, this, _1, _2, _3)); - _animatedCamera = make_unique(sceneGraph, _cameraAspect); - _staticCamera = make_unique(sceneGraph, _cameraAspect); + _animatedCamera = make_unique(_cameraAspect, sceneGraph); + _staticCamera = make_unique(_cameraAspect, sceneGraph); } void Area::add(const shared_ptr &object) { @@ -232,7 +232,7 @@ void Area::doDestroyObject(uint32_t objectId) { } } { - shared_ptr sceneNode(object->getModelSceneNode()); + auto sceneNode = object->sceneNode(); if (sceneNode) { _game->sceneGraph().removeRoot(sceneNode); } @@ -358,10 +358,12 @@ bool Area::handleKeyDown(const SDL_KeyboardEvent &event) { } void Area::printDebugInfo(const SpatialObject &object) { + auto model = static_pointer_cast(object.sceneNode()); + ostringstream ss; ss << boost::format("tag='%s'") % object.tag(); ss << boost::format(",pos=[%0.2f,%0.2f,%0.2f]") % object.position().x % object.position().y % object.position().z; - ss << boost::format(",model='%s'") % object.getModelSceneNode()->getName(); + ss << boost::format(",model='%s'") % model->model()->name(); debug("Selected object: " + ss.str()); } @@ -528,7 +530,7 @@ void Area::fill(SceneGraph &sceneGraph) { shared_ptr aabbNode(sceneNode->model()->getAABBNode()); if (aabbNode && _grass.texture) { glm::mat4 aabbTransform(glm::translate(aabbNode->absoluteTransform(), room.second->position())); - auto grass = make_shared(&sceneGraph, glm::vec2(_grass.quadSize), _grass.texture, aabbNode->mesh()->lightmap); + auto grass = make_shared(room.first, glm::vec2(_grass.quadSize), _grass.texture, aabbNode->mesh()->lightmap, &sceneGraph); for (auto &material : Surfaces::instance().getGrassSurfaceIndices()) { for (auto &face : aabbNode->getFacesByMaterial(material)) { vector vertices(aabbNode->mesh()->mesh->getTriangleCoords(face)); @@ -537,7 +539,7 @@ void Area::fill(SceneGraph &sceneGraph) { glm::vec3 baryPosition(getRandomBarycentric()); glm::vec3 position(aabbTransform * glm::vec4(barycentricToCartesian(vertices[0], vertices[1], vertices[2], baryPosition), 1.0f)); glm::vec2 lightmapUV(aabbNode->mesh()->mesh->getTriangleTexCoords2(face, baryPosition)); - GrassCluster cluster; + GrassSceneNode::Cluster cluster; cluster.position = move(position); cluster.variant = getRandomGrassVariant(); cluster.lightmapUV = move(lightmapUV); @@ -644,13 +646,14 @@ void Area::update3rdPersonCameraTarget() { shared_ptr partyLeader(_game->party().getLeader()); if (!partyLeader) return; - glm::vec3 position; + glm::vec3 position(partyLeader->position()); - if (partyLeader->getModelSceneNode()->getNodeAbsolutePosition("camerahook", position)) { - position += partyLeader->position(); - } else { - position = partyLeader->position(); + auto model = static_pointer_cast(partyLeader->sceneNode()); + shared_ptr cameraHook(model->model()->getNodeByName("camerahook")); + if (cameraHook) { + position += cameraHook->restPosition(); } + _thirdPersonCamera->setTargetPosition(position); } @@ -832,7 +835,7 @@ shared_ptr Area::createObject(ObjectType type, const string &blueprintRe auto spatial = dynamic_pointer_cast(object); if (spatial) { add(spatial); - auto model = spatial->getModelSceneNode(); + auto model = spatial->sceneNode(); if (model) { _game->sceneGraph().addRoot(model); } diff --git a/src/engine/game/object/area_collision.cpp b/src/engine/game/object/area_collision.cpp index b9854159..80829e17 100644 --- a/src/engine/game/object/area_collision.cpp +++ b/src/engine/game/object/area_collision.cpp @@ -39,7 +39,7 @@ bool Area::testElevationAt(const glm::vec2 &point, float &z, int &material, Room // Test non-walkable faces of object walkmeshes for (auto &o : _objects) { - shared_ptr model(o->getModelSceneNode()); + auto model = static_pointer_cast(o->sceneNode()); shared_ptr walkmesh(o->getWalkmesh()); if (!model || !walkmesh) continue; @@ -94,7 +94,7 @@ shared_ptr Area::getObjectAt(int x, int y) const { // Skip non-selectable objects and party leader if (!o->isSelectable() || o == partyLeader) continue; - shared_ptr model(o->getModelSceneNode()); + auto model = static_pointer_cast(o->sceneNode()); if (!model) continue; // Distance to object must not exceed maximum collision distance @@ -124,7 +124,7 @@ bool Area::getCameraObstacle(const glm::vec3 &start, const glm::vec3 &end, glm:: for (auto &o : _objects) { if (o->type() != ObjectType::Door) continue; - shared_ptr model(o->getModelSceneNode()); + auto model = static_pointer_cast(o->sceneNode()); if (!model) continue; // Distance to object must not exceed maximum collision distance @@ -202,7 +202,7 @@ bool Area::isInLineOfSight(const Creature &subject, const SpatialObject &target) for (auto &o : _objects) { if (o->type() != ObjectType::Door) continue; - shared_ptr model(o->getModelSceneNode()); + auto model = static_pointer_cast(o->sceneNode()); if (!model) continue; // Distance to object must not exceed maximum collision distance diff --git a/src/engine/game/object/creature.cpp b/src/engine/game/object/creature.cpp index 12eb24da..7bd5c008 100644 --- a/src/engine/game/object/creature.cpp +++ b/src/engine/game/object/creature.cpp @@ -56,7 +56,6 @@ namespace game { static constexpr int kStrRefRemains = 38151; -static string g_headHookNode("headhook"); static string g_talkDummyNode("talkdummy"); Creature::Creature( @@ -117,7 +116,6 @@ void Creature::updateModel() { if (model) { model->setCullable(true); model->setDrawDistance(32.0f); - _headModel = model->getAttachedModel(g_headHookNode); if (!_stunt) { model->setLocalTransform(_transform); } @@ -158,11 +156,11 @@ void Creature::update(float dt) { } void Creature::updateModelAnimation() { - shared_ptr model(getModelSceneNode()); + auto model = static_pointer_cast(_sceneNode); if (!model) return; if (_animFireForget) { - if (!model->animator().isAnimationFinished()) return; + if (!model->isAnimationFinished()) return; _animFireForget = false; _animDirty = true; @@ -197,20 +195,10 @@ void Creature::updateModelAnimation() { } if (talkAnim) { - int addFlags = _lipAnimation ? AnimationFlags::syncLipAnim : 0; - if (_headModel) { - model->animator().playAnimation(anim, AnimationProperties::fromFlags(AnimationFlags::loop)); - _headModel->animator().playAnimation(anim, AnimationProperties::fromFlags(AnimationFlags::loop)); - _headModel->animator().playAnimation(talkAnim, AnimationProperties::fromFlags(AnimationFlags::loopOverlay | addFlags), _lipAnimation); - } else { - model->animator().playAnimation(anim, AnimationProperties::fromFlags(AnimationFlags::loop)); - model->animator().playAnimation(talkAnim, AnimationProperties::fromFlags(AnimationFlags::loopOverlay | addFlags), _lipAnimation); - } + model->playAnimation(anim, nullptr, AnimationProperties::fromFlags(AnimationFlags::loopOverlay | AnimationFlags::propagate)); + model->playAnimation(talkAnim, _lipAnimation, AnimationProperties::fromFlags(AnimationFlags::loopOverlay | AnimationFlags::propagate)); } else { - model->animator().playAnimation(anim, AnimationProperties::fromFlags(AnimationFlags::loopBlend)); - if (_headModel) { - _headModel->animator().playAnimation(anim, AnimationProperties::fromFlags(AnimationFlags::loopBlend)); - } + model->playAnimation(anim, nullptr, AnimationProperties::fromFlags(AnimationFlags::loopBlend | AnimationFlags::propagate)); } _animDirty = false; @@ -255,20 +243,12 @@ void Creature::playAnimation(const string &name, AnimationProperties properties, bool fireForget = !(properties.flags & AnimationFlags::loop); doPlayAnimation(fireForget, [&]() { - shared_ptr model(getModelSceneNode()); + auto model = static_pointer_cast(_sceneNode); if (!model) return; _animAction = actionToComplete; - // Extract "propagate to head model" flag - bool propagateHead = properties.flags & AnimationFlags::propagateHead; - properties.flags &= ~AnimationFlags::propagateHead; - - model->animator().playAnimation(name, properties); - - if (propagateHead && _headModel) { - _headModel->animator().playAnimation(name, move(properties)); - } + model->playAnimation(name, properties); }); } @@ -286,18 +266,10 @@ void Creature::playAnimation(const shared_ptr &anim, AnimationPropert bool fireForget = !(properties.flags & AnimationFlags::loop); doPlayAnimation(fireForget, [&]() { - shared_ptr model(getModelSceneNode()); + auto model = static_pointer_cast(_sceneNode); if (!model) return; - // Extract propagate to head model flag - bool propagateHead = properties.flags & AnimationFlags::propagateHead; - properties.flags &= ~AnimationFlags::propagateHead; - - model->animator().playAnimation(anim, properties); - - if (propagateHead && _headModel) { - _headModel->animator().playAnimation(anim, move(properties)); - } + model->playAnimation(anim, nullptr, properties); }); } @@ -332,13 +304,12 @@ bool Creature::equip(int slot, const shared_ptr &item) { if (_sceneNode) { updateModel(); - shared_ptr model(getModelSceneNode()); + auto model = static_pointer_cast(_sceneNode); if (model) { if (slot == InventorySlot::rightWeapon) { - shared_ptr weapon(model->getAttachedModel("rhand")); + auto weapon = static_pointer_cast(model->getAttachment("rhand")); if (weapon && weapon->model()->classification() == Model::Classification::Lightsaber) { - weapon->animator().setDefaultAnimation("powered", AnimationProperties::fromFlags(AnimationFlags::loop)); - weapon->animator().playAnimation("powerup"); + weapon->playAnimation("powerup"); } } } @@ -415,18 +386,15 @@ void Creature::clearPath() { } glm::vec3 Creature::getSelectablePosition() const { - shared_ptr model(getModelSceneNode()); + auto model = static_pointer_cast(_sceneNode); if (!model) return _position; + if (_dead) return model->getWorldCenterOfAABB(); - if (_dead) return model->getWorldCenterAABB(); + shared_ptr talkDummy(model->model()->getNodeByName(g_talkDummyNode)); - glm::vec3 position; - - if (model->getNodeAbsolutePosition(g_talkDummyNode, position)) { - return model->absoluteTransform() * glm::vec4(position, 1.0f); - } - - return model->getWorldCenterAABB(); + return talkDummy ? + (_transform * talkDummy->absoluteTransform())[3] : + model->getWorldCenterOfAABB(); } float Creature::getAttackRange() const { diff --git a/src/engine/game/object/creature.h b/src/engine/game/object/creature.h index 7cb43d2a..0b19ff97 100644 --- a/src/engine/game/object/creature.h +++ b/src/engine/game/object/creature.h @@ -22,11 +22,12 @@ #include #include "../../audio/stream.h" -#include "../../graphics/lip/lipanimation.h" +#include "../../graphics/lip/animation.h" #include "../../resource/format/2dareader.h" #include "../../resource/format/gffreader.h" #include "../../resource/types.h" -#include "../../scene/animation/eventlistener.h" +#include "../../scene/animeventlistener.h" +#include "../../scene/node/modelnode.h" #include "../../script/types.h" #include "../d20/attributes.h" @@ -226,7 +227,6 @@ private: Gender _gender { Gender::Male }; int _appearance { 0 }; ModelType _modelType { ModelType::Creature }; - std::shared_ptr _headModel; std::shared_ptr _portrait; std::map> _equipment; std::shared_ptr _path; diff --git a/src/engine/game/object/creature_appearance.cpp b/src/engine/game/object/creature_appearance.cpp index c49c541a..b6bcf6b1 100644 --- a/src/engine/game/object/creature_appearance.cpp +++ b/src/engine/game/object/creature_appearance.cpp @@ -42,15 +42,17 @@ namespace game { static const string g_headHookNode("headhook"); static const string g_maskHookNode("gogglehook"); +static const string g_rightHandNode("rhand"); +static const string g_leftHandNode("lhand"); shared_ptr Creature::buildModel() { - string modelName(getBodyModelName()); - if (modelName.empty()) return nullptr; + string bodyModelName(getBodyModelName()); + if (bodyModelName.empty()) return nullptr; - shared_ptr model(Models::instance().get(modelName)); - if (!model) return nullptr; + shared_ptr bodyModel(Models::instance().get(bodyModelName)); + if (!bodyModel) return nullptr; - auto modelSceneNode = make_unique(ModelUsage::Creature, model, _sceneGraph, set(), this); + auto bodySceneNode = make_unique(bodyModel, ModelUsage::Creature, _sceneGraph, this); // Body texture @@ -58,7 +60,7 @@ shared_ptr Creature::buildModel() { if (!bodyTextureName.empty()) { shared_ptr texture(Textures::instance().get(bodyTextureName, TextureUsage::Diffuse)); if (texture) { - modelSceneNode->setDiffuseTexture(texture); + bodySceneNode->setDiffuseTexture(texture); } } @@ -76,34 +78,42 @@ shared_ptr Creature::buildModel() { if (!headModelName.empty()) { shared_ptr headModel(Models::instance().get(headModelName)); if (headModel) { - shared_ptr headSceneNode(modelSceneNode->attach(g_headHookNode, headModel, ModelUsage::Creature)); - if (headSceneNode && maskModel) { - headSceneNode->attach(g_maskHookNode, maskModel, ModelUsage::Equipment); + shared_ptr headHook(bodyModel->getNodeByName(g_headHookNode)); + if (headHook) { + auto headSceneNode = make_shared(headModel, ModelUsage::Creature, _sceneGraph, this); + headSceneNode->setInanimateNodes(bodyModel->getAncestorNodes(headHook->id())); + bodySceneNode->attach(headHook->id(), headSceneNode); + if (maskModel) { + auto maskSceneNode = make_shared(maskModel, ModelUsage::Equipment, _sceneGraph, this); + headSceneNode->attach(g_maskHookNode, maskSceneNode); + } } } } - // Left weapon - - string leftWeaponModelName(getWeaponModelName(InventorySlot::leftWeapon)); - if (!leftWeaponModelName.empty()) { - shared_ptr leftWeaponModel(Models::instance().get(leftWeaponModelName)); - if (leftWeaponModel) { - modelSceneNode->attach("lhand", leftWeaponModel, ModelUsage::Equipment); - } - } - // Right weapon string rightWeaponModelName(getWeaponModelName(InventorySlot::rightWeapon)); if (!rightWeaponModelName.empty()) { - shared_ptr rightWeaponModel(Models::instance().get(rightWeaponModelName)); - if (rightWeaponModel) { - modelSceneNode->attach("rhand", rightWeaponModel, ModelUsage::Equipment); + shared_ptr weaponModel(Models::instance().get(rightWeaponModelName)); + if (weaponModel) { + auto weaponSceneNode = make_shared(weaponModel, ModelUsage::Equipment, _sceneGraph, this); + bodySceneNode->attach(g_rightHandNode, move(weaponSceneNode)); } } - return move(modelSceneNode); + // Left weapon + + string leftWeaponModelName(getWeaponModelName(InventorySlot::leftWeapon)); + if (!leftWeaponModelName.empty()) { + shared_ptr weaponModel(Models::instance().get(leftWeaponModelName)); + if (weaponModel) { + auto weaponSceneNode = make_shared(weaponModel, ModelUsage::Equipment, _sceneGraph, this); + bodySceneNode->attach(g_leftHandNode, move(weaponSceneNode)); + } + } + + return move(bodySceneNode); } string Creature::getBodyModelName() const { diff --git a/src/engine/game/object/door.cpp b/src/engine/game/object/door.cpp index fa89bbcb..3dd909fc 100644 --- a/src/engine/game/object/door.cpp +++ b/src/engine/game/object/door.cpp @@ -68,7 +68,7 @@ void Door::loadFromBlueprint(const string &resRef) { shared_ptr doors(Resources::instance().get2DA("genericdoors")); string modelName(boost::to_lower_copy(doors->getString(_genericType, "modelname"))); - auto model = make_unique(ModelUsage::Door, Models::instance().get(modelName), _sceneGraph); + auto model = make_unique(Models::instance().get(modelName), ModelUsage::Door, _sceneGraph); model->setCullable(true); model->setDrawDistance(FLT_MAX); _sceneNode = move(model); @@ -93,19 +93,19 @@ bool Door::isSelectable() const { } void Door::open(const shared_ptr &triggerrer) { - shared_ptr model(getModelSceneNode()); + auto model = static_pointer_cast(_sceneNode); if (model) { - model->animator().setDefaultAnimation("opened1", AnimationProperties::fromFlags(AnimationFlags::loop)); - model->animator().playAnimation("opening1"); + //model->setDefaultAnimation("opened1", AnimationProperties::fromFlags(AnimationFlags::loop)); + model->playAnimation("opening1"); } _open = true; } void Door::close(const shared_ptr &triggerrer) { - shared_ptr model(getModelSceneNode()); + auto model = static_pointer_cast(_sceneNode); if (model) { - model->animator().setDefaultAnimation("closed", AnimationProperties::fromFlags(AnimationFlags::loop)); - model->animator().playAnimation("closing1"); + //model->setDefaultAnimation("closed", AnimationProperties::fromFlags(AnimationFlags::loop)); + model->playAnimation("closing1"); } _open = false; } diff --git a/src/engine/game/object/placeable.cpp b/src/engine/game/object/placeable.cpp index 434ed207..cc1d92ac 100644 --- a/src/engine/game/object/placeable.cpp +++ b/src/engine/game/object/placeable.cpp @@ -62,7 +62,7 @@ void Placeable::loadFromBlueprint(const string &resRef) { shared_ptr placeables(Resources::instance().get2DA("placeables")); string modelName(boost::to_lower_copy(placeables->getString(_appearance, "modelname"))); - auto model = make_shared(ModelUsage::Placeable, Models::instance().get(modelName), _sceneGraph); + auto model = make_shared(Models::instance().get(modelName), ModelUsage::Placeable, _sceneGraph); model->setCullable(true); model->setDrawDistance(64.0f); _sceneNode = move(model); diff --git a/src/engine/game/object/spatial.cpp b/src/engine/game/object/spatial.cpp index 7db3ea8f..b33a7c51 100644 --- a/src/engine/game/object/spatial.cpp +++ b/src/engine/game/object/spatial.cpp @@ -118,10 +118,9 @@ float SpatialObject::getDistanceTo2(const SpatialObject &other) const { } bool SpatialObject::contains(const glm::vec3 &point) const { - auto model = getModelSceneNode(); - if (!model) return false; + if (!_sceneNode) return false; - const AABB &aabb = model->model()->aabb(); + const AABB &aabb = _sceneNode->aabb(); return (aabb * _transform).contains(point); } @@ -133,13 +132,15 @@ void SpatialObject::face(const SpatialObject &other) { } void SpatialObject::face(const glm::vec3 &point) { + if (point == _position) return; + glm::vec2 dir(glm::normalize(point - _position)); _orientation = glm::quat(glm::vec3(0.0f, 0.0f, -glm::atan(dir.x, dir.y))); updateTransform(); } void SpatialObject::faceAwayFrom(const SpatialObject &other) { - if (_id == other._id) return; + if (_id == other._id || _position == other.position()) return; glm::vec2 dir(glm::normalize(_position - other.position())); _orientation = glm::quat(glm::vec3(0.0f, 0.0f, -glm::atan(dir.x, dir.y))); @@ -214,19 +215,13 @@ bool SpatialObject::isSelectable() const { return false; } -shared_ptr SpatialObject::getModelSceneNode() const { - if (!_sceneNode || _sceneNode->type() != SceneNodeType::Model) return nullptr; - - return static_pointer_cast(_sceneNode); -} - shared_ptr SpatialObject::getWalkmesh() const { return nullptr; } glm::vec3 SpatialObject::getSelectablePosition() const { - auto model = getModelSceneNode(); - return model ? model->getWorldCenterAABB() : _position; + auto model = static_pointer_cast(_sceneNode); + return model ? model->getWorldCenterOfAABB() : _position; } void SpatialObject::setRoom(Room *room) { diff --git a/src/engine/game/object/spatial.h b/src/engine/game/object/spatial.h index a97b3ed9..260a20db 100644 --- a/src/engine/game/object/spatial.h +++ b/src/engine/game/object/spatial.h @@ -23,11 +23,13 @@ #include "object.h" +#include "glm/gtc/quaternion.hpp" #include "glm/mat4x4.hpp" #include "glm/vec3.hpp" #include "../../graphics/walkmesh/walkmesh.h" -#include "../../scene/node/modelnode.h" +#include "../../scene/animproperties.h" +#include "../../scene/node/scenenode.h" #include "../action/playanimation.h" #include "../enginetype/effect.h" @@ -66,7 +68,6 @@ public: virtual glm::vec3 getSelectablePosition() const; float getFacing() const { return glm::eulerAngles(_orientation).z; } - std::shared_ptr getModelSceneNode() const; virtual std::shared_ptr getWalkmesh() const; ObjectFactory &objectFactory() { return *_objectFactory; } diff --git a/src/engine/game/objectselect.cpp b/src/engine/game/objectselect.cpp index 18a5171f..9c699a7b 100644 --- a/src/engine/game/objectselect.cpp +++ b/src/engine/game/objectselect.cpp @@ -90,7 +90,7 @@ vector> ObjectSelector::getSelectableObjects() const { for (auto &object : _area->objects()) { if (!object->isSelectable() || object.get() == partyLeader.get()) continue; - shared_ptr model(object->getModelSceneNode()); + auto model = static_pointer_cast(object->sceneNode()); if (!model || !model->isVisible()) continue; float dist2 = object->getDistanceTo2(origin); diff --git a/src/engine/graphics/lip/lipanimation.cpp b/src/engine/graphics/lip/animation.cpp similarity index 98% rename from src/engine/graphics/lip/lipanimation.cpp rename to src/engine/graphics/lip/animation.cpp index 34031f10..1363f352 100644 --- a/src/engine/graphics/lip/lipanimation.cpp +++ b/src/engine/graphics/lip/animation.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include "lipanimation.h" +#include "animation.h" #include diff --git a/src/engine/graphics/lip/lipanimation.h b/src/engine/graphics/lip/animation.h similarity index 100% rename from src/engine/graphics/lip/lipanimation.h rename to src/engine/graphics/lip/animation.h diff --git a/src/engine/graphics/lip/lipreader.h b/src/engine/graphics/lip/lipreader.h index 77ea0550..e2be9cd8 100644 --- a/src/engine/graphics/lip/lipreader.h +++ b/src/engine/graphics/lip/lipreader.h @@ -19,7 +19,7 @@ #include "../../resource/format/binreader.h" -#include "lipanimation.h" +#include "animation.h" namespace reone { diff --git a/src/engine/graphics/lip/lips.h b/src/engine/graphics/lip/lips.h index 0ffed8fc..d30ba9fb 100644 --- a/src/engine/graphics/lip/lips.h +++ b/src/engine/graphics/lip/lips.h @@ -21,7 +21,7 @@ #include "../../common/cache.h" -#include "lipanimation.h" +#include "animation.h" namespace reone { diff --git a/src/engine/graphics/lip/lipwriter.h b/src/engine/graphics/lip/lipwriter.h index 98ee4a74..71f20978 100644 --- a/src/engine/graphics/lip/lipwriter.h +++ b/src/engine/graphics/lip/lipwriter.h @@ -21,7 +21,7 @@ #include -#include "lipanimation.h" +#include "animation.h" namespace reone { diff --git a/src/engine/graphics/model/animation.cpp b/src/engine/graphics/model/animation.cpp index a058c84c..f20c8e87 100644 --- a/src/engine/graphics/model/animation.cpp +++ b/src/engine/graphics/model/animation.cpp @@ -17,7 +17,7 @@ #include "animation.h" -#include +#include #include "../../common/collectionutil.h" @@ -32,7 +32,7 @@ Animation::Animation( float length, float transitionTime, shared_ptr rootNode, - vector &&events + vector events ) : _name(move(name)), _length(length), @@ -44,13 +44,14 @@ Animation::Animation( } void Animation::fillNodeByName() { - queue> nodes; + stack> nodes; nodes.push(_rootNode); while (!nodes.empty()) { - shared_ptr node(nodes.front()); + shared_ptr node(nodes.top()); nodes.pop(); + _nodeById.insert(make_pair(node->id(), node)); _nodeByName.insert(make_pair(node->name(), node)); for (auto &child : node->children()) { @@ -59,6 +60,10 @@ void Animation::fillNodeByName() { } } +shared_ptr Animation::getNodeById(uint16_t nodeId) const { + return getFromLookupOrNull(_nodeById, nodeId); +} + shared_ptr Animation::getNodeByName(const string &name) const { return getFromLookupOrNull(_nodeByName, name); } diff --git a/src/engine/graphics/model/animation.h b/src/engine/graphics/model/animation.h index 968ee9fd..447b28d2 100644 --- a/src/engine/graphics/model/animation.h +++ b/src/engine/graphics/model/animation.h @@ -27,8 +27,6 @@ namespace reone { namespace graphics { -class MdlReader; - class Animation : boost::noncopyable { public: struct Event { @@ -41,8 +39,9 @@ public: float length, float transitionTime, std::shared_ptr rootNode, - std::vector &&events); + std::vector events); + std::shared_ptr getNodeById(uint16_t nodeId) const; std::shared_ptr getNodeByName(const std::string &name) const; const std::string &name() const { return _name; } @@ -58,6 +57,7 @@ private: std::shared_ptr _rootNode; std::vector _events; + std::unordered_map> _nodeById; std::unordered_map> _nodeByName; void fillNodeByName(); diff --git a/src/engine/graphics/model/mdlreader.cpp b/src/engine/graphics/model/mdlreader.cpp index a477167f..f9d1b034 100644 --- a/src/engine/graphics/model/mdlreader.cpp +++ b/src/engine/graphics/model/mdlreader.cpp @@ -40,7 +40,7 @@ static constexpr int kMdlDataOffset = 12; static constexpr uint32_t kFunctionPtrTslPC = 4285200; static constexpr uint32_t kFunctionPtrTslXbox = 4285872; -// Classificaiton +// Classification static unordered_map g_classifications { { 0, Model::Classification::Other }, @@ -712,7 +712,12 @@ unique_ptr MdlReader::readAnimation(uint32_t offset) { sort(events.begin(), events.end(), [](auto &left, auto &right) { return left.time < right.time; }); } - return make_unique(move(name), length, transitionTime, move(rootNode), move(events)); + return make_unique( + move(name), + length, + transitionTime, + move(rootNode), + move(events)); } } // namespace graphics diff --git a/src/engine/graphics/model/model.cpp b/src/engine/graphics/model/model.cpp index ef7a31d9..87cfc14e 100644 --- a/src/engine/graphics/model/model.cpp +++ b/src/engine/graphics/model/model.cpp @@ -100,6 +100,33 @@ void Model::addAnimation(shared_ptr animation) { _animations.insert(make_pair(animation->name(), move(animation))); } +shared_ptr Model::getNodeById(uint16_t nodeId) const { + return getFromLookupOrNull(_nodeById, nodeId); +} + +shared_ptr Model::getNodeByName(const string &name) const { + return getFromLookupOrNull(_nodeByName, name); +} + +shared_ptr Model::getAABBNode() const { + for (auto &node : _nodeById) { + if (node.second->isAABBMesh()) return node.second; + } + return nullptr; +} + +shared_ptr Model::getAnimation(const string &name) const { + auto maybeAnim = _animations.find(name); + if (maybeAnim != _animations.end()) return maybeAnim->second; + + shared_ptr anim; + if (_superModel) { + anim = _superModel->getAnimation(name); + } + + return move(anim); +} + vector Model::getAnimationNames() const { vector result; @@ -116,30 +143,17 @@ vector Model::getAnimationNames() const { return move(result); } -shared_ptr Model::getAnimation(const string &name) const { - auto maybeAnim = _animations.find(name); - if (maybeAnim != _animations.end()) return maybeAnim->second; +set Model::getAncestorNodes(uint16_t parentId) const { + set result; - shared_ptr anim; - if (_superModel) { - anim = _superModel->getAnimation(name); - } - if (!anim) { - debug(boost::format("Model animation not found: '%s' '%s'") % name % _name, 2); + auto maybeParent = _nodeById.find(parentId); + if (maybeParent != _nodeById.end()) { + for (const ModelNode *node = maybeParent->second->parent(); node; node = node->parent()) { + result.insert(node->id()); + } } - return move(anim); -} - -shared_ptr Model::getNodeByName(const string &name) const { - return getFromLookupOrNull(_nodeByName, name); -} - -shared_ptr Model::getAABBNode() const { - for (auto &node : _nodeById) { - if (node.second->isAABBMesh()) return node.second; - } - return nullptr; + return move(result); } } // namespace graphics diff --git a/src/engine/graphics/model/model.h b/src/engine/graphics/model/model.h index cbd6b2da..bb3a0559 100644 --- a/src/engine/graphics/model/model.h +++ b/src/engine/graphics/model/model.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include #include @@ -63,10 +64,12 @@ public: bool isAffectedByFog() const { return _affectedByFog; } - std::vector getAnimationNames() const; - std::shared_ptr getAnimation(const std::string &name) const; + std::shared_ptr getNodeById(uint16_t nodeId) const; std::shared_ptr getNodeByName(const std::string &name) const; std::shared_ptr getAABBNode() const; + std::shared_ptr getAnimation(const std::string &name) const; + std::vector getAnimationNames() const; + std::set getAncestorNodes(uint16_t parentId) const; const std::string &name() const { return _name; } Classification classification() const { return _classification; } diff --git a/src/engine/graphics/model/modelnode.h b/src/engine/graphics/model/modelnode.h index 56cc4bbc..7dc32ee4 100644 --- a/src/engine/graphics/model/modelnode.h +++ b/src/engine/graphics/model/modelnode.h @@ -155,36 +155,14 @@ public: Lighten }; - template - struct Constraints { - T start; - T mid; - T end; - }; - UpdateMode updateMode { UpdateMode::Invalid }; RenderMode renderMode { RenderMode::Invalid }; BlendMode blendMode { BlendMode::Invalid }; - int renderOrder { 0 }; - std::shared_ptr texture; int gridWidth { 0 }; int gridHeight { 0 }; - int frameStart { 0 }; - int frameEnd { 0 }; - - glm::vec2 size { 0.0f }; - float birthrate { 0.0f }; /**< rate of particle birth per second */ - float lifeExpectancy { 0.0f }; /**< life of each particle in seconds */ - float velocity { 0.0f }; - float randomVelocity { 0.0f }; - float spread { 0.0f }; + int renderOrder { 0 }; bool loop { false }; - float fps { 0.0f }; - - Constraints particleSize; - Constraints color; - Constraints alpha; }; struct Reference { @@ -225,6 +203,10 @@ public: // Specialization + bool isMesh() const { return static_cast(_mesh); } + bool isLight() const { return static_cast(_light); } + bool isEmitter() const { return static_cast(_emitter); } + bool isReference() const { return static_cast(_reference); } bool isSkinMesh() const { return _mesh && _mesh->skin; } bool isDanglyMesh() const { return _mesh && _mesh->danglyMesh; } bool isAABBMesh() const { return _mesh && _mesh->aabbTree; } diff --git a/src/engine/graphics/pixelutil.cpp b/src/engine/graphics/pixelutil.cpp index 0ceec5ae..f24e4815 100644 --- a/src/engine/graphics/pixelutil.cpp +++ b/src/engine/graphics/pixelutil.cpp @@ -45,10 +45,6 @@ uint32_t getInternalPixelFormatGL(PixelFormat format) { return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case PixelFormat::Depth: return GL_DEPTH_COMPONENT; - case PixelFormat::RG16F: - return GL_RG16F; - case PixelFormat::RGB16F: - return GL_RGB16F; default: throw logic_error("Unsupported pixel format: " + to_string(static_cast(format))); } diff --git a/src/engine/graphics/texture/texture.cpp b/src/engine/graphics/texture/texture.cpp index a23e8230..4caa8396 100644 --- a/src/engine/graphics/texture/texture.cpp +++ b/src/engine/graphics/texture/texture.cpp @@ -181,8 +181,6 @@ void Texture::fillTarget(uint32_t target, int level, int width, int height, cons case PixelFormat::BGR: case PixelFormat::BGRA: case PixelFormat::Depth: - case PixelFormat::RG16F: - case PixelFormat::RGB16F: glTexImage2D(target, level, getInternalPixelFormatGL(_pixelFormat), width, height, 0, getPixelFormatGL(), getPixelTypeGL(), pixels); break; default: @@ -195,7 +193,6 @@ uint32_t Texture::getPixelFormatGL() const { case PixelFormat::Grayscale: return GL_RED; case PixelFormat::RGB: - case PixelFormat::RGB16F: return GL_RGB; case PixelFormat::RGBA: case PixelFormat::DXT1: @@ -207,8 +204,6 @@ uint32_t Texture::getPixelFormatGL() const { return GL_BGRA; case PixelFormat::Depth: return GL_DEPTH_COMPONENT; - case PixelFormat::RG16F: - return GL_RG; default: throw logic_error("Unsupported pixel format: " + to_string(static_cast(_pixelFormat))); } @@ -223,8 +218,6 @@ uint32_t Texture::getPixelTypeGL() const { case PixelFormat::BGRA: return GL_UNSIGNED_BYTE; case PixelFormat::Depth: - case PixelFormat::RG16F: - case PixelFormat::RGB16F: return GL_FLOAT; default: throw logic_error("Unsupported pixel format: " + to_string(static_cast(_pixelFormat))); diff --git a/src/engine/graphics/types.h b/src/engine/graphics/types.h index 5ad1aa8b..277d5524 100644 --- a/src/engine/graphics/types.h +++ b/src/engine/graphics/types.h @@ -24,6 +24,10 @@ namespace reone { namespace graphics { constexpr int kNumCubeFaces = 6; + +constexpr float kDefaultClipPlaneNear = 0.1f; +constexpr float kDefaultClipPlaneFar = 10000.0f; + constexpr int kMaxBones = 128; constexpr int kMaxLights = 8; constexpr int kMaxParticles = 32; @@ -39,10 +43,7 @@ enum class PixelFormat { BGRA, DXT1, DXT5, - Depth, - - RG16F, - RGB16F + Depth }; /** diff --git a/src/engine/gui/scenebuilder.cpp b/src/engine/gui/scenebuilder.cpp index dc7ab081..ab1ee096 100644 --- a/src/engine/gui/scenebuilder.cpp +++ b/src/engine/gui/scenebuilder.cpp @@ -51,13 +51,16 @@ unique_ptr SceneBuilder::build() { _modelScale + _modelOffset.y, _zNear, _zFar)); - auto camera = make_shared(scene.get(), projection, _aspect, _zNear, _zFar); + auto camera = make_shared("", projection, scene.get()); if (_cameraNodeName.empty()) { camera->setLocalTransform(_cameraTransform); } else { - MeshSceneNode *modelNode = model->getModelNode(_cameraNodeName); - if (modelNode) { - camera->setLocalTransform(modelNode->absoluteTransform() * _cameraTransform); + shared_ptr refModelNode(model->model()->getNodeByName(_cameraNodeName)); + if (refModelNode) { + shared_ptr refSceneNode(model->getNodeById(refModelNode->id())); + if (refSceneNode) { + camera->setLocalTransform(refModelNode->absoluteTransform() * _cameraTransform); + } } } diff --git a/src/engine/scene/animation/channel.cpp b/src/engine/scene/animation/channel.cpp deleted file mode 100644 index 2444f261..00000000 --- a/src/engine/scene/animation/channel.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (c) 2020-2021 The reone project contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ - -#include "channel.h" - -#include - -#include "glm/common.hpp" - -#include "../node/meshnode.h" -#include "../node/modelnode.h" -#include "../types.h" - -using namespace std; - -using namespace reone::graphics; - -namespace reone { - -namespace scene { - -AnimationChannel::AnimationChannel(ModelSceneNode *sceneNode, set ignoreNodes) : - _sceneNode(sceneNode), - _ignoreNodes(move(ignoreNodes)) { - - if (!sceneNode) { - throw invalid_argument("sceneNode must not be null"); - } -} - -void AnimationChannel::reset() { - _animation.reset(); - _lipAnimation.reset(); - _time = 0.0f; - _freeze = false; - _finished = false; -} - -void AnimationChannel::reset(shared_ptr anim, AnimationProperties properties, shared_ptr lipAnim) { - if (!anim) { - throw invalid_argument("anim must not be null"); - } - _animation = move(anim); - _properties = move(properties); - _lipAnimation = move(lipAnim); - _time = 0.0f; - _freeze = false; - _finished = false; -} - -void AnimationChannel::update(float dt, bool visible) { - if (!_animation || _freeze || _finished) return; - - float newTime, length; - - if (_properties.flags & AnimationFlags::syncLipAnim) { - length = _lipAnimation->length(); - newTime = glm::min(_time + dt, length); - - } else { - length = _animation->length(); - newTime = glm::min(_time + _properties.speed * dt, length); - - // Signal animation events between the previous time and the current time - for (auto &event : _animation->events()) { - if (event.time > _time && event.time <= newTime) { - _sceneNode->signalEvent(event.name); - } - } - } - - if (visible) { - _stateById.clear(); - computeSceneNodeStates(*_animation->rootNode()); - } - - _time = newTime; - - if (_time == length) { - bool loop = _properties.flags & AnimationFlags::loop; - bool syncLipAnim = _properties.flags & AnimationFlags::syncLipAnim; - if (loop && !syncLipAnim) { - _time = 0.0f; - } else { - _finished = true; - } - } -} - -void AnimationChannel::computeSceneNodeStates(const ModelNode &animNode) { - if (_ignoreNodes.count(animNode.name()) == 0) { - MeshSceneNode *modelNodeSceneNode = _sceneNode->getModelNode(animNode.name()); - if (modelNodeSceneNode) { - const ModelNode *modelNode = modelNodeSceneNode->modelNode(); - bool transformChanged = false; - float scale = 1.0f; - glm::vec3 position(modelNode->restPosition()); - glm::quat orientation(modelNode->restOrientation()); - - if (_properties.flags & AnimationFlags::syncLipAnim) { - uint8_t leftFrameIdx, rightFrameIdx; - float factor; - if (_lipAnimation->getKeyframes(_time, leftFrameIdx, rightFrameIdx, factor)) { - float animScale; - if (animNode.getScale(leftFrameIdx, rightFrameIdx, factor, animScale)) { - scale = animScale; - transformChanged = true; - } - glm::vec3 animPosiiton; - if (animNode.getPosition(leftFrameIdx, rightFrameIdx, factor, animPosiiton)) { - position += _properties.scale * animPosiiton; - transformChanged = true; - } - glm::quat animOrientation; - if (animNode.getOrientation(leftFrameIdx, rightFrameIdx, factor, animOrientation)) { - orientation = move(animOrientation); - transformChanged = true; - } - } - } else { - float animScale; - if (animNode.scale().getByTime(_time, animScale)) { - scale = animScale; - transformChanged = true; - } - glm::vec3 animPosition; - if (animNode.position().getByTime(_time, animPosition)) { - position += _properties.scale * animPosition; - transformChanged = true; - } - glm::quat animOrientation; - if (animNode.orientation().getByTime(_time, animOrientation)) { - orientation = move(animOrientation); - transformChanged = true; - } - } - - SceneNodeState state; - float alpha; - if (animNode.alpha().getByTime(_time, alpha)) { - state.flags |= SceneNodeStateFlags::alpha; - state.alpha = alpha; - } - glm::vec3 selfIllumColor; - if (animNode.selfIllumColor().getByTime(_time, selfIllumColor)) { - state.flags |= SceneNodeStateFlags::selfIllum; - state.selfIllumColor = move(selfIllumColor); - } - glm::vec3 lightColor; - if (animNode.color().getByTime(_time, lightColor)) { - state.flags |= SceneNodeStateFlags::lightColor; - state.lightColor = move(lightColor); - } - float lightMultiplier; - if (animNode.multiplier().getByTime(_time, lightMultiplier)) { - state.flags |= SceneNodeStateFlags::lightMultiplier; - state.lightMultiplier = lightMultiplier; - } - float lightRadius; - if (animNode.radius().getByTime(_time, lightRadius)) { - state.flags |= SceneNodeStateFlags::lightRadius; - state.lightRadius = lightRadius; - } - if (transformChanged) { - glm::mat4 transform(1.0f); - transform = glm::scale(transform, glm::vec3(scale)); - transform = glm::translate(transform, position); - transform *= glm::mat4_cast(orientation); - state.flags |= SceneNodeStateFlags::transform; - state.transform = move(transform); - } - _stateById.insert(make_pair(modelNode->id(), move(state))); - } - } - - for (auto &child : animNode.children()) { - computeSceneNodeStates(*child); - } -} - -void AnimationChannel::freeze() { - _freeze = true; -} - -bool AnimationChannel::isSameAnimation(const Animation &anim, const AnimationProperties &properties, shared_ptr lipAnim) const { - return _animation.get() == &anim && _properties == properties && _lipAnimation == lipAnim; -} - -bool AnimationChannel::isActive() const { - return _animation && !_finished; -} - -bool AnimationChannel::isPastTransitionTime() const { - return _animation && _time > _animation->transitionTime(); -} - -bool AnimationChannel::isFinished() const { - return _animation && _finished; -} - -float AnimationChannel::getTransitionTime() const { - return _animation ? _animation->transitionTime() : 0.0f; -} - -bool AnimationChannel::getSceneNodeStateById(uint16_t nodeId, SceneNodeState &state) const { - auto maybeState = _stateById.find(nodeId); - if (maybeState != _stateById.end()) { - state = maybeState->second; - return true; - } - return false; -} - -string AnimationChannel::getAnimationName() const { - return _animation ? _animation->name() : ""; -} - -void AnimationChannel::setTime(float time) { - _time = time; -} - -} // namespace scene - -} // namespace reone diff --git a/src/engine/scene/animation/channel.h b/src/engine/scene/animation/channel.h deleted file mode 100644 index 25807d9d..00000000 --- a/src/engine/scene/animation/channel.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2020-2021 The reone project contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include "../../graphics/lip/lipanimation.h" -#include "../../graphics/model/animation.h" -#include "../../graphics/model/model.h" -#include "../../graphics/model/modelnode.h" - -#include "properties.h" -#include "scenenodestate.h" - -namespace reone { - -namespace scene { - -class ModelSceneNode; - -/** - * Represents a single animation being played on a model. Multiple animation - * channels can be blended or overlayed by SceneNodeAnimator. - * - * @see SceneNodeAnimator - */ -class AnimationChannel { -public: - /** - * @param sceneNode scene node to apply animations to - * @param skipNodes list of model node names to ignore - */ - AnimationChannel(ModelSceneNode *sceneNode, std::set ignoreNodes); - - void reset(); - void reset(std::shared_ptr anim, AnimationProperties properties, std::shared_ptr lipAnim = nullptr); - - /** - * @param dt frame delta time - * @param visible whether the animated scene node is visible - */ - void update(float dt, bool visible = true); - - void freeze(); - - bool isSameAnimation(const graphics::Animation &anim, const AnimationProperties &properties, std::shared_ptr lipAnim = nullptr) const; - - /** - * @return true if this animation channel contains an animation that is not finished, false otherwise - */ - bool isActive() const; - - /** - * @return true if this animation channel contains that is past transition time, false otherwise - */ - bool isPastTransitionTime() const; - - /** - * @return true if this animation channel contains an animation that is finished, false otherwise - */ - bool isFinished() const; - - float getTransitionTime() const; - bool getSceneNodeStateById(uint16_t nodeId, SceneNodeState &state) const; - std::string getAnimationName() const; - - float time() const { return _time; } - - void setTime(float time); - -private: - ModelSceneNode *_sceneNode; - std::set _ignoreNodes; - - std::shared_ptr _animation; - AnimationProperties _properties; - std::shared_ptr _lipAnimation; - float _time { 0.0f }; - bool _freeze { false }; - bool _finished { false }; - std::unordered_map _stateById; - - void computeSceneNodeStates(const graphics::ModelNode &animNode); -}; - -} // namespace scene - -} // namespace reone diff --git a/src/engine/scene/animation/scenenodeanimator.cpp b/src/engine/scene/animation/scenenodeanimator.cpp deleted file mode 100644 index af884df8..00000000 --- a/src/engine/scene/animation/scenenodeanimator.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2020-2021 The reone project contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ - -#include "scenenodeanimator.h" - -#include - -#include "../node/lightnode.h" -#include "../node/meshnode.h" -#include "../node/modelnode.h" -#include "../types.h" - -using namespace std; - -using namespace reone::graphics; - -namespace reone { - -namespace scene { - -static constexpr float kTransitionDuration = 0.25f; - -SceneNodeAnimator::SceneNodeAnimator(ModelSceneNode *sceneNode, set ignoreNodes) : - _sceneNode(sceneNode), - _ignoreNodes(ignoreNodes) { - - if (!sceneNode) { - throw invalid_argument("sceneNode must not be null"); - } - for (int i = 0; i < kChannelCount; ++i) { - _channels.push_back(AnimationChannel(sceneNode, ignoreNodes)); - } -} - -void SceneNodeAnimator::update(float dt, bool visible) { - // Regardless of the composition mode, when there is no active animation on - // the first channel, start the default animation - if (!_channels[0].isActive()) { - playDefaultAnimation(); - return; - } - - // In the Blend mode, if the animation on the first channel is past - // transition time, stop animation on the second channel - if (isInTransition() && _channels[0].isPastTransitionTime()) { - _channels[1].reset(); - _transition = false; - } - - // Update animation channels - for (auto &channel : _channels) { - channel.update(dt, visible); - } - - if (visible) { - // Compute and apply node states to the managed model - _stateById.clear(); - computeSceneNodeStates(*_sceneNode->model()->rootNode()); - applySceneNodeStates(*_sceneNode->model()->rootNode()); - } -} - -void SceneNodeAnimator::playDefaultAnimation() { - if (!_defaultAnimName.empty()) { - playAnimation(_defaultAnimName, _defaultAnimProperties); - } -} - -bool SceneNodeAnimator::isInTransition() const { - return _compositionMode == CompositionMode::Blend && _transition; -} - -void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 parentTransform) { - if (modelNode.isSkinMesh()) return; - - SceneNodeState state; - state.flags |= SceneNodeStateFlags::transform; - - glm::mat4 localTransform(modelNode.localTransform()); - - if (isInTransition()) { - float delta = 1.0f - (_channels[0].getTransitionTime() - _channels[0].time()) / _channels[0].getTransitionTime(); - - // In the Blend mode, blend animations on the first two channels (only transforms) - SceneNodeState channel0State, channel1State; - bool hasChannel0State = _channels[0].getSceneNodeStateById(modelNode.id(), channel0State); - bool hasChannel1State = _channels[1].getSceneNodeStateById(modelNode.id(), channel1State); - if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::transform) && - hasChannel1State && (channel1State.flags & SceneNodeStateFlags::transform)) { - - glm::quat orientation0(glm::toQuat(channel0State.transform)); - glm::quat orientation1(glm::toQuat(channel1State.transform)); - localTransform = glm::translate(glm::mat4(1.0f), glm::vec3(channel0State.transform[3])); - localTransform *= glm::mat4_cast(glm::slerp(orientation1, orientation0, delta)); - - } else if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::transform)) { - localTransform = move(channel0State.transform); - - } else if (hasChannel1State && (channel1State.flags & SceneNodeStateFlags::transform)) { - localTransform = move(channel1State.transform); - } - if (hasChannel0State) { - if (channel0State.flags & SceneNodeStateFlags::alpha) { - state.flags |= SceneNodeStateFlags::alpha; - state.alpha = channel0State.alpha; - } - if (channel0State.flags & SceneNodeStateFlags::selfIllum) { - state.flags |= SceneNodeStateFlags::selfIllum; - state.selfIllumColor = channel0State.selfIllumColor; - } - if (channel0State.flags & SceneNodeStateFlags::lightColor) { - state.flags |= SceneNodeStateFlags::lightColor; - state.lightColor = channel0State.lightColor; - } - if (channel0State.flags & SceneNodeStateFlags::lightMultiplier) { - state.flags |= SceneNodeStateFlags::lightMultiplier; - state.lightMultiplier = channel0State.lightMultiplier; - } - if (channel0State.flags & SceneNodeStateFlags::lightRadius) { - state.flags |= SceneNodeStateFlags::lightRadius; - state.lightRadius = channel0State.lightRadius; - } - } - } else if (_compositionMode == CompositionMode::Overlay) { - // In the Overlay mode, for each state component select the first animation channel to have state for the given node - int componentsLeft = SceneNodeStateFlags::all; - for (int i = kChannelCount - 1; i >= 0; --i) { - SceneNodeState channelState; - if (_channels[i].isActive() && _channels[i].getSceneNodeStateById(modelNode.id(), channelState)) { - if ((channelState.flags & SceneNodeStateFlags::transform) && (componentsLeft & SceneNodeStateFlags::transform)) { - localTransform = move(channelState.transform); - componentsLeft &= ~SceneNodeStateFlags::transform; - } - if ((channelState.flags & SceneNodeStateFlags::alpha) && (componentsLeft & SceneNodeStateFlags::alpha)) { - state.flags |= SceneNodeStateFlags::alpha; - state.alpha = channelState.alpha; - componentsLeft &= ~SceneNodeStateFlags::alpha; - } - if ((channelState.flags & SceneNodeStateFlags::selfIllum) && (componentsLeft & SceneNodeStateFlags::selfIllum)) { - state.flags |= SceneNodeStateFlags::selfIllum; - state.selfIllumColor = move(channelState.selfIllumColor); - componentsLeft &= ~SceneNodeStateFlags::selfIllum; - } - if ((channelState.flags & SceneNodeStateFlags::lightColor) && (componentsLeft & SceneNodeStateFlags::lightColor)) { - state.flags |= SceneNodeStateFlags::lightColor; - state.lightColor = move(channelState.lightColor); - componentsLeft &= ~SceneNodeStateFlags::lightColor; - } - if ((channelState.flags & SceneNodeStateFlags::lightMultiplier) && (componentsLeft & SceneNodeStateFlags::lightMultiplier)) { - state.flags |= SceneNodeStateFlags::lightMultiplier; - state.lightMultiplier = channelState.lightMultiplier; - componentsLeft &= ~SceneNodeStateFlags::lightMultiplier; - } - if ((channelState.flags & SceneNodeStateFlags::lightRadius) && (componentsLeft & SceneNodeStateFlags::lightRadius)) { - state.flags |= SceneNodeStateFlags::lightRadius; - state.lightRadius = channelState.lightRadius; - componentsLeft &= ~SceneNodeStateFlags::lightRadius; - } - } - } - } else { - // Otherwise, select animation on the first channel - SceneNodeState channelState; - if (_channels[0].getSceneNodeStateById(modelNode.id(), channelState)) { - if (channelState.flags & SceneNodeStateFlags::transform) { - localTransform = move(channelState.transform); - } - if (channelState.flags & SceneNodeStateFlags::alpha) { - state.flags |= SceneNodeStateFlags::alpha; - state.alpha = channelState.alpha; - } - if (channelState.flags & SceneNodeStateFlags::selfIllum) { - state.flags |= SceneNodeStateFlags::selfIllum; - state.selfIllumColor = move(channelState.selfIllumColor); - } - if (channelState.flags & SceneNodeStateFlags::lightColor) { - state.flags |= SceneNodeStateFlags::lightColor; - state.lightColor = move(channelState.lightColor); - } - if (channelState.flags & SceneNodeStateFlags::lightMultiplier) { - state.flags |= SceneNodeStateFlags::lightMultiplier; - state.lightMultiplier = channelState.lightMultiplier; - } - if (channelState.flags & SceneNodeStateFlags::lightRadius) { - state.flags |= SceneNodeStateFlags::lightRadius; - state.lightRadius = channelState.lightRadius; - } - } - } - - glm::mat4 absTransform(parentTransform * localTransform); - state.transform = absTransform; - _stateById.insert(make_pair(modelNode.id(), move(state))); - - for (auto &child : modelNode.children()) { - computeSceneNodeStates(*child, absTransform); - } -} - -void SceneNodeAnimator::applySceneNodeStates(ModelNode &modelNode) { - // Do not apply transforms to skinned model nodes - if (modelNode.isSkinMesh()) return; - - auto maybeState = _stateById.find(modelNode.id()); - if (maybeState != _stateById.end()) { - const SceneNodeState &state = maybeState->second; - MeshSceneNode *sceneNode = _sceneNode->getModelNodeById(modelNode.id()); - if (state.flags & SceneNodeStateFlags::transform) { - sceneNode->setLocalTransform(state.transform); - sceneNode->setBoneTransform(state.transform * modelNode.absoluteTransformInverse()); - } - if (state.flags & SceneNodeStateFlags::alpha) { - sceneNode->setAlpha(state.alpha); - } - if (state.flags & SceneNodeStateFlags::selfIllum) { - sceneNode->setSelfIllumColor(state.selfIllumColor); - } - LightSceneNode *light = _sceneNode->getLightNodeById(modelNode.id()); - if (light) { - if (state.flags & SceneNodeStateFlags::lightColor) { - light->setColor(state.lightColor); - } - if (state.flags & SceneNodeStateFlags::lightMultiplier) { - light->setMultiplier(state.lightMultiplier); - } - if (state.flags & SceneNodeStateFlags::lightRadius) { - light->setRadius(state.lightRadius); - } - } - } - - for (auto &child : modelNode.children()) { - applySceneNodeStates(*child); - } -} - -void SceneNodeAnimator::playAnimation(const string &name, AnimationProperties properties) { - shared_ptr model(_sceneNode->model()); - shared_ptr anim(model->getAnimation(name)); - if (anim) { - playAnimation(move(anim), move(properties)); - } -} - -void SceneNodeAnimator::playAnimation(shared_ptr anim, AnimationProperties properties, shared_ptr lipAnim) { - if (!anim) return; - - _compositionMode = determineCompositionMode(properties.flags); - - // Clear composition flags - properties.flags &= ~(AnimationFlags::blend | AnimationFlags::overlay); - - // If scale is 0.0, replace it with models scale - if (properties.scale == 0.0f) { - properties.scale = _sceneNode->model()->animationScale(); - } - - switch (_compositionMode) { - case CompositionMode::Mono: - if (!_channels[0].isSameAnimation(*anim, properties, lipAnim)) { - // Play the specified animation on the first channel and stop animation on other channels - _channels[0].reset(anim, properties, lipAnim); - for (int i = 1; i < kChannelCount; ++i) { - _channels[i].reset(); - } - } - break; - case CompositionMode::Blend: - if (!_channels[0].isSameAnimation(*anim, properties, lipAnim)) { - if (_channels[0].isActive()) { - // Play the specified animation on the first channel - previous animation is moved onto the second channel and is freezed - _channels[1] = _channels[0]; - _channels[0].reset(anim, properties, lipAnim); - _channels[0].setTime(glm::max(0.0f, anim->transitionTime() - kTransitionDuration)); - _channels[1].freeze(); - _transition = true; - } else { - _channels[0].reset(anim, properties, lipAnim); - _transition = false; - } - } - break; - case CompositionMode::Overlay: - // Play the specified animation on the first vacant channel, if any - for (int i = 0; i < kChannelCount; ++i) { - if (!_channels[i].isActive()) { - _channels[i].reset(anim, properties, lipAnim); - break; - } - } - break; - default: - break; - } -} - -SceneNodeAnimator::CompositionMode SceneNodeAnimator::determineCompositionMode(int flags) const { - return (flags & AnimationFlags::blend) ? - CompositionMode::Blend : - ((flags & AnimationFlags::overlay) ? CompositionMode::Overlay : CompositionMode::Mono); -} - -bool SceneNodeAnimator::isAnimationFinished() const { - return _channels[0].isFinished(); -} - -void SceneNodeAnimator::setDefaultAnimation(string name, AnimationProperties properties) { - _defaultAnimName = move(name); - _defaultAnimProperties = move(properties); -} - -} // namespace scene - -} // namespace reone diff --git a/src/engine/scene/animation/scenenodeanimator.h b/src/engine/scene/animation/scenenodeanimator.h deleted file mode 100644 index 0dd197d9..00000000 --- a/src/engine/scene/animation/scenenodeanimator.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2020-2021 The reone project contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include "glm/mat4x4.hpp" - -#include "../../graphics/model/animation.h" -#include "../../graphics/model/modelnode.h" - -#include "channel.h" -#include "properties.h" -#include "scenenodestate.h" - -namespace reone { - -namespace scene { - -class ModelSceneNode; - -/** - * Responsible for applying animations to model scene nodes. Manages up to - * kChannelCount animation channels, which it either blends or overlays depending - * on the composition mode. - */ -class SceneNodeAnimator { -public: - /** - * @param sceneNode scene node to apply animations to - * @param skipNodes names of model nodes to ignore - */ - SceneNodeAnimator(ModelSceneNode *sceneNode, std::set ignoreNodes); - - /** - * @param dt frame delta time - * @param visible whether the animated scene node is visible - */ - void update(float dt, bool visible = true); - - void playAnimation(const std::string &name, AnimationProperties properties = AnimationProperties()); - void playAnimation(std::shared_ptr anim, AnimationProperties properties = AnimationProperties(), std::shared_ptr lipAnim = nullptr); - void playDefaultAnimation(); - - bool isAnimationFinished() const; - - void setDefaultAnimation(std::string name, AnimationProperties properties = AnimationProperties()); - -private: - static constexpr int kChannelCount = 8; - - enum class CompositionMode { - Mono, /**< only animation on the first channel is being played */ - Overlay, /**< animations on all channels play simultaneously */ - Blend /**< animation on the second channel is transitioned into animation on the first channel */ - }; - - struct NodeState { - glm::mat4 transform { 1.0f }; - float alpha { 1.0f }; - glm::vec3 selfIllumColor { 0.0f }; - }; - - ModelSceneNode *_sceneNode; - std::set _ignoreNodes; - std::vector _channels; - - CompositionMode _compositionMode { CompositionMode::Overlay }; - bool _transition { false }; /**< is there an animation transition going on? */ - std::unordered_map _stateById; - - std::string _defaultAnimName; - AnimationProperties _defaultAnimProperties; - - void computeSceneNodeStates(graphics::ModelNode &modelNode, glm::mat4 parentTransform = glm::mat4(1.0f)); - void applySceneNodeStates(graphics::ModelNode &modelNode); - - bool isInTransition() const; - - /** - * Determines the composition mode by animation flags. - */ - CompositionMode determineCompositionMode(int flags) const; -}; - -} // namespace scene - -} // namespace reone diff --git a/src/engine/scene/animation/eventlistener.h b/src/engine/scene/animeventlistener.h similarity index 100% rename from src/engine/scene/animation/eventlistener.h rename to src/engine/scene/animeventlistener.h diff --git a/src/engine/scene/animation/properties.h b/src/engine/scene/animproperties.h similarity index 100% rename from src/engine/scene/animation/properties.h rename to src/engine/scene/animproperties.h diff --git a/src/engine/scene/node/cameranode.cpp b/src/engine/scene/node/cameranode.cpp index 33863d7d..77636644 100644 --- a/src/engine/scene/node/cameranode.cpp +++ b/src/engine/scene/node/cameranode.cpp @@ -27,27 +27,21 @@ namespace reone { namespace scene { -CameraSceneNode::CameraSceneNode(SceneGraph *sceneGraph, glm::mat4 projection, float aspect, float nearPlane, float farPlane) : - SceneNode(SceneNodeType::Camera, sceneGraph), - _projection(projection), - _aspect(aspect), - _nearPlane(nearPlane), - _farPlane(farPlane) { - - updateFrustum(); +CameraSceneNode::CameraSceneNode(string name, glm::mat4 projection, SceneGraph *sceneGraph) : + SceneNode(move(name), SceneNodeType::Camera, sceneGraph), + _projection(projection) { } -void CameraSceneNode::updateAbsoluteTransform() { - SceneNode::updateAbsoluteTransform(); - updateView(); - updateFrustum(); +void CameraSceneNode::onAbsoluteTransformChanged() { + computeView(); + computeFrustumPlanes(); } -void CameraSceneNode::updateView() { - _view = glm::inverse(_absoluteTransform); +void CameraSceneNode::computeView() { + _view = _absTransformInv; } -void CameraSceneNode::updateFrustum() { +void CameraSceneNode::computeFrustumPlanes() { // Implementation of http://www.cs.otago.ac.nz/postgrads/alexis/planeExtraction.pdf glm::mat4 vp(_projection * _view); @@ -111,9 +105,9 @@ bool CameraSceneNode::isInFrustum(const SceneNode &other) const { isInFrustum(other.absoluteTransform()[3]); } -void CameraSceneNode::setProjection(const glm::mat4 &projection) { - _projection = projection; - updateFrustum(); +void CameraSceneNode::setProjection(glm::mat4 projection) { + _projection = move(projection); + computeFrustumPlanes(); } } // namespace scene diff --git a/src/engine/scene/node/cameranode.h b/src/engine/scene/node/cameranode.h index 485640c3..2ce94bfc 100644 --- a/src/engine/scene/node/cameranode.h +++ b/src/engine/scene/node/cameranode.h @@ -27,7 +27,7 @@ namespace scene { class CameraSceneNode : public SceneNode { public: - CameraSceneNode(SceneGraph *sceneGraph, glm::mat4 projection, float aspect, float nearPlane, float farPlane); + CameraSceneNode(std::string name, glm::mat4 projection, SceneGraph *sceneGraph); bool isInFrustum(const glm::vec3 &point) const; bool isInFrustum(const graphics::AABB &aabb) const; @@ -35,18 +35,12 @@ public: const glm::mat4 &projection() const { return _projection; } const glm::mat4 &view() const { return _view; } - float aspect() const { return _aspect; } - float nearPlane() const { return _nearPlane; } - float farPlane() const { return _farPlane; } - void setProjection(const glm::mat4 &projection); + void setProjection(glm::mat4 projection); private: glm::mat4 _projection { 1.0f }; glm::mat4 _view { 1.0f }; - float _aspect { 1.0f }; - float _nearPlane { 0.0f }; - float _farPlane { 0.0f }; // Frustum planes @@ -59,10 +53,10 @@ private: // END Frustum planes - void updateAbsoluteTransform() override; + void computeView(); + void computeFrustumPlanes(); - void updateView(); - void updateFrustum(); + void onAbsoluteTransformChanged() override; }; } // namespace scene diff --git a/src/engine/scene/grasscluster.h b/src/engine/scene/node/dummynode.h similarity index 75% rename from src/engine/scene/grasscluster.h rename to src/engine/scene/node/dummynode.h index 388e04ea..412a7ffc 100644 --- a/src/engine/scene/grasscluster.h +++ b/src/engine/scene/node/dummynode.h @@ -17,17 +17,16 @@ #pragma once -#include "glm/vec2.hpp" -#include "glm/vec3.hpp" +#include "modelnodescenenode.h" namespace reone { namespace scene { -struct GrassCluster { - glm::vec3 position { 0.0f }; - glm::vec2 lightmapUV { 0.0f }; - int variant { 0 }; +class DummySceneNode : public ModelNodeSceneNode { +public: + DummySceneNode(std::shared_ptr modelNode, SceneGraph *sceneGraph) : ModelNodeSceneNode(std::move(modelNode), SceneNodeType::Dummy, sceneGraph) { + } }; } // namespace scene diff --git a/src/engine/scene/node/emitternode.cpp b/src/engine/scene/node/emitternode.cpp index e341bb34..e2a1f617 100644 --- a/src/engine/scene/node/emitternode.cpp +++ b/src/engine/scene/node/emitternode.cpp @@ -42,25 +42,36 @@ namespace reone { namespace scene { static constexpr float kMotionBlurStrength = 0.25f; +static constexpr float kProjectileSpeed = 16.0f; -EmitterSceneNode::EmitterSceneNode(const ModelSceneNode *modelSceneNode, const shared_ptr &emitter, SceneGraph *sceneGraph) : - SceneNode(SceneNodeType::Emitter, sceneGraph), - _modelSceneNode(modelSceneNode), - _emitter(emitter) { +EmitterSceneNode::EmitterSceneNode(const ModelSceneNode *model, shared_ptr modelNode, SceneGraph *sceneGraph) : + ModelNodeSceneNode(modelNode, SceneNodeType::Emitter, sceneGraph), + _model(model) { - if (!modelSceneNode) { - throw invalid_argument("modelSceneNode must not be null"); - } - if (!emitter) { - throw invalid_argument("emitter must not be null"); + if (!model) { + throw invalid_argument("model must not be null"); } + _particleSize.start = modelNode->sizeStart().getByFrameOrElse(0, 1.0f); + _particleSize.end = modelNode->sizeEnd().getByFrameOrElse(0, 1.0f); + _color.start = modelNode->colorStart().getByFrameOrElse(0, glm::vec3(1.0f)); + _color.mid = modelNode->colorMid().getByFrameOrElse(0, glm::vec3(1.0f)); + _color.end = modelNode->colorEnd().getByFrameOrElse(0, glm::vec3(1.0f)); + _alpha.start = modelNode->alphaStart().getByFrameOrElse(0, 1.0f); + _alpha.mid = modelNode->alphaMid().getByFrameOrElse(0, 1.0f); + _alpha.end = modelNode->alphaEnd().getByFrameOrElse(0, 1.0f); + _frameStart = modelNode->frameStart().getByFrameOrElse(0, 0.0f); + _frameEnd = modelNode->frameEnd().getByFrameOrElse(0, 0.0f); + _size.x = modelNode->xSize().getByFrameOrElse(0, 1.0f); + _size.y = modelNode->xSize().getByFrameOrElse(0, 1.0f); + _birthrate = modelNode->birthrate().getByFrameOrElse(0, 0.0f); + _lifeExpectancy = modelNode->lifeExp().getByFrameOrElse(0, 0.0f); + _velocity = modelNode->velocity().getByFrameOrElse(0, 0.0f); + _randomVelocity = modelNode->randVel().getByFrameOrElse(0, 0.0f); + _spread = modelNode->spread().getByFrameOrElse(0, 0.0f); + _fps = modelNode->fps().getByFrameOrElse(0, 0.0f); - init(); -} - -void EmitterSceneNode::init() { - if (_emitter->birthrate != 0) { - _birthInterval = 1.0f / static_cast(_emitter->birthrate); + if (_birthrate != 0.0f) { + _birthInterval = 1.0f / static_cast(_birthrate); } } @@ -72,14 +83,14 @@ void EmitterSceneNode::update(float dt) { spawnParticles(dt); for (auto &particle : _particles) { - particle->update(dt); + updateParticle(*particle, dt); } } void EmitterSceneNode::removeExpiredParticles(float dt) { for (auto it = _particles.begin(); it != _particles.end(); ) { auto &particle = (*it); - if (particle->isExpired()) { + if (isParticleExpired(*particle)) { it = _particles.erase(it); } else { ++it; @@ -88,9 +99,11 @@ void EmitterSceneNode::removeExpiredParticles(float dt) { } void EmitterSceneNode::spawnParticles(float dt) { - switch (_emitter->updateMode) { + shared_ptr emitter(_modelNode->emitter()); + + switch (emitter->updateMode) { case ModelNode::Emitter::UpdateMode::Fountain: - if (_emitter->birthrate != 0.0f) { + if (_birthrate != 0.0f) { if (_birthTimer.advance(dt)) { if (_particles.size() < kMaxParticles) { doSpawnParticle(); @@ -100,7 +113,7 @@ void EmitterSceneNode::spawnParticles(float dt) { } break; case ModelNode::Emitter::UpdateMode::Single: - if (!_spawned || (_particles.empty() && _emitter->loop)) { + if (!_spawned || (_particles.empty() && emitter->loop)) { doSpawnParticle(); _spawned = true; } @@ -111,49 +124,54 @@ void EmitterSceneNode::spawnParticles(float dt) { } void EmitterSceneNode::doSpawnParticle() { - float halfW = 0.005f * _emitter->size.x; - float halfH = 0.005f * _emitter->size.y; + shared_ptr emitter(_modelNode->emitter()); + float halfW = 0.005f * _size.x; + float halfH = 0.005f * _size.y; glm::vec3 position(random(-halfW, halfW), random(-halfH, halfH), 0.0f); float sign; - if (_emitter->spread > glm::pi() && random(0, 1) != 0) { + if (_spread > glm::pi() && random(0, 1) != 0) { sign = -1.0f; } else { sign = 1.0f; } - float velocity = sign * (_emitter->velocity + random(0.0f, _emitter->randomVelocity)); + float velocity = sign * (_velocity + random(0.0f, _randomVelocity)); - auto particle = make_shared(position, velocity, this); + auto particle = make_shared(); + particle->position = move(position); + particle->velocity = velocity; + particle->emitter = this; _particles.push_back(particle); } void EmitterSceneNode::drawParticles(const vector &particles) { if (particles.empty()) return; - shared_ptr texture(_emitter->texture); + shared_ptr emitter(_modelNode->emitter()); + shared_ptr texture(emitter->texture); if (!texture) return; ShaderUniforms uniforms(_sceneGraph->uniformsPrototype()); uniforms.combined.featureMask |= UniformFeatureFlags::particles; - uniforms.particles->gridSize = glm::vec2(_emitter->gridWidth, _emitter->gridHeight); - uniforms.particles->render = static_cast(_emitter->renderMode); + uniforms.particles->gridSize = glm::vec2(emitter->gridWidth, emitter->gridHeight); + uniforms.particles->render = static_cast(emitter->renderMode); for (size_t i = 0; i < particles.size(); ++i) { const Particle &particle = *particles[i]; - glm::mat4 transform(_absoluteTransform); - transform = glm::translate(transform, particles[i]->position()); - if (_emitter->renderMode == ModelNode::Emitter::RenderMode::MotionBlur) { - transform = glm::scale(transform, glm::vec3((1.0f + kMotionBlurStrength * _modelSceneNode->projectileSpeed()) * particle.size(), particle.size(), particle.size())); + glm::mat4 transform(_absTransform); + transform = glm::translate(transform, particles[i]->position); + if (emitter->renderMode == ModelNode::Emitter::RenderMode::MotionBlur) { + transform = glm::scale(transform, glm::vec3((1.0f + kMotionBlurStrength * kProjectileSpeed) * particle.size, particle.size, particle.size)); } else { - transform = glm::scale(transform, glm::vec3(particle.size())); + transform = glm::scale(transform, glm::vec3(particle.size)); } uniforms.particles->particles[i].transform = move(transform); - uniforms.particles->particles[i].color = glm::vec4(particle.color(), 1.0f); - uniforms.particles->particles[i].size = glm::vec2(particle.size()); - uniforms.particles->particles[i].alpha = particle.alpha(); - uniforms.particles->particles[i].frame = particle.frame(); + uniforms.particles->particles[i].color = glm::vec4(particle.color, 1.0f); + uniforms.particles->particles[i].size = glm::vec2(particle.size); + uniforms.particles->particles[i].alpha = particle.alpha; + uniforms.particles->particles[i].frame = particle.frame; } Shaders::instance().activate(ShaderProgram::ParticleParticle, uniforms); @@ -161,7 +179,7 @@ void EmitterSceneNode::drawParticles(const vector &particles) { StateManager::instance().setActiveTextureUnit(TextureUnits::diffuse); texture->bind(); - bool lighten = _emitter->blendMode == ModelNode::Emitter::BlendMode::Lighten; + bool lighten = emitter->blendMode == ModelNode::Emitter::BlendMode::Lighten; if (lighten) { StateManager::instance().withLightenBlending([&particles]() { Meshes::instance().getBillboard()->drawInstanced(static_cast(particles.size())); diff --git a/src/engine/scene/node/emitternode.h b/src/engine/scene/node/emitternode.h index af8868b0..b2f8d4ca 100644 --- a/src/engine/scene/node/emitternode.h +++ b/src/engine/scene/node/emitternode.h @@ -17,8 +17,6 @@ #pragma once -#include "scenenode.h" - #include #include "glm/vec3.hpp" @@ -26,7 +24,7 @@ #include "../../common/timer.h" #include "../../graphics/model/modelnode.h" -#include "../particle.h" +#include "modelnodescenenode.h" namespace reone { @@ -34,9 +32,28 @@ namespace scene { class ModelSceneNode; -class EmitterSceneNode : public SceneNode { +class EmitterSceneNode : public ModelNodeSceneNode { public: - EmitterSceneNode(const ModelSceneNode *modelSceneNode, const std::shared_ptr &emitter, SceneGraph *sceneGraph); + struct Particle { + glm::vec3 position { 0.0f }; + float velocity { 0.0f }; + EmitterSceneNode *emitter { nullptr }; + float animLength { 0.0f }; + float lifetime { 0.0f }; + int frame { 0 }; + float size { 1.0f }; + glm::vec3 color { 1.0f }; + float alpha { 1.0f }; + }; + + template + struct Constraints { + T start; + T mid; + T end; + }; + + EmitterSceneNode(const ModelSceneNode *model, std::shared_ptr modelNode, SceneGraph *sceneGraph); void update(float dt) override; @@ -49,23 +66,43 @@ public: void detonate(); - std::shared_ptr emitter() const { return _emitter; } const std::vector> &particles() const { return _particles; } private: - const ModelSceneNode *_modelSceneNode; - std::shared_ptr _emitter; + const ModelSceneNode *_model; + + Constraints _particleSize; + Constraints _color; + Constraints _alpha; + + int _frameStart { 0 }; + int _frameEnd { 0 }; + glm::vec2 _size { 0.0f }; + float _birthrate { 0.0f }; /**< rate of particle birth per second */ + float _lifeExpectancy { 0.0f }; /**< life of each particle in seconds */ + float _velocity { 0.0f }; + float _randomVelocity { 0.0f }; + float _spread { 0.0f }; + float _fps { 0.0f }; float _birthInterval { 0.0f }; Timer _birthTimer; std::vector> _particles; bool _spawned { false }; - void init(); - void spawnParticles(float dt); void removeExpiredParticles(float dt); void doSpawnParticle(); + + // Particles + + void initParticle(Particle &particle); + void updateParticle(Particle &particle, float dt); + void updateParticleAnimation(Particle &particle, float dt); + + bool isParticleExpired(Particle &particle) const; + + // END Particles }; } // namespace scene diff --git a/src/engine/scene/node/emitternode_particle.cpp b/src/engine/scene/node/emitternode_particle.cpp new file mode 100644 index 00000000..13dfb01b --- /dev/null +++ b/src/engine/scene/node/emitternode_particle.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020-2021 The reone project contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include "emitternode.h" + +using namespace std; + +using namespace reone::graphics; + +namespace reone { + +namespace scene { + +void EmitterSceneNode::initParticle(Particle &particle) { + shared_ptr emitter(_modelNode->emitter()); + + if (_fps > 0) { + particle.animLength = (_frameEnd - _frameStart + 1) / static_cast(_fps); + } + + particle.frame = _frameStart; +} + +void EmitterSceneNode::updateParticle(Particle &particle, float dt) { + shared_ptr emitter(_modelNode->emitter()); + + if (_lifeExpectancy != -1) { + particle.lifetime = glm::min(particle.lifetime + dt, static_cast(_lifeExpectancy)); + } else if (particle.lifetime == particle.animLength) { + particle.lifetime = 0.0f; + } else { + particle.lifetime = glm::min(particle.lifetime + dt, particle.animLength); + } + + if (!isParticleExpired(particle)) { + particle.position.z += particle.velocity * dt; + updateParticleAnimation(particle, dt); + } +} + +template +static T interpolateConstraints(const EmitterSceneNode::Constraints &constraints, float t) { + T result; + if (t < 0.5f) { + float tt = 2.0f * t; + result = (1.0f - tt) * constraints.start + tt * constraints.mid; + } else { + float tt = 2.0f * (t - 0.5f); + result = (1.0f - tt) * constraints.mid + tt * constraints.end; + } + return result; +} + +void EmitterSceneNode::updateParticleAnimation(Particle &particle, float dt) { + shared_ptr emitter(_modelNode->emitter()); + + float maturity; + if (_lifeExpectancy != -1) { + maturity = particle.lifetime / static_cast(_lifeExpectancy); + } else if (particle.animLength > 0.0f) { + maturity = particle.lifetime / particle.animLength; + } else { + maturity = 0.0f; + } + + particle.frame = static_cast(glm::ceil(_frameStart + maturity * (_frameEnd - _frameStart))); + particle.size = interpolateConstraints(_particleSize, maturity); + particle.color = interpolateConstraints(_color, maturity); + particle.alpha = interpolateConstraints(_alpha, maturity); +} + +bool EmitterSceneNode::isParticleExpired(Particle &particle) const { + shared_ptr emitter(_modelNode->emitter()); + return _lifeExpectancy != -1 && particle.lifetime >= _lifeExpectancy; +} + +} // namespace scene + +} // namespace reone diff --git a/src/engine/scene/node/grassnode.cpp b/src/engine/scene/node/grassnode.cpp index 104633d8..b2382567 100644 --- a/src/engine/scene/node/grassnode.cpp +++ b/src/engine/scene/node/grassnode.cpp @@ -33,8 +33,8 @@ namespace reone { namespace scene { -GrassSceneNode::GrassSceneNode(SceneGraph *graph, glm::vec2 quadSize, shared_ptr texture, shared_ptr lightmap) : - SceneNode(SceneNodeType::Grass, graph), +GrassSceneNode::GrassSceneNode(string name, glm::vec2 quadSize, shared_ptr texture, shared_ptr lightmap, SceneGraph *graph) : + SceneNode(move(name), SceneNodeType::Grass, graph), _quadSize(move(quadSize)), _texture(texture), _lightmap(move(lightmap)) { @@ -42,18 +42,17 @@ GrassSceneNode::GrassSceneNode(SceneGraph *graph, glm::vec2 quadSize, shared_ptr if (!texture) { throw invalid_argument("texture must not be null"); } - _transparent = true; } void GrassSceneNode::clear() { _clusters.clear(); } -void GrassSceneNode::addCluster(GrassCluster cluster) { +void GrassSceneNode::addCluster(Cluster cluster) { _clusters.push_back(move(cluster)); } -void GrassSceneNode::drawClusters(const vector &clusters) { +void GrassSceneNode::drawClusters(const vector &clusters) { StateManager::instance().setActiveTextureUnit(TextureUnits::diffuse); _texture->bind(); diff --git a/src/engine/scene/node/grassnode.h b/src/engine/scene/node/grassnode.h index ff77e438..7c691816 100644 --- a/src/engine/scene/node/grassnode.h +++ b/src/engine/scene/node/grassnode.h @@ -19,32 +19,34 @@ #include "../../graphics/texture/texture.h" -#include "../grasscluster.h" - #include "scenenode.h" namespace reone { namespace scene { -class CameraSceneNode; - class GrassSceneNode : public SceneNode { public: - GrassSceneNode(SceneGraph *graph, glm::vec2 quadSize, std::shared_ptr texture, std::shared_ptr lightmap = nullptr); + struct Cluster { + glm::vec3 position { 0.0f }; + glm::vec2 lightmapUV { 0.0f }; + int variant { 0 }; + }; + + GrassSceneNode(std::string name, glm::vec2 quadSize, std::shared_ptr texture, std::shared_ptr lightmap, SceneGraph *graph); void clear(); - void addCluster(GrassCluster cluster); + void addCluster(Cluster cluster); - void drawClusters(const std::vector &clusters); + void drawClusters(const std::vector &clusters); - const std::vector &clusters() const { return _clusters; } + const std::vector &clusters() const { return _clusters; } private: glm::vec2 _quadSize { 0.0f }; std::shared_ptr _texture; std::shared_ptr _lightmap; - std::vector _clusters; + std::vector _clusters; }; } // namespace scene diff --git a/src/engine/scene/node/lightnode.cpp b/src/engine/scene/node/lightnode.cpp index 5e48530a..53635fed 100644 --- a/src/engine/scene/node/lightnode.cpp +++ b/src/engine/scene/node/lightnode.cpp @@ -17,6 +17,8 @@ #include "lightnode.h" +#include + #include "glm/ext.hpp" #include "../../graphics/mesh/meshes.h" @@ -36,9 +38,18 @@ namespace reone { namespace scene { -LightSceneNode::LightSceneNode(int priority, SceneGraph *sceneGraph) : - SceneNode(SceneNodeType::Light, sceneGraph), - _priority(priority) { +static constexpr float kMinDirectionalLightRadius = 1000.0f; + +LightSceneNode::LightSceneNode(const ModelSceneNode *model, shared_ptr modelNode, SceneGraph *sceneGraph) : + ModelNodeSceneNode(modelNode, SceneNodeType::Light, sceneGraph), + _model(model) { + + if (!model) { + throw invalid_argument("model must not be null"); + } + _radius = modelNode->radius().getByFrameOrElse(0, 1.0f); + _multiplier = modelNode->multiplier().getByFrameOrElse(0, 1.0f); + _color = modelNode->color().getByFrameOrElse(0, glm::vec3(1.0f)); } void LightSceneNode::drawLensFlares(const ModelNode::LensFlare &flare) { @@ -48,7 +59,7 @@ void LightSceneNode::drawLensFlares(const ModelNode::LensFlare &flare) { StateManager::instance().setActiveTextureUnit(TextureUnits::diffuse); flare.texture->bind(); - glm::vec4 lightPos(_absoluteTransform[3]); + glm::vec4 lightPos(_absTransform[3]); glm::vec4 lightPosNdc(camera->projection() * camera->view() * lightPos); float w = _sceneGraph->options().width; @@ -79,6 +90,10 @@ void LightSceneNode::drawLensFlares(const ModelNode::LensFlare &flare) { }); } +bool LightSceneNode::isDirectional() const { + return _radius >= kMinDirectionalLightRadius; +} + } // namespace scene } // namespace reone diff --git a/src/engine/scene/node/lightnode.h b/src/engine/scene/node/lightnode.h index fca1272b..e2b598d0 100644 --- a/src/engine/scene/node/lightnode.h +++ b/src/engine/scene/node/lightnode.h @@ -17,58 +17,38 @@ #pragma once -#include "../types.h" - #include "../../graphics/model/modelnode.h" -#include "scenenode.h" +#include "modelnodescenenode.h" namespace reone { namespace scene { -class LightSceneNode : public SceneNode { +class ModelSceneNode; + +class LightSceneNode : public ModelNodeSceneNode { public: - LightSceneNode(int priority, SceneGraph *sceneGraph); + LightSceneNode(const ModelSceneNode *model, std::shared_ptr modelNode, SceneGraph *sceneGraph); void drawLensFlares(const graphics::ModelNode::LensFlare &flare); - bool isShadow() const { return _shadow; } - bool isAmbientOnly() const { return _ambientOnly; } - bool isDirectional() const { return _directional; } + bool isDirectional() const; - const glm::vec3 &color() const { return _color; } - int priority() const { return _priority; } - float multiplier() const { return _multiplier; } float radius() const { return _radius; } - float flareRadius() const { return _flareRadius; } - const std::vector &flares() const { return _flares; } + float multiplier() const { return _multiplier; } + const glm::vec3 &color() const { return _color; } - void setColor(glm::vec3 color) { _color = std::move(color); } - void setMultiplier(float multiplier) { _multiplier = multiplier; } void setRadius(float radius) { _radius = radius; } - void setShadow(bool shadow) { _shadow = shadow; } - void setAmbientOnly(bool ambientOnly) { _ambientOnly = ambientOnly; } - void setDirectional(bool directional) { _directional = directional; } - void setFlareRadius(float radius) { _flareRadius = radius; } - void setFlares(std::vector flares) { _flares = std::move(flares); } + void setMultiplier(float multiplier) { _multiplier = multiplier; } + void setColor(glm::vec3 color) { _color = std::move(color); } private: - int _priority; + const ModelSceneNode *_model; - glm::vec3 _color { 1.0f }; - float _multiplier { 1.0f }; - float _radius { 1.0f }; - bool _shadow { false }; - bool _ambientOnly { false }; - bool _directional { false }; - - // Light flares - - float _flareRadius { 0.0f }; - std::vector _flares; - - // END Light flares + float _radius { 0.0f }; + float _multiplier { 0.0f }; + glm::vec3 _color { 0.0f }; }; } // namespace scene diff --git a/src/engine/scene/node/meshnode.cpp b/src/engine/scene/node/meshnode.cpp index 804c78c7..9b83f7a9 100644 --- a/src/engine/scene/node/meshnode.cpp +++ b/src/engine/scene/node/meshnode.cpp @@ -49,23 +49,16 @@ static constexpr float kUvAnimationSpeed = 250.0f; static bool g_debugWalkmesh = false; -MeshSceneNode::MeshSceneNode(SceneGraph *sceneGraph, const ModelSceneNode *modelSceneNode, ModelNode *modelNode) : - SceneNode(SceneNodeType::ModelNode, sceneGraph), - _modelSceneNode(modelSceneNode), - _modelNode(modelNode) { +MeshSceneNode::MeshSceneNode(const ModelSceneNode *model, shared_ptr modelNode, SceneGraph *sceneGraph) : + ModelNodeSceneNode(modelNode, SceneNodeType::Mesh, sceneGraph), + _model(model) { - if (!modelSceneNode) { - throw invalid_argument("modelSceneNode must not be null"); - } - if (!modelNode) { - throw invalid_argument("modelNode must not be null"); - } - if (_modelNode->alpha().getNumFrames() > 0) { - _alpha = _modelNode->alpha().getByFrame(0); - } - if (_modelNode->selfIllumColor().getNumFrames() > 0) { - _selfIllumColor = _modelNode->selfIllumColor().getByFrame(0); + if (!model) { + throw invalid_argument("model must not be null"); } + _alpha = _modelNode->alpha().getByFrameOrElse(0, 1.0f); + _selfIllumColor = _modelNode->selfIllumColor().getByFrameOrElse(0, glm::vec3(0.0f)); + initTextures(); } @@ -82,15 +75,20 @@ void MeshSceneNode::initTextures() { } void MeshSceneNode::refreshMaterial() { - if (!_textures.diffuse) return; + _material = Material(); - shared_ptr material(Materials::instance().get(_textures.diffuse->name())); - if (material) { - _material = *material; + if (_textures.diffuse) { + shared_ptr material(Materials::instance().get(_textures.diffuse->name())); + if (material) { + _material = *material; + } } } void MeshSceneNode::refreshAdditionalTextures() { + _textures.envmap.reset(); + _textures.bumpmap.reset(); + if (!_textures.diffuse) return; const Texture::Features &features = _textures.diffuse->features(); @@ -105,58 +103,65 @@ void MeshSceneNode::refreshAdditionalTextures() { } void MeshSceneNode::update(float dt) { + SceneNode::update(dt); + shared_ptr mesh(_modelNode->mesh()); if (mesh) { - // UV animation - if (mesh->uvAnimation.dir.x != 0.0f || mesh->uvAnimation.dir.y != 0.0f) { - _uvOffset += kUvAnimationSpeed * mesh->uvAnimation.dir * dt; - _uvOffset -= glm::floor(_uvOffset); - } + updateUVAnimation(dt, *mesh); + updateBumpmapAnimation(dt, *mesh); + updateDanglyMeshAnimation(dt, *mesh); + } +} - // Bumpmap UV animation - if (_textures.bumpmap) { - const Texture::Features &features = _textures.bumpmap->features(); - if (features.procedureType == Texture::ProcedureType::Cycle) { - int frameCount = features.numX * features.numY; - float length = frameCount / static_cast(features.fps); - _bumpmapTime = glm::min(_bumpmapTime + dt, length); - _bumpmapFrame = static_cast(glm::round((frameCount - 1) * (_bumpmapTime / length))); - if (_bumpmapTime == length) { - _bumpmapTime = 0.0f; - } +void MeshSceneNode::updateUVAnimation(float dt, const ModelNode::TriangleMesh &mesh) { + if (mesh.uvAnimation.dir.x != 0.0f || mesh.uvAnimation.dir.y != 0.0f) { + _uvOffset += kUvAnimationSpeed * mesh.uvAnimation.dir * dt; + _uvOffset -= glm::floor(_uvOffset); + } +} + +void MeshSceneNode::updateBumpmapAnimation(float dt, const ModelNode::TriangleMesh &mesh) { + if (!_textures.bumpmap) return; + + const Texture::Features &features = _textures.bumpmap->features(); + if (features.procedureType == Texture::ProcedureType::Cycle) { + int frameCount = features.numX * features.numY; + float length = frameCount / static_cast(features.fps); + _bumpmapTime = glm::min(_bumpmapTime + dt, length); + _bumpmapFrame = static_cast(glm::round((frameCount - 1) * (_bumpmapTime / length))); + if (_bumpmapTime == length) { + _bumpmapTime = 0.0f; + } + } +} + +void MeshSceneNode::updateDanglyMeshAnimation(float dt, const ModelNode::TriangleMesh &mesh) { + shared_ptr danglyMesh(mesh.danglyMesh); + if (!danglyMesh) return; + + bool forceApplied = glm::length2(_danglymeshAnimation.force) > 0.0f; + if (forceApplied) { + // When force is applied, stride in the opposite direction from the applied force + glm::vec3 strideDir(-_danglymeshAnimation.force); + glm::vec3 maxStride(danglyMesh->displacement); + _danglymeshAnimation.stride = glm::clamp(_danglymeshAnimation.stride + danglyMesh->period * strideDir * dt, -maxStride, maxStride); + } else { + // When force is not applied, gradually nullify stride + float strideMag2 = glm::length2(_danglymeshAnimation.stride); + if (strideMag2 > 0.0f) { + glm::vec3 strideDir(-_danglymeshAnimation.stride); + _danglymeshAnimation.stride += danglyMesh->period * strideDir * dt; + if ((strideDir.x > 0.0f && _danglymeshAnimation.stride.x > 0.0f) || (strideDir.x < 0.0f && _danglymeshAnimation.stride.x < 0.0f)) { + _danglymeshAnimation.stride.x = 0.0f; } - } - - // Danglymesh animation - shared_ptr danglyMesh(mesh->danglyMesh); - if (danglyMesh) { - bool forceApplied = glm::length2(_danglymeshAnimation.force) > 0.0f; - if (forceApplied) { - // When force is applied, stride in the opposite direction from the applied force - glm::vec3 strideDir(-_danglymeshAnimation.force); - glm::vec3 maxStride(danglyMesh->displacement); - _danglymeshAnimation.stride = glm::clamp(_danglymeshAnimation.stride + danglyMesh->period * strideDir * dt, -maxStride, maxStride); - } else { - // When force is not applied, gradually nullify stride - float strideMag2 = glm::length2(_danglymeshAnimation.stride); - if (strideMag2 > 0.0f) { - glm::vec3 strideDir(-_danglymeshAnimation.stride); - _danglymeshAnimation.stride += danglyMesh->period * strideDir * dt; - if ((strideDir.x > 0.0f && _danglymeshAnimation.stride.x > 0.0f) || (strideDir.x < 0.0f && _danglymeshAnimation.stride.x < 0.0f)) { - _danglymeshAnimation.stride.x = 0.0f; - } - if ((strideDir.y > 0.0f && _danglymeshAnimation.stride.y > 0.0f) || (strideDir.y < 0.0f && _danglymeshAnimation.stride.y < 0.0f)) { - _danglymeshAnimation.stride.y = 0.0f; - } - if ((strideDir.z > 0.0f && _danglymeshAnimation.stride.z > 0.0f) || (strideDir.z < 0.0f && _danglymeshAnimation.stride.z < 0.0f)) { - _danglymeshAnimation.stride.z = 0.0f; - } - } + if ((strideDir.y > 0.0f && _danglymeshAnimation.stride.y > 0.0f) || (strideDir.y < 0.0f && _danglymeshAnimation.stride.y < 0.0f)) { + _danglymeshAnimation.stride.y = 0.0f; + } + if ((strideDir.z > 0.0f && _danglymeshAnimation.stride.z > 0.0f) || (strideDir.z < 0.0f && _danglymeshAnimation.stride.z < 0.0f)) { + _danglymeshAnimation.stride.z = 0.0f; } } } - - SceneNode::update(dt); } bool MeshSceneNode::shouldRender() const { @@ -184,7 +189,7 @@ bool MeshSceneNode::isTransparent() const { if (!mesh) return false; // Meshless nodes are opaque // Character models are opaque - if (_modelSceneNode->model()->classification() == Model::Classification::Character) return false; + if (_model->model()->classification() == Model::Classification::Character) return false; // Model nodes with alpha less than 1.0 are transparent if (_alpha < 1.0f) return true; @@ -233,8 +238,8 @@ void MeshSceneNode::drawSingle(bool shadowPass) { if (isFeatureEnabled(Feature::HDR)) { uniforms.combined.featureMask |= UniformFeatureFlags::hdr; } - uniforms.combined.general.model = _absoluteTransform; - uniforms.combined.general.alpha = _modelSceneNode->alpha() * _alpha; + uniforms.combined.general.model = _absTransform; + uniforms.combined.general.alpha = _alpha; uniforms.combined.general.ambientColor = glm::vec4(_sceneGraph->ambientLightColor(), 1.0f); ShaderProgram program; @@ -278,7 +283,7 @@ void MeshSceneNode::drawSingle(bool shadowPass) { uniforms.combined.bumpmaps.frame = _bumpmapFrame; } - bool receivesShadows = isReceivingShadows(*_modelSceneNode, *this); + bool receivesShadows = isReceivingShadows(*_model, *this); if (receivesShadows) { uniforms.combined.featureMask |= UniformFeatureFlags::shadows; } @@ -290,8 +295,8 @@ void MeshSceneNode::drawSingle(bool shadowPass) { if (i < static_cast(mesh->skin->boneNodeId.size())) { uint16_t nodeId = mesh->skin->boneNodeId[i]; if (nodeId != 0xffff) { - MeshSceneNode *bone = _modelSceneNode->getModelNodeById(nodeId); - if (bone) { + shared_ptr bone(_model->getNodeById(nodeId)); + if (bone && bone->type() == SceneNodeType::Mesh) { uniforms.skeletal->bones[i] = _modelNode->absoluteTransformInverse() * bone->boneTransform() * _modelNode->absoluteTransform(); } } @@ -338,7 +343,7 @@ void MeshSceneNode::drawSingle(bool shadowPass) { } } - if (_sceneGraph->isFogEnabled() && _modelSceneNode->model()->isAffectedByFog()) { + if (_sceneGraph->isFogEnabled() && _model->model()->isAffectedByFog()) { uniforms.combined.featureMask |= UniformFeatureFlags::fog; uniforms.combined.general.fogNear = _sceneGraph->fogNear(); uniforms.combined.general.fogFar = _sceneGraph->fogFar(); @@ -401,7 +406,7 @@ void MeshSceneNode::drawSingle(bool shadowPass) { } bool MeshSceneNode::isLightingEnabled() const { - if (!isLightingEnabledByUsage(_modelSceneNode->usage())) return false; + if (!isLightingEnabledByUsage(_model->usage())) return false; // Lighting is disabled for lightmapped models, unless dynamic room lighting is enabled if (_textures.lightmap && !isFeatureEnabled(Feature::DynamicRoomLighting)) return false; @@ -418,14 +423,10 @@ bool MeshSceneNode::isLightingEnabled() const { void MeshSceneNode::setAppliedForce(glm::vec3 force) { if (_modelNode->isDanglyMesh()) { // Convert force from world to object space - _danglymeshAnimation.force = _absoluteTransformInv * glm::vec4(force, 0.0f); + _danglymeshAnimation.force = _absTransformInv * glm::vec4(force, 0.0f); } } -glm::vec3 MeshSceneNode::getOrigin() const { - return _absoluteTransform * glm::vec4(_modelNode->mesh()->mesh->aabb().center(), 1.0f); -} - void MeshSceneNode::setDiffuseTexture(const shared_ptr &texture) { _textures.diffuse = texture; refreshMaterial(); diff --git a/src/engine/scene/node/meshnode.h b/src/engine/scene/node/meshnode.h index 885c239e..64004b45 100644 --- a/src/engine/scene/node/meshnode.h +++ b/src/engine/scene/node/meshnode.h @@ -17,7 +17,7 @@ #pragma once -#include "scenenode.h" +#include "modelnodescenenode.h" #include "../../graphics/material.h" #include "../../graphics/model/model.h" @@ -29,32 +29,25 @@ namespace scene { class ModelSceneNode; -class MeshSceneNode : public SceneNode { +class MeshSceneNode : public ModelNodeSceneNode { public: - MeshSceneNode(SceneGraph *sceneGraph, const ModelSceneNode *modelSceneNode, graphics::ModelNode *modelNode); + MeshSceneNode(const ModelSceneNode *model, std::shared_ptr modelNode, SceneGraph *sceneGraph); void update(float dt) override; - void drawSingle(bool shadowPass); - void setAppliedForce(glm::vec3 force); - bool shouldRender() const; bool shouldCastShadows() const; - glm::vec3 getOrigin() const override; - - bool isTransparent() const override; + bool isTransparent() const; bool isSelfIlluminated() const; - const ModelSceneNode *modelSceneNode() const { return _modelSceneNode; } - const graphics::ModelNode *modelNode() const { return _modelNode; } - const glm::mat4 &boneTransform() const { return _boneTransform; } + const ModelSceneNode *model() const { return _model; } - void setBoneTransform(glm::mat4 transform) { _boneTransform = std::move(transform); } void setDiffuseTexture(const std::shared_ptr &texture); void setAlpha(float alpha) { _alpha = alpha; } void setSelfIllumColor(glm::vec3 color) { _selfIllumColor = std::move(color); } + void setAppliedForce(glm::vec3 force); private: struct NodeTextures { @@ -69,17 +62,15 @@ private: glm::vec3 stride { 0.0f }; /**< how far have vertices traveled from the rest position? */ } _danglymeshAnimation; - const ModelSceneNode *_modelSceneNode; - const graphics::ModelNode *_modelNode; + const ModelSceneNode *_model; graphics::Material _material; - glm::mat4 _animTransform { 1.0f }; - glm::mat4 _boneTransform { 1.0f }; glm::vec2 _uvOffset { 0.0f }; float _bumpmapTime { 0.0f }; int _bumpmapFrame { 0 }; float _alpha { 1.0f }; glm::vec3 _selfIllumColor { 0.0f }; + bool _transparent { false }; void initTextures(); @@ -87,6 +78,14 @@ private: void refreshAdditionalTextures(); bool isLightingEnabled() const; + + // Animation + + void updateUVAnimation(float dt, const graphics::ModelNode::TriangleMesh &mesh); + void updateBumpmapAnimation(float dt, const graphics::ModelNode::TriangleMesh &mesh); + void updateDanglyMeshAnimation(float dt, const graphics::ModelNode::TriangleMesh &mesh); + + // END Animation }; } // namespace scene diff --git a/src/engine/scene/node/modelnode.cpp b/src/engine/scene/node/modelnode.cpp index 324f9680..07eea40c 100644 --- a/src/engine/scene/node/modelnode.cpp +++ b/src/engine/scene/node/modelnode.cpp @@ -15,327 +15,173 @@ * along with this program. If not, see . */ -#include #include #include "../../common/collectionutil.h" #include "../../common/log.h" -#include "../../graphics/featureutil.h" -#include "../../graphics/mesh/meshes.h" -#include "../../resource/resources.h" #include "../scenegraph.h" #include "../types.h" -#include "cameranode.h" #include "emitternode.h" #include "lightnode.h" #include "meshnode.h" #include "modelnode.h" using namespace std; -using namespace std::placeholders; using namespace reone::graphics; -using namespace reone::resource; namespace reone { namespace scene { -const float kMinDirectionalLightRadius = 1000.0f; - -static bool g_debugAABB = false; - ModelSceneNode::ModelSceneNode( + shared_ptr model, ModelUsage usage, - const shared_ptr &model, SceneGraph *sceneGraph, - set ignoreNodes, IAnimationEventListener *animEventListener ) : - SceneNode(SceneNodeType::Model, sceneGraph), - _animEventListener(animEventListener), - _usage(usage), + SceneNode(model->name(), SceneNodeType::Model, sceneGraph), _model(model), - _animator(this, ignoreNodes) { - - initModelNodes(); + _usage(usage), + _animEventListener(animEventListener) { + if (!model) { + throw invalid_argument("model must not be null"); + } _volumetric = true; -} - -static bool validateEmitter(const ModelNode::Emitter &emitter) { - switch (emitter.updateMode) { - case ModelNode::Emitter::UpdateMode::Fountain: - case ModelNode::Emitter::UpdateMode::Single: - case ModelNode::Emitter::UpdateMode::Explosion: - break; - default: - warn("validateEmitter: unsupported update mode: " + to_string(static_cast(emitter.updateMode))); - return false; - } - - switch (emitter.renderMode) { - case ModelNode::Emitter::RenderMode::Normal: - case ModelNode::Emitter::RenderMode::BillboardToWorldZ: - case ModelNode::Emitter::RenderMode::MotionBlur: - case ModelNode::Emitter::RenderMode::BillboardToLocalZ: - case ModelNode::Emitter::RenderMode::AlignedToParticleDir: - break; - default: - warn("validateEmitter: unsupported render mode: " + to_string(static_cast(emitter.renderMode))); - return false; - } - - switch (emitter.blendMode) { - case ModelNode::Emitter::BlendMode::Normal: - case ModelNode::Emitter::BlendMode::Lighten: - break; - default: - warn("validateEmitter: unsupported blend mode: " + to_string(static_cast(emitter.blendMode))); - return false; - } - - return true; -} - -void ModelSceneNode::initModelNodes() { - shared_ptr rootNode(getModelNodeSceneNode(*_model->rootNode())); - addChild(rootNode); - - stack nodes; - nodes.push(rootNode.get()); - - while (!nodes.empty()) { - MeshSceneNode *sceneNode = nodes.top(); - nodes.pop(); - - const ModelNode *modelNode = sceneNode->modelNode(); - _modelNodeById.insert(make_pair(modelNode->id(), sceneNode)); - - for (auto &child : modelNode->children()) { - shared_ptr childNode(getModelNodeSceneNode(*child)); - addChild(childNode); - nodes.push(childNode.get()); - - shared_ptr light(child->light()); - if (light) { - // Light is considered directional if its radius exceeds a certain threshold - float radius = child->radius().getByFrameOrElse(0, 1.0f); - bool directional = radius >= kMinDirectionalLightRadius; - - auto lightNode = make_shared(light->priority, _sceneGraph); - lightNode->setColor(child->color().getByFrameOrElse(0, glm::vec3(1.0f))); - lightNode->setMultiplier(child->multiplier().getByFrameOrElse(0, 1.0f)); - lightNode->setRadius(radius); - lightNode->setShadow(light->shadow); - lightNode->setAmbientOnly(light->ambientOnly); - lightNode->setDirectional(directional); - lightNode->setFlareRadius(light->flareRadius); - lightNode->setFlares(light->flares); - - childNode->addChild(lightNode); - _lightNodeById.insert(make_pair(modelNode->id(), lightNode.get())); - } - - shared_ptr emitter(child->emitter()); - if (emitter && validateEmitter(*emitter)) { - auto emitterNode = make_shared(this, emitter, _sceneGraph); - childNode->addChild(emitterNode); - _emitters.push_back(emitterNode); - } - - // If model node is a reference, attach the model it contains to the model nodes scene node - shared_ptr reference(child->reference()); - if (reference) { - attach(*childNode, reference->model, _usage); - } - } - } + buildNodeTree(_model->rootNode(), this); computeAABB(); } -unique_ptr ModelSceneNode::getModelNodeSceneNode(ModelNode &node) const { - auto sceneNode = make_unique(_sceneGraph, this, &node); - sceneNode->setLocalTransform(node.absoluteTransform()); - return move(sceneNode); +void ModelSceneNode::buildNodeTree(shared_ptr node, SceneNode *parent) { + // Convert model node to scene node + shared_ptr sceneNode; + if (node->isMesh()) { + sceneNode = newMeshSceneNode(node); + } else if (node->isLight()) { + sceneNode = newLightSceneNode(node); + } else if (node->isEmitter()) { + sceneNode = newEmitterSceneNode(node); + } else { + sceneNode = newDummySceneNode(node); + } + + if (node->isSkinMesh()) { + // Reparent skin meshes to prevent animation being applied twice + glm::mat4 transform(node->parent()->absoluteTransform() * node->localTransform()); + sceneNode->setLocalTransform(move(transform)); + addChild(sceneNode); + } else { + sceneNode->setLocalTransform(node->localTransform()); + parent->addChild(sceneNode); + } + _nodeById.insert(make_pair(node->id(), sceneNode)); + + if (node->isReference()) { + auto model = make_shared(node->reference()->model, _usage, _sceneGraph, _animEventListener); + attach(node->id(), move(model)); + } + for (auto &child : node->children()) { + buildNodeTree(child, sceneNode.get()); + } } void ModelSceneNode::update(float dt) { - _animator.update(dt, !_culled); + // Optimization: skip invisible models + if (!_visible) return; + SceneNode::update(dt); -} - -void ModelSceneNode::draw() { - if (g_debugAABB) { - glm::mat4 transform(_absoluteTransform * _aabb.transform()); - - ShaderUniforms uniforms(_sceneGraph->uniformsPrototype()); - uniforms.combined.general.model = move(transform); - - Shaders::instance().activate(ShaderProgram::ModelColor, uniforms); - Meshes::instance().getAABB()->draw(); - } -} - -shared_ptr ModelSceneNode::attach(const string &parent, const shared_ptr &model, ModelUsage usage) { - MeshSceneNode *parentNode = getModelNode(parent); - return parentNode ? attach(*parentNode, model, usage) : nullptr; -} - -shared_ptr ModelSceneNode::attach(MeshSceneNode &parent, const shared_ptr &model, ModelUsage usage) { - const ModelNode *parentModelNode = parent.modelNode(); - uint16_t parentId = parentModelNode->id(); - - auto maybeAttached = _attachedModels.find(parentId); - if (maybeAttached != _attachedModels.end()) { - parent.removeChild(*maybeAttached->second); - _attachedModels.erase(maybeAttached); - } - if (model) { - set ignoreNodes; - for (const ModelNode *node = parentModelNode; node; node = node->parent()) { - ignoreNodes.insert(node->name()); - } - auto modelNode = make_shared(usage, model, _sceneGraph, ignoreNodes); - parent.addChild(modelNode); - - return _attachedModels.insert(make_pair(parentId, move(modelNode))).first->second; - } - - return nullptr; - -} - -MeshSceneNode *ModelSceneNode::getModelNode(const string &name) const { - shared_ptr modelNode(_model->getNodeByName(name)); - if (!modelNode) return nullptr; - - return getFromLookupOrNull(_modelNodeById, modelNode->id()); -} - -MeshSceneNode *ModelSceneNode::getModelNodeById(uint16_t nodeId) const { - return getFromLookupOrNull(_modelNodeById, nodeId); -} - -LightSceneNode *ModelSceneNode::getLightNodeById(uint16_t nodeId) const { - return getFromLookupOrNull(_lightNodeById, nodeId); -} - -shared_ptr ModelSceneNode::getAttachedModel(const string &parent) const { - shared_ptr parentModelNode(_model->getNodeByName(parent)); - if (!parentModelNode) return nullptr; - - return getFromLookupOrNull(_attachedModels, parentModelNode->id()); -} - -void ModelSceneNode::attach(const string &parent, const shared_ptr &node) { - shared_ptr parentModelNode(_model->getNodeByName(parent)); - if (!parentModelNode) { - warn(boost::format("Scene node %s: model node not found: %s") % _model->name() % parent); - return; - } - uint16_t parentId = parentModelNode->id(); - - auto maybeNode = _modelNodeById.find(parentId); - if (maybeNode != _modelNodeById.end()) { - maybeNode->second->addChild(node); - } -} - -bool ModelSceneNode::getNodeAbsolutePosition(const string &name, glm::vec3 &position) const { - shared_ptr node(_model->getNodeByName(name)); - if (!node) { - shared_ptr superModel(_model->superModel()); - if (superModel) { - node = superModel->getNodeByName(name); - } - } - if (!node) return false; - - position = node->absoluteTransform()[3]; - - return true; -} - -glm::vec3 ModelSceneNode::getWorldCenterAABB() const { - return _absoluteTransform * glm::vec4(_aabb.center(), 1.0f); -} - -const string &ModelSceneNode::getName() const { - return _model->name(); -} - -void ModelSceneNode::setDiffuseTexture(const shared_ptr &texture) { - for (auto &child : _children) { - if (child->type() == SceneNodeType::ModelNode) { - static_pointer_cast(child)->setDiffuseTexture(texture); - } - } -} - -void ModelSceneNode::setVisible(bool visible) { - if (_visible == visible) return; - - _visible = visible; - - for (auto &attached : _attachedModels) { - attached.second->setVisible(visible); - } -} - -void ModelSceneNode::setAlpha(float alpha) { - _alpha = alpha; - - for (auto &attached : _attachedModels) { - attached.second->setAlpha(alpha); - } + updateAnimations(dt); } void ModelSceneNode::computeAABB() { _aabb.reset(); - stack nodes; - nodes.push(this); - - while (!nodes.empty()) { - SceneNode *node = nodes.top(); - nodes.pop(); - - if (node->type() == SceneNodeType::ModelNode) { - auto modelNode = static_cast(node); - shared_ptr mesh(modelNode->modelNode()->mesh()); - if (mesh) { - _aabb.expand(mesh->mesh->aabb() * node->localTransform()); - } - } - - for (auto &child : node->children()) { - nodes.push(child.get()); + for (auto &node : _nodeById) { + if (node.second->type() == SceneNodeType::Mesh) { + shared_ptr modelNode(node.second->modelNode()); + AABB modelSpaceAABB(modelNode->mesh()->mesh->aabb() * modelNode->absoluteTransform()); + _aabb.expand(modelSpaceAABB); } } } +unique_ptr ModelSceneNode::newDummySceneNode(shared_ptr node) const { + return make_unique(node, _sceneGraph); +} + +unique_ptr ModelSceneNode::newMeshSceneNode(shared_ptr node) const { + return make_unique(this, node, _sceneGraph); +} + +unique_ptr ModelSceneNode::newLightSceneNode(shared_ptr node) const { + return make_unique(this, node, _sceneGraph); +} + +unique_ptr ModelSceneNode::newEmitterSceneNode(shared_ptr node) const { + return make_unique(this, node, _sceneGraph); +} + void ModelSceneNode::signalEvent(const string &name) { - debug(boost::format("Animation event signalled: %s %s") % _model->name() % name, 3); + debug(boost::format("Model '%s': event '%s' signalled") % _model->name() % name, 3); if (name == "detonate") { - for (auto &emitter : _emitters) { - emitter->detonate(); + for (auto &node : _nodeById) { + if (node.second->type() == SceneNodeType::Emitter) { + static_pointer_cast(node.second)->detonate(); + } } } else if (_animEventListener) { _animEventListener->onEventSignalled(name); } } -void ModelSceneNode::setAppliedForce(glm::vec3 force) { - for (auto &nodePair : _modelNodeById) { - nodePair.second->setAppliedForce(force); +void ModelSceneNode::attach(uint16_t parentId, shared_ptr node) { + auto maybeParent = _nodeById.find(parentId); + if (maybeParent == _nodeById.end()) return; + + shared_ptr parent(maybeParent->second); + parent->addChild(node); + _attachmentByNodeId.insert(make_pair(parentId, node)); +} + +void ModelSceneNode::attach(const string &parentName, shared_ptr node) { + auto parent = _model->getNodeByName(parentName); + if (parent) { + attach(parent->id(), node); } - for (auto &attached : _attachedModels) { - attached.second->setAppliedForce(force); +} + +shared_ptr ModelSceneNode::getNodeById(uint16_t nodeId) const { + return getFromLookupOrNull(_nodeById, nodeId); +} + +shared_ptr ModelSceneNode::getAttachment(const string &parentName) const { + auto parent = _model->getNodeByName(parentName); + return parent ? getFromLookupOrNull(_attachmentByNodeId, parent->id()) : nullptr; +} + +void ModelSceneNode::setDiffuseTexture(shared_ptr texture) { + for (auto &child : _children) { + if (child->type() == SceneNodeType::Mesh) { + static_pointer_cast(child)->setDiffuseTexture(texture); + } + } +} + +void ModelSceneNode::setAppliedForce(glm::vec3 force) { + for (auto &node : _nodeById) { + if (node.second->type() == SceneNodeType::Mesh) { + static_pointer_cast(node.second)->setAppliedForce(force); + } + } + for (auto &attachment : _attachmentByNodeId) { + if (attachment.second->type() == SceneNodeType::Model) { + static_pointer_cast(attachment.second)->setAppliedForce(force); + } } } diff --git a/src/engine/scene/node/modelnode.h b/src/engine/scene/node/modelnode.h index 818d613f..d6f15613 100644 --- a/src/engine/scene/node/modelnode.h +++ b/src/engine/scene/node/modelnode.h @@ -17,91 +17,148 @@ #pragma once +#include #include #include +#include "../../graphics/lip/animation.h" #include "../../graphics/model/model.h" -#include "../../graphics/shader/shaders.h" -#include "../../graphics/walkmesh/walkmesh.h" -#include "../animation/eventlistener.h" -#include "../animation/scenenodeanimator.h" +#include "../animeventlistener.h" +#include "../animproperties.h" #include "../types.h" -#include "scenenode.h" +#include "dummynode.h" +#include "emitternode.h" +#include "lightnode.h" +#include "meshnode.h" namespace reone { namespace scene { -class EmitterSceneNode; -class LightSceneNode; -class MeshSceneNode; +constexpr float kDefaultDrawDistance = 1024.0f; +constexpr int kNumAnimationChannels = 8; class ModelSceneNode : public SceneNode { public: ModelSceneNode( + std::shared_ptr model, ModelUsage usage, - const std::shared_ptr &model, SceneGraph *sceneGraph, - std::set ignoreNodes = std::set(), IAnimationEventListener *animEventListener = nullptr); void update(float dt) override; - void draw() override; void computeAABB(); void signalEvent(const std::string &name); + + std::shared_ptr getNodeById(uint16_t nodeId) const; + + std::shared_ptr model() const { return _model; } + ModelUsage usage() const { return _usage; } + float drawDistance() const { return _drawDistance; } + + void setDrawDistance(float distance) { _drawDistance = distance; } + void setDiffuseTexture(std::shared_ptr texture); void setAppliedForce(glm::vec3 force); - MeshSceneNode *getModelNode(const std::string &name) const; - MeshSceneNode *getModelNodeById(uint16_t nodeId) const; - LightSceneNode *getLightNodeById(uint16_t nodeId) const; - std::shared_ptr getAttachedModel(const std::string &parent) const; - bool getNodeAbsolutePosition(const std::string &name, glm::vec3 &position) const; - glm::vec3 getWorldCenterAABB() const; - const std::string &getName() const; + // Animation - ModelUsage usage() const { return _usage; } - std::shared_ptr model() const { return _model; } - std::shared_ptr walkmesh() const { return _walkmesh; } - float alpha() const { return _alpha; } - float projectileSpeed() const { return _projectileSpeed; } - SceneNodeAnimator &animator() { return _animator; } + void playAnimation(const std::string &name, AnimationProperties properties = AnimationProperties()); + void playAnimation(std::shared_ptr anim, std::shared_ptr lipAnim = nullptr, AnimationProperties properties = AnimationProperties()); - void setVisible(bool visible) override; - void setDiffuseTexture(const std::shared_ptr &texture); - void setAlpha(float alpha); - void setProjectileSpeed(float speed) { _projectileSpeed = speed; } - void setWalkmesh(std::shared_ptr walkmesh) { _walkmesh = std::move(walkmesh); } + bool isAnimationFinished() const; + + void setInanimateNodes(std::set nodes) { _inanimateNodes = std::move(nodes); } + + // END Animation // Attachments - std::shared_ptr attach(const std::string &parent, const std::shared_ptr &model, ModelUsage usage); - std::shared_ptr attach(MeshSceneNode &parent, const std::shared_ptr &model, ModelUsage usage); - void attach(const std::string &parent, const std::shared_ptr &node); + void attach(uint16_t parentId, std::shared_ptr node); + void attach(const std::string &parentName, std::shared_ptr node); + + std::shared_ptr getAttachment(const std::string &parentName) const; // END Attachments private: + enum class AnimationBlendMode { + Single, + Blend, + Overlay + }; + + struct AnimationStateFlags { + static constexpr int transform = 1; + static constexpr int alpha = 2; + static constexpr int selfIllumColor = 4; + }; + + struct AnimationState { + int flags { 0 }; + glm::mat4 transform { 1.0f }; + float alpha { 0.0f }; + glm::vec3 selfIllumColor { 0.0f }; + }; + + struct AnimationChannel { + std::shared_ptr anim; + std::shared_ptr lipAnim; + AnimationProperties properties; + float time { 0.0f }; + std::unordered_map stateById; + + // Flags + + bool finished { false }; /**< channel contains a fire-and-forget animation that has finished playing */ + bool transition { false }; /**< when computing states, use animation transition time as channel time */ + bool freeze { false }; /**< channel time is not to be updated */ + + // END Flags + }; + + std::shared_ptr _model; + ModelUsage _usage; IAnimationEventListener *_animEventListener; - ModelUsage _usage; - std::shared_ptr _model; - std::shared_ptr _walkmesh; - SceneNodeAnimator _animator; + float _drawDistance { kDefaultDrawDistance }; - std::unordered_map _modelNodeById; - std::unordered_map _lightNodeById; - std::vector> _emitters; - std::unordered_map> _attachedModels; - bool _visible { true }; - float _alpha { 1.0f }; - float _projectileSpeed { 0.0f }; + // Lookups - void initModelNodes(); + std::unordered_map> _nodeById; + std::unordered_map> _attachmentByNodeId; - std::unique_ptr getModelNodeSceneNode(graphics::ModelNode &node) const; + // END Lookups + + // Animation + + AnimationChannel _animChannels[kNumAnimationChannels]; + AnimationBlendMode _animBlendMode { AnimationBlendMode::Single }; + std::set _inanimateNodes; /**< node identifiers that are not to be animated */ + + // END Animation + + void buildNodeTree(std::shared_ptr node, SceneNode *parent); + + std::unique_ptr newDummySceneNode(std::shared_ptr node) const; + std::unique_ptr newMeshSceneNode(std::shared_ptr node) const; + std::unique_ptr newLightSceneNode(std::shared_ptr node) const; + std::unique_ptr newEmitterSceneNode(std::shared_ptr node) const; + + // Animation + + void resetAnimationChannel(AnimationChannel &channel, std::shared_ptr anim = nullptr, std::shared_ptr lipAnim = nullptr, AnimationProperties properties = AnimationProperties()); + void updateAnimations(float dt); + void updateAnimationChannel(AnimationChannel &channel, float dt); + void computeAnimationStates(AnimationChannel &channel, float time, const graphics::ModelNode &modelNode); + void applyAnimationStates(const graphics::ModelNode &modelNode); + void computeBoneTransforms(); + + static AnimationBlendMode getAnimationBlendMode(int flags); + + // END Animation }; } // namespace scene diff --git a/src/engine/scene/node/modelnode_animation.cpp b/src/engine/scene/node/modelnode_animation.cpp new file mode 100644 index 00000000..7e25fd19 --- /dev/null +++ b/src/engine/scene/node/modelnode_animation.cpp @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2020-2021 The reone project contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#include "modelnode.h" + +#include "glm/gtx/matrix_decompose.hpp" +#include "glm/gtx/transform.hpp" + +using namespace std; + +using namespace reone::graphics; + +namespace reone { + +namespace scene { + +void ModelSceneNode::playAnimation(const string &name, AnimationProperties properties) { + shared_ptr anim(_model->getAnimation(name)); + if (anim) { + playAnimation(anim, nullptr, move(properties)); + } +} + +void ModelSceneNode::playAnimation(shared_ptr anim, shared_ptr lipAnim, AnimationProperties properties) { + if (properties.scale == 0.0f) { + properties.scale = _model->animationScale(); + } + AnimationBlendMode blendMode = getAnimationBlendMode(properties.flags); + + switch (blendMode) { + case AnimationBlendMode::Single: + // In Single mode, add animation to the first channel + resetAnimationChannel(_animChannels[0], anim, lipAnim, properties); + break; + + case AnimationBlendMode::Blend: { + // In Blend mode, if there is an unfinished animation in the first + // channel, move it into the second channel and initiate transition + bool transition = false; + if (_animChannels[0].anim && !_animChannels[0].finished) { + _animChannels[1] = _animChannels[0]; + _animChannels[1].transition = false; + _animChannels[1].freeze = true; + transition = true; + } + // Add animation to the first channel + resetAnimationChannel(_animChannels[0], anim, lipAnim, properties); + _animChannels[0].transition = transition; + break; + } + + case AnimationBlendMode::Overlay: + // In Overlay mode, add animation to the first channel and move all + // other channels down the stack. If current mode is not Overlay, + // reset all other channels. + if (_animBlendMode == AnimationBlendMode::Overlay) { + for (int i = kNumAnimationChannels - 1; i > 0; --i) { + _animChannels[i] = _animChannels[i - 1]; + } + } else { + for (int i = 1; i < kNumAnimationChannels; ++i) { + resetAnimationChannel(_animChannels[i]); + } + } + resetAnimationChannel(_animChannels[0], anim, lipAnim, properties); + break; + + default: + break; + } + + _animBlendMode = blendMode; + + // Optionally propagate animation to attachments + if (properties.flags & AnimationFlags::propagate) { + for (auto &attachment : _attachmentByNodeId) { + if (attachment.second->type() == SceneNodeType::Model) { + static_pointer_cast(attachment.second)->playAnimation(anim, lipAnim, properties); + } + } + } +} + +ModelSceneNode::AnimationBlendMode ModelSceneNode::getAnimationBlendMode(int flags) { + return (flags & AnimationFlags::blend) ? + AnimationBlendMode::Blend : + ((flags & AnimationFlags::overlay) ? AnimationBlendMode::Overlay : AnimationBlendMode::Single); +} + +void ModelSceneNode::resetAnimationChannel(AnimationChannel &channel, shared_ptr anim, shared_ptr lipAnim, AnimationProperties properties) { + channel.anim = move(anim); + channel.lipAnim = move(lipAnim); + channel.properties = move(properties); + channel.time = 0.0f; + channel.stateById.clear(); + channel.finished = false; + channel.transition = false; + channel.freeze = false; +} + +void ModelSceneNode::updateAnimations(float dt) { + for (auto &channel : _animChannels) { + updateAnimationChannel(channel, dt); + } + + // Apply states and compute bone transforms only when this model is not culled + if (!_culled) { + applyAnimationStates(*_model->rootNode()); + computeBoneTransforms(); + } +} + +void ModelSceneNode::updateAnimationChannel(AnimationChannel &channel, float dt) { + // Do not update if there is no animation, freezed or a finished animation + // in the channel + if (!channel.anim || channel.freeze || channel.finished) return; + + // Take length from the lip animation, if any + float length = channel.lipAnim ? channel.lipAnim->length() : channel.anim->length(); + + // Advance time + channel.time = glm::min(length, channel.time + channel.properties.speed * dt); + + // Clear transition flag if past transition time + if (channel.transition && channel.time >= channel.anim->transitionTime()) { + channel.transition = false; + } + + // Signal events between previous and current time + for (auto &event : channel.anim->events()) { + if (event.time > channel.time && event.time <= channel.time) { + signalEvent(event.name); + } + } + + // Loop or finish playing the animation + if (channel.time == length) { + bool loop = channel.properties.flags & AnimationFlags::loop; + if (loop) { + channel.time = 0.0f; + } else { + channel.finished = true; + } + } + + // Compute animation states only when this model is not culled + if (!_culled) { + float time = channel.transition ? channel.anim->transitionTime() : channel.time; + channel.stateById.clear(); + computeAnimationStates(channel, time, *_model->rootNode()); + } +} + +void ModelSceneNode::computeAnimationStates(AnimationChannel &channel, float time, const ModelNode &modelNode) { + shared_ptr animNode(channel.anim->getNodeById(modelNode.id())); + if (animNode && _inanimateNodes.count(modelNode.id()) == 0) { + AnimationState state; + state.flags = 0; + + glm::vec3 position(modelNode.restPosition()); + glm::quat orientation(modelNode.restOrientation()); + float scale = 1.0f; + + if (channel.lipAnim) { + uint8_t leftShape, rightShape; + float factor; + if (channel.lipAnim->getKeyframes(time, leftShape, rightShape, factor)) { + glm::vec3 animPosition; + if (animNode->getPosition(leftShape, rightShape, factor, animPosition)) { + position += channel.properties.scale * animPosition; + state.flags |= AnimationStateFlags::transform; + } + glm::quat animOrientation; + if (animNode->getOrientation(leftShape, rightShape, factor, animOrientation)) { + orientation = move(animOrientation); + state.flags |= AnimationStateFlags::transform; + } + float animScale; + if (animNode->getScale(leftShape, rightShape, factor, animScale)) { + scale = animScale; + state.flags |= AnimationStateFlags::transform; + } + } + } else { + glm::vec3 animPosition; + if (animNode->position().getByTime(time, animPosition)) { + position += channel.properties.scale * animPosition; + state.flags |= AnimationStateFlags::transform; + } + glm::quat animOrientation; + if (animNode->orientation().getByTime(time, animOrientation)) { + orientation = move(animOrientation); + state.flags |= AnimationStateFlags::transform; + } + float animScale; + if (animNode->scale().getByTime(time, animScale)) { + scale = animScale; + state.flags |= AnimationStateFlags::transform; + } + } + + if (state.flags & AnimationStateFlags::transform) { + state.transform *= glm::scale(glm::vec3(scale)); + state.transform *= glm::translate(position); + state.transform *= glm::mat4_cast(orientation); + } + + float animAlpha; + if (animNode->alpha().getByTime(time, animAlpha)) { + state.flags |= AnimationStateFlags::alpha; + state.alpha = animAlpha; + } + + glm::vec3 animSelfIllum; + if (animNode->selfIllumColor().getByTime(time, animSelfIllum)) { + state.flags |= AnimationStateFlags::selfIllumColor; + state.selfIllumColor = move(animSelfIllum); + } + + channel.stateById.insert(make_pair(modelNode.id(), move(state))); + } + + for (auto &child : modelNode.children()) { + computeAnimationStates(channel, time, *child); + } +} + +void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) { + auto maybeSceneNode = _nodeById.find(modelNode.id()); + if (maybeSceneNode != _nodeById.end()) { + shared_ptr sceneNode(maybeSceneNode->second); + AnimationState combined; + + switch (_animBlendMode) { + case AnimationBlendMode::Single: + case AnimationBlendMode::Blend: { + bool blend = _animBlendMode == AnimationBlendMode::Blend && _animChannels[0].transition; + auto state1 = _animChannels[0].stateById.count(modelNode.id()) > 0 ? _animChannels[0].stateById[modelNode.id()] : AnimationState(); + auto state2 = _animChannels[1].stateById.count(modelNode.id()) > 0 ? _animChannels[1].stateById[modelNode.id()] : AnimationState(); + if (blend && state1.flags & AnimationStateFlags::transform && state2.flags & AnimationStateFlags::transform) { + float factor = glm::min(1.0f, _animChannels[0].time / _animChannels[0].anim->transitionTime()); + glm::vec3 scale1, scale2, translation1, translation2, skew; + glm::quat orientation1, orientation2; + glm::vec4 perspective; + glm::decompose(state1.transform, scale1, orientation1, translation1, skew, perspective); + glm::decompose(state2.transform, scale2, orientation2, translation2, skew, perspective); + combined.flags |= AnimationStateFlags::transform; + combined.transform *= glm::scale(glm::mix(scale2, scale1, factor)); + combined.transform *= glm::translate(glm::mix(translation2, translation1, factor)); + combined.transform *= glm::mat4_cast(glm::slerp(orientation2, orientation1, factor)); + } else if (state1.flags & AnimationStateFlags::transform) { + combined.flags |= AnimationStateFlags::transform; + combined.transform = state1.transform; + } + if (state1.flags & AnimationStateFlags::alpha) { + combined.flags |= AnimationStateFlags::alpha; + combined.alpha = state1.alpha; + } + if (state1.flags & AnimationStateFlags::selfIllumColor) { + combined.flags |= AnimationStateFlags::selfIllumColor; + combined.selfIllumColor = state1.selfIllumColor; + } + break; + } + case AnimationBlendMode::Overlay: + for (int i = 0; i < kNumAnimationChannels; ++i) { + auto maybeState = _animChannels[i].stateById.find(modelNode.id()); + if (maybeState != _animChannels[i].stateById.end()) { + const AnimationState &state = maybeState->second; + if ((state.flags & AnimationStateFlags::transform) && !(combined.flags & AnimationStateFlags::transform)) { + combined.flags |= AnimationStateFlags::transform; + combined.transform = state.transform; + } + if ((state.flags & AnimationStateFlags::alpha) && !(combined.flags & AnimationStateFlags::alpha)) { + combined.flags |= AnimationStateFlags::alpha; + combined.alpha = state.alpha; + } + if ((state.flags & AnimationStateFlags::selfIllumColor) && !(combined.flags & AnimationStateFlags::selfIllumColor)) { + combined.flags |= AnimationStateFlags::selfIllumColor; + combined.selfIllumColor = state.selfIllumColor; + } + } + } + break; + default: + break; + } + + if (combined.flags & AnimationStateFlags::transform) { + sceneNode->setLocalTransform(combined.transform); + } else if (!modelNode.isSkinMesh()) { + sceneNode->setLocalTransform(modelNode.localTransform()); + } + if (combined.flags & AnimationStateFlags::alpha) { + static_pointer_cast(sceneNode)->setAlpha(combined.alpha); + } + if (combined.flags & AnimationStateFlags::selfIllumColor) { + static_pointer_cast(sceneNode)->setSelfIllumColor(combined.selfIllumColor); + } + } + + for (auto &child : modelNode.children()) { + applyAnimationStates(*child); + } +} + +void ModelSceneNode::computeBoneTransforms() { + for (auto &node : _nodeById) { + glm::mat4 transform(1.0f); + transform = node.second->absoluteTransform() * node.second->modelNode()->absoluteTransformInverse(); // make relative to the rest pose (world space) + transform = _absTransformInv * transform; // world space to model space + node.second->setBoneTransform(move(transform)); + } +} + +bool ModelSceneNode::isAnimationFinished() const { + return _animChannels[0].anim && _animChannels[0].finished; +} + +} // namespace scene + +} // namespace reone diff --git a/src/engine/scene/animation/scenenodestate.h b/src/engine/scene/node/modelnodescenenode.cpp similarity index 66% rename from src/engine/scene/animation/scenenodestate.h rename to src/engine/scene/node/modelnodescenenode.cpp index 465268fc..5d47a5a3 100644 --- a/src/engine/scene/animation/scenenodestate.h +++ b/src/engine/scene/node/modelnodescenenode.cpp @@ -15,29 +15,26 @@ * along with this program. If not, see . */ -#pragma once +#include "modelnodescenenode.h" -#include "glm/mat4x4.hpp" -#include "glm/vec3.hpp" +#include -#include "../types.h" +using namespace std; + +using namespace reone::graphics; namespace reone { namespace scene { -/** - * @see SceneNodeStateFlags - */ -struct SceneNodeState { - int flags { 0 }; - glm::mat4 transform { 1.0f }; - float alpha { 1.0f }; - glm::vec3 selfIllumColor { 0.0f }; - glm::vec3 lightColor { 1.0f }; - float lightMultiplier { 1.0f }; - float lightRadius { 1.0f }; -}; +ModelNodeSceneNode::ModelNodeSceneNode(shared_ptr modelNode, SceneNodeType type, SceneGraph *sceneGraph) : + SceneNode(modelNode->name(), type, sceneGraph), + _modelNode(modelNode) { + + if (!modelNode) { + throw invalid_argument("modelNode must not be null"); + } +} } // namespace scene diff --git a/src/engine/scene/particle.h b/src/engine/scene/node/modelnodescenenode.h similarity index 50% rename from src/engine/scene/particle.h rename to src/engine/scene/node/modelnodescenenode.h index f16d72bb..05d4c345 100644 --- a/src/engine/scene/particle.h +++ b/src/engine/scene/node/modelnodescenenode.h @@ -17,44 +17,28 @@ #pragma once -#include "glm/vec3.hpp" +#include "../../graphics/model/modelnode.h" + +#include "scenenode.h" namespace reone { namespace scene { -class EmitterSceneNode; - -class Particle { +class ModelNodeSceneNode : public SceneNode { public: - Particle(glm::vec3 position, float velocity, EmitterSceneNode *emitter); + std::shared_ptr modelNode() const { return _modelNode; } + const glm::mat4 &boneTransform() const { return _boneTransform; } - void update(float dt); + void setBoneTransform(glm::mat4 transform) { _boneTransform = std::move(transform); } - bool isExpired() const; +protected: + std::shared_ptr _modelNode; - EmitterSceneNode *emitter() const { return _emitter; } - const glm::vec3 &position() const { return _position; } - float size() const { return _size; } - int frame() const { return _frame; } - const glm::vec3 &color() const { return _color; } - float alpha() const { return _alpha; } + ModelNodeSceneNode(std::shared_ptr modelNode, SceneNodeType type, SceneGraph *sceneGraph); private: - glm::vec3 _position; - float _velocity; - EmitterSceneNode *_emitter; - - float _animLength { 0.0f }; - int _renderOrder { 0 }; - float _lifetime { 0.0f }; - int _frame { 0 }; - float _size { 1.0f }; - glm::vec3 _color { 1.0f }; - float _alpha { 1.0f }; - - void init(); - void updateAnimation(float dt); + glm::mat4 _boneTransform { 1.0f }; /**< model space transform relative to the rest pose */ }; } // namespace scene diff --git a/src/engine/scene/node/scenenode.cpp b/src/engine/scene/node/scenenode.cpp index ad67c61a..68f9ed20 100644 --- a/src/engine/scene/node/scenenode.cpp +++ b/src/engine/scene/node/scenenode.cpp @@ -17,34 +17,50 @@ #include "scenenode.h" -#include - #include "glm/gtx/norm.hpp" -#include "../scenegraph.h" - using namespace std; namespace reone { namespace scene { -SceneNode::SceneNode(SceneNodeType type, SceneGraph *sceneGraph) : _type(type), _sceneGraph(sceneGraph) { +SceneNode::SceneNode(string name, SceneNodeType type, SceneGraph *sceneGraph) : + _name(move(name)), + _type(type), + _sceneGraph(sceneGraph) { } -void SceneNode::addChild(const shared_ptr &node) { - node->setParent(this); +void SceneNode::addChild(shared_ptr node) { + node->_parent = this; + node->computeAbsoluteTransforms(); _children.push_back(node); } +void SceneNode::computeAbsoluteTransforms() { + if (_parent) { + _absTransform = _parent->_absTransform * _localTransform; + } else { + _absTransform = _localTransform; + } + _absTransformInv = glm::inverse(_absTransform); + + for (auto &child : _children) { + child->computeAbsoluteTransforms(); + } + + onAbsoluteTransformChanged(); +} + void SceneNode::removeChild(SceneNode &node) { auto maybeChild = find_if( _children.begin(), _children.end(), - [&node](const shared_ptr &n) { return n.get() == &node; }); + [&node](auto &n) { return n.get() == &node; }); if (maybeChild != _children.end()) { - node.setParent(nullptr); + node._parent = nullptr; + node.computeAbsoluteTransforms(); _children.erase(maybeChild); } } @@ -62,7 +78,7 @@ void SceneNode::draw() { } glm::vec3 SceneNode::getOrigin() const { - return glm::vec3(_absoluteTransform[3]); + return glm::vec3(_absTransform[3]); } float SceneNode::getDistanceTo(const glm::vec3 &point) const { @@ -81,36 +97,13 @@ float SceneNode::getDistanceTo2(const SceneNode &other) const { return glm::distance2(getOrigin(), other.getOrigin()); } -void SceneNode::setParent(const SceneNode *parent) { - _parent = parent; - updateAbsoluteTransform(); +glm::vec3 SceneNode::getWorldCenterOfAABB() const { + return _absTransform * glm::vec4(_aabb.center(), 1.0f); } -void SceneNode::updateAbsoluteTransform() { - _absoluteTransform = _parent ? _parent->_absoluteTransform : glm::mat4(1.0f); - _absoluteTransform *= _localTransform; - _absoluteTransformInv = glm::inverse(_absoluteTransform); - - for (auto &child : _children) { - child->updateAbsoluteTransform(); - } -} - -void SceneNode::setLocalTransform(const glm::mat4 &transform) { - _localTransform = transform; - updateAbsoluteTransform(); -} - -void SceneNode::setPosition(glm::vec3 position) { - setLocalTransform(glm::translate(glm::mat4(1.0f), position)); -} - -void SceneNode::setVisible(bool visible) { - _visible = visible; -} - -void SceneNode::setTransparent(bool transparent) { - _transparent = transparent; +void SceneNode::setLocalTransform(glm::mat4 transform) { + _localTransform = move(transform); + computeAbsoluteTransforms(); } } // namespace scene diff --git a/src/engine/scene/node/scenenode.h b/src/engine/scene/node/scenenode.h index 2bdf46a5..60f77157 100644 --- a/src/engine/scene/node/scenenode.h +++ b/src/engine/scene/node/scenenode.h @@ -18,11 +18,13 @@ #pragma once #include +#include #include #include #include "glm/mat4x4.hpp" +#include "glm/vec3.hpp" #include "../../graphics/aabb.h" @@ -32,96 +34,96 @@ namespace reone { namespace scene { -constexpr float kDefaultDrawDistance = 1024.0f; - class SceneGraph; class SceneNode : boost::noncopyable { public: - void addChild(const std::shared_ptr &node); + void addChild(std::shared_ptr node); void removeChild(SceneNode &node); virtual void update(float dt); - virtual void draw(); bool isVisible() const { return _visible; } - virtual bool isTransparent() const { return _transparent; } - bool isVolumetric() const { return _volumetric; } bool isCullable() const { return _cullable; } bool isCulled() const { return _culled; } + bool isVolumetric() const { return _volumetric; } - virtual glm::vec3 getOrigin() const; + glm::vec3 getOrigin() const; /** - * @return distance from the origin of this node to the point + * @return distance from the origin of this node to the specified point */ float getDistanceTo(const glm::vec3 &point) const; /** - * @return squared distance from the origin of this node to the point + * @return squared distance from the origin of this node to the specified point */ float getDistanceTo2(const glm::vec3 &point) const; /** - * @return shortest distance from the origin of this node to the other node + * @return distance between origins of this and the specified node */ float getDistanceTo(const SceneNode &other) const; /** - * @return shortest distance (squared) from the origin of this node to the other node + * @return squared distance between origins of this and the specified node */ float getDistanceTo2(const SceneNode &other) const; + glm::vec3 getWorldCenterOfAABB() const; + + const std::string &name() const { return _name; } SceneNodeType type() const { return _type; } const SceneNode *parent() const { return _parent; } - const glm::mat4 &localTransform() const { return _localTransform; } - const glm::mat4 &absoluteTransform() const { return _absoluteTransform; } - const glm::mat4 &absoluteTransformInverse() const { return _absoluteTransformInv; } - const graphics::AABB &aabb() const { return _aabb; } const std::vector> &children() const { return _children; } - float drawDistance() const { return _drawDistance; } + const graphics::AABB &aabb() const { return _aabb; } - void setParent(const SceneNode *parent); - virtual void setLocalTransform(const glm::mat4 &transform); - void setPosition(glm::vec3 position); - virtual void setVisible(bool visible); - void setTransparent(bool transparent); - void setDrawDistance(float distance) { _drawDistance = distance; } + void setVisible(bool visible) { _visible = visible; } void setCullable(bool cullable) { _cullable = cullable; } void setCulled(bool culled) { _culled = culled; } + // Transformations + + const glm::mat4 &localTransform() const { return _localTransform; } + const glm::mat4 &absoluteTransform() const { return _absTransform; } + const glm::mat4 &absoluteTransformInverse() const { return _absTransformInv; } + + void setLocalTransform(glm::mat4 transform); + + // END Transformations + protected: + std::string _name; SceneNodeType _type; SceneGraph *_sceneGraph; graphics::AABB _aabb; - float _drawDistance { kDefaultDrawDistance }; - - bool _visible { true }; - bool _transparent { false }; - bool _volumetric { false }; /**< does this model have a bounding box, or is it a point? */ - bool _cullable { false }; /**< can this model be frustum- or distance-culled? */ - bool _culled { false }; /**< has this model been frustum- or distance-culled? */ - - // Relations - const SceneNode *_parent { nullptr }; std::vector> _children; - // END Relations - - // Transformation matrices + // Transformations glm::mat4 _localTransform { 1.0f }; - glm::mat4 _absoluteTransform { 1.0f }; - glm::mat4 _absoluteTransformInv { 1.0f }; + glm::mat4 _absTransform { 1.0f }; + glm::mat4 _absTransformInv { 1.0f }; - // END Transformation matrices + // END Transformations - SceneNode(SceneNodeType type, SceneGraph *sceneGraph); + // Flags - virtual void updateAbsoluteTransform(); + bool _visible { true }; + bool _cullable { false }; /**< can this scene node be frustum- or distance-culled? */ + bool _culled { false }; /**< has this scene node been frustum- or distance-culled? */ + bool _volumetric { false }; /**< does this scene node have a bounding box? */ + + // END Flags + + SceneNode(std::string name, SceneNodeType type, SceneGraph *sceneGraph); + + void computeAbsoluteTransforms(); + + virtual void onAbsoluteTransformChanged() { } }; } // namespace scene diff --git a/src/engine/scene/particle.cpp b/src/engine/scene/particle.cpp deleted file mode 100644 index 1cb6beea..00000000 --- a/src/engine/scene/particle.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2020-2021 The reone project contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ - -#include "particle.h" - -#include - -#include "node/emitternode.h" - -using namespace std; - -using namespace reone::graphics; - -namespace reone { - -namespace scene { - -Particle::Particle(glm::vec3 position, float velocity, EmitterSceneNode *emitter) : - _position(position), - _velocity(velocity), - _emitter(emitter) { - - if (!emitter) { - throw invalid_argument("emitter must not be null"); - } - - init(); -} - -void Particle::init() { - shared_ptr emitter(_emitter->emitter()); - - if (emitter->fps > 0) { - _animLength = (emitter->frameEnd - emitter->frameStart + 1) / static_cast(emitter->fps); - } - - _renderOrder = emitter->renderOrder; - _frame = emitter->frameStart; -} - -void Particle::update(float dt) { - shared_ptr emitter(_emitter->emitter()); - - if (emitter->lifeExpectancy != -1) { - _lifetime = glm::min(_lifetime + dt, static_cast(emitter->lifeExpectancy)); - } else if (_lifetime == _animLength) { - _lifetime = 0.0f; - } else { - _lifetime = glm::min(_lifetime + dt, _animLength); - } - - if (!isExpired()) { - _position.z += _velocity * dt; - updateAnimation(dt); - } -} - -template -static T interpolateConstraints(const ModelNode::Emitter::Constraints &constraints, float t) { - T result; - if (t < 0.5f) { - float tt = 2.0f * t; - result = (1.0f - tt) * constraints.start + tt * constraints.mid; - } else { - float tt = 2.0f * (t - 0.5f); - result = (1.0f - tt) * constraints.mid + tt * constraints.end; - } - return result; -} - -void Particle::updateAnimation(float dt) { - shared_ptr emitter(_emitter->emitter()); - - float maturity; - if (emitter->lifeExpectancy != -1) { - maturity = _lifetime / static_cast(emitter->lifeExpectancy); - } else if (_animLength > 0.0f) { - maturity = _lifetime / _animLength; - } else { - maturity = 0.0f; - } - - _frame = static_cast(glm::ceil(emitter->frameStart + maturity * (emitter->frameEnd - emitter->frameStart))); - _size = interpolateConstraints(emitter->particleSize, maturity); - _color = interpolateConstraints(emitter->color, maturity); - _alpha = interpolateConstraints(emitter->alpha, maturity); -} - -bool Particle::isExpired() const { - shared_ptr emitter(_emitter->emitter()); - return emitter->lifeExpectancy != -1 && _lifetime >= emitter->lifeExpectancy; -} - -} // namespace scene - -} // namespace reone diff --git a/src/engine/scene/scenegraph.cpp b/src/engine/scene/scenegraph.cpp index d7d77af3..dadfd3bc 100644 --- a/src/engine/scene/scenegraph.cpp +++ b/src/engine/scene/scenegraph.cpp @@ -76,12 +76,16 @@ void SceneGraph::update(float dt) { void SceneGraph::cullRoots() { for (auto &root : _roots) { - bool culled = - !root->isVisible() || - root->getDistanceTo2(*_activeCamera) > root->drawDistance() * root->drawDistance() || - (root->isCullable() && !_activeCamera->isInFrustum(*root)); + if (root->type() != SceneNodeType::Model) continue; - root->setCulled(culled); + auto modelRoot = static_pointer_cast(root); + + bool culled = + !modelRoot->isVisible() || + modelRoot->getDistanceTo2(*_activeCamera) > modelRoot->drawDistance() * modelRoot->drawDistance() || + (modelRoot->isCullable() && !_activeCamera->isInFrustum(*root)); + + modelRoot->setCulled(culled); } } @@ -89,7 +93,7 @@ void SceneGraph::updateLighting() { _closestLights.clear(); if (_lightingRefNode) { - getLightsAt(*_lightingRefNode, _closestLights, kMaxLights, [](auto &light) { return !light.isAmbientOnly(); }); + getLightsAt(*_lightingRefNode, _closestLights, kMaxLights, [](auto &light) { return !light.modelNode()->light()->ambientOnly; }); } } @@ -127,7 +131,7 @@ void SceneGraph::refreshFromSceneNode(const std::shared_ptr &node) { } break; } - case SceneNodeType::ModelNode: { + case SceneNodeType::Mesh: { // For model nodes, determine whether they should be rendered and cast shadows auto modelNode = static_pointer_cast(node); if (modelNode->shouldRender()) { @@ -168,7 +172,7 @@ void SceneGraph::refreshShadowLight() { if (_lightingRefNode) { vector lights; - getLightsAt(*_lightingRefNode, lights, 1, [](auto &light) { return light.isShadow(); }); + getLightsAt(*_lightingRefNode, lights, 1, [](auto &light) { return light.modelNode()->light()->shadow; }); if (!lights.empty()) { nextShadowLight = lights.front(); @@ -221,11 +225,11 @@ void SceneGraph::prepareParticles() { static glm::vec4 viewport(-1.0f, -1.0f, 1.0f, 1.0f); // Extract particles from all emitters, sort them by depth - vector> particlesZ; + vector> particlesZ; for (auto &emitter : _emitters) { glm::mat4 modelView(_activeCamera->view() * emitter->absoluteTransform()); for (auto &particle : emitter->particles()) { - glm::vec3 screen(glm::project(particle->position(), modelView, _activeCamera->projection(), viewport)); + glm::vec3 screen(glm::project(particle->position, modelView, _activeCamera->projection(), viewport)); if (screen.z >= 0.5f && glm::abs(screen.x) <= 1.0f && glm::abs(screen.y) <= 1.0f) { particlesZ.push_back(make_pair(particle.get(), screen.z)); } @@ -238,18 +242,18 @@ void SceneGraph::prepareParticles() { // Map (particle, Z) pairs to (emitter, particles) _particles.clear(); EmitterSceneNode *emitter = nullptr; - vector emitterParticles; + vector emitterParticles; for (auto &pair : particlesZ) { - if (pair.first->emitter() != emitter) { + if (pair.first->emitter != emitter) { flushEmitterParticles(emitter, emitterParticles); - emitter = pair.first->emitter(); + emitter = pair.first->emitter; } emitterParticles.push_back(pair.first); } flushEmitterParticles(emitter, emitterParticles); } -void SceneGraph::flushEmitterParticles(EmitterSceneNode *emitter, vector &particles) { +void SceneGraph::flushEmitterParticles(EmitterSceneNode *emitter, vector &particles) { if (emitter && !particles.empty()) { _particles.push_back(make_pair(emitter, particles)); particles.clear(); @@ -266,8 +270,8 @@ void SceneGraph::prepareGrass() { float grassDistance2 = kMaxGrassDistance * kMaxGrassDistance; for (auto &node : _grass) { - vector clusters; - vector> clustersZ; + vector clusters; + vector> clustersZ; for (auto &cluster : node->clusters()) { float distance2 = glm::distance2(cameraPos, cluster.position); if (distance2 <= grassDistance2) { @@ -303,25 +307,11 @@ void SceneGraph::draw(bool shadowPass) { return; } - // Render opaque roots - for (auto &root : _roots) { - if (!root->isTransparent()) { - root->draw(); - } - } - // Render opaque meshes for (auto &mesh : _opaqueMeshes) { mesh->drawSingle(false); } - // Render transparent roots - for (auto &root : _roots) { - if (root->isTransparent()) { - root->draw(); - } - } - // Render transparent meshes for (auto &mesh : _transparentMeshes) { mesh->drawSingle(false); @@ -340,10 +330,11 @@ void SceneGraph::draw(bool shadowPass) { // Render lens flares for (auto &light : _lights) { // Ignore lights that are too far away or outside of camera frustum - if (_activeCamera->getDistanceTo2(*light) > light->flareRadius() * light->flareRadius() || + float flareRadius = light->modelNode()->light()->flareRadius; + if (_activeCamera->getDistanceTo2(*light) > flareRadius * flareRadius || !_activeCamera->isInFrustum(light->absoluteTransform()[3])) continue; - for (auto &flare : light->flares()) { + for (auto &flare : light->modelNode()->light()->flares) { light->drawLensFlares(flare); } } @@ -372,8 +363,11 @@ void SceneGraph::getLightsAt( // Sort lights by priority and radius sort(lights.begin(), lights.end(), [&distances](LightSceneNode *left, LightSceneNode *right) { - if (left->priority() < right->priority()) return true; - if (left->priority() > right->priority()) return false; + int leftPriority = left->modelNode()->light()->priority; + int rightPriority = right->modelNode()->light()->priority; + + if (leftPriority < rightPriority) return true; + if (leftPriority > rightPriority) return false; float leftDistance = distances.find(left)->second; float rightDistance = distances.find(right)->second; diff --git a/src/engine/scene/scenegraph.h b/src/engine/scene/scenegraph.h index 385b92e8..63d85768 100644 --- a/src/engine/scene/scenegraph.h +++ b/src/engine/scene/scenegraph.h @@ -29,20 +29,16 @@ #include "../graphics/shader/shaders.h" #include "../graphics/types.h" -#include "grasscluster.h" +#include "node/cameranode.h" +#include "node/emitternode.h" +#include "node/grassnode.h" +#include "node/lightnode.h" +#include "node/meshnode.h" namespace reone { namespace scene { -class CameraSceneNode; -class EmitterSceneNode; -class GrassSceneNode; -class LightSceneNode; -class MeshSceneNode; -class Particle; -class SceneNode; - /** * Responsible for managing drawable objects and their relations. * @@ -125,8 +121,8 @@ private: std::vector _lights; std::vector _emitters; std::vector _grass; - std::vector>> _particles; - std::vector>> _grassClusters; + std::vector>> _particles; + std::vector>> _grassClusters; uint32_t _textureId { 0 }; bool _updateRoots { true }; @@ -164,7 +160,7 @@ private: void prepareParticles(); void prepareGrass(); - inline void flushEmitterParticles(EmitterSceneNode *emitter, std::vector &particles); + inline void flushEmitterParticles(EmitterSceneNode *emitter, std::vector &particles); }; } // namespace scene diff --git a/src/engine/scene/types.h b/src/engine/scene/types.h index 76925480..9f3b106a 100644 --- a/src/engine/scene/types.h +++ b/src/engine/scene/types.h @@ -22,10 +22,10 @@ namespace reone { namespace scene { enum class SceneNodeType { - Mesh, - Model, - ModelNode, + Dummy, Camera, + Model, + Mesh, Light, Emitter, Grass @@ -39,15 +39,14 @@ enum class ModelUsage { Door, Equipment, Projectile, - Other + Camera }; struct AnimationFlags { static constexpr int loop = 1; static constexpr int blend = 2; /**< blend previous animation into the next one */ static constexpr int overlay = 4; /**< overlay next animation on top of the previous one */ - static constexpr int propagateHead = 8; /**< propagate animation to the head model, if any */ - static constexpr int syncLipAnim = 0x10; /**< animation must be synchronized with the lip animation */ + static constexpr int propagate = 8; /**< propagate animation to attached models */ static constexpr int loopOverlay = loop | overlay; static constexpr int loopBlend = loop | blend;