////////////////////////////////////////////////////////////////////////// // onDetach //virtual void ScnComponent::onDetach( ScnEntityWeakRef Parent ) { BcAssertMsg( ParentEntity_.isValid() == BcTrue, "Attempting to detach component that is already detached!" ); BcAssertMsg( ParentEntity_ == Parent, "Attempting to detach component from an entity it isn't attached to!" ); ParentEntity_ = NULL; }
////////////////////////////////////////////////////////////////////////// // getResource ReObjectRef< CsResource > CsCore::getResource( const BcChar* pFullName ) { ReObjectRef< CsResource > Handle; if( pFullName != NULL ) { BcChar FullNameBuffer[ 1024 ]; BcAssertMsg( BcStrLength( pFullName ) < sizeof( FullNameBuffer ), "CsPackageImporter: Full name too long." ); BcStrCopy( FullNameBuffer, pFullName ); BcChar* pPackageNameBuffer = NULL; BcChar* pResourceNameBuffer = NULL; BcChar* pTypeNameBuffer = NULL; BcAssertMsg( BcStrStr( FullNameBuffer, "." ) != NULL, "CsCore: Missing package from \"%s\"", FullNameBuffer ); BcAssertMsg( BcStrStr( FullNameBuffer, ":" ) != NULL, "CsCore: Missing type from \"%s\"", FullNameBuffer ); pPackageNameBuffer = &FullNameBuffer[ 0 ]; pResourceNameBuffer = BcStrStr( FullNameBuffer, "." ); pTypeNameBuffer = BcStrStr( FullNameBuffer, ":" ); *pResourceNameBuffer++ = '\0'; *pTypeNameBuffer++ = '\0'; BcName PackageName = pPackageNameBuffer; BcName ResourceName = pResourceNameBuffer; internalFindResource( PackageName, ResourceName, ReManager::GetClass( pTypeNameBuffer ), Handle ); BcAssertMsg( Handle.isValid(), "CsCore: Unable to find \"%s\"", FullNameBuffer ); } return Handle; }
////////////////////////////////////////////////////////////////////////// // loadJsonFile BcBool CsPackageImporter::loadJsonFile( const BcChar* pFileName, Json::Value& Root ) { BcBool Success = BcFalse; BcFile File; if( File.open( pFileName ) ) { const BcU8* pData = File.readAllBytes(); Json::Reader Reader; if( Reader.parse( (const char*)pData, (const char*)pData + File.size(), Root ) ) { Success = BcTrue; } else { PSY_LOG( "Failed to parse Json:\n %s\n", Reader.getFormatedErrorMessages().c_str() ); BcAssertMsg( BcFalse, "Failed to parse \"%s\", see log for more details.", pFileName ); } BcMemFree( (void*)pData ); } else { BcAssertMsg( BcFalse, "Failed to load \"%s\"", pFileName ); } return Success; }
////////////////////////////////////////////////////////////////////////// // preInitialise void CsResource::preInitialise( const BcName& Name, BcU32 Index, CsPackage* pPackage ) { BcAssertMsg( Name != BcName::INVALID, "Resource can not have an invalid name." ); BcAssertMsg( Name != BcName::NONE, "Resource can not have a none name." ); setName( Name ); setOwner( pPackage ); Index_ = Index; }
////////////////////////////////////////////////////////////////////////// // setParentEntity void ScnComponent::setParentEntity( ScnEntityWeakRef Entity ) { BcAssertMsg( !isFlagSet( scnCF_ATTACHED ), "ScnComponent: Already attached, can't reassign parent entity." ); BcAssertMsg( !isFlagSet( scnCF_PENDING_ATTACH ), "ScnComponent: Currently attaching, can't reassign parent entity." ); BcAssertMsg( !isFlagSet( scnCF_PENDING_DETACH ), "ScnComponent: Currently detaching, can't reassign parent entity." ); BcAssertMsg( !isFlagSet( scnCF_PENDING_DESTROY ), "ScnComponent: Currently being destroyed, can't reassign parent entity." ); BcAssertMsg( ParentEntity_ == nullptr, "ScnComponent: Already have a parent, can't reassign parent entity." ); ParentEntity_ = Entity; }
////////////////////////////////////////////////////////////////////////// // onAttach //virtual void ScnComponent::onAttach( ScnEntityWeakRef Parent ) { BcAssertMsg( !isFlagSet( scnCF_ATTACHED ), "Attempting to attach component when it's already attached!" ); BcAssertMsg( isFlagSet( scnCF_PENDING_ATTACH ), "ScnComponent: Not pending attach!" ); clearFlag( scnCF_PENDING_ATTACH ); setFlag( scnCF_ATTACHED ); ParentEntity_ = Parent; }
////////////////////////////////////////////////////////////////////////// // setRenderTarget RsFrameBufferDesc& RsFrameBufferDesc::setRenderTarget( BcU32 Idx, RsTexture* Texture ) { BcAssertMsg( ( Texture->getDesc().BindFlags_ & RsResourceBindFlags::RENDER_TARGET ) != RsResourceBindFlags::NONE, "Can't bind texture as render target, was it created with RsResourceBindFlags::RENDER_TARGET?" ); BcAssertMsg( Texture->getDesc().Type_ == RsTextureType::TEX2D, "Can only use TEX2D textures." ); RenderTargets_[ Idx ] = Texture; return *this; }
////////////////////////////////////////////////////////////////////////// // onDetach //virtual void ScnComponent::onDetach( ScnEntityWeakRef Parent ) { BcAssertMsg( ParentEntity_ == Parent, "Attempting to detach component from an entity it isn't attached to!" ); BcAssertMsg( isFlagSet( scnCF_ATTACHED ), "ScnComponent: Not attached!" ); BcAssertMsg( isFlagSet( scnCF_PENDING_DETACH ), "ScnComponent: Not pending detach!" ); clearFlag( scnCF_PENDING_DETACH ); clearFlag( scnCF_ATTACHED ); setFlag( scnCF_PENDING_DESTROY ); ParentEntity_ = nullptr; }
////////////////////////////////////////////////////////////////////////// // Ctor CsPackage::CsPackage( const BcName& Name ): Name_( Name ), pLoader_( NULL ) { // Cache paths related to this package. const BcPath PackedPackage( CsCore::pImpl()->getPackagePackedPath( Name ) ); const BcPath ImportPackage( CsCore::pImpl()->getPackageImportPath( Name ) ); // Keep attempting load. BcBool LoaderValid = BcFalse; do { BcBool PackedPackageExists = FsCore::pImpl()->fileExists( (*PackedPackage).c_str() ); if( PackedPackageExists ) { pLoader_ = new CsPackageLoader( this, PackedPackage ); // If the loader has no error it's valid to continue. if( !pLoader_->hasError() ) { LoaderValid = BcTrue; } else { delete pLoader_; pLoader_ = NULL; } } #if PSY_SERVER // Attempt to import. if( LoaderValid == BcFalse ) { BcBool ImportPackageExists = FsCore::pImpl()->fileExists( (*ImportPackage).c_str() ); BcAssertMsg( ImportPackageExists, "CsPackage: Missing import package \"%s\"", (*ImportPackage).c_str() ); if( ImportPackageExists ) { CsPackageImporter Importer; BcBool ImportSucceeded = Importer.import( Name ); BcAssertMsg( ImportSucceeded, "CsPackage: Failed to import \"%s\"", (*ImportPackage).c_str() ); } } #endif } while( LoaderValid == BcFalse ); }
////////////////////////////////////////////////////////////////////////// // initialise //virtual void ScnParticleSystemComponent::initialise( const Json::Value& Object ) { Super::initialise( Object ); // Grab number of particles. NoofParticles_ = Object["noofparticles"].asUInt(); ScnMaterialRef Material = getPackage()->getPackageCrossRef( Object["material"].asUInt() ); if( !CsCore::pImpl()->createResource( BcName::INVALID, getPackage(), MaterialComponent_, Material, scnSPF_PARTICLE_3D ) ) { BcAssertMsg( BcFalse, "Material invalid blah." ); } IsLocalSpace_ = Object["localspace"].asBool(); // Cache texture bounds. ScnTextureRef Texture = Material->getTexture( "aDiffuseTex" ); for( BcU32 Idx = 0; Idx < Texture->noofRects(); ++Idx ) { ScnRect Rect = Texture->getRect( Idx ); UVBounds_.push_back( MaVec4d( Rect.X_, Rect.Y_, Rect.X_ + Rect.W_, Rect.Y_ + Rect.H_ ) ); } WorldTransformParam_ = MaterialComponent_->findParameter( "uWorldTransform" ); BcMemZero( &VertexBuffers_, sizeof( VertexBuffers_ ) ); pParticleBuffer_ = NULL; CurrentVertexBuffer_ = 0; PotentialFreeParticle_ = 0; }
////////////////////////////////////////////////////////////////////////// // setDepthStencilTarget RsFrameBufferDesc& RsFrameBufferDesc::setDepthStencilTarget( RsTexture* Texture ) { BcAssertMsg( ( Texture->getDesc().BindFlags_ & RsResourceBindFlags::DEPTH_STENCIL ) != RsResourceBindFlags::NONE, "Can't bind texture as depth stencil target, was it created with RsResourceBindFlags::DEPTH_STENCIL?" ); DepthStencilTarget_ = Texture; return *this; }
////////////////////////////////////////////////////////////////////////// // updateTexture bool RsContextNull::updateTexture( class RsTexture* Texture, const struct RsTextureSlice& Slice, RsResourceUpdateFlags Flags, RsTextureUpdateFunc UpdateFunc ) { BcAssertMsg( BcCurrentThreadId() == OwningThread_, "Calling context calls from invalid thread." ); BcUnusedVar( Texture ); BcUnusedVar( Slice ); BcUnusedVar( Flags ); BcUnusedVar( UpdateFunc ); const auto& TextureDesc = Texture->getDesc(); std::unique_ptr< BcU8[] > TempBuffer; BcU32 Width = BcMax( 1, TextureDesc.Width_ >> Slice.Level_ ); BcU32 Height = BcMax( 1, TextureDesc.Height_ >> Slice.Level_ ); BcU32 Depth = BcMax( 1, TextureDesc.Depth_ >> Slice.Level_ ); BcU32 DataSize = RsTextureFormatSize( TextureDesc.Format_, Width, Height, Depth, 1 ); TempBuffer.reset( new BcU8[ DataSize ] ); RsTextureLock Lock = { TempBuffer.get(), TextureDesc.Width_, TextureDesc.Width_ * TextureDesc.Height_ }; UpdateFunc( Texture, Lock ); return true; }
////////////////////////////////////////////////////////////////////////// // destroyBuffer bool RsContextNull::destroyBuffer( class RsBuffer* Buffer ) { BcAssertMsg( BcCurrentThreadId() == OwningThread_, "Calling context calls from invalid thread." ); BcUnusedVar( Buffer ); return true; }
////////////////////////////////////////////////////////////////////////// // createRenderState bool RsContextNull::createRenderState( RsRenderState* RenderState ) { BcAssertMsg( BcCurrentThreadId() == OwningThread_, "Calling context calls from invalid thread." ); BcUnusedVar( RenderState ); return true; }
////////////////////////////////////////////////////////////////////////// // addPrimitive void ScnDebugRenderComponent::addPrimitive( eRsPrimitiveType Type, ScnDebugRenderComponentVertex* pVertices, BcU32 NoofVertices, BcU32 Layer, BcBool UseMatrixStack ) { BcAssertMsg( MaterialComponent_.isValid(), "ScnDebugRenderComponent: Material component has not been set!" ); // Check if the vertices are owned by us, if not copy in. if( pVertices < pVertices_ || pVertices_ >= pVerticesEnd_ ) { ScnDebugRenderComponentVertex* pNewVertices = allocVertices( NoofVertices ); if( pNewVertices != NULL ) { BcMemCopy( pNewVertices, pVertices, sizeof( ScnDebugRenderComponentVertex ) * NoofVertices ); pVertices = pNewVertices; } } // TODO: If there was a previous primitive which we can marge into, attempt to. BcU32 VertexIndex = convertVertexPointerToIndex( pVertices ); ScnDebugRenderComponentPrimitiveSection PrimitiveSection = { Type, VertexIndex, NoofVertices, Layer, MaterialComponent_ }; PrimitiveSectionList_.push_back( PrimitiveSection ); LastPrimitiveSection_ = (BcU32)PrimitiveSectionList_.size() - 1; }
////////////////////////////////////////////////////////////////////////// // drawPrimitives void RsContextNull::drawPrimitives( RsTopologyType PrimitiveType, BcU32 IndexOffset, BcU32 NoofIndices ) { BcAssertMsg( BcCurrentThreadId() == OwningThread_, "Calling context calls from invalid thread." ); BcUnusedVar( PrimitiveType ); BcUnusedVar( IndexOffset ); BcUnusedVar( NoofIndices ); }
ReClass* GetClass( BcName Name ) { auto NameString = (*Name); // If it begins with "class", "struct", or "enum", strip it. // NOTE: We should move this work into the demangling stuff. if( NameString.substr( 0, 6 ) == "class " || NameString.substr( 0, 7 ) == "struct " || NameString.substr( 0, 5 ) == "enum " ) { Name = BcName( NameString.substr( NameString.find( " " ) + 1, NameString.length() - 1 ) ); } // Try find class. ReClass* FoundType = GetType( Name ); if( FoundType == nullptr ) { BcAssertMsg( BcIsGameThread(), "Reflection can only modify database on the game thread." ); FoundType = new ReClass( Name ); Types_[ Name ] = FoundType; } // Use dynamic cast here instead of our internal RTTI. // It may still be under construction so not yet valid. return dynamic_cast< ReClass* >( FoundType ); }
////////////////////////////////////////////////////////////////////////// // destroySamplerState bool RsContextNull::destroySamplerState( RsSamplerState* SamplerState ) { BcAssertMsg( BcCurrentThreadId() == OwningThread_, "Calling context calls from invalid thread." ); BcUnusedVar( SamplerState ); return true; }
ReEnum* GetEnum( BcName Name ) { auto NameString = ( *Name ); // If it begins with "class", "struct", or "enum", strip it. // NOTE: We should move this work into the demangling stuff. if( NameString.substr( 0, 5 ) == "enum " ) { Name = BcName( NameString.substr( NameString.find( " " ) + 1, NameString.length() - 1 ) ); } // Try find class. ReClass* FoundType = GetType( Name ); if( FoundType == nullptr ) { BcAssertMsg( BcIsGameThread(), "Reflection can only modify database on the game thread." ); FoundType = new ReEnum( Name ); Types_[ Name ] = FoundType; } if( FoundType->isTypeOf< ReEnum >() ) { return static_cast< ReEnum* >( FoundType ); } else { return nullptr; } }
////////////////////////////////////////////////////////////////////////// // destroyTexture bool RsContextNull::destroyTexture( class RsTexture* Texture ) { BcAssertMsg( BcCurrentThreadId() == OwningThread_, "Calling context calls from invalid thread." ); BcUnusedVar( Texture ); return true; }
////////////////////////////////////////////////////////////////////////// // update //virtual void RsRenderTargetGL::update() { GLuint ColourBufferHandle = pColourBuffer_->getHandle< GLuint >(); GLuint DepthStencilBufferHandle = pDepthStencilBuffer_->getHandle< GLuint >(); GLuint FrameBufferHandle = pFrameBuffer_->getHandle< GLuint >(); GLuint TextureHandle = pTexture_->getHandle< GLuint >(); // Bind framebuffer. glBindFramebuffer( GL_FRAMEBUFFER, FrameBufferHandle ); // NOTE: Don't need a render buffer for colour attachment. Remove this when we get to MRT. // Setup attachments. glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER_EXT, ColourBufferHandle ); glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, DepthStencilBufferHandle ); // Bind texture to framebuffer. glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, TextureHandle, 0 ); // Check the framebuffer is complete. GLenum Status = glCheckFramebufferStatus( GL_FRAMEBUFFER ); BcAssertMsg( Status == GL_FRAMEBUFFER_COMPLETE, "RsRenderTargetGL: Framebuffer not complete." ); // Unbind framebuffer. glBindFramebuffer( GL_FRAMEBUFFER, 0 ); // Set out handle to be the framebuffer handle. setHandle< GLuint >( FrameBufferHandle ); }
////////////////////////////////////////////////////////////////////////// // markReady void CsResource::markDestroy() { BcU32 OldStage = InitStage_.exchange( INIT_STAGE_DESTROY ); BcAssertMsg( OldStage == INIT_STAGE_READY, "CsResource: Trying to mark \"%s\" for destruction when it's not ready.", (*getName()).c_str() ); CsCore::pImpl()->destroyResource( this ); }
////////////////////////////////////////////////////////////////////////// // 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; }
////////////////////////////////////////////////////////////////////////// // requestPackage CsPackage* CsCore::requestPackage( const BcName& Package ) { CsPackage* pPackage = findPackage( Package ); if( pPackage != NULL ) { return pPackage; } // Check for a packed package. BcPath PackedPackage( getPackagePackedPath( Package ) ); BcPath ImportPackage( getPackageImportPath( Package ) ); BcBool PackageExists = FsCore::pImpl()->fileExists( (*PackedPackage).c_str() ) || FsCore::pImpl()->fileExists( (*ImportPackage).c_str() ); // If it exists, create it. Internally it will trigger it's own load. if( PackageExists ) { pPackage = new CsPackage( Package ); PackageList_.push_back( pPackage ); } else { BcAssertMsg( BcFalse, "CsCore: Can't import package, missing \"%s\" or \"%s\"", (*PackedPackage).c_str(), (*ImportPackage).c_str() ); } // return pPackage; }
////////////////////////////////////////////////////////////////////////// // setUniformBuffer void RsContextNull::setUniformBuffer( BcU32 Handle, class RsBuffer* UniformBuffer ) { BcAssertMsg( BcCurrentThreadId() == OwningThread_, "Calling context calls from invalid thread." ); BcUnusedVar( Handle ); BcUnusedVar( UniformBuffer ); }
////////////////////////////////////////////////////////////////////////// // execute //virtual void SysJobWorker::execute() { // Enter loop. while( Active_ ) { PSY_PROFILER_SECTION( SysJobWorker_BeginWait_Profile, "SysJobWorker_BeginWait" ); // Wait till we are told to resume. { std::unique_lock< std::mutex > ResumeLock( ResumeMutex_ ); ResumeEvent_.wait( ResumeLock, [ this ]{ return pCurrentJob_ != NULL; } ); } PSY_PROFILER_SECTION( SysJobWorker_EndWait_Profile, "SysJobWorker_EndWait" ); if( Active_ == BcTrue ) { BcAssertMsg( pCurrentJob_ != NULL, "No job has been given!" ); } BcAssertMsg( HaveJob_ == BcTrue, "SysJobWorker: We have a job pointer set, but haven't got it via giveJob." ); // Start timing the job. #if !PSY_PRODUCTION BcTimer Timer; Timer.mark(); #endif // Execute our job. pCurrentJob_->internalExecute(); #if !PSY_PRODUCTION // Add time spent to our total. const BcU32 TimeWorkingUS = static_cast< BcU32 >( Timer.time() * 1000000.0f );; TimeWorkingUS_ += TimeWorkingUS; JobsExecuted_++; #endif // No job now, clean up. delete pCurrentJob_; pCurrentJob_ = NULL; HaveJob_ = BcFalse; // Signal job queue parent to schedule. pParent_->schedule(); } }
////////////////////////////////////////////////////////////////////////// // Dtor CsPackageLoader::~CsPackageLoader() { BcAssertMsg( hasPendingCallback() == BcFalse, "CsPackageLoader: Callbacks are pending." ); File_.close(); BcMemFree( pPackageData_ ); pPackageData_ = NULL; }
void ScnCanvasComponent::render( class ScnViewComponent* pViewComponent, RsFrame* pFrame, RsRenderSort Sort ) { if( HaveVertexBufferLock_ == BcFalse ) { return; } BcAssertMsg( HaveVertexBufferLock_ == BcTrue, "ScnCanvasComponent: Can't render without a vertex buffer lock." ); // HUD pass. Sort.Layer_ = 0; Sort.Pass_ = RS_SORT_PASS_MAX; // NOTE: Could do this sort inside of the renderer, but I'm just gonna keep the canvas // as one solid object as to not conflict with other canvas objects when rendered // to the scene. Will not sort by transparency or anything either. std::stable_sort( PrimitiveSectionList_.begin(), PrimitiveSectionList_.end(), ScnCanvasComponentPrimitiveSectionCompare() ); // Cache last material instance. ScnMaterialComponent* pLastMaterialComponent = NULL; for( BcU32 Idx = 0; Idx < PrimitiveSectionList_.size(); ++Idx ) { ScnCanvasComponentRenderNode* pRenderNode = pFrame->newObject< ScnCanvasComponentRenderNode >(); pRenderNode->NoofSections_ = 1;//PrimitiveSectionList_.size(); pRenderNode->pPrimitiveSections_ = pFrame->alloc< ScnCanvasComponentPrimitiveSection >( 1 ); pRenderNode->pPrimitive_ = pRenderResource_->pPrimitive_; // Copy primitive sections in. BcMemCopy( pRenderNode->pPrimitiveSections_, &PrimitiveSectionList_[ Idx ], sizeof( ScnCanvasComponentPrimitiveSection ) * 1 ); // Bind material. // NOTE: We should be binding for every single draw call. We can have the material deal with redundancy internally // if need be. If using multiple canvases we could potentially lose a material bind. //if( pLastMaterialComponent != pRenderNode->pPrimitiveSections_->MaterialComponent_ ) { pLastMaterialComponent = pRenderNode->pPrimitiveSections_->MaterialComponent_; pLastMaterialComponent->bind( pFrame, Sort ); } // Add to frame. pRenderNode->Sort_ = Sort; pFrame->addRenderNode( pRenderNode ); } // Unlock vertex buffer. pRenderResource_->pVertexBuffer_->setNoofUpdateVertices( VertexIndex_ ); pRenderResource_->pVertexBuffer_->unlock(); HaveVertexBufferLock_ = BcFalse; // Flip the render resource. CurrentRenderResource_ = 1 - CurrentRenderResource_; // Reset render resource pointers to aid debugging. pRenderResource_ = NULL; pVertices_ = pVerticesEnd_ = NULL; }
eEvtReturn OnPostOsOpen_CreateClient( EvtID, const EvtBaseEvent& ) { GMainWindow = new OsClientSDL(); if( GMainWindow->create( GPsySetupParams.Name_.c_str(), GInstance_, GResolutionWidth, GResolutionHeight, BcFalse, GPsySetupParams.Flags_ & psySF_WINDOW ? BcTrue : BcFalse ) == BcFalse ) { BcAssertMsg( BcFalse, "Failed to create client!" ); return evtRET_REMOVE; } // Get rendering context. if( RsCore::pImpl() != NULL ) { RsContext* pContext = RsCore::pImpl()->getContext( GMainWindow ); BcAssertMsg( pContext != NULL, "Failed to create render context!" ); } return evtRET_REMOVE; }
////////////////////////////////////////////////////////////////////////// // setFields void ReClass::setFields( BcU32 NoofFields, const ReField* pFields ) { BcAssertMsg( Fields_.size() == 0, "Fields already set." ); Fields_.reserve( NoofFields ); for( BcU32 Idx = 0; Idx < NoofFields; ++Idx ) { Fields_.push_back( &pFields[ Idx ] ); } }