////////////////////////////////////////////////////////////////////////// // findLongestDirection MaVec3d GaEnemyComponent::findLongestDirection() { // Ray cast in the 4 directions to determine path. BcF32 LongestDistance = 0.0f; MaVec3d LongestDirection( 0.0f, 0.0f, 0.0f ); MaVec3d Directions[] = { MaVec3d( 1.0f, 0.0f, 0.0f ), MaVec3d( -1.0f, 0.0f, 0.0f ), MaVec3d( 0.0f, 1.0f, 0.0f ), MaVec3d( 0.0f, -1.0f, 0.0f ), }; MaVec3d Position = getParentEntity()->getWorldPosition(); BcBSPPointInfo BSPPointInfo; for( BcU32 Idx = 0; Idx < 4; ++Idx ) { if( BSP_->lineIntersection( Position, Position + Directions[ Idx ] * 256.0f, &BSPPointInfo ) ) { if( BSPPointInfo.Distance_ > LongestDistance ) { LongestDistance = BSPPointInfo.Distance_; LongestDirection = Directions[ Idx ]; } } } BcPrintf( "GaEnemyComponent: Got direction [%.1f, %.1f, %.f1]\n", LongestDirection.x(), LongestDirection.y(), LongestDirection.z() ); return LongestDirection; }
virtual void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) { ScnDebugRenderComponent::pImpl()->drawLine( MaVec3d( from.x(), from.y(), from.z() ), MaVec3d( to.x(), to.y(), to.z() ), RsColour( color.x(), color.y(), color.z(), 1.0f ) ); }
////////////////////////////////////////////////////////////////////////// // drawEllipsoid void ScnDebugRenderComponent::drawEllipsoid( const MaVec3d& Position, const MaVec3d& Size, const RsColour& Colour, BcU32 Layer ) { // Draw outer circles for all axis. BcU32 LOD = 16; BcF32 Angle = 0.0f; BcF32 AngleInc = ( BcPI * 2.0f ) / BcF32( LOD ); // Draw axis lines. for( BcU32 i = 0; i < LOD; ++i ) { MaVec2d PosA( BcCos( Angle ), -BcSin( Angle ) ); MaVec2d PosB( BcCos( Angle + AngleInc ), -BcSin( Angle + AngleInc ) ); MaVec3d XAxisA = MaVec3d( 0.0f, PosA.x() * Size.y(), PosA.y() * Size.z() ); MaVec3d YAxisA = MaVec3d( PosA.x() * Size.x(), 0.0f, PosA.y() * Size.z() ); MaVec3d ZAxisA = MaVec3d( PosA.x() * Size.x(), PosA.y() * Size.y(), 0.0f ); MaVec3d XAxisB = MaVec3d( 0.0f, PosB.x() * Size.y(), PosB.y() * Size.z() ); MaVec3d YAxisB = MaVec3d( PosB.x() * Size.x(), 0.0f, PosB.y() * Size.z() ); MaVec3d ZAxisB = MaVec3d( PosB.x() * Size.x(), PosB.y() * Size.y(), 0.0f ); drawLine( XAxisA + Position, XAxisB + Position, Colour, 0 ); drawLine( YAxisA + Position, YAxisB + Position, Colour, 0 ); drawLine( ZAxisA + Position, ZAxisB + Position, Colour, 0 ); Angle += AngleInc; } // Draw a cross down centre. MaVec3d XAxis = MaVec3d( Size.x(), 0.0f, 0.0f ); MaVec3d YAxis = MaVec3d( 0.0f, Size.y(), 0.0f ); MaVec3d ZAxis = MaVec3d( 0.0f, 0.0f, Size.z() ); drawLine( Position - XAxis, Position + XAxis, Colour, Layer ); drawLine( Position - YAxis, Position + YAxis, Colour, Layer ); drawLine( Position - ZAxis, Position + ZAxis, Colour, Layer ); }
////////////////////////////////////////////////////////////////////////// // setMaterialParameters void ScnLightComponent::setMaterialParameters( BcU32 LightIndex, ScnMaterialComponent* MaterialComponent ) { const MaVec4d& Position( getParentEntity()->getWorldMatrix().row3() ); const MaVec4d& Direction( getParentEntity()->getWorldMatrix().row2() ); MaterialComponent->setLightParameters( LightIndex, MaVec3d( Position.x(), Position.y(), Position.z() ), MaVec3d( Direction.x(), Direction.y(), Direction.z() ), AmbientColour_, DiffuseColour_, AttnC_, AttnL_, AttnQ_ ); }
//////////////////////////////////////////////////////////////////////////b // initialise void GaRobotComponent::initialise( const Json::Value& Object ) { Team_ = Object[ "team" ].asUInt(); TargetDistance_ = 1.0f; TargetPosition_ = MaVec3d( 0.0f, 0.0f, 0.0f ); MaxVelocity_ = 4.0f; Health_ = 100.0f; Energy_ = 0.0f; EnergyChargeRate_ = 5.0f; WeaponACoolDown_ = 0.1f; WeaponACost_ = 10.0f; WeaponATimer_ = 0.0f; WeaponBCoolDown_ = 4.0f; WeaponBCost_ = 50.0f; WeaponBTimer_ = 0.0f; MoveTimer_ = 0.0f; CurrentState_ = 0; CurrentOp_ = 0; NextOp_ = 0; CurrentOpTimer_ = 0.0f; CurrentOpTime_ = 0.1f; MoveAngle_ = 0.0f; }
////////////////////////////////////////////////////////////////////////// // initialise //virtual void ScnSoundEmitterComponent::initialise( const Json::Value& Object ) { Super::initialise(); Position_ = MaVec3d( 0.0f, 0.0f, 0.0f ); Gain_ = 1.0f; Pitch_ = 1.0f; }
////////////////////////////////////////////////////////////////////////// // updateParticles void ScnParticleSystemComponent::updateParticles( BcF32 Tick ) { // Wait for upload to have completed. UploadFence_.wait(); // TODO: Iterate over every "affector" at a time, rather than by particle. // - See "updateParticle". MaAABB FullAABB( MaVec3d( 0.0f, 0.0f, 0.0f ), MaVec3d( 0.0f, 0.0f, 0.0f ) ); // Not optimal, but clear code is clear. (For now...) for( BcU32 Idx = 0; Idx < NoofParticles_; ++Idx ) { ScnParticle& Particle = pParticleBuffer_[ Idx ]; if( Particle.Alive_ ) { // Update particle. updateParticle( Particle, Tick ); // Expand AABB by particle's max bounds. const BcF32 MaxHalfSize = BcMax( Particle.Scale_.x(), Particle.Scale_.y() ) * 0.5f; FullAABB.expandBy( Particle.Position_ - MaVec3d( MaxHalfSize, MaxHalfSize, MaxHalfSize ) ); FullAABB.expandBy( Particle.Position_ + MaVec3d( MaxHalfSize, MaxHalfSize, MaxHalfSize ) ); } } // Transform AABB. if( IsLocalSpace_ ) { const MaMat4d& WorldTransform = getParentEntity()->getWorldMatrix(); AABB_ = FullAABB.transform( WorldTransform ); } else { AABB_ = FullAABB; } UpdateFence_.decrement(); }
////////////////////////////////////////////////////////////////////////// // 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(); } }
////////////////////////////////////////////////////////////////////////// // addPrimitive void ScnCanvasComponent::addPrimitive( RsTopologyType Type, ScnCanvasComponentVertex* pVertices, BcU32 NoofVertices, BcU32 Layer, BcBool UseMatrixStack ) { BcAssertMsg( MaterialComponent_.isValid(), "ScnCanvasComponent: Material component has not been set!" ); // Check if the vertices are owned by us, if not copy in. if( pVertices < pVertices_ || pVertices_ >= pVerticesEnd_ ) { ScnCanvasComponentVertex* pNewVertices = allocVertices( NoofVertices ); if( pNewVertices != NULL ) { BcMemCopy( pNewVertices, pVertices, sizeof( ScnCanvasComponentVertex ) * NoofVertices ); pVertices = pNewVertices; } } // Matrix stack. if( UseMatrixStack == BcTrue && IsIdentity_ == BcFalse ) { MaMat4d Matrix = getMatrix(); for( BcU32 Idx = 0; Idx < NoofVertices; ++Idx ) { ScnCanvasComponentVertex* pVertex = &pVertices[ Idx ]; MaVec3d Vertex = MaVec3d( pVertex->X_, pVertex->Y_, pVertex->Z_ ) * Matrix; pVertex->X_ = Vertex.x(); pVertex->Y_ = Vertex.y(); pVertex->Z_ = Vertex.z(); } } // TODO: If there was a previous primitive which we can marge into, attempt to. BcU32 VertexIndex = convertVertexPointerToIndex( pVertices ); ScnCanvasComponentPrimitiveSection PrimitiveSection = { Type, VertexIndex, NoofVertices, Layer, MaterialComponent_, nullptr }; PrimitiveSectionList_.push_back( PrimitiveSection ); LastPrimitiveSection_ = (BcU32)PrimitiveSectionList_.size() - 1; }
////////////////////////////////////////////////////////////////////////// // lineCast BcBool ScnPhysicsWorldComponent::lineCast( const MaVec3d& A, const MaVec3d& B, MaVec3d& Intersection, MaVec3d& Normal ) { btCollisionWorld::ClosestRayResultCallback HitResult( btVector3( A.x(), A.y(), A.z() ), btVector3( B.x(), B.y(), B.z() ) ); DynamicsWorld_->rayTest( btVector3( A.x(), A.y(), A.z() ), btVector3( B.x(), B.y(), B.z() ), HitResult ); if( HitResult.hasHit() ) { Intersection = MaVec3d( HitResult.m_hitPointWorld.x(), HitResult.m_hitPointWorld.y(), HitResult.m_hitPointWorld.z() ); Normal = MaVec3d( HitResult.m_hitNormalWorld.x(), HitResult.m_hitNormalWorld.y(), HitResult.m_hitNormalWorld.z() ); return BcTrue; } return BcFalse; }
////////////////////////////////////////////////////////////////////////// // fireWeaponB void GaRobotComponent::fireWeaponB( BcF32 Radius ) { if( WeaponBTimer_ < 0.0f && Energy_ > WeaponBCost_ ) { auto Robots = getRobots( 1 - Team_ ); if( Robots.size() > 0 ) { Energy_ -= WeaponBCost_; WeaponBTimer_ = WeaponBCoolDown_; MoveTimer_ = WeaponBCoolDown_ * 0.1f; // Spawn entity. ScnEntitySpawnParams EntityParams = { "default", BcName( "WeaponEntity", 1 ), BcName( "WeaponEntity", 1 ), getParentEntity()->getLocalMatrix(), getParentEntity()->getParentEntity() }; auto Entity = ScnCore::pImpl()->spawnEntity( EntityParams ); BcAssert( Entity != nullptr ); auto WeaponComponent = Entity->getComponentByType< GaWeaponComponent >(); WeaponComponent->TargetPosition_ = Robots[ 0 ]->getParentEntity()->getLocalPosition(); // Randomise target position slightly. WeaponComponent->TargetPosition_ += MaVec3d( BcRandom::Global.randRealRange( -1.0f, 1.0f ), 0.0f, BcRandom::Global.randRealRange( -1.0f, 1.0f ) ).normal() * Radius; playSound( "weaponb" ); } } else { playSound( "fail" ); } }
////////////////////////////////////////////////////////////////////////// // 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 ); }
////////////////////////////////////////////////////////////////////////// // drawSpriteCentered void ScnCanvasComponent::drawSpriteCenteredUp3D( const MaVec3d& Position, const MaVec2d& Size, BcU32 TextureIdx, const RsColour& Colour, BcU32 Layer ) { MaVec3d NewPosition = Position - MaVec3d( Size.x() * 0.5f, 0.0f, Size.y() * 0.5f ); drawSpriteUp3D( NewPosition, Size, TextureIdx, Colour, Layer ); }
{ NearestWeapon = Weapon; } } MaVec3d AvoidPosition = NearestWeapon->TargetPosition_; #else MaVec3d AvoidPosition; for( auto* Weapon : Weapons ) { auto WeaponPosition = Weapon->getParentEntity()->getLocalPosition(); AvoidPosition += WeaponPosition;; } AvoidPosition /= BcF32( Weapons.size() ); #endif auto Direction = ( LocalPosition - AvoidPosition ); auto Perp = MaVec3d( Direction.z(), 0.0f, -Direction.x() ); // Move nearest point away. ThisRobot->TargetPosition_ = LocalPosition + Perp.normal() * Distance; return BcErrorCode; } }, /** * Attack weapon a. * Radius to spray. */ { "op_attack_a", []( GaRobotComponent* ThisRobot, BcU32 Radius )->BcU32 {
////////////////////////////////////////////////////////////////////////// // drawGrid void ScnDebugRenderComponent::drawGrid( const MaVec3d& Position, const MaVec3d& Size, BcF32 StepSize, BcF32 SubDivideMultiple, BcU32 Layer ) { BcU32 NoofAxis = ( Size.x() > 0 ? 1 : 0 ) + ( Size.y() > 0 ? 1 : 0 ) + ( Size.z() > 0 ? 1 : 0 ); BcAssertMsg( NoofAxis == 2, "Only supports 2 axis in the grid!" ); // Determine which axis to draw along. MaVec3d XAxis; MaVec3d YAxis; BcF32 XSize = 0.0f; BcF32 YSize = 0.0f; if( Size.x() > 0.0f ) { XAxis = MaVec3d( Size.x(), 0.0f, 0.0f ); XSize = Size.x(); if( Size.y() > 0.0f ) { YAxis = MaVec3d( 0.0f, Size.y(), 0.0f ); YSize = Size.y(); } else { YAxis = MaVec3d( 0.0f, 0.0f, Size.z() ); YSize = Size.z(); } } else { XAxis = MaVec3d( 0.0f, Size.y(), 0.0f ); YAxis = MaVec3d( 0.0f, 0.0f, Size.z() ); XSize = Size.y(); YSize = Size.z(); } // Normalise. XAxis.normalise(); YAxis.normalise(); while( StepSize < XSize && StepSize < YSize ) { // Draw grid. for( BcF32 X = 0.0f; X <= XSize; X += StepSize ) { MaVec3d A1( Position + ( XAxis * X ) + ( YAxis * YSize ) ); MaVec3d B1( Position + ( XAxis * X ) + ( YAxis * -YSize ) ); MaVec3d A2( Position + ( XAxis * -X ) + ( YAxis * YSize ) ); MaVec3d B2( Position + ( XAxis * -X ) + ( YAxis * -YSize ) ); drawLine( A1, B1, RsColour( 1.0f, 1.0f, 1.0f, 0.05f ), Layer ); drawLine( A2, B2, RsColour( 1.0f, 1.0f, 1.0f, 0.05f ), Layer ); } for( BcF32 Y = 0.0f; Y <= YSize; Y += StepSize ) { MaVec3d A1( Position + ( XAxis * YSize ) + ( YAxis * Y ) ); MaVec3d B1( Position + ( XAxis * -YSize ) + ( YAxis * Y ) ); MaVec3d A2( Position + ( XAxis * YSize ) + ( YAxis * -Y ) ); MaVec3d B2( Position + ( XAxis * -YSize ) + ( YAxis * -Y ) ); drawLine( A1, B1, RsColour( 1.0f, 1.0f, 1.0f, 0.05f ), Layer ); drawLine( A2, B2, RsColour( 1.0f, 1.0f, 1.0f, 0.05f ), Layer ); } StepSize *= SubDivideMultiple; } }
////////////////////////////////////////////////////////////////////////// // drawLine void ScnCanvasComponent::drawLine( const MaVec2d& PointA, const MaVec2d& PointB, const RsColour& Colour, BcU32 Layer ) { ScnCanvasComponentVertex* pVertices = allocVertices( 2 ); ScnCanvasComponentVertex* pFirstVertex = pVertices; // Only draw if we can allocate vertices. if( pVertices != NULL ) { // Now copy in data. BcU32 ABGR = Colour.asABGR(); pVertices->X_ = PointA.x(); pVertices->Y_ = PointA.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->ABGR_ = ABGR; ++pVertices; pVertices->X_ = PointB.x(); pVertices->Y_ = PointB.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->ABGR_ = ABGR; // Quickly check last primitive. BcBool AddNewPrimitive = BcTrue; if( LastPrimitiveSection_ != BcErrorCode ) { ScnCanvasComponentPrimitiveSection& PrimitiveSection = PrimitiveSectionList_[ LastPrimitiveSection_ ]; // If the last primitive was the same type as ours we can append to it. // NOTE: Need more checks here later. if( PrimitiveSection.Type_ == RsTopologyType::LINE_LIST && PrimitiveSection.Layer_ == Layer && PrimitiveSection.MaterialComponent_ == MaterialComponent_ ) { PrimitiveSection.NoofVertices_ += 2; // Matrix stack. // TODO: Factor into a seperate function. if( IsIdentity_ == BcFalse ) { MaMat4d Matrix = getMatrix(); for( BcU32 Idx = 0; Idx < 2; ++Idx ) { ScnCanvasComponentVertex* pVertex = &pFirstVertex[ Idx ]; MaVec3d Vertex = MaVec3d( pVertex->X_, pVertex->Y_, pVertex->Z_ ) * Matrix; pVertex->X_ = Vertex.x(); pVertex->Y_ = Vertex.y(); pVertex->Z_ = Vertex.z(); pVertices->W_ = 1.0f; } } AddNewPrimitive = BcFalse; } } // Add primitive. if( AddNewPrimitive == BcTrue ) { addPrimitive( RsTopologyType::LINE_LIST, pFirstVertex, 2, Layer, BcTrue ); } } }
////////////////////////////////////////////////////////////////////////// // drawSprite void ScnCanvasComponent::drawSprite( const MaVec2d& Position, const MaVec2d& Size, BcU32 TextureIdx, const RsColour& Colour, BcU32 Layer ) { ScnCanvasComponentVertex* pVertices = allocVertices( 6 ); ScnCanvasComponentVertex* pFirstVertex = pVertices; const MaVec2d CornerA = Position; const MaVec2d CornerB = Position + Size; const ScnRect Rect = DiffuseTexture_.isValid() ? DiffuseTexture_->getRect( TextureIdx ) : ScnRect(); // Only draw if we can allocate vertices. if( pVertices != NULL ) { // Now copy in data. BcU32 ABGR = Colour.asABGR(); pVertices->X_ = CornerA.x(); pVertices->Y_ = CornerA.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->U_ = Rect.X_; pVertices->V_ = Rect.Y_; pVertices->ABGR_ = ABGR; ++pVertices; pVertices->X_ = CornerB.x(); pVertices->Y_ = CornerA.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->U_ = Rect.X_ + Rect.W_; pVertices->V_ = Rect.Y_; pVertices->ABGR_ = ABGR; ++pVertices; pVertices->X_ = CornerA.x(); pVertices->Y_ = CornerB.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->U_ = Rect.X_; pVertices->V_ = Rect.Y_ + Rect.H_; pVertices->ABGR_ = ABGR; ++pVertices; pVertices->X_ = CornerA.x(); pVertices->Y_ = CornerB.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->U_ = Rect.X_; pVertices->V_ = Rect.Y_ + Rect.H_; pVertices->ABGR_ = ABGR; ++pVertices; pVertices->X_ = CornerB.x(); pVertices->Y_ = CornerA.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->U_ = Rect.X_ + Rect.W_; pVertices->V_ = Rect.Y_; pVertices->ABGR_ = ABGR; ++pVertices; pVertices->X_ = CornerB.x(); pVertices->Y_ = CornerB.y(); pVertices->Z_ = 0.0f; pVertices->W_ = 1.0f; pVertices->U_ = Rect.X_ + Rect.W_; pVertices->V_ = Rect.Y_ + Rect.H_; pVertices->ABGR_ = ABGR; // Quickly check last primitive. BcBool AddNewPrimitive = BcTrue; if( LastPrimitiveSection_ != BcErrorCode ) { ScnCanvasComponentPrimitiveSection& PrimitiveSection = PrimitiveSectionList_[ LastPrimitiveSection_ ]; // If the last primitive was the same type as ours we can append to it. // NOTE: Need more checks here later. if( PrimitiveSection.Type_ == RsTopologyType::TRIANGLE_LIST && PrimitiveSection.Layer_ == Layer && PrimitiveSection.MaterialComponent_ == MaterialComponent_ ) { PrimitiveSection.NoofVertices_ += 6; // Matrix stack. // TODO: Factor into a seperate function. if( IsIdentity_ == BcFalse ) { MaMat4d Matrix = getMatrix(); for( BcU32 Idx = 0; Idx < 6; ++Idx ) { ScnCanvasComponentVertex* pVertex = &pFirstVertex[ Idx ]; MaVec3d Vertex = MaVec3d( pVertex->X_, pVertex->Y_, pVertex->Z_ ) * Matrix; pVertex->X_ = Vertex.x(); pVertex->Y_ = Vertex.y(); pVertex->Z_ = Vertex.z(); pVertex->W_ = 1.0f; } } AddNewPrimitive = BcFalse; } } // Add primitive. if( AddNewPrimitive == BcTrue ) { addPrimitive( RsTopologyType::TRIANGLE_LIST, pFirstVertex, 6, Layer, BcTrue ); } } }
////////////////////////////////////////////////////////////////////////// // import BcBool ScnAnimationImport::import( const Json::Value& ) { #if PSY_IMPORT_PIPELINE if( Source_.empty() ) { PSY_LOG( "ERROR: Missing 'source' field.\n" ); return BcFalse; } CsResourceImporter::addDependency( Source_.c_str() ); auto PropertyStore = aiCreatePropertyStore(); aiLogStream AssimpLogger = { AssimpLogStream, (char*)this }; aiAttachLogStream( &AssimpLogger ); Scene_ = aiImportFileExWithProperties( Source_.c_str(), 0, nullptr, PropertyStore ); aiReleasePropertyStore( PropertyStore ); if( Scene_ != nullptr ) { PSY_LOG( "Found %u animations:\n", Scene_->mNumAnimations ); for( int Idx = 0; Idx < (int)Scene_->mNumAnimations; ++Idx ) { PSY_LOG( " - %s\n", Scene_->mAnimations[ Idx ]->mName.C_Str() ); } // Build animated nodes list. Need this to calculate relative transforms later. recursiveParseAnimatedNodes( Scene_->mRootNode, BcErrorCode ); // Pack down animation into useful internal format. BcAssert( Scene_->mNumAnimations == 1 ); for( BcU32 AnimationIdx = 0; AnimationIdx < 1; ++AnimationIdx ) { auto* Animation = Scene_->mAnimations[ AnimationIdx ]; BcF32 Rate = 1.0f; BcU32 Duration = static_cast< BcU32 >( Animation->mDuration / Rate ); // Setup data streams. ScnAnimationHeader Header; Header.NoofNodes_ = Animation->mNumChannels; Header.NoofPoses_ = Duration; Header.Flags_ = scnAF_DEFAULT; Header.Packing_ = scnAP_R16S16T16; // TODO: Make this configurable when we factor out into another class. HeaderStream_ << Header; // Animation node file data. ScnAnimationNodeFileData NodeFileData; for( BcU32 NodeIdx = 0; NodeIdx < Animation->mNumChannels; ++NodeIdx ) { auto* Channel = Animation->mChannels[ NodeIdx ]; NodeFileData.Name_ = CsResourceImporter::addString( Channel->mNodeName.C_Str() ); NodeStream_ << NodeFileData; } // Calculate output pose. for( BcF32 Time = 0.0f; Time <= Animation->mDuration; Time += Rate ) { ScnAnimationPoseFileData Pose; Pose.Time_ = Time / FrameRate_; Pose.KeyDataOffset_ = static_cast< BcU32 >( KeyStream_.dataSize() ); // Iterate over all node channels to generate keys. for( BcU32 ChannelIdx = 0; ChannelIdx < Animation->mNumChannels; ++ChannelIdx ) { auto* Channel = Animation->mChannels[ ChannelIdx ]; auto& AnimatedNode = findAnimatedNode( Channel->mNodeName.C_Str() ); aiVector3D OutPositionKey; aiVector3D OutScaleKey; aiQuaternion OutRotationKey; // Extract position. GetKeyNodeAnim( Channel->mPositionKeys, Channel->mNumPositionKeys, Time, BcTrue, OutPositionKey ); // Extract scale. GetKeyNodeAnim( Channel->mScalingKeys, Channel->mNumScalingKeys, Time, BcTrue, OutScaleKey ); // Extract rotation. GetKeyNodeAnim( Channel->mRotationKeys, Channel->mNumRotationKeys, Time, BcTrue, OutRotationKey ); // Combine key into transform. ScnAnimationTransform Transform; Transform.R_ = MaQuat( OutRotationKey.x, OutRotationKey.y, OutRotationKey.z, OutRotationKey.w ); Transform.S_ = MaVec3d( OutScaleKey.x, OutScaleKey.y, OutScaleKey.z ); Transform.T_ = MaVec3d( OutPositionKey.x, OutPositionKey.y, OutPositionKey.z ); // Store as local matrix. Transform.toMatrix( AnimatedNode.LocalTransform_ ); } // Calculate local node matrices relative to their parents. for( auto& AnimatedNode : AnimatedNodes_ ) { if( AnimatedNode.ParentIdx_ != BcErrorCode ) { auto& ParentAnimatedNode( AnimatedNodes_[ AnimatedNode.ParentIdx_ ] ); MaMat4d ParentLocal = ParentAnimatedNode.LocalTransform_; AnimatedNode.WorldTransform_ = ParentLocal * AnimatedNode.LocalTransform_; } else { AnimatedNode.WorldTransform_ = AnimatedNode.LocalTransform_; } } // Write out pose keys. ScnAnimationTransformKey_R16S16T16 OutKey; for( BcU32 ChannelIdx = 0; ChannelIdx < Animation->mNumChannels; ++ChannelIdx ) { auto* Channel = Animation->mChannels[ ChannelIdx ]; const auto& AnimatedNode = findAnimatedNode( Channel->mNodeName.C_Str() ); // Extract individual transform elements. ScnAnimationTransform Transform; Transform.fromMatrix( AnimatedNode.LocalTransform_ ); // Pack into output key. OutKey.pack( Transform.R_, Transform.S_, Transform.T_ ); KeyStream_ << OutKey; } // Final size + CRC. Pose.KeyDataSize_ = static_cast< BcU32 >( KeyStream_.dataSize() - Pose.KeyDataOffset_ ); Pose.CRC_ = BcHash::GenerateCRC32( 0, KeyStream_.pData() + Pose.KeyDataOffset_, Pose.KeyDataSize_ ); // Write out pose. PoseStream_ << Pose; } // Write out chunks. CsResourceImporter::addChunk( BcHash( "header" ), HeaderStream_.pData(), HeaderStream_.dataSize(), 16, csPCF_IN_PLACE ); CsResourceImporter::addChunk( BcHash( "nodes" ), NodeStream_.pData(), NodeStream_.dataSize() ); CsResourceImporter::addChunk( BcHash( "poses" ), PoseStream_.pData(), PoseStream_.dataSize() ); CsResourceImporter::addChunk( BcHash( "keys" ), KeyStream_.pData(), KeyStream_.dataSize() ); } aiReleaseImport( Scene_ ); Scene_ = nullptr; // return BcTrue; } #endif // PSY_IMPORT_PIPELINE return BcFalse; }
////////////////////////////////////////////////////////////////////////// // update void GaStructureComponent::update( BcF32 Tick ) { // Get water. auto Water = getParentEntity()->getComponentAnyParentByType< GaWaterComponent >(); // If we have point masses, calculate position. const size_t NoofPointMasses = Physics_->getNoofPointMasses(); if( Physics_->getNoofPointMasses() > 0 ) { // Points with weight. const BcF32 Gravity = 500.0f; for( auto WeightedPoint : WeightedPoints_ ) { Physics_->setPointMassAcceleration( WeightedPoint, MaVec2d( 0.0f, Gravity ) ); } // Points with bouyancy. for( auto BouyantPoint : BouyantPoints_ ) { const auto& PointMass = Physics_->getPointMass( BouyantPoint ); auto WaterPosition = Water->getWaterSurfacePosition( PointMass.CurrPosition_ ); BcF32 Diff = PointMass.CurrPosition_.y() - WaterPosition.y(); if( Diff > 1.0f ) { Physics_->setPointMassAcceleration( BouyantPoint, MaVec2d( 0.0f, -Gravity ) ); } else { Physics_->setPointMassAcceleration( BouyantPoint, MaVec2d( 0.0f, Gravity ) ); } } // Position. MaVec2d Centre( Physics_->getPointMassPosition( 0 ) ); getParentEntity()->setLocalPosition( MaVec3d( Centre, 0.0f ) ); // Rotation. MaVec2d Direction = Centre - Physics_->getPointMassPosition( 1 ); BcF32 Angle = std::atan2f( Direction.x(), Direction.y() ); Sprite_->setRotation( Angle ); if( Timer_ <= CalculatedFireRate_ ) { Timer_ += Tick; } } switch( StructureType_ ) { case GaStructureType::BASE: break; case GaStructureType::TURRET: if( Timer_ >= CalculatedFireRate_ ) { // Find a tentacle. GaTentacleComponent* NearestTentacle = Game_->getNearestTentacle( getParentEntity()->getWorldPosition().xy(), BcFalse ); if( NearestTentacle == nullptr ) { NearestTentacle = Game_->getNearestTentacle( getParentEntity()->getWorldPosition().xy(), BcTrue ); } // Spawn a projectile. if( NearestTentacle && TemplateProjectile_ ) { auto Entity = ScnCore::pImpl()->spawnEntity( ScnEntitySpawnParams( BcName::INVALID, TemplateProjectile_, getParentEntity()->getWorldMatrix(), Game_->getParentEntity() ) ); auto Projectile = Entity->getComponentByType< GaProjectileComponent >(); Projectile->setLevel( Level_ ); Projectile->setTarget( NearestTentacle->getParentEntity() ); Game_->launchProjectile( Projectile ); // Time to spawn! Timer_ -= CalculatedFireRate_; } } break; case GaStructureType::RESOURCE: break; case GaStructureType::POTATO: break; case GaStructureType::MINE: break; } }
MaVec3d MaAABB::corner( BcU32 i ) const { return MaVec3d( ( i & 1 ) ? Min_.x() : Max_.x(), ( i & 2 ) ? Min_.y() : Max_.y(), ( i & 4 ) ? Min_.z() : Max_.z() ); }
////////////////////////////////////////////////////////////////////////// // rayIntersect BcBool MaAABB::lineIntersect( const MaVec3d& Start, const MaVec3d& End, MaVec3d* pIntersectionPoint, MaVec3d* pIntersectionNormal ) const { // Planes. Screw it. // Totally inoptimal. MaPlane Planes[6]; MaVec3d Intersects[6]; BcF32 Distance; for( BcU32 i = 0; i < 6; ++i ) { Planes[i] = facePlane( i ); if( !Planes[i].lineIntersection( Start, End, Distance, Intersects[i] ) ) { Intersects[i] = MaVec3d( 1e24f, 1e24f, 1e24f ); } } // Reject classified and find nearest. BcF32 Nearest = 1e24f; BcU32 iNearest = BcErrorCode; for( BcU32 i = 0; i < 6; ++i ) { // For every point... // ...check against planes. BcBool Valid = BcTrue; for( BcU32 j = 0; j < 6; ++j ) { if( Planes[j].classify( Intersects[i] ) == MaPlane::bcPC_FRONT ) { Valid = BcFalse; break; } } // If its valid, check distance. if( Valid ) { BcF32 Distance = ( Start - Intersects[i] ).magnitudeSquared(); if( Distance < Nearest ) { Nearest = Distance; iNearest = i; } } } // if( iNearest != BcErrorCode ) { if( pIntersectionPoint != NULL ) { *pIntersectionPoint = Intersects[ iNearest ]; } if( pIntersectionNormal != NULL ) { *pIntersectionNormal = Planes[ iNearest ].normal(); } return BcTrue; } return BcFalse; }
////////////////////////////////////////////////////////////////////////// // update //virtual void GaEnemyComponent::update( BcF32 Tick ) { // Find move vector. BcF32 MoveSpeed = 2.0f; MaVec3d MoveVector; PulseTimer_ += Tick; // Set direction and handle if we need to change direction. MaVec3d Position = getParentEntity()->getWorldPosition(); MoveVector = Direction_; BcBSPPointInfo BSPPointInfo; if( BSP_->lineIntersection( Position, Position + Direction_ * 256.0f, &BSPPointInfo ) ) { if( BSPPointInfo.Distance_ < 1.0f ) { // New direction. Direction_ = Direction_.reflect( BSPPointInfo.Plane_.normal() ); // Belt & braces: it will become denormalised over time. Direction_.normalise(); // Clamp position (this is to prevent wandering, if angles aren't sitting on an integer, expect weirdness). Position = MaVec3d( BcRound( Position.x() ), BcRound( Position.y() ), BcRound( Position.z() ) ); } } BcF32 PulseTime = 2.0f; if( BSP_->canSeePlayer( Position ) ) { BcPrintf( "I CAN SEE UUU\n" ); PulseTime = 0.5f; IsTargetting_ = BcTrue; TargetTimer_ -= Tick; } else { IsTargetting_ = BcFalse; TargetTimer_ = 3.0f; } // Pulse. if( PulseTimer_ > PulseTime ) { Pressure_->setSample( Position, 0.5f ); PulseTimer_ = 0.0f; } // Shoot player. if( IsTargetting_ && TargetTimer_ < 0.0f ) { BSP_->killPlayer(); } // Set the move. if( !IsTargetting_ ) { MaVec3d AppliedMoveVector = MoveVector; AppliedMoveVector.z( 0.0f ); AppliedMoveVector = AppliedMoveVector.normal() * MoveSpeed; Pawn_->setMove( AppliedMoveVector ); } else { Pawn_->setMove( MaVec3d( 0.0f, 0.0f, 0.0f ) ); } }
//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 ); } }
//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 ); } }