diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 999324c51c..b0bd95eb95 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -89,12 +89,12 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode + cellwater terraintexturemode actor ) opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase - cellarrow cellmarker cellborder pathgrid actor + cellarrow cellmarker cellborder pathgrid ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 249908bb09..9c397879e9 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -1,7 +1,6 @@ #include "actoradapter.hpp" -#include - +#include #include #include #include @@ -45,17 +44,56 @@ namespace CSMWorld void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO + // Setup + const int TypeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + + // Handle each record + for (int row = rowStart; row <= rowEnd; ++row) + { + int type = mReferenceables.getData(row, TypeColumn).toInt(); + if (type == CSMWorld::UniversalId::Type_Creature || type == CSMWorld::UniversalId::Type_Npc) + { + // Update the cached npc or creature + std::string refId = mReferenceables.getId(row); + if (mActorPartMaps.find(refId) != mActorPartMaps.end()) + updateActor(refId); + } + else if (type == CSMWorld::UniversalId::Type_Armor) + { + // TODO update everything? + // store all items referenced when creating map and check against that here + } + else if (type == CSMWorld::UniversalId::Type_Clothing) + { + // TODO update everything? + } + } } void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + for (int row = rowStart; row <= rowEnd; ++row) + { + std::string raceId = mRaces.getId(row); + updateNpcsWithRace(raceId); + } } void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { // TODO + Log(Debug::Info) << "Body Part Changed (" << topLeft.row() << ", " << topLeft.column() << ") (" << botRight.row() << ", " << botRight.column() << ")"; + } + + QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const + { + while (index.parent().isValid()) + index = index.parent(); + return index; } ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale) @@ -174,10 +212,6 @@ namespace CSMWorld npcMap.emplace(static_cast(part.mPart), bodyPartId); } } - else if (recordType == CSMWorld::UniversalId::Type_Weapon) - { - // TODO - } } } } @@ -233,10 +267,24 @@ namespace CSMWorld } mActorPartMaps[refId] = npcMap; + emit actorChanged(refId); } void ActorAdapter::updateCreature(const std::string& refId) { - // TODO + emit actorChanged(refId); + } + + void ActorAdapter::updateNpcsWithRace(const std::string& raceId) + { + for (auto it : mActorPartMaps) + { + auto& refId = it.first; + auto& npc = dynamic_cast&>(mReferenceables.getRecord(refId)).get(); + if (npc.mRace == raceId) + { + updateNpc(refId); + } + } } } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 09bd2682ba..ad308fc022 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -60,6 +60,11 @@ namespace CSMWorld private: + ActorAdapter(const ActorAdapter&) = delete; + ActorAdapter& operator=(const ActorAdapter&) = delete; + + QModelIndex getHighestIndex(QModelIndex) const; + RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale); void updateRaceParts(const std::string& raceId); @@ -67,6 +72,8 @@ namespace CSMWorld void updateNpc(const std::string& refId); void updateCreature(const std::string& refId); + void updateNpcsWithRace(const std::string& raceId); + RefIdCollection& mReferenceables; IdCollection& mRaces; IdCollection& mBodyParts; diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index a61d31de67..6d663ad5e6 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -20,6 +20,7 @@ namespace CSVRender Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) : mId(id) + , mInitialized(false) , mType(type) , mData(data) , mBaseNode(new osg::Group()) @@ -45,7 +46,22 @@ namespace CSVRender } catch (std::exception& e) { - Log(Debug::Error) << "Exception in Actor::update(): " << e.what(); + Log(Debug::Info) << "Exception in Actor::update(): " << e.what(); + } + + if (!mInitialized) + { + mInitialized = true; + connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), this, SLOT(handleActorChanged(const std::string&))); + } + } + + void Actor::handleActorChanged(const std::string& refId) + { + if (mId == refId) + { + Log(Debug::Info) << "Actor::actorChanged " << mId; + update(); } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 4f809c1720..42a6019ed7 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -5,6 +5,8 @@ #include +#include + #include #include @@ -26,8 +28,9 @@ namespace SceneUtil namespace CSVRender { /// Handles loading an npc or creature - class Actor + class Actor : public QObject { + Q_OBJECT public: /// Creates an actor. /// \param id The referenceable id @@ -41,6 +44,9 @@ namespace CSVRender /// (Re)creates the npc or creature renderable void update(); + private slots: + void handleActorChanged(const std::string& refId); + private: void updateCreature(); void updateNpc(); @@ -54,6 +60,7 @@ namespace CSVRender static const std::string MeshPrefix; std::string mId; + bool mInitialized; int mType; CSMWorld::Data& mData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index dbfd595b6a..63f1bc8a82 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -134,9 +134,10 @@ void CSVRender::Object::update() { if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - Actor actor(mReferenceableId, recordType, mData); - actor.update(); - mBaseNode->addChild(actor.getBaseNode()); + if (!mActor) + mActor.reset(new Actor(mReferenceableId, recordType, mData)); + mActor->update(); + mBaseNode->addChild(mActor->getBaseNode()); } else { diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 3e54093d3e..10a46fc103 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -1,6 +1,7 @@ #ifndef OPENCS_VIEW_OBJECT_H #define OPENCS_VIEW_OBJECT_H +#include #include #include @@ -41,6 +42,7 @@ namespace CSMWorld namespace CSVRender { + class Actor; class Object; // An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query @@ -98,6 +100,7 @@ namespace CSVRender osg::ref_ptr mMarker[3]; int mSubMode; float mMarkerTransparency; + std::unique_ptr mActor; /// Not implemented Object (const Object&);