diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index fe3ac1c764..de15ba8746 100644 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -76,7 +76,7 @@ public: CGameViewImpl(CGame* game) : Game(game), ColladaManager(), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager), - ObjectManager(MeshManager, SkeletonAnimManager), + ObjectManager(MeshManager, SkeletonAnimManager, *game->GetSimulation2()), ViewCamera(), CullCamera(), LockCullCamera(false), diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 85df102854..162e7b933a 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -103,10 +103,7 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_, // delete all remaining non-entity units if (pUnitMan) - { pUnitMan->DeleteAll(); - pUnitMan->SetNextID(0); - } // unpack the data if (!only_xml) @@ -968,26 +965,6 @@ int CXMLReader::ReadOldEntities(XMBElement parent, double end_time) { XMBElementList entities = parent.GetChildNodes(); - // If this is the first time in ReadOldEntities, find the next free ID number - // in case we need to allocate new ones in the future - if (entity_idx == 0) - { - int maxUnitID = -1; - - XERO_ITER_EL(parent, entity) - { - debug_assert(entity.GetNodeName() == el_entity); - - XMBAttributeList attrs = entity.GetAttributes(); - utf16string uid = attrs.GetNamedItem(at_uid); - int unitId = uid.empty() ? -1 : CStr(uid).ToInt(); - maxUnitID = std::max(maxUnitID, unitId); - } - - if (m_MapReader.pUnitMan) - m_MapReader.pUnitMan->SetNextID(maxUnitID + 1); - } - while (entity_idx < entities.Count) { // all new state at this scope and below doesn't need to be diff --git a/source/graphics/Material.cpp b/source/graphics/Material.cpp index 7b07592b90..7bd560843d 100644 --- a/source/graphics/Material.cpp +++ b/source/graphics/Material.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -34,16 +34,6 @@ static SMaterialColor IdentityEmissive(0.0f, 0.0f, 0.0f, 1.0f); static SMaterialColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f); -bool SMaterialColor::operator==(const SMaterialColor& color) -{ - return ( - r == color.r && - g == color.g && - b == color.b && - a == color.a - ); -} - CMaterial::CMaterial() : m_Diffuse(IdentityDiffuse), m_Ambient(IdentityAmbient), @@ -54,30 +44,6 @@ CMaterial::CMaterial() m_PlayerID(PLAYER_ID_NONE), m_TextureColor(BrokenColor) { - ComputeHash(); -} - -CMaterial::CMaterial(const CMaterial& material) -{ - (*this) = material; -} - -CMaterial::~CMaterial() -{ -} - -void CMaterial::operator=(const CMaterial& material) -{ - m_Diffuse = material.m_Diffuse; - m_Ambient = material.m_Ambient; - m_Specular = material.m_Specular; - m_Emissive = material.m_Emissive; - - m_SpecularPower = material.m_SpecularPower; - m_Alpha = material.m_Alpha; - m_PlayerID = material.m_PlayerID; - m_TextureColor = material.m_TextureColor; - ComputeHash(); } bool CMaterial::operator==(const CMaterial& material) @@ -163,70 +129,47 @@ void CMaterial::SetPlayerColor(const CColor& colour) m_TextureColor = SMaterialColor(colour.r, colour.g, colour.b, colour.a); } - void CMaterial::SetTexture(const CStr& texture) { m_Texture = texture; - ComputeHash(); } void CMaterial::SetVertexProgram(const CStr& prog) { m_VertexProgram = prog; - ComputeHash(); } void CMaterial::SetFragmentProgram(const CStr& prog) { m_FragmentProgram = prog; - ComputeHash(); } void CMaterial::SetDiffuse(const SMaterialColor& color) { m_Diffuse = color; - ComputeHash(); } void CMaterial::SetAmbient(const SMaterialColor& color) { m_Ambient = color; - ComputeHash(); } void CMaterial::SetSpecular(const SMaterialColor& color) { m_Specular = color; - ComputeHash(); } void CMaterial::SetEmissive(const SMaterialColor& color) { m_Emissive = color; - ComputeHash(); } void CMaterial::SetSpecularPower(float power) { m_SpecularPower = power; - ComputeHash(); } void CMaterial::SetUsesAlpha(bool flag) { m_Alpha = flag; - ComputeHash(); -} - -void CMaterial::ComputeHash() -{ - m_Hash = - m_Diffuse.Sum() + - m_Ambient.Sum() + - m_Specular.Sum() + - m_Emissive.Sum() + - m_SpecularPower + - (float)m_Texture.GetHashCode() + - (float)m_VertexProgram.GetHashCode() + - (float)m_FragmentProgram.GetHashCode(); } diff --git a/source/graphics/Material.h b/source/graphics/Material.h index 0dc424c8e6..679edaa44f 100644 --- a/source/graphics/Material.h +++ b/source/graphics/Material.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -38,26 +38,14 @@ public: b = _b; a = _a; } - SMaterialColor(const SMaterialColor& color) + bool operator==(const SMaterialColor& color) { - r = color.r; - g = color.g; - b = color.b; - a = color.a; - } - - void operator=(const SMaterialColor& color) - { - r = color.r; - g = color.g; - b = color.b; - a = color.a; - } - bool operator==(const SMaterialColor& color); - - float Sum() - { - return (r + g + b + a); + return ( + r == color.r && + g == color.g && + b == color.b && + a == color.a + ); } }; @@ -65,12 +53,9 @@ class CMaterial { public: CMaterial(); - CMaterial(const CMaterial& material); - virtual ~CMaterial(); void Bind(); void Unbind(); - float GetHash() { return m_Hash; } const CStr& GetTexture() { return m_Texture; } const CStr& GetVertexProgram() { return m_VertexProgram; } @@ -105,13 +90,8 @@ public: void SetSpecularPower(float power); void SetUsesAlpha(bool flag); - void operator=(const CMaterial& material); bool operator==(const CMaterial& material); protected: - void ComputeHash(); - - float m_Hash; - // Various reflective color properties SMaterialColor m_Diffuse; SMaterialColor m_Ambient; diff --git a/source/graphics/MeshManager.cpp b/source/graphics/MeshManager.cpp index 67b3a08ddc..d4bee2ae11 100644 --- a/source/graphics/MeshManager.cpp +++ b/source/graphics/MeshManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,8 +25,6 @@ #include "ps/FileIo.h" // to get access to its CError #include "ps/Profile.h" -#define LOG_CATEGORY L"mesh" - // TODO: should this cache models while they're not actively in the game? // (Currently they'll probably be deleted when the reference count drops to 0, // even if it's quite possible that they'll get reloaded very soon.) @@ -49,13 +47,13 @@ CModelDefPtr CMeshManager::GetMesh(const VfsPath& pathname) if (iter != m_MeshMap.end() && !iter->second.expired()) return CModelDefPtr(iter->second); - PROFILE( "load mesh" ); + PROFILE("load mesh"); VfsPath pmdFilename = m_ColladaManager.GetLoadableFilename(name, CColladaManager::PMD); if (pmdFilename.empty()) { - LOG(CLogger::Error, LOG_CATEGORY, L"Could not load mesh '%ls'", pathname.string().c_str()); + LOGERROR(L"Could not load mesh '%ls'", pathname.string().c_str()); return CModelDefPtr(); } @@ -67,7 +65,7 @@ CModelDefPtr CMeshManager::GetMesh(const VfsPath& pathname) } catch (PSERROR_File&) { - LOG(CLogger::Error, LOG_CATEGORY, L"Could not load mesh '%ls'", pathname.string().c_str()); + LOGERROR(L"Could not load mesh '%ls'", pathname.string().c_str()); return CModelDefPtr(); } } diff --git a/source/graphics/Model.cpp b/source/graphics/Model.cpp index f5c8fa800c..bfb1c7332e 100644 --- a/source/graphics/Model.cpp +++ b/source/graphics/Model.cpp @@ -42,7 +42,7 @@ CModel::CModel(CSkeletonAnimManager& skeletonAnimManager) : m_Parent(NULL), m_Flags(0), m_Anim(NULL), m_AnimTime(0), m_BoneMatrices(NULL), m_InverseBindBoneMatrices(NULL), - m_PositionValid(false), m_ShadingColor(1,1,1,1), + m_PositionValid(false), m_ShadingColor(1,1,1,1), m_PlayerID((size_t)-1), m_SkeletonAnimManager(skeletonAnimManager) { } @@ -238,6 +238,8 @@ void CModel::Update(float time) { if (m_Anim && m_Anim->m_AnimDef && m_BoneMatrices) { + time *= m_Anim->m_Speed; + float oldAnimTime = m_AnimTime; // update animation time, but don't calculate bone matrices - do that (lazily) when @@ -288,7 +290,10 @@ bool CModel::NeedsNewAnim(float time) const if (m_Anim && m_Anim->m_AnimDef && m_BoneMatrices) { + time *= m_Anim->m_Speed; + float duration = m_Anim->m_AnimDef->GetDuration(); + if (m_AnimTime + time > duration) return true; } @@ -300,6 +305,8 @@ void CModel::CheckActionTriggers(float time, bool& action, bool& action2) const { if (m_Anim && m_Anim->m_AnimDef && m_BoneMatrices) { + time *= m_Anim->m_Speed; + if (m_AnimTime <= m_Anim->m_ActionPos && m_AnimTime + time > m_Anim->m_ActionPos) action = true; @@ -498,19 +505,24 @@ void CModel::SetTransform(const CMatrix3D& transform) void CModel::SetMaterial(const CMaterial &material) { m_Material = material; - if(m_Material.GetTexture().Trim(PS_TRIM_BOTH).length() > 0) - { - // [TODO: uh, shouldn't this be doing something?] - } } void CModel::SetPlayerID(size_t id) { - m_Material.SetPlayerColor(id); + m_PlayerID = id; + + if (id != (size_t)-1) + m_Material.SetPlayerColor(id); + for (std::vector::iterator it = m_Props.begin(); it != m_Props.end(); ++it) it->m_Model->SetPlayerID(id); } +size_t CModel::GetPlayerID() +{ + return m_PlayerID; +} + void CModel::SetPlayerColor(const CColor& colour) { m_Material.SetPlayerColor(colour); diff --git a/source/graphics/Model.h b/source/graphics/Model.h index e8c928d3f8..76d2cccda1 100644 --- a/source/graphics/Model.h +++ b/source/graphics/Model.h @@ -84,9 +84,10 @@ public: void SetTexture(const CTexture& tex) { m_Texture=tex; } // set the model's material void SetMaterial(const CMaterial &material); - // set the model's player ID, recursively through props. CUnit::SetPlayerID - // should normally be used instead. + // set the model's player ID, recursively through props void SetPlayerID(size_t id); + // get the model's player ID, recursively through props; initial default is (size_t)-1 + size_t GetPlayerID(); // set the model's player colour void SetPlayerColor(const CColor& colour); // set the models mod color @@ -227,6 +228,8 @@ private: // modulating color CColor m_ShadingColor; + size_t m_PlayerID; + // manager object which can load animations for us CSkeletonAnimManager& m_SkeletonAnimManager; }; diff --git a/source/graphics/ObjectBase.cpp b/source/graphics/ObjectBase.cpp index a5464363de..b5440fa096 100644 --- a/source/graphics/ObjectBase.cpp +++ b/source/graphics/ObjectBase.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -39,19 +39,12 @@ CObjectBase::CObjectBase(CObjectManager& objectManager) m_Properties.m_FloatOnWater = false; } -bool CObjectBase::Load(const std::wstring& filename) +bool CObjectBase::Load(const VfsPath& pathname) { - m_VariantGroups.clear(); - - VfsPath pathname(VfsPath(L"art/actors/")/filename); - CXeromyces XeroFile; if (XeroFile.Load(pathname) != PSRETURN_OK) return false; - m_Name = filename; - m_ShortName = fs::basename(pathname); - // Define all the elements used in the XML file #define EL(x) int el_##x = XeroFile.GetElementID(#x) #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) @@ -88,6 +81,12 @@ bool CObjectBase::Load(const std::wstring& filename) } + m_VariantGroups.clear(); + + m_Pathname = pathname; + m_ShortName = fs::basename(pathname); + + // Set up the vector> m_Variants to contain the right number // of elements, to avoid wasteful copying/reallocation later. { @@ -206,7 +205,7 @@ bool CObjectBase::Load(const std::wstring& filename) if (currentGroup->size() == 0) { - LOG(CLogger::Error, LOG_CATEGORY, L"Actor group has zero variants ('%ls')", filename.c_str()); + LOG(CLogger::Error, LOG_CATEGORY, L"Actor group has zero variants ('%ls')", pathname.string().c_str()); } ++currentGroup; @@ -228,6 +227,11 @@ bool CObjectBase::Load(const std::wstring& filename) return true; } +bool CObjectBase::Reload() +{ + return Load(m_Pathname); +} + std::vector CObjectBase::CalculateVariationKey(const std::vector >& selections) { // (TODO: see CObjectManager::FindObjectVariation for an opportunity to diff --git a/source/graphics/ObjectBase.h b/source/graphics/ObjectBase.h index d6c5df19af..9373e31ba3 100644 --- a/source/graphics/ObjectBase.h +++ b/source/graphics/ObjectBase.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -74,8 +74,8 @@ public: VfsPath texture; VfsPath model; CStr color; - std::multimap props; - std::multimap anims; + std::multimap props; + std::multimap anims; }; CObjectBase(CObjectManager& objectManager); @@ -97,10 +97,20 @@ public: // variant names. std::vector > GetVariantGroups() const; - bool Load(const std::wstring& filename); + /** + * Initialise this object by loading from the given file. + * Returns false on error. + */ + bool Load(const VfsPath& pathname); - // object name - CStrW m_Name; + /** + * Reload this object from the file that it was previously loaded from. + * Returns false on error. + */ + bool Reload(); + + // filename that this was loaded from + VfsPath m_Pathname; // short human-readable name CStrW m_ShortName; diff --git a/source/graphics/ObjectEntry.cpp b/source/graphics/ObjectEntry.cpp index 5076944b8c..f33362c018 100644 --- a/source/graphics/ObjectEntry.cpp +++ b/source/graphics/ObjectEntry.cpp @@ -175,7 +175,7 @@ bool CObjectEntry::BuildVariation(const std::vector >& selections m_AmmunitionModel = oe->m_Model; m_AmmunitionPoint = modeldef->FindPropPoint(ppn); if (! m_AmmunitionPoint) - LOG(CLogger::Error, LOG_CATEGORY, L"Failed to find matching prop point called \"%hs\" in model \"%ls\" for actor \"%ls\"", ppn.c_str(), m_ModelName.string().c_str(), m_Base->m_Name.c_str()); + LOG(CLogger::Error, LOG_CATEGORY, L"Failed to find matching prop point called \"%hs\" in model \"%ls\" for actor \"%ls\"", ppn.c_str(), m_ModelName.string().c_str(), m_Base->m_ShortName.c_str()); } else { @@ -187,7 +187,7 @@ bool CObjectEntry::BuildVariation(const std::vector >& selections propmodel->SetAnimation(oe->GetRandomAnimation("idle")); } else - LOG(CLogger::Error, LOG_CATEGORY, L"Failed to find matching prop point called \"%hs\" in model \"%ls\" for actor \"%ls\"", prop.m_PropPointName.c_str(), m_ModelName.string().c_str(), m_Base->m_Name.c_str()); + LOG(CLogger::Error, LOG_CATEGORY, L"Failed to find matching prop point called \"%hs\" in model \"%ls\" for actor \"%ls\"", prop.m_PropPointName.c_str(), m_ModelName.string().c_str(), m_Base->m_ShortName.c_str()); } } diff --git a/source/graphics/ObjectManager.cpp b/source/graphics/ObjectManager.cpp index 8c82fb74d4..27d805d124 100644 --- a/source/graphics/ObjectManager.cpp +++ b/source/graphics/ObjectManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,10 +22,11 @@ #include "graphics/ObjectBase.h" #include "graphics/ObjectEntry.h" #include "ps/CLogger.h" +#include "ps/Game.h" #include "ps/Profile.h" #include "ps/Filesystem.h" - -#define LOG_CATEGORY L"graphics" +#include "simulation2/Simulation2.h" +#include "simulation2/components/ICmpVisual.h" template static void delete_pair_2nd(std::pair v) @@ -51,14 +52,22 @@ bool CObjectManager::ObjectKey::operator< (const CObjectManager::ObjectKey& a) c return ActorVariation < a.ActorVariation; } -CObjectManager::CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager) -: m_MeshManager(meshManager), m_SkeletonAnimManager(skeletonAnimManager) +static LibError ReloadChangedFileCB(void* param, const VfsPath& path) { + return static_cast(param)->ReloadChangedFile(path); +} + +CObjectManager::CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager, CSimulation2& simulation) +: m_MeshManager(meshManager), m_SkeletonAnimManager(skeletonAnimManager), m_Simulation(simulation) +{ + RegisterFileReloadFunc(ReloadChangedFileCB, this); } CObjectManager::~CObjectManager() { UnloadObjects(); + + UnregisterFileReloadFunc(ReloadChangedFileCB, this); } @@ -76,7 +85,9 @@ CObjectBase* CObjectManager::FindObjectBase(const wchar_t* objectname) CObjectBase* obj = new CObjectBase(*this); - if (obj->Load(objectname)) + VfsPath pathname(VfsPath(L"art/actors/")/objectname); + + if (obj->Load(pathname)) { m_ObjectBases[objectname] = obj; return obj; @@ -84,7 +95,7 @@ CObjectBase* CObjectManager::FindObjectBase(const wchar_t* objectname) else delete obj; - LOG(CLogger::Error, LOG_CATEGORY, L"CObjectManager::FindObjectBase(): Cannot find object '%ls'", objectname); + LOGERROR(L"CObjectManager::FindObjectBase(): Cannot find object '%ls'", objectname); return 0; } @@ -107,12 +118,12 @@ CObjectEntry* CObjectManager::FindObjectVariation(const wchar_t* objname, const CObjectEntry* CObjectManager::FindObjectVariation(CObjectBase* base, const std::vector >& selections) { - PROFILE( "object variation loading" ); + PROFILE("object variation loading"); // Look to see whether this particular variation has already been loaded std::vector choices = base->CalculateVariationKey(selections); - ObjectKey key (base->m_Name, choices); + ObjectKey key (base->m_Pathname.string(), choices); std::map::iterator it = m_Objects.find(key); if (it != m_Objects.end()) @@ -165,22 +176,23 @@ void CObjectManager::UnloadObjects() m_ObjectBases.clear(); } - - -static LibError GetObjectName_ThunkCb(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), uintptr_t cbData) +LibError CObjectManager::ReloadChangedFile(const VfsPath& path) { - std::vector* names = (std::vector*)cbData; - CStr name(pathname.string()); - names->push_back(name.AfterFirst("actors/")); - return INFO::CB_CONTINUE; -} + for (std::map::iterator it = m_ObjectBases.begin(); it != m_ObjectBases.end(); ++it) + { + if (it->second->m_Pathname == path) + { + it->second->Reload(); -void CObjectManager::GetAllObjectNames(std::vector& names) -{ - (void)fs_util::ForEachFile(g_VFS, L"art/actors/", GetObjectName_ThunkCb, (uintptr_t)&names, L"*.xml", fs_util::DIR_RECURSIVE); -} + // Slightly ugly hack: The graphics system doesn't preserve enough information to regenerate the + // object with all correct variations, and we don't want to waste space storing it just for the + // rare occurrence of hotloading, so we'll tell the component (which does preserve the information) + // to do the reloading itself + const std::map& cmps = m_Simulation.GetEntitiesWithInterface(IID_Visual); + for (std::map::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit) + static_cast(eit->second)->Hotload(it->first); + } + } -void CObjectManager::GetPropObjectNames(std::vector& names) -{ - (void)fs_util::ForEachFile(g_VFS, L"art/actors/props/", GetObjectName_ThunkCb, (uintptr_t)&names, L"*.xml", fs_util::DIR_RECURSIVE); + return INFO::OK; } diff --git a/source/graphics/ObjectManager.h b/source/graphics/ObjectManager.h index 9c03aade11..0391b061e4 100644 --- a/source/graphics/ObjectManager.h +++ b/source/graphics/ObjectManager.h @@ -21,7 +21,9 @@ #include #include #include + #include "ps/CStr.h" +#include "lib/file/vfs/vfs_path.h" class CEntityTemplate; class CMatrix3D; @@ -29,6 +31,7 @@ class CMeshManager; class CObjectBase; class CObjectEntry; class CSkeletonAnimManager; +class CSimulation2; /////////////////////////////////////////////////////////////////////////////////////////// // CObjectManager: manager class for all possible actor types @@ -36,21 +39,23 @@ class CObjectManager { NONCOPYABLE(CObjectManager); public: + // Unique identifier of an actor variation struct ObjectKey { - ObjectKey(const CStr& name, const std::vector& var) + ObjectKey(const CStrW& name, const std::vector& var) : ActorName(name), ActorVariation(var) {} - CStr ActorName; - std::vector ActorVariation; - bool operator< (const CObjectManager::ObjectKey& a) const; + + private: + CStrW ActorName; + std::vector ActorVariation; }; public: // constructor, destructor - CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager); + CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager& skeletonAnimManager, CSimulation2& simulation); ~CObjectManager(); // Provide access to the manager classes for meshes and animations - they're @@ -69,13 +74,16 @@ public: CObjectEntry* FindObjectVariation(const wchar_t* objname, const std::vector >& selections); CObjectEntry* FindObjectVariation(CObjectBase* base, const std::vector >& selections); - // Get all names, quite slowly. (Intended only for Atlas.) - static void GetAllObjectNames(std::vector& names); - static void GetPropObjectNames(std::vector& names); + /** + * Reload any scripts that were loaded from the given filename. + * (This is used to implement hotloading.) + */ + LibError ReloadChangedFile(const VfsPath& path); private: CMeshManager& m_MeshManager; CSkeletonAnimManager& m_SkeletonAnimManager; + CSimulation2& m_Simulation; std::map m_Objects; std::map m_ObjectBases; diff --git a/source/graphics/Unit.cpp b/source/graphics/Unit.cpp index 5fdd9c5496..3ab78e7250 100644 --- a/source/graphics/Unit.cpp +++ b/source/graphics/Unit.cpp @@ -32,7 +32,7 @@ CUnit::CUnit(CObjectEntry* object, CEntity* entity, CObjectManager& objectManager, const std::set& actorSelections) : m_Object(object), m_Model(object->m_Model->Clone()), - m_ID(invalidUnitId), m_ActorSelections(actorSelections), m_PlayerID(invalidPlayerId), + m_ID(invalidUnitId), m_ActorSelections(actorSelections), m_ObjectManager(objectManager) { m_Animation = new CUnitAnimation(*this); @@ -174,13 +174,6 @@ void CUnit::UpdateModel(float frameTime) m_Animation->Update(frameTime*1000.0f); } - -void CUnit::SetPlayerID(size_t id) -{ - m_PlayerID = id; - m_Model->SetPlayerID(m_PlayerID); -} - void CUnit::SetEntitySelection(const CStr& selection) { CStr selection_lc = selection.LowerCase(); @@ -218,8 +211,7 @@ void CUnit::ReloadObject() // Copy the old instance-specific settings from the old model to the new instance newModel->SetTransform(m_Model->GetTransform()); - if (m_PlayerID != invalidPlayerId) - newModel->SetPlayerID(m_PlayerID); + newModel->SetPlayerID(m_Model->GetPlayerID()); newModel->CopyAnimationFrom(m_Model); delete m_Model; diff --git a/source/graphics/Unit.h b/source/graphics/Unit.h index ac5c87445d..ab8c3deb9b 100644 --- a/source/graphics/Unit.h +++ b/source/graphics/Unit.h @@ -90,12 +90,6 @@ public: // matching 'name'. bool IsPlayingAnimation(const CStr& name); - // Set player ID of this unit (and the attached entity and actor) - void SetPlayerID(size_t id); - - // Get player ID of this unit - size_t GetPlayerID() { return m_PlayerID; } - // Most units have a hopefully-unique ID number, so they can be referred to // persistently despite saving/loading maps. Default for new units is -1; should // usually be set to CUnitManager::GetNewID() after creation. @@ -111,8 +105,6 @@ private: CObjectEntry* m_Object; // object model representation; never NULL CModel* m_Model; - // player id of this unit (only read for graphical effects), or ~0 if unspecified - size_t m_PlayerID; CUnitAnimation* m_Animation; diff --git a/source/graphics/UnitAnimation.cpp b/source/graphics/UnitAnimation.cpp index 66776239d3..aa0f6f4573 100644 --- a/source/graphics/UnitAnimation.cpp +++ b/source/graphics/UnitAnimation.cpp @@ -75,6 +75,10 @@ void CUnitAnimation::SetAnimationSync(float actionTime, float repeatTime) // Set the speed so it loops once in repeatTime float speed = duration / repeatTime; + // Compensate for the animation's scale factor + if (model.m_Anim->m_Speed) + speed /= model.m_Anim->m_Speed; + // Need to offset so that start+actionTime*speed = ActionPos float start = model.m_Anim->m_ActionPos - actionTime*speed; // Wrap it so that it's within the animation diff --git a/source/graphics/UnitManager.cpp b/source/graphics/UnitManager.cpp index 6ca5e8ab51..3de155fd0f 100644 --- a/source/graphics/UnitManager.cpp +++ b/source/graphics/UnitManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -36,7 +36,6 @@ /////////////////////////////////////////////////////////////////////////////// // CUnitManager constructor CUnitManager::CUnitManager() -: m_NextID(0) { } @@ -135,18 +134,3 @@ CUnit* CUnitManager::CreateUnit(const CStrW& actorName, CEntity* entity, const s AddUnit(unit); return unit; } - -/////////////////////////////////////////////////////////////////////////////// -// FindByID -// TODO: this is hopelessly inefficient -CUnit* CUnitManager::FindByID(size_t id) const -{ - if (id == invalidUnitId) - return NULL; - - for (size_t i = 0; i < m_Units.size(); ++i) - if (m_Units[i]->GetID() == id) - return m_Units[i]; - - return NULL; -} diff --git a/source/graphics/UnitManager.h b/source/graphics/UnitManager.h index 98e65ba9f1..b0e4f15662 100644 --- a/source/graphics/UnitManager.h +++ b/source/graphics/UnitManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -60,19 +60,11 @@ public: // return the closest unit, or null if everything missed CUnit* PickUnit(const CVector3D& origin, const CVector3D& dir, bool entitiesOnly) const; - CUnit* FindByID(size_t id) const; - - size_t GetNewID() { return m_NextID++; } - - void SetNextID(size_t n) { m_NextID = n; } - void SetObjectManager(CObjectManager& objectManager) { m_ObjectManager = &objectManager; } private: // list of all known units std::vector m_Units; - // next ID number to be assigned to a unit created in the editor - size_t m_NextID; // graphical object manager; may be NULL if not set up CObjectManager* m_ObjectManager; }; diff --git a/source/ps/Filesystem.cpp b/source/ps/Filesystem.cpp index e24003808f..ac62115465 100644 --- a/source/ps/Filesystem.cpp +++ b/source/ps/Filesystem.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -20,22 +20,29 @@ #include "gui/GUIManager.h" #include "ps/CLogger.h" -#include "ps/Game.h" -#include "simulation2/Simulation2.h" #include "lib/res/h_mgr.h" // h_reload #include "lib/sysdep/dir_watch.h" -#define LOG_CATEGORY L"file" - PIVFS g_VFS; +static std::vector > g_ReloadFuncs; + bool FileExists(const VfsPath& pathname) { return g_VFS->GetFileInfo(pathname, 0) == INFO::OK; } +void RegisterFileReloadFunc(FileReloadFunc func, void* obj) +{ + g_ReloadFuncs.push_back(std::make_pair(func, obj)); +} + +void UnregisterFileReloadFunc(FileReloadFunc func, void* obj) +{ + g_ReloadFuncs.erase(std::remove(g_ReloadFuncs.begin(), g_ReloadFuncs.end(), std::make_pair(func, obj))); +} // try to skip unnecessary work by ignoring uninteresting notifications. static bool CanIgnore(const DirWatchNotification& notification) @@ -69,9 +76,14 @@ LibError ReloadChangedFiles() VfsPath pathname; RETURN_ERR(g_VFS->GetVirtualPath(notifications[i].Pathname(), pathname)); RETURN_ERR(g_VFS->Invalidate(pathname)); + + // Tell each hotloadable system about this file change: + RETURN_ERR(g_GUI->ReloadChangedFiles(pathname)); - if (g_Game && g_Game->GetSimulation2()) - RETURN_ERR(g_Game->GetSimulation2()->ReloadChangedFile(pathname)); + + for (size_t j = 0; j < g_ReloadFuncs.size(); ++j) + g_ReloadFuncs[j].first(g_ReloadFuncs[j].second, pathname); + RETURN_ERR(h_reload(pathname)); } } @@ -99,7 +111,7 @@ PSRETURN CVFSFile::Load(const VfsPath& filename) LibError ret = g_VFS->LoadFile(filename, m_Buffer, m_BufferSize); if (ret != INFO::OK) { - LOG(CLogger::Error, LOG_CATEGORY, L"CVFSFile: file %ls couldn't be opened (vfs_load: %ld)", filename.string().c_str(), ret); + LOGERROR(L"CVFSFile: file %ls couldn't be opened (vfs_load: %ld)", filename.string().c_str(), ret); return PSRETURN_CVFSFile_LoadFailed; } diff --git a/source/ps/Filesystem.h b/source/ps/Filesystem.h index 31a0928e4f..5af1f4b233 100644 --- a/source/ps/Filesystem.h +++ b/source/ps/Filesystem.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -32,6 +32,22 @@ extern PIVFS g_VFS; extern bool FileExists(const VfsPath& pathname); +/** + * callback function type for file change notifications + */ +typedef LibError (*FileReloadFunc)(void* param, const VfsPath& path); + +/** + * register a callback function to be called by ReloadChangedFiles + */ +void RegisterFileReloadFunc(FileReloadFunc func, void* obj); + +/** + * delete a callback function registered with RegisterFileReloadFunc + * (removes any with the same func and obj) + */ +void UnregisterFileReloadFunc(FileReloadFunc func, void* obj); + /** * poll for directory change notifications and reload all affected files. * must be called regularly (e.g. once a frame), else notifications diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp index a7ab135407..85dade6027 100644 --- a/source/simulation2/Simulation2.cpp +++ b/source/simulation2/Simulation2.cpp @@ -52,11 +52,18 @@ public: m_SimContext.m_Terrain = terrain; m_ComponentManager.LoadComponentTypes(); + RegisterFileReloadFunc(ReloadChangedFileCB, this); + // m_EnableOOSLog = true; // TODO: this should be a command-line flag or similar // (can't call ResetState here since the scripts haven't been loaded yet) } + ~CSimulation2Impl() + { + UnregisterFileReloadFunc(ReloadChangedFileCB, this); + } + CParamNode LoadXML(const std::wstring& name) { CParamNode ret; @@ -112,6 +119,11 @@ public: bool LoadScripts(const VfsPath& path); LibError ReloadChangedFile(const VfsPath& path); + static LibError ReloadChangedFileCB(void* param, const VfsPath& path) + { + return static_cast(param)->ReloadChangedFile(path); + } + bool Update(int turnLength, const std::vector& commands); void Interpolate(float frameLength, float frameOffset); diff --git a/source/simulation2/components/CCmpProjectileManager.cpp b/source/simulation2/components/CCmpProjectileManager.cpp index 1b733b4c41..1afb98bebc 100644 --- a/source/simulation2/components/CCmpProjectileManager.cpp +++ b/source/simulation2/components/CCmpProjectileManager.cpp @@ -136,7 +136,7 @@ void CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D std::wstring name = sourceVisual->GetProjectileActor(); if (name.empty()) { - LOGERROR(L"Unit with actor '%ls' launched a projectile but has no actor on 'projectile' attachpoint", sourceVisual->GetActor().c_str()); + LOGERROR(L"Unit with actor '%ls' launched a projectile but has no actor on 'projectile' attachpoint", sourceVisual->GetActorShortName().c_str()); return; } diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp index a60ed0e232..4b923ae0e9 100644 --- a/source/simulation2/components/CCmpVisualActor.cpp +++ b/source/simulation2/components/CCmpVisualActor.cpp @@ -47,6 +47,7 @@ public: DEFAULT_COMPONENT_ALLOCATOR(VisualActor) + std::wstring m_ActorName; CUnit* m_Unit; // Current animation state @@ -54,6 +55,9 @@ public: bool m_AnimOnce; float m_AnimSpeed; std::wstring m_SoundGroup; + float m_AnimDesync; + + float m_AnimSyncRepeatTime; // 0.0 if not synced CCmpVisualActor() : m_Unit(NULL) @@ -93,23 +97,22 @@ public: // TODO: we should do some fancy animation of under-construction buildings rising from the ground, // but for now we'll just use the foundation actor and ignore the normal one - std::wstring name; if (paramNode.GetChild("Foundation").IsOk() && paramNode.GetChild("FoundationActor").IsOk()) - name = paramNode.GetChild("FoundationActor").ToString(); + m_ActorName = paramNode.GetChild("FoundationActor").ToString(); else - name = paramNode.GetChild("Actor").ToString(); + m_ActorName = paramNode.GetChild("Actor").ToString(); std::set selections; - m_Unit = context.GetUnitManager().CreateUnit(name, NULL, selections); + m_Unit = context.GetUnitManager().CreateUnit(m_ActorName, NULL, selections); if (!m_Unit) { // The error will have already been logged return; } - SelectAnimation("idle", false, 0.f, L""); + m_Unit->SetID(GetEntityId()); - m_Unit->SetID(GetEntityId()); // TODO: is it safe to be using entity IDs for unit IDs? + SelectAnimation("idle", false, 0.f, L""); } virtual void Deinit(const CSimContext& context) @@ -127,10 +130,7 @@ public: if (serialize.IsDebug()) { - if (m_Unit == NULL) - serialize.StringASCII("actor", "[none]", 0, 256); - else - serialize.String("actor", m_Unit->GetObject().m_Base->m_Name, 0, 256); + serialize.String("actor", m_ActorName, 0, 256); } // TODO: store random variation. This ought to be synchronised across saved games @@ -170,7 +170,7 @@ public: { const CMessageOwnershipChanged& msgData = static_cast (msg); if (m_Unit) - m_Unit->SetPlayerID(msgData.to); + m_Unit->GetModel().SetPlayerID(msgData.to); break; } } @@ -190,11 +190,11 @@ public: return m_Unit->GetModel().GetTransform().GetTranslation(); } - virtual std::wstring GetActor() + virtual std::wstring GetActorShortName() { if (!m_Unit) return L""; - return m_Unit->GetObject().m_Base->m_Name; + return m_Unit->GetObject().m_Base->m_ShortName; } virtual std::wstring GetProjectileActor() @@ -212,14 +212,14 @@ public: if (!isfinite(speed) || speed < 0) // JS 'undefined' converts to NaN, which causes Bad Things speed = 1.f; - float desync = 0.05f; // TODO: make this an argument - m_AnimName = name; m_AnimOnce = once; m_AnimSpeed = speed; m_SoundGroup = soundgroup; + m_AnimDesync = 0.05f; // TODO: make this an argument + m_AnimSyncRepeatTime = 0.0f; - m_Unit->SetAnimationState(name, once, speed, desync, false, soundgroup.c_str()); + m_Unit->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed, m_AnimDesync, false, m_SoundGroup.c_str()); } virtual void SetAnimationSync(float actiontime, float repeattime) @@ -227,7 +227,9 @@ public: if (!m_Unit) return; - m_Unit->SetAnimationSync(actiontime, repeattime); + m_AnimSyncRepeatTime = repeattime; + + m_Unit->SetAnimationSync(actiontime, m_AnimSyncRepeatTime); } virtual void SetShadingColour(fixed r, fixed g, fixed b, fixed a) @@ -238,6 +240,41 @@ public: m_Unit->GetModel().SetShadingColor(CColor(r.ToFloat(), g.ToFloat(), b.ToFloat(), a.ToFloat())); } + virtual void Hotload(const std::wstring& name) + { + if (!m_Unit) + return; + + if (name != m_ActorName) + return; + + std::set selections; + CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, NULL, selections); + + if (!newUnit) + return; + + // Save some data from the old unit + CColor shading = m_Unit->GetModel().GetShadingColor(); + size_t playerID = m_Unit->GetModel().GetPlayerID(); + + // Replace with the new unit + GetSimContext().GetUnitManager().DeleteUnit(m_Unit); + m_Unit = newUnit; + + m_Unit->SetID(GetEntityId()); + + m_Unit->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed, m_AnimDesync, false, m_SoundGroup.c_str()); + + // We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate + if (m_AnimSyncRepeatTime != 0.0f) + m_Unit->SetAnimationSync(0.0f, m_AnimSyncRepeatTime); + + m_Unit->GetModel().SetShadingColor(shading); + + m_Unit->GetModel().SetPlayerID(playerID); + } + private: void Interpolate(const CSimContext& context, float frameTime, float frameOffset); void RenderSubmit(const CSimContext& context, SceneCollector& collector, const CFrustum& frustum, bool culling); diff --git a/source/simulation2/components/ICmpVisual.h b/source/simulation2/components/ICmpVisual.h index 3a9c0fc577..e493906fc4 100644 --- a/source/simulation2/components/ICmpVisual.h +++ b/source/simulation2/components/ICmpVisual.h @@ -42,10 +42,10 @@ public: virtual CVector3D GetPosition() = 0; /** - * Return the filename of the actor that's being displayed, or the empty string on error. + * Return the short name of the actor that's being displayed, or the empty string on error. * (Not safe for use in simulation code.) */ - virtual std::wstring GetActor() = 0; + virtual std::wstring GetActorShortName() = 0; /** * Return the filename of the actor to be used for projectiles from this unit, or the empty string if none. @@ -84,6 +84,13 @@ public: */ virtual void SetShadingColour(fixed r, fixed g, fixed b, fixed a) = 0; + /** + * Called when an actor file has been modified and reloaded dynamically. + * If this component uses the named actor file, it should regenerate its actor + * to pick up the new definitions. + */ + virtual void Hotload(const std::wstring& name) = 0; + DECLARE_INTERFACE_TYPE(Visual) }; diff --git a/source/tools/atlas/GameInterface/ActorViewer.cpp b/source/tools/atlas/GameInterface/ActorViewer.cpp index d3dc1021d0..6d91c316bb 100644 --- a/source/tools/atlas/GameInterface/ActorViewer.cpp +++ b/source/tools/atlas/GameInterface/ActorViewer.cpp @@ -47,7 +47,7 @@ struct ActorViewerImpl : public Scene public: ActorViewerImpl() : Entity(INVALID_ENTITY), Terrain(), ColladaManager(), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager), - ObjectManager(MeshManager, SkeletonAnimManager), UnitManager(), Simulation2(&UnitManager, &Terrain) + UnitManager(), Simulation2(&UnitManager, &Terrain), ObjectManager(MeshManager, SkeletonAnimManager, Simulation2) { UnitManager.SetObjectManager(ObjectManager); } diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index 6f62f8b86c..b44034d8e0 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -131,32 +131,21 @@ QUERYHANDLER(GetObjectSettings) { View* view = View::GetView(msg->view); CSimulation2* simulation = view->GetSimulation2(); - if (simulation) - { - sObjectSettings settings; - settings.player = 0; - - CmpPtr cmpOwner (*simulation, view->GetEntityId(msg->id)); - if (!cmpOwner.null()) - { - int32_t player = cmpOwner->GetOwner(); - if (player != -1) - settings.player = player; - } - - // TODO: selections - - msg->settings = settings; - - return; - } - - CUnit* unit = view->GetUnit(msg->id); - if (! unit) return; sObjectSettings settings; - settings.player = unit->GetPlayerID(); + settings.player = 0; + CmpPtr cmpOwner (*simulation, view->GetEntityId(msg->id)); + if (!cmpOwner.null()) + { + int32_t player = cmpOwner->GetOwner(); + if (player != -1) + settings.player = player; + } + + // TODO: selections + +/* // Get the unit's possible variants and selected variants std::vector > groups = unit->GetObject().m_Base->GetVariantGroups(); const std::set& selections = unit->GetActorSelections(); @@ -194,6 +183,8 @@ QUERYHANDLER(GetObjectSettings) settings.variantgroups = variantgroups; settings.selections = std::vector (selections_set.begin(), selections_set.end()); // convert set->vector +*/ + msg->settings = settings; } @@ -208,27 +199,19 @@ BEGIN_COMMAND(SetObjectSettings) View* view = View::GetView(msg->view); CSimulation2* simulation = view->GetSimulation2(); - if (simulation) - { - CmpPtr cmpOwner (*simulation, view->GetEntityId(msg->id)); - m_PlayerOld = 0; - if (!cmpOwner.null()) - { - int32_t player = cmpOwner->GetOwner(); - if (player != -1) - m_PlayerOld = player; - } - // TODO: selections - } - else + CmpPtr cmpOwner (*simulation, view->GetEntityId(msg->id)); + m_PlayerOld = 0; + if (!cmpOwner.null()) { - CUnit* unit = view->GetUnit(msg->id); - if (! unit) return; - m_PlayerOld = unit->GetPlayerID(); - m_SelectionsOld = unit->GetActorSelections(); + int32_t player = cmpOwner->GetOwner(); + if (player != -1) + m_PlayerOld = player; } + // TODO: selections +// m_SelectionsOld = unit->GetActorSelections(); + m_PlayerNew = (size_t)settings.player; std::vector selections = *settings.selections; @@ -255,22 +238,13 @@ private: { View* view = View::GetView(msg->view); CSimulation2* simulation = view->GetSimulation2(); - if (simulation) - { - CmpPtr cmpOwner (*simulation, view->GetEntityId(msg->id)); - if (!cmpOwner.null()) - cmpOwner->SetOwner(player); - // TODO: selections - return; - } + CmpPtr cmpOwner (*simulation, view->GetEntityId(msg->id)); + if (!cmpOwner.null()) + cmpOwner->SetOwner(player); - CUnit* unit = view->GetUnit(msg->id); - if (! unit) return; - - unit->SetPlayerID(player); - - unit->SetActorSelections(selections); + // TODO: selections +// unit->SetActorSelections(selections); } }; END_COMMAND(SetObjectSettings); diff --git a/source/tools/atlas/GameInterface/View.cpp b/source/tools/atlas/GameInterface/View.cpp index a159155f8d..4c232d185a 100644 --- a/source/tools/atlas/GameInterface/View.cpp +++ b/source/tools/atlas/GameInterface/View.cpp @@ -261,11 +261,6 @@ CCamera& ViewGame::GetCamera() return *g_Game->GetView()->GetCamera(); } -CUnit* ViewGame::GetUnit(AtlasMessage::ObjectID id) -{ - return g_Game->GetWorld()->GetUnitManager().FindByID(id); -} - bool ViewGame::WantsHighFramerate() { if (g_Game->GetView()->GetCinema()->IsPlaying()) diff --git a/source/tools/atlas/GameInterface/View.h b/source/tools/atlas/GameInterface/View.h index 36517bf1c1..73807f436e 100644 --- a/source/tools/atlas/GameInterface/View.h +++ b/source/tools/atlas/GameInterface/View.h @@ -40,7 +40,6 @@ public: virtual CCamera& GetCamera() = 0; virtual CSimulation2* GetSimulation2() { return NULL; } virtual entity_id_t GetEntityId(AtlasMessage::ObjectID obj) { return (entity_id_t)obj; } - virtual CUnit* GetUnit(AtlasMessage::ObjectID UNUSED(id)) { return NULL; } virtual bool WantsHighFramerate() { return false; } virtual void SetParam(const std::wstring& name, bool value); @@ -77,7 +76,6 @@ public: virtual void Update(float frameLength); virtual void Render(); virtual CCamera& GetCamera(); - virtual CUnit* GetUnit(AtlasMessage::ObjectID id); virtual CSimulation2* GetSimulation2(); virtual bool WantsHighFramerate();