#include "StdAfx.h" #include "wstreamresource.h" #include #include "SceneApp.h" #include namespace { class NiEntityStreamingCached : public NiEntityStreamingAscii { public: NiEntityStreamingCached() { } virtual ~NiEntityStreamingCached() { } virtual bool Load(const char* RefPath, const char* CachedFileName) { Flush(); //if (!StoreWorkingPath(RefPath)) // return false; NiStrcpy(m_acFullPath, NI_MAX_PATH, RefPath); char* Slash = strrchr(m_acFullPath, '\\'); if (!Slash) { Slash = strrchr(m_acFullPath,'/'); } if (Slash) { *Slash = '\0'; }else { m_acFullPath[0] = '\0'; } RemoveAllScenes(); //-- Initialize m_kDOM.Init(CachedFileName); //-- Read from disk if (!m_kDOM.LoadFile()) { Flush(); ReportError("The GSA file could not be parsed as valid XML.", m_acFilenameErrorMsg, NULL, NULL); return false; } //-- Read from DOM (and create a Scene) if (!ReadFromDOM()) { Flush(); RemoveAllScenes(); return false; } Flush(); return true; } }; class _NiExternalAssetParams : public NiExternalAssetParams { friend class SceneGraphComponent; }; // 覆盖NDL 原本的Comp class SceneGraphComponent : public NiSceneGraphComponent { BOOL m_Requested; friend class WStreamResource; SceneGraphComponent():m_Requested(FALSE) { } virtual ~SceneGraphComponent() { } virtual void Update(NiEntityPropertyInterface* pkParentEntity, float fTime, NiEntityErrorInterface* pkErrors, NiExternalAssetManager* pkAssetManager) { if (ShouldReloadScene() && !m_Requested) { // Re-synchronize default and local data. m_kNifFilePath = GetNifFilePath(); // Clear out existing scene root. m_spSceneRoot = NULL; // TODO 可能是KFM 等. WStreamResource::LoadNif(m_kNifFilePath,this); m_Requested = TRUE; } if (m_spSceneRoot) { NiBool bDependentPropertiesFound = true; // Find dependent properties. NiPoint3 kTranslation; if (!pkParentEntity->GetPropertyData(ms_kTranslationName, kTranslation)) { bDependentPropertiesFound = false; pkErrors->ReportError(ERR_TRANSLATION_NOT_FOUND, NULL, pkParentEntity->GetName(), ms_kTranslationName); } NiMatrix3 kRotation; if (!pkParentEntity->GetPropertyData(ms_kRotationName, kRotation)) { bDependentPropertiesFound = false; pkErrors->ReportError(ERR_ROTATION_NOT_FOUND, NULL, pkParentEntity->GetName(), ms_kRotationName); } float fScale; if (!pkParentEntity->GetPropertyData(ms_kScaleName, fScale)) { bDependentPropertiesFound = false; pkErrors->ReportError(ERR_SCALE_NOT_FOUND, NULL, pkParentEntity->GetName(), ms_kScaleName); } // Use dependent properties to update transform of scene root. bool bUpdatedTransforms = false; if (bDependentPropertiesFound) { if (m_spSceneRoot->GetTranslate() != kTranslation) { m_spSceneRoot->SetTranslate(kTranslation); bUpdatedTransforms = true; } if (m_spSceneRoot->GetRotate() != kRotation) { m_spSceneRoot->SetRotate(kRotation); bUpdatedTransforms = true; } if (m_spSceneRoot->GetScale() != fScale) { m_spSceneRoot->SetScale(fScale); bUpdatedTransforms = true; } } // Update scene root with the provided time. if (bUpdatedTransforms) { m_spSceneRoot->Update(fTime); } else if (GetShouldUpdateSceneRoot()) { m_spSceneRoot->UpdateSelected(fTime); } } } public: static NiEntityPropertyInterface* CreateFunc(const char* pcParams = NULL) { return NiNew SceneGraphComponent; } BOOL FinalAddToScene(NiAVObject* Root) { if (Root == NULL) { return FALSE; } m_spSceneRoot = Root; SetShouldUpdateSceneRoot(NIBOOL_IS_TRUE( RecursiveFindAnimations(m_spSceneRoot))); // Perform initial update. m_spSceneRoot->Update(0.0f); m_spSceneRoot->UpdateNodeBound(); m_spSceneRoot->UpdateProperties(); m_spSceneRoot->UpdateEffects(); g_Game->ReCacheScene(); return TRUE; } }; };// namespace NiTexturePtr WStreamResource::sm_LoadingTex = NULL; NiTexturePtr WStreamResource::sm_ErrorTex = NULL; ILWStreamSystem* WStreamResource::sm_StreamSystem = NULL; WStreamResource::LoadingTextureList WStreamResource::m_LoadingTexList; const ResourceCallback WStreamResource::m_TextureCB = { WStreamResource::TextureLoadedCB, WStreamResource::TextureReferenceCB, NULL, WStreamResource::TextureLoadingProgressCallback, FALSE, }; const ResourceCallback WStreamResource::m_NifCB = { WStreamResource::NifLoadedCB, WStreamResource::NifReferenceCB, NULL, WStreamResource::NifLoadingProgressCallback, FALSE, }; const ResourceCallback WStreamResource::m_SceneCB = { WStreamResource::SceneLoadedCB, WStreamResource::SceneReferenceCB, NULL, WStreamResource::SceneLoadingProgressCallback, FALSE, }; void WStreamResource::SetStreamSystem(ILWStreamSystem* StreamSystem) { sm_StreamSystem = StreamSystem; } void WStreamResource::HookNDLCreator() { NiTFactory* pEntityFactory = NiFactories::GetEntityCompFactory(); NIASSERT(pEntityFactory); NiFixedString kName("NiSceneGraphComponent"); pEntityFactory->Register(kName, SceneGraphComponent::CreateFunc); } BOOL WStreamResource::Initialize(ILWStreamSystem* StreamSystem) { sm_StreamSystem = StreamSystem; NIASSERT(StreamSystem); sm_ErrorTex = NiSourceTexture::Create("Resource/Error.tga"); sm_LoadingTex = NiSourceTexture::Create("Resource/Loading.tga"); NIASSERT(sm_ErrorTex != NULL); NIASSERT(sm_LoadingTex); NiSourceTexture::ms_StreamCB = StreamTextureRequest; return TRUE; } void WStreamResource::Destroy() { // 避免这些引用Loading 的纹理在释放的时候删除Loading 纹理数据. LoadingTextureList::const_iterator it = m_LoadingTexList.begin(); LoadingTextureList::const_iterator end = m_LoadingTexList.end(); for ( ; it!= end; ++it) { NiSourceTexture* SrcTexture = *it; SrcTexture->SetRendererData(NULL); sm_LoadingTex->DecRefCount(); } m_LoadingTexList.clear(); sm_ErrorTex = NULL; sm_LoadingTex = NULL; } void WStreamResource::ReviseURL(char* WebPath) { NIASSERT(WebPath); char* pc = WebPath; while (*pc) { if (*pc == '\\') { *pc = '/'; } pc++; } } void WStreamResource::Lock() { g_Game->Lock(); } void WStreamResource::Unlock() { g_Game->Unlock(); } void WStreamResource::LoadNif(const char* Path, void* UserData) { char URL[NI_MAX_PATH]; NiStrcpy(URL,NI_MAX_PATH,Path); ReviseURL(URL); sm_StreamSystem->StreamResource(URL,&m_NifCB,UserData); } void WStreamResource::LoadScene(const char* Path, void* pScene) { g_Game->SetLoading(TRUE); char URL[NI_MAX_PATH]; NiStrcpy(URL,NI_MAX_PATH,Path); ReviseURL(URL); sm_StreamSystem->StreamResource(URL,&m_SceneCB,pScene); } int __cdecl WStreamResource::StreamTextureRequest(NiSourceTexture* pThis, void* UserData) { // #1先设置为读取纹理. // TODO 理想状态下,我希望这些纹理能够以镜头方向自动生成纹理坐标. 但是我们需要获得NiTextureProp. // 目前为止尚不能找到很好的途径去获得. // 或者我们修改Ni**Render. 当设置纹理为LoadingTex的时候,就在Renderer 内部自动设置纹理坐标. NIASSERT(pThis); HRenderData RenderData = pThis->GetRendererData(); NIASSERT(RenderData == NULL); pThis->SetRendererData(sm_LoadingTex->GetRendererData()); // 增加引用计数,避免D3DTEXTURE 被删除. sm_LoadingTex->IncRefCount(); m_LoadingTexList.push_back(pThis); // #2提交下载请求. // TODO 如果我们能约定总是通过NiFixedString 来表达路径,那么我们可以优化StreamSystem内部的多次 // 字符串new delete 操作! char URL[NI_MAX_PATH]; NiStrcpy(URL,NI_MAX_PATH,pThis->GetPlatformSpecificFilename()); ReviseURL(URL); sm_StreamSystem->StreamResource(URL,&m_TextureCB,pThis); return 0; } ILWResource* WStreamResource::TextureLoadedCB(String* RequestURL, String* CacheFileName, void* UserData) { Lock(); _NiSourceTexture* Parent = (_NiSourceTexture*)UserData; NIASSERT(Parent); NIASSERT(Parent->GetRendererData() == sm_LoadingTex->GetRendererData()); // 我们需要的仅仅只有RenderData. sm_LoadingTex->DecRefCount(); // 减少引用计数. Parent->SetRendererData(NULL); // 寻找并从链表中删除 LoadingTextureList::iterator it = m_LoadingTexList.begin(); LoadingTextureList::const_iterator end = m_LoadingTexList.end(); for ( ; it!= end; ++it) { if ((_NiSourceTexture*)(*it) == Parent) { m_LoadingTexList.erase(it); break; } } // 设置路径 Parent->SetFilename(CacheFileName->c_str()); bool bRet = Parent->CreateRendererData(false); if (!bRet) { //log warning. } Unlock(); return NULL; } void WStreamResource::TextureReferenceCB(String* RequestUrl, String* CacheFileName, ILWResource* RefResource, void* userData) { } void WStreamResource::TextureLoadingProgressCallback(float Ratio, void *UserData) { } ILWResource* WStreamResource::SceneLoadedCB(String* RequestURL, String* CacheFileName, void* UserData) { Lock(); // TODO , 在主线程中来进行创建操作. 不要影响传输线程. NiEntityStreamingCached kStream; if (!kStream.Load(RequestURL->c_str(),CacheFileName->c_str())) { char caOut[NI_MAX_PATH]; NiSprintf(caOut, NI_MAX_PATH, "Failed: Loading file name %s", CacheFileName->c_str()); NiMessageBox(caOut, "Failed"); Unlock(); return NULL; } if (kStream.GetSceneCount() < 1) { NiMessageBox("Failed: Obtaining a Scene", "Failed"); Unlock(); return false; } NIASSERT(g_Game); g_Game->m_spEntityScene = kStream.GetSceneAt(0); NiScene* pScene = g_Game->m_spEntityScene; unsigned int uiCount = pScene->GetEntityCount(); for(unsigned int uiEntity=0; uiEntity< uiCount; uiEntity++) { NiEntityInterface* pkEntity = pScene->GetEntityAt(uiEntity); const char* pcName = pkEntity->GetName(); pkEntity->Update(NULL, 0.0, g_Game->m_spError, g_Game->m_spAssetManager); } //g_Game->SetLoading(FALSE); g_Game->PostLoadScene(); Unlock(); return NULL; } void WStreamResource::SceneReferenceCB(String* RequestUrl, String* CacheFileName, ILWResource* RefResource, void* userData) { } void WStreamResource::SceneLoadingProgressCallback(float Ratio, void *UserData) { } ILWResource* WStreamResource::NifLoadedCB(String* RefURL, String* CacheFileName, void* UserData) { Lock(); NiStream kStream; char RefPath[NI_MAX_PATH]; NiStrcpy(RefPath, NI_MAX_PATH, RefURL->c_str()); NiSearchPath* SP = kStream.GetSearchPath(); if (SP) { SP->SetReferencePath(RefPath); } // create new input file stream NiFile* kIst = NiFile::GetFile(CacheFileName->c_str(), NiFile::READ_ONLY); if (!kIst || !*kIst) { NiDelete kIst; Unlock(); return NULL; } bool bSuccess = kStream.Load(kIst); NiDelete kIst; if (!bSuccess) { NiMessageBox(CacheFileName->c_str(), "NIF Error"); Unlock(); return FALSE; } NiAVObjectPtr Root = NiDynamicCast(NiAVObject, kStream.GetObjectAt(0)); if (Root) { SceneGraphComponent* NifComp = (SceneGraphComponent*)UserData; NIASSERT(NifComp); NifComp->FinalAddToScene(Root); } Unlock(); return NULL; } void WStreamResource::NifReferenceCB(String* RequestUrl, String* CacheFileName, ILWResource* RefResource, void* userData) { } void WStreamResource::NifLoadingProgressCallback(float Ratio, void *UserData) { }