////////////////////////////////////////////////////////////////////////// // onDataLoaded void CsFileReaderRPC::onDataLoaded( void* pData, BcSize Size ) { // Chunk index is fed in as we can't simply match pointers here. BcU32 ChunkIdx = *(BcU32*)pData; pData = ((BcU32*)pData) + 1; Size -= sizeof( BcU32 ); // Find the chunk that data matches to. if( ChunkIdx < Header_.NoofChunks_ ) { CsFileChunk* pChunk = &pChunks_[ ChunkIdx ]; CsFileChunkProps* pChunkProps = &pChunkProps_[ ChunkIdx ]; BcAssert( pChunk->Size_ == Size ); // Copy data and cache pointer. BcU32 TotalHeaderSize = sizeof( CsFileHeader ) + sizeof( CsFileChunk ) * Header_.NoofChunks_; void* pInternalData = pData_ + ( pChunk->Offset_ - TotalHeaderSize ); BcMemCopy( pInternalData, pData, Size ); BcAssert( (BcU8*)pInternalData < ( pData_ + TotalDataSize_ ) ); pChunkProps->Status_ = CsFileChunkProps::STATUS_LOADED; ChunkDelegate_( this, ChunkIdx, pChunk, pInternalData ); } }
////////////////////////////////////////////////////////////////////////// // addChunk BcU32 CsPackageImporter::addChunk( BcU32 ID, const void* pData, BcSize Size, BcSize RequiredAlignment, BcU32 Flags ) { std::lock_guard< std::recursive_mutex > Lock( BuildingLock_ ); BcAssert( BuildingBeginCount_ > 0 ); BcAssert( Size > 0 ); BcAssert( BcPot( RequiredAlignment ) ); BcAssert( RequiredAlignment <= 4096 ); const BcU8* pPackedData = reinterpret_cast< const BcU8* >( pData ); size_t PackedSize = Size; BcBool HaveCompressed = BcFalse; // If we need to compress, do so. if( ( Flags & csPCF_COMPRESSED ) != 0 ) { if( BcCompressData( static_cast< const BcU8* >( pData ), Size, pPackedData, PackedSize ) ) { HaveCompressed = BcTrue; } else { // On failure, strip compressed flag. Flags &= ~csPCF_COMPRESSED; } } // Generate header. CsPackageChunkHeader ChunkHeader; ChunkHeader.ID_ = ID; ChunkHeader.Offset_ = 0; ChunkHeader.Flags_ = Flags; ChunkHeader.RequiredAlignment_ = static_cast< BcU32 >( RequiredAlignment ); ChunkHeader.PackedBytes_ = static_cast< BcU32 >( PackedSize ); ChunkHeader.UnpackedBytes_ = static_cast< BcU32 >( Size ); ChunkHeader.PackedHash_ = BcHash( (BcU8*)pPackedData, PackedSize ); ChunkHeader.UnpackedHash_ = BcHash( (BcU8*)pData, Size ); // Generate data. CsPackageChunkData ChunkData; ChunkData.Status_ = csPCS_NOT_LOADED; ChunkData.Managed_ = BcFalse; // Store as packed data. ChunkData.pPackedData_ = new BcU8[ PackedSize ]; ChunkData.pUnpackedData_ = NULL; BcMemCopy( ChunkData.pPackedData_, pPackedData, PackedSize ); if( HaveCompressed ) { delete [] pPackedData; } // Push into lists. ChunkHeaders_.push_back( ChunkHeader ); ChunkDatas_.push_back( ChunkData ); return static_cast< BcU32 >( ChunkHeaders_.size() - 1 ); }
////////////////////////////////////////////////////////////////////////// // onResourceHeadersLoaded void CsPackageLoader::onResourceHeadersLoaded( void* pData, BcSize Size ) { // Check we have the right data. BcAssert( pData == pResourceHeaders_ ); BcAssert( Size == sizeof( CsPackageResourceHeader ) * Header_.TotalResources_ ); // This callback is complete. --PendingCallbackCount_; }
////////////////////////////////////////////////////////////////////////// // fileChunkReady void ScnModel::fileChunkReady( BcU32 ChunkIdx, BcU32 ChunkID, void* pData ) { // If we have no render core get chunk 0 so we keep getting entered into. if( RsCore::pImpl() == NULL ) { requestChunk( 0 ); return; } if( ChunkID == BcHash( "header" ) ) { pHeader_ = (ScnModelHeader*)pData; } else if( ChunkID == BcHash( "nodetransformdata" ) ) { pNodeTransformData_ = (ScnModelNodeTransformData*)pData; } else if( ChunkID == BcHash( "nodepropertydata" ) ) { pNodePropertyData_ = (ScnModelNodePropertyData*)pData; // Mark up node names. // TODO: Automate this process with reflection! for( BcU32 NodeIdx = 0; NodeIdx < pHeader_->NoofNodes_; ++NodeIdx ) { ScnModelNodePropertyData* pNodePropertyNode = &pNodePropertyData_[ NodeIdx ]; markupName( pNodePropertyNode->Name_ ); } } else if( ChunkID == BcHash( "vertexdata" ) ) { BcAssert( pVertexBufferData_ == NULL || pVertexBufferData_ == pData ); pVertexBufferData_ = (BcU8*)pData; } else if( ChunkID == BcHash( "indexdata" ) ) { BcAssert( pIndexBufferData_ == NULL || pIndexBufferData_ == pData ); pIndexBufferData_ = (BcU8*)pData; } else if( ChunkID == BcHash( "vertexelements" ) ) { pVertexElements_ = (RsVertexElement*)pData; } else if( ChunkID == BcHash( "meshdata" ) ) { pMeshData_ = (ScnModelMeshData*)pData; RsVertexElement* pVertexElements = pVertexElements_; for( BcU32 Idx = 0; Idx < pHeader_->NoofPrimitives_; ++Idx ) { pMeshData_->VertexElements_ = pVertexElements; pVertexElements += pMeshData_->NoofVertexElements_; } markCreate(); // All data loaded, time to create. } }
////////////////////////////////////////////////////////////////////////// // add void ScnAnimationPose::add( const ScnAnimationPose& Reference, const ScnAnimationPose& A, const ScnAnimationPose& B, BcF32 T ) { BcAssert( A.NoofTransforms_ == NoofTransforms_ ); BcAssert( B.NoofTransforms_ == NoofTransforms_ ); for( BcU32 Idx = 0; Idx < NoofTransforms_; ++Idx ) { pTransforms_[ Idx ].add( Reference.pTransforms_[ Idx ], A.pTransforms_[ Idx ], B.pTransforms_[ Idx ], T ); } }
////////////////////////////////////////////////////////////////////////// // blend void ScnAnimationPose::blend( const ScnAnimationPose& A, const ScnAnimationPose& B, BcF32 T ) { BcAssert( A.NoofTransforms_ == NoofTransforms_ ); BcAssert( B.NoofTransforms_ == NoofTransforms_ ); for( BcU32 Idx = 0; Idx < NoofTransforms_; ++Idx ) { pTransforms_[ Idx ].blend( A.pTransforms_[ Idx ], B.pTransforms_[ Idx ], T ); } }
////////////////////////////////////////////////////////////////////////// // onStringTableLoaded void CsPackageLoader::onStringTableLoaded( void* pData, BcSize Size ) { // Check we have the right data. BcAssert( pData == pStringTable_ ); BcAssert( Size == Header_.StringTableBytes_ ); // String table is ready. IsStringTableReady_ = BcTrue; // This callback is complete. --PendingCallbackCount_; }
////////////////////////////////////////////////////////////////////////// // interpolatePose void ScnAnimationTreeTrackNode::interpolatePose() { if( AnimationQueue_.size() > 0 ) { BcAssert( pPoseFileDataA_ != nullptr ); BcAssert( pPoseFileDataB_ != nullptr ); const BcF32 TimeLength = pPoseFileDataB_->Time_ - pPoseFileDataA_->Time_; const BcF32 TimeRelative = Time_ - pPoseFileDataA_->Time_; const BcF32 LerpAmount = TimeRelative / TimeLength; pWorkingPose_->blend( *pPoseA_, *pPoseB_, LerpAmount ); } }
////////////////////////////////////////////////////////////////////////// // recreate void ScnViewComponent::recreateFrameBuffer() { if( RenderTarget_.isValid() || DepthStencilTarget_.isValid() ) { BcAssert( RenderTarget_.isValid() && DepthStencilTarget_.isValid() ); BcAssert( RenderTarget_->getWidth() && DepthStencilTarget_->getWidth() ); BcAssert( RenderTarget_->getHeight() && DepthStencilTarget_->getHeight() ); RsFrameBufferDesc FrameBufferDesc( 1 ); FrameBufferDesc.setRenderTarget( 0, RenderTarget_->getTexture() ); FrameBufferDesc.setDepthStencilTarget( DepthStencilTarget_->getTexture() ); FrameBuffer_ = RsCore::pImpl()->createFrameBuffer( FrameBufferDesc ); } }
////////////////////////////////////////////////////////////////////////// // type void MdlNode::type( BcU32 NodeType ) { if( NodeType != eNT_COLMESH && NodeType != eNT_ENTITY ) { BcAssert( ( NodeType_ & ~NodeType ) == 0 ); } if( ( NodeType_ & NodeType ) == 0 ) { NodeType_ |= NodeType; switch( NodeType ) { case eNT_EMPTY: break; case eNT_MESH: pNodeMeshObject_ = new MdlMesh(); break; case eNT_SKIN: pNodeSkinObject_ = new MdlMesh(); break; case eNT_COLMESH: // Naughty. //pNodeColMeshObject_ = new MdlMesh(); BcAssert( pNodeMeshObject_ != NULL ); break; case eNT_MORPH: pNodeMorphObject_ = new MdlMesh(); break; case eNT_ENTITY: pNodeEntityObject_ = new MdlEntity(); break; case eNT_LIGHT: pNodeLightObject_ = new MdlLight(); break; case eNT_PROJECTOR: pNodeProjectorObject_ = new MdlProjector(); break; } } }
////////////////////////////////////////////////////////////////////////// // addString BcU32 CsPackageImporter::addString( const BcChar* pString ) { std::lock_guard< std::recursive_mutex > Lock( BuildingLock_ ); BcAssert( BuildingBeginCount_ > 0 ); BcU32 CurrentOffset = 0; for( BcU32 Idx = 0; Idx < StringList_.size(); ++Idx ) { const std::string& StringEntry( StringList_[ Idx ] ); if( StringEntry == pString ) { return CurrentOffset; } // String length with null terminator. CurrentOffset += static_cast< BcU32 >( StringEntry.length() + 1 ); } // Add string to list. StringList_.push_back( pString ); // Return current offset. return CurrentOffset; }
////////////////////////////////////////////////////////////////////////// // importResource BcBool CsPackageImporter::importResource( CsResourceImporterUPtr Importer, const Json::Value& Resource ) { // Catch name being missing. if( Importer->getResourceName().empty() ) { PSY_LOG( "ERROR: Name not specified for resource.\n" ); return BcFalse; } // Catch type being missing. if( Importer->getResourceType().empty() ) { PSY_LOG( "ERROR: Type not specified for resource.\n" ); return BcFalse; } PSY_LOG( "INFO: Processing \"%s\" of type \"%s\"\n", Importer->getResourceName().c_str(), Importer->getResourceType().c_str() ); // Get first chunk used by resource. size_t FirstChunk = ChunkHeaders_.size(); BcBool SuccessfulImport = BcFalse; // NOTE: Eventually we will be exception safe throught the import // pipeline, so shouldn't need these adhoc try/catch blocks. try { PSY_LOGSCOPEDINDENT; SuccessfulImport = Importer->import( Resource ); // Check for error + critical messages. SuccessfulImport &= Importer->getMessageCount( CsMessageCategory::ERROR ) == 0; SuccessfulImport &= Importer->getMessageCount( CsMessageCategory::CRITICAL ) == 0; } catch( CsImportException ImportException ) { PSY_LOG( "ERROR: %s", ImportException.what() ); } // Handle success. if( SuccessfulImport ) { // Setup current resource header. CurrResourceHeader_.Name_ = addString( Importer->getResourceName().c_str() ); CurrResourceHeader_.Type_ = addString( Importer->getResourceType().c_str() ); CurrResourceHeader_.Flags_ = csPEF_DEFAULT; CurrResourceHeader_.FirstChunk_ = static_cast< BcU32 >( FirstChunk ); CurrResourceHeader_.LastChunk_ = static_cast< BcU32 >( ChunkHeaders_.size() - 1 ); // Assumes 1 chunk for resource. Fair assumption. // Make sure chunk indices are valid. BcAssert( CurrResourceHeader_.FirstChunk_ <= CurrResourceHeader_.LastChunk_ ); ResourceHeaders_.push_back( CurrResourceHeader_ ); } return SuccessfulImport; }
////////////////////////////////////////////////////////////////////////// // haveAnyValidResources BcBool CsPackage::haveAnyValidResources() const { BcAssert( BcIsGameThread() ); // If the data isn't ready, we will have valid resources soon. if( pLoader_->isDataReady() == BcFalse ) { return BcTrue; } // If our resource list is empty we can exit early. if( Resources_.size() == 0 ) { return BcFalse; } // Search through list for all valid resources. for( BcU32 Idx = 0; Idx < Resources_.size(); ++Idx ) { const CsResourceRef<>& Resource( Resources_[ Idx ] ); if( Resource.isValid() == BcTrue ) { return BcTrue; } } // return BcFalse; }
////////////////////////////////////////////////////////////////////////// // hasUnreferencedResources BcBool CsPackage::hasUnreferencedResources() const { BcAssert( BcIsGameThread() ); // If the data isn't ready, we are still referenced. if( pLoader_->isDataReady() == BcFalse ) { return BcFalse; } BcBool IsUnreferenced = BcTrue; for( BcU32 Idx = 0; Idx < Resources_.size(); ++Idx ) { const CsResourceRef<>& Resource( Resources_[ Idx ] ); // If we've got invalid resources, or a ref count of 1 (self-ref'd) we have unreferenced resources. if( Resource.isValid() == BcFalse || Resource.refCount() == 1 ) { return BcTrue; } } return BcFalse; }
////////////////////////////////////////////////////////////////////////// // allocParticle BcBool ScnParticleSystemComponent::allocParticle( ScnParticle*& pParticle ) { // We can't be allocating whilst we're updating. BcAssert( UpdateFence_.count() == 0 ); // TODO: Perhaps a free list of indices? Reordering of dead particles? // Either way I want the update to be cache friendly. for( BcU32 Idx = 0; Idx < NoofParticles_; ++Idx ) { BcU32 RealIdx = ( Idx + PotentialFreeParticle_ ) % NoofParticles_; // Slow. If we use powers of 2, we can &. ScnParticle* pPotentiallyFreeParticle = &pParticleBuffer_[ RealIdx ]; if( pPotentiallyFreeParticle->Alive_ == BcFalse ) { pParticle = pPotentiallyFreeParticle; // Prevent it being rendered for the first frame. pParticle->Scale_ = MaVec2d( 0.0f, 0.0 ); pParticle->Colour_ = RsColour( 0.0f, 0.0f, 0.0f, 0.0f ); ++PotentialFreeParticle_; return BcTrue; } } return BcFalse; }
////////////////////////////////////////////////////////////////////////// // buildNormals void MdlMesh::buildNormals() { for ( BcU32 i = 0; i < ( aIndices_.size() / 3 ); ++i ) { BcU32 TA = aIndices_[ ( i * 3 ) + 0 ].iVertex_; BcU32 TB = aIndices_[ ( i * 3 ) + 1 ].iVertex_; BcU32 TC = aIndices_[ ( i * 3 ) + 2 ].iVertex_; MdlVertex& VertA = aVertices_[ TA ]; MdlVertex& VertB = aVertices_[ TB ]; MdlVertex& VertC = aVertices_[ TC ]; BcVec3d VertPosA( VertA.Position_.x(), VertA.Position_.y(), VertA.Position_.z() ); BcVec3d VertPosB( VertB.Position_.x(), VertB.Position_.y(), VertB.Position_.z() ); BcVec3d VertPosC( VertC.Position_.x(), VertC.Position_.y(), VertC.Position_.z() ); BcVec3d Normal = ( VertPosA - VertPosB ).cross( ( VertPosB - VertPosC ) ); VertA.Normal_ += Normal; VertB.Normal_ += Normal; VertC.Normal_ += Normal; } for ( BcU32 i = 0; i < aVertices_.size(); ++i ) { MdlVertex& Vert = aVertices_[ i ]; Vert.bNormal_ = BcTrue; Vert.Normal_.normalise(); BcReal Mag = Vert.Normal_.magnitude(); BcAssert( BcAbs( Mag - 1.0f ) < 0.00001f ); } }
////////////////////////////////////////////////////////////////////////// // getContext //virtual RsContext* RsCoreImplGL::getContext( OsClient* pClient ) { BcAssert( BcIsGameThread() ); TContextMapIterator It = ContextMap_.find( pClient ); if( It != ContextMap_.end() ) { return It->second; } else { if( pClient != NULL ) { RsContextGL* pResource = new RsContextGL( pClient, ContextMap_[ NULL ] ); createResource( pResource ); // If we have no default context, set it. if( ContextMap_[ NULL ] == NULL ) { ContextMap_[ NULL ] = pResource; } // Store mapped to client. ContextMap_[ pClient ] = pResource; return pResource; } } return NULL; }
////////////////////////////////////////////////////////////////////////// // fileChunkReady void ScnTexture::fileChunkReady( BcU32 ChunkIdx, BcU32 ChunkID, void* pData ) { // If we have no render core get chunk 0 so we keep getting entered into. if( RsCore::pImpl() == NULL ) { requestChunk( 0 ); return; } if( ChunkID == BcHash( "header" ) ) { // Request all texture levels. for( BcU32 iLevel = 0; iLevel < Header_.Levels_; ++iLevel ) { requestChunk( ++ChunkIdx ); } // We update the header, create a new texture rather than updating. CreateNewTexture_ = BcTrue; } else if( ChunkID == BcHash( "body" ) ) { // Grab pointer to data. BcAssert( pTextureData_ == NULL || pTextureData_ == pData ); pTextureData_ = pData; // Setup. setup(); } }
////////////////////////////////////////////////////////////////////////// // requestChunk BcBool CsPackageLoader::requestChunk( BcU32 ResourceIdx, BcU32 ResourceChunkIdx, void* pDataLocation ) { BcAssert( IsDataReady_ ); CsPackageResourceHeader& ResourceHeader = pResourceHeaders_[ ResourceIdx ]; BcU32 ChunkIdx = ResourceHeader.FirstChunk_ + ResourceChunkIdx; CsPackageChunkHeader& ChunkHeader = pChunkHeaders_[ ChunkIdx ]; CsPackageChunkData& ChunkData = pChunkData_[ ChunkIdx ]; // Handle unmanaged data. if( ( ChunkHeader.Flags_ & csPCF_MANAGED ) == 0 ) { BcAssertMsg( pDataLocation != NULL, "CsPackageLoader: Unmanaged chunks require a data location." ); ChunkData.pUnpackedData_ = reinterpret_cast< BcU8* >( pDataLocation ); } // If the chunk isn't loaded, then process (this will kick off the load). if( ChunkData.Status_ == csPCS_NOT_LOADED || ChunkData.Status_ == csPCS_READY ) { processResourceChunk( ResourceIdx, ChunkIdx ); return BcTrue; } return BcFalse; }
////////////////////////////////////////////////////////////////////////// // updateParticle void ScnParticleSystemComponent::updateParticle( ScnParticle& Particle, BcReal Tick ) { // TODO: Move each section of this into "affectors": // - Physics affector: Move physically based on vel/accel. // - Colour lerp affector: Some more advanced colour interpolation. // - Maybe even fancier stuff like curve based interpolation of position, colour, scale? // - - Advantage: Min/Max and that stuff can live in the affector (memory saving++). // For now, KISS. This just needs to function, I can make it better later :) BcAssert( Particle.Alive_ == BcTrue ); // Do position. Particle.Position_ += Particle.Velocity_ * Tick; Particle.Velocity_ += Particle.Acceleration_ * Tick; // Calculate interpolators. BcReal LerpValue = Particle.CurrentTime_ / Particle.MaxTime_; Particle.Scale_.lerp( Particle.MinScale_, Particle.MaxScale_, LerpValue ); Particle.Colour_.lerp( Particle.MinColour_, Particle.MaxColour_, LerpValue ); // Advance current time. Particle.CurrentTime_ += Tick; // Kill particle! if( Particle.CurrentTime_ > Particle.MaxTime_ ) { Particle.Alive_ = BcFalse; } }
////////////////////////////////////////////////////////////////////////// // realloc void BcStream::realloc( BcSize NewSize ) { BcAssert( NewSize > BufferSize_ ); // Round up size NewSize = ( ( (BcSize)( NewSize ) + AllocSize_ - 1 ) & ~( AllocSize_ - 1 ) ); // if( pDataBuffer_ != NULL ) { BcU8* pNewBuffer = new BcU8[ NewSize ]; // Copy data to new buffer memcpy( pNewBuffer, pDataBuffer_, BufferSize_ ); // Free old buffer delete [] pDataBuffer_; // Assign new buffer. pDataBuffer_ = pNewBuffer; BufferSize_ = NewSize; } else { // New buffer from scratch. pDataBuffer_ = new BcU8[ NewSize ]; BufferSize_ = NewSize; } }
////////////////////////////////////////////////////////////////////////// // BcGetHardwareThreadCount BcU32 BcGetHardwareThreadCount() { SYSTEM_INFO SysInfo; ::GetSystemInfo( &SysInfo ); BcU32 RetVal = (BcU32)SysInfo.dwNumberOfProcessors; BcAssert( RetVal >= 1 ); return RetVal; }
////////////////////////////////////////////////////////////////////////// // addChunk BcU32 CsFileWriter::addChunk( BcU32 ID, void* pData, BcU32 Size ) { BcAssert( Size > 0 ); CsFileChunkNative Chunk = { ID, new BcU8[ Size ], Size }; BcMemCopy( Chunk.pData_, pData, Size ); Chunks_.push_back( Chunk ); return Chunks_.size() - 1; }
////////////////////////////////////////////////////////////////////////// // getNoofChunks BcU32 CsPackageLoader::getNoofChunks( BcU32 ResourceIdx ) { BcAssert( IsDataReady_ ); CsPackageResourceHeader& ResourceHeader = pResourceHeaders_[ ResourceIdx ]; return ( (BcU32)ResourceHeader.LastChunk_ - (BcU32)ResourceHeader.FirstChunk_ ) + 1; }
////////////////////////////////////////////////////////////////////////// // queueAnimation void ScnAnimationTreeTrackNode::queueAnimation( ScnAnimation* pAnimation ) { BcAssert( pAnimation != nullptr ); AnimationQueue_.push_back( pAnimation ); // TODO: Create a map to map any animation by node name onto our reference // pose. Probably want to do this asynchronously on a job. }
////////////////////////////////////////////////////////////////////////// // tick void SysKernel::tick() { BcAssert( BcIsGameThread() ); if( ShuttingDown_ == BcFalse ) { BcScopedLock< BcMutex > Lock( SystemLock_ ); // Add systems. addSystems(); // Remove systems. removeSystems(); // Iterate over and process all systems. TSystemListIterator Iter = SystemList_.begin(); while( Iter != SystemList_.end() && ShuttingDown_ == BcFalse ) { // Cache system. SysSystem* pSystem = (*Iter); // Process system. if( pSystem->process() == BcFalse ) { PendingRemoveSystemList_.push_back( pSystem ); } // Next system. ++Iter; } } else { BcScopedLock< BcMutex > Lock( SystemLock_ ); // Iterate over and process all systems. TSystemListReverseIterator Iter = SystemList_.rbegin(); if( Iter != SystemList_.rend() ) { // Cache system. SysSystem* pSystem = (*Iter); // Process system. if( pSystem->process() == BcFalse ) { PendingRemoveSystemList_.push_back( pSystem ); } } // Remove systems. removeSystems(); } // Dispatch callbacks. DelegateDispatcher_.dispatch(); }
////////////////////////////////////////////////////////////////////////// // onChunkHeadersLoaded void CsPackageLoader::onChunkHeadersLoaded( void* pData, BcSize Size ) { // Check we have the right data. BcAssert( pData == pChunkHeaders_ ); BcAssert( Size == sizeof( CsPackageChunkHeader ) * Header_.TotalChunks_ ); // Mark up all the resources. markupResources(); // Data is ready. IsDataReady_ = BcTrue; // Now initialise resources. initialiseResources(); // This callback is complete. --PendingCallbackCount_; }
////////////////////////////////////////////////////////////////////////// // onDetach //virtual void ScnDebugRenderComponent::onDetach( ScnEntityWeakRef Parent ) { getParentEntity()->detach( MaterialComponent_ ); BcAssert( pImpl_ == this ); pImpl_ = NULL; Super::onDetach( Parent ); }
////////////////////////////////////////////////////////////////////////// // visit //virtual void ScnRenderingVisitor::visit( class ScnRenderableComponent* pComponent ) { if( pViewComponent_->getRenderMask() & pComponent->getRenderMask() ) { PSY_LOGSCOPEDCATEGORY( *pComponent->getClass()->getName() ); BcAssert( pComponent->isReady() ); pComponent->render( pViewComponent_, pFrame_, Sort_ ); } }
////////////////////////////////////////////////////////////////////////// // onAttach //virtual void ScnDebugRenderComponent::onAttach( ScnEntityWeakRef Parent ) { Super::onAttach( Parent ); getParentEntity()->attach( MaterialComponent_ ); BcAssert( pImpl_ == NULL ); pImpl_ = this; }