/// Recursive function for resolving a package request. /// /// @param[out] rspPackage Resolved package. /// @param[in] packagePath Package object path. void CachePackageLoader::ResolvePackage( AssetPtr& rspPackage, AssetPath packagePath ) { HELIUM_ASSERT( !packagePath.IsEmpty() ); rspPackage = Asset::FindObject( packagePath ); if( !rspPackage ) { AssetPtr spParent; AssetPath parentPath = packagePath.GetParent(); if( !parentPath.IsEmpty() ) { ResolvePackage( spParent, parentPath ); HELIUM_ASSERT( spParent ); } HELIUM_VERIFY( Asset::CreateObject( rspPackage, Package::GetStaticType(), packagePath.GetName(), spParent ) ); HELIUM_ASSERT( rspPackage ); HELIUM_ASSERT( rspPackage->IsA( Package::GetStaticType()->GetMetaClass() ) ); } rspPackage->SetFlags( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED | Asset::FLAG_LOADED ); }
/// Set this object path based on the given parameters. /// /// @param[in] pNames Array of object names in the path, starting from the bottom level on up. /// @param[in] pInstanceIndices Array of object instance indices in the path, starting from the bottom level on up. /// @param[in] nameCount Number of object names. /// @param[in] packageCount Number of object names that are packages. void AssetPath::Set( const Name* pNames, const uint32_t* pInstanceIndices, size_t nameCount, size_t packageCount ) { HELIUM_ASSERT( pNames ); HELIUM_ASSERT( pInstanceIndices ); HELIUM_ASSERT( nameCount != 0 ); // Set up the entry for this path. Entry entry; entry.pParent = NULL; entry.name = pNames[ 0 ]; entry.instanceIndex = pInstanceIndices[ 0 ]; entry.bPackage = ( nameCount <= packageCount ); if( nameCount > 1 ) { size_t parentNameCount = nameCount - 1; AssetPath parentPath; parentPath.Set( pNames + 1, pInstanceIndices + 1, parentNameCount, Min( parentNameCount, packageCount ) ); entry.pParent = parentPath.m_pEntry; HELIUM_ASSERT( entry.pParent ); } // Look up/add the entry. m_pEntry = Add( entry ); HELIUM_ASSERT( m_pEntry ); }
void Helium::LooseAssetLoader::EnumerateRootPackages( DynamicArray< AssetPath > &packagePaths ) { FilePath dataDirectory; FileLocations::GetDataDirectory( dataDirectory ); DirectoryIterator packageDirectory( dataDirectory ); for( ; !packageDirectory.IsDone(); packageDirectory.Next() ) { if (packageDirectory.GetItem().m_Path.IsDirectory()) { AssetPath path; //std::string filename = packageDirectory.GetItem().m_Path.Parent(); std::vector< std::string > filename = packageDirectory.GetItem().m_Path.DirectoryAsVector(); HELIUM_ASSERT(!filename.empty()); std::string directory = filename.back(); if (directory.size() <= 0) { continue; } path.Set( Name( directory.c_str() ), true, AssetPath(NULL_NAME) ); packagePaths.Add( path ); } } }
inline std::string getPrefix( AssetPath p ) { unsigned s = p.find_last_of("/\\"); if( s == std::string::npos ) s = 0; else s++; return p.substr(s,3); }
/// Get the path to the package containing all world instances. /// /// @return World package path. AssetPath WorldManager::GetRootSceneDefinitionPackagePath() const { static AssetPath worldPackagePath; if( worldPackagePath.IsEmpty() ) { HELIUM_VERIFY( worldPackagePath.Set( TXT( "/Worlds" ) ) ); } return worldPackagePath; }
bool ProjectViewModel::IsContainer( const wxDataViewItem& item ) const { // root node can have children if ( !item.IsOk() ) { return true; } AssetPath *node = static_cast< AssetPath* >( item.GetID() ); return node ? node->IsPackage() : false; }
void asyncLoad( AssetPath path, AssetPromisePtr pr, AssetContentPtr c, Progress::Work w ) { try { { // TODO: Do this properly... // Get the contents of the file std::ifstream fs(path.c_str(), std::ios::binary); if( !fs.is_open() ) BOOST_THROW_EXCEPTION( InexistentAssetPathException(path) ); fs.seekg(0,std::ios::end); std::vector<char> buf((unsigned)fs.tellg() + 1); fs.seekg(0,std::ios::beg); fs.read(&buf[0],buf.capacity()); buf.resize((unsigned)fs.gcount()); fs.close(); // call the loading method on the AssetContent c->asyncLoad(buf); } mngr.prog_.makeProgress(w); // after loading is done, set the promise value to the content pr->set_value(c); } catch( ... ) { // transfer exceptions to the main thread pr->set_exception(boost::current_exception()); } boost::this_thread::yield(); }
int64_t AssetLoader::GetAssetFileTimestamp( const AssetPath &path ) { Package *pPackage = Asset::Find<Package>( path.GetParent() ); HELIUM_ASSERT( pPackage ); PackageLoader *pLoader = pPackage->GetLoader(); HELIUM_ASSERT( pLoader ); return pLoader->GetAssetFileSystemTimestamp( path ); }
/// Find an object based on its path name. /// /// @param[in] path FilePath of the object to locate. /// /// @return Pointer to the object if found, null pointer if not found. Asset* Asset::FindObject( AssetPath path ) { // Make sure the path isn't empty. if( path.IsEmpty() ) { return NULL; } // Assemble a list of object names and instance indices, from the top level on down. size_t pathDepth = 0; size_t packageDepth = 0; for( AssetPath testPath = path; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { ++pathDepth; if( testPath.IsPackage() ) { ++packageDepth; } } StackMemoryHeap<>& rStackHeap = ThreadLocalStackAllocator::GetMemoryHeap(); StackMemoryHeap<>::Marker stackMarker( rStackHeap ); Name* pPathNames = static_cast< Name* >( rStackHeap.Allocate( sizeof( Name ) * pathDepth ) ); HELIUM_ASSERT( pPathNames ); uint32_t* pInstanceIndices = static_cast< uint32_t* >( rStackHeap.Allocate( sizeof( uint32_t ) * pathDepth ) ); HELIUM_ASSERT( pInstanceIndices ); size_t pathIndex = pathDepth; for( AssetPath testPath = path; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { HELIUM_ASSERT( pathIndex != 0 ); --pathIndex; pPathNames[ pathIndex ] = testPath.GetName(); pInstanceIndices[ pathIndex ] = testPath.GetInstanceIndex(); } HELIUM_ASSERT( pathIndex == 0 ); // Search from the root. return FindChildOf( NULL, pPathNames, pInstanceIndices, pathDepth, packageDepth ); }
int64_t LoosePackageLoader::GetAssetFileSystemTimestamp( const AssetPath &path ) const { size_t index = FindObjectByName( path.GetRootName() ); HELIUM_ASSERT( index < m_objects.GetSize() ); if ( index < m_objects.GetSize() ) { return m_objects[ index ].fileTimeStamp; } else { return 0; } }
bool Helium::AssetResolver::Resolve( const Name& identity, Reflect::ObjectPtr& pointer, const Reflect::MetaClass* pointerClass ) { // Paths begin with / if (!identity.IsEmpty() && (*identity)[0] == '/') { HELIUM_TRACE( TraceLevels::Info, TXT( "Resolving object [%s]\n" ), identity.Get() ); AssetPath p; p.Set(*identity); size_t loadRequestId = AssetLoader::GetStaticInstance()->BeginLoadObject(p); m_Fixups.Push( Fixup( pointer, pointerClass, loadRequestId ) ); return true; } else { HELIUM_TRACE( TraceLevels::Info, TXT( "Deferring resolution of [%s] to archive\n" ), identity.Get() ); } return false; }
TEST(Engine, PackageObjectTest) { { AssetPath testPath; HELIUM_VERIFY( testPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "EngineTest" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "TestObject" ) ) ); AssetPtr spObject; HELIUM_VERIFY( gAssetLoader->LoadObject( testPath, spObject ) ); HELIUM_ASSERT( spObject ); Package* pTestPackageCast = Reflect::SafeCast< Package >( spObject.Get() ); HELIUM_ASSERT( !pTestPackageCast ); HELIUM_UNREF( pTestPackageCast ); // The following line should not compile... // Animation* pTestAnimationCast = Reflect::SafeCast< Animation >( pTestPackageCast ); // HELIUM_UNREF( pTestAnimationCast ); Asset* pTestObjectCast = Reflect::SafeCast< Asset >( spObject.Get() ); HELIUM_ASSERT( pTestObjectCast ); HELIUM_UNREF( pTestObjectCast ); } }
bool Helium::AssetResolver::Resolve( const Name& identity, Reflect::ObjectPtr& pointer, const Reflect::MetaClass* pointerClass ) { // Paths begin with / if (!identity.IsEmpty() && (*identity)[0] == '/') { HELIUM_TRACE( TraceLevels::Info, TXT( "Resolving object [%s]\n" ), identity.Get() ); AssetPath p; p.Set(*identity); size_t loadRequestId = AssetLoader::GetStaticInstance()->BeginLoadObject(p); m_Fixups.Push( Fixup( pointer, pointerClass, loadRequestId ) ); return true; } else { #if HELIUM_TOOLS // Some extra checking to make friendly error messages String str ( identity.Get() ); uint32_t index = Invalid< uint32_t >(); int parseSuccessful = str.Parse( "%d", &index ); if (!parseSuccessful) { HELIUM_TRACE( TraceLevels::Warning, "AssetResolver::Resolve - Identity '%s' is not a number, but doesn't start with '/'. If this is a path, it must begin with '/'!\n", *str); } #endif HELIUM_TRACE( TraceLevels::Debug, TXT( "Deferring resolution of [%s] to archive\n" ), identity.Get() ); } return false; }
/// Initialize this manager. /// /// @return True if this manager was initialized successfully, false if not. /// /// @see Shutdown() bool WorldManager::Initialize() { HELIUM_ASSERT( !m_spRootSceneDefinitionsPackage ); // Create the world package first. // XXX TMC: Note that we currently assume that the world package has no parents, so we don't need to handle // recursive package creation. If we want to move the world package to a subpackage, this will need to be // updated accordingly. AssetPath rootSceneDefinitionsPackagePath = GetRootSceneDefinitionPackagePath(); HELIUM_ASSERT( !rootSceneDefinitionsPackagePath.IsEmpty() ); HELIUM_ASSERT( rootSceneDefinitionsPackagePath.GetParent().IsEmpty() ); bool bCreateResult = Asset::Create< Package >( m_spRootSceneDefinitionsPackage, rootSceneDefinitionsPackagePath.GetName(), NULL ); HELIUM_ASSERT( bCreateResult ); if( !bCreateResult ) { HELIUM_TRACE( TraceLevels::Error, TXT( "WorldManager::Initialize(): Failed to create world definition package \"%s\".\n" ), *rootSceneDefinitionsPackagePath.ToString() ); return false; } HELIUM_ASSERT( m_spRootSceneDefinitionsPackage ); // Reset frame timings. m_actualFrameTickCount = 0; m_frameTickCount = 0; m_frameDeltaTickCount = 0; m_frameDeltaSeconds = 0.0f; // First frame still needs to be processed. m_bProcessedFirstFrame = false; return true; }
/// Update property preloading for the given object load request. /// /// @param[in] pRequest Load request to update. /// /// @return True if preloading still needs processing, false if it is complete. bool AssetLoader::TickPreload( LoadRequest* pRequest ) { HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( !( pRequest->stateFlags & ( LOAD_FLAG_LINKED | LOAD_FLAG_PRECACHED | LOAD_FLAG_LOADED ) ) ); PackageLoader* pPackageLoader = pRequest->pPackageLoader; HELIUM_ASSERT( pPackageLoader ); if( IsInvalid( pRequest->packageLoadRequestId ) ) { if( !pPackageLoader->TryFinishPreload() ) { // Still waiting for package loader preload. return false; } // Add an object load request. AssetPath path = pRequest->path; pRequest->packageLoadRequestId = pPackageLoader->BeginLoadObject( path, &pRequest->resolver ); if( IsInvalid( pRequest->packageLoadRequestId ) ) { pRequest->spObject = Asset::FindObject( path ); Asset* pObject = pRequest->spObject; if( pObject ) { HELIUM_TRACE( TraceLevels::Info, TXT( "AssetLoader: Asset \"%s\" is not serialized, but was found in memory.\n" ), *path.ToString() ); // Make sure the object is preloaded and linked, but still perform resource caching and load // finalization if necessary. pObject->SetFlags( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED ); AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRELOADED | LOAD_FLAG_LINKED ); return true; } HELIUM_TRACE( TraceLevels::Error, TXT( "AssetLoader: Asset \"%s\" is not serialized and does not exist in memory.\n" ), *path.ToString() ); AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_FULLY_LOADED | LOAD_FLAG_ERROR ); return true; } } HELIUM_ASSERT( IsValid( pRequest->packageLoadRequestId ) ); bool bFinished = pPackageLoader->TryFinishLoadObject( pRequest->packageLoadRequestId, pRequest->spObject ); if( !bFinished ) { // Still waiting for object to load. return false; } // Preload complete. SetInvalid( pRequest->packageLoadRequestId ); AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRELOADED ); return true; }
/// Begin asynchronous loading of an object. /// /// @param[in] path Asset path. /// /// @return ID for the load request if started successfully, invalid index if not. /// /// @see TryFinishLoad(), FinishLoad() size_t AssetLoader::BeginLoadObject( AssetPath path ) { HELIUM_TRACE( TraceLevels::Info, TXT(" AssetLoader::BeginLoadObject - Loading path %s\n"), *path.ToString() ); // Search for an existing load request with the given path. ConcurrentHashMap< AssetPath, LoadRequest* >::ConstAccessor requestConstAccessor; if( m_loadRequestMap.Find( requestConstAccessor, path ) ) { LoadRequest* pRequest = requestConstAccessor->Second(); HELIUM_ASSERT( pRequest ); AtomicIncrementRelease( pRequest->requestCount ); // We can release now, as the request shouldn't get released now that we've incremented its reference count. requestConstAccessor.Release(); return m_loadRequestPool.GetIndex( pRequest ); } Asset *pAsset = Asset::Find<Asset>( path ); if ( pAsset && !pAsset->GetAllFlagsSet( Asset::FLAG_LOADED ) ) { pAsset = NULL; } PackageLoader *pPackageLoader = 0; if ( pAsset ) { HELIUM_TRACE( TraceLevels::Info, TXT( "AssetLoader::BeginLoadObject(): Object \"%s\" already loaded.\n" ), *path.ToString() ); } else { // Get the package loader to use for the given object. pPackageLoader = GetPackageLoader( path ); if( !pPackageLoader ) { HELIUM_TRACE( TraceLevels::Error, TXT( "AssetLoader::BeginLoadObject(): Failed to locate package loader for \"%s\".\n" ), *path.ToString() ); return Invalid< size_t >(); } } // Add the load request. LoadRequest* pRequest = m_loadRequestPool.Allocate(); pRequest->path = path; pRequest->pPackageLoader = pPackageLoader; SetInvalid( pRequest->packageLoadRequestId ); pRequest->stateFlags = pAsset ? (pAsset->GetFlags() & Asset::FLAG_BROKEN ? LOAD_FLAG_FULLY_LOADED | LOAD_FLAG_ERROR : LOAD_FLAG_FULLY_LOADED ) : 0; pRequest->requestCount = 1; HELIUM_ASSERT( !pRequest->spObject ); pRequest->spObject = pAsset; ConcurrentHashMap< AssetPath, LoadRequest* >::Accessor requestAccessor; if( m_loadRequestMap.Insert( requestAccessor, KeyValue< AssetPath, LoadRequest* >( path, pRequest ) ) ) { // New load request was created, so tick it once to get the load process running. requestAccessor.Release(); TickLoadRequest( pRequest ); } else { // A matching request was added while we were building our request, so reuse it. m_loadRequestPool.Release( pRequest ); pRequest = requestAccessor->Second(); HELIUM_ASSERT( pRequest ); AtomicIncrementRelease( pRequest->requestCount ); // We can release now, as the request shouldn't get released now that we've incremented its reference count. requestAccessor.Release(); } return m_loadRequestPool.GetIndex( pRequest ); }
/// Deserialize the link tables for an object load. /// /// @param[in] pRequest Load request data. bool CachePackageLoader::ReadCacheData( LoadRequest* pRequest ) { HELIUM_ASSERT( pRequest ); uint8_t* pBufferCurrent = pRequest->pAsyncLoadBuffer; uint8_t* pPropertyDataEnd = pRequest->pPropertyDataEnd; HELIUM_ASSERT( pBufferCurrent ); HELIUM_ASSERT( pPropertyDataEnd ); HELIUM_ASSERT( pBufferCurrent <= pPropertyDataEnd ); // We know the owner's path immediately just by looking at the path we're currently loading AssetPath parentPath = pRequest->pEntry->path.GetParent(); pRequest->ownerLoadIndex = AssetLoader::GetInstance()->BeginLoadObject( parentPath ); if (IsInvalid<size_t>(pRequest->ownerLoadIndex)) { HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader: Failed to begin loading owning asset '%s' for '%s'.\n", *parentPath.ToString(), *pRequest->pEntry->path.ToString()); } // Scan the property stream.. another null terminated string with character count first uint32_t propertyStreamSize = 0; if( pBufferCurrent + sizeof( propertyStreamSize ) > pPropertyDataEnd ) { HELIUM_TRACE( TraceLevels::Error, "CachePackageLoader: End of buffer reached when attempting to deserialize \"%s\".\n", *pRequest->pEntry->path.ToString() ); return false; } MemoryCopy( &propertyStreamSize, pBufferCurrent, sizeof( propertyStreamSize ) ); pBufferCurrent += sizeof( propertyStreamSize ); if( propertyStreamSize > static_cast< size_t >( pPropertyDataEnd - pBufferCurrent ) ) { HELIUM_TRACE( TraceLevels::Error, "CachePackageLoader: Property stream size (%" PRIu32 " bytes) for \"%s\" exceeds the amount of data cached. Value will be clamped.\n", propertyStreamSize, *pRequest->pEntry->path.ToString() ); propertyStreamSize = static_cast< uint32_t >( pPropertyDataEnd - pBufferCurrent ); } pRequest->pPropertyDataBegin = pBufferCurrent; pPropertyDataEnd = pBufferCurrent + propertyStreamSize; pRequest->pPropertyDataEnd = pPropertyDataEnd; // Verify that it's null terminated HELIUM_ASSERT( pRequest->pPropertyDataEnd[-1] == 0); // Adjust the end of the persistent resource data stream to account for the resource sub-data count padded on // the end (note that non-resources will not have this padding). The count is at the END of the buffer, and // if we have enough room for a size left, then assume persistant resource starts immediately after the property // data ends, and ends at the count. if( pRequest->pPersistentResourceDataEnd - pPropertyDataEnd >= sizeof( uint32_t ) ) { pRequest->pPersistentResourceDataBegin = pPropertyDataEnd; pRequest->pPersistentResourceDataEnd -= sizeof( uint32_t ); HELIUM_ASSERT( pRequest->pPersistentResourceDataEnd[-1] == 0 ); } else { pRequest->pPersistentResourceDataBegin = pPropertyDataEnd; pRequest->pPersistentResourceDataEnd = pPropertyDataEnd; } return true; }
int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpCmdLine*/, int nCmdShow ) { ForceLoadComponentsDll(); #if HELIUM_TOOLS ForceLoadEditorSupportDll(); #endif HELIUM_TRACE_SET_LEVEL( TraceLevels::Debug ); Timer::StaticInitialize(); #if !HELIUM_RELEASE && !HELIUM_PROFILE Helium::InitializeSymbols(); #endif AsyncLoader::GetStaticInstance().Initialize(); FilePath baseDirectory; if ( !FileLocations::GetBaseDirectory( baseDirectory ) ) { HELIUM_TRACE( TraceLevels::Error, TXT( "Could not get base directory." ) ); return -1; } HELIUM_VERIFY( CacheManager::InitializeStaticInstance( baseDirectory ) ); Helium::Bullet::Initialize(); int resultCode = -1; { Reflect::Initialize(); Helium::Components::Initialize(); Helium::TaskScheduler::CalculateSchedule(); #if HELIUM_TOOLS #endif InitEngineJobsDefaultHeap(); InitGraphicsJobsDefaultHeap(); InitTestJobsDefaultHeap(); #if HELIUM_TOOLS //HELIUM_VERIFY( LooseAssetLoader::InitializeStaticInstance() ); HELIUM_VERIFY( LooseAssetLoader::InitializeStaticInstance() ); AssetPreprocessor* pAssetPreprocessor = AssetPreprocessor::CreateStaticInstance(); HELIUM_ASSERT( pAssetPreprocessor ); PlatformPreprocessor* pPlatformPreprocessor = new PcPreprocessor; HELIUM_ASSERT( pPlatformPreprocessor ); pAssetPreprocessor->SetPlatformPreprocessor( Cache::PLATFORM_PC, pPlatformPreprocessor ); #else HELIUM_VERIFY( CacheAssetLoader::InitializeStaticInstance() ); #endif #if !GTEST AssetLoader* gAssetLoader = NULL; #endif gAssetLoader = AssetLoader::GetStaticInstance(); HELIUM_ASSERT( gAssetLoader ); Config& rConfig = Config::GetStaticInstance(); rConfig.BeginLoad(); while( !rConfig.TryFinishLoad() ) { gAssetLoader->Tick(); } #if HELIUM_TOOLS ConfigPc::SaveUserConfig(); #endif uint32_t displayWidth; uint32_t displayHeight; //bool bFullscreen; bool bVsync; { StrongPtr< GraphicsConfig > spGraphicsConfig( rConfig.GetConfigObject< GraphicsConfig >( Name( TXT( "GraphicsConfig" ) ) ) ); HELIUM_ASSERT( spGraphicsConfig ); displayWidth = spGraphicsConfig->GetWidth(); displayHeight = spGraphicsConfig->GetHeight(); //bFullscreen = spGraphicsConfig->GetFullscreen(); bVsync = spGraphicsConfig->GetVsync(); } WNDCLASSEXW windowClass; windowClass.cbSize = sizeof( windowClass ); windowClass.style = 0; windowClass.lpfnWndProc = WindowProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = hInstance; windowClass.hIcon = NULL; windowClass.hCursor = NULL; windowClass.hbrBackground = NULL; windowClass.lpszMenuName = NULL; windowClass.lpszClassName = L"HeliumTestAppClass"; windowClass.hIconSm = NULL; HELIUM_VERIFY( RegisterClassEx( &windowClass ) ); WindowData windowData; windowData.hMainWnd = NULL; windowData.hSubWnd = NULL; windowData.bProcessMessages = true; windowData.bShutdownRendering = false; windowData.resultCode = 0; DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU; RECT windowRect; windowRect.left = 0; windowRect.top = 0; windowRect.right = static_cast< LONG >( displayWidth ); windowRect.bottom = static_cast< LONG >( displayHeight ); HELIUM_VERIFY( AdjustWindowRect( &windowRect, dwStyle, FALSE ) ); HWND hMainWnd = ::CreateWindowW( L"HeliumTestAppClass", L"Helium TestApp", dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, hInstance, NULL ); HELIUM_ASSERT( hMainWnd ); windowRect.left = 0; windowRect.top = 0; windowRect.right = static_cast< LONG >( displayWidth ); windowRect.bottom = static_cast< LONG >( displayHeight ); HELIUM_VERIFY( AdjustWindowRect( &windowRect, dwStyle, FALSE ) ); #if MULTI_WINDOW HWND hSubWnd = ::CreateWindowW( L"HeliumTestAppClass", L"Helium TestApp (second view)", dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, hInstance, NULL ); HELIUM_ASSERT( hSubWnd ); #endif windowData.hMainWnd = hMainWnd; SetWindowLongPtr( hMainWnd, GWLP_USERDATA, reinterpret_cast< LONG_PTR >( &windowData ) ); ShowWindow( hMainWnd, nCmdShow ); UpdateWindow( hMainWnd ); #if MULTI_WINDOW windowData.hSubWnd = hSubWnd; SetWindowLongPtr( hSubWnd, GWLP_USERDATA, reinterpret_cast< LONG_PTR >( &windowData ) ); ShowWindow( hSubWnd, nCmdShow ); UpdateWindow( hSubWnd ); #endif HELIUM_VERIFY( D3D9Renderer::CreateStaticInstance() ); Renderer* pRenderer = Renderer::GetStaticInstance(); HELIUM_ASSERT( pRenderer ); pRenderer->Initialize(); Renderer::ContextInitParameters contextInitParams; contextInitParams.pWindow = hMainWnd; contextInitParams.displayWidth = displayWidth; contextInitParams.displayHeight = displayHeight; contextInitParams.bVsync = bVsync; HELIUM_VERIFY( pRenderer->CreateMainContext( contextInitParams ) ); #if MULTI_WINDOW contextInitParams.pWindow = hSubWnd; RRenderContextPtr spSubRenderContext = pRenderer->CreateSubContext( contextInitParams ); HELIUM_ASSERT( spSubRenderContext ); #endif Input::Initialize(&hMainWnd, false); { AssetPath prePassShaderPath; HELIUM_VERIFY( prePassShaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING TXT( "Shaders" ) HELIUM_OBJECT_PATH_CHAR_STRING TXT( "PrePass.hlsl" ) ) ); AssetPtr spPrePassShader; HELIUM_VERIFY( AssetLoader::GetStaticInstance()->LoadObject( prePassShaderPath, spPrePassShader ) ); HELIUM_ASSERT( spPrePassShader.Get() ); } RenderResourceManager& rRenderResourceManager = RenderResourceManager::GetStaticInstance(); rRenderResourceManager.Initialize(); rRenderResourceManager.UpdateMaxViewportSize( displayWidth, displayHeight ); //// Create a scene definition SceneDefinitionPtr spSceneDefinition; gAssetLoader->LoadObject( AssetPath( TXT( "/ExampleGames/Empty/Scenes/TestScene:SceneDefinition" ) ), spSceneDefinition ); EntityDefinitionPtr spEntityDefinition; gAssetLoader->LoadObject( AssetPath( TXT( "/ExampleGames/Empty/Scenes/TestScene:TestBull_Entity" ) ), spEntityDefinition ); DynamicDrawer& rDynamicDrawer = DynamicDrawer::GetStaticInstance(); HELIUM_VERIFY( rDynamicDrawer.Initialize() ); RRenderContextPtr spMainRenderContext = pRenderer->GetMainContext(); HELIUM_ASSERT( spMainRenderContext ); WorldManager& rWorldManager = WorldManager::GetStaticInstance(); HELIUM_VERIFY( rWorldManager.Initialize() ); // Create a world WorldPtr spWorld( rWorldManager.CreateWorld( spSceneDefinition ) ); HELIUM_ASSERT( spWorld ); HELIUM_TRACE( TraceLevels::Info, TXT( "Created world \"%s\".\n" ), *spSceneDefinition->GetPath().ToString() ); //Slice *pRootSlice = spWorld->GetRootSlice(); //Entity *pEntity = pRootSlice->CreateEntity(spEntityDefinition); GraphicsScene* pGraphicsScene = spWorld->GetComponents().GetFirst<GraphicsManagerComponent>()->GetGraphicsScene(); HELIUM_ASSERT( pGraphicsScene ); GraphicsSceneView* pMainSceneView = NULL; if( pGraphicsScene ) { uint32_t mainSceneViewId = pGraphicsScene->AllocateSceneView(); if( IsValid( mainSceneViewId ) ) { float32_t aspectRatio = static_cast< float32_t >( displayWidth ) / static_cast< float32_t >( displayHeight ); RSurface* pDepthStencilSurface = rRenderResourceManager.GetDepthStencilSurface(); HELIUM_ASSERT( pDepthStencilSurface ); pMainSceneView = pGraphicsScene->GetSceneView( mainSceneViewId ); HELIUM_ASSERT( pMainSceneView ); pMainSceneView->SetRenderContext( spMainRenderContext ); pMainSceneView->SetDepthStencilSurface( pDepthStencilSurface ); pMainSceneView->SetAspectRatio( aspectRatio ); pMainSceneView->SetViewport( 0, 0, displayWidth, displayHeight ); pMainSceneView->SetClearColor( Color( 0x00202020 ) ); //spMainCamera->SetSceneViewId( mainSceneViewId ); #if MULTI_WINDOW uint32_t subSceneViewId = pGraphicsScene->AllocateSceneView(); if( IsValid( subSceneViewId ) ) { GraphicsSceneView* pSubSceneView = pGraphicsScene->GetSceneView( subSceneViewId ); HELIUM_ASSERT( pSubSceneView ); pSubSceneView->SetRenderContext( spSubRenderContext ); pSubSceneView->SetDepthStencilSurface( pDepthStencilSurface ); pSubSceneView->SetAspectRatio( aspectRatio ); pSubSceneView->SetViewport( 0, 0, displayWidth, displayHeight ); pSubSceneView->SetClearColor( Color( 0x00202020 ) ); //spSubCamera->SetSceneViewId( subSceneViewId ); } #endif } #if !HELIUM_RELEASE && !HELIUM_PROFILE BufferedDrawer& rSceneDrawer = pGraphicsScene->GetSceneBufferedDrawer(); rSceneDrawer.DrawScreenText( 20, 20, String( TXT( "CACHING" ) ), Color( 0xff00ff00 ), RenderResourceManager::DEBUG_FONT_SIZE_LARGE ); rSceneDrawer.DrawScreenText( 21, 20, String( TXT( "CACHING" ) ), Color( 0xff00ff00 ), RenderResourceManager::DEBUG_FONT_SIZE_LARGE ); //rSceneDrawer.Draw //Helium::DynamicDrawer &drawer = DynamicDrawer::GetStaticInstance(); //drawer. #endif } rWorldManager.Update(); float time = 0.0f; #if MULTI_WINDOW spSubRenderContext.Release(); #endif spMainRenderContext.Release(); Helium::StrongPtr<Helium::Texture2d> texture; gAssetLoader->LoadObject( AssetPath( TXT( "/Textures:Triangle.png" ) ), texture); Helium::RTexture2d *rTexture2d = texture->GetRenderResource2d(); while( windowData.bProcessMessages ) { #if GRAPHICS_SCENE_BUFFERED_DRAWER BufferedDrawer& rSceneDrawer = pGraphicsScene->GetSceneBufferedDrawer(); rSceneDrawer.DrawScreenText( 20, 20, String( TXT( "RUNNING" ) ), Color( 0xffffffff ), RenderResourceManager::DEBUG_FONT_SIZE_LARGE ); rSceneDrawer.DrawScreenText( 21, 20, String( TXT( "RUNNING" ) ), Color( 0xffffffff ), RenderResourceManager::DEBUG_FONT_SIZE_LARGE ); time += 0.01f; DynamicArray<SimpleVertex> verticesU; verticesU.New( -100.0f, -100.0f, 750.0f ); verticesU.New( 100.0f, -100.0f, 750.0f ); verticesU.New( 100.0f, 100.0f, 750.0f ); verticesU.New( -100.0f, 100.0f, 750.0f ); rSceneDrawer.DrawLineList( verticesU.GetData(), static_cast<uint32_t>( verticesU.GetSize() ) ); DynamicArray<SimpleTexturedVertex> verticesT; verticesT.New( Simd::Vector3( -100.0f, 100.0f, 750.0f ), Simd::Vector2( 0.0f, 0.0f ) ); verticesT.New( Simd::Vector3( 100.0f, 100.0f, 750.0f ), Simd::Vector2( 1.0f, 0.0f ) ); verticesT.New( Simd::Vector3( -100.0f, -100.0f, 750.0f ), Simd::Vector2( 0.0f, 1.0f ) ); verticesT.New( Simd::Vector3( 100.0f, -100.0f, 750.0f ), Simd::Vector2( 1.0f, 1.0f ) ); //rSceneDrawer.DrawTextured( // RENDERER_PRIMITIVE_TYPE_TRIANGLE_STRIP, // verticesT.GetData(), // verticesT.GetSize(), // NULL, // 2, // rTexture2d, Helium::Color(1.0f, 1.0f, 1.0f, 1.0f), Helium::RenderResourceManager::RASTERIZER_STATE_DEFAULT, Helium::RenderResourceManager::DEPTH_STENCIL_STATE_NONE); //rSceneDrawer.DrawTexturedQuad(rTexture2d); Helium::Simd::Matrix44 transform = Helium::Simd::Matrix44::IDENTITY; Simd::Vector3 location(0.0f, 400.0f, 0.0f); Simd::Quat rotation(Helium::Simd::Vector3::BasisZ, time); Simd::Vector3 scale(1000.0f, 1000.0f, 1000.0f); transform.SetRotationTranslationScaling(rotation, location, scale); rSceneDrawer.DrawTexturedQuad(rTexture2d, transform, Simd::Vector2(0.0f, 0.0f), Simd::Vector2(0.5f, 0.5f)); #endif //Helium::Simd::Vector3 up = Simd::Vector3::BasisY; ////Helium::Simd::Vector3 eye(5000.0f * sin(time), 0.0f, 5000.0f * cos(time)); //Helium::Simd::Vector3 eye(0.0f, 0.0f, -1000.0f); //Helium::Simd::Vector3 forward = Simd::Vector3::Zero - eye; //forward.Normalize(); ////pMainSceneView->SetClearColor( Color( 0xffffffff ) ); //pMainSceneView->SetView(eye, forward, up); if (Input::IsKeyDown(Input::KeyCodes::KC_A)) { HELIUM_TRACE( TraceLevels::Info, TXT( "A is down" ) ); } if (Input::IsKeyDown(Input::KeyCodes::KC_ESCAPE)) { HELIUM_TRACE( TraceLevels::Info, TXT( "Exiting" ) ); break; } MSG message; if( PeekMessage( &message, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &message ); DispatchMessage( &message ); if( windowData.bShutdownRendering ) { if( spWorld ) { spWorld->Shutdown(); } spWorld.Release(); WorldManager::DestroyStaticInstance(); spSceneDefinition.Release(); spEntityDefinition.Release(); DynamicDrawer::DestroyStaticInstance(); RenderResourceManager::DestroyStaticInstance(); Renderer::DestroyStaticInstance(); break; } if( message.message == WM_QUIT ) { windowData.bProcessMessages = false; windowData.resultCode = static_cast< int >( message.wParam ); resultCode = static_cast< int >( message.wParam ); break; } } rWorldManager.Update(); #if GRAPHICS_SCENE_BUFFERED_DRAWER if( pGraphicsScene ) { BufferedDrawer& rSceneDrawer = pGraphicsScene->GetSceneBufferedDrawer(); rSceneDrawer.DrawScreenText( 20, 20, String( TXT( "Debug text test!" ) ), Color( 0xffffffff ) ); } #endif } if( spWorld ) { spWorld->Shutdown(); } spWorld.Release(); } WorldManager::DestroyStaticInstance(); DynamicDrawer::DestroyStaticInstance(); RenderResourceManager::DestroyStaticInstance(); Helium::Input::Cleanup(); Renderer::DestroyStaticInstance(); JobManager::DestroyStaticInstance(); Config::DestroyStaticInstance(); #if HELIUM_TOOLS AssetPreprocessor::DestroyStaticInstance(); #endif AssetLoader::DestroyStaticInstance(); CacheManager::DestroyStaticInstance(); Helium::Components::Cleanup(); Reflect::Cleanup(); AssetType::Shutdown(); Asset::Shutdown(); Reflect::ObjectRefCountSupport::Shutdown(); Helium::Bullet::Cleanup(); AssetPath::Shutdown(); Name::Shutdown(); FileLocations::Shutdown(); ThreadLocalStackAllocator::ReleaseMemoryHeap(); #if HELIUM_ENABLE_MEMORY_TRACKING DynamicMemoryHeap::LogMemoryStats(); ThreadLocalStackAllocator::ReleaseMemoryHeap(); #endif return resultCode; }
/// Initialize all resources provided by this manager. /// /// @see Cleanup(), PostConfigUpdate() bool RenderResourceManager::Initialize() { // Release any existing resources. Cleanup(); // Get the renderer and graphics configuration. Renderer* pRenderer = Renderer::GetInstance(); if ( !pRenderer ) { return false; } Config* pConfig = Config::GetInstance(); if ( !HELIUM_VERIFY( pConfig ) ) { return false; } StrongPtr< GraphicsConfig > spGraphicsConfig( pConfig->GetConfigObject< GraphicsConfig >( Name( "GraphicsConfig" ) ) ); if ( !spGraphicsConfig ) { HELIUM_TRACE( TraceLevels::Error, "RenderResourceManager::Initialize(): Initialization failed; missing GraphicsConfig.\n" ); return false; } // Create the standard rasterizer states. RRasterizerState::Description rasterizerStateDesc; rasterizerStateDesc.fillMode = RENDERER_FILL_MODE_SOLID; rasterizerStateDesc.cullMode = RENDERER_CULL_MODE_BACK; rasterizerStateDesc.winding = RENDERER_WINDING_CLOCKWISE; rasterizerStateDesc.depthBias = 0; rasterizerStateDesc.slopeScaledDepthBias = 0.0f; m_rasterizerStates[RASTERIZER_STATE_DEFAULT] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[RASTERIZER_STATE_DEFAULT] ); rasterizerStateDesc.cullMode = RENDERER_CULL_MODE_NONE; m_rasterizerStates[RASTERIZER_STATE_DOUBLE_SIDED] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[RASTERIZER_STATE_DOUBLE_SIDED] ); rasterizerStateDesc.depthBias = 1; rasterizerStateDesc.slopeScaledDepthBias = 2.0f; m_rasterizerStates[RASTERIZER_STATE_SHADOW_DEPTH] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[RASTERIZER_STATE_SHADOW_DEPTH] ); rasterizerStateDesc.depthBias = 0; rasterizerStateDesc.slopeScaledDepthBias = 0.0f; rasterizerStateDesc.fillMode = RENDERER_FILL_MODE_WIREFRAME; m_rasterizerStates[RASTERIZER_STATE_WIREFRAME_DOUBLE_SIDED] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[RASTERIZER_STATE_WIREFRAME_DOUBLE_SIDED] ); rasterizerStateDesc.cullMode = RENDERER_CULL_MODE_BACK; m_rasterizerStates[RASTERIZER_STATE_WIREFRAME] = pRenderer->CreateRasterizerState( rasterizerStateDesc ); HELIUM_ASSERT( m_rasterizerStates[RASTERIZER_STATE_WIREFRAME] ); // Create the standard blend states. RBlendState::Description blendStateDesc; blendStateDesc.bBlendEnable = false; m_blendStates[BLEND_STATE_OPAQUE] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[BLEND_STATE_OPAQUE] ); blendStateDesc.colorWriteMask = 0; m_blendStates[BLEND_STATE_NO_COLOR] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[BLEND_STATE_NO_COLOR] ); blendStateDesc.colorWriteMask = RENDERER_COLOR_WRITE_MASK_FLAG_ALL; blendStateDesc.bBlendEnable = true; blendStateDesc.sourceFactor = RENDERER_BLEND_FACTOR_SRC_ALPHA; blendStateDesc.destinationFactor = RENDERER_BLEND_FACTOR_INV_SRC_ALPHA; blendStateDesc.function = RENDERER_BLEND_FUNCTION_ADD; m_blendStates[BLEND_STATE_TRANSPARENT] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[BLEND_STATE_TRANSPARENT] ); blendStateDesc.sourceFactor = RENDERER_BLEND_FACTOR_ONE; blendStateDesc.destinationFactor = RENDERER_BLEND_FACTOR_ONE; m_blendStates[BLEND_STATE_ADDITIVE] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[BLEND_STATE_ADDITIVE] ); blendStateDesc.function = RENDERER_BLEND_FUNCTION_REVERSE_SUBTRACT; m_blendStates[BLEND_STATE_SUBTRACTIVE] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[BLEND_STATE_SUBTRACTIVE] ); blendStateDesc.sourceFactor = RENDERER_BLEND_FACTOR_DEST_COLOR; blendStateDesc.destinationFactor = RENDERER_BLEND_FACTOR_ZERO; blendStateDesc.function = RENDERER_BLEND_FUNCTION_ADD; m_blendStates[BLEND_STATE_MODULATE] = pRenderer->CreateBlendState( blendStateDesc ); HELIUM_ASSERT( m_blendStates[BLEND_STATE_MODULATE] ); // Create the standard depth/stencil states. RDepthStencilState::Description depthStateDesc; depthStateDesc.stencilWriteMask = 0; depthStateDesc.bStencilTestEnable = false; depthStateDesc.depthFunction = RENDERER_COMPARE_FUNCTION_LESS_EQUAL; depthStateDesc.bDepthTestEnable = true; depthStateDesc.bDepthWriteEnable = true; m_depthStencilStates[DEPTH_STENCIL_STATE_DEFAULT] = pRenderer->CreateDepthStencilState( depthStateDesc ); HELIUM_ASSERT( m_depthStencilStates[DEPTH_STENCIL_STATE_DEFAULT] ); depthStateDesc.bDepthWriteEnable = false; m_depthStencilStates[DEPTH_STENCIL_STATE_TEST_ONLY] = pRenderer->CreateDepthStencilState( depthStateDesc ); HELIUM_ASSERT( m_depthStencilStates[DEPTH_STENCIL_STATE_TEST_ONLY] ); depthStateDesc.bDepthTestEnable = false; m_depthStencilStates[DEPTH_STENCIL_STATE_NONE] = pRenderer->CreateDepthStencilState( depthStateDesc ); HELIUM_ASSERT( m_depthStencilStates[DEPTH_STENCIL_STATE_NONE] ); // Create the standard sampler states that are not dependent on configuration settings. RSamplerState::Description samplerStateDesc; samplerStateDesc.filter = RENDERER_TEXTURE_FILTER_MIN_POINT_MAG_POINT_MIP_POINT; samplerStateDesc.addressModeW = RENDERER_TEXTURE_ADDRESS_MODE_CLAMP; samplerStateDesc.mipLodBias = 0; samplerStateDesc.maxAnisotropy = spGraphicsConfig->GetMaxAnisotropy(); for ( size_t addressModeIndex = 0; addressModeIndex < RENDERER_TEXTURE_ADDRESS_MODE_MAX; ++addressModeIndex ) { ERendererTextureAddressMode addressMode = static_cast<ERendererTextureAddressMode>( addressModeIndex ); samplerStateDesc.addressModeU = addressMode; samplerStateDesc.addressModeV = addressMode; samplerStateDesc.addressModeW = addressMode; m_samplerStates[TEXTURE_FILTER_POINT][addressModeIndex] = pRenderer->CreateSamplerState( samplerStateDesc ); HELIUM_ASSERT( m_samplerStates[TEXTURE_FILTER_POINT][addressModeIndex] ); } // Create the standard set of mesh vertex descriptions. RVertexDescription::Element vertexElements[6]; vertexElements[0].type = RENDERER_VERTEX_DATA_TYPE_FLOAT32_3; vertexElements[0].semantic = RENDERER_VERTEX_SEMANTIC_POSITION; vertexElements[0].semanticIndex = 0; vertexElements[0].bufferIndex = 0; vertexElements[1].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[1].semantic = RENDERER_VERTEX_SEMANTIC_COLOR; vertexElements[1].semanticIndex = 0; vertexElements[1].bufferIndex = 0; vertexElements[2].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[2].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[2].semanticIndex = 0; vertexElements[2].bufferIndex = 0; vertexElements[3].type = RENDERER_VERTEX_DATA_TYPE_FLOAT32_2; vertexElements[3].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[3].semanticIndex = 1; vertexElements[3].bufferIndex = 0; m_spSimpleVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 2 ); HELIUM_ASSERT( m_spSimpleVertexDescription ); m_spSimpleTexturedVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 3 ); HELIUM_ASSERT( m_spSimpleTexturedVertexDescription ); m_spProjectedVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 4 ); HELIUM_ASSERT( m_spProjectedVertexDescription ); vertexElements[1].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[1].semantic = RENDERER_VERTEX_SEMANTIC_NORMAL; vertexElements[1].semanticIndex = 0; vertexElements[1].bufferIndex = 0; vertexElements[2].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[2].semantic = RENDERER_VERTEX_SEMANTIC_TANGENT; vertexElements[2].semanticIndex = 0; vertexElements[2].bufferIndex = 0; vertexElements[3].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[3].semantic = RENDERER_VERTEX_SEMANTIC_COLOR; vertexElements[3].semanticIndex = 0; vertexElements[3].bufferIndex = 0; vertexElements[4].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[4].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[4].semanticIndex = 0; vertexElements[4].bufferIndex = 0; vertexElements[5].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[5].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[5].semanticIndex = 1; vertexElements[5].bufferIndex = 0; m_staticMeshVertexDescriptions[0] = pRenderer->CreateVertexDescription( vertexElements, 5 ); HELIUM_ASSERT( m_staticMeshVertexDescriptions[0] ); m_staticMeshVertexDescriptions[1] = pRenderer->CreateVertexDescription( vertexElements, 6 ); HELIUM_ASSERT( m_staticMeshVertexDescriptions[1] ); vertexElements[1].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[1].semantic = RENDERER_VERTEX_SEMANTIC_BLENDWEIGHT; vertexElements[1].semanticIndex = 0; vertexElements[1].bufferIndex = 0; vertexElements[2].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4; vertexElements[2].semantic = RENDERER_VERTEX_SEMANTIC_BLENDINDICES; vertexElements[2].semanticIndex = 0; vertexElements[2].bufferIndex = 0; vertexElements[3].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[3].semantic = RENDERER_VERTEX_SEMANTIC_NORMAL; vertexElements[3].semanticIndex = 0; vertexElements[3].bufferIndex = 0; vertexElements[4].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[4].semantic = RENDERER_VERTEX_SEMANTIC_TANGENT; vertexElements[4].semanticIndex = 0; vertexElements[4].bufferIndex = 0; vertexElements[5].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[5].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[5].semanticIndex = 0; vertexElements[5].bufferIndex = 0; m_spSkinnedMeshVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 6 ); HELIUM_ASSERT( m_spSkinnedMeshVertexDescription ); vertexElements[0].type = RENDERER_VERTEX_DATA_TYPE_FLOAT32_2; vertexElements[0].semantic = RENDERER_VERTEX_SEMANTIC_POSITION; vertexElements[0].semanticIndex = 0; vertexElements[0].bufferIndex = 0; vertexElements[1].type = RENDERER_VERTEX_DATA_TYPE_UINT8_4_NORM; vertexElements[1].semantic = RENDERER_VERTEX_SEMANTIC_COLOR; vertexElements[1].semanticIndex = 0; vertexElements[1].bufferIndex = 0; vertexElements[2].type = RENDERER_VERTEX_DATA_TYPE_FLOAT16_2; vertexElements[2].semantic = RENDERER_VERTEX_SEMANTIC_TEXCOORD; vertexElements[2].semanticIndex = 0; vertexElements[2].bufferIndex = 0; m_spScreenVertexDescription = pRenderer->CreateVertexDescription( vertexElements, 3 ); HELIUM_ASSERT( m_spScreenVertexDescription ); // Create configuration-dependent render resources. PostConfigUpdate(); // Attempt to load the depth-only pre-pass shader. // TODO: XXX TMC: Migrate to a more data-driven solution. AssetLoader* pAssetLoader = AssetLoader::GetInstance(); HELIUM_ASSERT( pAssetLoader ); #ifdef HELIUM_DIRECT3D AssetPath prePassShaderPath; HELIUM_VERIFY( prePassShaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING "Shaders" HELIUM_OBJECT_PATH_CHAR_STRING "PrePass.hlsl" ) ); AssetPtr spPrePassShader; HELIUM_VERIFY( pAssetLoader->LoadObject( prePassShaderPath, spPrePassShader ) ); Shader* pPrePassShader = Reflect::SafeCast< Shader >( spPrePassShader.Get() ); if ( HELIUM_VERIFY( pPrePassShader ) ) { size_t loadId = pPrePassShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if ( IsValid( loadId ) ) { while ( !pPrePassShader->TryFinishLoadVariant( loadId, m_spPrePassVertexShader ) ) { pAssetLoader->Tick(); } } } // Attempt to load the simple world-space, simple screen-space, and screen-space text shaders. // TODO: XXX TMC: Migrate to a more data-driven solution. AssetPath shaderPath; HELIUM_VERIFY( shaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING "Shaders" HELIUM_OBJECT_PATH_CHAR_STRING "Simple.hlsl" ) ); AssetPtr spShader; HELIUM_VERIFY( pAssetLoader->LoadObject( shaderPath, spShader ) ); Shader* pShader = Reflect::SafeCast< Shader >( spShader.Get() ); if ( HELIUM_VERIFY( pShader ) ) { size_t loadId = pShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if ( IsValid( loadId ) ) { while ( !pShader->TryFinishLoadVariant( loadId, m_spSimpleWorldSpaceVertexShader ) ) { pAssetLoader->Tick(); } } loadId = pShader->BeginLoadVariant( RShader::TYPE_PIXEL, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if ( IsValid( loadId ) ) { while ( !pShader->TryFinishLoadVariant( loadId, m_spSimpleWorldSpacePixelShader ) ) { pAssetLoader->Tick(); } } } HELIUM_VERIFY( shaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING "Shaders" HELIUM_OBJECT_PATH_CHAR_STRING "ScreenSpaceTexture.hlsl" ) ); HELIUM_VERIFY( pAssetLoader->LoadObject( shaderPath, spShader ) ); pShader = Reflect::SafeCast< Shader >( spShader.Get() ); if ( HELIUM_VERIFY( pShader ) ) { size_t loadId = pShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if ( IsValid( loadId ) ) { while ( !pShader->TryFinishLoadVariant( loadId, m_spSimpleScreenSpaceVertexShader ) ) { pAssetLoader->Tick(); } } loadId = pShader->BeginLoadVariant( RShader::TYPE_PIXEL, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if ( IsValid( loadId ) ) { while ( !pShader->TryFinishLoadVariant( loadId, m_spSimpleScreenSpacePixelShader ) ) { pAssetLoader->Tick(); } } } HELIUM_VERIFY( shaderPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING "Shaders" HELIUM_OBJECT_PATH_CHAR_STRING "ScreenText.hlsl" ) ); HELIUM_VERIFY( pAssetLoader->LoadObject( shaderPath, spShader ) ); pShader = Reflect::SafeCast< Shader >( spShader.Get() ); if ( HELIUM_VERIFY( pShader ) ) { size_t loadId = pShader->BeginLoadVariant( RShader::TYPE_VERTEX, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if ( IsValid( loadId ) ) { while ( !pShader->TryFinishLoadVariant( loadId, m_spScreenTextVertexShader ) ) { pAssetLoader->Tick(); } } loadId = pShader->BeginLoadVariant( RShader::TYPE_PIXEL, 0 ); HELIUM_ASSERT( IsValid( loadId ) ); if ( IsValid( loadId ) ) { while ( !pShader->TryFinishLoadVariant( loadId, m_spScreenTextPixelShader ) ) { pAssetLoader->Tick(); } } } // Attempt to load the debug fonts. // TODO: XXX TMC: Migrate to a more data-driven solution. AssetPath fontPath; AssetPtr spFont; HELIUM_VERIFY( fontPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING "Fonts" HELIUM_OBJECT_PATH_CHAR_STRING "DebugSmall" ) ); HELIUM_VERIFY( pAssetLoader->LoadObject( fontPath, spFont ) ); m_debugFonts[DEBUG_FONT_SIZE_SMALL] = Reflect::SafeCast< Font >( spFont.Get() ); spFont.Release(); HELIUM_VERIFY( fontPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING "Fonts" HELIUM_OBJECT_PATH_CHAR_STRING "DebugMedium" ) ); HELIUM_VERIFY( pAssetLoader->LoadObject( fontPath, spFont ) ); m_debugFonts[DEBUG_FONT_SIZE_MEDIUM] = Reflect::SafeCast< Font >( spFont.Get() ); spFont.Release(); HELIUM_VERIFY( fontPath.Set( HELIUM_PACKAGE_PATH_CHAR_STRING "Fonts" HELIUM_OBJECT_PATH_CHAR_STRING "DebugLarge" ) ); HELIUM_VERIFY( pAssetLoader->LoadObject( fontPath, spFont ) ); m_debugFonts[DEBUG_FONT_SIZE_LARGE] = Reflect::SafeCast< Font >( spFont.Get() ); spFont.Release(); #endif return true; }
/// Initialize this package loader. /// /// @param[in] packagePath Asset path of the package to load. /// /// @return True if this loader was initialized successfully, false if not. /// /// @see Shutdown() bool LoosePackageLoader::Initialize( AssetPath packagePath ) { Shutdown(); // Make sure the path represents a package. if( packagePath.IsEmpty() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::Initialize(): Empty package path specified.\n" ) ); return false; } HELIUM_TRACE( TraceLevels::Debug, TXT( "LoosePackageLoader::Initialize(): Initializing loader for package \"%s\".\n" ), *packagePath.ToString() ); if( !packagePath.IsPackage() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::Initialize(): \"%s\" does not represent a package path.\n" ), *packagePath.ToString() ); return false; } // Store the package path. m_packagePath = packagePath; // Attempt to locate the specified package if it already happens to exist. m_spPackage = Asset::Find< Package >( packagePath ); Package* pPackage = m_spPackage; if( pPackage ) { if( pPackage->GetLoader() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::Initialize(): Package \"%s\" already has a loader.\n" ), *packagePath.ToString() ); m_spPackage.Release(); return false; } pPackage->SetLoader( this ); } else { // Make sure we don't have a name clash with a non-package object. AssetPtr spObject( Asset::FindObject( packagePath ) ); if( spObject ) { HELIUM_ASSERT( !spObject->IsPackage() ); HELIUM_TRACE( TraceLevels::Error, ( TXT( "PackageLoader::Initialize(): Package loader cannot be initialized for \"%s\", as an " ) TXT( "object with the same name exists that is not a package.\n" ) ), *packagePath.ToString() ); return false; } } // Build the package file path. If the package is a user configuration package, use the user data directory, // otherwise use the global data directory. Config& rConfig = Config::GetStaticInstance(); FilePath dataDirectory; if ( !FileLocations::GetDataDirectory( dataDirectory ) ) { HELIUM_TRACE( TraceLevels::Error, TXT( "PackageLoader::Initialize(): Could not obtain user data directory." ) ); return false; } // Set up to read the TOC (which may not exist) //SetInvalid( m_packageTocFileSize ); // First do this check without a trailing "/" so that FilePath has to actually look at the file system FilePath package_dir = dataDirectory + packagePath.ToFilePathString().GetData(); if (!package_dir.Exists()) { // Some packages like types or uninitialized user config packages may not exist on file system m_packageDirPath = package_dir + TXT("/"); return true; } if (!package_dir.IsDirectory()) { // Packages should not be files return false; } // But internally we will store this m_packageDirPath = package_dir + TXT("/"); return true; }
/// Begin asynchronous pre-loading of package information. /// /// @see TryFinishPreload() bool LoosePackageLoader::BeginPreload() { HELIUM_ASSERT( !m_startPreloadCounter ); HELIUM_ASSERT( !m_preloadedCounter ); HELIUM_ASSERT( IsInvalid( m_parentPackageLoadId ) ); // Load the parent package if we need to create the current package. if( !m_spPackage ) { AssetPath parentPackagePath = m_packagePath.GetParent(); if( !parentPackagePath.IsEmpty() ) { AssetLoader* pAssetLoader = AssetLoader::GetStaticInstance(); HELIUM_ASSERT( pAssetLoader ); m_parentPackageLoadId = pAssetLoader->BeginLoadObject( parentPackagePath ); HELIUM_ASSERT( IsValid( m_parentPackageLoadId ) ); } } AsyncLoader &rAsyncLoader = AsyncLoader::GetStaticInstance(); if ( !m_packageDirPath.Exists() ) { HELIUM_TRACE( TraceLevels::Warning, "LoosePackageLoader::BeginPreload - Package physical path '%s' does not exist\n", m_packageDirPath.c_str()); } else if ( !m_packageDirPath.IsDirectory() ) { HELIUM_TRACE( TraceLevels::Warning, "LoosePackageLoader::BeginPreload - Package physical path '%s' is not a directory\n", m_packageDirPath.c_str()); } else { DirectoryIterator packageDirectory( m_packageDirPath ); HELIUM_TRACE( TraceLevels::Info, TXT(" LoosePackageLoader::BeginPreload - Issuing read requests for all files in %s\n"), m_packageDirPath.c_str() ); for( ; !packageDirectory.IsDone(); packageDirectory.Next() ) { const DirectoryIteratorItem& item = packageDirectory.GetItem(); #if HELIUM_TOOLS if ( item.m_Path.IsDirectory() ) { AssetPath packagePath; std::string name = item.m_Path.DirectoryAsVector().back(); packagePath.Set( Name( name.c_str() ), true, m_packagePath ); m_childPackagePaths.Add( packagePath ); HELIUM_TRACE( TraceLevels::Info, TXT("- Skipping directory [%s]\n"), item.m_Path.c_str(), item.m_Path.Extension().c_str() ); } else #endif if ( item.m_Path.Extension() == Persist::ArchiveExtensions[ Persist::ArchiveTypes::Json ] ) { HELIUM_TRACE( TraceLevels::Info, TXT("- Reading file [%s]\n"), item.m_Path.c_str() ); FileReadRequest *request = m_fileReadRequests.New(); request->expectedSize = item.m_Size; HELIUM_ASSERT( item.m_Size < UINT32_MAX ); // Create a buffer for the file to be read into temporarily request->pLoadBuffer = DefaultAllocator().Allocate( static_cast< size_t > ( item.m_Size ) + 1 ); static_cast< char* >( request->pLoadBuffer )[ static_cast< size_t > ( item.m_Size ) ] = '\0'; // for efficiency parsing text files HELIUM_ASSERT( request->pLoadBuffer ); // Queue up the read request->asyncLoadId = rAsyncLoader.QueueRequest( request->pLoadBuffer, String( item.m_Path.c_str() ), 0, static_cast< size_t >( item.m_Size ) ); HELIUM_ASSERT( IsValid( request->asyncLoadId ) ); request->filePath = item.m_Path; request->fileTimestamp = item.m_ModTime; } else { HELIUM_TRACE( TraceLevels::Info, TXT("- Skipping file [%s] (Extension is %s)\n"), item.m_Path.c_str(), item.m_Path.Extension().c_str() ); } } } AtomicExchangeRelease( m_startPreloadCounter, 1 ); return true; }
/// Update processing of object property preloading for a given load request. /// /// @param[in] pRequest Load request to process. /// /// @return True if object property preloading for the given load request has completed, false if not. bool LoosePackageLoader::TickDeserialize( LoadRequest* pRequest ) { HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( !( pRequest->flags & LOAD_FLAG_PROPERTY_PRELOADED ) ); Asset* pObject = pRequest->spObject; HELIUM_ASSERT( pRequest->index < m_objects.GetSize() ); SerializedObjectData& rObjectData = m_objects[ pRequest->index ]; // Wait for the template and owner objects to load. AssetLoader* pAssetLoader = AssetLoader::GetStaticInstance(); HELIUM_ASSERT( pAssetLoader ); if( !rObjectData.templatePath.IsEmpty() ) { if( IsValid( pRequest->templateLoadId ) ) { if( !pAssetLoader->TryFinishLoad( pRequest->templateLoadId, pRequest->spTemplate ) ) { return false; } SetInvalid( pRequest->templateLoadId ); } if( !pRequest->spTemplate ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader: Failed to load template object for \"%s\".\n" ), *rObjectData.objectPath.ToString() ); if( pObject ) { pObject->SetFlags( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED ); pObject->ConditionalFinalizeLoad(); } pRequest->flags |= LOAD_FLAG_PRELOADED | LOAD_FLAG_ERROR; return true; } } HELIUM_ASSERT( IsInvalid( pRequest->templateLoadId ) ); Asset* pTemplate = pRequest->spTemplate; AssetPath ownerPath = rObjectData.objectPath.GetParent(); if( !ownerPath.IsEmpty() ) { if( IsValid( pRequest->ownerLoadId ) ) { if( !pAssetLoader->TryFinishLoad( pRequest->ownerLoadId, pRequest->spOwner ) ) { return false; } SetInvalid( pRequest->ownerLoadId ); } if( !pRequest->spOwner ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader: Failed to load owner object for \"%s\".\n" ), *rObjectData.objectPath.ToString() ); if( pObject ) { pObject->SetFlags( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED ); pObject->ConditionalFinalizeLoad(); } pRequest->flags |= LOAD_FLAG_PRELOADED | LOAD_FLAG_ERROR; return true; } } HELIUM_ASSERT( IsInvalid( pRequest->ownerLoadId ) ); Asset* pOwner = pRequest->spOwner; AssetType* pType = pRequest->spType; HELIUM_ASSERT( pType ); HELIUM_ASSERT( !pOwner || pOwner->IsFullyLoaded() ); HELIUM_ASSERT( !pTemplate || pTemplate->IsFullyLoaded() ); AsyncLoader& rAsyncLoader = AsyncLoader::GetStaticInstance(); FilePath object_file_path = m_packageDirPath + *rObjectData.objectPath.GetName() + TXT( "." ) + Persist::ArchiveExtensions[ Persist::ArchiveTypes::Json ]; bool load_properties_from_file = true; size_t object_file_size = 0; if ( !IsValid( pRequest->asyncFileLoadId ) ) { if (!object_file_path.IsFile()) { if (pType->GetMetaClass()->IsType( Reflect::GetMetaClass< Resource >() )) { HELIUM_TRACE( TraceLevels::Info, TXT( "LoosePackageLoader::TickDeserialize(): No object file found for resource \"%s\". Expected file location: \"%s\". This is normal for newly added resources.\n" ), *rObjectData.objectPath.ToString(), *object_file_path); // We will allow continuing to load using all default properties. This behavior is to support dropping resources into the // data property and autogenerating objects from them. load_properties_from_file = false; } else { HELIUM_TRACE( TraceLevels::Warning, TXT( "LoosePackageLoader::TickDeserialize(): No object file found for object \"%s\". Expected file location: \"%s\"\n" ), *rObjectData.objectPath.ToString(), *object_file_path); } } else { Status status; status.Read( object_file_path.Get().c_str() ); int64_t i64_object_file_size = status.m_Size; if( i64_object_file_size == -1 ) { HELIUM_TRACE( TraceLevels::Warning, TXT( "LoosePackageLoader::TickDeserialize(): Could not get file size for object file of object \"%s\". Expected file location: \"%s\"\n" ), *rObjectData.objectPath.ToString(), *object_file_path ); } else if( i64_object_file_size == 0 ) { HELIUM_TRACE( TraceLevels::Warning, TXT( "LoosePackageLoader::TickDeserialize(): Object file \"%s\" for objct \"%s\" is empty.\n" ), *object_file_path, *rObjectData.objectPath.ToString() ); } else if( static_cast< uint64_t >( i64_object_file_size ) > static_cast< uint64_t >( ~static_cast< size_t >( 0 ) ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "LoosePackageLoader::TickDeserialize(): Object file \"%s\" exceeds the maximum size supported by " ) TXT( "the current platform (file size: %" ) PRIu64 TXT( " bytes; max supported: %" ) PRIuSZ TXT( " bytes).\n" ) ), object_file_path.c_str(), static_cast< uint64_t >( i64_object_file_size ), ~static_cast< size_t >( 0 ) ); } else { object_file_size = static_cast< size_t >(i64_object_file_size); } } if (!load_properties_from_file) { HELIUM_ASSERT(!object_file_size); } else if (!object_file_size) { pRequest->flags |= LOAD_FLAG_PRELOADED | LOAD_FLAG_ERROR; return true; } else { HELIUM_ASSERT( !pRequest->pAsyncFileLoadBuffer ); pRequest->pAsyncFileLoadBuffer = DefaultAllocator().Allocate( object_file_size ); HELIUM_ASSERT( pRequest->pAsyncFileLoadBuffer ); pRequest->asyncFileLoadBufferSize = object_file_size; pRequest->asyncFileLoadId = rAsyncLoader.QueueRequest( pRequest->pAsyncFileLoadBuffer, String(object_file_path.c_str()), 0, pRequest->asyncFileLoadBufferSize); } } size_t bytesRead = 0; if (load_properties_from_file) { HELIUM_ASSERT( IsValid( pRequest->asyncFileLoadId ) ); if ( !rAsyncLoader.TrySyncRequest( pRequest->asyncFileLoadId, bytesRead ) ) { return false; } } /////// POINT OF NO RETURN: We *will* return true after this point, and the object *will* be finished preloading, /////// for good or for bad. SetInvalid(pRequest->asyncFileLoadId); bool object_creation_failure = false; // If we already had an existing object, make sure the type and template match. if( pObject ) { const AssetType* pExistingType = pObject->GetAssetType(); HELIUM_ASSERT( pExistingType ); if( pExistingType != pType ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "LoosePackageLoader: Cannot load \"%s\" using the existing object as the types do not match " ) TXT( "(existing type: \"%s\"; serialized type: \"%s\".\n" ) ), *rObjectData.objectPath.ToString(), *pExistingType->GetName(), *pType->GetName() ); pObject->SetFlags( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED ); pObject->ConditionalFinalizeLoad(); object_creation_failure = true; } } else { bool bCreateResult = false; if (pRequest->forceReload) { // Create the object. bCreateResult = Asset::CreateObject( pRequest->spObject, pType, Name( NULL_NAME ), NULL, pTemplate ); } else { // Create the object. bCreateResult = Asset::CreateObject( pRequest->spObject, pType, rObjectData.objectPath.GetName(), pOwner, pTemplate ); } if( !bCreateResult ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader: Failed to create \"%s\" during loading.\n" ), *rObjectData.objectPath.ToString() ); object_creation_failure = true; } pObject = pRequest->spObject; HELIUM_ASSERT( pObject ); } if (load_properties_from_file && !object_creation_failure) { // Sanity checks for file load, then success path HELIUM_ASSERT( bytesRead == pRequest->asyncFileLoadBufferSize ); if( IsInvalid( bytesRead ) ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader: Failed to read the contents of object file \"%s\" in async load request \"%d\".\n" ), object_file_path.c_str(), pRequest->asyncFileLoadId ); } else if( bytesRead != pRequest->asyncFileLoadBufferSize ) { HELIUM_TRACE( TraceLevels::Warning, ( TXT( "LoosePackageLoader: Attempted to read %" ) PRIuSZ TXT( " bytes from object file \"%s\", " ) TXT( "but only %" ) PRIuSZ TXT( " bytes were read.\n" ) ), pRequest->asyncFileLoadBufferSize, object_file_path.c_str(), bytesRead ); } else { StaticMemoryStream archiveStream ( pRequest->pAsyncFileLoadBuffer, pRequest->asyncFileLoadBufferSize ); HELIUM_TRACE( TraceLevels::Info, TXT( "LoosePackageLoader: Reading %s. pResolver = %x\n"), object_file_path.c_str(), pRequest->pResolver); DynamicArray< Reflect::ObjectPtr > objects; objects.Push( pRequest->spObject.Get() ); // use existing objects Persist::ArchiveReaderJson::ReadFromStream( archiveStream, objects, pRequest->pResolver ); HELIUM_ASSERT( objects[0].Get() == pRequest->spObject.Get() ); } } if (load_properties_from_file) { DefaultAllocator().Free(pRequest->pAsyncFileLoadBuffer); pRequest->pAsyncFileLoadBuffer = NULL; pRequest->asyncFileLoadBufferSize = 0; } pRequest->flags |= LOAD_FLAG_PROPERTY_PRELOADED; if( object_creation_failure ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader: Deserialization of object \"%s\" failed.\n" ), *rObjectData.objectPath.ToString() ); pObject->SetFlags( Asset::FLAG_PRELOADED | Asset::FLAG_LINKED ); pObject->ConditionalFinalizeLoad(); pRequest->flags |= LOAD_FLAG_ERROR; } else if( !pObject->IsDefaultTemplate() ) { // If the object is a resource (not including the default template object for resource types), attempt to begin // loading any existing persistent resource data stored in the object cache. Resource* pResource = Reflect::SafeCast< Resource >( pObject ); if( pResource ) { Name objectCacheName = Name( HELIUM_ASSET_CACHE_NAME ); CacheManager& rCacheManager = CacheManager::GetStaticInstance(); Cache* pCache = rCacheManager.GetCache( objectCacheName ); HELIUM_ASSERT( pCache ); pCache->EnforceTocLoad(); const Cache::Entry* pEntry = pCache->FindEntry( rObjectData.objectPath, 0 ); if( pEntry && pEntry->size != 0 ) { HELIUM_ASSERT( IsInvalid( pRequest->persistentResourceDataLoadId ) ); HELIUM_ASSERT( !pRequest->pCachedObjectDataBuffer ); pRequest->pCachedObjectDataBuffer = static_cast< uint8_t* >( DefaultAllocator().Allocate( pEntry->size ) ); HELIUM_ASSERT( pRequest->pCachedObjectDataBuffer ); pRequest->cachedObjectDataBufferSize = pEntry->size; AsyncLoader& rAsyncLoader = AsyncLoader::GetStaticInstance(); pRequest->persistentResourceDataLoadId = rAsyncLoader.QueueRequest( pRequest->pCachedObjectDataBuffer, pCache->GetCacheFileName(), pEntry->offset, pEntry->size ); HELIUM_ASSERT( IsValid( pRequest->persistentResourceDataLoadId ) ); } } } if( IsInvalid( pRequest->persistentResourceDataLoadId ) ) { // No persistent resource data needs to be loaded. pObject->SetFlags( Asset::FLAG_PRELOADED ); pRequest->flags |= LOAD_FLAG_PERSISTENT_RESOURCE_PRELOADED; } // Asset is now preloaded. return true; }
/// @copydoc ResourceHandler::CacheResource() bool ShaderResourceHandler::CacheResource( AssetPreprocessor* pAssetPreprocessor, Resource* pResource, const String& rSourceFilePath ) { HELIUM_ASSERT( pAssetPreprocessor ); HELIUM_ASSERT( pResource ); const Shader* pShader = Reflect::AssertCast< const Shader >( pResource ); AssetPath shaderPath = pShader->GetPath(); HELIUM_TRACE( TraceLevels::Info, TXT( "ShaderResourceHandler: Caching \"%s\".\n" ), *shaderPath.ToString() ); DefaultAllocator allocator; FileStream* pSourceFileStream = FileStream::OpenFileStream( rSourceFilePath, FileStream::MODE_READ ); if( !pSourceFileStream ) { HELIUM_TRACE( TraceLevels::Error, TXT( "ShaderResourceHandler: Source file for shader resource \"%s\" failed to open properly.\n" ), *shaderPath.ToString() ); return false; } // Load the entire shader resource into memory. int64_t size64 = pSourceFileStream->GetSize(); HELIUM_ASSERT( size64 != -1 ); HELIUM_ASSERT( static_cast< uint64_t >( size64 ) <= static_cast< size_t >( -1 ) ); if( size64 > static_cast< uint64_t >( static_cast< size_t >( -1 ) ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Source file for shader resource \"%s\" is too large to fit into " ) TXT( "memory for preprocessing.\n" ) ), *shaderPath.ToString() ); delete pSourceFileStream; return false; } size_t size = static_cast< size_t >( size64 ); void* pShaderData = allocator.Allocate( size ); HELIUM_ASSERT( pShaderData ); if( !pShaderData ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Failed to allocate %" ) PRIuSZ TXT( " bytes for loading the source " ) TXT( "data of \"%s\" for preprocessing.\n" ) ), size, *shaderPath.ToString() ); delete pSourceFileStream; return false; } BufferedStream( pSourceFileStream ).Read( pShaderData, 1, size ); delete pSourceFileStream; // Parse all preprocessor toggle and selection tokens from the shader source. StrongPtr< Shader::PersistentResourceData > resourceData( new Shader::PersistentResourceData() ); const char* pLineEnd = static_cast< const char* >( pShaderData ); const char* pShaderEnd = pLineEnd + size; do { const char* pLineStart = pLineEnd; while( pLineEnd < pShaderEnd ) { char character = *pLineEnd; if( character == '\n' || character == '\r' ) { break; } ++pLineEnd; } ParseLine( shaderPath, *resourceData, pLineStart, pLineEnd ); while( pLineEnd < pShaderEnd ) { char character = *pLineEnd; if( character != '\n' && character != '\r' ) { break; } ++pLineEnd; } } while( pLineEnd < pShaderEnd ); allocator.Free( pShaderData ); // Serialize the persistent shader resource data for each platform. for( size_t platformIndex = 0; platformIndex < static_cast< size_t >( Cache::PLATFORM_MAX ); ++platformIndex ) { PlatformPreprocessor* pPreprocessor = pAssetPreprocessor->GetPlatformPreprocessor( static_cast< Cache::EPlatform >( platformIndex ) ); if( !pPreprocessor ) { continue; } Resource::PreprocessedData& rPreprocessedData = pResource->GetPreprocessedData( static_cast< Cache::EPlatform >( platformIndex ) ); SaveObjectToPersistentDataBuffer(resourceData.Get(), rPreprocessedData.persistentDataBuffer); rPreprocessedData.subDataBuffers.Resize( 0 ); rPreprocessedData.bLoaded = true; } return true; }
/// @copydoc PackageLoader::BeginLoadObject() size_t CachePackageLoader::BeginLoadObject( AssetPath path, Reflect::ObjectResolver *pResolver, bool forceReload ) { HELIUM_ASSERT( m_pCache ); HELIUM_ASSERT( !forceReload ); // Not supported // Don't load packages from the cache, but instead create them dynamically. if( path.IsPackage() ) { HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): \"%s\" is a package, resolving immediately.\n", *path.ToString() ); LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); pRequest->pEntry = NULL; pRequest->pResolver = pResolver; ResolvePackage( pRequest->spObject, path ); HELIUM_ASSERT( pRequest->spObject ); SetInvalid( pRequest->asyncLoadId ); pRequest->pAsyncLoadBuffer = NULL; pRequest->pPropertyDataBegin = NULL; pRequest->pPropertyDataEnd = NULL; pRequest->pPersistentResourceDataBegin = NULL; pRequest->pPersistentResourceDataEnd = NULL; SetInvalid( pRequest->ownerLoadIndex ); HELIUM_ASSERT( !pRequest->spOwner ); pRequest->forceReload = forceReload; pRequest->flags = LOAD_FLAG_PRELOADED; size_t requestId = m_loadRequests.Add( pRequest ); return requestId; } const Cache::Entry* pEntry = m_pCache->FindEntry( path, 0 ); if( !pEntry ) { HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): \"%s\" is not cached in this package. No load request added.\n", *path.ToString() ); return Invalid< size_t >(); } #ifndef NDEBUG size_t loadRequestSize = m_loadRequests.GetSize(); for( size_t loadRequestIndex = 0; loadRequestIndex < loadRequestSize; ++loadRequestIndex ) { if( !m_loadRequests.IsElementValid( loadRequestIndex ) ) { continue; } LoadRequest* pRequest = m_loadRequests[ loadRequestIndex ]; HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( pRequest->pEntry != pEntry ); if( pRequest->pEntry == pEntry ) { HELIUM_TRACE( TraceLevels::Error, "CachePackageLoader::BeginLoadObject(): Duplicate load request of \"%s\". No load request added.\n", *path.ToString() ); return Invalid< size_t >(); } } #endif LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); pRequest->pEntry = pEntry; pRequest->pResolver = pResolver; HELIUM_ASSERT( !pRequest->spObject ); SetInvalid( pRequest->asyncLoadId ); pRequest->pAsyncLoadBuffer = NULL; pRequest->pPropertyDataBegin = NULL; pRequest->pPropertyDataEnd = NULL; pRequest->pPersistentResourceDataBegin = NULL; pRequest->pPersistentResourceDataEnd = NULL; SetInvalid( pRequest->ownerLoadIndex ); HELIUM_ASSERT( !pRequest->spOwner ); pRequest->forceReload = forceReload; pRequest->flags = 0; // If a fully-loaded object already exists with the same name, do not attempt to re-load the object (just mark // the request as complete). pRequest->spObject = Asset::FindObject( pEntry->path ); Asset* pObject = pRequest->spObject; if( pObject && pObject->IsFullyLoaded() ) { HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): \"%s\" is already fully loaded. Bypassing load process.\n", *path.ToString() ); pRequest->flags = LOAD_FLAG_PRELOADED; } else { HELIUM_ASSERT( !pObject || !pObject->GetAnyFlagSet( Asset::FLAG_LOADED | Asset::FLAG_LINKED ) ); HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): Issuing async load of property data for \"%s\".\n", *path.ToString() ); size_t entrySize = pEntry->size; pRequest->pAsyncLoadBuffer = static_cast< uint8_t* >( DefaultAllocator().Allocate( entrySize ) ); HELIUM_ASSERT( pRequest->pAsyncLoadBuffer ); AsyncLoader* pAsyncLoader = AsyncLoader::GetInstance(); HELIUM_ASSERT( pAsyncLoader ); pRequest->asyncLoadId = pAsyncLoader->QueueRequest( pRequest->pAsyncLoadBuffer, m_pCache->GetCacheFileName(), pEntry->offset, entrySize ); HELIUM_ASSERT( IsValid( pRequest->asyncLoadId ) ); } size_t requestId = m_loadRequests.Add( pRequest ); HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): Load request for \"%s\" added (ID: %" PRIuSZ ").\n", *path.ToString(), requestId ); return requestId; }
/// Initialize this system. /// /// @param[in] rCommandLineInitialization Interface for initializing command-line parameters. /// @param[in] rMemoryHeapPreInitialization Interface for performing any necessary pre-initialization of dynamic /// memory heaps. /// @param[in] rAssetLoaderInitialization Interface for creating and initializing the main AssetLoader instance. /// Note that this must remain valid until Shutdown() is called on this /// system, as a reference to it will be held by this system. /// @param[in] rConfigInitialization Interface for initializing application configuration settings. /// @param[in] rWindowManagerInitialization Interface for creating and initializing the global window manager /// instance. /// @param[in] rRendererInitialization Interface for creating and initializing the global renderer instance. /// @param[in] pWorldType Type of World to create for the main world. If this is null, the /// actual World type will be used. bool GameSystem::Initialize( CommandLineInitialization& rCommandLineInitialization, MemoryHeapPreInitialization& rMemoryHeapPreInitialization, AssetLoaderInitialization& rAssetLoaderInitialization, ConfigInitialization& rConfigInitialization, WindowManagerInitialization& rWindowManagerInitialization, RendererInitialization& rRendererInitialization, AssetPath &rSystemDefinitionPath) { // Initialize the timer first of all, in case someone wants to use it. Timer::StaticInitialize(); // Initialize command-line parameters. bool bCommandLineInitSuccess = rCommandLineInitialization.Initialize( m_moduleName, m_arguments ); HELIUM_ASSERT( bCommandLineInitSuccess ); if( !bCommandLineInitSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Command-line initialization failed.\n" ) ); return false; } #if HELIUM_ENABLE_TRACE HELIUM_TRACE( TraceLevels::Info, TXT( "Module name: %s\n" ), *m_moduleName ); HELIUM_TRACE( TraceLevels::Info, TXT( "Command-line arguments:\n" ) ); size_t argumentCount = m_arguments.GetSize(); for( size_t argumentIndex = 0; argumentIndex < argumentCount; ++argumentIndex ) { HELIUM_TRACE( TraceLevels::Info, TXT( "* %s\n" ), *m_arguments[ argumentIndex ] ); } #endif // Initialize the async loading thread. bool bAsyncLoaderInitSuccess = AsyncLoader::GetStaticInstance().Initialize(); HELIUM_ASSERT( bAsyncLoaderInitSuccess ); if( !bAsyncLoaderInitSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Async loader initialization failed.\n" ) ); return false; } //pmd - Initialize the cache manager FilePath baseDirectory; if ( !FileLocations::GetBaseDirectory( baseDirectory ) ) { HELIUM_TRACE( TraceLevels::Error, TXT( "Could not get base directory." ) ); return false; } HELIUM_VERIFY( CacheManager::InitializeStaticInstance( baseDirectory ) ); // Initialize the reflection type registry and register Asset-based types. Reflect::Initialize(); Components::Initialize(); TaskScheduler::CalculateSchedule(); // Perform dynamic memory heap pre-initialization. rMemoryHeapPreInitialization.PreInitialize(); // Create and initialize the main AssetLoader instance. AssetLoader* pAssetLoader = rAssetLoaderInitialization.Initialize(); HELIUM_ASSERT( pAssetLoader ); if( !pAssetLoader ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Asset loader initialization failed.\n" ) ); return false; } m_pAssetLoaderInitialization = &rAssetLoaderInitialization; // Initialize system configuration. bool bConfigInitSuccess = rConfigInitialization.Initialize(); HELIUM_ASSERT( bConfigInitSuccess ); if( !bConfigInitSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Failed to initialize configuration settings.\n" ) ); return false; } if ( !rSystemDefinitionPath.IsEmpty() ) { pAssetLoader->LoadObject<SystemDefinition>( rSystemDefinitionPath, m_spSystemDefinition ); if ( !m_spSystemDefinition ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Could not find SystemDefinition. LoadObject on '%s' failed.\n" ), *rSystemDefinitionPath.ToString() ); } else { m_spSystemDefinition->Initialize(); } } // Initialize the job manager. bool bJobManagerInitSuccess = JobManager::GetStaticInstance().Initialize(); HELIUM_ASSERT( bJobManagerInitSuccess ); if( !bJobManagerInitSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Job manager initialization failed.\n" ) ); return false; } // Create and initialize the window manager (note that we need a window manager for message loop processing, so // the instance cannot be left null). bool bWindowManagerInitSuccess = rWindowManagerInitialization.Initialize(); HELIUM_ASSERT( bWindowManagerInitSuccess ); if( !bWindowManagerInitSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Window manager initialization failed.\n" ) ); return false; } // Create and initialize the renderer. bool bRendererInitSuccess = rRendererInitialization.Initialize(); HELIUM_ASSERT( bRendererInitSuccess ); if( !bRendererInitSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "GameSystem::Initialize(): Renderer initialization failed.\n" ) ); return false; } m_pRendererInitialization = &rRendererInitialization; // Initialize the world manager and main game world. WorldManager& rWorldManager = WorldManager::GetStaticInstance(); bool bWorldManagerInitSuccess = rWorldManager.Initialize(); HELIUM_ASSERT( bWorldManagerInitSuccess ); if( !bWorldManagerInitSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "World manager initialization failed.\n" ) ); return false; } // Initialization complete. return true; }
SetMappingRequest::SetMappingRequest(const AssetPath& path, const AssetHash& hash) : _path(path.trimmed()), _hash(hash) { };
RenameMappingRequest::RenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath) : _oldPath(oldPath.trimmed()), _newPath(newPath.trimmed()) { }
/// @copydoc PackageLoader::BeginLoadObject() size_t LoosePackageLoader::BeginLoadObject( AssetPath path, Reflect::ObjectResolver *pResolver, bool forceReload ) { HELIUM_TRACE( TraceLevels::Info, TXT(" LoosePackageLoader::BeginLoadObject - Loading path %s\n"), *path.ToString() ); HELIUM_TRACE( TraceLevels::Debug, TXT( "LoosePackageLoader::BeginLoadObject: Beginning load for path \"%s\".\n"), *path.ToString()); HELIUM_TRACE( TraceLevels::Debug, TXT( "LoosePackageLoader::BeginLoadObject: Beginning load for path \"%s\". pResolver = %x\n"), *path.ToString(), pResolver); // Make sure preloading has completed. HELIUM_ASSERT( m_preloadedCounter != 0 ); if( !m_preloadedCounter ) { return Invalid< size_t >(); } // If this package is requested, simply provide the (already loaded) package instance. if( path == m_packagePath ) { LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( m_spPackage ); pRequest->spObject = m_spPackage.Ptr(); SetInvalid( pRequest->index ); HELIUM_ASSERT( !pRequest->spType ); HELIUM_ASSERT( !pRequest->spTemplate ); HELIUM_ASSERT( !pRequest->spOwner ); SetInvalid( pRequest->templateLoadId ); SetInvalid( pRequest->ownerLoadId ); SetInvalid( pRequest->persistentResourceDataLoadId ); pRequest->pCachedObjectDataBuffer = NULL; pRequest->cachedObjectDataBufferSize = 0; SetInvalid( pRequest->asyncFileLoadId ); pRequest->pAsyncFileLoadBuffer = NULL; pRequest->asyncFileLoadBufferSize = 0; pRequest->pResolver = NULL; pRequest->forceReload = forceReload; pRequest->flags = LOAD_FLAG_PRELOADED; size_t requestId = m_loadRequests.Add( pRequest ); return requestId; } size_t objectIndex = FindObjectByPath( path ); size_t objectCount = GetObjectCount(); if( objectIndex >= objectCount ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::BeginLoadObject(): Failed to locate \"%s\" for loading. Verify the file exists.\n" ), *path.ToString() ); return Invalid< size_t >(); } SerializedObjectData& rObjectData = m_objects[ objectIndex ]; // Verify that the metadata was read successfully if( !rObjectData.bMetadataGood ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::BeginLoadObject(): Failed to read metadata for object \"%s\" during PackagePreload. Search log for parsing errors.\n" ), *path.ToString() ); return Invalid< size_t >(); } // Locate the type object. HELIUM_ASSERT( !rObjectData.typeName.IsEmpty() ); AssetType* pType = AssetType::Find( rObjectData.typeName ); if( !pType ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::BeginLoadObject(): Failed to locate type \"%s\" for loading object \"%s\".\n" ), *rObjectData.typeName, *path.ToString() ); HELIUM_TRACE( TraceLevels::Info, TXT( "Current registered types:\n" ) ); for ( AssetType::ConstIterator iter = AssetType::GetTypeBegin(); iter != AssetType::GetTypeEnd(); ++iter) { HELIUM_TRACE( TraceLevels::Info, TXT( " - %s\n" ), *iter->GetName() ); } return Invalid< size_t >(); } #ifndef NDEBUG size_t loadRequestSize = m_loadRequests.GetSize(); for( size_t loadRequestIndex = 0; loadRequestIndex < loadRequestSize; ++loadRequestIndex ) { if( !m_loadRequests.IsElementValid( loadRequestIndex ) ) { continue; } LoadRequest* pRequest = m_loadRequests[ loadRequestIndex ]; HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( pRequest->index != objectIndex ); if( pRequest->index == objectIndex ) { return Invalid< size_t >(); } } #endif LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( !pRequest->spObject ); pRequest->index = objectIndex; pRequest->spType = pType; HELIUM_ASSERT( !pRequest->spTemplate ); HELIUM_ASSERT( !pRequest->spOwner ); SetInvalid( pRequest->templateLoadId ); SetInvalid( pRequest->ownerLoadId ); SetInvalid( pRequest->persistentResourceDataLoadId ); pRequest->pCachedObjectDataBuffer = NULL; pRequest->cachedObjectDataBufferSize = 0; SetInvalid( pRequest->asyncFileLoadId ); pRequest->pAsyncFileLoadBuffer = NULL; pRequest->asyncFileLoadBufferSize = 0; pRequest->pResolver = pResolver; pRequest->forceReload = forceReload; pRequest->flags = 0; // If a fully-loaded object already exists with the same name, do not attempt to re-load the object (just mark // the request as complete). if ( !forceReload ) { pRequest->spObject = Asset::FindObject( path ); } Asset* pObject = pRequest->spObject; if( pObject && pObject->IsFullyLoaded() ) { pRequest->flags = LOAD_FLAG_PRELOADED; } else { HELIUM_ASSERT( !pObject || !pObject->GetAnyFlagSet( Asset::FLAG_LOADED | Asset::FLAG_LINKED ) ); // Begin loading the template and owner objects. Note that there isn't much reason to check for failure // until we tick this request, as we need to make sure any other load requests for the template/owner that // did succeed are properly synced anyway. AssetLoader* pAssetLoader = AssetLoader::GetStaticInstance(); HELIUM_ASSERT( pAssetLoader ); if( rObjectData.templatePath.IsEmpty() ) { // Make sure the template is fully loaded. Asset* pTemplate = pType->GetTemplate(); rObjectData.templatePath = pTemplate->GetPath(); if( pTemplate->IsFullyLoaded() ) { pRequest->spTemplate = pTemplate; } else { pRequest->templateLoadId = pAssetLoader->BeginLoadObject( rObjectData.templatePath ); } } else { pRequest->templateLoadId = pAssetLoader->BeginLoadObject( rObjectData.templatePath ); } AssetPath ownerPath = path.GetParent(); if( ownerPath == m_packagePath ) { // Easy check: if the owner is this package (which is likely), we don't need to load it. pRequest->spOwner = m_spPackage.Ptr(); } else if( !ownerPath.IsEmpty() ) { pRequest->ownerLoadId = pAssetLoader->BeginLoadObject( ownerPath ); } } size_t requestId = m_loadRequests.Add( pRequest ); return requestId; }
/// Parse the given shader source line for toggle and select options. /// /// @param[in] shaderPath Asset path of the shader resource being preprocessed (used for logging purposes /// only). /// @param[in] rResourceData Persistent shader resource data to update. /// @param[in] pLineStart Pointer to the first character in the line. /// @param[in] pLineEnd Pointer to the character just past the end of the line. void ShaderResourceHandler::ParseLine( AssetPath shaderPath, Shader::PersistentResourceData& rResourceData, const char* pLineStart, const char* pLineEnd ) { HELIUM_UNREF( shaderPath ); // Not used if logging is disabled. HELIUM_ASSERT( pLineStart ); HELIUM_ASSERT( pLineEnd >= pLineStart ); const char linePrefix[] = "//!"; const char toggleUserCommand[] = "@toggle"; const char selectUserCommand[] = "@select"; const char toggleSystemCommand[] = "@systoggle"; const char selectSystemCommand[] = "@sysselect"; size_t characterCount = static_cast< size_t >( pLineEnd - pLineStart ); // Only process lines that start with the special comment prefix. if( characterCount < HELIUM_ARRAY_COUNT( linePrefix ) - 1 || CompareString( pLineStart, linePrefix, HELIUM_ARRAY_COUNT( linePrefix ) - 1 ) != 0 ) { return; } pLineStart += HELIUM_ARRAY_COUNT( linePrefix ) - 1; characterCount -= HELIUM_ARRAY_COUNT( linePrefix ) - 1; // Split the line based on groups of whitespaces. CharString line( pLineStart, characterCount ); DynamicArray< CharString > splitLine; line.Split( splitLine, " \t\v\f", Invalid< size_t >(), true ); // Ignore the first split if it's empty (will occur if the command is preceded by whitespaces). size_t splitCount = splitLine.GetSize(); if( splitCount > 0 && splitLine[ 0 ].IsEmpty() ) { splitLine.Remove( 0 ); --splitCount; } // We need at least 2 splits (command and at least one command parameter). if( splitCount < 2 ) { return; } // Process the command. DynamicArray< CharString > splitCommand; splitLine[ 0 ].Split( splitCommand, '_' ); size_t commandSplitCount = splitCommand.GetSize(); if( commandSplitCount < 1 || commandSplitCount > 2 ) { // Invalid command format. return; } const CharString& rCommand = splitCommand[ 0 ]; bool bToggleUserCommand = ( rCommand == toggleUserCommand ); bool bSelectUserCommand = ( !bToggleUserCommand && rCommand == selectUserCommand ); bool bToggleSystemCommand = ( !( bToggleUserCommand | bSelectUserCommand ) && rCommand == toggleSystemCommand ); bool bSelectSystemCommand = ( !( bToggleUserCommand | bSelectUserCommand | bToggleSystemCommand ) && rCommand == selectSystemCommand ); if( !( bToggleUserCommand | bSelectUserCommand | bToggleSystemCommand | bSelectSystemCommand ) ) { return; } /// Make sure the option name (first parameter after the command name) is valid. String convertedString; HELIUM_VERIFY( ( StringConverter< char, char >::Convert( convertedString, splitLine[ 1 ] ) ) ); Name optionName( convertedString ); if( optionName.IsEmpty() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "ShaderResourceHandler: Skipping empty option in shader resource \"%s\".\n" ), *shaderPath.ToString() ); return; } // Make sure an existing toggle or selection option exists with the parsed option name. Shader::Options& rSystemOptions = rResourceData.GetSystemOptions(); Shader::Options& rUserOptions = rResourceData.GetUserOptions(); DynamicArray< Shader::Toggle >& rSystemToggles = rSystemOptions.GetToggles(); DynamicArray< Shader::Select >& rSystemSelects = rSystemOptions.GetSelects(); DynamicArray< Shader::Toggle >& rUserToggles = rUserOptions.GetToggles(); DynamicArray< Shader::Select >& rUserSelects = rUserOptions.GetSelects(); if( ParseLineDuplicateOptionCheck( optionName, rSystemToggles ) || ParseLineDuplicateOptionCheck( optionName, rSystemSelects ) || ParseLineDuplicateOptionCheck( optionName, rUserToggles ) || ParseLineDuplicateOptionCheck( optionName, rUserSelects ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Duplicate option name \"%s\" found in shader resource \"%s\". Only " ) TXT( "the first option will be used.\n" ) ), *optionName, *shaderPath.ToString() ); return; } // Handle shader-specific command flags (option applies to all shader types if no flags are specified). uint32_t shaderFlags = ( 1 << RShader::TYPE_MAX ) - 1; if( commandSplitCount > 1 ) { shaderFlags = 0; const CharString& rShaderFlags = splitCommand[ 1 ]; size_t shaderFlagCount = rShaderFlags.GetSize(); for( size_t flagIndex = 0; flagIndex < shaderFlagCount; ++flagIndex ) { char flagCharacter = rShaderFlags[ flagIndex ]; if( flagCharacter == 'v' ) { shaderFlags |= ( 1 << RShader::TYPE_VERTEX ); } else if( flagCharacter == 'p' ) { shaderFlags |= ( 1 << RShader::TYPE_PIXEL ); } } } // Parse the command parameters. if( bToggleUserCommand | bToggleSystemCommand ) { Shader::Toggle* pToggle = ( bToggleUserCommand ? rUserToggles : rSystemToggles ).New(); HELIUM_ASSERT( pToggle ); pToggle->name = optionName; pToggle->shaderTypeFlags = shaderFlags; if( splitCount > 2 ) { HELIUM_TRACE( TraceLevels::Warning, ( TXT( "ShaderResourceHandler: Extra tokens for toggle command \"%s\" in shader resource \"%s\" " ) TXT( "ignored.\n" ) ), *splitLine[ 1 ], *shaderPath.ToString() ); } } else { if( splitCount < 3 || ( splitCount < 4 && splitLine[ 2 ] == "NONE" ) ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "ShaderResourceHandler: Missing options for select command \"%s\" in shader resource " ) TXT( "\"%s\".\n" ) ), *splitLine[ 1 ], *shaderPath.ToString() ); return; } Shader::Select* pSelect = ( bSelectUserCommand ? rUserSelects : rSystemSelects ).New(); HELIUM_ASSERT( pSelect ); pSelect->name = optionName; pSelect->shaderTypeFlags = shaderFlags; pSelect->bOptional = ( splitLine[ 2 ] == "NONE" ); size_t choiceIndex = ( pSelect->bOptional ? 3 : 2 ); for( ; choiceIndex < splitCount; ++choiceIndex ) { HELIUM_VERIFY( ( StringConverter< char, char >::Convert( convertedString, splitLine[ choiceIndex ] ) ) ); pSelect->choices.New( convertedString ); } } }
GetMappingRequest::GetMappingRequest(const AssetPath& path) : _path(path.trimmed()) { };