//////////////////////////////////////////////////////////////////////////
// pushMatrix
void ScnCanvasComponent::pushMatrix( const MaMat4d& Matrix )
{
	const MaMat4d& CurrMatrix = getMatrix();
	MaMat4d NewMatrix = Matrix * CurrMatrix;
	MatrixStack_.push_back( NewMatrix );
	IsIdentity_ = NewMatrix.isIdentity();
}
//////////////////////////////////////////////////////////////////////////
// preUpdate
//virtual
void ScnCanvasComponent::preUpdate( BcF32 Tick )
{
	Super::update( Tick );

	if( Clear_ )
	{
		clear();

		// Push new ortho matrix.
		// Just use default client size.
		auto Client = OsCore::pImpl()->getClient( 0 );

		MaMat4d Projection;
		Projection.orthoProjection(
			Left_ * Client->getWidth() * 0.5f,
			Right_ * Client->getWidth() * 0.5f,
			Top_ * Client->getHeight() * 0.5f,
			Bottom_ * Client->getHeight() * 0.5f,
			-1.0f, 
			1.0f );

		// Push projection matrix onto stack.
		pushMatrix( Projection );

		// Push view matrix onto stack.
		pushMatrix( ViewMatrix_ );
	}
}
Exemple #3
0
//////////////////////////////////////////////////////////////////////////
// update
//virtual
void ScnModelComponent::postUpdate( BcF32 Tick )
{
	Super::postUpdate( Tick );

	UpdateFence_.increment();
	updateNodes( BaseTransform_ * getParentEntity()->getWorldMatrix() );

#if DEBUG_RENDER_NODES
	BcU32 NoofNodes = Model_->pHeader_->NoofNodes_;
	for( BcU32 NodeIdx = 0; NodeIdx < NoofNodes; ++NodeIdx )
	{
		ScnModelNodeTransformData* pNodeTransformData = &pNodeTransformData_[ NodeIdx ];
		ScnModelNodePropertyData* pNodePropertyData = &Model_->pNodePropertyData_[ NodeIdx ];

		MaMat4d ThisMatrix = pNodeTransformData->WorldTransform_;
		MaMat4d ParentMatrix = pNodePropertyData->ParentIndex_ != BcErrorCode ? 
			pNodeTransformData_[ pNodePropertyData->ParentIndex_ ].WorldTransform_ :
			getParentEntity()->getWorldMatrix();

		ScnDebugRenderComponent::pImpl()->drawMatrix( 
			ThisMatrix, RsColour::WHITE, 2000 );
		ScnDebugRenderComponent::pImpl()->drawLine( 
			ParentMatrix.translation(), 
			ThisMatrix.translation(), 
			RsColour::WHITE, 1000 );
	}

#endif // DEBUG_RENDER_NODES
}
//////////////////////////////////////////////////////////////////////////
// update
//virtual
void GaSpeechBubbleComponent::update( BcF32 Tick )
{
	Super::update( Tick );

	if ( !FontComponent_.isValid() )
	{
		Canvas_ = ParentEntity_->getComponentAnyParentByType< ScnCanvasComponent >();
		FontComponent_ = ParentEntity_->getComponentAnyParentByType< ScnFontComponent >();
	}
	if ( !SpeechBubble_.isValid() )
	{
		SpeechBubble_ = ParentEntity_->getComponentByType<ScnSpriteComponent>( BcName( "SpeechBubbleComponent", 0 ) );
	}
	if ( SpeechBubble_.isValid() )
	{
		SpeechBubble_->setColour( RsColour( 1, 1, 1, Visible_ ? 0.8 : 0 ) );
	}
	if ( !TargetEntity_.isValid() )
	{
		// We aren't even gonna bother
		return;
	}

	MaMat4d TextScaleMatrix;
	//TextScaleMatrix.scale( MaVec4d( 0.04f, 0.04f, 1.0f, 1.0f ) );
	TextScaleMatrix.scale( MaVec4d( 1.00f, -1.00f, 1.0f, 1.0f ) );

	FontComponent_->setAlphaTestStepping( MaVec2d( 0.4f, 0.45f ) );


	if (Visible_)
	{
		TimeBeenVisible_ += Tick;
		if ( TimeBeenVisible_ > VisibleTime_ )
			Visible_ = false;
		//MaMat4d Matrix = getParentEntity()->getWorldMatrix();
		//Matrix = Canvas_->popMatrix();
		Canvas_->pushMatrix( TextScaleMatrix );
	
		MaVec2d Size;
		MaVec3d worldPos = TargetEntity_->getWorldPosition();
		MaVec2d Position( 0 , 0 );
		MaVec2d localPos  = SpeechBubble_->getPosition();
		localPos  = MaVec2d(TargetEntity_->getWorldPosition().x(), -TargetEntity_->getWorldPosition().y() ) + FontOffset_;
		SpeechBubble_->setPosition( MaVec2d( TargetEntity_->getWorldPosition().x(), TargetEntity_->getWorldPosition().y() )  + SpriteOffset_ );
		for( BcU32 Idx = 0; Idx < Text_.size(); ++Idx )
		{
			const auto& Option( Text_[ Idx ] );
			const auto Colour = RsColour::BLACK;
			Size = FontComponent_->drawCentered( Canvas_, localPos + Position, Text_[ Idx ] , Colour, 280 );
			Position += MaVec2d( 0.0f, Size.y() );
		}



		Canvas_->popMatrix();
	}
	//Canvas_->pushMatrix( Matrix );
	//Canvas_->setMatrix( Matrix );
}
void GaGameComponent::drawMinimap()
{
	OsClient* Client = OsCore::pImpl()->getClient( 0 );
	MaVec2d ClientSize( Client->getWidth(), Client->getHeight() );
	MaVec2d Size( 100.0f, 100.0f );
	MaMat4d Transform;
	Transform.translation( ClientSize - MaVec2d( 100.0f, 100.0f ) );
	Canvas_->pushMatrix( Transform );
	Canvas_->drawBox( -Size, Size, RsColour( 0.05f, 0.05f, 0.05f, 1.0f ), 0 );
	
	for( auto* Unit : Units_ )
	{
		auto Position = Unit->getParentEntity()->getWorldPosition();

		// HACK: Rotate and flip appropriately later.
		Position.x( -Position.x() );
;
		if( Position.x() > -99.0f && Position.x() < 99.0f &&
			Position.z() > -99.0f && Position.z() < 99.0f )
		{
			Canvas_->drawBox( Position.xz() - MaVec2d( 1.0f, 1.0f ), Position.xz() + MaVec2d( 1.0f, 1.0f ), RsColour( 1.0f, 1.0f, 1.0f, 1.0f ), 0 );
		}
	}

	Canvas_->drawLineBox( -Size, Size, RsColour( 0.0f, 0.0f, 0.0f, 1.0f ), 0 );
	Canvas_->popMatrix();
}
Exemple #6
0
//////////////////////////////////////////////////////////////////////////
// initialise
void ScnModelComponent::initialise()
{
	// Setup base transform.
	MaMat4d Scale;
	Scale.scale( Scale_ );
	BaseTransform_.rotation( Rotation_ );
	BaseTransform_ = BaseTransform_ * Scale;
	BaseTransform_.translation( Position_ );
}
Exemple #7
0
//////////////////////////////////////////////////////////////////////////
// bakeTransform
void MdlMesh::bakeTransform( const MaMat4d& Transform )
{
	MaMat4d NrmTransform = Transform;
	NrmTransform.translation( MaVec3d( 0.0f, 0.0f, 0.0f ) );
	for( BcU32 i = 0; i < aVertices_.size(); ++i )
	{
		aVertices_[ i ].Position_ = aVertices_[ i ].Position_ * Transform;
		aVertices_[ i ].Normal_ = aVertices_[ i ].Normal_ * NrmTransform;
		aVertices_[ i ].Normal_.normalise();
		aVertices_[ i ].Tangent_ = aVertices_[ i ].Tangent_ * NrmTransform;
		aVertices_[ i ].Tangent_.normalise();
	}
}
//////////////////////////////////////////////////////////////////////////
// update
//virtual
void GaRobotComponent::update( BcF32 Tick )
{
	if( Health_ <= 0.0f )
	{
		return;
	}

	CurrentOpTimer_ -= Tick;
	if( CurrentOpTimer_ < 0.0f )
	{
		CurrentOpTimer_ += CurrentOpTime_;

		// Handle robot program.
		BcBool ExecutedCode = BcFalse;
		if( Program_.size() > 0 )
		{
			CurrentOp_ = NextOp_;
			const auto& Op = Program_[ CurrentOp_ ];
			if( Op.State_ == CurrentState_ )
			{
				auto Condition = ProgramFunctionMap_[ Op.Condition_ ];
				if( Condition != nullptr )
				{
					if( Condition( this, Op.ConditionVar_ ) )
					{
						auto Operation = ProgramFunctionMap_[ Op.Operation_ ];
						if( Operation == nullptr )
						{
							BcPrintf( "No operation \"%s\"\n", Op.Operation_.c_str() );
						}
						else
						{
							auto RetVal = Operation( this, Op.OperationVar_ );
							if( RetVal != BcErrorCode )
							{
								CurrentState_ = RetVal;
							}
						}
					}
				}
				ExecutedCode = BcTrue;
			}

			// Advance to next valid op.
			if( ExecutedCode )
			{
				for( BcU32 Idx = 0; Idx < Program_.size(); ++Idx )
				{
					NextOp_ = ( NextOp_ + 1 ) % Program_.size();
					if( Program_[ NextOp_ ].State_ == CurrentState_ )
					{
						break;
					}
				}
			}

			// Did we fail to run code? If so, reset to op 0 and the state of op 0.
			if( ExecutedCode == BcFalse )
			{
				NextOp_ = 0;
				CurrentState_ = Program_[ NextOp_ ].State_;
			}
		}
	}

	// Grab entity + position.
	auto Entity = getParentEntity();
	auto LocalPosition = Entity->getLocalPosition();

	// Move if we need to move towards our target position.
	if( ( TargetPosition_ - LocalPosition ).magnitudeSquared() > ( TargetDistance_ * TargetDistance_ ) )
	{
		if( MoveTimer_ <= 0.0f )
		{
			Velocity_ +=  ( TargetPosition_ - LocalPosition ).normal() * MaxVelocity_;
		}
	}
	else
	{
		BcF32 SlowDownTick = BcClamp( Tick * 50.0f, 0.0f, 1.0f );
		Velocity_ -= ( Velocity_ * SlowDownTick );
	}

	// TODO LATER: Do rotation.
	if( Velocity_.magnitudeSquared() > 0.1f )
	{
		auto Angle = std::atan2( Velocity_.z(), Velocity_.x() ) + BcPIDIV2;

		MaMat4d RotMat;
		RotMat.rotation( MaVec3d( 0.0f, Angle, 0.0f ) );
		Base_->setLocalMatrix( RotMat );
	}

	// TODO LATER: Do rotation.
	auto Robots = getRobots( 1 - Team_ );
	if( Robots.size() > 0 )
	{
		auto Robot = Robots[ 0 ];
		auto RobotPosition = Robot->getParentEntity()->getLocalPosition();
		auto VectorTo = RobotPosition - LocalPosition;

		// Push out of away.
		if( VectorTo.magnitude() < 3.0f )
		{
			BcF32 Factor = ( 3.0f - VectorTo.magnitude() ) / 3.0f;
			BcF32 InvFactor = 1.0f - Factor;

			Velocity_ = ( -( VectorTo.normal() * MaxVelocity_ ) * Factor * 3.0f ) + ( Velocity_ * InvFactor );
		}

		// Face turret.
		auto Angle = std::atan2( VectorTo.z(), VectorTo.x() ) + BcPIDIV2;

		MaMat4d RotMat;
		RotMat.rotation( MaVec3d( 0.0f, Angle, 0.0f ) );
		Turret_->setLocalMatrix( RotMat );
	}

	LocalPosition += Velocity_ * Tick;

	// Slow down velocity.
	BcF32 SlowDownTick = BcClamp( Tick * 10.0f, 0.0f, 1.0f );
	Velocity_ -= ( Velocity_ * SlowDownTick );

	if( Velocity_.magnitude() > MaxVelocity_ )
	{
		Velocity_ = Velocity_.normal() * MaxVelocity_;
	}

	// Set local position.
	Entity->setLocalPosition( LocalPosition );

	// Handle health + energy.
	Health_ = BcClamp( Health_, 0.0f, 100.0f );
	Energy_ = BcClamp( Energy_ + ( EnergyChargeRate_ * Tick ), 0.0f, 100.0f );

	// Weapon timers.
	WeaponATimer_ = BcMax( WeaponATimer_ - Tick, -1.0f );
	WeaponBTimer_ = BcMax( WeaponBTimer_ - Tick, -1.0f );

	MoveTimer_ = BcMax( MoveTimer_ - Tick, -1.0f );

	// Health/energy bars.
	OsClient* Client = OsCore::pImpl()->getClient( 0 );
	BcF32 Width = BcF32( Client->getWidth() ) * 0.5f;
	BcF32 Height = BcF32( Client->getHeight() ) * 0.5f;
	MaMat4d Projection;
	Projection.orthoProjection( -Width, Width, Height, -Height, -1.0f, 1.0f );
	Canvas_->pushMatrix( Projection );

	Canvas_->setMaterialComponent( Material_ );

	auto ScreenPos = View_->getScreenPosition( getParentEntity()->getWorldPosition() );
	ScreenPos -= MaVec2d( 0.0f, Height / 8.0f );
	auto TLPos = ScreenPos - MaVec2d( Width / 16.0f, Height / 64.0f );
	auto BRPos = ScreenPos + MaVec2d( Width / 16.0f, Height / 64.0f );

	// Draw background.
	Canvas_->drawBox( TLPos, BRPos, RsColour::BLACK, 0 );

	// Draw inner bars.
	TLPos += MaVec2d( 1.0f, 1.0f );
	BRPos -= MaVec2d( 1.0f, 1.0f );

	auto HealthTL = MaVec2d(
		TLPos.x(),
		TLPos.y() );	
	auto HealthBR = MaVec2d( 
		TLPos.x() + ( BRPos.x() - TLPos.x() ) * ( Health_ / 100.0f ),
		( TLPos.y() + BRPos.y() ) * 0.5f );

	auto EnergyTL = MaVec2d(
		TLPos.x(),
		( TLPos.y() + BRPos.y() ) * 0.5f );
	auto EnergyBR = MaVec2d( 
		TLPos.x() + ( BRPos.x() - TLPos.x() ) * ( Energy_ / 100.0f ),
		BRPos.y() );

	Canvas_->drawBox( HealthTL, HealthBR, RsColour::GREEN, 0 );
	Canvas_->drawBox( EnergyTL, EnergyBR, RsColour::BLUE, 0 );

	Canvas_->popMatrix( );

	ScnDebugRenderComponent::pImpl()->drawLine(
		LocalPosition,
		TargetPosition_,
		RsColour::WHITE,
		0 );

	Super::update( Tick );
}
//virtual
void ScnParticleSystemComponent::render( class ScnViewComponent* pViewComponent, RsFrame* pFrame, RsRenderSort Sort )
{
	// Wait for update fence.
	UpdateFence_.wait();

	// Grab vertex buffer and flip for next frame to use.
	TVertexBuffer& VertexBuffer = VertexBuffers_[ CurrentVertexBuffer_ ];
	CurrentVertexBuffer_ = 1 - CurrentVertexBuffer_;

	// Lock vertex buffer.
	VertexBuffer.pVertexBuffer_->lock();

	// Iterate over alive particles, and setup vertex buffer.
	BcU32 NoofParticlesToRender = 0;
	ScnParticleVertex* pVertex = VertexBuffer.pVertexArray_;
	for( BcU32 Idx = 0; Idx < NoofParticles_; ++Idx )
	{
		ScnParticle& Particle = pParticleBuffer_[ Idx ];

		if( Particle.Alive_ )
		{
			// Half size.
			const MaVec2d HalfSize = Particle.Scale_ * 0.5f;
			const BcF32 MaxHalfSize = BcMax( HalfSize.x(), HalfSize.y() );
			BcAssert( Particle.TextureIndex_ < UVBounds_.size() );
			const MaVec4d& UVBounds( UVBounds_[ Particle.TextureIndex_ ] );

			// Crappy rotation implementation :P
			const BcF32 Radians = Particle.Rotation_;
			MaVec2d CornerA = MaVec2d( -1.0f, -1.0f ) * HalfSize;
			MaVec2d CornerB = MaVec2d(  1.0f, -1.0f ) * HalfSize;
			MaVec2d CornerC = MaVec2d(  1.0f,  1.0f ) * HalfSize;
			MaVec2d CornerD = MaVec2d( -1.0f,  1.0f ) * HalfSize;
			if( Radians != NULL )
			{
				MaMat4d Rotation;
				Rotation.rotation( MaVec3d( 0.0f, 0.0f, Radians ) );
				CornerA = CornerA * Rotation;
				CornerB = CornerB * Rotation;
				CornerC = CornerC * Rotation;
				CornerD = CornerD * Rotation;
			}

			const BcU32 Colour = Particle.Colour_.asABGR();

			// Grab vertices.
			ScnParticleVertex& VertexA = *pVertex++;
			ScnParticleVertex& VertexB = *pVertex++;
			ScnParticleVertex& VertexC = *pVertex++;

			ScnParticleVertex& VertexD = *pVertex++;
			ScnParticleVertex& VertexE = *pVertex++;
			ScnParticleVertex& VertexF = *pVertex++;
			
			// 
			VertexA.X_ = Particle.Position_.x();
			VertexA.Y_ = Particle.Position_.y();
			VertexA.Z_ = Particle.Position_.z();
			VertexA.NX_ = CornerA.x();
			VertexA.NY_ = CornerA.y();
			VertexA.NZ_ = 0.0f;
			VertexA.U_ = UVBounds.x();
			VertexA.V_ = UVBounds.y();
			VertexA.RGBA_ = Colour;

			// 
			VertexB.X_ = Particle.Position_.x();
			VertexB.Y_ = Particle.Position_.y();
			VertexB.Z_ = Particle.Position_.z();
			VertexB.NX_ = CornerB.x();
			VertexB.NY_ = CornerB.y();
			VertexB.NZ_ = 0.0f;
			VertexB.U_ = UVBounds.z();
			VertexB.V_ = UVBounds.y();
			VertexB.RGBA_ = Colour;

			// 
			VertexC.X_ = Particle.Position_.x();
			VertexC.Y_ = Particle.Position_.y();
			VertexC.Z_ = Particle.Position_.z();
			VertexC.NX_ = CornerC.x();
			VertexC.NY_ = CornerC.y();
			VertexC.NZ_ = 0.0f;
			VertexC.U_ = UVBounds.z();
			VertexC.V_ = UVBounds.w();
			VertexC.RGBA_ = Colour;

			// 
			VertexD.X_ = Particle.Position_.x();
			VertexD.Y_ = Particle.Position_.y();
			VertexD.Z_ = Particle.Position_.z();
			VertexD.NX_ = CornerC.x();
			VertexD.NY_ = CornerC.y();
			VertexD.NZ_ = 0.0f;
			VertexD.U_ = UVBounds.z();
			VertexD.V_ = UVBounds.w();
			VertexD.RGBA_ = Colour;

			// 
			VertexE.X_ = Particle.Position_.x();
			VertexE.Y_ = Particle.Position_.y();
			VertexE.Z_ = Particle.Position_.z();
			VertexE.NX_ = CornerD.x();
			VertexE.NY_ = CornerD.y();
			VertexE.NZ_ = 0.0f;
			VertexE.U_ = UVBounds.x();
			VertexE.V_ = UVBounds.w();
			VertexE.RGBA_ = Colour;

			// 
			VertexF.X_ = Particle.Position_.x();
			VertexF.Y_ = Particle.Position_.y();
			VertexF.Z_ = Particle.Position_.z();
			VertexF.NX_ = CornerA.x();
			VertexF.NY_ = CornerA.y();
			VertexF.NZ_ = 0.0f;
			VertexF.U_ = UVBounds.x();
			VertexF.V_ = UVBounds.y();
			VertexF.RGBA_ = Colour;
			
			//
			++NoofParticlesToRender;
		}
	}

	// Update and unlock vertex buffer.	
	VertexBuffer.pVertexBuffer_->setNoofUpdateVertices( NoofParticlesToRender * 6 );
	VertexBuffer.pVertexBuffer_->unlock();

	// Draw particles last.
	if( NoofParticlesToRender > 0 )
	{
		Sort.Layer_ = 15;

		// Bind material.
		if( IsLocalSpace_ )
		{
			const MaMat4d& WorldTransform = getParentEntity()->getWorldMatrix();
			MaterialComponent_->setParameter( WorldTransformParam_, WorldTransform );
		}
		else
		{
			MaterialComponent_->setParameter( WorldTransformParam_, MaMat4d() );
		}

		// Set material parameters for view.
		pViewComponent->setMaterialParameters( MaterialComponent_ );

		// Bind material component.
		MaterialComponent_->bind( pFrame, Sort );

		// Setup render node.
		ScnParticleSystemComponentRenderNode* pRenderNode = pFrame->newObject< ScnParticleSystemComponentRenderNode >();
		pRenderNode->pPrimitive_ = VertexBuffer.pPrimitive_;
		pRenderNode->NoofIndices_ = NoofParticlesToRender * 6;

		// Add to frame.
		pRenderNode->Sort_ = Sort;
		pFrame->addRenderNode( pRenderNode );
	}
}
//virtual
void ScnParticleSystemComponent::render( class ScnViewComponent* pViewComponent, RsFrame* pFrame, RsRenderSort Sort )
{
	// Wait for update fence.
	UpdateFence_.wait();

	// Grab vertex buffer and flip for next frame to use.
	TVertexBuffer& VertexBuffer = VertexBuffers_[ CurrentVertexBuffer_ ];
	CurrentVertexBuffer_ = 1 - CurrentVertexBuffer_;

	// Calculate lock size.
	BcU32 NoofParticlesToRender = 0;
	for( BcU32 Idx = 0; Idx < NoofParticles_; ++Idx )
	{
		ScnParticle& Particle = pParticleBuffer_[ Idx ];

		if( Particle.Alive_ )
		{
			++NoofParticlesToRender;
		}
	}

	// Lock vertex buffer.
	if( NoofParticlesToRender > 0 )
	{
		UploadFence_.increment();
		RsCore::pImpl()->updateBuffer( 
			VertexBuffer.pVertexBuffer_,
			0,
			NoofParticlesToRender * sizeof( ScnParticleVertex ) * 6,
			RsResourceUpdateFlags::ASYNC,
			[ this, NoofParticlesToRender ]
			( RsBuffer* Buffer, const RsBufferLock& Lock )
			{
				BcU32 NoofParticlesRendered = 0;
				ScnParticleVertex* pVertex = reinterpret_cast< ScnParticleVertex* >( Lock.Buffer_ );
				for( BcU32 Idx = 0; Idx < NoofParticles_; ++Idx )
				{
					ScnParticle& Particle = pParticleBuffer_[ Idx ];

					if( Particle.Alive_ )
					{
						++NoofParticlesRendered;

						// Half size.
						const MaVec2d HalfSize = Particle.Scale_ * 0.5f;
						BcAssert( Particle.TextureIndex_ < UVBounds_.size() );
						const MaVec4d& UVBounds( UVBounds_[ Particle.TextureIndex_ ] );

						// Crappy rotation implementation :P
						const BcF32 Radians = Particle.Rotation_;
						MaVec2d CornerA = MaVec2d( -1.0f, -1.0f ) * HalfSize;
						MaVec2d CornerB = MaVec2d(  1.0f, -1.0f ) * HalfSize;
						MaVec2d CornerC = MaVec2d(  1.0f,  1.0f ) * HalfSize;
						MaVec2d CornerD = MaVec2d( -1.0f,  1.0f ) * HalfSize;
						
						if( Radians != 0.0f )
						{
							MaMat4d Rotation;
							Rotation.rotation( MaVec3d( 0.0f, 0.0f, Radians ) );
							CornerA = CornerA * Rotation;
							CornerB = CornerB * Rotation;
							CornerC = CornerC * Rotation;
							CornerD = CornerD * Rotation;
						}

						const BcU32 Colour = Particle.Colour_.asABGR();

						// Grab vertices.
						ScnParticleVertex& VertexA = *pVertex++;
						ScnParticleVertex& VertexB = *pVertex++;
						ScnParticleVertex& VertexC = *pVertex++;

						ScnParticleVertex& VertexD = *pVertex++;
						ScnParticleVertex& VertexE = *pVertex++;
						ScnParticleVertex& VertexF = *pVertex++;
			
						// 
						VertexA.X_ = Particle.Position_.x();
						VertexA.Y_ = Particle.Position_.y();
						VertexA.Z_ = Particle.Position_.z();
						VertexA.W_ = 1.0f;
						VertexA.NX_ = CornerA.x();
						VertexA.NY_ = CornerA.y();
						VertexA.NZ_ = 0.0f;
						VertexA.U_ = UVBounds.x();
						VertexA.V_ = UVBounds.y();
						VertexA.RGBA_ = Colour;

						// 
						VertexB.X_ = Particle.Position_.x();
						VertexB.Y_ = Particle.Position_.y();
						VertexB.Z_ = Particle.Position_.z();
						VertexB.W_ = 1.0f;
						VertexB.NX_ = CornerB.x();
						VertexB.NY_ = CornerB.y();
						VertexB.NZ_ = 0.0f;
						VertexB.U_ = UVBounds.z();
						VertexB.V_ = UVBounds.y();
						VertexB.RGBA_ = Colour;

						// 
						VertexC.X_ = Particle.Position_.x();
						VertexC.Y_ = Particle.Position_.y();
						VertexC.Z_ = Particle.Position_.z();
						VertexC.W_ = 1.0f;
						VertexC.NX_ = CornerC.x();
						VertexC.NY_ = CornerC.y();
						VertexC.NZ_ = 0.0f;
						VertexC.U_ = UVBounds.z();
						VertexC.V_ = UVBounds.w();
						VertexC.RGBA_ = Colour;

						// 
						VertexD.X_ = Particle.Position_.x();
						VertexD.Y_ = Particle.Position_.y();
						VertexD.Z_ = Particle.Position_.z();
						VertexD.W_ = 1.0f;
						VertexD.NX_ = CornerC.x();
						VertexD.NY_ = CornerC.y();
						VertexD.NZ_ = 0.0f;
						VertexD.U_ = UVBounds.z();
						VertexD.V_ = UVBounds.w();
						VertexD.RGBA_ = Colour;

						// 
						VertexE.X_ = Particle.Position_.x();
						VertexE.Y_ = Particle.Position_.y();
						VertexE.Z_ = Particle.Position_.z();
						VertexE.W_ = 1.0f;
						VertexE.NX_ = CornerD.x();
						VertexE.NY_ = CornerD.y();
						VertexE.NZ_ = 0.0f;
						VertexE.U_ = UVBounds.x();
						VertexE.V_ = UVBounds.w();
						VertexE.RGBA_ = Colour;

						// 
						VertexF.X_ = Particle.Position_.x();
						VertexF.Y_ = Particle.Position_.y();
						VertexF.Z_ = Particle.Position_.z();
						VertexF.W_ = 1.0f;
						VertexF.NX_ = CornerA.x();
						VertexF.NY_ = CornerA.y();
						VertexF.NZ_ = 0.0f;
						VertexF.U_ = UVBounds.x();
						VertexF.V_ = UVBounds.y();
						VertexF.RGBA_ = Colour;
					}
				}

				BcAssert( NoofParticlesRendered == NoofParticlesToRender );
				UploadFence_.decrement();
			} );
	}

	// Update uniform buffer.
	if( IsLocalSpace_ )
	{
		VertexBuffer.ObjectUniforms_.WorldTransform_ = getParentEntity()->getWorldMatrix();
	}
	else
	{
		VertexBuffer.ObjectUniforms_.WorldTransform_ = MaMat4d();
	}

	// Upload uniforms.
	RsCore::pImpl()->updateBuffer( 
		VertexBuffer.UniformBuffer_,
		0, sizeof( VertexBuffer.ObjectUniforms_ ),
		RsResourceUpdateFlags::ASYNC,
		[ this, VertexBuffer ]( RsBuffer* Buffer, const RsBufferLock& Lock )
		{
			BcMemCopy( Lock.Buffer_, &VertexBuffer.ObjectUniforms_, sizeof( VertexBuffer.ObjectUniforms_ ) );
		} );

	// Draw particles last.
	if( NoofParticlesToRender > 0 )
	{
		Sort.Layer_ = 15;

		// Set material parameters for view.
		pViewComponent->setMaterialParameters( MaterialComponent_ );

		// Bind material component.
		MaterialComponent_->bind( pFrame, Sort );

		// Setup render node.
		ScnParticleSystemComponentRenderNode* pRenderNode = pFrame->newObject< ScnParticleSystemComponentRenderNode >();
		pRenderNode->VertexBuffer_ = VertexBuffer.pVertexBuffer_;
		pRenderNode->VertexDeclaration_ = VertexDeclaration_;
		pRenderNode->NoofIndices_ = NoofParticlesToRender * 6;

		// Add to frame.
		pRenderNode->Sort_ = Sort;
		pFrame->addRenderNode( pRenderNode );
	}
}