Initial commit
This commit is contained in:
540
engine/game/aiClient.cc
Executable file
540
engine/game/aiClient.cc
Executable file
@@ -0,0 +1,540 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "game/aiClient.h"
|
||||
#include "core/realComp.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "game/player.h"
|
||||
#include "game/moveManager.h"
|
||||
|
||||
#include "console/consoleInternal.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT( AIClient );
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
AIClient::AIClient() {
|
||||
mMoveMode = ModeStop;
|
||||
mMoveDestination.set( 0.0f, 0.0f, 0.0f );
|
||||
mAimLocation.set( 0.0f, 0.0f, 0.0f );
|
||||
mMoveSpeed = 1.0f;
|
||||
mMoveTolerance = 0.25f;
|
||||
|
||||
// Clear the triggers
|
||||
for( int i = 0; i < MaxTriggerKeys; i++ )
|
||||
mTriggers[i] = false;
|
||||
|
||||
mAimToDestination = true;
|
||||
mTargetInLOS = false;
|
||||
|
||||
mLocation.set( 0.0f, 0.0f, 0.0f );
|
||||
mPlayer = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
AIClient::~AIClient() {
|
||||
// Blah
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object the bot is targeting
|
||||
*
|
||||
* @param targetObject The object to target
|
||||
*/
|
||||
void AIClient::setTargetObject( ShapeBase *targetObject ) {
|
||||
if ( !targetObject || !bool( mTargetObject ) || targetObject->getId() != mTargetObject->getId() )
|
||||
mTargetInLOS = false;
|
||||
|
||||
mTargetObject = targetObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target object
|
||||
*
|
||||
* @return Object bot is targeting
|
||||
*/
|
||||
S32 AIClient::getTargetObject() const {
|
||||
if( bool( mTargetObject ) )
|
||||
return mTargetObject->getId();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the speed at which this AI moves
|
||||
*
|
||||
* @param speed Speed to move, default player was 10
|
||||
*/
|
||||
void AIClient::setMoveSpeed( F32 speed ) {
|
||||
if( speed <= 0.0f )
|
||||
mMoveSpeed = 0.0f;
|
||||
else
|
||||
mMoveSpeed = getMin( 1.0f, speed );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the movement mode for this AI
|
||||
*
|
||||
* @param mode Movement mode, see enum
|
||||
*/
|
||||
void AIClient::setMoveMode( S32 mode ) {
|
||||
if( mode < 0 || mode >= ModeCount )
|
||||
mode = 0;
|
||||
|
||||
if( mode != mMoveMode ) {
|
||||
switch( mode ) {
|
||||
case ModeStuck:
|
||||
throwCallback( "onStuck" );
|
||||
break;
|
||||
case ModeMove:
|
||||
if( mMoveMode == ModeStuck )
|
||||
throwCallback( "onUnStuck" );
|
||||
else
|
||||
throwCallback( "onMove" );
|
||||
break;
|
||||
case ModeStop:
|
||||
throwCallback( "onStop" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mMoveMode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how far away from the move location is considered
|
||||
* "on target"
|
||||
*
|
||||
* @param tolerance Movement tolerance for error
|
||||
*/
|
||||
void AIClient::setMoveTolerance( const F32 tolerance ) {
|
||||
mMoveTolerance = getMax( 0.1f, tolerance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the bot to run to
|
||||
*
|
||||
* @param location Point to run to
|
||||
*/
|
||||
void AIClient::setMoveDestination( const Point3F &location ) {
|
||||
// Ok, here's the story...we're going to aim where we are going UNLESS told otherwise
|
||||
if( mAimToDestination ) {
|
||||
mAimLocation = location;
|
||||
mAimLocation.z = 0.0f;
|
||||
}
|
||||
|
||||
mMoveDestination = location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the bot to aim at
|
||||
*
|
||||
* @param location Point to aim at
|
||||
*/
|
||||
void AIClient::setAimLocation( const Point3F &location ) {
|
||||
mAimLocation = location;
|
||||
mAimToDestination = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the aim location and sets it to the bot's
|
||||
* current destination so he looks where he's going
|
||||
*/
|
||||
void AIClient::clearAim() {
|
||||
mAimLocation = Point3F( 0.0f, 0.0f, 0.0f );
|
||||
mAimToDestination = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets the move list for an object, in the case
|
||||
* of the AI, it actually calculates the moves, and then
|
||||
* sends them down the pipe.
|
||||
*
|
||||
* @param movePtr Pointer to move the move list into
|
||||
* @param numMoves Number of moves in the move list
|
||||
*/
|
||||
void AIClient::getMoveList( Move **movePtr,U32 *numMoves ) {
|
||||
//initialize the move structure and return pointers
|
||||
mMove = NullMove;
|
||||
*movePtr = &mMove;
|
||||
*numMoves = 1;
|
||||
|
||||
// Check if we got a player
|
||||
mPlayer = NULL;
|
||||
mPlayer = static_cast<Player *>( getControlObject() );
|
||||
|
||||
// We got a something controling us?
|
||||
if( !mPlayer )
|
||||
return;
|
||||
|
||||
|
||||
// What is The Matrix?
|
||||
MatrixF moveMatrix;
|
||||
moveMatrix.set( EulerF( 0, 0, 0 ) );
|
||||
moveMatrix.setColumn( 3, Point3F( 0, 0, 0 ) );
|
||||
moveMatrix.transpose();
|
||||
|
||||
// Position / rotation variables
|
||||
F32 curYaw, curPitch;
|
||||
F32 newYaw, newPitch;
|
||||
F32 xDiff, yDiff, zDiff;
|
||||
|
||||
|
||||
F32 moveSpeed = mMoveSpeed;
|
||||
|
||||
switch( mMoveMode ) {
|
||||
|
||||
case ModeStop:
|
||||
return; // Stop means no action
|
||||
break;
|
||||
|
||||
case ModeStuck:
|
||||
// Fall through, so we still try to move
|
||||
case ModeMove:
|
||||
|
||||
// Get my location
|
||||
MatrixF const& myTransform = mPlayer->getTransform();
|
||||
myTransform.getColumn( 3, &mLocation );
|
||||
|
||||
// Set rotation variables
|
||||
Point3F rotation = mPlayer->getRotation();
|
||||
Point3F headRotation = mPlayer->getHeadRotation();
|
||||
curYaw = rotation.z;
|
||||
curPitch = headRotation.x;
|
||||
xDiff = mAimLocation.x - mLocation.x;
|
||||
yDiff = mAimLocation.y - mLocation.y;
|
||||
|
||||
// first do Yaw
|
||||
if( !isZero( xDiff ) || !isZero( yDiff ) ) {
|
||||
// use the cur yaw between -Pi and Pi
|
||||
while( curYaw > M_2PI )
|
||||
curYaw -= M_2PI;
|
||||
while( curYaw < -M_2PI )
|
||||
curYaw += M_2PI;
|
||||
|
||||
// find the new yaw
|
||||
newYaw = mAtan( xDiff, yDiff );
|
||||
|
||||
// find the yaw diff
|
||||
F32 yawDiff = newYaw - curYaw;
|
||||
|
||||
// make it between 0 and 2PI
|
||||
if( yawDiff < 0.0f )
|
||||
yawDiff += M_2PI;
|
||||
else if( yawDiff >= M_2PI )
|
||||
yawDiff -= M_2PI;
|
||||
|
||||
// now make sure we take the short way around the circle
|
||||
if( yawDiff > M_PI )
|
||||
yawDiff -= M_2PI;
|
||||
else if( yawDiff < -M_PI )
|
||||
yawDiff += M_2PI;
|
||||
|
||||
mMove.yaw = yawDiff;
|
||||
|
||||
// set up the movement matrix
|
||||
moveMatrix.set( EulerF( 0, 0, newYaw ) );
|
||||
}
|
||||
else
|
||||
moveMatrix.set( EulerF( 0, 0, curYaw ) );
|
||||
|
||||
// next do pitch
|
||||
F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len();
|
||||
|
||||
if( !isZero( horzDist ) ) {
|
||||
//we shoot from the gun, not the eye...
|
||||
F32 vertDist = mAimLocation.z;
|
||||
|
||||
newPitch = mAtan( horzDist, vertDist ) - ( M_PI / 2.0f );
|
||||
|
||||
F32 pitchDiff = newPitch - curPitch;
|
||||
mMove.pitch = pitchDiff;
|
||||
}
|
||||
|
||||
// finally, mMove towards mMoveDestination
|
||||
xDiff = mMoveDestination.x - mLocation.x;
|
||||
yDiff = mMoveDestination.y - mLocation.y;
|
||||
|
||||
|
||||
// Check if we should mMove, or if we are 'close enough'
|
||||
if( ( ( mFabs( xDiff ) > mMoveTolerance ) ||
|
||||
( mFabs( yDiff ) > mMoveTolerance ) ) && ( !isZero( mMoveSpeed ) ) )
|
||||
{
|
||||
if( isZero( xDiff ) )
|
||||
mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
|
||||
else if( isZero( yDiff ) )
|
||||
mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
|
||||
else if( mFabs( xDiff ) > mFabs( yDiff ) ) {
|
||||
F32 value = mFabs( yDiff / xDiff ) * mMoveSpeed;
|
||||
mMove.y = ( mLocation.y > mMoveDestination.y ? -value : value );
|
||||
mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed );
|
||||
}
|
||||
else {
|
||||
F32 value = mFabs( xDiff / yDiff ) * mMoveSpeed;
|
||||
mMove.x = ( mLocation.x > mMoveDestination.x ? -value : value );
|
||||
mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
|
||||
}
|
||||
|
||||
//now multiply the mMove vector by the transpose of the object rotation matrix
|
||||
moveMatrix.transpose();
|
||||
Point3F newMove;
|
||||
moveMatrix.mulP( Point3F( mMove.x, mMove.y, 0 ), &newMove );
|
||||
|
||||
//and sub the result back in the mMove structure
|
||||
mMove.x = newMove.x;
|
||||
mMove.y = newMove.y;
|
||||
|
||||
// We should check to see if we are stuck...
|
||||
if( mLocation.x == mLastLocation.x &&
|
||||
mLocation.y == mLastLocation.y &&
|
||||
mLocation.z == mLastLocation.z ) {
|
||||
|
||||
// We're stuck...probably
|
||||
setMoveMode( ModeStuck );
|
||||
}
|
||||
else
|
||||
setMoveMode( ModeMove );
|
||||
}
|
||||
else {
|
||||
// Ok, we are close enough, lets stop
|
||||
|
||||
// setMoveMode( ModeStop ); // DON'T use this, it'll throw the wrong callback
|
||||
mMoveMode = ModeStop;
|
||||
throwCallback( "onReachDestination" ); // Callback
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Test for target location in sight
|
||||
RayInfo dummy;
|
||||
Point3F targetLoc = mMoveDestination; // Change this
|
||||
|
||||
if( mPlayer ) {
|
||||
if( !mPlayer->getContainer()->castRay( mLocation, targetLoc, InteriorObjectType |
|
||||
StaticShapeObjectType | StaticObjectType |
|
||||
TerrainObjectType, &dummy ) ) {
|
||||
if( !mTargetInLOS )
|
||||
throwCallback( "onTargetEnterLOS" );
|
||||
}
|
||||
else {
|
||||
if( mTargetInLOS )
|
||||
throwCallback( "onTargetExitLOS" );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over the trigger status
|
||||
for( int i = 0; i < MaxTriggerKeys; i++ ) {
|
||||
mMove.trigger[i] = mTriggers[i];
|
||||
mTriggers[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is just called to stop the bots from running amuck
|
||||
* while the mission cycles
|
||||
*/
|
||||
void AIClient::missionCycleCleanup() {
|
||||
setMoveMode( ModeStop );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility function to throw callbacks
|
||||
*/
|
||||
void AIClient::throwCallback( const char *name ) {
|
||||
Con::executef( this, 2, name );
|
||||
}
|
||||
|
||||
/**
|
||||
* What gets called when this gets created, different from constructor
|
||||
*/
|
||||
void AIClient::onAdd( const char *nameSpace ) {
|
||||
|
||||
// This doesn't work...
|
||||
//
|
||||
if( dStrcmp( nameSpace, mNameSpace->mName ) ) {
|
||||
Con::linkNamespaces( mNameSpace->mName, nameSpace );
|
||||
mNameSpace = Con::lookupNamespace( nameSpace );
|
||||
}
|
||||
|
||||
throwCallback( "onAdd" );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Console Functions
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sets the move speed for an AI object
|
||||
*/
|
||||
ConsoleMethod( AIConnection, setMoveSpeed, void, 3, 3, "ai.setMoveSpeed( float );" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
ai->setMoveSpeed( dAtof( argv[2] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all AI movement, halt!
|
||||
*/
|
||||
ConsoleMethod( AIConnection, stop, void, 2, 2, "ai.stop();" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
ai->setMoveMode( AIClient::ModeStop );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the AI to aim at the location provided
|
||||
*/
|
||||
ConsoleMethod( AIConnection, setAimLocation, void, 3, 3, "ai.setAimLocation( x y z );" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
Point3F v( 0.0f,0.0f,0.0f );
|
||||
dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z );
|
||||
|
||||
ai->setAimLocation( v );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the AI to move to the location provided
|
||||
*/
|
||||
ConsoleMethod( AIConnection, setMoveDestination, void, 3, 3, "ai.setMoveDestination( x y z );" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
Point3F v( 0.0f, 0.0f, 0.0f );
|
||||
dSscanf( argv[2], "%f %f", &v.x, &v.y );
|
||||
|
||||
ai->setMoveDestination( v );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point the AI is aiming at
|
||||
*/
|
||||
ConsoleMethod( AIConnection, getAimLocation, const char *, 2, 2, "ai.getAimLocation();" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
Point3F aimPoint = ai->getAimLocation();
|
||||
|
||||
char *returnBuffer = Con::getReturnBuffer( 256 );
|
||||
dSprintf( returnBuffer, 256, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z );
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point the AI is set to move to
|
||||
*/
|
||||
ConsoleMethod( AIConnection, getMoveDestination, const char *, 2, 2, "ai.getMoveDestination();" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
Point3F movePoint = ai->getMoveDestination();
|
||||
|
||||
char *returnBuffer = Con::getReturnBuffer( 256 );
|
||||
dSprintf( returnBuffer, 256, "%f %f %f", movePoint.x, movePoint.y, movePoint.z );
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bots target object
|
||||
*/
|
||||
ConsoleMethod( AIConnection, setTargetObject, void, 3, 3, "ai.setTargetObject( obj );" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
|
||||
// Find the target
|
||||
ShapeBase *targetObject;
|
||||
if( Sim::findObject( argv[2], targetObject ) )
|
||||
ai->setTargetObject( targetObject );
|
||||
else
|
||||
ai->setTargetObject( NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object the AI is targeting
|
||||
*/
|
||||
ConsoleMethod( AIConnection, getTargetObject, S32, 2, 2, "ai.getTargetObject();" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
|
||||
return ai->getTargetObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the bot the mission is cycling
|
||||
*/
|
||||
ConsoleMethod( AIConnection, missionCycleCleanup, void, 2, 2, "ai.missionCycleCleanup();" ) {
|
||||
AIClient *ai = static_cast<AIClient*>( object );
|
||||
ai->missionCycleCleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the AI to run mode
|
||||
*/
|
||||
ConsoleMethod( AIConnection, move, void, 2, 2, "ai.move();" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
ai->setMoveMode( AIClient::ModeMove );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the AI's location in the world
|
||||
*/
|
||||
ConsoleMethod( AIConnection, getLocation, const char *, 2, 2, "ai.getLocation();" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
Point3F locPoint = ai->getLocation();
|
||||
|
||||
char *returnBuffer = Con::getReturnBuffer( 256 );
|
||||
dSprintf( returnBuffer, 256, "%f %f %f", locPoint.x, locPoint.y, locPoint.z );
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an AI Player to the game
|
||||
*/
|
||||
ConsoleFunction( aiAddPlayer, S32 , 2, 3, "aiAddPlayer( 'playerName'[, 'AIClassType'] );" ) {
|
||||
// Create the player
|
||||
AIClient *aiPlayer = new AIClient();
|
||||
aiPlayer->registerObject();
|
||||
aiPlayer->setGhostFrom(false);
|
||||
aiPlayer->setGhostTo(false);
|
||||
aiPlayer->setSendingEvents(false);
|
||||
aiPlayer->setTranslatesStrings(true);
|
||||
aiPlayer->setEstablished();
|
||||
|
||||
// Add the connection to the client group
|
||||
SimGroup *g = Sim::getClientGroup();
|
||||
g->addObject( aiPlayer );
|
||||
|
||||
char *name = new char[ dStrlen( argv[1] ) + 1];
|
||||
char *ns = new char[ dStrlen( argv[2] ) + 1];
|
||||
|
||||
dStrcpy( name, argv[1] );
|
||||
dStrcpy( ns, argv[2] );
|
||||
|
||||
// Execute the connect console function, this is the same
|
||||
// onConnect function invoked for normal client connections
|
||||
Con::executef( aiPlayer, 2, "onConnect", name );
|
||||
|
||||
// Now execute the onAdd command and feed it the namespace
|
||||
if( argc > 2 )
|
||||
aiPlayer->onAdd( ns );
|
||||
else
|
||||
aiPlayer->onAdd( "AIClient" );
|
||||
|
||||
return aiPlayer->getId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tells the AI to move forward 100 units...TEST FXN
|
||||
*/
|
||||
ConsoleMethod( AIConnection, moveForward, void, 2, 2, "ai.moveForward();" ) {
|
||||
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
ShapeBase *player = ai->getControlObject();
|
||||
Point3F location;
|
||||
MatrixF const &myTransform = player->getTransform();
|
||||
myTransform.getColumn( 3, &location );
|
||||
|
||||
location.y += 100.0f;
|
||||
|
||||
ai->setMoveDestination( location );
|
||||
} // *** /TEST FXN
|
||||
96
engine/game/aiClient.h
Executable file
96
engine/game/aiClient.h
Executable file
@@ -0,0 +1,96 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AICLIENT_H_
|
||||
#define _AICLIENT_H_
|
||||
|
||||
#ifndef _AICONNECTION_H_
|
||||
#include "game/aiConnection.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
class Player;
|
||||
|
||||
class AIClient : public AIConnection {
|
||||
|
||||
typedef AIConnection Parent;
|
||||
|
||||
private:
|
||||
enum {
|
||||
FireTrigger = 0,
|
||||
JumpTrigger = 2,
|
||||
JetTrigger = 3,
|
||||
GrenadeTrigger = 4,
|
||||
MineTrigger = 5
|
||||
};
|
||||
|
||||
F32 mMoveSpeed;
|
||||
S32 mMoveMode;
|
||||
F32 mMoveTolerance; // How close to the destination before we stop
|
||||
|
||||
bool mTriggers[MaxTriggerKeys];
|
||||
|
||||
Player *mPlayer;
|
||||
|
||||
Point3F mMoveDestination;
|
||||
Point3F mLocation;
|
||||
Point3F mLastLocation; // For stuck check
|
||||
|
||||
bool mAimToDestination;
|
||||
Point3F mAimLocation;
|
||||
bool mTargetInLOS;
|
||||
|
||||
SimObjectPtr<ShapeBase> mTargetObject;
|
||||
|
||||
// Utility Methods
|
||||
void throwCallback( const char *name );
|
||||
public:
|
||||
|
||||
DECLARE_CONOBJECT( AIClient );
|
||||
|
||||
enum {
|
||||
ModeStop = 0,
|
||||
ModeMove,
|
||||
ModeStuck,
|
||||
ModeCount // This is in there as a max index value
|
||||
};
|
||||
|
||||
AIClient();
|
||||
~AIClient();
|
||||
|
||||
void getMoveList( Move **movePtr,U32 *numMoves );
|
||||
|
||||
// ---Targeting and aiming sets/gets
|
||||
void setTargetObject( ShapeBase *targetObject );
|
||||
S32 getTargetObject() const;
|
||||
|
||||
// ---Movement sets/gets
|
||||
void setMoveSpeed( const F32 speed );
|
||||
F32 getMoveSpeed() const { return mMoveSpeed; }
|
||||
|
||||
void setMoveMode( S32 mode );
|
||||
S32 getMoveMode() const { return mMoveMode; }
|
||||
|
||||
void setMoveTolerance( const F32 tolerance );
|
||||
F32 getMoveTolerance() const { return mMoveTolerance; }
|
||||
|
||||
void setMoveDestination( const Point3F &location );
|
||||
Point3F getMoveDestination() const { return mMoveDestination; }
|
||||
|
||||
Point3F getLocation() const { return mLocation; }
|
||||
|
||||
// ---Facing(Aiming) sets/gets
|
||||
void setAimLocation( const Point3F &location );
|
||||
Point3F getAimLocation() const { return mAimLocation; }
|
||||
void clearAim();
|
||||
|
||||
// ---Other
|
||||
void missionCycleCleanup();
|
||||
void onAdd( const char *nameSpace );
|
||||
};
|
||||
|
||||
#endif
|
||||
192
engine/game/aiConnection.cc
Executable file
192
engine/game/aiConnection.cc
Executable file
@@ -0,0 +1,192 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/aiConnection.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT( AIConnection );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
AIConnection::AIConnection() {
|
||||
mAIControlled = true;
|
||||
mMove = NullMove;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AIConnection::clearMoves( U32 )
|
||||
{
|
||||
// Clear the pending move list. This connection generates moves
|
||||
// on the fly, so there are never any pending moves.
|
||||
}
|
||||
|
||||
void AIConnection::setMove(Move* m)
|
||||
{
|
||||
mMove = *m;
|
||||
}
|
||||
|
||||
const Move& AIConnection::getMove()
|
||||
{
|
||||
return mMove;
|
||||
}
|
||||
|
||||
/// Retrive the pending moves
|
||||
/**
|
||||
* The GameConnection base class queues moves for delivery to the
|
||||
* controll object. This function is normally used to retrieve the
|
||||
* queued moves recieved from the client. The AI connection does not
|
||||
* have a connected client and simply generates moves on-the-fly
|
||||
* base on it's current state.
|
||||
*/
|
||||
void AIConnection::getMoveList( Move **lngMove, U32 *numMoves )
|
||||
{
|
||||
*numMoves = 1;
|
||||
*lngMove = &mMove;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console functions & methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static inline F32 moveClamp(F32 v)
|
||||
{
|
||||
// Support function to convert/clamp the input into a move rotation
|
||||
// which only allows 0 -> M_2PI.
|
||||
F32 a = mClampF(v,-M_PI,M_PI);
|
||||
return (a < 0)? a + M_2PI: a;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Construct and connect an AI connection object
|
||||
ConsoleFunction(aiConnect, S32 , 2, 20, "(...)"
|
||||
"Make a new AIConnection, and pass arguments to the onConnect script callback.")
|
||||
{
|
||||
// Create the connection
|
||||
AIConnection *aiConnection = new AIConnection();
|
||||
aiConnection->registerObject();
|
||||
|
||||
// Add the connection to the client group
|
||||
SimGroup *g = Sim::getClientGroup();
|
||||
g->addObject( aiConnection );
|
||||
|
||||
// Prep the arguments for the console exec...
|
||||
// Make sure and leav args[1] empty.
|
||||
const char* args[21];
|
||||
args[0] = "onConnect";
|
||||
for (S32 i = 1; i < argc; i++)
|
||||
args[i + 1] = argv[i];
|
||||
|
||||
// Execute the connect console function, this is the same
|
||||
// onConnect function invoked for normal client connections
|
||||
Con::execute(aiConnection, argc + 1, args);
|
||||
return aiConnection->getId();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
ConsoleMethod(AIConnection,setMove,void,4, 4,"(string field, float value)"
|
||||
"Set a field on the current move.\n\n"
|
||||
"@param field One of {'x','y','z','yaw','pitch','roll'}\n"
|
||||
"@param value Value to set field to.")
|
||||
{
|
||||
Move move = object->getMove();
|
||||
|
||||
// Ok, a little slow for now, but this is just an example..
|
||||
if (!dStricmp(argv[2],"x"))
|
||||
move.x = mClampF(dAtof(argv[3]),-1,1);
|
||||
else
|
||||
if (!dStricmp(argv[2],"y"))
|
||||
move.y = mClampF(dAtof(argv[3]),-1,1);
|
||||
else
|
||||
if (!dStricmp(argv[2],"z"))
|
||||
move.z = mClampF(dAtof(argv[3]),-1,1);
|
||||
else
|
||||
if (!dStricmp(argv[2],"yaw"))
|
||||
move.yaw = moveClamp(dAtof(argv[3]));
|
||||
else
|
||||
if (!dStricmp(argv[2],"pitch"))
|
||||
move.pitch = moveClamp(dAtof(argv[3]));
|
||||
else
|
||||
if (!dStricmp(argv[2],"roll"))
|
||||
move.roll = moveClamp(dAtof(argv[3]));
|
||||
|
||||
//
|
||||
object->setMove(&move);
|
||||
}
|
||||
|
||||
ConsoleMethod(AIConnection,getMove,F32,3, 3,"(string field)"
|
||||
"Get the given field of a move.\n\n"
|
||||
"@param field One of {'x','y','z','yaw','pitch','roll'}\n"
|
||||
"@returns The requested field on the current move.")
|
||||
{
|
||||
const Move& move = object->getMove();
|
||||
if (!dStricmp(argv[2],"x"))
|
||||
return move.x;
|
||||
if (!dStricmp(argv[2],"y"))
|
||||
return move.y;
|
||||
if (!dStricmp(argv[2],"z"))
|
||||
return move.z;
|
||||
if (!dStricmp(argv[2],"yaw"))
|
||||
return move.yaw;
|
||||
if (!dStricmp(argv[2],"pitch"))
|
||||
return move.pitch;
|
||||
if (!dStricmp(argv[2],"roll"))
|
||||
return move.roll;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ConsoleMethod(AIConnection,setFreeLook,void,3, 3,"(bool isFreeLook)"
|
||||
"Enable/disable freelook on the current move.")
|
||||
{
|
||||
Move move = object->getMove();
|
||||
move.freeLook = dAtob(argv[2]);
|
||||
object->setMove(&move);
|
||||
}
|
||||
|
||||
ConsoleMethod(AIConnection,getFreeLook,bool,2, 2,"getFreeLook()"
|
||||
"Is freelook on for the current move?")
|
||||
{
|
||||
return object->getMove().freeLook;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(AIConnection,setTrigger,void,4, 4,"(int trigger, bool set)"
|
||||
"Set a trigger.")
|
||||
{
|
||||
S32 idx = dAtoi(argv[2]);
|
||||
if (idx >= 0 && idx < MaxTriggerKeys) {
|
||||
Move move = object->getMove();
|
||||
move.trigger[idx] = dAtob(argv[3]);
|
||||
object->setMove(&move);
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleMethod(AIConnection,getTrigger,bool,4, 4,"(int trigger)"
|
||||
"Is the given trigger set?")
|
||||
{
|
||||
S32 idx = dAtoi(argv[2]);
|
||||
if (idx >= 0 && idx < MaxTriggerKeys)
|
||||
return object->getMove().trigger[idx];
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(AIConnection,getAddress,const char*,2, 2,"")
|
||||
{
|
||||
// Override the netConnection method to return to indicate
|
||||
// this is an ai connection.
|
||||
return "ai:local";
|
||||
}
|
||||
39
engine/game/aiConnection.h
Executable file
39
engine/game/aiConnection.h
Executable file
@@ -0,0 +1,39 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AICONNECTION_H_
|
||||
#define _AICONNECTION_H_
|
||||
|
||||
#ifndef _GAMECONNECTION_H_
|
||||
#include "game/gameConnection.h"
|
||||
#endif
|
||||
#ifndef _MOVEMANAGER_H_
|
||||
#include "game/moveManager.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class AIConnection : public GameConnection
|
||||
{
|
||||
typedef GameConnection Parent;
|
||||
|
||||
protected:
|
||||
Move mMove;
|
||||
|
||||
public:
|
||||
AIConnection();
|
||||
DECLARE_CONOBJECT( AIConnection );
|
||||
|
||||
// Interface
|
||||
const Move& getMove();
|
||||
void setMove(Move *m);
|
||||
|
||||
// GameConnection overrides
|
||||
void clearMoves(U32 n);
|
||||
virtual void getMoveList(Move **,U32 *numMoves);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
406
engine/game/aiPlayer.cc
Executable file
406
engine/game/aiPlayer.cc
Executable file
@@ -0,0 +1,406 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "game/aiPlayer.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "core/realComp.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "game/moveManager.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(AIPlayer);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
AIPlayer::AIPlayer()
|
||||
{
|
||||
mMoveDestination.set( 0.0f, 0.0f, 0.0f );
|
||||
mMoveSpeed = 1.0f;
|
||||
mMoveTolerance = 0.25f;
|
||||
mMoveSlowdown = true;
|
||||
mMoveState = ModeStop;
|
||||
|
||||
mAimObject = 0;
|
||||
mAimLocationSet = false;
|
||||
mTargetInLOS = false;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
|
||||
mTypeMask |= AIObjectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
AIPlayer::~AIPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the speed at which this AI moves
|
||||
*
|
||||
* @param speed Speed to move, default player was 10
|
||||
*/
|
||||
void AIPlayer::setMoveSpeed( F32 speed )
|
||||
{
|
||||
mMoveSpeed = getMax(0.0f, getMin( 1.0f, speed ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops movement for this AI
|
||||
*/
|
||||
void AIPlayer::stopMove()
|
||||
{
|
||||
mMoveState = ModeStop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how far away from the move location is considered
|
||||
* "on target"
|
||||
*
|
||||
* @param tolerance Movement tolerance for error
|
||||
*/
|
||||
void AIPlayer::setMoveTolerance( const F32 tolerance )
|
||||
{
|
||||
mMoveTolerance = getMax( 0.1f, tolerance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the bot to run to
|
||||
*
|
||||
* @param location Point to run to
|
||||
*/
|
||||
void AIPlayer::setMoveDestination( const Point3F &location, bool slowdown )
|
||||
{
|
||||
mMoveDestination = location;
|
||||
mMoveState = ModeMove;
|
||||
mMoveSlowdown = slowdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object the bot is targeting
|
||||
*
|
||||
* @param targetObject The object to target
|
||||
*/
|
||||
void AIPlayer::setAimObject( GameBase *targetObject )
|
||||
{
|
||||
mAimObject = targetObject;
|
||||
mTargetInLOS = false;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object the bot is targeting and an offset to add to target location
|
||||
*
|
||||
* @param targetObject The object to target
|
||||
* @param offset The offest from the target location to aim at
|
||||
*/
|
||||
void AIPlayer::setAimObject( GameBase *targetObject, Point3F offset )
|
||||
{
|
||||
mAimObject = targetObject;
|
||||
mTargetInLOS = false;
|
||||
mAimOffset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the bot to aim at
|
||||
*
|
||||
* @param location Point to aim at
|
||||
*/
|
||||
void AIPlayer::setAimLocation( const Point3F &location )
|
||||
{
|
||||
mAimObject = 0;
|
||||
mAimLocationSet = true;
|
||||
mAimLocation = location;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the aim location and sets it to the bot's
|
||||
* current destination so he looks where he's going
|
||||
*/
|
||||
void AIPlayer::clearAim()
|
||||
{
|
||||
mAimObject = 0;
|
||||
mAimLocationSet = false;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates the moves for the AI player
|
||||
*
|
||||
* @param movePtr Pointer to move the move list into
|
||||
*/
|
||||
bool AIPlayer::getAIMove(Move *movePtr)
|
||||
{
|
||||
*movePtr = NullMove;
|
||||
|
||||
// Use the eye as the current position.
|
||||
MatrixF eye;
|
||||
getEyeTransform(&eye);
|
||||
Point3F location = eye.getPosition();
|
||||
Point3F rotation = getRotation();
|
||||
|
||||
// Orient towards the aim point, aim object, or towards
|
||||
// our destination.
|
||||
if (mAimObject || mAimLocationSet || mMoveState == ModeMove) {
|
||||
|
||||
// Update the aim position if we're aiming for an object
|
||||
if (mAimObject)
|
||||
mAimLocation = mAimObject->getPosition() + mAimOffset;
|
||||
else
|
||||
if (!mAimLocationSet)
|
||||
mAimLocation = mMoveDestination;
|
||||
|
||||
F32 xDiff = mAimLocation.x - location.x;
|
||||
F32 yDiff = mAimLocation.y - location.y;
|
||||
if (!isZero(xDiff) || !isZero(yDiff)) {
|
||||
|
||||
// First do Yaw
|
||||
// use the cur yaw between -Pi and Pi
|
||||
F32 curYaw = rotation.z;
|
||||
while (curYaw > M_2PI)
|
||||
curYaw -= M_2PI;
|
||||
while (curYaw < -M_2PI)
|
||||
curYaw += M_2PI;
|
||||
|
||||
// find the yaw offset
|
||||
F32 newYaw = mAtan( xDiff, yDiff );
|
||||
F32 yawDiff = newYaw - curYaw;
|
||||
|
||||
// make it between 0 and 2PI
|
||||
if( yawDiff < 0.0f )
|
||||
yawDiff += M_2PI;
|
||||
else if( yawDiff >= M_2PI )
|
||||
yawDiff -= M_2PI;
|
||||
|
||||
// now make sure we take the short way around the circle
|
||||
if( yawDiff > M_PI )
|
||||
yawDiff -= M_2PI;
|
||||
else if( yawDiff < -M_PI )
|
||||
yawDiff += M_2PI;
|
||||
|
||||
movePtr->yaw = yawDiff;
|
||||
|
||||
// Next do pitch.
|
||||
if (!mAimObject && !mAimLocationSet) {
|
||||
// Level out if were just looking at our next way point.
|
||||
Point3F headRotation = getHeadRotation();
|
||||
movePtr->pitch = -headRotation.x;
|
||||
}
|
||||
else {
|
||||
// This should be adjusted to run from the
|
||||
// eye point to the object's center position. Though this
|
||||
// works well enough for now.
|
||||
F32 vertDist = mAimLocation.z - location.z;
|
||||
F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
F32 newPitch = mAtan( horzDist, vertDist ) - ( M_PI / 2.0f );
|
||||
if (mFabs(newPitch) > 0.01) {
|
||||
Point3F headRotation = getHeadRotation();
|
||||
movePtr->pitch = newPitch - headRotation.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Level out if we're not doing anything else
|
||||
Point3F headRotation = getHeadRotation();
|
||||
movePtr->pitch = -headRotation.x;
|
||||
}
|
||||
|
||||
// Move towards the destination
|
||||
if (mMoveState == ModeMove) {
|
||||
F32 xDiff = mMoveDestination.x - location.x;
|
||||
F32 yDiff = mMoveDestination.y - location.y;
|
||||
|
||||
// Check if we should mMove, or if we are 'close enough'
|
||||
if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) {
|
||||
mMoveState = ModeStop;
|
||||
throwCallback("onReachDestination");
|
||||
}
|
||||
else {
|
||||
// Build move direction in world space
|
||||
if (isZero(xDiff))
|
||||
movePtr->y = (location.y > mMoveDestination.y)? -1 : 1;
|
||||
else
|
||||
if (isZero(yDiff))
|
||||
movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
|
||||
else
|
||||
if (mFabs(xDiff) > mFabs(yDiff)) {
|
||||
F32 value = mFabs(yDiff / xDiff);
|
||||
movePtr->y = (location.y > mMoveDestination.y)? -value : value;
|
||||
movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
|
||||
}
|
||||
else {
|
||||
F32 value = mFabs(xDiff / yDiff);
|
||||
movePtr->x = (location.x > mMoveDestination.x)? -value : value;
|
||||
movePtr->y = (location.y > mMoveDestination.y)? -1 : 1;
|
||||
}
|
||||
|
||||
// Rotate the move into object space (this really only needs
|
||||
// a 2D matrix)
|
||||
Point3F newMove;
|
||||
MatrixF moveMatrix;
|
||||
moveMatrix.set(EulerF(0, 0, -(rotation.z + movePtr->yaw)));
|
||||
moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0 ), &newMove );
|
||||
movePtr->x = newMove.x;
|
||||
movePtr->y = newMove.y;
|
||||
|
||||
// Set movement speed. We'll slow down once we get close
|
||||
// to try and stop on the spot...
|
||||
if (mMoveSlowdown) {
|
||||
F32 speed = mMoveSpeed;
|
||||
F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff);
|
||||
F32 maxDist = 5;
|
||||
if (dist < maxDist)
|
||||
speed *= dist / maxDist;
|
||||
movePtr->x *= speed;
|
||||
movePtr->y *= speed;
|
||||
}
|
||||
else {
|
||||
movePtr->x *= mMoveSpeed;
|
||||
movePtr->y *= mMoveSpeed;
|
||||
}
|
||||
|
||||
// We should check to see if we are stuck...
|
||||
if (location == mLastLocation) {
|
||||
throwCallback("onMoveStuck");
|
||||
mMoveState = ModeStop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test for target location in sight if it's an object. The LOS is
|
||||
// run from the eye position to the center of the object's bounding,
|
||||
// which is not very accurate.
|
||||
if (mAimObject) {
|
||||
MatrixF eyeMat;
|
||||
getEyeTransform(&eyeMat);
|
||||
eyeMat.getColumn(3,&location);
|
||||
Point3F targetLoc = mAimObject->getBoxCenter();
|
||||
|
||||
// This ray ignores non-static shapes. Cast Ray returns true
|
||||
// if it hit something.
|
||||
RayInfo dummy;
|
||||
if (getContainer()->castRay( location, targetLoc,
|
||||
InteriorObjectType | StaticShapeObjectType | StaticObjectType |
|
||||
TerrainObjectType, &dummy)) {
|
||||
if (mTargetInLOS) {
|
||||
throwCallback( "onTargetExitLOS" );
|
||||
mTargetInLOS = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!mTargetInLOS) {
|
||||
throwCallback( "onTargetEnterLOS" );
|
||||
mTargetInLOS = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Replicate the trigger state into the move so that
|
||||
// triggers can be controlled from scripts.
|
||||
for( int i = 0; i < MaxTriggerKeys; i++ )
|
||||
movePtr->trigger[i] = getImageTriggerState(i);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to throw callbacks. Callbacks always occure
|
||||
* on the datablock class.
|
||||
*
|
||||
* @param name Name of script function to call
|
||||
*/
|
||||
void AIPlayer::throwCallback( const char *name )
|
||||
{
|
||||
Con::executef(getDataBlock(), 2, name, scriptThis());
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Console Functions
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod( AIPlayer, stop, void, 2, 2, "()"
|
||||
"Stop moving.")
|
||||
{
|
||||
object->stopMove();
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, clearAim, void, 2, 2, "()"
|
||||
"Stop aiming at anything.")
|
||||
{
|
||||
object->clearAim();
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, setMoveSpeed, void, 3, 3, "( float speed )"
|
||||
"Sets the move speed for an AI object.")
|
||||
{
|
||||
object->setMoveSpeed( dAtof( argv[2] ) );
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, setMoveDestination, void, 3, 4, "(Point3F goal, bool slowDown=true)"
|
||||
"Tells the AI to move to the location provided.")
|
||||
{
|
||||
Point3F v( 0.0f, 0.0f, 0.0f );
|
||||
dSscanf( argv[2], "%g %g %g", &v.x, &v.y, &v.z );
|
||||
bool slowdown = (argc > 3)? dAtob(argv[3]): true;
|
||||
object->setMoveDestination( v, slowdown);
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, getMoveDestination, const char *, 2, 2, "()"
|
||||
"Returns the point the AI is set to move to.")
|
||||
{
|
||||
Point3F movePoint = object->getMoveDestination();
|
||||
|
||||
char *returnBuffer = Con::getReturnBuffer( 256 );
|
||||
dSprintf( returnBuffer, 256, "%g %g %g", movePoint.x, movePoint.y, movePoint.z );
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, setAimLocation, void, 3, 3, "( Point3F target )"
|
||||
"Tells the AI to aim at the location provided.")
|
||||
{
|
||||
Point3F v( 0.0f,0.0f,0.0f );
|
||||
dSscanf( argv[2], "%g %g %g", &v.x, &v.y, &v.z );
|
||||
|
||||
object->setAimLocation( v );
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, getAimLocation, const char *, 2, 2, "()"
|
||||
"Returns the point the AI is aiming at.")
|
||||
{
|
||||
Point3F aimPoint = object->getAimLocation();
|
||||
|
||||
char *returnBuffer = Con::getReturnBuffer( 256 );
|
||||
dSprintf( returnBuffer, 256, "%g %g %g", aimPoint.x, aimPoint.y, aimPoint.z );
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, setAimObject, void, 3, 4, "( GameBase obj, [Point3F offset] )"
|
||||
"Sets the bot's target object. Optionally set an offset from target location.")
|
||||
{
|
||||
Point3F off( 0.0f, 0.0f, 0.0f );
|
||||
|
||||
// Find the target
|
||||
GameBase *targetObject;
|
||||
if( Sim::findObject( argv[2], targetObject ) )
|
||||
{
|
||||
if (argc == 4)
|
||||
dSscanf( argv[3], "%g %g %g", &off.x, &off.y, &off.z );
|
||||
|
||||
object->setAimObject( targetObject, off );
|
||||
}
|
||||
else
|
||||
object->setAimObject( 0, off );
|
||||
}
|
||||
|
||||
ConsoleMethod( AIPlayer, getAimObject, S32, 2, 2, "()"
|
||||
"Gets the object the AI is targeting.")
|
||||
{
|
||||
GameBase* obj = object->getAimObject();
|
||||
return obj? obj->getId(): -1;
|
||||
}
|
||||
|
||||
69
engine/game/aiPlayer.h
Executable file
69
engine/game/aiPlayer.h
Executable file
@@ -0,0 +1,69 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AIPLAYER_H_
|
||||
#define _AIPLAYER_H_
|
||||
|
||||
#ifndef _PLAYER_H_
|
||||
#include "game/player.h"
|
||||
#endif
|
||||
|
||||
|
||||
class AIPlayer : public Player {
|
||||
|
||||
typedef Player Parent;
|
||||
|
||||
public:
|
||||
enum MoveState {
|
||||
ModeStop,
|
||||
ModeMove,
|
||||
ModeStuck,
|
||||
};
|
||||
|
||||
private:
|
||||
MoveState mMoveState;
|
||||
F32 mMoveSpeed;
|
||||
F32 mMoveTolerance; // Distance from destination before we stop
|
||||
Point3F mMoveDestination; // Destination for movement
|
||||
Point3F mLastLocation; // For stuck check
|
||||
bool mMoveSlowdown; // Slowdown as we near the destination
|
||||
|
||||
SimObjectPtr<GameBase> mAimObject; // Object to point at, overrides location
|
||||
bool mAimLocationSet; // Has an aim location been set?
|
||||
Point3F mAimLocation; // Point to look at
|
||||
bool mTargetInLOS; // Is target object visible?
|
||||
|
||||
Point3F mAimOffset;
|
||||
|
||||
// Utility Methods
|
||||
void throwCallback( const char *name );
|
||||
public:
|
||||
DECLARE_CONOBJECT( AIPlayer );
|
||||
|
||||
AIPlayer();
|
||||
~AIPlayer();
|
||||
|
||||
virtual bool getAIMove( Move *move );
|
||||
|
||||
// Targeting and aiming sets/gets
|
||||
void setAimObject( GameBase *targetObject );
|
||||
void setAimObject( GameBase *targetObject, Point3F offset );
|
||||
GameBase* getAimObject() const { return mAimObject; }
|
||||
void setAimLocation( const Point3F &location );
|
||||
Point3F getAimLocation() const { return mAimLocation; }
|
||||
void clearAim();
|
||||
|
||||
// Movement sets/gets
|
||||
void setMoveSpeed( const F32 speed );
|
||||
F32 getMoveSpeed() const { return mMoveSpeed; }
|
||||
void setMoveTolerance( const F32 tolerance );
|
||||
F32 getMoveTolerance() const { return mMoveTolerance; }
|
||||
void setMoveDestination( const Point3F &location, bool slowdown );
|
||||
Point3F getMoveDestination() const { return mMoveDestination; }
|
||||
void stopMove();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
343
engine/game/aiWheeledVehicle.cc
Executable file
343
engine/game/aiWheeledVehicle.cc
Executable file
@@ -0,0 +1,343 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "math/mMatrix.h"
|
||||
#include "math/mPoint.h"
|
||||
#include "core/realComp.h"
|
||||
#include "game/aiWheeledVehicle.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(AIWheeledVehicle);
|
||||
|
||||
|
||||
EulerF extractEuler(const MatrixF & matrix)
|
||||
{
|
||||
const F32 * mat = (const F32*)matrix;
|
||||
|
||||
EulerF r;
|
||||
r.x = mAsin(mat[MatrixF::idx(2,1)]);
|
||||
|
||||
if(mCos(r.x) != 0.f)
|
||||
{
|
||||
r.y = mAtan(-mat[MatrixF::idx(2,0)], mat[MatrixF::idx(2,2)]);
|
||||
r.z = mAtan(-mat[MatrixF::idx(0,1)], mat[MatrixF::idx(1,1)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
r.y = 0.f;
|
||||
r.z = mAtan(mat[MatrixF::idx(1,0)], mat[MatrixF::idx(0,0)]);
|
||||
}
|
||||
|
||||
return(r);
|
||||
}
|
||||
|
||||
AIWheeledVehicle::AIWheeledVehicle() :
|
||||
WheeledVehicle()
|
||||
{
|
||||
mMoveDestination.set( 0.0f, 0.0f, 0.0f );
|
||||
mMoveSpeed = 1.0f;
|
||||
mMoveTolerance = 0.25f;
|
||||
mMoveSlowdown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the speed at which this AI moves
|
||||
*
|
||||
* @param speed Speed to move, default player was 10
|
||||
*/
|
||||
void AIWheeledVehicle::setMoveSpeed( F32 speed )
|
||||
{
|
||||
mMoveSpeed = getMax(0.0f, getMin( 1.0f, speed ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops movement for this AI
|
||||
*/
|
||||
void AIWheeledVehicle::stopMove()
|
||||
{
|
||||
mMoveState = ModeStop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how far away from the move location is considered
|
||||
* "on target"
|
||||
*
|
||||
* @param tolerance Movement tolerance for error
|
||||
*/
|
||||
void AIWheeledVehicle::setMoveTolerance( const F32 tolerance )
|
||||
{
|
||||
mMoveTolerance = getMax( 0.1f, tolerance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the bot to run to
|
||||
*
|
||||
* @param location Point to run to
|
||||
*/
|
||||
void AIWheeledVehicle::setMoveDestination( const Point3F &location, bool slowdown )
|
||||
{
|
||||
mMoveDestination = location;
|
||||
mMoveState = ModeMove;
|
||||
mMoveSlowdown = slowdown;
|
||||
}
|
||||
|
||||
// Build a Triangle .. calculate angle of rotation required to meet target..
|
||||
// man there has to be a better way! >:)
|
||||
F32 AIWheeledVehicle::getSteeringAngle()
|
||||
{
|
||||
// What is our target
|
||||
Point3F desired;
|
||||
desired=mMoveDestination;
|
||||
|
||||
MatrixF mat = getTransform();
|
||||
Point3F center, front;
|
||||
Point3F wFront;
|
||||
Box3F box = getObjBox();
|
||||
|
||||
box.getCenter(¢er);
|
||||
front=center;
|
||||
front.y=box.max.y; // should be true for all these objects
|
||||
|
||||
getWorldBox().getCenter(¢er);
|
||||
front=center+front;
|
||||
|
||||
Point3F objFront=front;
|
||||
Point3F offset = front - center;
|
||||
EulerF rot;
|
||||
rot=extractEuler(mat);
|
||||
MatrixF transform(rot);
|
||||
transform.mulV(offset, &wFront);
|
||||
front = wFront + center;
|
||||
|
||||
Point3F ftoc;
|
||||
ftoc.x=mFabs(front.x-center.x);
|
||||
ftoc.y=mFabs(front.y-center.y);
|
||||
ftoc.z=mFabs(front.z-center.z);
|
||||
F32 fToc=mSqrt((ftoc.x*ftoc.x)+(ftoc.y*ftoc.y));
|
||||
|
||||
Point3F ltoc;
|
||||
ltoc.x=mFabs(desired.x-center.x);
|
||||
ltoc.y=mFabs(desired.y-center.y);
|
||||
ltoc.z=mFabs(desired.z-center.z);
|
||||
F32 lToc=mSqrt((ltoc.x*ltoc.x)+(ltoc.y*ltoc.y));
|
||||
|
||||
Point3F ftol;
|
||||
ftol.x=mFabs(front.x-desired.x);
|
||||
ftol.y=mFabs(front.y-desired.y);
|
||||
ftol.z=mFabs(front.z-desired.z);
|
||||
F32 fTol=mSqrt((ftol.x*ftol.x)+(ftol.y*ftol.y));
|
||||
|
||||
F32 myAngle = mAcos(((lToc*lToc) + (fToc * fToc) - (fTol*fTol))/(2*lToc*fToc));
|
||||
Point3F location = getPosition();
|
||||
|
||||
F32 xDiff = desired.x - location.x;
|
||||
F32 yDiff = desired.y - location.y;
|
||||
|
||||
F32 finalYaw=mRadToDeg(myAngle);
|
||||
|
||||
F32 maxSteeringAngle=0;
|
||||
|
||||
VehicleData *vd= (VehicleData*) getDataBlock();
|
||||
maxSteeringAngle=vd->maxSteeringAngle;
|
||||
|
||||
// if(finalYaw > 150)
|
||||
// steerState = TurnAround;
|
||||
if(finalYaw < 5 && mLastSteered != 0)
|
||||
steerState = Straight;
|
||||
else if(finalYaw < 5)
|
||||
steerState = SteerNull;
|
||||
else
|
||||
{// Quickly Hack out left or right turn info
|
||||
Point3F rotData=objFront-desired;
|
||||
MatrixF leftM(-rot);
|
||||
Point3F leftP;
|
||||
leftM.mulV(rotData, &leftP);
|
||||
leftP = leftP + desired;
|
||||
|
||||
if(leftP.x<desired.x)
|
||||
steerState=Right;
|
||||
else
|
||||
steerState=Left;
|
||||
}
|
||||
|
||||
Point2F steering = mSteering;
|
||||
|
||||
F32 steer=0;
|
||||
switch(steerState)
|
||||
{
|
||||
case SteerNull:
|
||||
break;
|
||||
case Left:
|
||||
steer=myAngle < maxSteeringAngle ? -myAngle-steering.x: -maxSteeringAngle-steering.x;
|
||||
mLastSteered=steer;
|
||||
break;
|
||||
case Right:
|
||||
steer=myAngle < maxSteeringAngle ? myAngle-steering.x: maxSteeringAngle-steering.x;
|
||||
mLastSteered=steer;
|
||||
break;
|
||||
case Straight:
|
||||
steer=-steering.x;
|
||||
mLastSteered=0;
|
||||
break;
|
||||
case TurnAround:
|
||||
steer=maxSteeringAngle-steering.x;
|
||||
mLastSteered=steer;
|
||||
break;
|
||||
};
|
||||
|
||||
// Con::printf("AI Steering : %f", steer);
|
||||
return steer;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates the moves for the AI player
|
||||
*
|
||||
* @param movePtr Pointer to move the move list into
|
||||
*/
|
||||
bool AIWheeledVehicle::getAIMove(Move *movePtr)
|
||||
{
|
||||
*movePtr = NullMove;
|
||||
|
||||
if (!mDisableMove) {
|
||||
// Use the eye as the current position.
|
||||
MatrixF eye;
|
||||
getEyeTransform(&eye);
|
||||
Point3F location = getPosition();
|
||||
Point4F rotation;
|
||||
getTransform().getColumn(1, &rotation);
|
||||
|
||||
// Orient towards our destination.
|
||||
if (mMoveState == ModeMove || mMoveState == ModeReverse) {
|
||||
movePtr->yaw = getSteeringAngle();
|
||||
}
|
||||
|
||||
// Move towards the destination
|
||||
if (mMoveState == ModeMove) {
|
||||
F32 xDiff = mMoveDestination.x - location.x;
|
||||
F32 yDiff = mMoveDestination.y - location.y;
|
||||
|
||||
// Check if we should mMove, or if we are 'close enough'
|
||||
if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) {
|
||||
mMoveState = ModeStop;
|
||||
throwCallback("onReachDestination");
|
||||
}
|
||||
else {
|
||||
// Build move direction in world space
|
||||
if (isZero(xDiff))
|
||||
movePtr->y = (location.y > mMoveDestination.y)? -1 : 1;
|
||||
else
|
||||
if (isZero(yDiff))
|
||||
movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
|
||||
else
|
||||
if (mFabs(xDiff) > mFabs(yDiff)) {
|
||||
F32 value = mFabs(yDiff / xDiff);
|
||||
movePtr->y = value; // (location.y > mMoveDestination.y)? -value : value;
|
||||
movePtr->x = (location.x > mMoveDestination.x)? -1 : 1;
|
||||
}
|
||||
else {
|
||||
F32 value = mFabs(xDiff / yDiff);
|
||||
movePtr->x = (location.x > mMoveDestination.x)? -value : value;
|
||||
movePtr->y = 1; // (location.y > mMoveDestination.y)? -1 : 1;
|
||||
}
|
||||
|
||||
// Rotate the move into object space (this really only needs
|
||||
// a 2D matrix)
|
||||
Point3F newMove;
|
||||
MatrixF moveMatrix;
|
||||
moveMatrix.set(EulerF(0, 0, -(rotation.z + movePtr->yaw)));
|
||||
moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0 ), &newMove );
|
||||
movePtr->x = newMove.x;
|
||||
movePtr->y = newMove.y;
|
||||
|
||||
// Set movement speed. We'll slow down once we get close
|
||||
// to try and stop on the spot...
|
||||
if (mMoveSlowdown) {
|
||||
F32 speed = mMoveSpeed;
|
||||
F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff);
|
||||
F32 maxDist = 5;
|
||||
if (dist < maxDist)
|
||||
speed *= dist / maxDist;
|
||||
movePtr->x *= speed;
|
||||
movePtr->y *= speed;
|
||||
}
|
||||
else {
|
||||
movePtr->x *= mMoveSpeed;
|
||||
movePtr->y *= mMoveSpeed;
|
||||
}
|
||||
|
||||
// We should check to see if we are stuck...
|
||||
if (location == mLastLocation) {
|
||||
throwCallback("onMoveStuck");
|
||||
mMoveState = ModeStop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replicate the trigger state into the move so that
|
||||
// triggers can be controlled from scripts.
|
||||
|
||||
for( int i = 0; i < MaxTriggerKeys; i++ )
|
||||
movePtr->trigger[i] = getImageTriggerState(i);
|
||||
|
||||
} else {
|
||||
mRigid.setAtRest();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to throw callbacks. Callbacks always occure
|
||||
* on the datablock class.
|
||||
*
|
||||
* @param name Name of script function to call
|
||||
*/
|
||||
void AIWheeledVehicle::throwCallback( const char *name )
|
||||
{
|
||||
Con::executef(getDataBlock(), 2, name, scriptThis());
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Console Functions
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod( AIWheeledVehicle, stop, void, 2, 2, "()"
|
||||
"Stop moving.")
|
||||
{
|
||||
object->stopMove();
|
||||
}
|
||||
|
||||
ConsoleMethod( AIWheeledVehicle, setMoveSpeed, void, 3, 3, "( float speed )"
|
||||
"Sets the move speed for an AI object.")
|
||||
{
|
||||
object->setMoveSpeed( dAtof( argv[2] ) );
|
||||
}
|
||||
|
||||
ConsoleMethod( AIWheeledVehicle, setMoveTolerance, void, 3, 3, "(float speed)" "Sets the movetolerance")
|
||||
{
|
||||
object->setMoveTolerance(dAtof(argv[2]));
|
||||
}
|
||||
|
||||
ConsoleMethod( AIWheeledVehicle, setMoveDestination, void, 3, 4, "(Point3F goal, bool slowDown=true)"
|
||||
"Tells the AI to move to the location provided.")
|
||||
{
|
||||
Point3F v( 0.0f, 0.0f, 0.0f );
|
||||
dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z );
|
||||
bool slowdown = (argc > 3)? dAtob(argv[3]): true;
|
||||
object->setMoveDestination( v, slowdown);
|
||||
}
|
||||
|
||||
ConsoleMethod( AIWheeledVehicle, getMoveDestination, const char *, 2, 2, "()"
|
||||
"Returns the point the AI is set to move to.")
|
||||
{
|
||||
Point3F movePoint = object->getMoveDestination();
|
||||
|
||||
char *returnBuffer = Con::getReturnBuffer( 256 );
|
||||
dSprintf( returnBuffer, 256, "%f %f %f", movePoint.x, movePoint.y, movePoint.z );
|
||||
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
|
||||
63
engine/game/aiWheeledVehicle.h
Executable file
63
engine/game/aiWheeledVehicle.h
Executable file
@@ -0,0 +1,63 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AIWheeledVehicle_h
|
||||
#define _AIWheeledVehicle_h
|
||||
|
||||
#ifndef _WHEELEDVEHICLE_H_
|
||||
#include "game/vehicles/wheeledVehicle.h"
|
||||
#endif
|
||||
|
||||
class AIWheeledVehicle : public WheeledVehicle
|
||||
{
|
||||
typedef WheeledVehicle Parent;
|
||||
public:
|
||||
enum MoveState {
|
||||
ModeStop,
|
||||
ModeMove,
|
||||
ModeStuck,
|
||||
ModeReverse
|
||||
};
|
||||
|
||||
enum DrivingState {
|
||||
SteerNull,
|
||||
Left,
|
||||
Right,
|
||||
Straight,
|
||||
TurnAround
|
||||
};
|
||||
protected:
|
||||
MoveState mMoveState;
|
||||
F32 mMoveSpeed;
|
||||
F32 mMoveTolerance; // Distance from destination before we stop
|
||||
Point3F mMoveDestination; // Destination for movement
|
||||
Point3F mLastLocation; // For stuck check
|
||||
bool mMoveSlowdown; // Slowdown as we near the destination
|
||||
|
||||
// Steering
|
||||
DrivingState steerState;
|
||||
F32 mLastSteered;
|
||||
F32 getSteeringAngle();
|
||||
|
||||
// Utility Methods
|
||||
void throwCallback( const char *name );
|
||||
virtual bool getAIMove(Move* move);
|
||||
public:
|
||||
AIWheeledVehicle();
|
||||
|
||||
// Movement sets/gets
|
||||
void setMoveSpeed( const F32 speed );
|
||||
F32 getMoveSpeed() const { return mMoveSpeed; }
|
||||
void setMoveTolerance( const F32 tolerance );
|
||||
F32 getMoveTolerance() const { return mMoveTolerance; }
|
||||
void setMoveDestination( const Point3F &location, bool slowdown );
|
||||
Point3F getMoveDestination() const { return mMoveDestination; }
|
||||
void stopMove();
|
||||
|
||||
|
||||
DECLARE_CONOBJECT(AIWheeledVehicle);
|
||||
};
|
||||
|
||||
#endif
|
||||
258
engine/game/ambientAudioManager.cc
Executable file
258
engine/game/ambientAudioManager.cc
Executable file
@@ -0,0 +1,258 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/ambientAudioManager.h"
|
||||
#include "interior/interiorInstance.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "interior/interior.h"
|
||||
#include "terrain/waterBlock.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
|
||||
AmbientAudioManager gAmbientAudioManager;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
AmbientAudioManager::AmbientAudioManager()
|
||||
{
|
||||
mOutsideScale = 1.f;
|
||||
mInteriorAudioHandle = NULL_AUDIOHANDLE;
|
||||
mPowerAudioHandle = NULL_AUDIOHANDLE;
|
||||
mLastAlarmState = false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunction(setPowerAudioProfiles, void, 3, 3, "( AudioProfile powerUp, AudioProfile powerDown)"
|
||||
"Set the ambient audio manager's power up/down profiles.")
|
||||
{
|
||||
gAmbientAudioManager.mPowerUpProfile = dynamic_cast<AudioProfile*>(Sim::findObject(argv[1]));
|
||||
gAmbientAudioManager.mPowerDownProfile = dynamic_cast<AudioProfile*>(Sim::findObject(argv[2]));
|
||||
}
|
||||
|
||||
void AmbientAudioManager::addEmitter(AudioEmitter * emitter)
|
||||
{
|
||||
mEmitters.push_back(emitter);
|
||||
updateEmitter(emitter);
|
||||
}
|
||||
|
||||
void AmbientAudioManager::removeEmitter(AudioEmitter * emitter)
|
||||
{
|
||||
for(U32 i = 0; i < mEmitters.size(); i++)
|
||||
if(mEmitters[i] == emitter)
|
||||
{
|
||||
mEmitters.erase_fast(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool AmbientAudioManager::getOutsideScale(F32 * pScale, InteriorInstance ** pInterior)
|
||||
{
|
||||
GameConnection * gc = dynamic_cast<GameConnection*>(NetConnection::getConnectionToServer());
|
||||
if(!gc)
|
||||
return(false);
|
||||
|
||||
MatrixF camMat;
|
||||
if(!gc->getControlCameraTransform(0.032, &camMat))
|
||||
return(false);
|
||||
|
||||
Point3F pos;
|
||||
camMat.getColumn(3, &pos);
|
||||
|
||||
RayInfo collision;
|
||||
if(!gClientContainer.castRay(pos, Point3F(pos.x, pos.y, pos.z - 2000.f), InteriorObjectType, &collision))
|
||||
{
|
||||
*pScale = 1.f;
|
||||
*pInterior = 0;
|
||||
return(true);
|
||||
}
|
||||
|
||||
// could have hit a null face.. dont change anything
|
||||
if(collision.face == -1)
|
||||
{
|
||||
*pScale = mOutsideScale;
|
||||
*pInterior = static_cast<InteriorInstance*>(mInteriorInstance);
|
||||
return(true);
|
||||
}
|
||||
|
||||
InteriorInstance * interior = dynamic_cast<InteriorInstance *>(collision.object);
|
||||
if(!interior)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "AmbientAudioManager::getOutsideScale: invalid interior on collision");
|
||||
return(false);
|
||||
}
|
||||
|
||||
// 'inside'?
|
||||
if(!interior->getDetailLevel(0)->isSurfaceOutsideVisible(collision.face))
|
||||
{
|
||||
// how 'inside' are we?
|
||||
F32 insideScale = 1.f;
|
||||
interior->getPointInsideScale(pos, &insideScale);
|
||||
|
||||
// desire 'outside' scale...
|
||||
*pScale = 1.f - insideScale;
|
||||
*pInterior = interior;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pScale = 1.f;
|
||||
*pInterior = 0;
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
void AmbientAudioManager::stopInteriorAudio()
|
||||
{
|
||||
if(mInteriorAudioHandle != NULL_AUDIOHANDLE)
|
||||
alxStop(mInteriorAudioHandle);
|
||||
if(mPowerAudioHandle != NULL_AUDIOHANDLE)
|
||||
alxStop(mPowerAudioHandle);
|
||||
|
||||
mInteriorInstance = 0;
|
||||
mInteriorAudioHandle = mPowerAudioHandle = NULL_AUDIOHANDLE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void AmbientAudioManager::update()
|
||||
{
|
||||
if(!bool(mInteriorInstance) && (mInteriorAudioHandle != NULL_AUDIOHANDLE))
|
||||
stopInteriorAudio();
|
||||
|
||||
F32 scale = mOutsideScale;
|
||||
InteriorInstance * interior = 0;
|
||||
|
||||
if(!getOutsideScale(&scale, &interior))
|
||||
stopInteriorAudio();
|
||||
|
||||
// handle the interior sound...
|
||||
if(interior)
|
||||
{
|
||||
if(bool(mInteriorInstance) && (interior != static_cast<InteriorInstance*>(mInteriorInstance)))
|
||||
stopInteriorAudio();
|
||||
|
||||
// update
|
||||
if(bool(mInteriorInstance))
|
||||
{
|
||||
AudioProfile * profile = interior->getAudioProfile();
|
||||
if(profile)
|
||||
{
|
||||
if(mLastAlarmState ^ mInteriorInstance->inAlarmState())
|
||||
{
|
||||
if(mLastAlarmState)
|
||||
{
|
||||
mLastAlarmState = false;
|
||||
|
||||
// play powerup
|
||||
if(bool(mPowerUpProfile))
|
||||
mPowerAudioHandle = alxPlay(mPowerUpProfile, &mInteriorInstance->getTransform());
|
||||
}
|
||||
else
|
||||
{
|
||||
alxStop(mInteriorAudioHandle);
|
||||
mInteriorAudioHandle = NULL_AUDIOHANDLE;
|
||||
mLastAlarmState = true;
|
||||
|
||||
// play powerdown
|
||||
if(bool(mPowerDownProfile))
|
||||
mPowerAudioHandle = alxPlay(mPowerDownProfile, &mInteriorInstance->getTransform());
|
||||
}
|
||||
}
|
||||
|
||||
// play the ambient noise when the powerup sound is done
|
||||
if((mInteriorAudioHandle == NULL_AUDIOHANDLE) && (mPowerAudioHandle == NULL_AUDIOHANDLE)
|
||||
&& (mLastAlarmState == false))
|
||||
mInteriorAudioHandle = alxPlay(profile, &mInteriorInstance->getTransform());
|
||||
|
||||
if(mInteriorAudioHandle != NULL_AUDIOHANDLE)
|
||||
alxSourcef(mInteriorAudioHandle, AL_GAIN_LINEAR, 1.f - scale);
|
||||
|
||||
if(mPowerAudioHandle != NULL_AUDIOHANDLE)
|
||||
{
|
||||
if(alxIsPlaying(mPowerAudioHandle))
|
||||
alxSourcef(mPowerAudioHandle, AL_GAIN_LINEAR, 1.f - scale);
|
||||
else
|
||||
mPowerAudioHandle = NULL_AUDIOHANDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // start
|
||||
{
|
||||
mInteriorInstance = interior;
|
||||
mLastAlarmState = mInteriorInstance->inAlarmState();
|
||||
|
||||
if(!mLastAlarmState)
|
||||
{
|
||||
AudioProfile * profile = interior->getAudioProfile();
|
||||
if(profile)
|
||||
mInteriorAudioHandle = alxPlay(profile, &mInteriorInstance->getTransform());
|
||||
}
|
||||
else
|
||||
mInteriorAudioHandle = NULL_AUDIOHANDLE;
|
||||
}
|
||||
}
|
||||
else
|
||||
stopInteriorAudio();
|
||||
|
||||
updateEnvironment();
|
||||
if(scale == mOutsideScale)
|
||||
return;
|
||||
|
||||
mOutsideScale = scale;
|
||||
|
||||
// do all the outside sounds
|
||||
for(U32 i = 0; i < mEmitters.size(); i++)
|
||||
updateEmitter(mEmitters[i]);
|
||||
|
||||
}
|
||||
|
||||
void AmbientAudioManager::updateEnvironment()
|
||||
{
|
||||
F32 scale = 0.f;
|
||||
|
||||
// inside?
|
||||
if(bool(mInteriorInstance))
|
||||
{
|
||||
mCurrentEnvironment = mInteriorInstance->getAudioEnvironment();
|
||||
scale = 1.f - mOutsideScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentEnvironment = 0;
|
||||
|
||||
Point3F pos;
|
||||
alxGetListenerPoint3F(AL_POSITION, &pos);
|
||||
|
||||
// check if submerged
|
||||
SimpleQueryList sql;
|
||||
gClientSceneGraph->getWaterObjectList(sql);
|
||||
|
||||
// grab the audio environment from the waterblock
|
||||
for(U32 i = 0; i < sql.mList.size(); i++)
|
||||
{
|
||||
WaterBlock * wBlock = dynamic_cast<WaterBlock*>(sql.mList[i]);
|
||||
if(wBlock && wBlock->isPointSubmerged(pos))
|
||||
{
|
||||
mCurrentEnvironment = wBlock->getAudioEnvironment();
|
||||
scale = 1.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mCurrentEnvironment != alxGetEnvironment())
|
||||
alxSetEnvironment(mCurrentEnvironment);
|
||||
|
||||
if(mEnvironmentScale != scale)
|
||||
{
|
||||
mEnvironmentScale = scale;
|
||||
#pragma message("todo") /*
|
||||
alxEnvironmentf(AL_ENV_EFFECT_VOLUME_EXT, mEnvironmentScale);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAudioManager::updateEmitter(AudioEmitter * emitter)
|
||||
{
|
||||
if(emitter->mOutsideAmbient && (emitter->mAudioHandle != NULL_AUDIOHANDLE))
|
||||
alxSourcef(emitter->mAudioHandle, AL_GAIN_LINEAR, emitter->mDescription.mVolume * mOutsideScale);
|
||||
}
|
||||
59
engine/game/ambientAudioManager.h
Executable file
59
engine/game/ambientAudioManager.h
Executable file
@@ -0,0 +1,59 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AMBIENTAUDIOMANAGER_H_
|
||||
#define _AMBIENTAUDIOMANAGER_H_
|
||||
|
||||
#ifndef _AUDIOEMITTER_H_
|
||||
#include "game/audioEmitter.h"
|
||||
#endif
|
||||
|
||||
class InteriorInstance;
|
||||
|
||||
/// The AmbientAudioManager manages varying the properties of audio emitters
|
||||
/// based on the player's position.
|
||||
///
|
||||
/// It not only provides a notion of "outside"-ness and "inside"-ness to Torque's
|
||||
/// sound library, but it also varies sounds based on the powered status of interiors,
|
||||
/// and plays the PowerUp/PowerDown sounds as needed.
|
||||
///
|
||||
/// AudioEmitters automatically add themselves to the AmbientAudioManager, see
|
||||
/// AudioEmitter::onAdd() and AudioEmitter::onRemove(). update() is called in
|
||||
/// clientProcess(), and gAmbientAudioManager stores the global reference to the
|
||||
/// AmbientAudioManager.
|
||||
class AmbientAudioManager
|
||||
{
|
||||
|
||||
private:
|
||||
F32 mOutsideScale; ///< 0:inside -> 1:outside
|
||||
Vector<AudioEmitter*> mEmitters;
|
||||
SimObjectPtr<InteriorInstance> mInteriorInstance;
|
||||
|
||||
SimObjectPtr<AudioEnvironment> mCurrentEnvironment;
|
||||
F32 mEnvironmentScale;
|
||||
|
||||
AUDIOHANDLE mInteriorAudioHandle;
|
||||
AUDIOHANDLE mPowerAudioHandle;
|
||||
bool mLastAlarmState;
|
||||
|
||||
bool getOutsideScale(F32 *, InteriorInstance **);
|
||||
void updateEnvironment();
|
||||
void updateEmitter(AudioEmitter *);
|
||||
void stopInteriorAudio();
|
||||
|
||||
public:
|
||||
SimObjectPtr<AudioProfile> mPowerUpProfile;
|
||||
SimObjectPtr<AudioProfile> mPowerDownProfile;
|
||||
|
||||
AmbientAudioManager();
|
||||
|
||||
void addEmitter(AudioEmitter*);
|
||||
void removeEmitter(AudioEmitter*);
|
||||
void update();
|
||||
};
|
||||
|
||||
extern AmbientAudioManager gAmbientAudioManager;
|
||||
|
||||
#endif
|
||||
1070
engine/game/audioEmitter.cc
Executable file
1070
engine/game/audioEmitter.cc
Executable file
File diff suppressed because it is too large
Load Diff
152
engine/game/audioEmitter.h
Executable file
152
engine/game/audioEmitter.h
Executable file
@@ -0,0 +1,152 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUDIOEMITTER_H_
|
||||
#define _AUDIOEMITTER_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _AUDIODATABLOCK_H_
|
||||
#include "audio/audioDataBlock.h"
|
||||
#endif
|
||||
#ifndef _NETCONNECTION_H_
|
||||
#include "sim/netConnection.h"
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
class AudioEmitter : public SceneObject
|
||||
{
|
||||
friend class AmbientAudioManager;
|
||||
friend class AudioEmitterToEvent;
|
||||
|
||||
private:
|
||||
typedef SceneObject Parent;
|
||||
|
||||
AUDIOHANDLE mAudioHandle;
|
||||
U32 mAudioProfileId;
|
||||
U32 mAudioDescriptionId;
|
||||
|
||||
// field data
|
||||
AudioProfile * mAudioProfile;
|
||||
AudioDescription * mAudioDescription;
|
||||
StringTableEntry mFilename;
|
||||
|
||||
bool mUseProfileDescription;
|
||||
static Audio::Description smDefaultDescription;
|
||||
Audio::Description mDescription;
|
||||
bool mOutsideAmbient;
|
||||
S32 mLoopCount;
|
||||
U32 mEventID;
|
||||
bool mEnableVisualFeedback;
|
||||
bool mOwnedByClient;
|
||||
F32 mAnimRotAngle;
|
||||
|
||||
enum Dirty
|
||||
{
|
||||
Profile = BIT(0),
|
||||
Description = BIT(1),
|
||||
Filename = BIT(2),
|
||||
UseProfileDescription = BIT(3),
|
||||
Volume = BIT(4),
|
||||
IsLooping = BIT(5),
|
||||
Is3D = BIT(6),
|
||||
ReferenceDistance = BIT(7),
|
||||
MaxDistance = BIT(8),
|
||||
ConeInsideAngle = BIT(9),
|
||||
ConeOutsideAngle = BIT(10),
|
||||
ConeOutsideVolume = BIT(11),
|
||||
ConeVector = BIT(12),
|
||||
LoopCount = BIT(13),
|
||||
MinLoopGap = BIT(14),
|
||||
MaxLoopGap = BIT(15),
|
||||
Transform = BIT(16),
|
||||
AudioType = BIT(17),
|
||||
OutsideAmbient = BIT(18),
|
||||
|
||||
AllDirtyMask = BIT(19) - 1,
|
||||
|
||||
SourceMask = (Profile|Description|Filename),
|
||||
LoopingMask = (LoopCount|MinLoopGap|MaxLoopGap),
|
||||
Is3DMask = (ReferenceDistance|MaxDistance|ConeInsideAngle|ConeOutsideAngle|ConeOutsideVolume|ConeVector),
|
||||
UseProfileDescriptionMask = (Volume|LoopingMask|Is3DMask|IsLooping|Is3D|AudioType),
|
||||
};
|
||||
BitSet32 mDirty;
|
||||
bool readDirtyFlag(BitStream *, U32);
|
||||
|
||||
public:
|
||||
|
||||
AudioEmitter(bool client = false);
|
||||
|
||||
void processLoopEvent();
|
||||
bool update();
|
||||
|
||||
enum UpdateMasks {
|
||||
InitialUpdateMask = BIT(0),
|
||||
TransformUpdateMask = BIT(1),
|
||||
DirtyUpdateMask = BIT(2),
|
||||
};
|
||||
|
||||
void packData(NetConnection * conn, U32 mask, BitStream * stream);
|
||||
void unpackData(NetConnection * conn, BitStream * stream);
|
||||
|
||||
// SceneObject
|
||||
void setTransform (const MatrixF &mat);
|
||||
void setScale (const VectorF &scale);
|
||||
|
||||
void inspectPreApply();
|
||||
void inspectPostApply();
|
||||
|
||||
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
void render3DVisualFeedBack(void);
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
DECLARE_CONOBJECT(AudioEmitter);
|
||||
};
|
||||
|
||||
////---------------------------------------------------------------------------
|
||||
//class ClientAudioEmitter : public AudioEmitter
|
||||
//{
|
||||
// private:
|
||||
// typedef AudioEmitter Parent;
|
||||
//
|
||||
// public:
|
||||
// U32 mEventCount;
|
||||
// ClientAudioEmitter();
|
||||
// bool onAdd();
|
||||
//
|
||||
// static void initPersistFields();
|
||||
// DECLARE_CONOBJECT(ClientAudioEmitter);
|
||||
//};
|
||||
//
|
||||
////---------------------------------------------------------------------------
|
||||
//class AudioEmitterToEvent : public NetEvent
|
||||
//{
|
||||
// private:
|
||||
// SimObjectPtr<ClientAudioEmitter> mEmitter;
|
||||
//
|
||||
// public:
|
||||
// AudioEmitterToEvent(ClientAudioEmitter * emitter = 0);
|
||||
//
|
||||
// void notifyDelivered(NetConnection *, bool);
|
||||
// void write(NetConnection *, BitStream *);
|
||||
// void pack(NetConnection *, BitStream *);
|
||||
// void unpack(NetConnection *, BitStream *);
|
||||
// void process(NetConnection *) {}
|
||||
//
|
||||
// DECLARE_CONOBJECT(AudioEmitterToEvent);
|
||||
//};
|
||||
|
||||
#endif
|
||||
42
engine/game/auth.h
Executable file
42
engine/game/auth.h
Executable file
@@ -0,0 +1,42 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUTH_H_
|
||||
#define _AUTH_H_
|
||||
|
||||
#ifndef _EVENT_H_
|
||||
#include "platform/event.h"
|
||||
#endif
|
||||
|
||||
/// Formerly contained a certificate, showing that something was valid.
|
||||
class Auth2Certificate
|
||||
{
|
||||
U32 xxx;
|
||||
};
|
||||
|
||||
/// Formerly contained data indicating whether a user is valid.
|
||||
struct AuthInfo
|
||||
{
|
||||
enum {
|
||||
MaxNameLen = 31,
|
||||
};
|
||||
|
||||
bool valid;
|
||||
char name[MaxNameLen + 1];
|
||||
};
|
||||
|
||||
/// Formerly validated the server's authentication info.
|
||||
inline bool validateAuthenticatedServer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Formerly validated the client's authentication info.
|
||||
inline bool validateAuthenticatedClient()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
214
engine/game/badWordFilter.cc
Executable file
214
engine/game/badWordFilter.cc
Executable file
@@ -0,0 +1,214 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "platform/platform.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "game/badWordFilter.h"
|
||||
|
||||
BadWordFilter *gBadWordFilter = NULL;
|
||||
bool BadWordFilter::filteringEnabled = true;
|
||||
|
||||
BadWordFilter::BadWordFilter()
|
||||
{
|
||||
dStrcpy(defaultReplaceStr, "knqwrtlzs");
|
||||
filterTables.push_back(new FilterTable);
|
||||
curOffset = 0;
|
||||
}
|
||||
|
||||
BadWordFilter::~BadWordFilter()
|
||||
{
|
||||
for(U32 i = 0; i < filterTables.size(); i++)
|
||||
delete filterTables[i];
|
||||
}
|
||||
|
||||
void BadWordFilter::create()
|
||||
{
|
||||
Con::addVariable("pref::enableBadWordFilter", TypeBool, &filteringEnabled);
|
||||
gBadWordFilter = new BadWordFilter;
|
||||
gBadWordFilter->addBadWord("shit");
|
||||
gBadWordFilter->addBadWord("fuck");
|
||||
gBadWordFilter->addBadWord("cock");
|
||||
gBadWordFilter->addBadWord("bitch");
|
||||
gBadWordFilter->addBadWord("cunt");
|
||||
gBadWordFilter->addBadWord("nigger");
|
||||
gBadWordFilter->addBadWord("bastard");
|
||||
gBadWordFilter->addBadWord("dick");
|
||||
gBadWordFilter->addBadWord("whore");
|
||||
gBadWordFilter->addBadWord("goddamn");
|
||||
gBadWordFilter->addBadWord("asshole");
|
||||
}
|
||||
|
||||
void BadWordFilter::destroy()
|
||||
{
|
||||
delete gBadWordFilter;
|
||||
gBadWordFilter = NULL;
|
||||
}
|
||||
|
||||
|
||||
U8 BadWordFilter::remapTable[257] = "------------------------------------------------OI---------------ABCDEFGHIJKLMNOPQRSTUVWXYZ------ABCDEFGHIJKLMNOPQRSTUVWXYZ-----C--F--TT--S-C-Z-----------S-C-ZY--CLOY-S-CA---R---UT-UP--IO-----AAAAAAACEEEEIIIIDNOOOOOXOUUUUYDBAAAAAAACEEEEIIIIDNOOOOO-OUUUUYDY";
|
||||
U8 BadWordFilter::randomJunk[MaxBadwordLength+1] = "REMsg rk34n4ksqow;xnskq;KQoaWnZa";
|
||||
|
||||
BadWordFilter::FilterTable::FilterTable()
|
||||
{
|
||||
for(U32 i = 0; i < 26; i++)
|
||||
nextState[i] = TerminateNotFound;
|
||||
}
|
||||
|
||||
bool BadWordFilter::addBadWord(const char *cword)
|
||||
{
|
||||
FilterTable *curFilterTable = filterTables[0];
|
||||
// prescan the word to see if it has any skip chars
|
||||
const U8 *word = (const U8 *) cword;
|
||||
const U8 *walk = word;
|
||||
if(dStrlen(cword) > MaxBadwordLength)
|
||||
return false;
|
||||
while(*walk)
|
||||
{
|
||||
if(remapTable[*walk] == '-')
|
||||
return false;
|
||||
walk++;
|
||||
}
|
||||
while(*word)
|
||||
{
|
||||
U8 remap = remapTable[*word] - 'A';
|
||||
U16 state = curFilterTable->nextState[remap];
|
||||
|
||||
if(state < TerminateNotFound)
|
||||
{
|
||||
// this character is already in the state table...
|
||||
curFilterTable = filterTables[state];
|
||||
}
|
||||
else if(state == TerminateFound)
|
||||
{
|
||||
// a subset of this word is already in the table...
|
||||
// exit out.
|
||||
return false;
|
||||
}
|
||||
else if(state == TerminateNotFound)
|
||||
{
|
||||
if(word[1])
|
||||
{
|
||||
curFilterTable->nextState[remap] = filterTables.size();
|
||||
filterTables.push_back(new FilterTable);
|
||||
curFilterTable = filterTables[filterTables.size() - 1];
|
||||
}
|
||||
else
|
||||
curFilterTable->nextState[remap] = TerminateFound;
|
||||
}
|
||||
word++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BadWordFilter::setDefaultReplaceStr(const char *str)
|
||||
{
|
||||
U32 len = dStrlen(str);
|
||||
if(len < 2 || len >= sizeof(defaultReplaceStr))
|
||||
return false;
|
||||
dStrcpy(defaultReplaceStr, str);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BadWordFilter::filterString(char *cstring, const char *replaceStr)
|
||||
{
|
||||
if(!replaceStr)
|
||||
replaceStr = defaultReplaceStr;
|
||||
U8 *string = (U8 *) cstring;
|
||||
U8 *starts[MaxBadwordLength];
|
||||
U8 *curStart = string;
|
||||
U32 replaceLen = dStrlen(replaceStr);
|
||||
while(*curStart)
|
||||
{
|
||||
FilterTable *curFilterTable = filterTables[0];
|
||||
S32 index = 0;
|
||||
U8 *walk = curStart;
|
||||
while(*walk)
|
||||
{
|
||||
U8 remap = remapTable[*walk];
|
||||
if(remap != '-')
|
||||
{
|
||||
starts[index++] = walk;
|
||||
U16 table = curFilterTable->nextState[remap - 'A'];
|
||||
if(table < TerminateNotFound)
|
||||
curFilterTable = filterTables[table];
|
||||
else if(table == TerminateNotFound)
|
||||
{
|
||||
curStart++;
|
||||
break;
|
||||
}
|
||||
else // terminate found
|
||||
{
|
||||
for(U32 i = 0; i < index; i++)
|
||||
{
|
||||
starts[i][0] = (U8 )replaceStr[curOffset % replaceLen];
|
||||
curOffset += randomJunk[curOffset & (MaxBadwordLength - 1)];
|
||||
}
|
||||
curStart = walk + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
walk++;
|
||||
}
|
||||
if(!*walk)
|
||||
curStart++;
|
||||
}
|
||||
}
|
||||
|
||||
bool BadWordFilter::containsBadWords(const char *cstring)
|
||||
{
|
||||
U8 *string = (U8 *) cstring;
|
||||
U8 *curStart = string;
|
||||
while(*curStart)
|
||||
{
|
||||
FilterTable *curFilterTable = filterTables[0];
|
||||
S32 index = 0;
|
||||
U8 *walk = curStart;
|
||||
while(*walk)
|
||||
{
|
||||
U8 remap = remapTable[*walk];
|
||||
if(remap != '-')
|
||||
{
|
||||
U16 table = curFilterTable->nextState[remap - 'A'];
|
||||
if(table < TerminateNotFound)
|
||||
curFilterTable = filterTables[table];
|
||||
else if(table == TerminateNotFound)
|
||||
{
|
||||
curStart++;
|
||||
break;
|
||||
}
|
||||
else // terminate found
|
||||
return true;
|
||||
}
|
||||
walk++;
|
||||
}
|
||||
if(!*walk)
|
||||
curStart++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ConsoleFunction(addBadWord, bool, 2, 2, "addBadWord(someReallyNastyWord);")
|
||||
{
|
||||
argc;
|
||||
return gBadWordFilter->addBadWord(argv[1]);
|
||||
}
|
||||
|
||||
ConsoleFunction(filterString, const char *, 2, 3, "filterString(baseString,replacementChars);")
|
||||
{
|
||||
const char *replaceStr = NULL;
|
||||
if(argc == 3)
|
||||
replaceStr = argv[2];
|
||||
|
||||
char *ret = Con::getReturnBuffer(dStrlen(argv[1]) + 1);
|
||||
dStrcpy(ret, argv[1]);
|
||||
gBadWordFilter->filterString(ret, replaceStr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ConsoleFunction(containsBadWords, bool, 2, 2, "containsBadWords(text);")
|
||||
{
|
||||
argc;
|
||||
return gBadWordFilter->containsBadWords(argv[1]);
|
||||
}
|
||||
49
engine/game/badWordFilter.h
Executable file
49
engine/game/badWordFilter.h
Executable file
@@ -0,0 +1,49 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _H_BADWORDFILTER
|
||||
#define _H_BADWORDFILTER
|
||||
|
||||
#include "core/tVector.h"
|
||||
|
||||
class BadWordFilter
|
||||
{
|
||||
private:
|
||||
struct FilterTable
|
||||
{
|
||||
U16 nextState[26]; // only 26 alphabetical chars.
|
||||
FilterTable();
|
||||
};
|
||||
friend struct FilterTable;
|
||||
Vector<FilterTable*> filterTables;
|
||||
|
||||
enum {
|
||||
TerminateNotFound = 0xFFFE,
|
||||
TerminateFound = 0xFFFF,
|
||||
MaxBadwordLength = 32,
|
||||
};
|
||||
char defaultReplaceStr[32];
|
||||
|
||||
BadWordFilter();
|
||||
~BadWordFilter();
|
||||
U32 curOffset;
|
||||
static U8 remapTable[257];
|
||||
static U8 randomJunk[MaxBadwordLength + 1];
|
||||
static bool filteringEnabled;
|
||||
|
||||
public:
|
||||
bool addBadWord(const char *word);
|
||||
bool setDefaultReplaceStr(const char *str);
|
||||
void filterString(char *string, const char *replaceStr = NULL);
|
||||
bool containsBadWords(const char *string);
|
||||
|
||||
static bool isEnabled() { return filteringEnabled; }
|
||||
static void setEnabled(bool enable) { filteringEnabled = enable; }
|
||||
static void create();
|
||||
static void destroy();
|
||||
};
|
||||
|
||||
extern BadWordFilter *gBadWordFilter;
|
||||
|
||||
#endif
|
||||
187
engine/game/banList.cc
Executable file
187
engine/game/banList.cc
Executable file
@@ -0,0 +1,187 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/banList.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/resManager.h"
|
||||
#include "core/fileStream.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(BanList);
|
||||
BanList gBanList;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void BanList::addBan(S32 uniqueId, const char *TA, S32 banTime)
|
||||
{
|
||||
S32 curTime = Platform::getTime();
|
||||
|
||||
if(banTime != 0 && banTime < curTime)
|
||||
return;
|
||||
|
||||
// make sure this bastard isn't already banned on this server
|
||||
Vector<BanInfo>::iterator i;
|
||||
for(i = list.begin();i != list.end();i++)
|
||||
{
|
||||
if(uniqueId == i->uniqueId)
|
||||
{
|
||||
i->bannedUntil = banTime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BanInfo b;
|
||||
dStrcpy(b.transportAddress, TA);
|
||||
b.uniqueId = uniqueId;
|
||||
b.bannedUntil = banTime;
|
||||
|
||||
if(!dStrnicmp(b.transportAddress, "ip:", 3))
|
||||
{
|
||||
char *c = dStrchr(b.transportAddress+3, ':');
|
||||
if(c)
|
||||
{
|
||||
*(c+1) = '*';
|
||||
*(c+2) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
list.push_back(b);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void BanList::addBanRelative(S32 uniqueId, const char *TA, S32 numSeconds)
|
||||
{
|
||||
S32 curTime = Platform::getTime();
|
||||
S32 banTime = 0;
|
||||
if(numSeconds != -1)
|
||||
banTime = curTime + numSeconds;
|
||||
|
||||
addBan(uniqueId, TA, banTime);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void BanList::removeBan(S32 uniqueId, const char *)
|
||||
{
|
||||
Vector<BanInfo>::iterator i;
|
||||
for(i = list.begin();i != list.end();i++)
|
||||
{
|
||||
if(uniqueId == i->uniqueId)
|
||||
{
|
||||
list.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool BanList::isBanned(S32 uniqueId, const char *)
|
||||
{
|
||||
S32 curTime = Platform::getTime();
|
||||
|
||||
Vector<BanInfo>::iterator i;
|
||||
for(i = list.begin();i != list.end();)
|
||||
{
|
||||
if(i->bannedUntil != 0 && i->bannedUntil < curTime)
|
||||
{
|
||||
list.erase(i);
|
||||
continue;
|
||||
}
|
||||
else if(uniqueId == i->uniqueId)
|
||||
return true;
|
||||
i++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool BanList::isTAEq(const char *bannedTA, const char *TA)
|
||||
{
|
||||
char a, b;
|
||||
for(;;)
|
||||
{
|
||||
a = *bannedTA++;
|
||||
b = *TA++;
|
||||
if(a == '*' || (!a && b == ':')) // ignore port
|
||||
return true;
|
||||
if(dTolower(a) != dTolower(b))
|
||||
return false;
|
||||
if(!a)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void BanList::exportToFile(const char *name)
|
||||
{
|
||||
FileStream banlist;
|
||||
|
||||
char filename[1024];
|
||||
Con::expandScriptFilename(filename, sizeof(filename), name);
|
||||
if(ResourceManager->openFileForWrite(banlist, filename, FileStream::Write))
|
||||
{
|
||||
char buf[1024];
|
||||
Vector<BanInfo>::iterator i;
|
||||
for(i = list.begin(); i != list.end(); i++)
|
||||
{
|
||||
dSprintf(buf, sizeof(buf), "BanList::addAbsolute(%d, \"%s\", %d);\r\n", i->uniqueId, i->transportAddress, i->bannedUntil);
|
||||
banlist.write(dStrlen(buf), buf);
|
||||
}
|
||||
}
|
||||
|
||||
banlist.close();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// console methods
|
||||
// ---------------------------------------------------------
|
||||
|
||||
ConsoleStaticMethod( BanList, addAbsolute, void, 4, 4, "( int ID, TransportAddress TA, int banTime )"
|
||||
"Ban a user until a given time.\n\n"
|
||||
"@param ID Unique ID of the player.\n"
|
||||
"@param TA Address from which the player connected.\n"
|
||||
"@param banTime Time at which they will be allowed back in.")
|
||||
{
|
||||
gBanList.addBan( dAtoi( argv[1] ), argv[2], dAtoi( argv[3] ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleStaticMethod( BanList, add, void, 4, 4, "( int ID, TransportAddress TA, int banLength )"
|
||||
"Ban a user for banLength seconds.\n\n"
|
||||
"@param ID Unique ID of the player.\n"
|
||||
"@param TA Address from which the player connected.\n"
|
||||
"@param banTime Time at which they will be allowed back in.")
|
||||
{
|
||||
gBanList.addBanRelative( dAtoi( argv[1] ), argv[2], dAtoi( argv[3] ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleStaticMethod( BanList, removeBan, void, 3, 3, "( int ID, TransportAddress TA )"
|
||||
"Unban someone.\n\n"
|
||||
"@param ID Unique ID of the player.\n"
|
||||
"@param TA Address from which the player connected.\n")
|
||||
|
||||
{
|
||||
gBanList.removeBan( dAtoi( argv[1] ), argv[2] );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleStaticMethod( BanList, isBanned, bool, 3, 3, "( int ID, TransportAddress TA )"
|
||||
"Is someone banned?\n\n"
|
||||
"@param ID Unique ID of the player.\n"
|
||||
"@param TA Address from which the player connected.\n")
|
||||
{
|
||||
return (gBanList.isBanned( dAtoi( argv[1] ), argv[2] ));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleStaticMethod( BanList, export, void, 2, 2, "(string filename)"
|
||||
"Dump the banlist to a file.")
|
||||
{
|
||||
gBanList.exportToFile( argv[1] );
|
||||
}
|
||||
46
engine/game/banList.h
Executable file
46
engine/game/banList.h
Executable file
@@ -0,0 +1,46 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BANLIST_H_
|
||||
#define _BANLIST_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
/// Helper class to keep track of bans.
|
||||
class BanList : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
public:
|
||||
|
||||
struct BanInfo
|
||||
{
|
||||
S32 uniqueId;
|
||||
char transportAddress[128];
|
||||
S32 bannedUntil;
|
||||
};
|
||||
|
||||
Vector<BanInfo> list;
|
||||
|
||||
BanList(){}
|
||||
~BanList(){}
|
||||
|
||||
void addBan(S32 uniqueId, const char *TA, S32 banTime);
|
||||
void addBanRelative(S32 uniqueId, const char *TA, S32 numSeconds);
|
||||
void removeBan(S32 uniqueId, const char *TA);
|
||||
bool isBanned(S32 uniqueId, const char *TA);
|
||||
bool isTAEq(const char *bannedTA, const char *TA);
|
||||
void exportToFile(const char *fileName);
|
||||
|
||||
DECLARE_CONOBJECT(BanList);
|
||||
};
|
||||
|
||||
extern BanList gBanList;
|
||||
|
||||
#endif
|
||||
696
engine/game/camera.cc
Executable file
696
engine/game/camera.cc
Executable file
@@ -0,0 +1,696 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "game/game.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "core/dnet.h"
|
||||
#include "game/camera.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "editor/editor.h"
|
||||
|
||||
#define MaxPitch 1.3962
|
||||
#define CameraRadius 0.05;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(CameraData);
|
||||
|
||||
void CameraData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void CameraData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
}
|
||||
|
||||
void CameraData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Camera);
|
||||
F32 Camera::mMovementSpeed = 40;
|
||||
|
||||
Camera::Camera()
|
||||
{
|
||||
mNetFlags.clear(Ghostable);
|
||||
mTypeMask |= CameraObjectType;
|
||||
delta.pos = Point3F(0,0,100);
|
||||
delta.rot = Point3F(0,0,0);
|
||||
delta.posVec = delta.rotVec = VectorF(0,0,0);
|
||||
mObjToWorld.setColumn(3,delta.pos);
|
||||
mRot = delta.rot;
|
||||
|
||||
mMinOrbitDist = 0;
|
||||
mMaxOrbitDist = 0;
|
||||
mCurOrbitDist = 0;
|
||||
mOrbitObject = NULL;
|
||||
mPosition.set(0.f, 0.f, 0.f);
|
||||
mObservingClientObject = false;
|
||||
mode = 2;
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool Camera::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
mObjBox.max = mObjScale;
|
||||
mObjBox.min = mObjScale;
|
||||
mObjBox.min.neg();
|
||||
resetWorldBox();
|
||||
|
||||
if(isClientObject())
|
||||
gClientContainer.addObject(this);
|
||||
else
|
||||
gServerContainer.addObject(this);
|
||||
|
||||
// addToScene();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Camera::onEditorEnable()
|
||||
{
|
||||
mNetFlags.set(Ghostable);
|
||||
}
|
||||
|
||||
void Camera::onEditorDisable()
|
||||
{
|
||||
mNetFlags.clear(Ghostable);
|
||||
}
|
||||
|
||||
void Camera::onRemove()
|
||||
{
|
||||
// removeFromScene();
|
||||
if (getContainer())
|
||||
getContainer()->removeObject(this);
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// check if the object needs to be observed through its own camera...
|
||||
void Camera::getCameraTransform(F32* pos, MatrixF* mat)
|
||||
{
|
||||
// The camera doesn't support a third person mode,
|
||||
// so we want to override the default ShapeBase behavior.
|
||||
ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
|
||||
if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
|
||||
obj->getCameraTransform(pos, mat);
|
||||
else
|
||||
getEyeTransform(mat);
|
||||
}
|
||||
|
||||
F32 Camera::getCameraFov()
|
||||
{
|
||||
ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
|
||||
if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
|
||||
return(obj->getCameraFov());
|
||||
else
|
||||
return(Parent::getCameraFov());
|
||||
}
|
||||
|
||||
F32 Camera::getDefaultCameraFov()
|
||||
{
|
||||
ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
|
||||
if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
|
||||
return(obj->getDefaultCameraFov());
|
||||
else
|
||||
return(Parent::getDefaultCameraFov());
|
||||
}
|
||||
|
||||
bool Camera::isValidCameraFov(F32 fov)
|
||||
{
|
||||
ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
|
||||
if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
|
||||
return(obj->isValidCameraFov(fov));
|
||||
else
|
||||
return(Parent::isValidCameraFov(fov));
|
||||
}
|
||||
|
||||
void Camera::setCameraFov(F32 fov)
|
||||
{
|
||||
ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
|
||||
if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
|
||||
obj->setCameraFov(fov);
|
||||
else
|
||||
Parent::setCameraFov(fov);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Camera::processTick(const Move* move)
|
||||
{
|
||||
Parent::processTick(move);
|
||||
Point3F vec,pos;
|
||||
if (move)
|
||||
{
|
||||
// If using editor then force camera into fly mode
|
||||
if(gEditingMission && mode != FlyMode)
|
||||
setFlyMode();
|
||||
|
||||
// Update orientation
|
||||
delta.rotVec = mRot;
|
||||
mObjToWorld.getColumn(3,&delta.posVec);
|
||||
|
||||
mRot.x += move->pitch;
|
||||
if(mRot.x > MaxPitch)
|
||||
mRot.x = MaxPitch;
|
||||
else if(mRot.x < -MaxPitch)
|
||||
mRot.x = -MaxPitch;
|
||||
|
||||
mRot.z += move->yaw;
|
||||
|
||||
if(mode == OrbitObjectMode || mode == OrbitPointMode)
|
||||
{
|
||||
if(mode == OrbitObjectMode && bool(mOrbitObject))
|
||||
{
|
||||
// If this is a shapebase, use its render eye transform
|
||||
// to avoid jittering.
|
||||
GameBase *castObj = mOrbitObject;
|
||||
ShapeBase* shape = dynamic_cast<ShapeBase*>(castObj);
|
||||
if( shape != NULL )
|
||||
{
|
||||
MatrixF ret;
|
||||
shape->getRenderEyeTransform( &ret );
|
||||
mPosition = ret.getPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hopefully this is a static object that doesn't move,
|
||||
// because the worldbox doesn't get updated between ticks.
|
||||
mOrbitObject->getWorldBox().getCenter(&mPosition);
|
||||
}
|
||||
}
|
||||
setPosition(mPosition, mRot);
|
||||
validateEyePoint(1.0f, &mObjToWorld);
|
||||
pos = mPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update pos
|
||||
bool faster = move->trigger[0] || move->trigger[1];
|
||||
F32 scale = mMovementSpeed * (faster + 1);
|
||||
|
||||
mObjToWorld.getColumn(3,&pos);
|
||||
mObjToWorld.getColumn(0,&vec);
|
||||
pos += vec * move->x * TickSec * scale;
|
||||
mObjToWorld.getColumn(1,&vec);
|
||||
pos += vec * move->y * TickSec * scale;
|
||||
mObjToWorld.getColumn(2,&vec);
|
||||
pos += vec * move->z * TickSec * scale;
|
||||
setPosition(pos,mRot);
|
||||
}
|
||||
|
||||
// If on the client, calc delta for backstepping
|
||||
if (isClientObject())
|
||||
{
|
||||
delta.pos = pos;
|
||||
delta.rot = mRot;
|
||||
delta.posVec = delta.posVec - delta.pos;
|
||||
delta.rotVec = delta.rotVec - delta.rot;
|
||||
}
|
||||
setMaskBits(MoveMask);
|
||||
}
|
||||
|
||||
if(getControllingClient() && mContainer)
|
||||
updateContainer();
|
||||
}
|
||||
|
||||
void Camera::onDeleteNotify(SimObject *obj)
|
||||
{
|
||||
Parent::onDeleteNotify(obj);
|
||||
if (obj == (SimObject*)mOrbitObject)
|
||||
{
|
||||
mOrbitObject = NULL;
|
||||
|
||||
if(mode == OrbitObjectMode)
|
||||
mode = OrbitPointMode;
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::interpolateTick(F32 dt)
|
||||
{
|
||||
Parent::interpolateTick(dt);
|
||||
Point3F rot = delta.rot + delta.rotVec * dt;
|
||||
|
||||
if(mode == OrbitObjectMode || mode == OrbitPointMode)
|
||||
{
|
||||
if(mode == OrbitObjectMode && bool(mOrbitObject))
|
||||
{
|
||||
// If this is a shapebase, use its render eye transform
|
||||
// to avoid jittering.
|
||||
GameBase *castObj = mOrbitObject;
|
||||
ShapeBase* shape = dynamic_cast<ShapeBase*>(castObj);
|
||||
if( shape != NULL )
|
||||
{
|
||||
MatrixF ret;
|
||||
shape->getRenderEyeTransform( &ret );
|
||||
mPosition = ret.getPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hopefully this is a static object that doesn't move,
|
||||
// because the worldbox doesn't get updated between ticks.
|
||||
mOrbitObject->getWorldBox().getCenter(&mPosition);
|
||||
}
|
||||
}
|
||||
setRenderPosition(mPosition, rot);
|
||||
validateEyePoint(1.0f, &mRenderObjToWorld);
|
||||
}
|
||||
else
|
||||
{
|
||||
Point3F pos = delta.pos + delta.posVec * dt;
|
||||
setRenderPosition(pos,rot);
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::setPosition(const Point3F& pos,const Point3F& rot)
|
||||
{
|
||||
MatrixF xRot, zRot;
|
||||
xRot.set(EulerF(rot.x, 0, 0));
|
||||
zRot.set(EulerF(0, 0, rot.z));
|
||||
MatrixF temp;
|
||||
temp.mul(zRot, xRot);
|
||||
temp.setColumn(3, pos);
|
||||
Parent::setTransform(temp);
|
||||
mRot = rot;
|
||||
}
|
||||
|
||||
void Camera::setRenderPosition(const Point3F& pos,const Point3F& rot)
|
||||
{
|
||||
MatrixF xRot, zRot;
|
||||
xRot.set(EulerF(rot.x, 0, 0));
|
||||
zRot.set(EulerF(0, 0, rot.z));
|
||||
MatrixF temp;
|
||||
temp.mul(zRot, xRot);
|
||||
temp.setColumn(3, pos);
|
||||
Parent::setRenderTransform(temp);
|
||||
mRot = rot;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Camera::writePacketData(GameConnection *connection, BitStream *bstream)
|
||||
{
|
||||
// Update client regardless of status flags.
|
||||
Parent::writePacketData(connection, bstream);
|
||||
|
||||
Point3F pos;
|
||||
mObjToWorld.getColumn(3,&pos);
|
||||
bstream->setCompressionPoint(pos);
|
||||
mathWrite(*bstream, pos);
|
||||
bstream->write(mRot.x);
|
||||
bstream->write(mRot.z);
|
||||
|
||||
U32 writeMode = mode;
|
||||
Point3F writePos = mPosition;
|
||||
S32 gIndex = -1;
|
||||
if(mode == OrbitObjectMode)
|
||||
{
|
||||
gIndex = bool(mOrbitObject) ? connection->getGhostIndex(mOrbitObject): -1;
|
||||
if(gIndex == -1)
|
||||
{
|
||||
writeMode = OrbitPointMode;
|
||||
mOrbitObject->getWorldBox().getCenter(&writePos);
|
||||
}
|
||||
}
|
||||
bstream->writeRangedU32(writeMode, CameraFirstMode, CameraLastMode);
|
||||
|
||||
if (writeMode == OrbitObjectMode || writeMode == OrbitPointMode)
|
||||
{
|
||||
bstream->write(mMinOrbitDist);
|
||||
bstream->write(mMaxOrbitDist);
|
||||
bstream->write(mCurOrbitDist);
|
||||
if(writeMode == OrbitObjectMode)
|
||||
{
|
||||
bstream->writeFlag(mObservingClientObject);
|
||||
bstream->writeInt(gIndex, NetConnection::GhostIdBitSize);
|
||||
}
|
||||
if (writeMode == OrbitPointMode)
|
||||
bstream->writeCompressedPoint(writePos);
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::readPacketData(GameConnection *connection, BitStream *bstream)
|
||||
{
|
||||
Parent::readPacketData(connection, bstream);
|
||||
Point3F pos,rot;
|
||||
mathRead(*bstream, &pos);
|
||||
bstream->setCompressionPoint(pos);
|
||||
bstream->read(&rot.x);
|
||||
bstream->read(&rot.z);
|
||||
|
||||
GameBase* obj = 0;
|
||||
mode = bstream->readRangedU32(CameraFirstMode, CameraLastMode);
|
||||
mObservingClientObject = false;
|
||||
if (mode == OrbitObjectMode || mode == OrbitPointMode) {
|
||||
bstream->read(&mMinOrbitDist);
|
||||
bstream->read(&mMaxOrbitDist);
|
||||
bstream->read(&mCurOrbitDist);
|
||||
|
||||
if(mode == OrbitObjectMode)
|
||||
{
|
||||
mObservingClientObject = bstream->readFlag();
|
||||
S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
|
||||
obj = static_cast<GameBase*>(connection->resolveGhost(gIndex));
|
||||
}
|
||||
if (mode == OrbitPointMode)
|
||||
bstream->readCompressedPoint(&mPosition);
|
||||
}
|
||||
if (obj != (GameBase*)mOrbitObject) {
|
||||
if (mOrbitObject) {
|
||||
clearProcessAfter();
|
||||
clearNotify(mOrbitObject);
|
||||
}
|
||||
mOrbitObject = obj;
|
||||
if (mOrbitObject) {
|
||||
processAfter(mOrbitObject);
|
||||
deleteNotify(mOrbitObject);
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(pos,rot);
|
||||
delta.pos = pos;
|
||||
delta.rot = rot;
|
||||
delta.rotVec.set(0,0,0);
|
||||
delta.posVec.set(0,0,0);
|
||||
}
|
||||
|
||||
U32 Camera::packUpdate(NetConnection *con, U32 mask, BitStream *bstream)
|
||||
{
|
||||
Parent::packUpdate(con,mask,bstream);
|
||||
|
||||
// The rest of the data is part of the control object packet update.
|
||||
// If we're controlled by this client, we don't need to send it.
|
||||
if(bstream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
|
||||
return 0;
|
||||
|
||||
if (bstream->writeFlag(mask & MoveMask)) {
|
||||
Point3F pos;
|
||||
mObjToWorld.getColumn(3,&pos);
|
||||
bstream->write(pos.x);
|
||||
bstream->write(pos.y);
|
||||
bstream->write(pos.z);
|
||||
bstream->write(mRot.x);
|
||||
bstream->write(mRot.z);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Camera::unpackUpdate(NetConnection *con, BitStream *bstream)
|
||||
{
|
||||
Parent::unpackUpdate(con,bstream);
|
||||
|
||||
// controlled by the client?
|
||||
if(bstream->readFlag())
|
||||
return;
|
||||
|
||||
if (bstream->readFlag()) {
|
||||
Point3F pos,rot;
|
||||
bstream->read(&pos.x);
|
||||
bstream->read(&pos.y);
|
||||
bstream->read(&pos.z);
|
||||
bstream->read(&rot.x);
|
||||
bstream->read(&rot.z);
|
||||
setPosition(pos,rot);
|
||||
|
||||
// New delta for client side interpolation
|
||||
delta.pos = pos;
|
||||
delta.rot = rot;
|
||||
delta.posVec = delta.rotVec = VectorF(0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Camera::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void Camera::consoleInit()
|
||||
{
|
||||
Con::addVariable("Camera::movementSpeed",TypeF32,&mMovementSpeed);
|
||||
}
|
||||
|
||||
Point3F &Camera::getPosition()
|
||||
{
|
||||
static Point3F position;
|
||||
mObjToWorld.getColumn(3, &position);
|
||||
return position;
|
||||
}
|
||||
|
||||
ConsoleMethod( Camera, getPosition, const char *, 2, 2, "()"
|
||||
"Get the position of the camera.\n\n"
|
||||
"@returns A string of form \"x y z\".")
|
||||
{
|
||||
static char buffer[100];
|
||||
|
||||
Point3F& pos = object->getPosition();
|
||||
dSprintf(buffer, sizeof(buffer),"%g %g %g",pos.x,pos.y,pos.z);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ConsoleMethod( Camera, setOrbitMode, void, 7, 8, "(GameBase orbitObject, transform mat, float minDistance,"
|
||||
" float maxDistance, float curDistance, bool ownClientObject)"
|
||||
"Set the camera to orbit around some given object.\n\n"
|
||||
"@param orbitObject Object we want to orbit.\n"
|
||||
"@param mat A set of fields: posX posY posZ aaX aaY aaZ aaTheta\n"
|
||||
"@param minDistance Minimum distance to keep from object.\n"
|
||||
"@param maxDistance Maximum distance to keep from object.\n"
|
||||
"@param curDistance Distance to set initially from object.\n"
|
||||
"@param ownClientObj Are we observing an object owned by us?")
|
||||
{
|
||||
Point3F pos;
|
||||
AngAxisF aa;
|
||||
F32 minDis, maxDis, curDis;
|
||||
|
||||
GameBase *orbitObject = NULL;
|
||||
if(Sim::findObject(argv[2],orbitObject) == false)
|
||||
{
|
||||
Con::warnf("Cannot orbit non-existing object.");
|
||||
object->setFlyMode();
|
||||
return;
|
||||
}
|
||||
|
||||
dSscanf(argv[3],"%g %g %g %g %g %g %g",
|
||||
&pos.x,&pos.y,&pos.z,&aa.axis.x,&aa.axis.y,&aa.axis.z,&aa.angle);
|
||||
minDis = dAtof(argv[4]);
|
||||
maxDis = dAtof(argv[5]);
|
||||
curDis = dAtof(argv[6]);
|
||||
|
||||
object->setOrbitMode(orbitObject, pos, aa, minDis, maxDis, curDis, (argc == 8) ? dAtob(argv[7]) : false);
|
||||
}
|
||||
|
||||
ConsoleMethod( Camera, setFlyMode, void, 2, 2, "()"
|
||||
"Set the camera to be able to fly freely.")
|
||||
{
|
||||
object->setFlyMode();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Camera::renderImage(SceneState*, SceneRenderImage*)
|
||||
{
|
||||
if(gEditingMission)
|
||||
{
|
||||
glPushMatrix();
|
||||
dglMultMatrix(&mObjToWorld);
|
||||
glScalef(mObjScale.x,mObjScale.y,mObjScale.z);
|
||||
wireCube(Point3F(1, 1, 1),Point3F(0,0,0));
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
// NEW Observer Code
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
void Camera::setFlyMode()
|
||||
{
|
||||
mode = FlyMode;
|
||||
|
||||
if(bool(mOrbitObject)) {
|
||||
clearProcessAfter();
|
||||
clearNotify(mOrbitObject);
|
||||
}
|
||||
mOrbitObject = NULL;
|
||||
}
|
||||
|
||||
void Camera::setOrbitMode(GameBase *obj, Point3F &pos, AngAxisF &rot, F32 minDist, F32 maxDist, F32 curDist, bool ownClientObject)
|
||||
{
|
||||
mObservingClientObject = ownClientObject;
|
||||
|
||||
rot;
|
||||
if(bool(mOrbitObject)) {
|
||||
clearProcessAfter();
|
||||
clearNotify(mOrbitObject);
|
||||
}
|
||||
mOrbitObject = obj;
|
||||
if(bool(mOrbitObject))
|
||||
{
|
||||
processAfter(mOrbitObject);
|
||||
deleteNotify(mOrbitObject);
|
||||
mOrbitObject->getWorldBox().getCenter(&mPosition);
|
||||
mode = OrbitObjectMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = OrbitPointMode;
|
||||
mPosition = pos;
|
||||
}
|
||||
|
||||
QuatF q(rot);
|
||||
MatrixF tempMat(true);
|
||||
q.setMatrix(&tempMat);
|
||||
Point3F dir;
|
||||
tempMat.getColumn(1, &dir);
|
||||
|
||||
setPosition(mPosition, dir);
|
||||
|
||||
mMinOrbitDist = minDist;
|
||||
mMaxOrbitDist = maxDist;
|
||||
mCurOrbitDist = curDist;
|
||||
}
|
||||
|
||||
|
||||
void Camera::validateEyePoint(F32 pos, MatrixF *mat)
|
||||
{
|
||||
if (pos != 0)
|
||||
{
|
||||
// Use the eye transform to orient the camera
|
||||
Point3F dir;
|
||||
mat->getColumn(1, &dir);
|
||||
pos *= mMaxOrbitDist - mMinOrbitDist;
|
||||
|
||||
// Use the camera node's pos.
|
||||
Point3F startPos = getRenderPosition();
|
||||
Point3F endPos;
|
||||
|
||||
// Make sure we don't extend the camera into anything solid
|
||||
if(mOrbitObject)
|
||||
mOrbitObject->disableCollision();
|
||||
disableCollision();
|
||||
RayInfo collision;
|
||||
U32 mask = TerrainObjectType |
|
||||
InteriorObjectType |
|
||||
WaterObjectType |
|
||||
StaticShapeObjectType |
|
||||
PlayerObjectType |
|
||||
ItemObjectType |
|
||||
VehicleObjectType;
|
||||
|
||||
Container* pContainer = isServerObject() ? &gServerContainer : &gClientContainer;
|
||||
if (!pContainer->castRay(startPos, startPos - dir * 2.5 * pos, mask, &collision))
|
||||
endPos = startPos - dir * pos;
|
||||
else
|
||||
{
|
||||
float dot = mDot(dir, collision.normal);
|
||||
if(dot > 0.01)
|
||||
{
|
||||
float colDist = mDot(startPos - collision.point, dir) - (1 / dot) * CameraRadius;
|
||||
if(colDist > pos)
|
||||
colDist = pos;
|
||||
if(colDist < 0)
|
||||
colDist = 0;
|
||||
endPos = startPos - dir * colDist;
|
||||
}
|
||||
else
|
||||
endPos = startPos - dir * pos;
|
||||
}
|
||||
mat->setColumn(3,endPos);
|
||||
enableCollision();
|
||||
if(mOrbitObject)
|
||||
mOrbitObject->enableCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::setPosition(const Point3F& pos, const Point3F& rot, MatrixF *mat)
|
||||
{
|
||||
MatrixF xRot, zRot;
|
||||
xRot.set(EulerF(rot.x, 0, 0));
|
||||
zRot.set(EulerF(0, 0, rot.z));
|
||||
mat->mul(zRot, xRot);
|
||||
mat->setColumn(3,pos);
|
||||
mRot = rot;
|
||||
}
|
||||
|
||||
void Camera::setTransform(const MatrixF& mat)
|
||||
{
|
||||
// This method should never be called on the client.
|
||||
|
||||
// This currently converts all rotation in the mat into
|
||||
// rotations around the z and x axis.
|
||||
Point3F pos,vec;
|
||||
mat.getColumn(1,&vec);
|
||||
mat.getColumn(3,&pos);
|
||||
Point3F rot(-mAtan(vec.z, mSqrt(vec.x*vec.x + vec.y*vec.y)),0,-mAtan(-vec.x,vec.y));
|
||||
setPosition(pos,rot);
|
||||
}
|
||||
|
||||
void Camera::setRenderTransform(const MatrixF& mat)
|
||||
{
|
||||
// This method should never be called on the client.
|
||||
|
||||
// This currently converts all rotation in the mat into
|
||||
// rotations around the z and x axis.
|
||||
Point3F pos,vec;
|
||||
mat.getColumn(1,&vec);
|
||||
mat.getColumn(3,&pos);
|
||||
Point3F rot(-mAtan(vec.z, mSqrt(vec.x*vec.x + vec.y*vec.y)),0,-mAtan(-vec.x,vec.y));
|
||||
setRenderPosition(pos,rot);
|
||||
}
|
||||
|
||||
F32 Camera::getDamageFlash() const
|
||||
{
|
||||
if (mode == OrbitObjectMode && isServerObject() && bool(mOrbitObject))
|
||||
{
|
||||
const GameBase *castObj = mOrbitObject;
|
||||
const ShapeBase* psb = dynamic_cast<const ShapeBase*>(castObj);
|
||||
if (psb)
|
||||
return psb->getDamageFlash();
|
||||
}
|
||||
|
||||
return mDamageFlash;
|
||||
}
|
||||
|
||||
F32 Camera::getWhiteOut() const
|
||||
{
|
||||
if (mode == OrbitObjectMode && isServerObject() && bool(mOrbitObject))
|
||||
{
|
||||
const GameBase *castObj = mOrbitObject;
|
||||
const ShapeBase* psb = dynamic_cast<const ShapeBase*>(castObj);
|
||||
if (psb)
|
||||
return psb->getWhiteOut();
|
||||
}
|
||||
|
||||
return mWhiteOut;
|
||||
}
|
||||
|
||||
116
engine/game/camera.h
Executable file
116
engine/game/camera.h
Executable file
@@ -0,0 +1,116 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CAMERA_H_
|
||||
#define _CAMERA_H_
|
||||
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "game/shapeBase.h"
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
struct CameraData: public ShapeBaseData {
|
||||
typedef ShapeBaseData Parent;
|
||||
|
||||
//
|
||||
DECLARE_CONOBJECT(CameraData);
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Implements a basic camera object.
|
||||
class Camera: public ShapeBase
|
||||
{
|
||||
typedef ShapeBase Parent;
|
||||
|
||||
enum MaskBits {
|
||||
MoveMask = Parent::NextFreeMask,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
struct StateDelta {
|
||||
Point3F pos;
|
||||
Point3F rot;
|
||||
VectorF posVec;
|
||||
VectorF rotVec;
|
||||
};
|
||||
Point3F mRot;
|
||||
StateDelta delta;
|
||||
|
||||
static F32 mMovementSpeed;
|
||||
|
||||
void setPosition(const Point3F& pos,const Point3F& viewRot);
|
||||
void setRenderPosition(const Point3F& pos,const Point3F& viewRot);
|
||||
|
||||
SimObjectPtr<GameBase> mOrbitObject;
|
||||
F32 mMinOrbitDist;
|
||||
F32 mMaxOrbitDist;
|
||||
F32 mCurOrbitDist;
|
||||
Point3F mPosition;
|
||||
bool mObservingClientObject;
|
||||
|
||||
enum
|
||||
{
|
||||
StationaryMode = 0,
|
||||
|
||||
FreeRotateMode = 1,
|
||||
FlyMode = 2,
|
||||
OrbitObjectMode = 3,
|
||||
OrbitPointMode = 4,
|
||||
|
||||
CameraFirstMode = 0,
|
||||
CameraLastMode = 4
|
||||
};
|
||||
|
||||
int mode;
|
||||
void setPosition(const Point3F& pos,const Point3F& viewRot, MatrixF *mat);
|
||||
void setTransform(const MatrixF& mat);
|
||||
void setRenderTransform(const MatrixF& mat);
|
||||
F32 getCameraFov();
|
||||
F32 getDefaultCameraFov();
|
||||
bool isValidCameraFov(F32 fov);
|
||||
void setCameraFov(F32 fov);
|
||||
|
||||
F32 getDamageFlash() const;
|
||||
F32 getWhiteOut() const;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(Camera);
|
||||
|
||||
Camera();
|
||||
~Camera();
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
|
||||
void onEditorEnable();
|
||||
void onEditorDisable();
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void renderImage(SceneState* state, SceneRenderImage* image);
|
||||
void processTick(const Move* move);
|
||||
void interpolateTick(F32 delta);
|
||||
void getCameraTransform(F32* pos,MatrixF* mat);
|
||||
|
||||
void writePacketData(GameConnection *conn, BitStream *stream);
|
||||
void readPacketData(GameConnection *conn, BitStream *stream);
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
Point3F &getPosition();
|
||||
void setFlyMode();
|
||||
void setOrbitMode(GameBase *obj, Point3F &pos, AngAxisF &rot,
|
||||
F32 minDist, F32 maxDist, F32 curDist, bool ownClientObject);
|
||||
void validateEyePoint(F32 pos, MatrixF *mat);
|
||||
void onDeleteNotify(SimObject *obj);
|
||||
|
||||
GameBase * getOrbitObject() { return(mOrbitObject); }
|
||||
bool isObservingClientObject() { return(mObservingClientObject); }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
306
engine/game/cameraSpline.cc
Executable file
306
engine/game/cameraSpline.cc
Executable file
@@ -0,0 +1,306 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "game/cameraSpline.h"
|
||||
|
||||
#include "console/console.h"
|
||||
#include "platform/platformGL.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CameraSpline::Knot::Knot(const Knot &k)
|
||||
{
|
||||
mPosition = k.mPosition;
|
||||
mRotation = k.mRotation;
|
||||
mSpeed = k.mSpeed;
|
||||
mType = k.mType;
|
||||
mPath = k.mPath;
|
||||
prev = NULL; next = NULL;
|
||||
}
|
||||
|
||||
CameraSpline::Knot::Knot(const Point3F &p, const QuatF &r, F32 s, Knot::Type type, Knot::Path path)
|
||||
{
|
||||
mPosition = p;
|
||||
mRotation = r;
|
||||
mSpeed = s;
|
||||
mType = type;
|
||||
mPath = path;
|
||||
prev = NULL; next = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CameraSpline::CameraSpline()
|
||||
{
|
||||
mFront = NULL;
|
||||
mSize = 0;
|
||||
mIsMapDirty = true;
|
||||
VECTOR_SET_ASSOCIATION(mTimeMap);
|
||||
}
|
||||
|
||||
|
||||
CameraSpline::~CameraSpline()
|
||||
{
|
||||
removeAll();
|
||||
}
|
||||
|
||||
|
||||
void CameraSpline::push_back(Knot *w)
|
||||
{
|
||||
if (!mFront)
|
||||
{
|
||||
mFront = w;
|
||||
w->next = w;
|
||||
w->prev = w;
|
||||
}
|
||||
else
|
||||
{
|
||||
Knot *before = back();
|
||||
Knot *after = before->next;
|
||||
|
||||
w->next = before->next;
|
||||
w->prev = before;
|
||||
after->prev = w;
|
||||
before->next = w;
|
||||
}
|
||||
++mSize;
|
||||
mIsMapDirty = true;
|
||||
}
|
||||
|
||||
CameraSpline::Knot* CameraSpline::getKnot(S32 i)
|
||||
{
|
||||
Knot *k = mFront;
|
||||
while(i--)
|
||||
k = k->next;
|
||||
return k;
|
||||
}
|
||||
|
||||
CameraSpline::Knot* CameraSpline::remove(Knot *w)
|
||||
{
|
||||
if (w->next == mFront && w->prev == mFront)
|
||||
mFront = NULL;
|
||||
else
|
||||
{
|
||||
w->prev->next = w->next;
|
||||
w->next->prev = w->prev;
|
||||
if (mFront == w)
|
||||
mFront = w->next;
|
||||
}
|
||||
--mSize;
|
||||
mIsMapDirty = true;
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
void CameraSpline::removeAll()
|
||||
{
|
||||
while(front())
|
||||
delete remove(front());
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CameraSpline::buildTimeMap()
|
||||
{
|
||||
if (!mIsMapDirty)
|
||||
return;
|
||||
|
||||
mTimeMap.clear();
|
||||
mTimeMap.reserve(size()*3); // preallocate
|
||||
|
||||
// Initial node and knot value..
|
||||
TimeMap map;
|
||||
map.mTime = 0;
|
||||
map.mDistance = 0;
|
||||
mTimeMap.push_back(map);
|
||||
|
||||
Knot ka,kj,ki;
|
||||
value(0, &kj, true);
|
||||
F32 length = 0.0f;
|
||||
ka = kj;
|
||||
|
||||
// Loop through the knots and add nodes. Nodes are added for every knot and
|
||||
// whenever the spline length and segment length deviate by epsilon.
|
||||
F32 epsilon = Con::getFloatVariable("CameraSpline::epsilon", 0.90f);
|
||||
const F32 Step = 0.05f;
|
||||
F32 lt = 0,time = 0;
|
||||
do {
|
||||
if ((time += Step) > (mSize - 1))
|
||||
time = mSize - 1;
|
||||
|
||||
value(time, &ki, true);
|
||||
length += (ki.mPosition - kj.mPosition).len();
|
||||
F32 segment = (ki.mPosition - ka.mPosition).len();
|
||||
|
||||
if ((segment / length) < epsilon || time == (mSize - 1) || mFloor(lt) != mFloor(time)) {
|
||||
map.mTime = time;
|
||||
map.mDistance = length;
|
||||
mTimeMap.push_back(map);
|
||||
ka = ki;
|
||||
}
|
||||
kj = ki;
|
||||
lt = time;
|
||||
}
|
||||
while (time < mSize - 1);
|
||||
|
||||
mIsMapDirty = false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CameraSpline::renderTimeMap()
|
||||
{
|
||||
buildTimeMap();
|
||||
|
||||
glLineWidth(3);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
|
||||
MRandomLCG random(1376312589 * (U32)this);
|
||||
Knot a;
|
||||
S32 cr, cg, cb;
|
||||
for(Vector<TimeMap>::iterator itr=mTimeMap.begin(); itr != mTimeMap.end(); itr++)
|
||||
{
|
||||
value(itr->mTime, &a, true);
|
||||
|
||||
cr = random.randI(0,1);
|
||||
cg = random.randI(0,1);
|
||||
cb = random.randI(0,1);
|
||||
glColor4f(cr, cg, cb, 1);
|
||||
glVertex3f(a.mPosition.x, a.mPosition.y, a.mPosition.z);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
F32 CameraSpline::advanceTime(F32 t, S32 delta_ms)
|
||||
{
|
||||
buildTimeMap();
|
||||
Knot k;
|
||||
value(t, &k, false);
|
||||
F32 dist = getDistance(t) + k.mSpeed * (F32(delta_ms) / 1000.0f);
|
||||
return getTime(dist);
|
||||
}
|
||||
|
||||
|
||||
F32 CameraSpline::advanceDist(F32 t, F32 meters)
|
||||
{
|
||||
buildTimeMap();
|
||||
F32 dist = getDistance(t) + meters;
|
||||
return getTime(dist);
|
||||
}
|
||||
|
||||
|
||||
F32 CameraSpline::getDistance(F32 t)
|
||||
{
|
||||
if (mSize <= 1)
|
||||
return 0;
|
||||
|
||||
// Find the nodes spanning the time
|
||||
Vector<TimeMap>::iterator end = mTimeMap.begin() + 1, start;
|
||||
for (; end < (mTimeMap.end() - 1) && end->mTime < t; end++) { }
|
||||
start = end - 1;
|
||||
|
||||
// Interpolate between the two nodes
|
||||
F32 i = (t - start->mTime) / (end->mTime - start->mTime);
|
||||
return start->mDistance + (end->mDistance - start->mDistance) * i;
|
||||
}
|
||||
|
||||
|
||||
F32 CameraSpline::getTime(F32 d)
|
||||
{
|
||||
if (mSize <= 1)
|
||||
return 0;
|
||||
|
||||
// Find nodes spanning the distance
|
||||
Vector<TimeMap>::iterator end = mTimeMap.begin() + 1, start;
|
||||
for (; end < (mTimeMap.end() - 1) && end->mDistance < d; end++) { }
|
||||
start = end - 1;
|
||||
|
||||
// Check for duplicate points..
|
||||
F32 seg = end->mDistance - start->mDistance;
|
||||
if (!seg)
|
||||
return end->mTime;
|
||||
|
||||
// Interpolate between the two nodes
|
||||
F32 i = (d - start->mDistance) / (end->mDistance - start->mDistance);
|
||||
return start->mTime + (end->mTime - start->mTime) * i;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CameraSpline::value(F32 t, CameraSpline::Knot *result, bool skip_rotation)
|
||||
{
|
||||
// Verify that t is in range [0 >= t > size]
|
||||
AssertFatal(t >= 0 && t < size(), "t out of range");
|
||||
Knot *p1 = getKnot(mFloor(t));
|
||||
Knot *p2 = next(p1);
|
||||
|
||||
F32 i = t - mFloor(t); // adjust t to 0 to 1 on p1-p2 interval
|
||||
if (p1->mPath == Knot::SPLINE)
|
||||
{
|
||||
Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1);
|
||||
Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2);
|
||||
result->mPosition.x = mCatmullrom(i, p0->mPosition.x, p1->mPosition.x, p2->mPosition.x, p3->mPosition.x);
|
||||
result->mPosition.y = mCatmullrom(i, p0->mPosition.y, p1->mPosition.y, p2->mPosition.y, p3->mPosition.y);
|
||||
result->mPosition.z = mCatmullrom(i, p0->mPosition.z, p1->mPosition.z, p2->mPosition.z, p3->mPosition.z);
|
||||
}
|
||||
else
|
||||
{ // Linear
|
||||
result->mPosition.interpolate(p1->mPosition, p2->mPosition, i);
|
||||
}
|
||||
|
||||
if (skip_rotation)
|
||||
return;
|
||||
|
||||
buildTimeMap();
|
||||
|
||||
// find the two knots to interpolate rotation and velocity through since some
|
||||
// knots are only positional
|
||||
S32 start = mFloor(t);
|
||||
S32 end = (p2 == p1) ? start : (start + 1);
|
||||
while (p1->mType == Knot::POSITION_ONLY && p1 != front())
|
||||
{
|
||||
p1 = prev(p1);
|
||||
start--;
|
||||
}
|
||||
|
||||
while (p2->mType == Knot::POSITION_ONLY && p2 != back())
|
||||
{
|
||||
p2 = next(p2);
|
||||
end++;
|
||||
}
|
||||
|
||||
if (start == end) {
|
||||
result->mRotation = p1->mRotation;
|
||||
result->mSpeed = p1->mSpeed;
|
||||
}
|
||||
else {
|
||||
F32 c = getDistance(t);
|
||||
F32 d1 = getDistance(start);
|
||||
F32 d2 = getDistance(end);
|
||||
if (d1 == d2) {
|
||||
result->mRotation = p2->mRotation;
|
||||
result->mSpeed = p2->mSpeed;
|
||||
}
|
||||
else {
|
||||
i = (c-d1)/(d2-d1);
|
||||
result->mRotation.interpolate(p1->mRotation, p2->mRotation, i);
|
||||
result->mSpeed = (p1->mSpeed * (1.0f-i)) + (p2->mSpeed * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
91
engine/game/cameraSpline.h
Executable file
91
engine/game/cameraSpline.h
Executable file
@@ -0,0 +1,91 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _CAMERASPLINE_H_
|
||||
#define _CAMERASPLINE_H_
|
||||
|
||||
#include "math/mMath.h"
|
||||
#include "core/tVector.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class CameraSpline
|
||||
{
|
||||
public:
|
||||
struct Knot
|
||||
{
|
||||
public:
|
||||
Point3F mPosition;
|
||||
QuatF mRotation;
|
||||
F32 mSpeed; /// in meters per second
|
||||
enum Type {
|
||||
NORMAL,
|
||||
POSITION_ONLY,
|
||||
KINK,
|
||||
NUM_TYPE_BITS = 2
|
||||
}mType;
|
||||
|
||||
enum Path {
|
||||
LINEAR,
|
||||
SPLINE,
|
||||
NUM_PATH_BITS = 1
|
||||
}mPath;
|
||||
|
||||
F32 mDistance;
|
||||
Knot *prev;
|
||||
Knot *next;
|
||||
|
||||
Knot::Knot() {};
|
||||
Knot::Knot(const Knot &k);
|
||||
Knot::Knot(const Point3F &p, const QuatF &r, F32 s, Knot::Type type = NORMAL, Knot::Path path = SPLINE);
|
||||
};
|
||||
|
||||
|
||||
CameraSpline();
|
||||
~CameraSpline();
|
||||
|
||||
bool isEmpty() { return (mFront == NULL); }
|
||||
S32 size() { return mSize; }
|
||||
Knot* remove(Knot *w);
|
||||
void removeAll();
|
||||
|
||||
Knot* front() { return mFront; }
|
||||
Knot* back() { return (mFront == NULL) ? NULL : mFront->prev; }
|
||||
|
||||
void push_back(Knot *w);
|
||||
void push_front(Knot *w) { push_back(w); mFront = w; mIsMapDirty = true; }
|
||||
|
||||
Knot* getKnot(S32 i);
|
||||
Knot* next(Knot *k) { return (k->next == mFront) ? k : k->next; }
|
||||
Knot* prev(Knot *k) { return (k == mFront) ? k : k->prev; }
|
||||
|
||||
F32 advanceTime(F32 t, S32 delta_ms);
|
||||
F32 advanceDist(F32 t, F32 meters);
|
||||
void value(F32 t, Knot *result, bool skip_rotation=false);
|
||||
|
||||
F32 getDistance(F32 t);
|
||||
F32 getTime(F32 d);
|
||||
|
||||
void renderTimeMap();
|
||||
|
||||
|
||||
private:
|
||||
Knot *mFront;
|
||||
S32 mSize;
|
||||
bool mIsMapDirty;
|
||||
|
||||
struct TimeMap {
|
||||
F32 mTime;
|
||||
F32 mDistance;
|
||||
};
|
||||
|
||||
Vector<TimeMap> mTimeMap;
|
||||
void buildTimeMap();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
214
engine/game/collisionTest.cc
Executable file
214
engine/game/collisionTest.cc
Executable file
@@ -0,0 +1,214 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "game/game.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/console.h"
|
||||
#include "game/collisionTest.h"
|
||||
|
||||
static F32 BoxSize = 2;
|
||||
|
||||
bool CollisionTest::testPolytope = false;
|
||||
bool CollisionTest::testClippedPolyList = false;
|
||||
bool CollisionTest::testDepthSortList = false;
|
||||
bool CollisionTest::testExtrudedPolyList = false;
|
||||
bool CollisionTest::depthSort = false;
|
||||
bool CollisionTest::depthRender = false;
|
||||
bool CollisionTest::renderAlways = false;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
CollisionTest::CollisionTest()
|
||||
{
|
||||
}
|
||||
|
||||
CollisionTest::~CollisionTest()
|
||||
{
|
||||
}
|
||||
|
||||
void CollisionTest::consoleInit()
|
||||
{
|
||||
Con::addVariable("Collision::boxSize",TypeF32,&BoxSize);
|
||||
|
||||
Con::addVariable("Collision::testPolytope",TypeBool,&testPolytope);
|
||||
Con::addVariable("Collision::testClippedPolyList",TypeBool,&testClippedPolyList);
|
||||
Con::addVariable("Collision::testExtrudedPolyList",TypeBool,&testExtrudedPolyList);
|
||||
Con::addVariable("Collision::testDepthSortList",TypeBool,&testDepthSortList);
|
||||
|
||||
Con::addVariable("Collision::depthSort",TypeBool,&depthSort);
|
||||
Con::addVariable("Collision::depthRender",TypeBool,&depthRender);
|
||||
Con::addVariable("Collision::renderAlways",TypeBool,&renderAlways);
|
||||
}
|
||||
|
||||
void CollisionTest::collide(const MatrixF& transform)
|
||||
{
|
||||
//
|
||||
Point3F pos;
|
||||
transform.getColumn(3,&pos);
|
||||
boundingBox.min = pos - Point3F(BoxSize,BoxSize,BoxSize);
|
||||
boundingBox.max = pos + Point3F(BoxSize,BoxSize,BoxSize);
|
||||
boundingSphere.center = pos;
|
||||
boundingSphere.radius = BoxSize * 1.5;
|
||||
|
||||
if (testPolytope) {
|
||||
MatrixF imat(true);
|
||||
volume.buildBox(imat,boundingBox);
|
||||
tree.clear();
|
||||
}
|
||||
|
||||
if (testClippedPolyList) {
|
||||
polyList.clear();
|
||||
polyList.mPlaneList.clear();
|
||||
polyList.mNormal.set(0,0,0);
|
||||
|
||||
// Planes bounding the square.
|
||||
polyList.mPlaneList.setSize(6);
|
||||
polyList.mPlaneList[0].set(boundingBox.min,VectorF(-1,0,0));
|
||||
polyList.mPlaneList[1].set(boundingBox.max,VectorF(0,1,0));
|
||||
polyList.mPlaneList[2].set(boundingBox.max,VectorF(1,0,0));
|
||||
polyList.mPlaneList[3].set(boundingBox.min,VectorF(0,-1,0));
|
||||
polyList.mPlaneList[4].set(boundingBox.min,VectorF(0,0,-1));
|
||||
polyList.mPlaneList[5].set(boundingBox.max,VectorF(0,0,1));
|
||||
}
|
||||
|
||||
if (testDepthSortList) {
|
||||
depthSortList.clear();
|
||||
mDepthSortExtent.set(5,20,5); // hard-code for now
|
||||
MatrixF mat = transform;
|
||||
mat.inverse(); // we want world to camera (or whatever transform represents)
|
||||
depthSortList.set(mat,mDepthSortExtent);
|
||||
|
||||
// we use a different box and sphere...
|
||||
// our box starts at the camera and goes forward mDepthSortExtent.y
|
||||
// with width and height of mDepthSortExtent.x and mDepthSortExtent.z
|
||||
Point3F x,y,z,p;
|
||||
transform.getColumn(0,&x);
|
||||
transform.getColumn(1,&y);
|
||||
transform.getColumn(2,&z);
|
||||
transform.getColumn(3,&p);
|
||||
x *= 0.5f * mDepthSortExtent.x;
|
||||
y *= mDepthSortExtent.y;
|
||||
z *= 0.5f * mDepthSortExtent.z;
|
||||
Point3F boxMin = p;
|
||||
Point3F boxMax = p;
|
||||
boxMin.setMin(p-x-z);
|
||||
boxMin.setMin(p-x+z);
|
||||
boxMin.setMin(p+x-z);
|
||||
boxMin.setMin(p+x+z);
|
||||
boxMin.setMin(p-x-z+y);
|
||||
boxMin.setMin(p-x+z+y);
|
||||
boxMin.setMin(p+x-z+y);
|
||||
boxMin.setMin(p+x+z+y);
|
||||
|
||||
boxMax.setMax(p-x-z);
|
||||
boxMax.setMax(p-x+z);
|
||||
boxMax.setMax(p+x-z);
|
||||
boxMax.setMax(p+x+z);
|
||||
boxMax.setMax(p-x-z+y);
|
||||
boxMax.setMax(p-x+z+y);
|
||||
boxMax.setMax(p+x-z+y);
|
||||
boxMax.setMax(p+x+z+y);
|
||||
|
||||
Point3F boxCenter = boxMin + boxMax;
|
||||
boxCenter *= 0.5f;
|
||||
F32 boxRadius = (boxMax-boxMin).len();
|
||||
|
||||
mDepthBox.min = boxMin;
|
||||
mDepthBox.max = boxMax;
|
||||
mDepthSphere.center = boxCenter;
|
||||
mDepthSphere.radius = boxRadius;
|
||||
}
|
||||
|
||||
if (testExtrudedPolyList) {
|
||||
MatrixF imat(1);
|
||||
polyhedron.buildBox(imat,boundingBox);
|
||||
VectorF v1(0,3,0);
|
||||
transform.mulV(v1,&extrudeVector);
|
||||
extrudedList.extrude(polyhedron,extrudeVector);
|
||||
extrudedList.setVelocity(extrudeVector);
|
||||
extrudedList.setCollisionList(&collisionList);
|
||||
|
||||
Point3F p1 = pos + extrudeVector;
|
||||
boundingBox.min = boundingBox.max = pos;
|
||||
boundingBox.min.setMin(p1);
|
||||
boundingBox.max.setMax(p1);
|
||||
boundingBox.min -= Point3F(BoxSize,BoxSize,BoxSize);
|
||||
boundingBox.max += Point3F(BoxSize,BoxSize,BoxSize);
|
||||
boundingSphere.radius += extrudeVector.len();
|
||||
}
|
||||
|
||||
if (testPolytope || testClippedPolyList || testExtrudedPolyList || testDepthSortList) {
|
||||
testPos = boundingSphere.center;
|
||||
gClientContainer.findObjects(0xFFFFFFFF,CollisionTest::callback,this);
|
||||
}
|
||||
|
||||
if (testExtrudedPolyList) {
|
||||
extrudedList.adjustCollisionTime();
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionTest::callback(SceneObject* obj, void *thisPtr)
|
||||
{
|
||||
CollisionTest* ptr = reinterpret_cast<CollisionTest*>(thisPtr);
|
||||
|
||||
if (testPolytope) {
|
||||
if (BSPNode* root = obj->buildCollisionBSP(&ptr->tree,ptr->boundingBox,ptr->boundingSphere))
|
||||
ptr->volume.intersect(obj,root);
|
||||
}
|
||||
if (testClippedPolyList) {
|
||||
obj->buildPolyList(&ptr->polyList,ptr->boundingBox,ptr->boundingSphere);
|
||||
}
|
||||
if (testExtrudedPolyList) {
|
||||
obj->buildPolyList(&ptr->extrudedList,ptr->boundingBox,ptr->boundingSphere);
|
||||
}
|
||||
if (testDepthSortList) {
|
||||
obj->buildPolyList(&ptr->depthSortList,ptr->mDepthBox,ptr->mDepthSphere);
|
||||
}
|
||||
}
|
||||
|
||||
extern void wireCube(F32 size,Point3F pos);
|
||||
|
||||
void CollisionTest::render()
|
||||
{
|
||||
bool collision = false;
|
||||
if (testPolytope || renderAlways) {
|
||||
if (volume.didIntersect())
|
||||
volume.render();
|
||||
}
|
||||
if (testClippedPolyList || renderAlways) {
|
||||
if (polyList.mPolyList.size())
|
||||
collision = true;
|
||||
polyList.render();
|
||||
}
|
||||
if (testExtrudedPolyList || renderAlways) {
|
||||
if (collisionList.count)
|
||||
collision = true;
|
||||
extrudedList.render();
|
||||
glPushAttrib(GL_DEPTH_BUFFER_BIT);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
polyhedron.render(extrudeVector,collisionList.t);
|
||||
glPopAttrib();
|
||||
}
|
||||
if (testDepthSortList || renderAlways) {
|
||||
if (depthSort && testDepthSortList)
|
||||
depthSortList.sort();
|
||||
// should we write depth values of polys...
|
||||
// if polys are correctly sorted then writing depth values
|
||||
// should result in no overlap of polys when looking down
|
||||
// from camera...otoh, if polys are out of order, we should
|
||||
// see overlap
|
||||
DepthSortList::renderWithDepth = depthRender;
|
||||
depthSortList.render();
|
||||
}
|
||||
|
||||
if (collision || renderAlways)
|
||||
wireCube(BoxSize,testPos);
|
||||
}
|
||||
|
||||
91
engine/game/collisionTest.h
Executable file
91
engine/game/collisionTest.h
Executable file
@@ -0,0 +1,91 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLISIONTEST_H_
|
||||
#define _COLLISIONTEST_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
|
||||
#ifndef _POLYTOPE_H_
|
||||
#include "collision/polytope.h"
|
||||
#endif
|
||||
#ifndef _CLIPPEDPOLYLIST_H_
|
||||
#include "collision/clippedPolyList.h"
|
||||
#endif
|
||||
#ifndef _EXTRUDEDPOLYLIST_H_
|
||||
#include "collision/extrudedPolyList.h"
|
||||
#endif
|
||||
#ifndef _DEPTHSORTLIST_H_
|
||||
#include "collision/depthSortList.h"
|
||||
#endif
|
||||
#ifndef _POLYHEDRON_H_
|
||||
#include "collision/polyhedron.h"
|
||||
#endif
|
||||
|
||||
/// Helper class for collision detection.
|
||||
struct CollisionTest
|
||||
{
|
||||
/// @name Basic Settings
|
||||
/// @{
|
||||
Box3F boundingBox;
|
||||
SphereF boundingSphere;
|
||||
static bool renderAlways;
|
||||
|
||||
Point3F testPos;
|
||||
/// @}
|
||||
|
||||
/// @name Depth Sort List
|
||||
/// Use a slightly different box and sphere for depthSortList.
|
||||
/// @{
|
||||
Box3F mDepthBox;
|
||||
SphereF mDepthSphere;
|
||||
Point3F mDepthSortExtent;
|
||||
/// @}
|
||||
|
||||
/// @name Polytope/BSP test
|
||||
/// @{
|
||||
static bool testPolytope;
|
||||
BSPTree tree;
|
||||
Polytope volume;
|
||||
/// @}
|
||||
|
||||
/// @name Clipped polylists
|
||||
/// @{
|
||||
static bool testClippedPolyList;
|
||||
ClippedPolyList polyList;
|
||||
/// @}
|
||||
|
||||
/// @name Depth sorted polylists
|
||||
/// @{
|
||||
static bool testDepthSortList;
|
||||
static bool depthSort;
|
||||
static bool depthRender;
|
||||
DepthSortList depthSortList;
|
||||
/// @}
|
||||
|
||||
/// @name Extruded
|
||||
/// @{
|
||||
CollisionList collisionList;
|
||||
static bool testExtrudedPolyList;
|
||||
Polyhedron polyhedron;
|
||||
VectorF extrudeVector;
|
||||
ExtrudedPolyList extrudedList;
|
||||
/// @}
|
||||
|
||||
/// @name Implementation
|
||||
/// @{
|
||||
CollisionTest();
|
||||
~CollisionTest();
|
||||
static void consoleInit();
|
||||
static void callback(SceneObject*, void *thisPtr);
|
||||
void collide(const MatrixF& transform);
|
||||
void render();
|
||||
/// @}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
954
engine/game/debris.cc
Executable file
954
engine/game/debris.cc
Executable file
@@ -0,0 +1,954 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/debris.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "math/mathUtils.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "sceneGraph/sceneState.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "sceneGraph/detailManager.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "ts/tsPartInstance.h"
|
||||
#include "game/fx/particleEngine.h"
|
||||
#include "game/fx/explosion.h"
|
||||
|
||||
const U32 csmStaticCollisionMask = TerrainObjectType |
|
||||
InteriorObjectType;
|
||||
|
||||
const U32 csmDynamicCollisionMask = StaticShapeObjectType;
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Debris Data
|
||||
//**************************************************************************
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(DebrisData);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//--------------------------------------------------------------------------
|
||||
DebrisData::DebrisData()
|
||||
{
|
||||
dMemset( emitterList, 0, sizeof( emitterList ) );
|
||||
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
|
||||
|
||||
explosion = NULL;
|
||||
explosionId = 0;
|
||||
|
||||
velocity = 0.0;
|
||||
velocityVariance = 0.0;
|
||||
elasticity = 0.3;
|
||||
friction = 0.2;
|
||||
numBounces = 0;
|
||||
bounceVariance = 0;
|
||||
minSpinSpeed = maxSpinSpeed = 0.0;
|
||||
render2D = false;
|
||||
staticOnMaxBounce = false;
|
||||
explodeOnMaxBounce = false;
|
||||
snapOnMaxBounce = false;
|
||||
lifetime = 3.0;
|
||||
lifetimeVariance = 0.0;
|
||||
minSpinSpeed = 0.0;
|
||||
maxSpinSpeed = 0.0;
|
||||
textureName = NULL;
|
||||
mTypeMask |= DebrisObjectType;
|
||||
shapeName = NULL;
|
||||
fade = true;
|
||||
useRadiusMass = false;
|
||||
baseRadius = 1.0;
|
||||
gravModifier = 1.0;
|
||||
terminalVelocity = 0.0;
|
||||
ignoreWater = true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize - Check data
|
||||
//--------------------------------------------------------------------------
|
||||
bool DebrisData::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
for( int i=0; i<DDC_NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( !emitterList[i] && emitterIDList[i] != 0 )
|
||||
{
|
||||
if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
|
||||
{
|
||||
Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(emitter): 0x%x", emitterIDList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!explosion && explosionId != 0)
|
||||
{
|
||||
if (!Sim::findObject( SimObjectId( explosionId ), explosion ))
|
||||
Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", explosionId);
|
||||
}
|
||||
|
||||
if( textureName )
|
||||
{
|
||||
texture = TextureHandle( textureName, MeshTexture );
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = TextureHandle(); // set default NULL tex
|
||||
}
|
||||
|
||||
|
||||
// validate data
|
||||
|
||||
if( velocityVariance > velocity )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName());
|
||||
velocityVariance = velocity;
|
||||
}
|
||||
if( friction < -10.0 || friction > 10.0 )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName());
|
||||
friction = 0.2;
|
||||
}
|
||||
if( elasticity < -10.0 || elasticity > 10.0 )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName());
|
||||
elasticity = 0.2;
|
||||
}
|
||||
if( lifetime < 0.0 || lifetime > 1000.0 )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName());
|
||||
lifetime = 3.0;
|
||||
}
|
||||
if( lifetimeVariance < 0.0 || lifetimeVariance > lifetime )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName());
|
||||
lifetimeVariance = 0.0;
|
||||
}
|
||||
if( numBounces < 0 || numBounces > 10000 )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: numBounces invalid", getName());
|
||||
numBounces = 3;
|
||||
}
|
||||
if( bounceVariance < 0 || bounceVariance > numBounces )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: bounceVariance invalid", getName());
|
||||
bounceVariance = 0;
|
||||
}
|
||||
if( minSpinSpeed < -10000.0 || minSpinSpeed > 10000.0 || minSpinSpeed > maxSpinSpeed )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName());
|
||||
minSpinSpeed = maxSpinSpeed - 1.0;
|
||||
}
|
||||
if( maxSpinSpeed < -10000.0 || maxSpinSpeed > 10000.0 )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName());
|
||||
maxSpinSpeed = 0.0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Preload
|
||||
//--------------------------------------------------------------------------
|
||||
bool DebrisData::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if (Parent::preload(server, errorBuffer) == false)
|
||||
return false;
|
||||
|
||||
if( server )
|
||||
return true;
|
||||
|
||||
if( shapeName && shapeName[0] != '\0' && !bool(shape) )
|
||||
{
|
||||
shape = ResourceManager->load(shapeName);
|
||||
if( bool(shape) == false )
|
||||
{
|
||||
dSprintf(errorBuffer, sizeof(errorBuffer), "DebrisData::load: Couldn't load shape \"%s\"", shapeName);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
|
||||
delete pDummy;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize console fields (static)
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(DebrisData)
|
||||
IMPLEMENT_SETDATATYPE(DebrisData)
|
||||
IMPLEMENT_GETDATATYPE(DebrisData)
|
||||
|
||||
void DebrisData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Display");
|
||||
addField("texture", TypeFilename, Offset(textureName, DebrisData));
|
||||
addField("shapeFile", TypeFilename, Offset(shapeName, DebrisData));
|
||||
addField("render2D", TypeBool, Offset(render2D, DebrisData));
|
||||
endGroup("Display");
|
||||
|
||||
addGroup("Datablocks");
|
||||
addField("emitters", TypeParticleEmitterDataPtr, Offset(emitterList, DebrisData), DDC_NUM_EMITTERS);
|
||||
addField("explosion", TypeExplosionDataPtr, Offset(explosion, DebrisData));
|
||||
endGroup("Datablocks");
|
||||
|
||||
addGroup("Physical Properties");
|
||||
addField("elasticity", TypeF32, Offset(elasticity, DebrisData));
|
||||
addField("friction", TypeF32, Offset(friction, DebrisData));
|
||||
addField("numBounces", TypeS32, Offset(numBounces, DebrisData));
|
||||
addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData));
|
||||
addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData));
|
||||
addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData));
|
||||
addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData));
|
||||
addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData));
|
||||
addField("velocity", TypeF32, Offset(velocity, DebrisData));
|
||||
addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData));
|
||||
addField("lifetime", TypeF32, Offset(lifetime, DebrisData));
|
||||
addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData));
|
||||
addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData));
|
||||
addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData));
|
||||
endGroup("Physical Properties");
|
||||
|
||||
addGroup("Behavior");
|
||||
addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData));
|
||||
addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData));
|
||||
addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData));
|
||||
addField("fade", TypeBool, Offset(fade, DebrisData));
|
||||
addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData));
|
||||
endGroup("Behavior");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Pack data
|
||||
//--------------------------------------------------------------------------
|
||||
void DebrisData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
stream->write(elasticity);
|
||||
stream->write(friction);
|
||||
stream->write(numBounces);
|
||||
stream->write(bounceVariance);
|
||||
stream->write(minSpinSpeed);
|
||||
stream->write(maxSpinSpeed);
|
||||
stream->write(render2D);
|
||||
stream->write(explodeOnMaxBounce);
|
||||
stream->write(staticOnMaxBounce);
|
||||
stream->write(snapOnMaxBounce);
|
||||
stream->write(lifetime);
|
||||
stream->write(lifetimeVariance);
|
||||
stream->write(minSpinSpeed);
|
||||
stream->write(maxSpinSpeed);
|
||||
stream->write(velocity);
|
||||
stream->write(velocityVariance);
|
||||
stream->write(fade);
|
||||
stream->write(useRadiusMass);
|
||||
stream->write(baseRadius);
|
||||
stream->write(gravModifier);
|
||||
stream->write(terminalVelocity);
|
||||
stream->write(ignoreWater);
|
||||
|
||||
stream->writeString( textureName );
|
||||
stream->writeString( shapeName );
|
||||
|
||||
for( int i=0; i<DDC_NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( stream->writeFlag( emitterList[i] != NULL ) )
|
||||
{
|
||||
stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
||||
}
|
||||
}
|
||||
|
||||
if( stream->writeFlag( explosion ) )
|
||||
{
|
||||
stream->writeRangedU32(packed? SimObjectId(explosion):
|
||||
explosion->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Unpack data
|
||||
//--------------------------------------------------------------------------
|
||||
void DebrisData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
stream->read(&elasticity);
|
||||
stream->read(&friction);
|
||||
stream->read(&numBounces);
|
||||
stream->read(&bounceVariance);
|
||||
stream->read(&minSpinSpeed);
|
||||
stream->read(&maxSpinSpeed);
|
||||
stream->read(&render2D);
|
||||
stream->read(&explodeOnMaxBounce);
|
||||
stream->read(&staticOnMaxBounce);
|
||||
stream->read(&snapOnMaxBounce);
|
||||
stream->read(&lifetime);
|
||||
stream->read(&lifetimeVariance);
|
||||
stream->read(&minSpinSpeed);
|
||||
stream->read(&maxSpinSpeed);
|
||||
stream->read(&velocity);
|
||||
stream->read(&velocityVariance);
|
||||
stream->read(&fade);
|
||||
stream->read(&useRadiusMass);
|
||||
stream->read(&baseRadius);
|
||||
stream->read(&gravModifier);
|
||||
stream->read(&terminalVelocity);
|
||||
stream->read(&ignoreWater);
|
||||
|
||||
textureName = stream->readSTString();
|
||||
shapeName = stream->readSTString();
|
||||
|
||||
for( int i=0; i<DDC_NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( stream->readFlag() )
|
||||
{
|
||||
emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
||||
}
|
||||
}
|
||||
|
||||
if(stream->readFlag())
|
||||
{
|
||||
explosionId = (S32)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
||||
}
|
||||
else
|
||||
{
|
||||
explosionId = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Debris
|
||||
//**************************************************************************
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Debris);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Initialize debris piece
|
||||
//----------------------------------------------------------------------------
|
||||
ConsoleMethod( Debris, init, bool, 4, 4, "(Point3F position, Point3F velocity)"
|
||||
"Set this piece of debris at the given position with the given velocity.")
|
||||
{
|
||||
Point3F pos;
|
||||
dSscanf( argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z );
|
||||
|
||||
Point3F vel;
|
||||
dSscanf( argv[3], "%g %g %g", &vel.x, &vel.y, &vel.z );
|
||||
|
||||
object->init( pos, vel );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//----------------------------------------------------------------------------
|
||||
Debris::Debris()
|
||||
{
|
||||
mTypeMask |= DebrisObjectType;
|
||||
|
||||
mVelocity = Point3F( 0.0, 0.0, 4.0 );
|
||||
mLifetime = gRandGen.randF( 1.0, 10.0 );
|
||||
mLastPos = getPosition();
|
||||
mNumBounces = gRandGen.randI( 0, 1 );
|
||||
mSize = 2.0;
|
||||
mElapsedTime = 0.0;
|
||||
mShape = NULL;
|
||||
mPart = NULL;
|
||||
mXRotSpeed = 0.0;
|
||||
mZRotSpeed = 0.0;
|
||||
mInitialTrans.identity();
|
||||
mRadius = 0.2;
|
||||
mStatic = false;
|
||||
|
||||
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Destructor
|
||||
//----------------------------------------------------------------------------
|
||||
Debris::~Debris()
|
||||
{
|
||||
if( mShape )
|
||||
{
|
||||
delete mShape;
|
||||
mShape = NULL;
|
||||
}
|
||||
|
||||
if( mPart )
|
||||
{
|
||||
delete mPart;
|
||||
mPart = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Init class (static)
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::initPersistFields()
|
||||
{
|
||||
addGroup("Misc");
|
||||
addField("lifetime", TypeF32, Offset(mLifetime, Debris));
|
||||
endGroup("Misc");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Init
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::init( const Point3F &position, const Point3F &velocity )
|
||||
{
|
||||
setPosition( position );
|
||||
setVelocity( velocity );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// On new data block
|
||||
//----------------------------------------------------------------------------
|
||||
bool Debris::onNewDataBlock( GameBaseData* dptr )
|
||||
{
|
||||
mDataBlock = dynamic_cast< DebrisData* >( dptr );
|
||||
if( !mDataBlock || !Parent::onNewDataBlock( dptr ) )
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// On Add
|
||||
//----------------------------------------------------------------------------
|
||||
bool Debris::onAdd()
|
||||
{
|
||||
// first check if we have a server connection, if we dont then this is on the server
|
||||
// and we should exit, then check if the parent fails to add the object
|
||||
NetConnection* conn = NetConnection::getConnectionToServer();
|
||||
if(!conn || !Parent::onAdd())
|
||||
return false;
|
||||
|
||||
F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance;
|
||||
mLifetime = mDataBlock->lifetime + lifeVar;
|
||||
|
||||
// create emitters
|
||||
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mDataBlock->emitterList[i] != NULL )
|
||||
{
|
||||
ParticleEmitter * pEmitter = new ParticleEmitter;
|
||||
pEmitter->onNewDataBlock( mDataBlock->emitterList[i] );
|
||||
if( !pEmitter->registerObject() )
|
||||
{
|
||||
Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
|
||||
delete pEmitter;
|
||||
pEmitter = NULL;
|
||||
}
|
||||
mEmitterList[i] = pEmitter;
|
||||
}
|
||||
}
|
||||
|
||||
// set particle sizes based on debris size
|
||||
F32 sizeList[ParticleEngine::PC_SIZE_KEYS];
|
||||
|
||||
if( mEmitterList[0] )
|
||||
{
|
||||
sizeList[0] = mSize * 0.5;
|
||||
sizeList[1] = mSize;
|
||||
sizeList[2] = mSize * 1.5;
|
||||
|
||||
mEmitterList[0]->setSizes( sizeList );
|
||||
}
|
||||
|
||||
if( mEmitterList[1] )
|
||||
{
|
||||
sizeList[0] = 0.0;
|
||||
sizeList[1] = mSize * 0.5;
|
||||
sizeList[2] = mSize;
|
||||
|
||||
mEmitterList[1]->setSizes( sizeList );
|
||||
}
|
||||
|
||||
S32 bounceVar = gRandGen.randI( -mDataBlock->bounceVariance, mDataBlock->bounceVariance );
|
||||
mNumBounces = mDataBlock->numBounces + bounceVar;
|
||||
|
||||
F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
|
||||
F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
|
||||
zRotSpeed *= gRandGen.randF( 0.1, 0.5 );
|
||||
|
||||
mRotAngles.set( xRotSpeed, 0.0, zRotSpeed );
|
||||
|
||||
mElasticity = mDataBlock->elasticity;
|
||||
mFriction = mDataBlock->friction;
|
||||
|
||||
// Setup our bounding box
|
||||
if( mDataBlock->shape )
|
||||
{
|
||||
mObjBox = mDataBlock->shape->bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
|
||||
}
|
||||
|
||||
if( mDataBlock->shape )
|
||||
{
|
||||
mShape = new TSShapeInstance( mDataBlock->shape, true);
|
||||
}
|
||||
|
||||
if( mPart )
|
||||
{
|
||||
// use half radius becuase we want debris to stick in ground
|
||||
mRadius = mPart->getRadius() * 0.5;
|
||||
mObjBox = mPart->getBounds();
|
||||
}
|
||||
|
||||
resetWorldBox();
|
||||
|
||||
mInitialTrans = getTransform();
|
||||
|
||||
if( mDataBlock->velocity != 0.0 )
|
||||
{
|
||||
F32 velocity = mDataBlock->velocity + gRandGen.randF( -mDataBlock->velocityVariance, mDataBlock->velocityVariance );
|
||||
|
||||
mVelocity.normalizeSafe();
|
||||
mVelocity *= velocity;
|
||||
}
|
||||
|
||||
// mass calculations
|
||||
if( mDataBlock->useRadiusMass )
|
||||
{
|
||||
if( mRadius < mDataBlock->baseRadius )
|
||||
mRadius = mDataBlock->baseRadius;
|
||||
|
||||
// linear falloff
|
||||
F32 multFactor = mDataBlock->baseRadius / mRadius;
|
||||
|
||||
mElasticity *= multFactor;
|
||||
mFriction *= multFactor;
|
||||
mRotAngles *= multFactor;
|
||||
}
|
||||
|
||||
// tell engine the debris exists
|
||||
gClientContainer.addObject(this);
|
||||
gClientSceneGraph->addObjectToScene(this);
|
||||
|
||||
removeFromProcessList();
|
||||
gClientProcessList.addObject(this);
|
||||
|
||||
conn->addObject(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// On Remove
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::onRemove()
|
||||
{
|
||||
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mEmitterList[i] )
|
||||
{
|
||||
mEmitterList[i]->deleteWhenEmpty();
|
||||
mEmitterList[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if( mPart )
|
||||
{
|
||||
TSShapeInstance *ss = mPart->getSourceShapeInstance();
|
||||
if( ss )
|
||||
{
|
||||
ss->decDebrisRefCount();
|
||||
if( ss->getDebrisRefCount() == 0 )
|
||||
{
|
||||
delete ss;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSceneManager->removeObjectFromScene(this);
|
||||
getContainer()->removeObject(this);
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Process tick
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::processTick(const Move*)
|
||||
{
|
||||
// Update real position info.
|
||||
setTransform(getRenderTransform());
|
||||
|
||||
if (mLifetime <= 0.0)
|
||||
deleteObject();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Advance Time
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::advanceTime( F32 dt )
|
||||
{
|
||||
mElapsedTime += dt;
|
||||
|
||||
mLifetime -= dt;
|
||||
if( mLifetime <= 0.0 )
|
||||
{
|
||||
mLifetime = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
mLastPos = getRenderPosition();
|
||||
|
||||
if( !mStatic )
|
||||
{
|
||||
rotate( dt );
|
||||
|
||||
Point3F nextPos = getRenderPosition();
|
||||
computeNewState( nextPos, mVelocity, dt );
|
||||
|
||||
if( bounce( nextPos, dt ) )
|
||||
{
|
||||
--mNumBounces;
|
||||
if( mNumBounces <= 0 )
|
||||
{
|
||||
if( mDataBlock->explodeOnMaxBounce )
|
||||
{
|
||||
explode();
|
||||
mLifetime = 0.0;
|
||||
}
|
||||
|
||||
if( mDataBlock->snapOnMaxBounce )
|
||||
{
|
||||
// orient debris so it's flat
|
||||
MatrixF stat = getRenderTransform();
|
||||
|
||||
Point3F dir;
|
||||
stat.getColumn( 1, &dir );
|
||||
dir.z = 0.0;
|
||||
|
||||
MatrixF newTrans = MathUtils::createOrientFromDir( dir );
|
||||
|
||||
// hack for shell casings to get them above ground. Need something better - bramage
|
||||
newTrans.setPosition( getPosition() + Point3F( 0.0, 0.0, 0.10 ) );
|
||||
|
||||
setRenderTransform( newTrans );
|
||||
}
|
||||
if( mDataBlock->staticOnMaxBounce )
|
||||
{
|
||||
mStatic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixF tmp = getRenderTransform();
|
||||
tmp.setPosition( nextPos );
|
||||
setRenderTransform(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
updateEmitters( (Point3F&)*getRenderPosition(), mVelocity, (U32)(dt * 1000.0));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Rotate debris
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::rotate( F32 dt )
|
||||
{
|
||||
MatrixF curTrans = getRenderTransform();
|
||||
curTrans.setPosition( Point3F(0.0, 0.0, 0.0) );
|
||||
|
||||
Point3F curAngles = mRotAngles * dt * M_PI/180.0;
|
||||
MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) );
|
||||
|
||||
curTrans.mul( rotMatrix );
|
||||
curTrans.setPosition( getRenderPosition() );
|
||||
setRenderTransform( curTrans );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Bounce the debris - returns true if debris bounces
|
||||
//----------------------------------------------------------------------------
|
||||
bool Debris::bounce( const Point3F &nextPos, F32 dt )
|
||||
{
|
||||
Point3F curPos = getRenderPosition();
|
||||
|
||||
Point3F dir = nextPos - curPos;
|
||||
if( dir.magnitudeSafe() == 0.0 )
|
||||
return false;
|
||||
|
||||
dir.normalizeSafe();
|
||||
|
||||
Point3F extent = nextPos + dir * mRadius;
|
||||
F32 totalDist = Point3F( extent - curPos ).magnitudeSafe();
|
||||
F32 moveDist = Point3F( nextPos - curPos ).magnitudeSafe();
|
||||
F32 movePercent = (moveDist / totalDist);
|
||||
|
||||
RayInfo rayInfo;
|
||||
U32 collisionMask = csmStaticCollisionMask;
|
||||
if( !mDataBlock->ignoreWater )
|
||||
collisionMask |= WaterObjectType;
|
||||
|
||||
if( getContainer()->castRay( curPos, extent, collisionMask, &rayInfo ) )
|
||||
{
|
||||
|
||||
Point3F reflection = mVelocity - rayInfo.normal * (mDot( mVelocity, rayInfo.normal ) * 2.0);
|
||||
mVelocity = reflection;
|
||||
|
||||
Point3F tangent = reflection - rayInfo.normal * mDot( reflection, rayInfo.normal );
|
||||
mVelocity -= tangent * mFriction;
|
||||
|
||||
Point3F velDir = mVelocity;
|
||||
velDir.normalizeSafe();
|
||||
|
||||
mVelocity *= mElasticity;
|
||||
|
||||
Point3F bouncePos = curPos + dir * rayInfo.t * movePercent;
|
||||
bouncePos += mVelocity * dt;
|
||||
|
||||
MatrixF tmp = getRenderTransform();
|
||||
tmp.setPosition( bouncePos );
|
||||
setRenderTransform(tmp);
|
||||
|
||||
mRotAngles *= mElasticity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Explode
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::explode()
|
||||
{
|
||||
if( !mDataBlock->explosion )
|
||||
return;
|
||||
|
||||
const Point3F explosionPos = getRenderPosition();
|
||||
|
||||
Explosion* pExplosion = new Explosion;
|
||||
pExplosion->onNewDataBlock(mDataBlock->explosion);
|
||||
|
||||
MatrixF trans( true );
|
||||
trans.setPosition( explosionPos );
|
||||
|
||||
pExplosion->setTransform( trans );
|
||||
pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1);
|
||||
if (!pExplosion->registerObject())
|
||||
delete pExplosion;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Compute state of debris as if it hasn't collided with anything
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::computeNewState( Point3F &newPos, Point3F &newVel, F32 dt )
|
||||
{
|
||||
// apply gravity
|
||||
Point3F force = Point3F(0, 0, -9.81 * mDataBlock->gravModifier );
|
||||
|
||||
if( mDataBlock->terminalVelocity > 0.0001 && newVel.magnitudeSafe() > mDataBlock->terminalVelocity )
|
||||
{
|
||||
newVel.normalizeSafe();
|
||||
newVel *= mDataBlock->terminalVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
newVel += force * dt;
|
||||
}
|
||||
|
||||
newPos += newVel * dt;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update emitters
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms )
|
||||
{
|
||||
Point3F axis = -vel;
|
||||
Point3F lastPos = mLastPos;
|
||||
|
||||
if( axis.magnitudeSafe() == 0.0 )
|
||||
axis = Point3F( 0.0, 0.0, 1.0 );
|
||||
|
||||
axis.normalizeSafe();
|
||||
|
||||
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mEmitterList[i] )
|
||||
{
|
||||
mEmitterList[i]->emitParticles( lastPos, pos, axis, vel, ms );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Render debris
|
||||
//----------------------------------------------------------------------------
|
||||
bool Debris::prepRenderImage(SceneState* state, const U32 stateKey,
|
||||
const U32 /*startZone*/, const bool /*modifyBaseState*/)
|
||||
{
|
||||
if (isLastState(state, stateKey))
|
||||
return false;
|
||||
setLastState(state, stateKey);
|
||||
|
||||
// This should be sufficient for most objects that don't manage zones, and
|
||||
// don't need to return a specialized RenderImage...
|
||||
if( state->isObjectRendered(this) && (mPart || mShape) )
|
||||
{
|
||||
Point3F cameraOffset;
|
||||
mObjToWorld.getColumn(3,&cameraOffset);
|
||||
cameraOffset -= state->getCameraPosition();
|
||||
F32 dist = cameraOffset.len();
|
||||
F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
|
||||
|
||||
if( mShape )
|
||||
{
|
||||
DetailManager::selectPotentialDetails(mShape,dist,invScale);
|
||||
if( mShape->getCurrentDetail() < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( mPart )
|
||||
{
|
||||
DetailManager::selectPotentialDetails(mPart,dist,invScale);
|
||||
}
|
||||
|
||||
SceneRenderImage* image = new SceneRenderImage;
|
||||
image->obj = this;
|
||||
image->isTranslucent = true;
|
||||
image->sortType = SceneRenderImage::Point;
|
||||
state->setImageRefPoint(this, image);
|
||||
|
||||
state->insertRenderImage(image);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Render Object
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::renderObject(SceneState* state, SceneRenderImage* )
|
||||
{
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
||||
|
||||
RectI viewport;
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
dglGetViewport(&viewport);
|
||||
|
||||
// Uncomment this if this is a "simple" (non-zone managing) object
|
||||
state->setupObjectProjection(this);
|
||||
|
||||
F32 alpha = 1.0;
|
||||
if( mDataBlock->fade && mLifetime < 1.0 )
|
||||
alpha = mLifetime;
|
||||
|
||||
if( (mShape && DetailManager::selectCurrentDetail(mShape)) ||
|
||||
(mPart && DetailManager::selectCurrentDetail(mPart)) )
|
||||
{
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
dglMultMatrix(&mObjToWorld);
|
||||
glScalef(mObjScale.x, mObjScale.y, mObjScale.z);
|
||||
|
||||
installLights();
|
||||
|
||||
Point3F cameraOffset;
|
||||
mObjToWorld.getColumn(3,&cameraOffset);
|
||||
cameraOffset -= state->getCameraPosition();
|
||||
F32 fogAmount = state->getHazeAndFog(cameraOffset.len(),cameraOffset.z);
|
||||
|
||||
if( mShape )
|
||||
{
|
||||
TSMesh::setOverrideFade( alpha );
|
||||
mShape->setupFog(fogAmount, state->getFogColor());
|
||||
mShape->render();
|
||||
TSMesh::setOverrideFade( 1.0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mPart->getCurrentObjectDetail() != -1)
|
||||
{
|
||||
TSShapeInstance *parent = mPart->getSourceShapeInstance();
|
||||
|
||||
parent->setupFog(fogAmount, state->getFogColor());
|
||||
TSMesh::setOverrideFade( alpha );
|
||||
mPart->render();
|
||||
TSMesh::setOverrideFade( 1.0 );
|
||||
}
|
||||
}
|
||||
|
||||
uninstallLights();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
render2D();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
dglSetViewport(viewport);
|
||||
|
||||
dglSetCanonicalState();
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Render 2D debris
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::render2D()
|
||||
{
|
||||
if( !mDataBlock->render2D )
|
||||
return;
|
||||
|
||||
glBindTexture( GL_TEXTURE_2D, mDataBlock->texture.getGLName() );
|
||||
glColor4f( 1.0, 1.0, 1.0, 1.0 );
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
dglDrawBillboard( getPosition(), 0.1, 0.0 );
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Set size
|
||||
//----------------------------------------------------------------------------
|
||||
void Debris::setSize( F32 size )
|
||||
{
|
||||
mSize = size;
|
||||
}
|
||||
153
engine/game/debris.h
Executable file
153
engine/game/debris.h
Executable file
@@ -0,0 +1,153 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DEBRIS_H_
|
||||
#define _DEBRIS_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
|
||||
class ParticleEmitterData;
|
||||
class ParticleEmitter;
|
||||
class ExplosionData;
|
||||
class TSPartInstance;
|
||||
class TSShapeInstance;
|
||||
class TSShape;
|
||||
|
||||
//**************************************************************************
|
||||
// Debris Data
|
||||
//**************************************************************************
|
||||
struct DebrisData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Data Decarations
|
||||
//-----------------------------------------------------------------------
|
||||
enum DebrisDataConst
|
||||
{
|
||||
DDC_NUM_EMITTERS = 2,
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Debris datablock
|
||||
//-----------------------------------------------------------------------
|
||||
F32 velocity;
|
||||
F32 velocityVariance;
|
||||
F32 friction;
|
||||
F32 elasticity;
|
||||
F32 lifetime;
|
||||
F32 lifetimeVariance;
|
||||
U32 numBounces;
|
||||
U32 bounceVariance;
|
||||
F32 minSpinSpeed;
|
||||
F32 maxSpinSpeed;
|
||||
bool render2D;
|
||||
bool explodeOnMaxBounce; ///< explodes after it has bounced max times
|
||||
bool staticOnMaxBounce; ///< becomes static after bounced max times
|
||||
bool snapOnMaxBounce; ///< snap into a "resting" position on last bounce
|
||||
bool fade;
|
||||
bool useRadiusMass; ///< use mass calculations based on radius
|
||||
F32 baseRadius; ///< radius at which the standard elasticity and friction apply
|
||||
F32 gravModifier; ///< how much gravity affects debris
|
||||
F32 terminalVelocity; ///< max velocity magnitude
|
||||
bool ignoreWater;
|
||||
|
||||
const char* shapeName;
|
||||
Resource<TSShape> shape;
|
||||
|
||||
StringTableEntry textureName;
|
||||
TextureHandle texture;
|
||||
|
||||
|
||||
S32 explosionId;
|
||||
ExplosionData * explosion;
|
||||
ParticleEmitterData* emitterList[DDC_NUM_EMITTERS];
|
||||
S32 emitterIDList[DDC_NUM_EMITTERS];
|
||||
|
||||
DebrisData();
|
||||
|
||||
bool onAdd();
|
||||
bool preload( bool server, char errorBuffer[256] );
|
||||
static void initPersistFields();
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(DebrisData);
|
||||
|
||||
};
|
||||
DECLARE_CONSOLETYPE(DebrisData)
|
||||
|
||||
//**************************************************************************
|
||||
// Debris
|
||||
//**************************************************************************
|
||||
class Debris : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
private:
|
||||
S32 mNumBounces;
|
||||
F32 mSize;
|
||||
Point3F mLastPos;
|
||||
Point3F mVelocity;
|
||||
F32 mLifetime;
|
||||
DebrisData * mDataBlock;
|
||||
F32 mElapsedTime;
|
||||
TSShapeInstance * mShape;
|
||||
TSPartInstance * mPart;
|
||||
MatrixF mInitialTrans;
|
||||
F32 mXRotSpeed;
|
||||
F32 mZRotSpeed;
|
||||
Point3F mRotAngles;
|
||||
F32 mRadius;
|
||||
bool mStatic;
|
||||
F32 mElasticity;
|
||||
F32 mFriction;
|
||||
|
||||
ParticleEmitter * mEmitterList[ DebrisData::DDC_NUM_EMITTERS ];
|
||||
|
||||
bool bounce( const Point3F &nextPos, F32 dt );
|
||||
void computeNewState( Point3F &newPos, Point3F &newVel, F32 dt );
|
||||
void explode();
|
||||
void render2D();
|
||||
void rotate( F32 dt );
|
||||
|
||||
protected:
|
||||
virtual void processTick(const Move* move);
|
||||
virtual void advanceTime( F32 dt );
|
||||
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void updateEmitters( Point3F &pos, Point3F &vel, U32 ms );
|
||||
|
||||
public:
|
||||
|
||||
Debris();
|
||||
~Debris();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
bool onNewDataBlock( GameBaseData* dptr );
|
||||
|
||||
void init( const Point3F &position, const Point3F &velocity );
|
||||
void setLifetime( F32 lifetime ){ mLifetime = lifetime; }
|
||||
void setPartInstance( TSPartInstance *part ){ mPart = part; }
|
||||
void setSize( F32 size );
|
||||
void setVelocity( const Point3F &vel ){ mVelocity = vel; }
|
||||
void setRotAngles( const Point3F &angles ){ mRotAngles = angles; }
|
||||
|
||||
DECLARE_CONOBJECT(Debris);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
202
engine/game/debugView.cc
Executable file
202
engine/game/debugView.cc
Executable file
@@ -0,0 +1,202 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/console.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "gui/core/guiTSControl.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/player.h"
|
||||
#include "game/debugView.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(DebugView);
|
||||
|
||||
DebugView::DebugView()
|
||||
{
|
||||
for (int i = 0; i < MaxTextLines; i++)
|
||||
mTextLines[i][0] = '\0';
|
||||
}
|
||||
|
||||
|
||||
ConsoleMethod( DebugView, addLine, void, 5, 5, "( Point3F start, Point3F end, Color3F color)"
|
||||
"Cause a line to be drawn persistently by the DebugView.")
|
||||
{
|
||||
Point3F start(0, 0, 0);
|
||||
Point3F end(0, 0, 0);
|
||||
ColorF color(0, 0, 0, 1.0f);
|
||||
int numArgsRead;
|
||||
|
||||
//read the args in
|
||||
numArgsRead = dSscanf(argv[2], "%g %g %g", &start.x, &start.y, &start.z);
|
||||
if (numArgsRead != 3)
|
||||
{
|
||||
Con::printf("%s() - invalid start point.", argv[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
numArgsRead = dSscanf(argv[3], "%g %g %g", &end.x, &end.y, &end.z);
|
||||
if (numArgsRead != 3)
|
||||
{
|
||||
Con::printf("%s() - invalid end point.", argv[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
numArgsRead = dSscanf(argv[4], "%g %g %g", &color.red, &color.green, &color.blue);
|
||||
if (numArgsRead != 3)
|
||||
{
|
||||
Con::printf("%s() - invalid color.", argv[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
object->addLine(start, end, color);
|
||||
}
|
||||
|
||||
ConsoleMethod( DebugView, clearLines, void, 2, 2, "()"
|
||||
"Clear all lines added by addLine.")
|
||||
{
|
||||
object->clearLines();
|
||||
}
|
||||
|
||||
ConsoleMethod( DebugView, setText, void, 4, 5, "(int line, string text, Color3F color=NULL)"
|
||||
"Set one of the lines in the DebugView to contain the specified text at the specified color.")
|
||||
{
|
||||
ColorF color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
bool setColor = false;
|
||||
if (argc >= 5)
|
||||
{
|
||||
int numArgsRead = dSscanf(argv[4], "%g %g %g", &color.red, &color.green, &color.blue);
|
||||
if (numArgsRead == 3)
|
||||
setColor = true;
|
||||
}
|
||||
object->setTextLine(dAtoi(argv[2]), argv[3], setColor ? &color : NULL);
|
||||
}
|
||||
|
||||
ConsoleMethod( DebugView, clearText, void, 2, 3, "(int line=-1)"
|
||||
"Clears all lines, or if a line is specified, that specific line.")
|
||||
{
|
||||
int lineNum = -1;
|
||||
if (argc == 3)
|
||||
lineNum = dAtoi(argv[2]);
|
||||
object->clearTextLine(lineNum);
|
||||
}
|
||||
|
||||
void DebugView::addLine(const Point3F &start, const Point3F &end, const ColorF &color)
|
||||
{
|
||||
DebugLine newLine(start, end, color);
|
||||
mLines.push_back(newLine);
|
||||
}
|
||||
|
||||
void DebugView::clearLines()
|
||||
{
|
||||
mLines.clear();
|
||||
}
|
||||
|
||||
void DebugView::setTextLine(int line, const char *text, ColorF *color)
|
||||
{
|
||||
if (line < 0 || line >= MaxTextLines || !text)
|
||||
return;
|
||||
dStrncpy(&mTextLines[line][0], text, MaxTextLineLength);
|
||||
mTextLines[line][MaxTextLineLength] = '\0';
|
||||
|
||||
if (!color)
|
||||
mTextColors[line] = mProfile->mFontColor;
|
||||
else
|
||||
mTextColors[line] = *color;
|
||||
}
|
||||
|
||||
void DebugView::clearTextLine(int line)
|
||||
{
|
||||
if (line < 0)
|
||||
{
|
||||
for (int i = 0; i < MaxTextLines; i++)
|
||||
mTextLines[i][0] = '\0';
|
||||
}
|
||||
else if (line < MaxTextLines)
|
||||
mTextLines[line][0] = '\0';
|
||||
}
|
||||
|
||||
void DebugView::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
S32 i;
|
||||
#if defined(TORQUE_DEBUG) || defined(INTERNAL_RELEASE)
|
||||
GuiTSCtrl *tsCtrl;
|
||||
if (! Sim::findObject("PlayGui", tsCtrl))
|
||||
{
|
||||
Con::printf("DebugView failed - unable to find TS ctrl.");
|
||||
return;
|
||||
}
|
||||
|
||||
//draw the lines first
|
||||
for (i = 0; i < mLines.size(); i++)
|
||||
{
|
||||
//project the line to the screen
|
||||
Point3F startPos, endPos;
|
||||
if (tsCtrl->project(mLines[i].start, &startPos) && tsCtrl->project(mLines[i].end, &endPos))
|
||||
{
|
||||
glBegin(GL_LINES);
|
||||
glColor4f(mLines[i].color.red, mLines[i].color.green, mLines[i].color.blue, 1.0f);
|
||||
glVertex2i((S32)startPos.x, (S32)startPos.y);
|
||||
glVertex2i((S32)endPos.x, (S32)endPos.y);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
//draw the task above each player's head
|
||||
SimGroup *g = Sim::getClientGroup();
|
||||
SimGroup::iterator j;
|
||||
for (j = g->begin(); j != g->end(); j++)
|
||||
{
|
||||
GameConnection *client = static_cast<GameConnection*>(*j);
|
||||
Player *player = NULL;
|
||||
if (! client->getControlObject())
|
||||
continue;
|
||||
|
||||
player = dynamic_cast<Player*>(client->getControlObject());
|
||||
if (! player)
|
||||
continue;
|
||||
|
||||
//draw a test string above everyone's head
|
||||
Point3F playerPos;
|
||||
MatrixF const& tempTransform = player->getTransform();
|
||||
tempTransform.getColumn(3, &playerPos);
|
||||
playerPos.z += 1.7f;
|
||||
Point3F textPos;
|
||||
if (tsCtrl->project(playerPos, &textPos))
|
||||
{
|
||||
//const char *textStr = client->getDataField("objective", NULL);
|
||||
const char *textStr = Con::executef(2, "aiGetTaskDesc", avar("%d", client->getId()));
|
||||
if (!textStr || !textStr[0])
|
||||
textStr = "Shoot Me!";
|
||||
if ((textStr[0] == 'E' || textStr[0] == 'F') && textStr[1] == ':')
|
||||
{
|
||||
if (textStr[0] == 'E')
|
||||
dglSetBitmapModulation(ColorF(1.0, 0.0, 0.0, 1.0));
|
||||
else
|
||||
dglSetBitmapModulation(ColorF(0.0, 1.0, 0.0, 1.0));
|
||||
dglDrawText(mFont, Point2I(textPos.x, textPos.y), &textStr[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
dglSetBitmapModulation(mProfile->mFontColor);
|
||||
dglDrawText(mFont, Point2I(textPos.x, textPos.y), textStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//draw the text - for final release, this is the only thing to be rendered
|
||||
Point2I textOffset = offset;
|
||||
for (i = 0; i < MaxTextLines; i++)
|
||||
{
|
||||
dglSetBitmapModulation(mTextColors[i]);
|
||||
if (mTextLines[i][0] != '\0')
|
||||
dglDrawText(mFont, textOffset, mTextLines[i]);
|
||||
textOffset.y += mFont->getHeight();
|
||||
}
|
||||
|
||||
renderChildControls(offset, updateRect);
|
||||
}
|
||||
|
||||
|
||||
57
engine/game/debugView.h
Executable file
57
engine/game/debugView.h
Executable file
@@ -0,0 +1,57 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DEBUGVIEW_H_
|
||||
#define _DEBUGVIEW_H_
|
||||
|
||||
#ifndef _GUITEXTCTRL_H_
|
||||
#include "gui/controls/guiTextCtrl.h"
|
||||
#endif
|
||||
|
||||
/// Helper class to render a HUD debug display.
|
||||
class DebugView : public GuiTextCtrl
|
||||
{
|
||||
private:
|
||||
typedef GuiTextCtrl Parent;
|
||||
|
||||
enum
|
||||
{
|
||||
MaxTextLines = 64,
|
||||
MaxTextLineLength = 255
|
||||
};
|
||||
|
||||
//text members
|
||||
char mTextLines[MaxTextLines][MaxTextLineLength + 1];
|
||||
ColorF mTextColors[MaxTextLines];
|
||||
|
||||
struct DebugLine
|
||||
{
|
||||
Point3F start;
|
||||
Point3F end;
|
||||
ColorF color;
|
||||
DebugLine(const Point3F &inStart, const Point3F &inEnd, const ColorI &inColor)
|
||||
{
|
||||
color = inColor;
|
||||
start = inStart;
|
||||
end = inEnd;
|
||||
}
|
||||
};
|
||||
Vector<DebugLine> mLines;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(DebugView);
|
||||
DebugView();
|
||||
|
||||
void addLine(const Point3F &start, const Point3F &end, const ColorF &color);
|
||||
void clearLines();
|
||||
|
||||
void setTextLine(int line, const char *text, ColorF *color);
|
||||
void clearTextLine(int line = -1);
|
||||
|
||||
void onRender(Point2I offset, const RectI &updateRect);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
34
engine/game/demoGame.h
Executable file
34
engine/game/demoGame.h
Executable file
@@ -0,0 +1,34 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUEGAME_H_
|
||||
#define _TORQUEGAME_H_
|
||||
|
||||
#ifndef _GAMEINTERFACE_H_
|
||||
#include "platform/gameInterface.h"
|
||||
#endif
|
||||
|
||||
/// Implementation of GameInterface for the demo app.
|
||||
class DemoGame : public GameInterface
|
||||
{
|
||||
public:
|
||||
void textureKill();
|
||||
void textureResurrect();
|
||||
void refreshWindow();
|
||||
|
||||
int main(int argc, const char **argv);
|
||||
|
||||
void processPacketReceiveEvent(PacketReceiveEvent *event);
|
||||
void processMouseMoveEvent(MouseMoveEvent *event);
|
||||
void processInputEvent(InputEvent *event);
|
||||
void processQuitEvent();
|
||||
void processTimeEvent(TimeEvent *event);
|
||||
void processConsoleEvent(ConsoleEvent *event);
|
||||
void processConnectedAcceptEvent(ConnectedAcceptEvent *event);
|
||||
void processConnectedReceiveEvent(ConnectedReceiveEvent *event);
|
||||
void processConnectedNotifyEvent(ConnectedNotifyEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
||||
238
engine/game/fireballAtmosphere.cc
Executable file
238
engine/game/fireballAtmosphere.cc
Executable file
@@ -0,0 +1,238 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "audio/audioDataBlock.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "sceneGraph/sceneState.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "game/player.h"
|
||||
#include "game/fireballAtmosphere.h"
|
||||
#include "game/debris.h"
|
||||
#include "math/mathUtils.h"
|
||||
|
||||
#define DML_DIR "textures/"
|
||||
#define COLOR_OFFSET 0.25
|
||||
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(FireballAtmosphere);
|
||||
IMPLEMENT_CO_DATABLOCK_V1(FireballAtmosphereData);
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Fireball Atmosphere Data
|
||||
//**************************************************************************
|
||||
FireballAtmosphereData::FireballAtmosphereData()
|
||||
{
|
||||
|
||||
fireball = NULL;
|
||||
fireballID = 0;
|
||||
}
|
||||
|
||||
IMPLEMENT_CONSOLETYPE(FireballAtmosphereData)
|
||||
IMPLEMENT_GETDATATYPE(FireballAtmosphereData)
|
||||
IMPLEMENT_SETDATATYPE(FireballAtmosphereData)
|
||||
|
||||
void FireballAtmosphereData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
addField("fireball", TypeDebrisDataPtr, Offset(fireball, FireballAtmosphereData));
|
||||
}
|
||||
|
||||
bool FireballAtmosphereData::onAdd()
|
||||
{
|
||||
if(Parent::onAdd() == false)
|
||||
return false;
|
||||
|
||||
if(!fireball && fireballID != 0)
|
||||
{
|
||||
if(!Sim::findObject(SimObjectId(fireballID), fireball))
|
||||
Con::errorf(ConsoleLogEntry::General, "FireballAtmosphereData::preload: Invalid packet, bad datablockId(fireball): 0x%x", fireballID);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FireballAtmosphereData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
if(stream->writeFlag(fireball))
|
||||
{
|
||||
stream->writeRangedU32(packed? SimObjectId(fireball):
|
||||
fireball->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FireballAtmosphereData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
if(stream->readFlag())
|
||||
fireballID = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// Fireball Atmosphere
|
||||
//**************************************************************************
|
||||
FireballAtmosphere::FireballAtmosphere()
|
||||
{
|
||||
mTimeSinceLastDrop = 0.0;
|
||||
|
||||
mDropRadius = 600.0;
|
||||
mDropsPerMinute = 3.0;
|
||||
mMinDropAngle = 0.0;
|
||||
mMaxDropAngle = 30.0;
|
||||
mStartVelocity = 20.0;
|
||||
mDropHeight = 500.0;
|
||||
mDropDir.set(0.5, 0.5, -0.5);
|
||||
}
|
||||
|
||||
void FireballAtmosphere::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("dropRadius", TypeF32, Offset(mDropRadius, FireballAtmosphere));
|
||||
addField("dropsPerMinute", TypeF32, Offset(mDropsPerMinute, FireballAtmosphere));
|
||||
addField("minDropAngle", TypeF32, Offset(mMinDropAngle, FireballAtmosphere));
|
||||
addField("maxDropAngle", TypeF32, Offset(mMaxDropAngle, FireballAtmosphere));
|
||||
addField("startVelocity", TypeF32, Offset(mStartVelocity, FireballAtmosphere));
|
||||
addField("dropHeight", TypeF32, Offset(mDropHeight, FireballAtmosphere));
|
||||
addField("dropDir", TypePoint3F, Offset(mDropDir, FireballAtmosphere));
|
||||
}
|
||||
|
||||
bool FireballAtmosphere::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if(mDataBlock && mDataBlock->fireball)
|
||||
mDataBlock->fireball->terminalVelocity = mStartVelocity;
|
||||
|
||||
mDropDir.normalize();
|
||||
|
||||
mObjBox.min.set(-1e6, -1e6, -1e6);
|
||||
mObjBox.max.set( 1e6, 1e6, 1e6);
|
||||
|
||||
resetWorldBox();
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FireballAtmosphere::onRemove()
|
||||
{
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
bool FireballAtmosphere::onNewDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
mDataBlock = dynamic_cast<FireballAtmosphereData*>(dptr);
|
||||
if(!mDataBlock || !Parent::onNewDataBlock(dptr))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FireballAtmosphere::prepRenderImage(SceneState* state, const U32 stateKey, const U32, const bool)
|
||||
{
|
||||
if(isLastState(state, stateKey))
|
||||
return false;
|
||||
setLastState(state, stateKey);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FireballAtmosphere::renderObject(SceneState* , SceneRenderImage*)
|
||||
{
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
||||
}
|
||||
|
||||
void FireballAtmosphere::advanceTime(F32 dt)
|
||||
{
|
||||
mTimeSinceLastDrop += dt;
|
||||
F32 dropFrequency = 60.0 / mDropsPerMinute;
|
||||
|
||||
if(mTimeSinceLastDrop > dropFrequency)
|
||||
{
|
||||
mTimeSinceLastDrop -= dropFrequency;
|
||||
dropNewFireball();
|
||||
}
|
||||
}
|
||||
|
||||
void FireballAtmosphere::dropNewFireball()
|
||||
{
|
||||
MatrixF camTrans;
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
connection->getControlCameraTransform(0.0, &camTrans);
|
||||
|
||||
Debris *fireball = new Debris;
|
||||
fireball->onNewDataBlock(mDataBlock->fireball);
|
||||
|
||||
// set velocity
|
||||
VectorF launchVel = MathUtils::randomDir(mDropDir, mMinDropAngle, mMaxDropAngle);
|
||||
launchVel *= mStartVelocity;
|
||||
|
||||
// set start point
|
||||
VectorF down(0.0, 0.0, -1.0);
|
||||
Point3F launchPoint = MathUtils::randomDir(down, 90.0, 90.0);
|
||||
launchPoint *= mDropRadius * gRandGen.randF(0.1, 1.0);
|
||||
launchPoint += camTrans.getPosition();
|
||||
launchPoint.z = 0.0;
|
||||
|
||||
F32 timeToHit = mDropHeight / launchVel.z;
|
||||
launchPoint += launchVel * timeToHit;
|
||||
|
||||
|
||||
if(!fireball->registerObject())
|
||||
delete fireball;
|
||||
else
|
||||
fireball->init(launchPoint, launchVel);
|
||||
}
|
||||
|
||||
U32 FireballAtmosphere::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if(stream->writeFlag(mask & GameBase::InitialUpdateMask))
|
||||
{
|
||||
stream->write(mDropRadius);
|
||||
stream->write(mDropsPerMinute);
|
||||
stream->write(mMaxDropAngle);
|
||||
stream->write(mMinDropAngle);
|
||||
stream->write(mStartVelocity);
|
||||
stream->write(mDropHeight);
|
||||
stream->write(mDropDir.x);
|
||||
stream->write(mDropDir.y);
|
||||
stream->write(mDropDir.z);
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void FireballAtmosphere::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
if(stream->readFlag())
|
||||
{
|
||||
stream->read(&mDropRadius);
|
||||
stream->read(&mDropsPerMinute);
|
||||
stream->read(&mMaxDropAngle);
|
||||
stream->read(&mMinDropAngle);
|
||||
stream->read(&mStartVelocity);
|
||||
stream->read(&mDropHeight);
|
||||
stream->read(&mDropDir.x);
|
||||
stream->read(&mDropDir.y);
|
||||
stream->read(&mDropDir.z);
|
||||
}
|
||||
}
|
||||
86
engine/game/fireballAtmosphere.h
Executable file
86
engine/game/fireballAtmosphere.h
Executable file
@@ -0,0 +1,86 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FIREBALLATMOSPHERE_H_
|
||||
#define _FIREBALLATMOSPHERE_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
|
||||
class AudioProfile;
|
||||
struct DebrisData;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Fireball Atmosphere Data
|
||||
//--------------------------------------------------------------------------
|
||||
class FireballAtmosphereData : public GameBaseData {
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
public:
|
||||
|
||||
DebrisData* fireball;
|
||||
S32 fireballID;
|
||||
|
||||
FireballAtmosphereData();
|
||||
|
||||
bool onAdd();
|
||||
static void initPersistFields();
|
||||
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(FireballAtmosphereData);
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Fireball atmosphere
|
||||
//--------------------------------------------------------------------------
|
||||
class FireballAtmosphere : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
private:
|
||||
FireballAtmosphereData *mDataBlock;
|
||||
AUDIOHANDLE mAudioHandle;
|
||||
F32 mTimeSinceLastDrop;
|
||||
|
||||
F32 mDropRadius;
|
||||
F32 mDropsPerMinute;
|
||||
F32 mMinDropAngle;
|
||||
F32 mMaxDropAngle;
|
||||
F32 mStartVelocity;
|
||||
F32 mDropHeight;
|
||||
VectorF mDropDir;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
void advanceTime(F32 dt);
|
||||
void dropNewFireball();
|
||||
|
||||
// Rendering
|
||||
protected:
|
||||
bool prepRenderImage(SceneState*, const U32, const U32, const bool);
|
||||
void renderObject(SceneState*, SceneRenderImage*);
|
||||
U32 packUpdate(NetConnection*, U32 mask, BitStream* stream);
|
||||
void unpackUpdate(NetConnection*, BitStream* stream);
|
||||
|
||||
public:
|
||||
|
||||
FireballAtmosphere();
|
||||
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
static void initPersistFields();
|
||||
|
||||
DECLARE_CONOBJECT(FireballAtmosphere);
|
||||
};
|
||||
|
||||
#endif // _H_FIREBALL_ATMOSPHERE
|
||||
124
engine/game/fps/guiClockHud.cc
Executable file
124
engine/game/fps/guiClockHud.cc
Executable file
@@ -0,0 +1,124 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/shapeBase.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// Vary basic HUD clock.
|
||||
/// Displays the current simulation time offset from some base. The base time
|
||||
/// is usually synchronized with the server as mission start time. This hud
|
||||
/// currently only displays minutes:seconds.
|
||||
class GuiClockHud : public GuiControl
|
||||
{
|
||||
typedef GuiControl Parent;
|
||||
|
||||
bool mShowFrame;
|
||||
bool mShowFill;
|
||||
|
||||
ColorF mFillColor;
|
||||
ColorF mFrameColor;
|
||||
ColorF mTextColor;
|
||||
|
||||
S32 mTimeOffset;
|
||||
|
||||
public:
|
||||
GuiClockHud();
|
||||
|
||||
void setTime(F32 newTime);
|
||||
F32 getTime();
|
||||
|
||||
void onRender( Point2I, const RectI &);
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT( GuiClockHud );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT( GuiClockHud );
|
||||
|
||||
GuiClockHud::GuiClockHud()
|
||||
{
|
||||
mShowFrame = mShowFill = true;
|
||||
mFillColor.set(0, 0, 0, 0.5);
|
||||
mFrameColor.set(0, 1, 0, 1);
|
||||
mTextColor.set( 0, 1, 0, 1 );
|
||||
|
||||
mTimeOffset = 0;
|
||||
}
|
||||
|
||||
void GuiClockHud::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Misc");
|
||||
addField( "showFill", TypeBool, Offset( mShowFill, GuiClockHud ) );
|
||||
addField( "showFrame", TypeBool, Offset( mShowFrame, GuiClockHud ) );
|
||||
addField( "fillColor", TypeColorF, Offset( mFillColor, GuiClockHud ) );
|
||||
addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiClockHud ) );
|
||||
addField( "textColor", TypeColorF, Offset( mTextColor, GuiClockHud ) );
|
||||
endGroup("Misc");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void GuiClockHud::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
// Background first
|
||||
if (mShowFill)
|
||||
dglDrawRectFill(updateRect, mFillColor);
|
||||
|
||||
// Convert ms time into hours, minutes and seconds.
|
||||
S32 time = S32(getTime());
|
||||
S32 secs = time % 60;
|
||||
S32 mins = (time % 3600) / 60;
|
||||
S32 hours = time / 3600;
|
||||
|
||||
// Currently only displays min/sec
|
||||
char buf[256];
|
||||
dSprintf(buf,sizeof(buf), "%02d:%02d",mins,secs);
|
||||
|
||||
// Center the text
|
||||
offset.x += (mBounds.extent.x - mProfile->mFont->getStrWidth((const UTF8 *)buf)) / 2;
|
||||
offset.y += (mBounds.extent.y - mProfile->mFont->getHeight()) / 2;
|
||||
dglSetBitmapModulation(mTextColor);
|
||||
dglDrawText(mProfile->mFont, offset, buf);
|
||||
dglClearBitmapModulation();
|
||||
|
||||
// Border last
|
||||
if (mShowFrame)
|
||||
dglDrawRect(updateRect, mFrameColor);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void GuiClockHud::setTime(F32 time)
|
||||
{
|
||||
// Set the current time in seconds.
|
||||
mTimeOffset = S32(time * 1000) - Platform::getVirtualMilliseconds();
|
||||
}
|
||||
|
||||
F32 GuiClockHud::getTime()
|
||||
{
|
||||
// Return elapsed time in seconds.
|
||||
return F32(mTimeOffset + Platform::getVirtualMilliseconds()) / 1000;
|
||||
}
|
||||
|
||||
ConsoleMethod(GuiClockHud,setTime,void,3, 3,"(time in sec)Sets the current base time for the clock")
|
||||
{
|
||||
object->setTime(dAtof(argv[2]));
|
||||
}
|
||||
|
||||
ConsoleMethod(GuiClockHud,getTime, F32, 2, 2,"()Returns current time in secs.")
|
||||
{
|
||||
return object->getTime();
|
||||
}
|
||||
148
engine/game/fps/guiCrossHairHud.cc
Executable file
148
engine/game/fps/guiCrossHairHud.cc
Executable file
@@ -0,0 +1,148 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "gui/controls/guiBitmapCtrl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/shapeBase.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Vary basic cross hair hud.
|
||||
/// Uses the base bitmap control to render a bitmap, and decides whether
|
||||
/// to draw or not depending on the current control object and it's state.
|
||||
/// If there is ShapeBase object under the cross hair and it's named,
|
||||
/// then a small health bar is displayed.
|
||||
class GuiCrossHairHud : public GuiBitmapCtrl
|
||||
{
|
||||
typedef GuiBitmapCtrl Parent;
|
||||
|
||||
ColorF mDamageFillColor;
|
||||
ColorF mDamageFrameColor;
|
||||
Point2I mDamageRectSize;
|
||||
Point2I mDamageOffset;
|
||||
|
||||
protected:
|
||||
void drawDamage(Point2I offset, F32 damage, F32 opacity);
|
||||
|
||||
public:
|
||||
GuiCrossHairHud();
|
||||
|
||||
void onRender( Point2I, const RectI &);
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT( GuiCrossHairHud );
|
||||
};
|
||||
|
||||
/// Valid object types for which the cross hair will render, this
|
||||
/// should really all be script controlled.
|
||||
static const U32 ObjectMask = PlayerObjectType | VehicleObjectType;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT( GuiCrossHairHud );
|
||||
|
||||
GuiCrossHairHud::GuiCrossHairHud()
|
||||
{
|
||||
mDamageFillColor.set( 0, 1, 0, 1 );
|
||||
mDamageFrameColor.set( 1, 0.6, 0, 1 );
|
||||
mDamageRectSize.set(50, 4);
|
||||
mDamageOffset.set(0,32);
|
||||
}
|
||||
|
||||
void GuiCrossHairHud::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
addGroup("Damage");
|
||||
addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiCrossHairHud ) );
|
||||
addField( "damageFrameColor", TypeColorF, Offset( mDamageFrameColor, GuiCrossHairHud ) );
|
||||
addField( "damageRect", TypePoint2I, Offset( mDamageRectSize, GuiCrossHairHud ) );
|
||||
addField( "damageOffset", TypePoint2I, Offset( mDamageOffset, GuiCrossHairHud ) );
|
||||
endGroup("Damage");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void GuiCrossHairHud::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
// Must have a connection and player control object
|
||||
GameConnection* conn = GameConnection::getConnectionToServer();
|
||||
if (!conn)
|
||||
return;
|
||||
ShapeBase* control = conn->getControlObject();
|
||||
if (!control || !(control->getType() & ObjectMask) || !conn->isFirstPerson())
|
||||
return;
|
||||
|
||||
// Parent render.
|
||||
Parent::onRender(offset,updateRect);
|
||||
|
||||
// Get control camera info
|
||||
MatrixF cam;
|
||||
Point3F camPos;
|
||||
conn->getControlCameraTransform(0,&cam);
|
||||
cam.getColumn(3, &camPos);
|
||||
|
||||
// Extend the camera vector to create an endpoint for our ray
|
||||
Point3F endPos;
|
||||
cam.getColumn(1, &endPos);
|
||||
endPos *= gClientSceneGraph->getVisibleDistance();
|
||||
endPos += camPos;
|
||||
|
||||
// Collision info. We're going to be running LOS tests and we
|
||||
// don't want to collide with the control object.
|
||||
static U32 losMask = TerrainObjectType | InteriorObjectType | ShapeBaseObjectType;
|
||||
control->disableCollision();
|
||||
|
||||
RayInfo info;
|
||||
if (gClientContainer.castRay(camPos, endPos, losMask, &info)) {
|
||||
// Hit something... but we'll only display health for named
|
||||
// ShapeBase objects. Could mask against the object type here
|
||||
// and do a static cast if it's a ShapeBaseObjectType, but this
|
||||
// isn't a performance situation, so I'll just use dynamic_cast.
|
||||
if (ShapeBase* obj = dynamic_cast<ShapeBase*>(info.object))
|
||||
if (obj->getShapeName()) {
|
||||
offset.x = updateRect.point.x + updateRect.extent.x / 2;
|
||||
offset.y = updateRect.point.y + updateRect.extent.y / 2;
|
||||
drawDamage(offset + mDamageOffset, obj->getDamageValue(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore control object collision
|
||||
control->enableCollision();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
Display a damage bar ubove the shape.
|
||||
This is a support funtion, called by onRender.
|
||||
*/
|
||||
void GuiCrossHairHud::drawDamage(Point2I offset, F32 damage, F32 opacity)
|
||||
{
|
||||
mDamageFillColor.alpha = mDamageFrameColor.alpha = opacity;
|
||||
|
||||
// Damage should be 0->1 (0 being no damage,or healthy), but
|
||||
// we'll just make sure here as we flip it.
|
||||
damage = mClampF(1 - damage, 0, 1);
|
||||
|
||||
// Center the bar
|
||||
RectI rect(offset, mDamageRectSize);
|
||||
rect.point.x -= mDamageRectSize.x / 2;
|
||||
|
||||
// Draw the border
|
||||
dglDrawRect(rect, mDamageFrameColor);
|
||||
|
||||
// Draw the damage % fill
|
||||
rect.point += Point2I(1, 1);
|
||||
rect.extent -= Point2I(1, 1);
|
||||
rect.extent.x = (S32)(rect.extent.x * damage);
|
||||
if (rect.extent.x == 1)
|
||||
rect.extent.x = 2;
|
||||
if (rect.extent.x > 0)
|
||||
dglDrawRectFill(rect, mDamageFillColor);
|
||||
}
|
||||
165
engine/game/fps/guiHealthBarHud.cc
Executable file
165
engine/game/fps/guiHealthBarHud.cc
Executable file
@@ -0,0 +1,165 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/shapeBase.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// A basic health bar control.
|
||||
/// This gui displays the damage value of the current PlayerObjectType
|
||||
/// control object. The gui can be set to pulse if the health value
|
||||
/// drops below a set value. This control only works if a server
|
||||
/// connection exists and it's control object is a PlayerObjectType. If
|
||||
/// either of these requirements is false, the control is not rendered.
|
||||
class GuiHealthBarHud : public GuiControl
|
||||
{
|
||||
typedef GuiControl Parent;
|
||||
|
||||
bool mShowFrame;
|
||||
bool mShowFill;
|
||||
bool mDisplayEnergy;
|
||||
bool mFlipped;
|
||||
|
||||
ColorF mFillColor;
|
||||
ColorF mFrameColor;
|
||||
ColorF mDamageFillColor;
|
||||
|
||||
S32 mPulseRate;
|
||||
F32 mPulseThreshold;
|
||||
|
||||
F32 mValue;
|
||||
|
||||
public:
|
||||
GuiHealthBarHud();
|
||||
|
||||
void onRender( Point2I, const RectI &);
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT( GuiHealthBarHud );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT( GuiHealthBarHud );
|
||||
|
||||
GuiHealthBarHud::GuiHealthBarHud()
|
||||
{
|
||||
mShowFrame = mShowFill = true;
|
||||
mFlipped = mDisplayEnergy = false;
|
||||
mFillColor.set(0, 0, 0, 0.5);
|
||||
mFrameColor.set(0, 1, 0, 1);
|
||||
mDamageFillColor.set(0, 1, 0, 1);
|
||||
|
||||
mPulseRate = 0;
|
||||
mPulseThreshold = 0.3f;
|
||||
mValue = 0.2f;
|
||||
}
|
||||
|
||||
void GuiHealthBarHud::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Colors");
|
||||
addField( "fillColor", TypeColorF, Offset( mFillColor, GuiHealthBarHud ) );
|
||||
addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiHealthBarHud ) );
|
||||
addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiHealthBarHud ) );
|
||||
endGroup("Colors");
|
||||
|
||||
addGroup("Pulse");
|
||||
addField( "pulseRate", TypeS32, Offset( mPulseRate, GuiHealthBarHud ) );
|
||||
addField( "pulseThreshold", TypeF32, Offset( mPulseThreshold, GuiHealthBarHud ) );
|
||||
endGroup("Pulse");
|
||||
|
||||
addGroup("Misc");
|
||||
addField( "flipped", TypeBool, Offset( mFlipped, GuiHealthBarHud ) );
|
||||
addField( "showFill", TypeBool, Offset( mShowFill, GuiHealthBarHud ) );
|
||||
addField( "showFrame", TypeBool, Offset( mShowFrame, GuiHealthBarHud ) );
|
||||
addField( "displayEnergy", TypeBool, Offset( mDisplayEnergy, GuiHealthBarHud ) );
|
||||
endGroup("Misc");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
Gui onRender method.
|
||||
Renders a health bar with filled background and border.
|
||||
*/
|
||||
void GuiHealthBarHud::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
// Must have a connection and player control object
|
||||
GameConnection* conn = GameConnection::getConnectionToServer();
|
||||
if (!conn)
|
||||
return;
|
||||
ShapeBase* control = conn->getControlObject();
|
||||
if (!control || !(control->getType() & PlayerObjectType))
|
||||
return;
|
||||
|
||||
if(mDisplayEnergy)
|
||||
{
|
||||
mValue = control->getEnergyValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We'll just grab the damage right off the control object.
|
||||
// Damage value 0 = no damage.
|
||||
mValue = 1 - control->getDamageValue();
|
||||
}
|
||||
|
||||
|
||||
// Background first
|
||||
if (mShowFill)
|
||||
dglDrawRectFill(updateRect, mFillColor);
|
||||
|
||||
// Pulse the damage fill if it's below the threshold
|
||||
if (mPulseRate != 0)
|
||||
{
|
||||
if (mValue < mPulseThreshold)
|
||||
{
|
||||
F32 time = Platform::getVirtualMilliseconds();
|
||||
F32 alpha = mFmod(time,mPulseRate) / (mPulseRate / 2.0);
|
||||
mDamageFillColor.alpha = (alpha > 1.0)? 2.0 - alpha: alpha;
|
||||
}
|
||||
else
|
||||
mDamageFillColor.alpha = 1;
|
||||
}
|
||||
|
||||
// Render damage fill %
|
||||
RectI rect(updateRect);
|
||||
if(mBounds.extent.x > mBounds.extent.y)
|
||||
{
|
||||
if(mFlipped)
|
||||
{
|
||||
S32 bottomX = rect.point.x + rect.extent.x;
|
||||
rect.extent.x = (S32)(rect.extent.x * mValue);
|
||||
rect.point.x = bottomX - rect.extent.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.extent.x = (S32)(rect.extent.x * mValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mFlipped)
|
||||
{
|
||||
rect.extent.y = (S32)(rect.extent.y * mValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
S32 bottomY = rect.point.y + rect.extent.y;
|
||||
rect.extent.y = (S32)(rect.extent.y * mValue);
|
||||
rect.point.y = bottomY - rect.extent.y;
|
||||
}
|
||||
}
|
||||
|
||||
dglDrawRectFill(rect, mDamageFillColor);
|
||||
|
||||
// Border last
|
||||
if (mShowFrame)
|
||||
dglDrawRect(updateRect, mFrameColor);
|
||||
}
|
||||
250
engine/game/fps/guiShapeNameHud.cc
Executable file
250
engine/game/fps/guiShapeNameHud.cc
Executable file
@@ -0,0 +1,250 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "dgl/gNewFont.h"
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "gui/core/guiTSControl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "game/shapeBase.h"
|
||||
#include "game/gameConnection.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Displays name & damage above shape objects.
|
||||
///
|
||||
/// This control displays the name and damage value of all named
|
||||
/// ShapeBase objects on the client. The name and damage of objects
|
||||
/// within the control's display area are overlayed above the object.
|
||||
///
|
||||
/// This GUI control must be a child of a TSControl, and a server connection
|
||||
/// and control object must be present.
|
||||
///
|
||||
/// This is a stand-alone control and relies only on the standard base GuiControl.
|
||||
class GuiShapeNameHud : public GuiControl
|
||||
{
|
||||
typedef GuiControl Parent;
|
||||
|
||||
// field data
|
||||
ColorF mFillColor;
|
||||
ColorF mFrameColor;
|
||||
ColorF mTextColor;
|
||||
|
||||
F32 mVerticalOffset;
|
||||
F32 mDistanceFade;
|
||||
bool mShowFrame;
|
||||
bool mShowFill;
|
||||
|
||||
protected:
|
||||
void drawName( Point2I offset, const char *buf, F32 opacity);
|
||||
|
||||
public:
|
||||
GuiShapeNameHud();
|
||||
|
||||
// GuiControl
|
||||
virtual void onRender(Point2I offset, const RectI &updateRect);
|
||||
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT( GuiShapeNameHud );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiShapeNameHud);
|
||||
|
||||
/// Default distance for object's information to be displayed.
|
||||
static const F32 cDefaultVisibleDistance = 500.0f;
|
||||
|
||||
GuiShapeNameHud::GuiShapeNameHud()
|
||||
{
|
||||
mFillColor.set( 0.25, 0.25, 0.25, 0.25 );
|
||||
mFrameColor.set( 0, 1, 0, 1 );
|
||||
mTextColor.set( 0, 1, 0, 1 );
|
||||
mShowFrame = mShowFill = true;
|
||||
mVerticalOffset = 0.5;
|
||||
mDistanceFade = 0.1;
|
||||
}
|
||||
|
||||
void GuiShapeNameHud::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
addGroup("Colors");
|
||||
addField( "fillColor", TypeColorF, Offset( mFillColor, GuiShapeNameHud ) );
|
||||
addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiShapeNameHud ) );
|
||||
addField( "textColor", TypeColorF, Offset( mTextColor, GuiShapeNameHud ) );
|
||||
endGroup("Colors");
|
||||
|
||||
addGroup("Misc");
|
||||
addField( "showFill", TypeBool, Offset( mShowFill, GuiShapeNameHud ) );
|
||||
addField( "showFrame", TypeBool, Offset( mShowFrame, GuiShapeNameHud ) );
|
||||
addField( "verticalOffset", TypeF32, Offset( mVerticalOffset, GuiShapeNameHud ) );
|
||||
addField( "distanceFade", TypeF32, Offset( mDistanceFade, GuiShapeNameHud ) );
|
||||
endGroup("Misc");
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Core rendering method for this control.
|
||||
///
|
||||
/// This method scans through all the current client ShapeBase objects.
|
||||
/// If one is named, it displays the name and damage information for it.
|
||||
///
|
||||
/// Information is offset from the center of the object's bounding box,
|
||||
/// unless the object is a PlayerObjectType, in which case the eye point
|
||||
/// is used.
|
||||
///
|
||||
/// @param updateRect Extents of control.
|
||||
void GuiShapeNameHud::onRender( Point2I, const RectI &updateRect)
|
||||
{
|
||||
// Background fill first
|
||||
if (mShowFill)
|
||||
dglDrawRectFill(updateRect, mFillColor);
|
||||
|
||||
// Must be in a TS Control
|
||||
GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());
|
||||
if (!parent) return;
|
||||
|
||||
// Must have a connection and control object
|
||||
GameConnection* conn = GameConnection::getConnectionToServer();
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
ShapeBase* control = conn->getControlObject();
|
||||
if (!control)
|
||||
return;
|
||||
|
||||
// Get control camera info
|
||||
MatrixF cam;
|
||||
Point3F camPos;
|
||||
VectorF camDir;
|
||||
conn->getControlCameraTransform(0,&cam);
|
||||
cam.getColumn(3, &camPos);
|
||||
cam.getColumn(1, &camDir);
|
||||
|
||||
F32 camFov;
|
||||
conn->getControlCameraFov(&camFov);
|
||||
camFov = mDegToRad(camFov) / 2;
|
||||
|
||||
// Visible distance info & name fading
|
||||
F32 visDistance = gClientSceneGraph->getVisibleDistance();
|
||||
F32 visDistanceSqr = visDistance * visDistance;
|
||||
F32 fadeDistance = visDistance * mDistanceFade;
|
||||
|
||||
// Collision info. We're going to be running LOS tests and we
|
||||
// don't want to collide with the control object.
|
||||
static U32 losMask = TerrainObjectType | InteriorObjectType | ShapeBaseObjectType;
|
||||
control->disableCollision();
|
||||
|
||||
// All ghosted objects are added to the server connection group,
|
||||
// so we can find all the shape base objects by iterating through
|
||||
// our current connection.
|
||||
for (SimSetIterator itr(conn); *itr; ++itr)
|
||||
{
|
||||
if ((*itr)->getType() & ShapeBaseObjectType)
|
||||
{
|
||||
ShapeBase* shape = static_cast<ShapeBase*>(*itr);
|
||||
if (shape != control && shape->getShapeName())
|
||||
{
|
||||
|
||||
// Target pos to test, if it's a player run the LOS to his eye
|
||||
// point, otherwise we'll grab the generic box center.
|
||||
Point3F shapePos;
|
||||
if (shape->getType() & PlayerObjectType)
|
||||
{
|
||||
MatrixF eye;
|
||||
|
||||
// Use the render eye transform, otherwise we'll see jittering
|
||||
shape->getRenderEyeTransform(&eye);
|
||||
eye.getColumn(3, &shapePos);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the render transform instead of the box center
|
||||
// otherwise it'll jitter.
|
||||
MatrixF srtMat = shape->getRenderTransform();
|
||||
srtMat.getColumn(3, &shapePos);
|
||||
}
|
||||
|
||||
VectorF shapeDir = shapePos - camPos;
|
||||
|
||||
// Test to see if it's in range
|
||||
F32 shapeDist = shapeDir.lenSquared();
|
||||
if (shapeDist == 0 || shapeDist > visDistanceSqr)
|
||||
continue;
|
||||
shapeDist = mSqrt(shapeDist);
|
||||
|
||||
// Test to see if it's within our viewcone, this test doesn't
|
||||
// actually match the viewport very well, should consider
|
||||
// projection and box test.
|
||||
shapeDir.normalize();
|
||||
F32 dot = mDot(shapeDir, camDir);
|
||||
if (dot < camFov)
|
||||
continue;
|
||||
|
||||
// Test to see if it's behind something, and we want to
|
||||
// ignore anything it's mounted on when we run the LOS.
|
||||
RayInfo info;
|
||||
shape->disableCollision();
|
||||
ShapeBase *mount = shape->getObjectMount();
|
||||
|
||||
if (mount)
|
||||
mount->disableCollision();
|
||||
bool los = !gClientContainer.castRay(camPos, shapePos,losMask, &info);
|
||||
shape->enableCollision();
|
||||
if (mount)
|
||||
mount->enableCollision();
|
||||
|
||||
if (!los)
|
||||
continue;
|
||||
|
||||
// Project the shape pos into screen space and calculate
|
||||
// the distance opacity used to fade the labels into the
|
||||
// distance.
|
||||
Point3F projPnt;
|
||||
shapePos.z += mVerticalOffset;
|
||||
if (!parent->project(shapePos, &projPnt))
|
||||
continue;
|
||||
F32 opacity = (shapeDist < fadeDistance)? 1.0:
|
||||
1.0 - (shapeDist - fadeDistance) / (visDistance - fadeDistance);
|
||||
|
||||
// Render the shape's name
|
||||
drawName(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getShapeName(),opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore control object collision
|
||||
control->enableCollision();
|
||||
|
||||
// Border last
|
||||
if (mShowFrame)
|
||||
dglDrawRect(updateRect, mFrameColor);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Render object names.
|
||||
///
|
||||
/// Helper function for GuiShapeNameHud::onRender
|
||||
///
|
||||
/// @param offset Screen coordinates to render name label. (Text is centered
|
||||
/// horizontally about this location, with bottom of text at
|
||||
/// specified y position.)
|
||||
/// @param name String name to display.
|
||||
/// @param opacity Opacity of name (a fraction).
|
||||
void GuiShapeNameHud::drawName(Point2I offset, const char *name, F32 opacity)
|
||||
{
|
||||
// Center the name
|
||||
offset.x -= mProfile->mFont->getStrWidth((const UTF8 *)name) / 2;
|
||||
offset.y -= mProfile->mFont->getHeight();
|
||||
|
||||
// Deal with opacity and draw.
|
||||
mTextColor.alpha = opacity;
|
||||
dglSetBitmapModulation(mTextColor);
|
||||
dglDrawText(mProfile->mFont, offset, name);
|
||||
dglClearBitmapModulation();
|
||||
}
|
||||
|
||||
154
engine/game/fx/cameraFXMgr.cc
Executable file
154
engine/game/fx/cameraFXMgr.cc
Executable file
@@ -0,0 +1,154 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "game/fx/cameraFXMgr.h"
|
||||
|
||||
#include "math/mRandom.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "dgl/dgl.h"
|
||||
|
||||
// global cam fx
|
||||
CameraFXManager gCamFXMgr;
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Camera effect
|
||||
//**************************************************************************
|
||||
CameraFX::CameraFX()
|
||||
{
|
||||
mElapsedTime = 0.0;
|
||||
mDuration = 1.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Update
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraFX::update( F32 dt )
|
||||
{
|
||||
mElapsedTime += dt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Camera shake effect
|
||||
//**************************************************************************
|
||||
CameraShake::CameraShake()
|
||||
{
|
||||
mFreq.zero();
|
||||
mAmp.zero();
|
||||
mStartAmp.zero();
|
||||
mTimeOffset.zero();
|
||||
mCamFXTrans.identity();
|
||||
mFalloff = 10.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Update
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraShake::update( F32 dt )
|
||||
{
|
||||
Parent::update( dt );
|
||||
|
||||
fadeAmplitude();
|
||||
|
||||
VectorF camOffset;
|
||||
camOffset.x = mAmp.x * sin( M_2PI * (mTimeOffset.x + mElapsedTime) * mFreq.x );
|
||||
camOffset.y = mAmp.y * sin( M_2PI * (mTimeOffset.y + mElapsedTime) * mFreq.y );
|
||||
camOffset.z = mAmp.z * sin( M_2PI * (mTimeOffset.z + mElapsedTime) * mFreq.z );
|
||||
|
||||
VectorF rotAngles;
|
||||
rotAngles.x = camOffset.x * 10.0 * M_PI/180.0;
|
||||
rotAngles.y = camOffset.y * 10.0 * M_PI/180.0;
|
||||
rotAngles.z = camOffset.z * 10.0 * M_PI/180.0;
|
||||
MatrixF rotMatrix( EulerF( rotAngles.x, rotAngles.y, rotAngles.z ) );
|
||||
|
||||
mCamFXTrans = rotMatrix;
|
||||
mCamFXTrans.setPosition( camOffset );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Fade out the amplitude over time
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraShake::fadeAmplitude()
|
||||
{
|
||||
F32 percentDone = (mElapsedTime / mDuration);
|
||||
if( percentDone > 1.0 ) percentDone = 1.0;
|
||||
|
||||
F32 time = 1 + percentDone * mFalloff;
|
||||
time = 1 / (time * time);
|
||||
|
||||
mAmp = mStartAmp * time;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraShake::init()
|
||||
{
|
||||
mTimeOffset.x = 0.0;
|
||||
mTimeOffset.y = gRandGen.randF();
|
||||
mTimeOffset.z = gRandGen.randF();
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// CameraFXManager
|
||||
//**************************************************************************
|
||||
CameraFXManager::CameraFXManager()
|
||||
{
|
||||
mCamFXTrans.identity();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Destructor
|
||||
//--------------------------------------------------------------------------
|
||||
CameraFXManager::~CameraFXManager()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Add new effect to currently running list
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraFXManager::addFX( CameraFX *newFX )
|
||||
{
|
||||
mFXList.link( newFX );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Clear all currently running camera effects
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraFXManager::clear()
|
||||
{
|
||||
mFXList.free();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Update camera effects
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraFXManager::update( F32 dt )
|
||||
{
|
||||
CameraFXPtr *cur = NULL;
|
||||
mCamFXTrans.identity();
|
||||
|
||||
for( cur = mFXList.next( cur ); cur; cur = mFXList.next( cur ) )
|
||||
{
|
||||
CameraFX * curFX = *cur;
|
||||
curFX->update( dt );
|
||||
MatrixF fxTrans = curFX->getTrans();
|
||||
|
||||
mCamFXTrans.mul( fxTrans );
|
||||
|
||||
if( curFX->isExpired() )
|
||||
{
|
||||
CameraFXPtr *prev = mFXList.prev( cur );
|
||||
mFXList.free( cur );
|
||||
cur = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
engine/game/fx/cameraFXMgr.h
Executable file
89
engine/game/fx/cameraFXMgr.h
Executable file
@@ -0,0 +1,89 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CAMERAFXMGR_H_
|
||||
#define _CAMERAFXMGR_H_
|
||||
|
||||
#ifndef _LLIST_H_
|
||||
#include "core/llist.h"
|
||||
#endif
|
||||
#ifndef _MPOINT_H_
|
||||
#include "math/mPoint.h"
|
||||
#endif
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
|
||||
//**************************************************************************
|
||||
// Abstract camera effect template
|
||||
//**************************************************************************
|
||||
class CameraFX
|
||||
{
|
||||
protected:
|
||||
F32 mElapsedTime;
|
||||
F32 mDuration;
|
||||
MatrixF mCamFXTrans;
|
||||
|
||||
public:
|
||||
CameraFX();
|
||||
|
||||
MatrixF & getTrans(){ return mCamFXTrans; }
|
||||
bool isExpired(){ return mElapsedTime >= mDuration; }
|
||||
void setDuration( F32 duration ){ mDuration = duration; }
|
||||
|
||||
virtual void update( F32 dt );
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Camera shake effect
|
||||
//--------------------------------------------------------------------------
|
||||
class CameraShake : public CameraFX
|
||||
{
|
||||
typedef CameraFX Parent;
|
||||
|
||||
VectorF mFreq; // these are vectors to represent these values in 3D
|
||||
VectorF mStartAmp;
|
||||
VectorF mAmp;
|
||||
VectorF mTimeOffset;
|
||||
F32 mFalloff;
|
||||
|
||||
public:
|
||||
CameraShake();
|
||||
|
||||
void init();
|
||||
void fadeAmplitude();
|
||||
void setFalloff( F32 falloff ){ mFalloff = falloff; }
|
||||
void setFrequency( VectorF &freq ){ mFreq = freq; }
|
||||
void setAmplitude( VectorF & ){ mStartAmp = amp; }
|
||||
|
||||
virtual void update( F32 dt );
|
||||
};
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CameraFXManager
|
||||
//**************************************************************************
|
||||
class CameraFXManager
|
||||
{
|
||||
typedef CameraFX * CameraFXPtr;
|
||||
|
||||
LList< CameraFXPtr > mFXList;
|
||||
MatrixF mCamFXTrans;
|
||||
|
||||
public:
|
||||
void addFX( CameraFX *newFX );
|
||||
void clear();
|
||||
MatrixF & getTrans(){ return mCamFXTrans; }
|
||||
void update( F32 dt );
|
||||
|
||||
CameraFXManager();
|
||||
~CameraFXManager();
|
||||
|
||||
};
|
||||
|
||||
extern CameraFXManager gCamFXMgr;
|
||||
|
||||
|
||||
#endif
|
||||
1125
engine/game/fx/explosion.cc
Executable file
1125
engine/game/fx/explosion.cc
Executable file
File diff suppressed because it is too large
Load Diff
172
engine/game/fx/explosion.h
Executable file
172
engine/game/fx/explosion.h
Executable file
@@ -0,0 +1,172 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _EXPLOSION_H_
|
||||
#define _EXPLOSION_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
|
||||
class ParticleEmitter;
|
||||
class ParticleEmitterData;
|
||||
class TSThread;
|
||||
class AudioProfile;
|
||||
struct DebrisData;
|
||||
class ShockwaveData;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class ExplosionData : public GameBaseData {
|
||||
public:
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
enum ExplosionConsts
|
||||
{
|
||||
EC_NUM_DEBRIS_TYPES = 1,
|
||||
EC_NUM_EMITTERS = 4,
|
||||
EC_MAX_SUB_EXPLOSIONS = 5,
|
||||
EC_NUM_TIME_KEYS = 4,
|
||||
};
|
||||
|
||||
public:
|
||||
StringTableEntry dtsFileName;
|
||||
|
||||
bool faceViewer;
|
||||
|
||||
S32 particleDensity;
|
||||
F32 particleRadius;
|
||||
|
||||
AudioProfile* soundProfile;
|
||||
ParticleEmitterData* particleEmitter;
|
||||
S32 soundProfileId;
|
||||
S32 particleEmitterId;
|
||||
|
||||
Point3F explosionScale;
|
||||
F32 playSpeed;
|
||||
|
||||
Resource<TSShape> explosionShape;
|
||||
S32 explosionAnimation;
|
||||
|
||||
ParticleEmitterData* emitterList[EC_NUM_EMITTERS];
|
||||
S32 emitterIDList[EC_NUM_EMITTERS];
|
||||
|
||||
ShockwaveData * shockwave;
|
||||
S32 shockwaveID;
|
||||
bool shockwaveOnTerrain;
|
||||
|
||||
DebrisData * debrisList[EC_NUM_DEBRIS_TYPES];
|
||||
S32 debrisIDList[EC_NUM_DEBRIS_TYPES];
|
||||
|
||||
F32 debrisThetaMin;
|
||||
F32 debrisThetaMax;
|
||||
F32 debrisPhiMin;
|
||||
F32 debrisPhiMax;
|
||||
S32 debrisNum;
|
||||
S32 debrisNumVariance;
|
||||
F32 debrisVelocity;
|
||||
F32 debrisVelocityVariance;
|
||||
|
||||
// sub - explosions
|
||||
ExplosionData* explosionList[EC_MAX_SUB_EXPLOSIONS];
|
||||
S32 explosionIDList[EC_MAX_SUB_EXPLOSIONS];
|
||||
|
||||
S32 delayMS;
|
||||
S32 delayVariance;
|
||||
S32 lifetimeMS;
|
||||
S32 lifetimeVariance;
|
||||
|
||||
F32 offset;
|
||||
Point3F sizes[ EC_NUM_TIME_KEYS ];
|
||||
F32 times[ EC_NUM_TIME_KEYS ];
|
||||
|
||||
// camera shake data
|
||||
bool shakeCamera;
|
||||
VectorF camShakeFreq;
|
||||
VectorF camShakeAmp;
|
||||
F32 camShakeDuration;
|
||||
F32 camShakeRadius;
|
||||
F32 camShakeFalloff;
|
||||
|
||||
// Dynamic Lighting. The light is smoothly
|
||||
// interpolated from start to end time.
|
||||
F32 lightStartRadius;
|
||||
F32 lightEndRadius;
|
||||
ColorF lightStartColor;
|
||||
ColorF lightEndColor;
|
||||
|
||||
ExplosionData();
|
||||
DECLARE_CONOBJECT(ExplosionData);
|
||||
bool onAdd();
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(ExplosionData)
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class Explosion : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
private:
|
||||
ExplosionData* mDataBlock;
|
||||
|
||||
TSShapeInstance* mExplosionInstance;
|
||||
TSThread* mExplosionThread;
|
||||
|
||||
ParticleEmitter * mEmitterList[ ExplosionData::EC_NUM_EMITTERS ];
|
||||
|
||||
U32 mCurrMS;
|
||||
U32 mEndingMS;
|
||||
F32 mRandAngle;
|
||||
LightInfo mLight;
|
||||
|
||||
protected:
|
||||
Point3F mInitialNormal;
|
||||
F32 mFade;
|
||||
F32 mFog;
|
||||
bool mActive;
|
||||
S32 mDelayMS;
|
||||
F32 mRandomVal;
|
||||
U32 mCollideType;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool explode();
|
||||
|
||||
void processTick(const Move *move);
|
||||
void advanceTime(F32 dt);
|
||||
void updateEmitters( F32 dt );
|
||||
void launchDebris( Point3F &axis );
|
||||
void spawnSubExplosions();
|
||||
void setCurrentScale();
|
||||
|
||||
// Rendering
|
||||
protected:
|
||||
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
void prepModelView(SceneState*);
|
||||
void registerLights(LightManager * lm, bool lightingScene);
|
||||
|
||||
public:
|
||||
Explosion();
|
||||
~Explosion();
|
||||
void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0);
|
||||
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
void setCollideType( U32 cType ){ mCollideType = cType; }
|
||||
|
||||
DECLARE_CONOBJECT(Explosion);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
#endif // _H_EXPLOSION
|
||||
|
||||
1798
engine/game/fx/fxFoliageReplicator.cc
Executable file
1798
engine/game/fx/fxFoliageReplicator.cc
Executable file
File diff suppressed because it is too large
Load Diff
308
engine/game/fx/fxFoliageReplicator.h
Executable file
308
engine/game/fx/fxFoliageReplicator.h
Executable file
@@ -0,0 +1,308 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Written by Melvyn May, 4th August 2002.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOLIAGEREPLICATOR_H_
|
||||
#define _FOLIAGEREPLICATOR_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
|
||||
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
|
||||
|
||||
#define FXFOLIAGEREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticObjectType | \
|
||||
WaterObjectType )
|
||||
|
||||
#define FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticObjectType )
|
||||
|
||||
|
||||
#define FXFOLIAGE_ALPHA_EPSILON 1e-4
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxFoliageItem
|
||||
//------------------------------------------------------------------------------
|
||||
class fxFoliageItem
|
||||
{
|
||||
public:
|
||||
MatrixF Transform;
|
||||
F32 Width;
|
||||
F32 Height;
|
||||
Box3F FoliageBox;
|
||||
bool Flipped;
|
||||
F32 SwayPhase;
|
||||
F32 SwayTimeRatio;
|
||||
F32 LightPhase;
|
||||
F32 LightTimeRatio;
|
||||
U32 LastFrameSerialID;
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxFoliageCulledList
|
||||
//------------------------------------------------------------------------------
|
||||
class fxFoliageCulledList
|
||||
{
|
||||
public:
|
||||
fxFoliageCulledList() {};
|
||||
fxFoliageCulledList(Box3F SearchBox, fxFoliageCulledList* InVec);
|
||||
~fxFoliageCulledList() {};
|
||||
|
||||
void FindCandidates(Box3F SearchBox, fxFoliageCulledList* InVec);
|
||||
|
||||
U32 GetListCount(void) { return mCulledObjectSet.size(); };
|
||||
fxFoliageItem* GetElement(U32 index) { return mCulledObjectSet[index]; };
|
||||
|
||||
Vector<fxFoliageItem*> mCulledObjectSet; // Culled Object Set.
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxFoliageQuadNode
|
||||
//------------------------------------------------------------------------------
|
||||
class fxFoliageQuadrantNode
|
||||
{
|
||||
public:
|
||||
U32 Level;
|
||||
Box3F QuadrantBox;
|
||||
fxFoliageQuadrantNode* QuadrantChildNode[4];
|
||||
Vector<fxFoliageItem*> RenderList;
|
||||
|
||||
fxFoliageQuadrantNode()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxFoliageRenderList
|
||||
//------------------------------------------------------------------------------
|
||||
class fxFoliageRenderList
|
||||
{
|
||||
public:
|
||||
Point3F FarPosLeftUp; // View Frustum.
|
||||
Point3F FarPosLeftDown;
|
||||
Point3F FarPosRightUp;
|
||||
Point3F FarPosRightDown;
|
||||
Point3F CameraPosition; // Camera Position.
|
||||
Box3F mBox; // Clipping Box.
|
||||
PlaneF ViewPlanes[5]; // Clipping View-Planes.
|
||||
|
||||
Vector<fxFoliageItem*> mVisObjectSet; // Visible Object Set.
|
||||
F32 mHeightLerp; // Height Lerp.
|
||||
|
||||
public:
|
||||
bool IsQuadrantVisible(const Box3F VisBox, const MatrixF& RenderTransform);
|
||||
void SetupClipPlanes(SceneState* state, const F32 FarClipPlane);
|
||||
void CompileVisibleSet(const fxFoliageQuadrantNode* pNode, const MatrixF& RenderTransform, const bool UseDebug);
|
||||
void DrawQuadBox(const Box3F& QuadBox, const ColorF Colour);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxFoliageReplicator
|
||||
//------------------------------------------------------------------------------
|
||||
class fxFoliageReplicator : public SceneObject
|
||||
{
|
||||
private:
|
||||
typedef SceneObject Parent;
|
||||
|
||||
protected:
|
||||
|
||||
void CreateFoliage(void);
|
||||
void DestroyFoliage(void);
|
||||
|
||||
void SyncFoliageReplicators(void);
|
||||
|
||||
Box3F FetchQuadrant(Box3F Box, U32 Quadrant);
|
||||
void ProcessQuadrant(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList, U32 Quadrant);
|
||||
void ProcessNodeChildren(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList);
|
||||
|
||||
enum { FoliageReplicationMask = (1 << 0) };
|
||||
|
||||
|
||||
U32 mCreationAreaAngle;
|
||||
bool mClientReplicationStarted;
|
||||
bool mAddedToScene;
|
||||
U32 mCurrentFoliageCount;
|
||||
|
||||
Vector<fxFoliageQuadrantNode*> mFoliageQuadTree;
|
||||
Vector<fxFoliageItem*> mReplicatedFoliage;
|
||||
fxFoliageRenderList mFrustumRenderSet;
|
||||
|
||||
MRandomLCG RandomGen;
|
||||
F32 mFadeInGradient;
|
||||
F32 mFadeOutGradient;
|
||||
S32 mLastRenderTime;
|
||||
F32 mGlobalSwayPhase;
|
||||
F32 mGlobalSwayTimeRatio;
|
||||
F32 mGlobalLightPhase;
|
||||
F32 mGlobalLightTimeRatio;
|
||||
U32 mFrameSerialID;
|
||||
|
||||
U32 mQuadTreeLevels; // Quad-Tree Levels.
|
||||
U32 mPotentialFoliageNodes; // Potential Foliage Nodes.
|
||||
U32 mNextAllocatedNodeIdx; // Next Allocated Node Index.
|
||||
U32 mBillboardsAcquired; // Billboards Acquired.
|
||||
|
||||
public:
|
||||
fxFoliageReplicator();
|
||||
~fxFoliageReplicator();
|
||||
|
||||
void StartUp(void);
|
||||
void ShowReplication(void);
|
||||
void HideReplication(void);
|
||||
|
||||
// SceneObject
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
virtual bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone,
|
||||
const bool modifyBaseZoneState = false);
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void onEditorEnable();
|
||||
void onEditorDisable();
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// ConObject.
|
||||
static void initPersistFields();
|
||||
|
||||
// Field Data.
|
||||
class tagFieldData
|
||||
{
|
||||
public:
|
||||
|
||||
bool mUseDebugInfo;
|
||||
F32 mDebugBoxHeight;
|
||||
U32 mSeed;
|
||||
StringTableEntry mFoliageFile;
|
||||
TextureHandle mFoliageTexture;
|
||||
U32 mFoliageCount;
|
||||
U32 mFoliageRetries;
|
||||
|
||||
U32 mInnerRadiusX;
|
||||
U32 mInnerRadiusY;
|
||||
U32 mOuterRadiusX;
|
||||
U32 mOuterRadiusY;
|
||||
|
||||
F32 mMinWidth;
|
||||
F32 mMaxWidth;
|
||||
F32 mMinHeight;
|
||||
F32 mMaxHeight;
|
||||
bool mFixAspectRatio;
|
||||
bool mFixSizeToMax;
|
||||
F32 mOffsetZ;
|
||||
bool mRandomFlip;
|
||||
|
||||
bool mUseCulling;
|
||||
U32 mCullResolution;
|
||||
F32 mViewDistance;
|
||||
F32 mViewClosest;
|
||||
F32 mFadeInRegion;
|
||||
F32 mFadeOutRegion;
|
||||
F32 mAlphaCutoff;
|
||||
F32 mGroundAlpha;
|
||||
|
||||
bool mSwayOn;
|
||||
bool mSwaySync;
|
||||
F32 mSwayMagnitudeSide;
|
||||
F32 mSwayMagnitudeFront;
|
||||
F32 mMinSwayTime;
|
||||
F32 mMaxSwayTime;
|
||||
|
||||
bool mLightOn;
|
||||
bool mLightSync;
|
||||
F32 mMinLuminance;
|
||||
F32 mMaxLuminance;
|
||||
F32 mLightTime;
|
||||
|
||||
bool mAllowOnTerrain;
|
||||
bool mAllowOnInteriors;
|
||||
bool mAllowStatics;
|
||||
bool mAllowOnWater;
|
||||
bool mAllowWaterSurface;
|
||||
S32 mAllowedTerrainSlope;
|
||||
|
||||
bool mHideFoliage;
|
||||
bool mShowPlacementArea;
|
||||
U32 mPlacementBandHeight;
|
||||
ColorF mPlaceAreaColour;
|
||||
|
||||
tagFieldData()
|
||||
{
|
||||
// Set Defaults.
|
||||
mUseDebugInfo = false;
|
||||
mDebugBoxHeight = 1.0f;
|
||||
mSeed = 1376312589;
|
||||
mFoliageFile = StringTable->insert("");
|
||||
mFoliageTexture = TextureHandle();
|
||||
mFoliageCount = 10;
|
||||
mFoliageRetries = 100;
|
||||
|
||||
mInnerRadiusX = 0;
|
||||
mInnerRadiusY = 0;
|
||||
mOuterRadiusX = 128;
|
||||
mOuterRadiusY = 128;
|
||||
|
||||
mMinWidth = 1;
|
||||
mMaxWidth = 3;
|
||||
mMinHeight = 1;
|
||||
mMaxHeight = 5;
|
||||
mFixAspectRatio = true;
|
||||
mFixSizeToMax = false;
|
||||
mOffsetZ = 0;
|
||||
mRandomFlip = true;
|
||||
|
||||
mUseCulling = true;
|
||||
mCullResolution = 64;
|
||||
mViewDistance = 50.0f;
|
||||
mViewClosest = 1.0f;
|
||||
mFadeInRegion = 10.0f;
|
||||
mFadeOutRegion = 1.0f;
|
||||
mAlphaCutoff = 0.2f;
|
||||
mGroundAlpha = 1.0f;
|
||||
|
||||
mSwayOn = false;
|
||||
mSwaySync = false;
|
||||
mSwayMagnitudeSide = 0.1f;
|
||||
mSwayMagnitudeFront = 0.2f;
|
||||
mMinSwayTime = 3.0f;
|
||||
mMaxSwayTime = 10.0f;
|
||||
|
||||
mLightOn = false;
|
||||
mLightSync = false;
|
||||
mMinLuminance = 0.7f;
|
||||
mMaxLuminance = 1.0f;
|
||||
mLightTime = 5.0f;
|
||||
|
||||
mAllowOnTerrain = true;
|
||||
mAllowOnInteriors = true;
|
||||
mAllowStatics = true;
|
||||
mAllowOnWater = false;
|
||||
mAllowWaterSurface = false;
|
||||
mAllowedTerrainSlope = 90;
|
||||
|
||||
mHideFoliage = false;
|
||||
mShowPlacementArea = true;
|
||||
mPlacementBandHeight = 25;
|
||||
mPlaceAreaColour .set(0.4f, 0, 0.8f);
|
||||
}
|
||||
|
||||
} mFieldData;
|
||||
|
||||
// Declare Console Object.
|
||||
DECLARE_CONOBJECT(fxFoliageReplicator);
|
||||
};
|
||||
|
||||
#endif // _FOLIAGEREPLICATOR_H_
|
||||
1674
engine/game/fx/fxLight.cc
Executable file
1674
engine/game/fx/fxLight.cc
Executable file
File diff suppressed because it is too large
Load Diff
219
engine/game/fx/fxLight.h
Executable file
219
engine/game/fx/fxLight.h
Executable file
@@ -0,0 +1,219 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine - fxLight
|
||||
//
|
||||
// Written by Melvyn May, 4th May 2002.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FXLIGHTDB_H_
|
||||
#define _FXLIGHTDB_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
|
||||
#define FXLIGHTDBICONTEXTURE "common/editor/fxlighticon.png"
|
||||
|
||||
|
||||
class fxLightData : public GameBaseData
|
||||
{
|
||||
public:
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
fxLightData();
|
||||
|
||||
bool onAdd();
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
|
||||
// Datablock Flare.
|
||||
StringTableEntry mFlareTextureName; // Flare Texture Name.
|
||||
|
||||
// Datablock Light.
|
||||
bool mLightOn; // Light On Flag.
|
||||
F32 mRadius; // Radius.
|
||||
F32 mBrightness; // Brightness.
|
||||
ColorF mColour; // Colour.
|
||||
|
||||
// Datablock Flare.
|
||||
bool mFlareOn; // Flare On Flag.
|
||||
bool mFlareTP; // Flare Third Person Flag.
|
||||
ColorF mFlareColour; // Flare Colour.
|
||||
bool mConstantSizeOn; // Flare Use Constant Size Flag.
|
||||
F32 mConstantSize; // Flare Constant Size.
|
||||
F32 mNearSize; // Near Size.
|
||||
F32 mFarSize; // Far Size.
|
||||
F32 mNearDistance; // Near Distance.
|
||||
F32 mFarDistance; // Far Distance.
|
||||
F32 mFadeTime; // Fade Time.
|
||||
U32 mBlendMode; // Blend Mode.
|
||||
bool mLinkFlare; // Link Flare Animation.
|
||||
bool mLinkFlareSize; // Link Flare Size Animation.
|
||||
|
||||
// Datablock Animation.
|
||||
ColorF mMinColour; // Minimum Colour.
|
||||
ColorF mMaxColour; // Maximum Colour.
|
||||
F32 mMinBrightness; // Minimum Brightness.
|
||||
F32 mMaxBrightness; // Maximum Brightness.
|
||||
F32 mMinRadius; // Minimum Radius.
|
||||
F32 mMaxRadius; // Maximum Radius.
|
||||
Point3F mStartOffset; // Start Offset.
|
||||
Point3F mEndOffset; // Stop Offset.
|
||||
F32 mMinRotation; // Minimum Rotation.
|
||||
F32 mMaxRotation; // Maximum Rotation.
|
||||
bool mSingleColourKeys; // Single-Channel Colour Keys.
|
||||
StringTableEntry mRedKeys; // Red Animation Keys.
|
||||
StringTableEntry mGreenKeys; // Green Animation Keys.
|
||||
StringTableEntry mBlueKeys; // Blue Animation Keys.
|
||||
StringTableEntry mBrightnessKeys; // Brightness Animation Keys.
|
||||
StringTableEntry mRadiusKeys; // Radius Animation Keys.
|
||||
StringTableEntry mOffsetKeys; // Offset Animation Keys.
|
||||
StringTableEntry mRotationKeys; // Rotation Animation Keys.
|
||||
F32 mColourTime; // Colour Time (Seconds).
|
||||
F32 mBrightnessTime; // Brightness Time.
|
||||
F32 mRadiusTime; // Radius Time.
|
||||
F32 mOffsetTime; // Offset Time.
|
||||
F32 mRotationTime; // Rotation Time.
|
||||
bool mLerpColour; // Lerp Colour Flag.
|
||||
bool mLerpBrightness; // Lerp Brightness Flag.
|
||||
bool mLerpRadius; // Lerp Radius Flag.
|
||||
bool mLerpOffset; // Lerp Offset Flag.
|
||||
bool mLerpRotation; // Lerp Rotation Flag.
|
||||
bool mUseColour; // Use Colour Flag.
|
||||
bool mUseBrightness; // Use Brightness Flag.
|
||||
bool mUseRadius; // Use Radius Flag.
|
||||
bool mUseOffsets; // Use Position Offsets Flag.
|
||||
bool mUseRotation; // Use Rotation Flag.
|
||||
|
||||
|
||||
DECLARE_CONOBJECT(fxLightData);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(fxLightData)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxLight
|
||||
//------------------------------------------------------------------------------
|
||||
class fxLight : public GameBase
|
||||
{
|
||||
private:
|
||||
typedef GameBase Parent;
|
||||
fxLightData* mDataBlock;
|
||||
|
||||
U32 CheckKeySyntax(StringTableEntry Key);
|
||||
void CheckAnimationKeys(void);
|
||||
F32 GetLerpKey(StringTableEntry Key, U32 PosFrom, U32 PosTo, F32 ValueFrom, F32 ValueTo, F32 Lerp);
|
||||
void AnimateLight(void);
|
||||
void InitialiseAnimation(void);
|
||||
void ResetAnimation(void);
|
||||
bool TestLOS(const Point3F& ObjectPosition, SceneObject* AttachedObj);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
enum {
|
||||
fxLightConfigChangeMask = BIT(0),
|
||||
fxLightAttachChange = BIT(1)
|
||||
};
|
||||
|
||||
bool mAddedToScene;
|
||||
|
||||
MRandomLCG RandomGen;
|
||||
|
||||
// Textures.
|
||||
TextureHandle mIconTextureHandle;
|
||||
TextureHandle mFlareTextureHandle;
|
||||
|
||||
LightInfo mLight;
|
||||
U32 mLastAnimateTime;
|
||||
U32 mLastRenderTime;
|
||||
F32 mFlareScale;
|
||||
bool mAttached;
|
||||
GameBase* mpAttachedObject;
|
||||
bool mAttachWait;
|
||||
bool mAttachValid;
|
||||
|
||||
public:
|
||||
fxLight();
|
||||
~fxLight();
|
||||
|
||||
// *********************************
|
||||
// Configuration Interface.
|
||||
// *********************************
|
||||
|
||||
// Light.
|
||||
void setEnable(bool Status);
|
||||
void setFlareBitmap(const char* Name);
|
||||
|
||||
// Misc,
|
||||
void reset(void);
|
||||
void attachToObject(const char* ObjectName);
|
||||
void detachFromObject(void);
|
||||
|
||||
// GameBase.
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
void processTick(const Move*);
|
||||
|
||||
// SceneObject
|
||||
bool prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void onDeleteNotify(SimObject*);
|
||||
|
||||
void inspectPostApply();
|
||||
void registerLights(LightManager * lightManager, bool lightingScene);
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *, U32, BitStream *);
|
||||
void unpackUpdate(NetConnection *, BitStream *);
|
||||
|
||||
// ConObject
|
||||
static void initPersistFields();
|
||||
|
||||
// Field Data.
|
||||
bool mEnable; // Light Enable.
|
||||
|
||||
// Basis Light.
|
||||
F32 mIconSize; // Icon Size.
|
||||
|
||||
// Current Animation.
|
||||
ColorF mAnimationColour; // Current Colour.
|
||||
F32 mAnimationBrightness; // Current Brightness.
|
||||
F32 mAnimationRadius; // Current Radius.
|
||||
Point3F mAnimationPosition; // Current Position.
|
||||
Point3F mAnimationOffset; // Current Offset.
|
||||
F32 mAnimationRotation; // Current Rotation.
|
||||
|
||||
// Elapsed Times.
|
||||
F32 mColourElapsedTime; // Colour Elapsed Time.
|
||||
F32 mBrightnessElapsedTime; // Brightness Elapsed Time.
|
||||
F32 mRadiusElapsedTime; // Radius Elapsed Time.
|
||||
F32 mOffsetElapsedTime; // Offset Elapsed Time.
|
||||
F32 mRotationElapsedTime; // Rotation Elapsed Time.
|
||||
|
||||
// Time Scales.
|
||||
F32 mColourTimeScale; // Colour Time Scale.
|
||||
F32 mBrightnessTimeScale; // Brightness Time Scale.
|
||||
F32 mRadiusTimeScale; // Radius Time Scale.
|
||||
F32 mOffsetTimeScale; // Offset Time Scale.
|
||||
F32 mRotationTimeScale; // Rotation Time Scale.
|
||||
|
||||
// Key Lengths (Validity).
|
||||
U32 mRedKeysLength; // Red Keys Length.
|
||||
U32 mGreenKeysLength; // Green Keys Length.
|
||||
U32 mBlueKeysLength; // Blue Keys Length.
|
||||
U32 mBrightnessKeysLength; // Brightness Keys Length.
|
||||
U32 mRadiusKeysLength; // Radius Keys Length.
|
||||
U32 mOffsetKeysLength; // Offset Keys Length.
|
||||
U32 mRotationKeysLength; // Rotation Keys Length.
|
||||
|
||||
// Declare Console Object.
|
||||
DECLARE_CONOBJECT(fxLight);
|
||||
};
|
||||
|
||||
#endif // _FXLIGHTDB_H_
|
||||
818
engine/game/fx/fxShapeReplicator.cc
Executable file
818
engine/game/fx/fxShapeReplicator.cc
Executable file
@@ -0,0 +1,818 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Written by Melvyn May, Started on 13th March 2002, finished 27th March 2002.
|
||||
//
|
||||
// "My code is written for the Torque community, so do your worst with it,
|
||||
// just don't rip-it-off and call it your own without even thanking me".
|
||||
//
|
||||
// - Melv (working hard to become an associate ... hint).
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "console/simBase.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "fxShapeReplicator.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Put this in /example/common/editor/editor.cs in function [Editor::create()] (around line 66).
|
||||
//
|
||||
// // Ignore Replicated fxStatic Instances.
|
||||
// EWorldEditor.ignoreObjClass("fxShapeReplicatedStatic");
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Put this in /example/common/editor/EditorGui.cs in [function Creator::init( %this )]
|
||||
//
|
||||
// %Environment_Item[8] = "fxShapeReplicator"; <-- ADD THIS.
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Put the function in /example/common/editor/ObjectBuilderGui.gui [around line 458] ...
|
||||
//
|
||||
// function ObjectBuilderGui::buildfxShapeReplicator(%this)
|
||||
// {
|
||||
// %this.className = "fxShapeReplicator";
|
||||
// %this.process();
|
||||
// }
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Put this in /example/common/client/missionDownload.cs in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65)
|
||||
// after codeline 'onPhase2Complete();'.
|
||||
//
|
||||
// StartClientReplication();
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Put this in /engine/console/simBase.h (around line 509) in
|
||||
//
|
||||
// namespace Sim
|
||||
// {
|
||||
// DeclareNamedSet(fxReplicatorSet) <-- ADD THIS (Note no semi-colon).
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Put this in /engine/console/simBase.cc (around line 19) in
|
||||
//
|
||||
// ImplementNamedSet(fxReplicatorSet) <-- ADD THIS
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Put this in /engine/console/simManager.cc [function void init()] (around line 269).
|
||||
//
|
||||
// namespace Sim
|
||||
// {
|
||||
// InstantiateNamedSet(fxReplicatorSet); <-- ADD THIS
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
extern bool gEditingMission;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicator);
|
||||
IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicatedStatic);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxShapeReplicator
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fxShapeReplicator::fxShapeReplicator()
|
||||
{
|
||||
// Setup NetObject.
|
||||
mTypeMask |= StaticObjectType | StaticTSObjectType | StaticRenderedObjectType;
|
||||
mAddedToScene = false;
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
// Reset Client Replication Started.
|
||||
mClientReplicationStarted = false;
|
||||
|
||||
// Reset Shape Count.
|
||||
mCurrentShapeCount = 0;
|
||||
|
||||
// Reset Creation Area Angle Animation.
|
||||
mCreationAreaAngle = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fxShapeReplicator::~fxShapeReplicator()
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::initPersistFields()
|
||||
{
|
||||
// Initialise parents' persistent fields.
|
||||
Parent::initPersistFields();
|
||||
|
||||
// Add out own persistent fields.
|
||||
addGroup( "Debugging" );
|
||||
addField( "HideReplications", TypeBool, Offset( mFieldData.mHideReplications, fxShapeReplicator ) );
|
||||
addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxShapeReplicator ) );
|
||||
addField( "PlacementAreaHeight",TypeS32, Offset( mFieldData.mPlacementBandHeight, fxShapeReplicator ) );
|
||||
addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxShapeReplicator ) );
|
||||
endGroup( "Debugging" );
|
||||
|
||||
addGroup( "Media" );
|
||||
addField( "ShapeFile", TypeFilename, Offset( mFieldData.mShapeFile, fxShapeReplicator ) );
|
||||
endGroup( "Media" );
|
||||
|
||||
addGroup( "Replications" );
|
||||
addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxShapeReplicator ) );
|
||||
addField( "ShapeCount", TypeS32, Offset( mFieldData.mShapeCount, fxShapeReplicator ) );
|
||||
addField( "ShapeRetries", TypeS32, Offset( mFieldData.mShapeRetries, fxShapeReplicator ) );
|
||||
endGroup( "Replications" );
|
||||
|
||||
addGroup( "Placement Radius" );
|
||||
addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxShapeReplicator ) );
|
||||
addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxShapeReplicator ) );
|
||||
addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxShapeReplicator ) );
|
||||
addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxShapeReplicator ) );
|
||||
endGroup( "Placement Radius" );
|
||||
|
||||
addGroup( "Restraints" );
|
||||
addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxShapeReplicator ) );
|
||||
addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxShapeReplicator ) );
|
||||
addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxShapeReplicator ) );
|
||||
addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxShapeReplicator ) );
|
||||
addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxShapeReplicator ) );
|
||||
addField( "AlignToTerrain", TypeBool, Offset( mFieldData.mAlignToTerrain, fxShapeReplicator ) );
|
||||
addField( "Interactions", TypeBool, Offset( mFieldData.mInteractions, fxShapeReplicator ) );
|
||||
addField( "AllowedTerrainSlope",TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxShapeReplicator ) );
|
||||
addField( "TerrainAlignment", TypePoint3F, Offset( mFieldData.mTerrainAlignment, fxShapeReplicator ) );
|
||||
endGroup( "Restraints" );
|
||||
|
||||
addGroup( "Object Transforms" );
|
||||
addField( "FixShapeAspect", TypeBool, Offset( mFieldData.mFixShapeAspect, fxShapeReplicator ) );
|
||||
addField( "ShapeScaleMin", TypePoint3F, Offset( mFieldData.mShapeScaleMin, fxShapeReplicator ) );
|
||||
addField( "ShapeScaleMax", TypePoint3F, Offset( mFieldData.mShapeScaleMax, fxShapeReplicator ) );
|
||||
addField( "ShapeRotateMin", TypePoint3F, Offset( mFieldData.mShapeRotateMin, fxShapeReplicator ) );
|
||||
addField( "ShapeRotateMax", TypePoint3F, Offset( mFieldData.mShapeRotateMax, fxShapeReplicator ) );
|
||||
addField( "OffsetZ", TypeS32, Offset( mFieldData.mOffsetZ, fxShapeReplicator ) );
|
||||
endGroup( "Object Transforms" );
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::CreateShapes(void)
|
||||
{
|
||||
F32 HypX, HypY;
|
||||
F32 Angle;
|
||||
U32 RelocationRetry;
|
||||
Point3F ShapePosition;
|
||||
Point3F ShapeStart;
|
||||
Point3F ShapeEnd;
|
||||
Point3F ShapeScale;
|
||||
EulerF ShapeRotation;
|
||||
QuatF QRotation;
|
||||
bool CollisionResult;
|
||||
RayInfo RayEvent;
|
||||
TSShape* pShape;
|
||||
Container* pContainer;
|
||||
|
||||
|
||||
// Don't create shapes if we are hiding replications.
|
||||
if (mFieldData.mHideReplications) return;
|
||||
|
||||
// Cannot continue without shapes!
|
||||
if (mFieldData.mShapeFile == "") return;
|
||||
|
||||
// Check that we can position somewhere!
|
||||
if (!( mFieldData.mAllowOnTerrain ||
|
||||
mFieldData.mAllowOnInteriors ||
|
||||
mFieldData.mAllowStatics ||
|
||||
mFieldData.mAllowOnWater))
|
||||
{
|
||||
// Problem ...
|
||||
Con::warnf(ConsoleLogEntry::General, "[%s] - Could not place object, All alloweds are off!", getName());
|
||||
|
||||
// Return here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check Shapes.
|
||||
AssertFatal(mCurrentShapeCount==0,"Shapes already present, this should not be possible!")
|
||||
|
||||
// Check that we have a shape...
|
||||
if (!mFieldData.mShapeFile) return;
|
||||
|
||||
// Set Seed.
|
||||
RandomGen.setSeed(mFieldData.mSeed);
|
||||
|
||||
// Set shape vector.
|
||||
mReplicatedShapes.reserve(mFieldData.mShapeCount);
|
||||
|
||||
// Add shapes.
|
||||
for (U32 idx = 0; idx < mFieldData.mShapeCount; idx++)
|
||||
{
|
||||
fxShapeReplicatedStatic* fxStatic;
|
||||
|
||||
// Create our static shape.
|
||||
fxStatic = new fxShapeReplicatedStatic();
|
||||
|
||||
// Set the 'shapeName' field.
|
||||
fxStatic->setField("shapeName", mFieldData.mShapeFile);
|
||||
|
||||
// Is this Replicator on the Server?
|
||||
if (isServerObject())
|
||||
// Yes, so stop it from Ghosting. (Hack, Hack, Hack!)
|
||||
fxStatic->touchNetFlags(Ghostable, false);
|
||||
else
|
||||
// No, so flag as ghost object. (Another damn Hack!)
|
||||
fxStatic->touchNetFlags(IsGhost, true);
|
||||
|
||||
// Register the Object.
|
||||
if (!fxStatic->registerObject())
|
||||
{
|
||||
// Problem ...
|
||||
Con::warnf(ConsoleLogEntry::General, "[%s] - Could not load shape file '%s'!", getName(), mFieldData.mShapeFile);
|
||||
|
||||
// Destroy Shape.
|
||||
delete fxStatic;
|
||||
|
||||
// Destroy existing hapes.
|
||||
DestroyShapes();
|
||||
|
||||
// Quit.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Allocated Shape.
|
||||
pShape = fxStatic->getShape();
|
||||
|
||||
// Reset Relocation Retry.
|
||||
RelocationRetry = mFieldData.mShapeRetries;
|
||||
|
||||
// Find it a home ...
|
||||
do
|
||||
{
|
||||
|
||||
// Calculate a random offset
|
||||
HypX = RandomGen.randF(mFieldData.mInnerRadiusX, mFieldData.mOuterRadiusX);
|
||||
HypY = RandomGen.randF(mFieldData.mInnerRadiusY, mFieldData.mOuterRadiusY);
|
||||
Angle = RandomGen.randF(0, M_2PI);
|
||||
|
||||
// Calculate the new random position (in local space).
|
||||
Point3F randomShapePosLocal;
|
||||
randomShapePosLocal.x = HypX * mCos(Angle);
|
||||
randomShapePosLocal.y = HypY * mSin(Angle);
|
||||
|
||||
// Transform into world space coordinates
|
||||
Point3F shapePosWorld;
|
||||
MatrixF objToWorld = getRenderTransform();
|
||||
objToWorld.mulP(randomShapePosLocal, &shapePosWorld);
|
||||
ShapePosition = shapePosWorld;
|
||||
|
||||
// Initialise RayCast Search Start/End Positions.
|
||||
ShapeStart = ShapeEnd = ShapePosition;
|
||||
ShapeStart.z = 2000.f;
|
||||
ShapeEnd.z= -2000.f;
|
||||
|
||||
// Is this the Server?
|
||||
if (isServerObject())
|
||||
// Perform Ray Cast Collision on Server Terrain.
|
||||
CollisionResult = gServerContainer.castRay(ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent);
|
||||
else
|
||||
// Perform Ray Cast Collision on Client Terrain.
|
||||
CollisionResult = gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent);
|
||||
|
||||
// Did we hit anything?
|
||||
if (CollisionResult)
|
||||
{
|
||||
// For now, let's pretend we didn't get a collision.
|
||||
CollisionResult = false;
|
||||
|
||||
// Yes, so get it's type.
|
||||
U32 CollisionType = RayEvent.object->getTypeMask();
|
||||
|
||||
// Check Illegal Placements.
|
||||
if (((CollisionType & TerrainObjectType) && !mFieldData.mAllowOnTerrain) ||
|
||||
((CollisionType & InteriorObjectType) && !mFieldData.mAllowOnInteriors) ||
|
||||
((CollisionType & StaticTSObjectType) && !mFieldData.mAllowStatics) ||
|
||||
((CollisionType & WaterObjectType) && !mFieldData.mAllowOnWater) ) continue;
|
||||
|
||||
// If we collided with water and are not allowing on the water surface then let's find the
|
||||
// terrain underneath and pass this on as the original collision else fail.
|
||||
//
|
||||
// NOTE:- We need to do this on the server/client as appropriate.
|
||||
if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface)
|
||||
{
|
||||
// Is this the Server?
|
||||
if (isServerObject())
|
||||
{
|
||||
// Yes, so do it on the server container.
|
||||
if (!gServerContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so do it on the client container.
|
||||
if (!gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue;
|
||||
}
|
||||
}
|
||||
|
||||
// We passed with flying colours so carry on.
|
||||
CollisionResult = true;
|
||||
}
|
||||
|
||||
// Invalidate if we are below Allowed Terrain Angle.
|
||||
if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false;
|
||||
|
||||
// Wait until we get a collision.
|
||||
} while(!CollisionResult && --RelocationRetry);
|
||||
|
||||
// Check for Relocation Problem.
|
||||
if (RelocationRetry > 0)
|
||||
{
|
||||
// Adjust Impact point.
|
||||
RayEvent.point.z += mFieldData.mOffsetZ;
|
||||
|
||||
// Set New Position.
|
||||
ShapePosition = RayEvent.point;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Warning.
|
||||
Con::warnf(ConsoleLogEntry::General, "[%s] - Could not find satisfactory position for shape '%s' on %s!", getName(), mFieldData.mShapeFile,isServerObject()?"Server":"Client");
|
||||
|
||||
// Unregister Object.
|
||||
fxStatic->unregisterObject();
|
||||
|
||||
// Destroy Shape.
|
||||
delete fxStatic;
|
||||
|
||||
// Skip to next.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get Shape Transform.
|
||||
MatrixF XForm = fxStatic->getTransform();
|
||||
|
||||
// Are we aligning to Terrain?
|
||||
if (mFieldData.mAlignToTerrain)
|
||||
{
|
||||
// Yes, so set rotation to Terrain Impact Normal.
|
||||
ShapeRotation = RayEvent.normal * mFieldData.mTerrainAlignment;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so choose a new Rotation (in Radians).
|
||||
ShapeRotation.set( mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.x, mFieldData.mShapeRotateMax.x)),
|
||||
mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.y, mFieldData.mShapeRotateMax.y)),
|
||||
mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.z, mFieldData.mShapeRotateMax.z)));
|
||||
}
|
||||
|
||||
// Set Quaternion Roation.
|
||||
QRotation.set(ShapeRotation);
|
||||
|
||||
// Set Transform Rotation.
|
||||
QRotation.setMatrix(&XForm);
|
||||
|
||||
// Set Position.
|
||||
XForm.setColumn(3, ShapePosition);
|
||||
|
||||
// Set Shape Position / Rotation.
|
||||
fxStatic->setTransform(XForm);
|
||||
|
||||
// Choose a new Scale.
|
||||
F32 scaleX, scaleY, scaleZ;
|
||||
|
||||
// Choose a random X-Scale.
|
||||
scaleX = RandomGen.randF(mFieldData.mShapeScaleMin.x, mFieldData.mShapeScaleMax.x);
|
||||
|
||||
// Fix Shape Aspect?
|
||||
if ( mFieldData.mFixShapeAspect )
|
||||
{
|
||||
// Yes, so use Scale-X for all axis.
|
||||
scaleY = scaleZ = scaleX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so choose Scale-Y/Z.
|
||||
scaleY = RandomGen.randF(mFieldData.mShapeScaleMin.y, mFieldData.mShapeScaleMax.y);
|
||||
scaleZ = RandomGen.randF(mFieldData.mShapeScaleMin.z, mFieldData.mShapeScaleMax.z);
|
||||
}
|
||||
|
||||
// Set Shape Scale.
|
||||
ShapeScale.set( scaleX, scaleY, scaleZ );
|
||||
|
||||
// Set Shape Scale.
|
||||
fxStatic->setScale(ShapeScale);
|
||||
|
||||
// Lock it.
|
||||
fxStatic->setLocked(true);
|
||||
|
||||
// Store Shape in Replicated Shapes Vector.
|
||||
mReplicatedShapes[mCurrentShapeCount++] = fxStatic;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::DestroyShapes(void)
|
||||
{
|
||||
// Finish if we didn't create any shapes.
|
||||
if (mCurrentShapeCount == 0) return;
|
||||
|
||||
// Remove shapes.
|
||||
for (U32 idx = 0; idx < mCurrentShapeCount; idx++)
|
||||
{
|
||||
fxShapeReplicatedStatic* fxStatic;
|
||||
|
||||
// Fetch the Shape Object.
|
||||
fxStatic = mReplicatedShapes[idx];
|
||||
|
||||
// Got a Shape?
|
||||
if (fxStatic)
|
||||
{
|
||||
// Unlock it.
|
||||
fxStatic->setLocked(false);
|
||||
|
||||
// Unregister the object.
|
||||
fxStatic->unregisterObject();
|
||||
|
||||
// Delete it.
|
||||
delete fxStatic;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty the Replicated Shapes Vector.
|
||||
mReplicatedShapes.empty();
|
||||
|
||||
// Reset Shape Count.
|
||||
mCurrentShapeCount = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::RenewShapes(void)
|
||||
{
|
||||
// Destroy any shapes.
|
||||
DestroyShapes();
|
||||
|
||||
// Don't create shapes on the Server if we don't need interactions.
|
||||
if (isServerObject() && !mFieldData.mInteractions) return;
|
||||
|
||||
// Create Shapes.
|
||||
CreateShapes();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::StartUp(void)
|
||||
{
|
||||
// Flag, Client Replication Started.
|
||||
mClientReplicationStarted = true;
|
||||
|
||||
// Renew shapes.
|
||||
RenewShapes();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool fxShapeReplicator::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd()) return(false);
|
||||
|
||||
// Add the Replicator to the Replicator Set.
|
||||
dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"))->addObject(this);
|
||||
|
||||
// Set Default Object Box.
|
||||
mObjBox.min.set( -0.5, -0.5, -0.5 );
|
||||
mObjBox.max.set( 0.5, 0.5, 0.5 );
|
||||
|
||||
// Reset the World Box.
|
||||
resetWorldBox();
|
||||
|
||||
// Are we editing the Mission?
|
||||
if(gEditingMission)
|
||||
{
|
||||
// Yes, so set the Render Transform.
|
||||
setRenderTransform(mObjToWorld);
|
||||
|
||||
// Add to Scene.
|
||||
addToScene();
|
||||
mAddedToScene = true;
|
||||
|
||||
// If we are in the editor and we are on the client then
|
||||
// we can manually startup replication.
|
||||
if (isClientObject()) mClientReplicationStarted = true;
|
||||
}
|
||||
|
||||
// Renew shapes on Server.
|
||||
if (isServerObject()) RenewShapes();
|
||||
|
||||
// Return OK.
|
||||
return(true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::onRemove()
|
||||
{
|
||||
// Remove the Replicator from the Replicator Set.
|
||||
dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"))->removeObject(this);
|
||||
|
||||
// Are we editing the Mission?
|
||||
if(gEditingMission)
|
||||
{
|
||||
// Yes, so remove from Scene.
|
||||
removeFromScene();
|
||||
mAddedToScene = false;
|
||||
}
|
||||
|
||||
// Destroy Shapes.
|
||||
DestroyShapes();
|
||||
|
||||
// Do Parent.
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::inspectPostApply()
|
||||
{
|
||||
// Set Parent.
|
||||
Parent::inspectPostApply();
|
||||
|
||||
// Renew Shapes.
|
||||
RenewShapes();
|
||||
|
||||
// Set Replication Mask.
|
||||
setMaskBits(ReplicationMask);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::onEditorEnable()
|
||||
{
|
||||
// Are we in the Scene?
|
||||
if(!mAddedToScene)
|
||||
{
|
||||
// No, so add to scene.
|
||||
addToScene();
|
||||
mAddedToScene = true;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::onEditorDisable()
|
||||
{
|
||||
// Are we in the Scene?
|
||||
if(mAddedToScene)
|
||||
{
|
||||
// Yes, so remove from scene.
|
||||
removeFromScene();
|
||||
mAddedToScene = false;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ConsoleFunction(StartClientReplication, void, 1, 1, "StartClientReplication()")
|
||||
{
|
||||
// Find the Replicator Set.
|
||||
SimSet *fxReplicatorSet = dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"));
|
||||
|
||||
// Return if Error.
|
||||
if (!fxReplicatorSet) return;
|
||||
|
||||
// StartUp Replication Object.
|
||||
for (SimSetIterator itr(fxReplicatorSet); *itr; ++itr)
|
||||
{
|
||||
// Fetch the Replicator Object.
|
||||
fxShapeReplicator* Replicator = static_cast<fxShapeReplicator*>(*itr);
|
||||
// Start Client Objects Only.
|
||||
if (Replicator->isClientObject()) Replicator->StartUp();
|
||||
}
|
||||
// Info ...
|
||||
Con::printf("Client Replication Startup has Happened!");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool fxShapeReplicator::prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone,
|
||||
const bool modifyBaseZoneState)
|
||||
{
|
||||
// Return if last state.
|
||||
if (isLastState(state, stateKey)) return false;
|
||||
// Set Last State.
|
||||
setLastState(state, stateKey);
|
||||
|
||||
// Is Object Rendered?
|
||||
if (state->isObjectRendered(this))
|
||||
{
|
||||
// Yes, so get a SceneRenderImage.
|
||||
SceneRenderImage* image = new SceneRenderImage;
|
||||
// Populate it.
|
||||
image->obj = this;
|
||||
image->sortType = SceneRenderImage::Normal;
|
||||
// Insert it into the scene images.
|
||||
state->insertRenderImage(image);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::renderObject(SceneState* state, SceneRenderImage*)
|
||||
{
|
||||
// Return if placement area not needed.
|
||||
if (!mFieldData.mShowPlacementArea) return;
|
||||
|
||||
// Check we are in Canonical State.
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
||||
|
||||
// Setup out the Projection Matrix/Viewport.
|
||||
RectI viewport;
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
dglGetViewport(&viewport);
|
||||
state->setupBaseProjection();
|
||||
|
||||
// Setup our rendering state.
|
||||
glPushMatrix();
|
||||
dglMultMatrix(&getTransform());
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Do we need to draw the Inner Radius?
|
||||
if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY)
|
||||
{
|
||||
// Yes, so draw Inner Radius.
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++)
|
||||
{
|
||||
F32 XPos, YPos;
|
||||
|
||||
// Calculate Position.
|
||||
XPos = mFieldData.mInnerRadiusX * mCos(mDegToRad(-(F32)Angle));
|
||||
YPos = mFieldData.mInnerRadiusY * mSin(mDegToRad(-(F32)Angle));
|
||||
|
||||
// Set Colour.
|
||||
glColor4f( mFieldData.mPlaceAreaColour.red,
|
||||
mFieldData.mPlaceAreaColour.green,
|
||||
mFieldData.mPlaceAreaColour.blue,
|
||||
AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle));
|
||||
|
||||
// Draw Arc Line.
|
||||
glVertex3f( XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f);
|
||||
glVertex3f( XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f);
|
||||
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// Do we need to draw the Outer Radius?
|
||||
if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY)
|
||||
{
|
||||
// Yes, so draw Outer Radius.
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++)
|
||||
{
|
||||
F32 XPos, YPos;
|
||||
|
||||
// Calculate Position.
|
||||
XPos = mFieldData.mOuterRadiusX * mCos(mDegToRad(-(F32)Angle));
|
||||
YPos = mFieldData.mOuterRadiusY * mSin(mDegToRad(-(F32)Angle));
|
||||
|
||||
// Set Colour.
|
||||
glColor4f( mFieldData.mPlaceAreaColour.red,
|
||||
mFieldData.mPlaceAreaColour.green,
|
||||
mFieldData.mPlaceAreaColour.blue,
|
||||
AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle));
|
||||
|
||||
// Draw Arc Line.
|
||||
glVertex3f( XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f);
|
||||
glVertex3f( XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f);
|
||||
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// Restore rendering state.
|
||||
glDisable(GL_BLEND);
|
||||
glPopMatrix();
|
||||
|
||||
// Animate Area Selection.
|
||||
mCreationAreaAngle += 20;
|
||||
mCreationAreaAngle = mCreationAreaAngle % 360;
|
||||
|
||||
// Restore out nice and friendly canonical state.
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
dglSetViewport(viewport);
|
||||
|
||||
// Check we have restored Canonical State.
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
U32 fxShapeReplicator::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
|
||||
{
|
||||
// Pack Parent.
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
// Write Replication Flag.
|
||||
if (stream->writeFlag(mask & ReplicationMask))
|
||||
{
|
||||
stream->writeAffineTransform(mObjToWorld); // Replicator Position.
|
||||
|
||||
stream->writeInt(mFieldData.mSeed, 32); // Replicator Seed.
|
||||
stream->writeInt(mFieldData.mShapeCount, 32); // Shapes Count.
|
||||
stream->writeInt(mFieldData.mShapeRetries, 32); // Shapes Retries.
|
||||
stream->writeString(mFieldData.mShapeFile);
|
||||
stream->writeInt(mFieldData.mInnerRadiusX, 32); // Shapes Inner Radius X.
|
||||
stream->writeInt(mFieldData.mInnerRadiusY, 32); // Shapes Inner Radius Y.
|
||||
stream->writeInt(mFieldData.mOuterRadiusX, 32); // Shapes Outer Radius X.
|
||||
stream->writeInt(mFieldData.mOuterRadiusY, 32); // Shapes Outer Radius Y.
|
||||
mathWrite(*stream, mFieldData.mShapeScaleMin); // Shapes Scale Min.
|
||||
mathWrite(*stream, mFieldData.mShapeScaleMax); // Shapes Scale Max.
|
||||
mathWrite(*stream, mFieldData.mShapeRotateMin); // Shapes Rotate Min.
|
||||
mathWrite(*stream, mFieldData.mShapeRotateMax); // Shapes Rotate Max.
|
||||
stream->writeSignedInt(mFieldData.mOffsetZ, 32); // Shapes Offset Z.
|
||||
stream->writeFlag(mFieldData.mAllowOnTerrain); // Allow on Terrain.
|
||||
stream->writeFlag(mFieldData.mAllowOnInteriors); // Allow on Interiors.
|
||||
stream->writeFlag(mFieldData.mAllowStatics); // Allow on Statics.
|
||||
stream->writeFlag(mFieldData.mAllowOnWater); // Allow on Water.
|
||||
stream->writeFlag(mFieldData.mAllowWaterSurface); // Allow on Water Surface.
|
||||
stream->writeSignedInt(mFieldData.mAllowedTerrainSlope, 32); // Shapes Offset Z.
|
||||
stream->writeFlag(mFieldData.mAlignToTerrain); // Shapes AlignToTerrain.
|
||||
mathWrite(*stream, mFieldData.mTerrainAlignment); // Write Terrain Alignment.
|
||||
stream->writeFlag(mFieldData.mHideReplications); // Hide Replications.
|
||||
stream->writeFlag(mFieldData.mInteractions); // Shape Interactions.
|
||||
stream->writeFlag(mFieldData.mShowPlacementArea); // Show Placement Area Flag.
|
||||
stream->writeInt(mFieldData.mPlacementBandHeight, 32); // Placement Area Height.
|
||||
stream->write(mFieldData.mPlaceAreaColour);
|
||||
}
|
||||
|
||||
// Were done ...
|
||||
return(retMask);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::unpackUpdate(NetConnection * con, BitStream * stream)
|
||||
{
|
||||
// Unpack Parent.
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
// Read Replication Details.
|
||||
if(stream->readFlag())
|
||||
{
|
||||
MatrixF ReplicatorObjectMatrix;
|
||||
|
||||
stream->readAffineTransform(&ReplicatorObjectMatrix); // Replication Position.
|
||||
|
||||
mFieldData.mSeed = stream->readInt(32); // Replicator Seed.
|
||||
mFieldData.mShapeCount = stream->readInt(32); // Shapes Count.
|
||||
mFieldData.mShapeRetries = stream->readInt(32); // Shapes Retries.
|
||||
mFieldData.mShapeFile = stream->readSTString(); // Shape File.
|
||||
mFieldData.mInnerRadiusX = stream->readInt(32); // Shapes Inner Radius X.
|
||||
mFieldData.mInnerRadiusY = stream->readInt(32); // Shapes Inner Radius Y.
|
||||
mFieldData.mOuterRadiusX = stream->readInt(32); // Shapes Outer Radius X.
|
||||
mFieldData.mOuterRadiusY = stream->readInt(32); // Shapes Outer Radius Y.
|
||||
mathRead(*stream, &mFieldData.mShapeScaleMin); // Shapes Scale Min.
|
||||
mathRead(*stream, &mFieldData.mShapeScaleMax); // Shapes Scale Max.
|
||||
mathRead(*stream, &mFieldData.mShapeRotateMin); // Shapes Rotate Min.
|
||||
mathRead(*stream, &mFieldData.mShapeRotateMax); // Shapes Rotate Max.
|
||||
mFieldData.mOffsetZ = stream->readSignedInt(32); // Shapes Offset Z.
|
||||
mFieldData.mAllowOnTerrain = stream->readFlag(); // Allow on Terrain.
|
||||
mFieldData.mAllowOnInteriors = stream->readFlag(); // Allow on Interiors.
|
||||
mFieldData.mAllowStatics = stream->readFlag(); // Allow on Statics.
|
||||
mFieldData.mAllowOnWater = stream->readFlag(); // Allow on Water.
|
||||
mFieldData.mAllowWaterSurface = stream->readFlag(); // Allow on Water Surface.
|
||||
mFieldData.mAllowedTerrainSlope = stream->readSignedInt(32); // Allowed Terrain Slope.
|
||||
mFieldData.mAlignToTerrain = stream->readFlag(); // Read AlignToTerrain.
|
||||
mathRead(*stream, &mFieldData.mTerrainAlignment); // Read Terrain Alignment.
|
||||
mFieldData.mHideReplications = stream->readFlag(); // Hide Replications.
|
||||
mFieldData.mInteractions = stream->readFlag(); // Read Interactions.
|
||||
mFieldData.mShowPlacementArea = stream->readFlag(); // Show Placement Area Flag.
|
||||
mFieldData.mPlacementBandHeight = stream->readInt(32); // Placement Area Height.
|
||||
stream->read(&mFieldData.mPlaceAreaColour);
|
||||
|
||||
// Set Transform.
|
||||
setTransform(ReplicatorObjectMatrix);
|
||||
|
||||
// Renew Shapes (only if replication has started).
|
||||
if (mClientReplicationStarted) RenewShapes();
|
||||
}
|
||||
}
|
||||
|
||||
178
engine/game/fx/fxShapeReplicator.h
Executable file
178
engine/game/fx/fxShapeReplicator.h
Executable file
@@ -0,0 +1,178 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Written by Melvyn May, 13th March 2002.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SHAPEREPLICATOR_H_
|
||||
#define _SHAPEREPLICATOR_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _TSSTATIC_H_
|
||||
#include "game/tsStatic.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#endif
|
||||
|
||||
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
|
||||
|
||||
#define FXREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticObjectType | \
|
||||
WaterObjectType )
|
||||
|
||||
#define FXREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticObjectType )
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxShapeReplicatedStatic
|
||||
//------------------------------------------------------------------------------
|
||||
class fxShapeReplicatedStatic : public TSStatic
|
||||
{
|
||||
private:
|
||||
typedef SceneObject Parent;
|
||||
|
||||
public:
|
||||
fxShapeReplicatedStatic() {};
|
||||
~fxShapeReplicatedStatic() {};
|
||||
void touchNetFlags(const U32 m, bool setflag = true) { if (setflag) mNetFlags.set(m); else mNetFlags.clear(m); };
|
||||
TSShape* getShape(void) { return mShapeInstance->getShape(); };
|
||||
void setTransform(const MatrixF & mat) { Parent::setTransform(mat); setRenderTransform(mat); };
|
||||
|
||||
DECLARE_CONOBJECT(fxShapeReplicatedStatic);
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxShapeReplicator
|
||||
//------------------------------------------------------------------------------
|
||||
class fxShapeReplicator : public SceneObject
|
||||
{
|
||||
private:
|
||||
typedef SceneObject Parent;
|
||||
|
||||
protected:
|
||||
|
||||
void CreateShapes(void);
|
||||
void DestroyShapes(void);
|
||||
void RenewShapes(void);
|
||||
|
||||
enum { ReplicationMask = (1 << 0) };
|
||||
|
||||
U32 mCreationAreaAngle;
|
||||
bool mClientReplicationStarted;
|
||||
bool mAddedToScene;
|
||||
U32 mCurrentShapeCount;
|
||||
Vector<fxShapeReplicatedStatic*> mReplicatedShapes;
|
||||
MRandomLCG RandomGen;
|
||||
|
||||
|
||||
public:
|
||||
fxShapeReplicator();
|
||||
~fxShapeReplicator();
|
||||
|
||||
|
||||
void StartUp(void);
|
||||
void ShowReplication(void);
|
||||
void HideReplication(void);
|
||||
|
||||
// SceneObject
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
virtual bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone,
|
||||
const bool modifyBaseZoneState = false);
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void onEditorEnable();
|
||||
void onEditorDisable();
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// ConObject.
|
||||
static void initPersistFields();
|
||||
|
||||
// Field Data.
|
||||
class tagFieldData
|
||||
{
|
||||
public:
|
||||
|
||||
U32 mSeed;
|
||||
StringTableEntry mShapeFile;
|
||||
U32 mShapeCount;
|
||||
U32 mShapeRetries;
|
||||
bool mFixShapeAspect;
|
||||
Point3F mShapeScaleMin;
|
||||
Point3F mShapeScaleMax;
|
||||
Point3F mShapeRotateMin;
|
||||
Point3F mShapeRotateMax;
|
||||
U32 mInnerRadiusX;
|
||||
U32 mInnerRadiusY;
|
||||
U32 mOuterRadiusX;
|
||||
U32 mOuterRadiusY;
|
||||
S32 mOffsetZ;
|
||||
bool mAllowOnTerrain;
|
||||
bool mAllowOnInteriors;
|
||||
bool mAllowStatics;
|
||||
bool mAllowOnWater;
|
||||
S32 mAllowedTerrainSlope;
|
||||
bool mAlignToTerrain;
|
||||
bool mAllowWaterSurface;
|
||||
Point3F mTerrainAlignment;
|
||||
bool mInteractions;
|
||||
bool mHideReplications;
|
||||
bool mShowPlacementArea;
|
||||
U32 mPlacementBandHeight;
|
||||
ColorF mPlaceAreaColour;
|
||||
|
||||
tagFieldData()
|
||||
{
|
||||
// Set Defaults.
|
||||
mSeed = 1376312589;
|
||||
mShapeFile = StringTable->insert("");
|
||||
mShapeCount = 10;
|
||||
mShapeRetries = 100;
|
||||
mInnerRadiusX = 0;
|
||||
mInnerRadiusY = 0;
|
||||
mOuterRadiusX = 100;
|
||||
mOuterRadiusY = 100;
|
||||
mOffsetZ = 0;
|
||||
|
||||
mAllowOnTerrain = true;
|
||||
mAllowOnInteriors = true;
|
||||
mAllowStatics = true;
|
||||
mAllowOnWater = false;
|
||||
mAllowWaterSurface = false;
|
||||
mAllowedTerrainSlope= 90;
|
||||
mAlignToTerrain = false;
|
||||
mInteractions = true;
|
||||
|
||||
mHideReplications = false;
|
||||
|
||||
mShowPlacementArea = true;
|
||||
mPlacementBandHeight = 25;
|
||||
mPlaceAreaColour .set(0.4f, 0, 0.8f);
|
||||
|
||||
mFixShapeAspect = false;
|
||||
mShapeScaleMin .set(1, 1, 1);
|
||||
mShapeScaleMax .set(1, 1, 1);
|
||||
mShapeRotateMin .set(0, 0, 0);
|
||||
mShapeRotateMax .set(0, 0, 0);
|
||||
mTerrainAlignment .set(1, 1, 1);
|
||||
}
|
||||
|
||||
} mFieldData;
|
||||
|
||||
// Declare Console Object.
|
||||
DECLARE_CONOBJECT(fxShapeReplicator);
|
||||
};
|
||||
|
||||
#endif // _SHAPEREPLICATOR_H_
|
||||
2172
engine/game/fx/fxSunLight.cc
Executable file
2172
engine/game/fx/fxSunLight.cc
Executable file
File diff suppressed because it is too large
Load Diff
252
engine/game/fx/fxSunLight.h
Executable file
252
engine/game/fx/fxSunLight.h
Executable file
@@ -0,0 +1,252 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine - fxLight
|
||||
//
|
||||
// Written by Melvyn May, 27th August 2002.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FXSUNLIGHT_H_
|
||||
#define _FXSUNLIGHT_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxSunLight
|
||||
//------------------------------------------------------------------------------
|
||||
class fxSunLight : public SceneObject
|
||||
{
|
||||
private:
|
||||
typedef SceneObject Parent;
|
||||
|
||||
U32 CheckKeySyntax(StringTableEntry Key);
|
||||
void CheckAnimationKeys(void);
|
||||
F32 GetLerpKey(StringTableEntry Key, U32 PosFrom, U32 PosTo, F32 ValueFrom, F32 ValueTo, F32 Lerp);
|
||||
void AnimateSun(F32 ElapsedTime);
|
||||
void InitialiseAnimation(void);
|
||||
void ResetAnimation(void);
|
||||
bool TestLOS(const Point3F& Pos);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
enum { fxSunLightConfigChangeMask = BIT(0) };
|
||||
|
||||
bool mAddedToScene;
|
||||
|
||||
MRandomLCG RandomGen;
|
||||
|
||||
// Textures.
|
||||
TextureHandle mLocalFlareTextureHandle;
|
||||
TextureHandle mRemoteFlareTextureHandle;
|
||||
|
||||
F32 mElapsedTime;
|
||||
S32 mLastRenderTime;
|
||||
F32 mLocalFlareScale;
|
||||
Point3F mSunlightPosition; // Sunlight Frame Position.
|
||||
|
||||
public:
|
||||
fxSunLight();
|
||||
~fxSunLight();
|
||||
|
||||
// *********************************
|
||||
// Configuration Interface.
|
||||
// *********************************
|
||||
|
||||
// Debugging.
|
||||
void setEnable(bool Status);
|
||||
|
||||
// Media.
|
||||
void setFlareBitmaps(const char* LocalName, const char* RemoteName);
|
||||
|
||||
// Sun Orbit.
|
||||
void setSunAzimuth(F32 Azimuth);
|
||||
void setSunElevation(F32 Elevation);
|
||||
|
||||
// Flare.
|
||||
void setFlareTP(bool Status);
|
||||
void setFlareColour(ColorF Colour);
|
||||
void setFlareBrightness(F32 Brightness);
|
||||
void setFlareSize(F32 Size);
|
||||
void setFadeTime(F32 Time);
|
||||
void setBlendMode(U32 Mode);
|
||||
|
||||
// Animation Options.
|
||||
void setUseColour(bool Status);
|
||||
void setUseBrightness(bool Status);
|
||||
void setUseRotation(bool Status);
|
||||
void setUseSize(bool Status);
|
||||
void setUseAzimuth(bool Status);
|
||||
void setUseElevation(bool Status);
|
||||
void setLerpColour(bool Status);
|
||||
void setLerpBrightness(bool Status);
|
||||
void setLerpRotation(bool Status);
|
||||
void setLerpSize(bool Status);
|
||||
void setLerpAzimuth(bool Status);
|
||||
void setLerpElevation(bool Status);
|
||||
void setLinkFlareSize(bool Status);
|
||||
void setSingleColourKeys(bool Status);
|
||||
|
||||
// Animation Extents.
|
||||
void setMinColour(ColorF Colour);
|
||||
void setMaxColour(ColorF Colour);
|
||||
void setMinBrightness(F32 Brightness);
|
||||
void setMaxBrightness(F32 Brightness);
|
||||
void setMinRotation(F32 Rotation);
|
||||
void setMaxRotation(F32 Rotation);
|
||||
void setMinSize(F32 Size);
|
||||
void setMaxSize(F32 Size);
|
||||
void setMinAzimuth(F32 Azimuth);
|
||||
void setMaxAzimuth(F32 Azimuth);
|
||||
void setMinElevation(F32 Elevation);
|
||||
void setMaxElevation(F32 Elevation);
|
||||
|
||||
// Animation Keys.
|
||||
void setRedKeys(const char* Keys);
|
||||
void setGreenKeys(const char* Keys);
|
||||
void setBlueKeys(const char* Keys);
|
||||
void setBrightnessKeys(const char* Keys);
|
||||
void setRotationKeys(const char* Keys);
|
||||
void setSizeKeys(const char* Keys);
|
||||
void setAzimuthKeys(const char* Keys);
|
||||
void setElevationKeys(const char* Keys);
|
||||
|
||||
// Animation Times.
|
||||
void setColourTime(F32 Time);
|
||||
void setBrightnessTime(F32 Time);
|
||||
void setRotationTime(F32 Time);
|
||||
void setSizeTime(F32 Time);
|
||||
void setAzimuthTime(F32 Time);
|
||||
void setElevationTime(F32 Time);
|
||||
|
||||
// Misc,
|
||||
void reset(void);
|
||||
|
||||
// SceneObject
|
||||
bool prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// ConObject.
|
||||
static void initPersistFields();
|
||||
|
||||
bool mDoneSunLock;
|
||||
|
||||
// Field Data.
|
||||
|
||||
// Debugging.
|
||||
bool mEnable; // Light Enable.
|
||||
|
||||
// Media.
|
||||
StringTableEntry mLocalFlareTextureName; // Local Flare Texture Name.
|
||||
StringTableEntry mRemoteFlareTextureName; // Remote Flare Texture Name.
|
||||
|
||||
// Sun Orbit.
|
||||
F32 mSunAzimuth;
|
||||
F32 mSunElevation;
|
||||
bool mLockToRealSun; // Lock to Real Sun Flag.
|
||||
|
||||
// Flare.
|
||||
bool mFlareTP; // Flare Third Person Flag.
|
||||
ColorF mFlareColour; // Flare Colour.
|
||||
F32 mFlareBrightness; // Brightness.
|
||||
F32 mFlareSize; // Flare Size.
|
||||
F32 mFadeTime; // Fade Time.
|
||||
U32 mBlendMode; // Blend Mode.
|
||||
|
||||
// Animation Options.
|
||||
bool mUseColour; // Use Colour Flag.
|
||||
bool mUseBrightness; // Use Brightness Flag.
|
||||
bool mUseRotation; // Use Rotation Flag.
|
||||
bool mUseSize; // Use Size Flag.
|
||||
bool mUseAzimuth; // Use Azimuth Flag.
|
||||
bool mUseElevation; // Use Elevation Flag.
|
||||
bool mLerpColour; // Lerp Colour Flag.
|
||||
bool mLerpBrightness; // Lerp Brightness Flag.
|
||||
bool mLerpRotation; // Lerp Rotation Flag.
|
||||
bool mLerpSize; // Lerp Size Flag.
|
||||
bool mLerpAzimuth; // Lerp Azimuth Flag.
|
||||
bool mLerpElevation; // Lerp Elevation Flag.
|
||||
bool mLinkFlareSize; // Link Flare Size Animation.
|
||||
bool mSingleColourKeys; // Single-Channel Colour Keys.
|
||||
|
||||
// Animation Extents.
|
||||
ColorF mMinColour; // Minimum Colour.
|
||||
ColorF mMaxColour; // Maximum Colour.
|
||||
F32 mMinBrightness; // Minimum Brightness.
|
||||
F32 mMaxBrightness; // Maximum Brightness.
|
||||
F32 mMinRotation; // Minimum Rotation.
|
||||
F32 mMaxRotation; // Maximum Rotation.
|
||||
F32 mMinSize; // Minimum Size.
|
||||
F32 mMaxSize; // Maximum Size.
|
||||
F32 mMinAzimuth; // Minimum Azimuth.
|
||||
F32 mMaxAzimuth; // Maximum Azimuth.
|
||||
F32 mMinElevation; // Minimum Elevation.
|
||||
F32 mMaxElevation; // Maximum Elevation.
|
||||
|
||||
// Animation Keys.
|
||||
StringTableEntry mRedKeys; // Red Animation Keys.
|
||||
StringTableEntry mGreenKeys; // Green Animation Keys.
|
||||
StringTableEntry mBlueKeys; // Blue Animation Keys.
|
||||
StringTableEntry mBrightnessKeys; // Brightness Animation Keys.
|
||||
StringTableEntry mRotationKeys; // Rotation Animation Keys.
|
||||
StringTableEntry mSizeKeys; // Size Animation Keys.
|
||||
StringTableEntry mAzimuthKeys; // Size Azimuth Keys.
|
||||
StringTableEntry mElevationKeys; // Size Elevation Keys.
|
||||
|
||||
// Animation Times.
|
||||
F32 mColourTime; // Colour Time (Seconds).
|
||||
F32 mBrightnessTime; // Brightness Time.
|
||||
F32 mRotationTime; // Rotation Time.
|
||||
F32 mSizeTime; // Size Time.
|
||||
F32 mAzimuthTime; // Azimuth Time.
|
||||
F32 mElevationTime; // Elevation Time.
|
||||
|
||||
// Current Animation.
|
||||
ColorF mAnimationColour; // Current Colour.
|
||||
F32 mAnimationBrightness; // Current Brightness.
|
||||
F32 mAnimationRotation; // Current Rotation.
|
||||
F32 mAnimationSize; // Current Size.
|
||||
F32 mAnimationAzimuth; // Current Azimuth.
|
||||
F32 mAnimationElevation; // Current Elevation.
|
||||
|
||||
// Elapsed Times.
|
||||
F32 mColourElapsedTime; // Colour Elapsed Time.
|
||||
F32 mBrightnessElapsedTime; // Brightness Elapsed Time.
|
||||
F32 mRotationElapsedTime; // Rotation Elapsed Time.
|
||||
F32 mSizeElapsedTime; // Size Elapsed Time.
|
||||
F32 mAzimuthElapsedTime; // Azimuth Elapsed Time.
|
||||
F32 mElevationElapsedTime; // Elevation Elapsed Time.
|
||||
|
||||
// Time Scales.
|
||||
F32 mColourTimeScale; // Colour Time Scale.
|
||||
F32 mBrightnessTimeScale; // Brightness Time Scale.
|
||||
F32 mRotationTimeScale; // Rotation Time Scale.
|
||||
F32 mSizeTimeScale; // Size Time Scale.
|
||||
F32 mAzimuthTimeScale; // Azimuth Time Scale.
|
||||
F32 mElevationTimeScale; // Elevation Time Scale.
|
||||
|
||||
// Key Lengths (Validity).
|
||||
U32 mRedKeysLength; // Red Keys Length.
|
||||
U32 mGreenKeysLength; // Green Keys Length.
|
||||
U32 mBlueKeysLength; // Blue Keys Length.
|
||||
U32 mBrightnessKeysLength; // Brightness Keys Length.
|
||||
U32 mRotationKeysLength; // Rotation Keys Length.
|
||||
U32 mSizeKeysLength; // Size Keys Length.
|
||||
U32 mAzimuthKeysLength; // Azimuth Keys Length.
|
||||
U32 mElevationKeysLength; // Elevation Keys Length.
|
||||
|
||||
// Declare Console Object.
|
||||
DECLARE_CONOBJECT(fxSunLight);
|
||||
};
|
||||
|
||||
#endif // _FXSUNLIGHT_H_
|
||||
1226
engine/game/fx/lightning.cc
Executable file
1226
engine/game/fx/lightning.cc
Executable file
File diff suppressed because it is too large
Load Diff
214
engine/game/fx/lightning.h
Executable file
214
engine/game/fx/lightning.h
Executable file
@@ -0,0 +1,214 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _LIGHTNING_H_
|
||||
#define _LIGHTNING_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
#ifndef _GTEXMANAGER_H_
|
||||
#include "dgl/gTexManager.h"
|
||||
#endif
|
||||
#ifndef _LLIST_H_
|
||||
#include "core/llist.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
class ShapeBase;
|
||||
class LightningStrikeEvent;
|
||||
class AudioProfile;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
class LightningData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
public:
|
||||
enum Constants {
|
||||
MaxThunders = 8,
|
||||
MaxTextures = 8
|
||||
};
|
||||
|
||||
//-------------------------------------- Console set variables
|
||||
public:
|
||||
AudioProfile* thunderSounds[MaxThunders];
|
||||
AudioProfile* strikeSound;
|
||||
StringTableEntry strikeTextureNames[MaxTextures];
|
||||
|
||||
//-------------------------------------- load set variables
|
||||
public:
|
||||
S32 thunderSoundIds[MaxThunders];
|
||||
S32 strikeSoundID;
|
||||
|
||||
TextureHandle strikeTextures[MaxTextures];
|
||||
U32 numThunders;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
|
||||
|
||||
public:
|
||||
LightningData();
|
||||
~LightningData();
|
||||
|
||||
void packData(BitStream*);
|
||||
void unpackData(BitStream*);
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
|
||||
DECLARE_CONOBJECT(LightningData);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
struct LightningBolt
|
||||
{
|
||||
|
||||
struct Node
|
||||
{
|
||||
Point3F point;
|
||||
VectorF dirToMainLine;
|
||||
};
|
||||
|
||||
struct NodeManager
|
||||
{
|
||||
Node nodeList[10];
|
||||
|
||||
Point3F startPoint;
|
||||
Point3F endPoint;
|
||||
U32 numNodes;
|
||||
F32 maxAngle;
|
||||
|
||||
void generateNodes();
|
||||
};
|
||||
|
||||
NodeManager mMajorNodes;
|
||||
Vector< NodeManager > mMinorNodes;
|
||||
LList< LightningBolt > splitList;
|
||||
|
||||
F32 lifetime;
|
||||
F32 elapsedTime;
|
||||
F32 fadeTime;
|
||||
bool isFading;
|
||||
F32 percentFade;
|
||||
bool startRender;
|
||||
F32 renderTime;
|
||||
|
||||
F32 width;
|
||||
F32 chanceOfSplit;
|
||||
Point3F startPoint;
|
||||
Point3F endPoint;
|
||||
|
||||
U32 numMajorNodes;
|
||||
F32 maxMajorAngle;
|
||||
U32 numMinorNodes;
|
||||
F32 maxMinorAngle;
|
||||
|
||||
LightningBolt();
|
||||
~LightningBolt();
|
||||
|
||||
void createSplit( Point3F startPoint, Point3F endPoint, U32 depth, F32 width );
|
||||
F32 findHeight( Point3F &point, SceneGraph* sceneManager );
|
||||
void render( const Point3F &camPos );
|
||||
void renderSegment( NodeManager &segment, const Point3F &camPos, bool renderLastPoint );
|
||||
void generate();
|
||||
void generateMinorNodes();
|
||||
void startSplits();
|
||||
void update( F32 dt );
|
||||
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
class Lightning : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
|
||||
struct Strike {
|
||||
F32 xVal; // Position in cloud layer of strike
|
||||
F32 yVal; // top
|
||||
|
||||
bool targetedStrike; // Is this a targeted strike?
|
||||
U32 targetGID;
|
||||
|
||||
F32 deathAge; // Age at which this strike expires
|
||||
F32 currentAge; // Current age of this strike (updated by advanceTime)
|
||||
|
||||
LightningBolt bolt[3];
|
||||
|
||||
Strike* next;
|
||||
};
|
||||
struct Thunder {
|
||||
F32 tRemaining;
|
||||
Thunder* next;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
//-------------------------------------- Console set variables
|
||||
public:
|
||||
|
||||
U32 strikesPerMinute;
|
||||
F32 strikeWidth;
|
||||
F32 chanceToHitTarget;
|
||||
F32 strikeRadius;
|
||||
F32 boltStartRadius;
|
||||
ColorF color;
|
||||
ColorF fadeColor;
|
||||
bool useFog;
|
||||
|
||||
// Rendering
|
||||
protected:
|
||||
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
|
||||
// Time management
|
||||
void processTick(const Move *move);
|
||||
void interpolateTick(F32 delta);
|
||||
void advanceTime(F32 dt);
|
||||
|
||||
// Strike management
|
||||
void scheduleThunder(Strike*);
|
||||
|
||||
// Data members
|
||||
private:
|
||||
LightningData* mDataBlock;
|
||||
|
||||
protected:
|
||||
U32 mLastThink; // Valid only on server
|
||||
|
||||
Strike* mStrikeListHead; // Valid on on the client
|
||||
Thunder* mThunderListHead;
|
||||
|
||||
static const U32 csmTargetMask;
|
||||
|
||||
public:
|
||||
Lightning();
|
||||
~Lightning();
|
||||
|
||||
void applyDamage( const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject );
|
||||
void warningFlashes();
|
||||
void strikeRandomPoint();
|
||||
void strikeObject(ShapeBase*);
|
||||
void processEvent(LightningStrikeEvent*);
|
||||
|
||||
DECLARE_CONOBJECT(Lightning);
|
||||
static void initPersistFields();
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
};
|
||||
|
||||
#endif // _H_LIGHTNING
|
||||
|
||||
262
engine/game/fx/particleEmitter.cc
Executable file
262
engine/game/fx/particleEmitter.cc
Executable file
@@ -0,0 +1,262 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "game/fx/particleEmitter.h"
|
||||
#include "game/fx/particleEngine.h"
|
||||
#include "math/mathIO.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(ParticleEmitterNodeData);
|
||||
IMPLEMENT_CO_NETOBJECT_V1(ParticleEmitterNode);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//--------------------------------------
|
||||
//
|
||||
ParticleEmitterNodeData::ParticleEmitterNodeData()
|
||||
{
|
||||
timeMultiple = 1.0;
|
||||
}
|
||||
|
||||
ParticleEmitterNodeData::~ParticleEmitterNodeData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void ParticleEmitterNodeData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("timeMultiple", TypeF32, Offset(timeMultiple, ParticleEmitterNodeData));
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool ParticleEmitterNodeData::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (timeMultiple < 0.01 || timeMultiple > 100) {
|
||||
Con::warnf("ParticleEmitterNodeData::onAdd(%s): timeMultiple must be between 0.01 and 100", getName());
|
||||
timeMultiple = timeMultiple < 0.01 ? 0.01 : 100;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ParticleEmitterNodeData::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if (Parent::preload(server, errorBuffer) == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void ParticleEmitterNodeData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
stream->write(timeMultiple);
|
||||
}
|
||||
|
||||
void ParticleEmitterNodeData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
stream->read(&timeMultiple);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//--------------------------------------
|
||||
//
|
||||
ParticleEmitterNode::ParticleEmitterNode()
|
||||
{
|
||||
// Todo: ScopeAlways?
|
||||
mNetFlags.set(Ghostable);
|
||||
mTypeMask |= EnvironmentObjectType;
|
||||
|
||||
mEmitterDatablock = NULL;
|
||||
mEmitterDatablockId = 0;
|
||||
mEmitter = NULL;
|
||||
mVelocity = 1.0;
|
||||
}
|
||||
|
||||
ParticleEmitterNode::~ParticleEmitterNode()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void ParticleEmitterNode::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
addField("emitter", TypeParticleEmitterDataPtr, Offset(mEmitterDatablock, ParticleEmitterNode));
|
||||
addField("velocity", TypeF32, Offset(mVelocity, ParticleEmitterNode));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool ParticleEmitterNode::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (!mEmitterDatablock && mEmitterDatablockId != 0)
|
||||
{
|
||||
if (Sim::findObject(mEmitterDatablockId, mEmitterDatablock) == false)
|
||||
Con::errorf(ConsoleLogEntry::General, "ParticleEmitterNode::onAdd: Invalid packet, bad datablockId(mEmitterDatablock): %d", mEmitterDatablockId);
|
||||
}
|
||||
|
||||
if (mEmitterDatablock == NULL)
|
||||
return false;
|
||||
|
||||
if (isClientObject())
|
||||
{
|
||||
ParticleEmitter* pEmitter = new ParticleEmitter;
|
||||
pEmitter->onNewDataBlock(mEmitterDatablock);
|
||||
if (pEmitter->registerObject() == false)
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", mDataBlock->getName());
|
||||
delete pEmitter;
|
||||
return false;
|
||||
}
|
||||
mEmitter = pEmitter;
|
||||
}
|
||||
|
||||
mObjBox.min.set(-0.5, -0.5, -0.5);
|
||||
mObjBox.max.set( 0.5, 0.5, 0.5);
|
||||
resetWorldBox();
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ParticleEmitterNode::onRemove()
|
||||
{
|
||||
removeFromScene();
|
||||
|
||||
if (isClientObject())
|
||||
{
|
||||
mEmitter->deleteWhenEmpty();
|
||||
mEmitter = NULL;
|
||||
}
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void ParticleEmitterNode::onGroupAdd()
|
||||
{
|
||||
Parent::onGroupAdd();
|
||||
|
||||
// To make sure that we don't orphan our emitter on mission end,
|
||||
// let's add it to our parent group, so it will get cleaned up.
|
||||
SimGroup* myGroup = getGroup();
|
||||
if (myGroup && mEmitter)
|
||||
myGroup->addObject(mEmitter);
|
||||
}
|
||||
|
||||
bool ParticleEmitterNode::onNewDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
mDataBlock = dynamic_cast<ParticleEmitterNodeData*>(dptr);
|
||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
|
||||
return false;
|
||||
|
||||
// Todo: Uncomment if this is a "leaf" class
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void ParticleEmitterNode::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
|
||||
Point3F emitPoint, emitVelocity;
|
||||
Point3F emitAxis(0, 0, 1);
|
||||
getTransform().mulV(emitAxis);
|
||||
getTransform().getColumn(3, &emitPoint);
|
||||
emitVelocity = emitAxis * mVelocity;
|
||||
|
||||
mEmitter->emitParticles(emitPoint, emitPoint,
|
||||
emitAxis,
|
||||
emitVelocity, (U32)(dt * mDataBlock->timeMultiple * 1000.0f));
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
U32 ParticleEmitterNode::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
mathWrite(*stream, getTransform());
|
||||
mathWrite(*stream, getScale());
|
||||
if (stream->writeFlag(mEmitterDatablock != NULL)) {
|
||||
stream->writeRangedU32(mEmitterDatablock->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void ParticleEmitterNode::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
MatrixF temp;
|
||||
Point3F tempScale;
|
||||
mathRead(*stream, &temp);
|
||||
mathRead(*stream, &tempScale);
|
||||
|
||||
if (stream->readFlag()) {
|
||||
mEmitterDatablockId = stream->readRangedU32(DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
} else {
|
||||
mEmitterDatablockId = 0;
|
||||
}
|
||||
|
||||
setScale(tempScale);
|
||||
setTransform(temp);
|
||||
}
|
||||
|
||||
void ParticleEmitterNode::setEmitterDataBlock(ParticleEmitterData* data)
|
||||
{
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (mEmitter)
|
||||
{
|
||||
mEmitterDatablock = data;
|
||||
ParticleEmitter* pEmitter = new ParticleEmitter;
|
||||
pEmitter->onNewDataBlock(mEmitterDatablock);
|
||||
if (pEmitter->registerObject() == false) {
|
||||
Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", mDataBlock->getName());
|
||||
delete pEmitter;
|
||||
pEmitter = NULL;
|
||||
}
|
||||
if (pEmitter)
|
||||
{
|
||||
mEmitter->deleteWhenEmpty();
|
||||
mEmitter = pEmitter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleMethod(ParticleEmitterNode, setEmitterDataBlock, void, 3, 3, "(data)")
|
||||
{
|
||||
ParticleEmitterData* data = dynamic_cast<ParticleEmitterData*>(Sim::findObject(dAtoi(argv[2])));
|
||||
if (!data)
|
||||
data = dynamic_cast<ParticleEmitterData*>(Sim::findObject(argv[2]));
|
||||
|
||||
if (data)
|
||||
object->setEmitterDataBlock(data);
|
||||
}
|
||||
81
engine/game/fx/particleEmitter.h
Executable file
81
engine/game/fx/particleEmitter.h
Executable file
@@ -0,0 +1,81 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PARTICLEEMITTERDUMMY_H_
|
||||
#define _PARTICLEEMITTERDUMMY_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
|
||||
class ParticleEmitterData;
|
||||
class ParticleEmitter;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
class ParticleEmitterNodeData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
|
||||
//-------------------------------------- Console set variables
|
||||
public:
|
||||
F32 timeMultiple;
|
||||
|
||||
//-------------------------------------- load set variables
|
||||
public:
|
||||
|
||||
ParticleEmitterNodeData();
|
||||
~ParticleEmitterNodeData();
|
||||
|
||||
void packData(BitStream*);
|
||||
void unpackData(BitStream*);
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
|
||||
DECLARE_CONOBJECT(ParticleEmitterNodeData);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
class ParticleEmitterNode : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
private:
|
||||
ParticleEmitterNodeData* mDataBlock;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock(GameBaseData *dptr);
|
||||
|
||||
ParticleEmitterData* mEmitterDatablock;
|
||||
S32 mEmitterDatablockId;
|
||||
|
||||
ParticleEmitter* mEmitter;
|
||||
F32 mVelocity;
|
||||
|
||||
public:
|
||||
ParticleEmitterNode();
|
||||
~ParticleEmitterNode();
|
||||
|
||||
// Time/Move Management
|
||||
public:
|
||||
void advanceTime(F32 dt);
|
||||
void setEmitterDataBlock(ParticleEmitterData* data);
|
||||
|
||||
DECLARE_CONOBJECT(ParticleEmitterNode);
|
||||
static void initPersistFields();
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream* stream);
|
||||
|
||||
virtual void onGroupAdd();
|
||||
};
|
||||
|
||||
#endif // _H_PARTICLEEMISSIONDUMMY
|
||||
|
||||
1641
engine/game/fx/particleEngine.cc
Executable file
1641
engine/game/fx/particleEngine.cc
Executable file
File diff suppressed because it is too large
Load Diff
229
engine/game/fx/particleEngine.h
Executable file
229
engine/game/fx/particleEngine.h
Executable file
@@ -0,0 +1,229 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PARTICLEEMITTER_H_
|
||||
#define _PARTICLEEMITTER_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
//-------------------------------------- Engine initialization...
|
||||
//
|
||||
namespace ParticleEngine {
|
||||
|
||||
enum ParticleConsts
|
||||
{
|
||||
PC_COLOR_KEYS = 4,
|
||||
PC_SIZE_KEYS = 4,
|
||||
};
|
||||
|
||||
/// Initalize the particle engine
|
||||
void init();
|
||||
|
||||
/// Destroy the particle engine
|
||||
void destroy();
|
||||
|
||||
extern Point3F windVelocity; ///< Global wind velocity for all particles
|
||||
|
||||
/// Sets the wind velocity for all particles
|
||||
/// @param vel Velocity
|
||||
inline void setWindVelocity(const Point3F & vel) { windVelocity = vel; }
|
||||
|
||||
/// Returns the wind velocity
|
||||
inline Point3F getWindVelocity() { return windVelocity; }
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-------------------------------------- The data and the Emitter class
|
||||
// are all that the game should deal
|
||||
// with (other than initializing the
|
||||
// global engine pointer of course)
|
||||
//
|
||||
struct Particle;
|
||||
class ParticleData;
|
||||
|
||||
//--------------------------------------
|
||||
class ParticleEmitterData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
public:
|
||||
ParticleEmitterData();
|
||||
DECLARE_CONOBJECT(ParticleEmitterData);
|
||||
static void initPersistFields();
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
|
||||
bool onAdd();
|
||||
|
||||
bool loadParameters();
|
||||
bool reload();
|
||||
|
||||
public:
|
||||
S32 ejectionPeriodMS; ///< Time, in Miliseconds, between particle ejection
|
||||
S32 periodVarianceMS; ///< Varience in ejection peroid between 0 and n
|
||||
|
||||
F32 ejectionVelocity; ///< Ejection velocity
|
||||
F32 velocityVariance; ///< Variance for velocity between 0 and n
|
||||
F32 ejectionOffset; ///< Z offset from emitter point to eject from
|
||||
|
||||
F32 thetaMin; ///< Minimum angle, from the horizontal plane, to eject from
|
||||
F32 thetaMax; ///< Maximum angle, from the horizontal plane, to eject from
|
||||
|
||||
F32 phiReferenceVel; ///< Reference angle, from the verticle plane, to eject from
|
||||
F32 phiVariance; ///< Varience from the reference angle, from 0 to n
|
||||
|
||||
U32 lifetimeMS; ///< Lifetime of particles
|
||||
U32 lifetimeVarianceMS; ///< Varience in lifetime from 0 to n
|
||||
|
||||
bool overrideAdvance; ///<
|
||||
bool orientParticles; ///< Particles always face the screen
|
||||
bool orientOnVelocity; ///< Particles face the screen at the start
|
||||
bool useEmitterSizes; ///< Use emitter specified sizes instead of datablock sizes
|
||||
bool useEmitterColors; ///< Use emitter specified colors instead of datablock colors
|
||||
|
||||
StringTableEntry particleString; ///< Used to load particle data directly from a string
|
||||
Vector<ParticleData*> particleDataBlocks; ///< Datablocks for particle emissions
|
||||
Vector<U32> dataBlockIds; ///< Datablock IDs which corospond to the particleDataBlocks
|
||||
};
|
||||
DECLARE_CONSOLETYPE(ParticleEmitterData)
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
class ParticleEmitter : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
friend class PEngine;
|
||||
|
||||
public:
|
||||
ParticleEmitter();
|
||||
~ParticleEmitter();
|
||||
|
||||
/// Sets sizes of particles based on sizelist provided
|
||||
/// @param sizeList List of sizes
|
||||
void setSizes( F32 *sizeList );
|
||||
|
||||
/// Sets colors for particles based on color list provided
|
||||
/// @param colorList List of colors
|
||||
void setColors( ColorF *colorList );
|
||||
|
||||
ParticleEmitterData *getDataBlock(){ return mDataBlock; }
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
|
||||
/// By default, a particle renderer will wait for it's owner to delete it. When this
|
||||
/// is turned on, it will delete itself as soon as it's particle count drops to zero.
|
||||
void deleteWhenEmpty();
|
||||
|
||||
/// @name Particle Emission
|
||||
/// Main interface for creating particles. The emitter does _not_ track changes
|
||||
/// in axis or velocity over the course of a single update, so this should be called
|
||||
/// at a fairly fine grain. The emitter will potentially track the last particle
|
||||
/// to be created into the next call to this function in order to create a uniformly
|
||||
/// random time distribution of the particles. If the object to which the emitter is
|
||||
/// attached is in motion, it should try to ensure that for call (n+1) to this
|
||||
/// function, start is equal to the end from call (n). This will ensure a uniform
|
||||
/// spatial distribution.
|
||||
/// @{
|
||||
|
||||
void emitParticles(const Point3F& start,
|
||||
const Point3F& end,
|
||||
const Point3F& axis,
|
||||
const Point3F& velocity,
|
||||
const U32 numMilliseconds);
|
||||
void emitParticles(const Point3F& point,
|
||||
const bool useLastPosition,
|
||||
const Point3F& axis,
|
||||
const Point3F& velocity,
|
||||
const U32 numMilliseconds);
|
||||
void emitParticles(const Point3F& rCenter,
|
||||
const Point3F& rNormal,
|
||||
const F32 radius,
|
||||
const Point3F& velocity,
|
||||
S32 count);
|
||||
/// @}
|
||||
|
||||
virtual void setTransform(const MatrixF & mat);
|
||||
|
||||
protected:
|
||||
/// @name Internal interface
|
||||
/// @{
|
||||
|
||||
/// Adds a particle
|
||||
/// @param pos Initial position of particle
|
||||
/// @param axis
|
||||
/// @param vel Initial velocity
|
||||
/// @param axisx
|
||||
void addParticle(const Point3F &pos, const Point3F &axis, const Point3F &vel, const Point3F &axisx);
|
||||
|
||||
/// Renders a particle facing the camera with a spin factor
|
||||
/// @param part Particle
|
||||
/// @param basePnts Base points for the quad the particle is rendered on
|
||||
/// @param camView Camera view matrix
|
||||
/// @param spinFactor 0.0-1.0 modifyer for
|
||||
void renderBillboardParticle( Particle &part, Point3F *basePnts, MatrixF &camView, F32 spinFactor );
|
||||
|
||||
/// Renders a particle which will face the camera but spin itself to look
|
||||
/// like it is facing a particular velocity.
|
||||
/// @param part Particle
|
||||
/// @param camPos Camera position
|
||||
void renderOrientedParticle( Particle &part, const Point3F &camPos );
|
||||
|
||||
/// Updates the bounding box for the particle system
|
||||
bool updateBBox(const Point3F &position);
|
||||
|
||||
/// @}
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
void processTick(const Move *move);
|
||||
void advanceTime(F32 dt);
|
||||
|
||||
// Rendering
|
||||
protected:
|
||||
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
|
||||
// PEngine interface
|
||||
private:
|
||||
/// Removes the provided particle from the system and lets the caller of the
|
||||
/// function assume control of it
|
||||
/// @param part Particle
|
||||
void stealParticle(Particle *part);
|
||||
|
||||
private:
|
||||
ParticleEmitterData* mDataBlock;
|
||||
|
||||
/// This is used so we only update our transform/bounding box
|
||||
/// on ticks, to minimize calls to setTransform.
|
||||
bool mNeedTransformUpdate;
|
||||
|
||||
Particle* mParticleListHead;
|
||||
|
||||
U32 mInternalClock;
|
||||
|
||||
U32 mNextParticleTime;
|
||||
|
||||
Point3F mLastPosition;
|
||||
bool mHasLastPosition;
|
||||
|
||||
bool mDeleteWhenEmpty;
|
||||
bool mDeleteOnTick;
|
||||
|
||||
S32 mLifetimeMS;
|
||||
S32 mElapsedTimeMS;
|
||||
|
||||
F32 sizes[ParticleEngine::PC_SIZE_KEYS];
|
||||
ColorF colors[ParticleEngine::PC_COLOR_KEYS];
|
||||
};
|
||||
|
||||
#endif // _H_PARTICLEEMITTER
|
||||
|
||||
989
engine/game/fx/precipitation.cc
Executable file
989
engine/game/fx/precipitation.cc
Executable file
@@ -0,0 +1,989 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/fx/precipitation.h"
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "sceneGraph/sceneState.h"
|
||||
#include "terrain/sky.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/player.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "platform/profiler.h"
|
||||
|
||||
static const U32 dropHitMask = TerrainObjectType |
|
||||
InteriorObjectType |
|
||||
WaterObjectType |
|
||||
StaticShapeObjectType |
|
||||
StaticTSObjectType;
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Precipitation);
|
||||
IMPLEMENT_CO_DATABLOCK_V1(PrecipitationData);
|
||||
|
||||
//----------------------------------------------------------
|
||||
// PrecipitationData
|
||||
//----------------------------------------------------------
|
||||
PrecipitationData::PrecipitationData()
|
||||
{
|
||||
soundProfile = NULL;
|
||||
soundProfileId = 0;
|
||||
|
||||
mDropName = StringTable->insert("");
|
||||
mSplashName = StringTable->insert("");
|
||||
|
||||
mDropSize = 0.5;
|
||||
mSplashSize = 0.5;
|
||||
mUseTrueBillboards = true;
|
||||
mSplashMS = 250;
|
||||
}
|
||||
IMPLEMENT_CONSOLETYPE(PrecipitationData)
|
||||
IMPLEMENT_GETDATATYPE(PrecipitationData)
|
||||
IMPLEMENT_SETDATATYPE(PrecipitationData)
|
||||
|
||||
void PrecipitationData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("soundProfile", TypeAudioProfilePtr, Offset(soundProfile, PrecipitationData));
|
||||
addField("dropTexture", TypeFilename, Offset(mDropName, PrecipitationData));
|
||||
addField("splashTexture", TypeFilename, Offset(mSplashName, PrecipitationData));
|
||||
addField("dropSize", TypeF32, Offset(mDropSize, PrecipitationData));
|
||||
addField("splashSize", TypeF32, Offset(mSplashSize, PrecipitationData));
|
||||
addField("splashMS", TypeS32, Offset(mSplashMS, PrecipitationData));
|
||||
addField("useTrueBillboards", TypeBool, Offset(mUseTrueBillboards, PrecipitationData));
|
||||
}
|
||||
|
||||
bool PrecipitationData::onAdd()
|
||||
{
|
||||
if (Parent::onAdd() == false)
|
||||
return false;
|
||||
|
||||
if (!soundProfile && soundProfileId != 0)
|
||||
if (Sim::findObject(soundProfileId, soundProfile) == false)
|
||||
Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for precipitation datablock");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrecipitationData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
if (stream->writeFlag(soundProfile != NULL))
|
||||
stream->writeRangedU32(soundProfile->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
stream->writeString(mDropName);
|
||||
stream->writeString(mSplashName);
|
||||
stream->write(mDropSize);
|
||||
stream->write(mSplashSize);
|
||||
stream->write(mSplashMS);
|
||||
stream->writeFlag(mUseTrueBillboards);
|
||||
}
|
||||
|
||||
void PrecipitationData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
if (stream->readFlag())
|
||||
soundProfileId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
||||
else
|
||||
soundProfileId = 0;
|
||||
|
||||
mDropName = stream->readSTString();
|
||||
mSplashName = stream->readSTString();
|
||||
|
||||
stream->read(&mDropSize);
|
||||
stream->read(&mSplashSize);
|
||||
stream->read(&mSplashMS);
|
||||
mUseTrueBillboards = stream->readFlag();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Precipitation!
|
||||
//----------------------------------------------------------
|
||||
Precipitation::Precipitation()
|
||||
{
|
||||
mTypeMask |= ProjectileObjectType;
|
||||
mNetFlags.set(Ghostable|ScopeAlways);
|
||||
|
||||
mDropHead = NULL;
|
||||
mSplashHead = NULL;
|
||||
mNumDrops = 5000;
|
||||
mPercentage = 1.0;
|
||||
|
||||
mMinSpeed = 1.5;
|
||||
mMaxSpeed = 2.0;
|
||||
|
||||
mBoxWidth = 200;
|
||||
mBoxHeight = 100;
|
||||
|
||||
mMinMass = 0.75;
|
||||
mMaxMass = 0.85;
|
||||
|
||||
mMaxTurbulence = 0.1;
|
||||
mTurbulenceSpeed = 0.2;
|
||||
mUseTurbulence = false;
|
||||
|
||||
mRotateWithCamVel = true;
|
||||
|
||||
mDoCollision = true;
|
||||
|
||||
mStormData.valid = false;
|
||||
mStormData.startPct = 0;
|
||||
mStormData.endPct = 0;
|
||||
mStormData.startTime = 0;
|
||||
mStormData.totalTime = 0;
|
||||
|
||||
mAudioHandle = 0;
|
||||
mDropHandle = TextureHandle();
|
||||
mSplashHandle = TextureHandle();
|
||||
|
||||
U32 count = 0;
|
||||
for (U32 v = 0; v < DROPS_PER_SIDE; v++)
|
||||
{
|
||||
const F32 y1 = (F32) v / DROPS_PER_SIDE;
|
||||
const F32 y2 = (F32)(v+1) / DROPS_PER_SIDE;
|
||||
|
||||
for (U32 u = 0; u < DROPS_PER_SIDE; u++)
|
||||
{
|
||||
const F32 x1 = (F32) u / DROPS_PER_SIDE;
|
||||
const F32 x2 = (F32)(u+1) / DROPS_PER_SIDE;
|
||||
|
||||
texCoords[4*count+0].x = x1;
|
||||
texCoords[4*count+0].y = y1;
|
||||
|
||||
texCoords[4*count+1].x = x2;
|
||||
texCoords[4*count+1].y = y1;
|
||||
|
||||
texCoords[4*count+2].x = x2;
|
||||
texCoords[4*count+2].y = y2;
|
||||
|
||||
texCoords[4*count+3].x = x1;
|
||||
texCoords[4*count+3].y = y2;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (U32 v = 0; v < FRAMES_PER_SIDE; v++)
|
||||
{
|
||||
const F32 y1 = (F32) v / FRAMES_PER_SIDE;
|
||||
const F32 y2 = (F32)(v+1) / FRAMES_PER_SIDE;
|
||||
|
||||
for (U32 u = 0; u < FRAMES_PER_SIDE; u++)
|
||||
{
|
||||
const F32 x1 = (F32) u / FRAMES_PER_SIDE;
|
||||
const F32 x2 = (F32)(u+1) / FRAMES_PER_SIDE;
|
||||
|
||||
splashCoords[4*count+0].x = x1;
|
||||
splashCoords[4*count+0].y = y1;
|
||||
|
||||
splashCoords[4*count+1].x = x2;
|
||||
splashCoords[4*count+1].y = y1;
|
||||
|
||||
splashCoords[4*count+2].x = x2;
|
||||
splashCoords[4*count+2].y = y2;
|
||||
|
||||
splashCoords[4*count+3].x = x1;
|
||||
splashCoords[4*count+3].y = y2;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Precipitation::~Precipitation()
|
||||
{
|
||||
}
|
||||
|
||||
void Precipitation::inspectPostApply()
|
||||
{
|
||||
if (isClientObject())
|
||||
fillDropList();
|
||||
|
||||
setMaskBits(DataMask);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Console stuff...
|
||||
//--------------------------------------------------------------------------
|
||||
void Precipitation::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Movement");
|
||||
addField("minSpeed", TypeF32, Offset(mMinSpeed, Precipitation));
|
||||
addField("maxSpeed", TypeF32, Offset(mMaxSpeed, Precipitation));
|
||||
|
||||
addField("minMass", TypeF32, Offset(mMinMass, Precipitation));
|
||||
addField("maxMass", TypeF32, Offset(mMaxMass, Precipitation));
|
||||
endGroup("Movement");
|
||||
|
||||
addGroup("Turbulence");
|
||||
addField("maxTurbulence", TypeF32, Offset(mMaxTurbulence, Precipitation));
|
||||
addField("turbulenceSpeed", TypeF32, Offset(mTurbulenceSpeed, Precipitation));
|
||||
|
||||
addField("rotateWithCamVel", TypeBool, Offset(mRotateWithCamVel, Precipitation));
|
||||
addField("useTurbulence", TypeBool, Offset(mUseTurbulence, Precipitation));
|
||||
endGroup("Turbulence");
|
||||
|
||||
addField("numDrops", TypeS32, Offset(mNumDrops, Precipitation));
|
||||
addField("boxWidth", TypeF32, Offset(mBoxWidth, Precipitation));
|
||||
addField("boxHeight", TypeF32, Offset(mBoxHeight, Precipitation));
|
||||
|
||||
addField("doCollision", TypeBool, Offset(mDoCollision, Precipitation));
|
||||
}
|
||||
|
||||
//-----------------------------------
|
||||
// Console methods...
|
||||
ConsoleMethod(Precipitation, setPercentange, void, 3, 3, "precipitation.setPercentage(percentage <0.0 to 1.0>)")
|
||||
{
|
||||
object->setPercentage(dAtof(argv[2]));
|
||||
}
|
||||
|
||||
ConsoleMethod(Precipitation, modifyStorm, void, 4, 4, "precipitation.modifyStorm(Percentage <0 to 1>, Time<sec>)")
|
||||
{
|
||||
object->modifyStorm(dAtof(argv[2]), dAtof(argv[3]) * 1000);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Backend
|
||||
//--------------------------------------------------------------------------
|
||||
bool Precipitation::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (isClientObject())
|
||||
{
|
||||
if (mDataBlock->soundProfile)
|
||||
mAudioHandle = alxPlay(mDataBlock->soundProfile, &getTransform() );
|
||||
|
||||
fillDropList();
|
||||
}
|
||||
|
||||
mObjBox.min.set(-1e6, -1e6, -1e6);
|
||||
mObjBox.max.set( 1e6, 1e6, 1e6);
|
||||
|
||||
if (isClientObject())
|
||||
{
|
||||
mDropHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mDropName, MeshTexture);
|
||||
mSplashHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mSplashName, MeshTexture);
|
||||
}
|
||||
|
||||
resetWorldBox();
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Precipitation::onRemove()
|
||||
{
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
|
||||
if (mAudioHandle)
|
||||
alxStop(mAudioHandle);
|
||||
mAudioHandle = 0;
|
||||
|
||||
if (isClientObject())
|
||||
killDropList();
|
||||
}
|
||||
|
||||
bool Precipitation::onNewDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
mDataBlock = dynamic_cast<PrecipitationData*>(dptr);
|
||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
|
||||
return false;
|
||||
|
||||
if (isClientObject())
|
||||
{
|
||||
mDropHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mDropName, MeshTexture);
|
||||
mSplashHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mSplashName, MeshTexture);
|
||||
}
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 Precipitation::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||
{
|
||||
Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if (stream->writeFlag(mask & DataMask))
|
||||
{
|
||||
stream->write(mNumDrops);
|
||||
stream->write(mMinSpeed);
|
||||
stream->write(mMaxSpeed);
|
||||
stream->write(mBoxWidth);
|
||||
stream->write(mBoxHeight);
|
||||
stream->write(mMinMass);
|
||||
stream->write(mMaxMass);
|
||||
stream->write(mMaxTurbulence);
|
||||
stream->write(mTurbulenceSpeed);
|
||||
stream->writeFlag(mUseTurbulence);
|
||||
stream->writeFlag(mRotateWithCamVel);
|
||||
stream->writeFlag(mDoCollision);
|
||||
}
|
||||
|
||||
if (stream->writeFlag(mask & PercentageMask))
|
||||
{
|
||||
stream->write(mPercentage);
|
||||
}
|
||||
|
||||
if (stream->writeFlag(!(mask & ~(DataMask | PercentageMask | StormMask)) && (mask & StormMask)))
|
||||
{
|
||||
stream->write(mStormData.endPct);
|
||||
stream->write(mStormData.totalTime);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Precipitation::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
U32 oldDrops = mNumDrops * mPercentage;
|
||||
if (stream->readFlag())
|
||||
{
|
||||
stream->read(&mNumDrops);
|
||||
stream->read(&mMinSpeed);
|
||||
stream->read(&mMaxSpeed);
|
||||
stream->read(&mBoxWidth);
|
||||
stream->read(&mBoxHeight);
|
||||
stream->read(&mMinMass);
|
||||
stream->read(&mMaxMass);
|
||||
stream->read(&mMaxTurbulence);
|
||||
stream->read(&mTurbulenceSpeed);
|
||||
mUseTurbulence = stream->readFlag();
|
||||
mRotateWithCamVel = stream->readFlag();
|
||||
mDoCollision = stream->readFlag();
|
||||
}
|
||||
|
||||
if (stream->readFlag())
|
||||
{
|
||||
stream->read(&mPercentage);
|
||||
}
|
||||
|
||||
if (stream->readFlag())
|
||||
{
|
||||
F32 pct;
|
||||
U32 time;
|
||||
stream->read(&pct);
|
||||
stream->read(&time);
|
||||
modifyStorm(pct, time);
|
||||
}
|
||||
|
||||
U32 newDrops = mNumDrops * mPercentage;
|
||||
if (isClientObject() && oldDrops != newDrops)
|
||||
fillDropList();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Support functions
|
||||
//--------------------------------------------------------------------------
|
||||
VectorF Precipitation::getWindVelocity()
|
||||
{
|
||||
Sky* sky = gClientSceneGraph->getCurrentSky();
|
||||
return (sky && sky->mEffectPrecip) ? -sky->getWindVelocity() : VectorF(0,0,0);
|
||||
}
|
||||
|
||||
void Precipitation::fillDropList()
|
||||
{
|
||||
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
|
||||
|
||||
F32 density = Con::getFloatVariable("$pref::precipitationDensity", 1.0f);
|
||||
U32 newDropCount = (U32)(mNumDrops * mPercentage * density);
|
||||
U32 dropCount = 0;
|
||||
if (mDropHead)
|
||||
{
|
||||
Raindrop* curr = mDropHead;
|
||||
while (curr)
|
||||
{
|
||||
dropCount++;
|
||||
curr = curr->next;
|
||||
if (dropCount == newDropCount && curr)
|
||||
{
|
||||
//delete the remaining drops
|
||||
Raindrop* next = curr->next;
|
||||
curr->next = NULL;
|
||||
while (next)
|
||||
{
|
||||
Raindrop* last = next;
|
||||
next = next->next;
|
||||
last->next = NULL;
|
||||
destroySplash(last);
|
||||
delete last;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dropCount < newDropCount)
|
||||
{
|
||||
//move to the end
|
||||
Raindrop* curr = mDropHead;
|
||||
if (curr)
|
||||
{
|
||||
while (curr->next)
|
||||
curr = curr->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
mDropHead = curr = new Raindrop;
|
||||
spawnNewDrop(curr);
|
||||
dropCount++;
|
||||
}
|
||||
|
||||
//and add onto it
|
||||
while (dropCount < newDropCount)
|
||||
{
|
||||
curr->next = new Raindrop;
|
||||
curr = curr->next;
|
||||
spawnNewDrop(curr);
|
||||
dropCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Precipitation::killDropList()
|
||||
{
|
||||
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
|
||||
|
||||
Raindrop* curr = mDropHead;
|
||||
while (curr)
|
||||
{
|
||||
Raindrop* next = curr->next;
|
||||
delete curr;
|
||||
curr = next;
|
||||
}
|
||||
mDropHead = NULL;
|
||||
}
|
||||
|
||||
void Precipitation::spawnDrop(Raindrop *drop)
|
||||
{
|
||||
PROFILE_START(PrecipSpawnDrop);
|
||||
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
|
||||
|
||||
drop->velocity = Platform::getRandom() * (mMaxSpeed - mMinSpeed) + mMinSpeed;
|
||||
|
||||
drop->position.x = Platform::getRandom() * mBoxWidth - (mBoxWidth / 2);
|
||||
drop->position.y = Platform::getRandom() * mBoxWidth - (mBoxWidth / 2);
|
||||
|
||||
drop->texCoordIndex = (U32)(Platform::getRandom() * ((F32)DROPS_PER_SIDE*DROPS_PER_SIDE - 0.5));
|
||||
drop->valid = true;
|
||||
drop->time = Platform::getRandom() * M_2PI;
|
||||
drop->mass = Platform::getRandom() * (mMaxMass - mMinMass) + mMinMass;
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
void Precipitation::spawnNewDrop(Raindrop *drop)
|
||||
{
|
||||
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
|
||||
|
||||
spawnDrop(drop);
|
||||
drop->position.z = Platform::getRandom() * mBoxHeight - (mBoxHeight / 2);
|
||||
}
|
||||
|
||||
void Precipitation::findDropCutoff(Raindrop *drop)
|
||||
{
|
||||
PROFILE_START(PrecipFindDropCutoff);
|
||||
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
|
||||
|
||||
if (mDoCollision)
|
||||
{
|
||||
VectorF windVel = getWindVelocity();
|
||||
VectorF velocity = windVel / drop->mass - VectorF(0, 0, drop->velocity);
|
||||
velocity.normalize();
|
||||
|
||||
Point3F end = drop->position + 100 * velocity;
|
||||
Point3F start = drop->position - 500 * velocity;
|
||||
|
||||
RayInfo rInfo;
|
||||
if (getContainer()->castRay(start, end, dropHitMask, &rInfo))
|
||||
{
|
||||
drop->hitPos = rInfo.point;
|
||||
drop->hitType = rInfo.object->getTypeMask();
|
||||
}
|
||||
else
|
||||
drop->hitPos = Point3F(0,0,-1000);
|
||||
|
||||
drop->valid = drop->position.z > drop->hitPos.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
drop->hitPos = Point3F(0,0,-1000);
|
||||
drop->valid = true;
|
||||
}
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
void Precipitation::createSplash(Raindrop *drop)
|
||||
{
|
||||
PROFILE_START(PrecipCreateSplash);
|
||||
if (drop != mSplashHead && !(drop->nextSplashDrop || drop->prevSplashDrop))
|
||||
{
|
||||
if (!mSplashHead)
|
||||
{
|
||||
mSplashHead = drop;
|
||||
drop->prevSplashDrop = NULL;
|
||||
drop->nextSplashDrop = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSplashHead->prevSplashDrop = drop;
|
||||
drop->nextSplashDrop = mSplashHead;
|
||||
drop->prevSplashDrop = NULL;
|
||||
mSplashHead = drop;
|
||||
}
|
||||
}
|
||||
|
||||
drop->animStartTime = Platform::getVirtualMilliseconds();
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
void Precipitation::destroySplash(Raindrop *drop)
|
||||
{
|
||||
PROFILE_START(PrecipDestroySplash);
|
||||
if (drop == mSplashHead)
|
||||
{
|
||||
mSplashHead = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unlink.
|
||||
if (drop->nextSplashDrop)
|
||||
drop->nextSplashDrop->prevSplashDrop = drop->prevSplashDrop;
|
||||
|
||||
if (drop->prevSplashDrop)
|
||||
drop->prevSplashDrop->nextSplashDrop = drop->nextSplashDrop;
|
||||
|
||||
drop->nextSplashDrop = NULL;
|
||||
drop->prevSplashDrop = NULL;
|
||||
}
|
||||
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Processing
|
||||
//--------------------------------------------------------------------------
|
||||
void Precipitation::setPercentage(F32 pct)
|
||||
{
|
||||
mPercentage = pct;
|
||||
if (isServerObject())
|
||||
setMaskBits(PercentageMask);
|
||||
}
|
||||
|
||||
void Precipitation::modifyStorm(F32 pct, U32 ms)
|
||||
{
|
||||
pct = mClampF(pct, 0, 1);
|
||||
mStormData.endPct = pct;
|
||||
mStormData.totalTime = ms;
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
setMaskBits(StormMask);
|
||||
return;
|
||||
}
|
||||
|
||||
mStormData.startTime = Platform::getVirtualMilliseconds();
|
||||
mStormData.startPct = mPercentage;
|
||||
mStormData.valid = true;
|
||||
}
|
||||
|
||||
void Precipitation::interpolateTick(F32 delta)
|
||||
{
|
||||
PROFILE_START(PrecipInterpolate);
|
||||
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
|
||||
|
||||
Raindrop* curr = mDropHead;
|
||||
|
||||
VectorF windVel = getWindVelocity();
|
||||
F32 dt = 1-delta;
|
||||
|
||||
while (curr)
|
||||
{
|
||||
if (!curr->toRender)
|
||||
{
|
||||
curr = curr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
VectorF turbulence = dt * windVel;
|
||||
if (mUseTurbulence)
|
||||
{
|
||||
F32 renderTime = curr->time + dt * mTurbulenceSpeed;
|
||||
turbulence += VectorF(mSin(renderTime), mCos(renderTime), 0) * mMaxTurbulence;
|
||||
}
|
||||
|
||||
curr->renderPosition = curr->position + turbulence / curr->mass;
|
||||
curr->renderPosition.z -= dt * curr->velocity;
|
||||
|
||||
curr = curr->next;
|
||||
}
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
void Precipitation::processTick(const Move *)
|
||||
{
|
||||
//nothing to do on the server
|
||||
if (isServerObject())
|
||||
return;
|
||||
|
||||
//we need to update positions and do some collision here
|
||||
GameConnection* conn = GameConnection::getConnectionToServer();
|
||||
if (!conn)
|
||||
return; //need connection to server
|
||||
|
||||
ShapeBase* camObj = conn->getCameraObject();
|
||||
if (!camObj)
|
||||
return;
|
||||
|
||||
PROFILE_START(PrecipProcess);
|
||||
|
||||
//update the storm if necessary
|
||||
if (mStormData.valid)
|
||||
{
|
||||
F32 t = (Platform::getVirtualMilliseconds() - mStormData.startTime) / (F32)mStormData.totalTime;
|
||||
if (t >= 1)
|
||||
{
|
||||
mPercentage = mStormData.endPct;
|
||||
mStormData.valid = false;
|
||||
}
|
||||
else
|
||||
mPercentage = mStormData.startPct * (1-t) + mStormData.endPct * t;
|
||||
fillDropList();
|
||||
}
|
||||
|
||||
MatrixF camMat;
|
||||
camObj->getEyeTransform(&camMat);
|
||||
Point3F camPos, camDir;
|
||||
camMat.getColumn(3, &camPos);
|
||||
camMat.getColumn(1, &camDir);
|
||||
camDir.normalize();
|
||||
F32 fovDot = camObj->getCameraFov() / 180;
|
||||
|
||||
Raindrop* curr = mDropHead;
|
||||
|
||||
//make a box
|
||||
Box3F box(camPos.x - mBoxWidth / 2, camPos.y - mBoxWidth / 2, camPos.z - mBoxHeight / 2,
|
||||
camPos.x + mBoxWidth / 2, camPos.y + mBoxWidth / 2, camPos.z + mBoxHeight / 2);
|
||||
|
||||
//offset the renderbox in the direction of the camera direction
|
||||
//in order to have more of the drops actually rendered
|
||||
box.min.x += camDir.x * mBoxWidth / 4;
|
||||
box.max.x += camDir.x * mBoxWidth / 4;
|
||||
box.min.y += camDir.y * mBoxWidth / 4;
|
||||
box.max.y += camDir.y * mBoxWidth / 4;
|
||||
box.min.z += camDir.z * mBoxHeight / 4;
|
||||
box.max.z += camDir.z * mBoxHeight / 4;
|
||||
|
||||
VectorF windVel = getWindVelocity();
|
||||
|
||||
while (curr)
|
||||
{
|
||||
//update position
|
||||
if (mUseTurbulence)
|
||||
curr->time += mTurbulenceSpeed;
|
||||
curr->position += windVel / curr->mass;
|
||||
curr->position.z -= curr->velocity;
|
||||
|
||||
//wrap position
|
||||
wrapDrop(curr, box);
|
||||
|
||||
if (curr->valid)
|
||||
{
|
||||
if (curr->position.z < curr->hitPos.z)
|
||||
{
|
||||
curr->valid = false;
|
||||
|
||||
// Bump back so we don't spawn splashes where they ought not
|
||||
// be. It might be better to revisit this and use the hitPos.
|
||||
curr->position -= windVel / curr->mass;
|
||||
curr->position.z += curr->velocity;
|
||||
|
||||
//do some funky effect thingy for hitting something
|
||||
if (mSplashHandle.getGLName() != 0)
|
||||
createSplash(curr);
|
||||
}
|
||||
}
|
||||
|
||||
//render test
|
||||
VectorF lookVec = curr->position - camPos;
|
||||
curr->toRender = curr->valid ? mDot(lookVec, camDir) > fovDot : false;
|
||||
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
//update splashes
|
||||
curr = mSplashHead;
|
||||
U32 currTime = Platform::getVirtualMilliseconds();
|
||||
while (curr)
|
||||
{
|
||||
F32 pct = (F32)(currTime - curr->animStartTime) / mDataBlock->mSplashMS;
|
||||
if (pct >= 1.0f)
|
||||
{
|
||||
Raindrop *next = curr->nextSplashDrop;
|
||||
destroySplash(curr);
|
||||
curr = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
curr->splashIndex = (U32)((FRAMES_PER_SIDE*FRAMES_PER_SIDE) * pct);
|
||||
curr = curr->nextSplashDrop;
|
||||
}
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Rendering
|
||||
//--------------------------------------------------------------------------
|
||||
bool Precipitation::prepRenderImage(SceneState* state, const U32 stateKey,
|
||||
const U32 /*startZone*/, const bool /*modifyBaseState*/)
|
||||
{
|
||||
if (isLastState(state, stateKey))
|
||||
return false;
|
||||
setLastState(state, stateKey);
|
||||
|
||||
// This should be sufficient for most objects that don't manage zones, and
|
||||
// don't need to return a specialized RenderImage...
|
||||
if (state->isObjectRendered(this)) {
|
||||
SceneRenderImage* image = new SceneRenderImage;
|
||||
image->obj = this;
|
||||
image->isTranslucent = true;
|
||||
image->sortType = SceneRenderImage::EndSort;
|
||||
state->insertRenderImage(image);
|
||||
}
|
||||
|
||||
if (!mAudioHandle && mDataBlock->soundProfile)
|
||||
mAudioHandle = alxPlay(mDataBlock->soundProfile, &getTransform());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Precipitation::renderObject(SceneState* state, SceneRenderImage*)
|
||||
{
|
||||
PROFILE_START(PrecipRender);
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
||||
|
||||
RectI viewport;
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
dglGetViewport(&viewport);
|
||||
|
||||
// Uncomment this if this is a "simple" (non-zone managing) object
|
||||
state->setupObjectProjection(this);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
|
||||
renderPrecip(state);
|
||||
renderSplashes(state);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
dglSetViewport(viewport);
|
||||
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void Precipitation::renderPrecip(SceneState *state)
|
||||
{
|
||||
GameConnection* conn = GameConnection::getConnectionToServer();
|
||||
if (!conn)
|
||||
return; //need connection to server
|
||||
|
||||
ShapeBase* camObj = conn->getCameraObject();
|
||||
if (!camObj)
|
||||
return;
|
||||
|
||||
PROFILE_START(PrecipRenderPrecip);
|
||||
|
||||
Point3F camPos = state->getCameraPosition();
|
||||
VectorF camVel = camObj->getVelocity();
|
||||
|
||||
Raindrop *curr = mDropHead;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, mDropHandle.getGLName());
|
||||
|
||||
static Point3F verts[4];
|
||||
glVertexPointer(3, GL_FLOAT, 0, verts);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glColor3f(1.0, 1.0, 1.0);
|
||||
|
||||
VectorF windVel = getWindVelocity();
|
||||
|
||||
while (curr)
|
||||
{
|
||||
if (!curr->toRender)
|
||||
{
|
||||
curr = curr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
Point3F pos = curr->renderPosition;
|
||||
VectorF orthoDir = (camPos - pos);
|
||||
|
||||
VectorF velocity = windVel / curr->mass;
|
||||
if (mRotateWithCamVel && camVel != VectorF(0,0,0))
|
||||
{
|
||||
F32 distance = orthoDir.len();
|
||||
velocity -= camVel / (distance > 2 ? distance : 2) * 0.3;
|
||||
}
|
||||
|
||||
velocity.z -= curr->velocity;
|
||||
velocity.normalize();
|
||||
orthoDir.normalize();
|
||||
|
||||
VectorF right;
|
||||
VectorF up;
|
||||
|
||||
// two forms of billboards - true billboards (1st codeblock)
|
||||
// or axis-aligned with velocity (2nd codeblock)
|
||||
// the axis-aligned billboards are aligned with the velocity
|
||||
// of the raindrop, and tilted slightly towards the camera
|
||||
if (mDataBlock->mUseTrueBillboards)
|
||||
{
|
||||
state->mModelview.getRow(0,&right);
|
||||
state->mModelview.getRow(2,&up);
|
||||
right.normalize();
|
||||
up.normalize();
|
||||
}
|
||||
else
|
||||
{
|
||||
right = mCross(-velocity, orthoDir);
|
||||
right.normalize();
|
||||
up = mCross(orthoDir, right) * 0.5 - velocity * 0.5;
|
||||
up.normalize();
|
||||
}
|
||||
right *= mDataBlock->mDropSize;
|
||||
up *= mDataBlock->mDropSize;
|
||||
|
||||
verts[0] = pos + right + up;
|
||||
verts[1] = pos - right + up;
|
||||
verts[2] = pos - right - up;
|
||||
verts[3] = pos + right - up;
|
||||
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, &texCoords[4*curr->texCoordIndex]);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
//debug collision render
|
||||
//if (curr->cutoffHeight != -1000)
|
||||
//{
|
||||
// VectorF windVel = getWindVelocity();
|
||||
// VectorF velocity = windVel / curr->mass - VectorF(0, 0, curr->velocity);
|
||||
// velocity.normalize();
|
||||
|
||||
// Point3F start = curr->position;// - 10000 * velocity;
|
||||
// F32 height = start.z - curr->cutoffHeight;
|
||||
// F32 t = height / velocity.z;
|
||||
// Point3F end = start - t * velocity;
|
||||
|
||||
// glDisable(GL_TEXTURE_2D);
|
||||
// glDisable(GL_BLEND);
|
||||
|
||||
// glBegin(GL_LINES);
|
||||
// glColor3f(1.0, 0.0, 0.0);
|
||||
// glVertex3fv(&(start.x));
|
||||
// glColor3f(0.0, 1.0, 0.0);
|
||||
// glVertex3fv(&(end.x));
|
||||
// glEnd();
|
||||
|
||||
// glBegin(GL_TRIANGLE_FAN);
|
||||
// glColor3f(0.0,0.0,1.0);
|
||||
// glVertex3fv(&((end - right + up).x));
|
||||
// glVertex3fv(&((end + right + up).x));
|
||||
// glVertex3fv(&((end + right - up).x));
|
||||
// glVertex3fv(&((end - right - up).x));
|
||||
// glEnd();
|
||||
|
||||
// glEnable(GL_TEXTURE_2D);
|
||||
// glEnable(GL_BLEND);
|
||||
//}
|
||||
//end debug collision render
|
||||
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
void Precipitation::renderSplashes(SceneState *state)
|
||||
{
|
||||
PROFILE_START(PrecipRenderSplash);
|
||||
|
||||
//setup the billboard
|
||||
VectorF right, up;
|
||||
state->mModelview.getRow(0, &right);
|
||||
state->mModelview.getRow(2, &up);
|
||||
right.normalize();
|
||||
up.normalize();
|
||||
right *= mDataBlock->mSplashSize;
|
||||
up *= mDataBlock->mSplashSize;
|
||||
|
||||
glColor3f(1.0, 1.0, 1.0);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, mSplashHandle.getGLName());
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
static Point3F verts[4];
|
||||
glVertexPointer(3, GL_FLOAT, 0, verts);
|
||||
|
||||
Raindrop *curr = mSplashHead;
|
||||
while (curr)
|
||||
{
|
||||
verts[0] = curr->hitPos + right + up;
|
||||
verts[1] = curr->hitPos - right + up;
|
||||
verts[2] = curr->hitPos - right - up;
|
||||
verts[3] = curr->hitPos + right - up;
|
||||
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, &splashCoords[4*curr->splashIndex]);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
curr = curr->nextSplashDrop;
|
||||
}
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
PROFILE_END();
|
||||
}
|
||||
237
engine/game/fx/precipitation.h
Executable file
237
engine/game/fx/precipitation.h
Executable file
@@ -0,0 +1,237 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PRECIPITATION_H_
|
||||
#define _PRECIPITATION_H_
|
||||
|
||||
#include "game/gameBase.h"
|
||||
#include "audio/audioDataBlock.h"
|
||||
|
||||
/// How many drops are on a side of the material texture
|
||||
#define DROPS_PER_SIDE 4
|
||||
/// How many frames are on a side of a splash animation
|
||||
#define FRAMES_PER_SIDE 2
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Precipitation datablock.
|
||||
class PrecipitationData : public GameBaseData {
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
public:
|
||||
AudioProfile* soundProfile;
|
||||
S32 soundProfileId; ///< Ambient sound
|
||||
|
||||
StringTableEntry mDropName; ///< Texture filename for raindrop
|
||||
StringTableEntry mSplashName; ///< Texture filename for splash
|
||||
|
||||
F32 mDropSize; ///< Droplet billboard size
|
||||
F32 mSplashSize; ///< Splash billboard size
|
||||
bool mUseTrueBillboards; ///< True to use true billboards, false for axis-aligned billboards
|
||||
S32 mSplashMS; ///< How long in milliseconds a splash will last
|
||||
|
||||
PrecipitationData();
|
||||
DECLARE_CONOBJECT(PrecipitationData);
|
||||
bool onAdd();
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(PrecipitationData)
|
||||
|
||||
struct Raindrop
|
||||
{
|
||||
F32 velocity; ///< How fast the drop is falling downwards
|
||||
Point3F position; ///< Position of the drop
|
||||
Point3F renderPosition; ///< Interpolated render-position of the drop
|
||||
F32 time; ///< Time into the turbulence function
|
||||
F32 mass; ///< Mass of drop used for how much turbulence/wind effects the drop
|
||||
|
||||
U32 texCoordIndex; ///< Which piece of the material will be used
|
||||
|
||||
bool toRender; ///< Don't want to render all drops, just the ones that pass a few tests
|
||||
bool valid; ///< Drop becomes invalid after hitting something. Just keep updating
|
||||
///< the position of it, but don't render until it hits the bottom
|
||||
///< of the renderbox and respawns
|
||||
|
||||
Point3F hitPos; ///< Point at which the drop will collide with something
|
||||
|
||||
Raindrop *nextSplashDrop; ///< Linked list cruft for easily adding/removing stuff from the splash list
|
||||
Raindrop *prevSplashDrop; ///< Same as next but previous!
|
||||
SimTime animStartTime; ///< Animation time tracker
|
||||
U32 splashIndex; ///< Texture index for which frame of the splash to render
|
||||
U32 hitType; ///< What kind of object the drop will hit
|
||||
|
||||
Raindrop* next; ///< linked list cruft
|
||||
|
||||
Raindrop()
|
||||
{
|
||||
velocity = 0;
|
||||
time = 0;
|
||||
mass = 1;
|
||||
texCoordIndex = 0;
|
||||
next = NULL;
|
||||
toRender = false;
|
||||
valid = true;
|
||||
nextSplashDrop = NULL;
|
||||
prevSplashDrop = NULL;
|
||||
animStartTime = 0;
|
||||
splashIndex = 0;
|
||||
hitType = 0;
|
||||
hitPos = Point3F(0,0,0);
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class Precipitation : public GameBase
|
||||
{
|
||||
private:
|
||||
|
||||
typedef GameBase Parent;
|
||||
PrecipitationData* mDataBlock;
|
||||
|
||||
Raindrop *mDropHead; ///< Drop linked list head
|
||||
Raindrop *mSplashHead; ///< Splash linked list head
|
||||
Point2F texCoords[4 * DROPS_PER_SIDE*DROPS_PER_SIDE]; ///< texture coords for rain texture
|
||||
Point2F splashCoords[4 * FRAMES_PER_SIDE*FRAMES_PER_SIDE]; ///< texture coordinates for splash texture
|
||||
|
||||
AUDIOHANDLE mAudioHandle; ///< Ambient sound handle
|
||||
TextureHandle mDropHandle; ///< Texture handle for raindrop
|
||||
TextureHandle mSplashHandle; ///< Texture handle for splash
|
||||
|
||||
//console exposed variables
|
||||
S32 mNumDrops; ///< Number of drops in the scene
|
||||
F32 mPercentage; ///< Server-side set var (NOT exposed to console)
|
||||
///< which controls how many drops are present [0,1]
|
||||
|
||||
F32 mMinSpeed; ///< Minimum downward speed of drops
|
||||
F32 mMaxSpeed; ///< Maximum downward speed of drops
|
||||
|
||||
F32 mMinMass; ///< Minimum mass of drops
|
||||
F32 mMaxMass; ///< Maximum mass of drops
|
||||
|
||||
F32 mBoxWidth; ///< How far away in the x and y directions drops will render
|
||||
F32 mBoxHeight; ///< How high drops will render
|
||||
|
||||
F32 mMaxTurbulence; ///< Coefficient to sin/cos for adding turbulence
|
||||
F32 mTurbulenceSpeed; ///< How fast the turbulence wraps in a circle
|
||||
bool mUseTurbulence; ///< Whether to use turbulence or not (MAY EFFECT PERFORMANCE)
|
||||
|
||||
bool mRotateWithCamVel; ///< Rotate the drops relative to the camera velocity
|
||||
///< This is useful for "streak" type drops
|
||||
|
||||
bool mDoCollision; ///< Whether or not to do collision
|
||||
|
||||
struct
|
||||
{
|
||||
bool valid;
|
||||
U32 startTime;
|
||||
U32 totalTime;
|
||||
F32 startPct;
|
||||
F32 endPct;
|
||||
} mStormData;
|
||||
|
||||
//other functions...
|
||||
void processTick(const Move*);
|
||||
void interpolateTick(F32 delta);
|
||||
|
||||
VectorF getWindVelocity();
|
||||
void fillDropList(); ///< Adds/removes drops from the list to have the right # of drops
|
||||
void killDropList(); ///< Deletes the entire drop list
|
||||
void spawnDrop(Raindrop *drop); ///< Fills drop info with random velocity, x/y positions, and mass
|
||||
void spawnNewDrop(Raindrop *drop); ///< Same as spawnDrop except also does z position
|
||||
void findDropCutoff(Raindrop *drop); ///< Casts a ray to see if/when a drop will collide
|
||||
inline void wrapDrop(Raindrop *drop, Box3F &box); ///< Wraps a drop within the specified box
|
||||
|
||||
void createSplash(Raindrop *drop); ///< Adds a drop to the splash list
|
||||
void destroySplash(Raindrop *drop); ///< Removes a drop from the splash list
|
||||
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
// Rendering
|
||||
bool prepRenderImage(SceneState*, const U32, const U32, const bool);
|
||||
void renderObject(SceneState*, SceneRenderImage*);
|
||||
void renderPrecip(SceneState *state);
|
||||
void renderSplashes(SceneState *state);
|
||||
|
||||
public:
|
||||
|
||||
Precipitation();
|
||||
~Precipitation();
|
||||
void inspectPostApply();
|
||||
|
||||
enum
|
||||
{
|
||||
DataMask = Parent::NextFreeMask << 0,
|
||||
PercentageMask = Parent::NextFreeMask << 1,
|
||||
StormMask = Parent::NextFreeMask << 2,
|
||||
NextFreeMask = Parent::NextFreeMask << 3
|
||||
};
|
||||
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
DECLARE_CONOBJECT(Precipitation);
|
||||
static void initPersistFields();
|
||||
|
||||
U32 packUpdate(NetConnection*, U32 mask, BitStream* stream);
|
||||
void unpackUpdate(NetConnection*, BitStream* stream);
|
||||
|
||||
void setPercentage(F32 pct);
|
||||
void modifyStorm(F32 pct, U32 ms);
|
||||
};
|
||||
|
||||
inline void Precipitation::wrapDrop(Raindrop *drop, Box3F &box)
|
||||
{
|
||||
bool recalcCutoff = false;
|
||||
//could probably be slightly optimized to get rid of the while loops
|
||||
if (drop->position.x < box.min.x)
|
||||
{
|
||||
while (drop->position.x < box.min.x)
|
||||
drop->position.x += mBoxWidth;
|
||||
recalcCutoff = true;
|
||||
}
|
||||
else if (drop->position.x > box.max.x)
|
||||
{
|
||||
while (drop->position.x > box.max.x)
|
||||
drop->position.x -= mBoxWidth;
|
||||
recalcCutoff = true;
|
||||
}
|
||||
|
||||
if (drop->position.y < box.min.y)
|
||||
{
|
||||
while (drop->position.y < box.min.y)
|
||||
drop->position.y += mBoxWidth;
|
||||
recalcCutoff = true;
|
||||
}
|
||||
else if (drop->position.y > box.max.y)
|
||||
{
|
||||
while (drop->position.y > box.max.y)
|
||||
drop->position.y -= mBoxWidth;
|
||||
recalcCutoff = true;
|
||||
}
|
||||
|
||||
if (drop->position.z < box.min.z)
|
||||
{
|
||||
spawnDrop(drop);
|
||||
drop->position.x += box.min.x;
|
||||
drop->position.y += box.min.y;
|
||||
while (drop->position.z < box.min.z)
|
||||
drop->position.z += mBoxHeight;
|
||||
recalcCutoff = true;
|
||||
}
|
||||
else if (drop->position.z > box.max.z)
|
||||
{
|
||||
while (drop->position.z > box.max.z)
|
||||
drop->position.z -= mBoxHeight;
|
||||
recalcCutoff = true;
|
||||
}
|
||||
|
||||
if(recalcCutoff)
|
||||
findDropCutoff(drop);
|
||||
}
|
||||
|
||||
#endif // PRECIPITATION_H_
|
||||
|
||||
818
engine/game/fx/splash.cc
Executable file
818
engine/game/fx/splash.cc
Executable file
@@ -0,0 +1,818 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/fx/splash.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "platform/platformAudio.h"
|
||||
#include "audio/audioDataBlock.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "sceneGraph/sceneState.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "game/fx/explosion.h"
|
||||
#include "game/fx/particleEngine.h"
|
||||
#include "sim/netConnection.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
MRandomLCG sgRandom(0xdeadbeef);
|
||||
|
||||
} // namespace {}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(SplashData);
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Splash);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Splash Data
|
||||
//--------------------------------------------------------------------------
|
||||
SplashData::SplashData()
|
||||
{
|
||||
soundProfile = NULL;
|
||||
soundProfileId = 0;
|
||||
|
||||
scale.set(1, 1, 1);
|
||||
|
||||
dMemset( emitterList, 0, sizeof( emitterList ) );
|
||||
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
|
||||
|
||||
delayMS = 0;
|
||||
delayVariance = 0;
|
||||
lifetimeMS = 1000;
|
||||
lifetimeVariance = 0;
|
||||
width = 4.0;
|
||||
numSegments = 10;
|
||||
velocity = 5.0;
|
||||
height = 0.0;
|
||||
acceleration = 0.0;
|
||||
texWrap = 1.0;
|
||||
texFactor = 3.0;
|
||||
ejectionFreq = 5;
|
||||
ejectionAngle = 45.0;
|
||||
ringLifetime = 1.0;
|
||||
startRadius = 0.5;
|
||||
explosion = NULL;
|
||||
explosionId = 0;
|
||||
|
||||
dMemset( textureName, 0, sizeof( textureName ) );
|
||||
|
||||
U32 i;
|
||||
for( i=0; i<NUM_TIME_KEYS; i++ )
|
||||
times[i] = 1.0;
|
||||
|
||||
times[0] = 0.0;
|
||||
|
||||
for( i=0; i<NUM_TIME_KEYS; i++ )
|
||||
colors[i].set( 1.0, 1.0, 1.0, 1.0 );
|
||||
|
||||
}
|
||||
|
||||
IMPLEMENT_CONSOLETYPE(SplashData)
|
||||
IMPLEMENT_SETDATATYPE(SplashData)
|
||||
IMPLEMENT_GETDATATYPE(SplashData)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Init fields
|
||||
//--------------------------------------------------------------------------
|
||||
void SplashData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("soundProfile", TypeAudioProfilePtr, Offset(soundProfile, SplashData));
|
||||
addField("scale", TypePoint3F, Offset(scale, SplashData));
|
||||
addField("emitter", TypeParticleEmitterDataPtr, Offset(emitterList, SplashData), NUM_EMITTERS);
|
||||
addField("delayMS", TypeS32, Offset(delayMS, SplashData));
|
||||
addField("delayVariance", TypeS32, Offset(delayVariance, SplashData));
|
||||
addField("lifetimeMS", TypeS32, Offset(lifetimeMS, SplashData));
|
||||
addField("lifetimeVariance", TypeS32, Offset(lifetimeVariance, SplashData));
|
||||
addField("width", TypeF32, Offset(width, SplashData));
|
||||
addField("numSegments", TypeS32, Offset(numSegments, SplashData));
|
||||
addField("velocity", TypeF32, Offset(velocity, SplashData));
|
||||
addField("height", TypeF32, Offset(height, SplashData));
|
||||
addField("acceleration", TypeF32, Offset(acceleration, SplashData));
|
||||
addField("times", TypeF32, Offset(times, SplashData), NUM_TIME_KEYS);
|
||||
addField("colors", TypeColorF, Offset(colors, SplashData), NUM_TIME_KEYS);
|
||||
addField("texture", TypeFilename, Offset(textureName, SplashData), NUM_TEX);
|
||||
addField("texWrap", TypeF32, Offset(texWrap, SplashData));
|
||||
addField("texFactor", TypeF32, Offset(texFactor, SplashData));
|
||||
addField("ejectionFreq", TypeF32, Offset(ejectionFreq, SplashData));
|
||||
addField("ejectionAngle", TypeF32, Offset(ejectionAngle, SplashData));
|
||||
addField("ringLifetime", TypeF32, Offset(ringLifetime, SplashData));
|
||||
addField("startRadius", TypeF32, Offset(startRadius, SplashData));
|
||||
addField("explosion", TypeExplosionDataPtr, Offset(explosion, SplashData));
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// On add - verify data settings
|
||||
//--------------------------------------------------------------------------
|
||||
bool SplashData::onAdd()
|
||||
{
|
||||
if (Parent::onAdd() == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Pack data
|
||||
//--------------------------------------------------------------------------
|
||||
void SplashData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
mathWrite(*stream, scale);
|
||||
stream->write(delayMS);
|
||||
stream->write(delayVariance);
|
||||
stream->write(lifetimeMS);
|
||||
stream->write(lifetimeVariance);
|
||||
stream->write(width);
|
||||
stream->write(numSegments);
|
||||
stream->write(velocity);
|
||||
stream->write(height);
|
||||
stream->write(acceleration);
|
||||
stream->write(texWrap);
|
||||
stream->write(texFactor);
|
||||
stream->write(ejectionFreq);
|
||||
stream->write(ejectionAngle);
|
||||
stream->write(ringLifetime);
|
||||
stream->write(startRadius);
|
||||
|
||||
if( stream->writeFlag( explosion ) )
|
||||
{
|
||||
stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
||||
}
|
||||
|
||||
S32 i;
|
||||
for( i=0; i<NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( stream->writeFlag( emitterList[i] != NULL ) )
|
||||
{
|
||||
stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
||||
}
|
||||
}
|
||||
|
||||
for( i=0; i<NUM_TIME_KEYS; i++ )
|
||||
{
|
||||
stream->write( colors[i] );
|
||||
}
|
||||
|
||||
for( i=0; i<NUM_TIME_KEYS; i++ )
|
||||
{
|
||||
stream->write( times[i] );
|
||||
}
|
||||
|
||||
for( i=0; i<NUM_TEX; i++ )
|
||||
{
|
||||
stream->writeString(textureName[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Unpack data
|
||||
//--------------------------------------------------------------------------
|
||||
void SplashData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
mathRead(*stream, &scale);
|
||||
stream->read(&delayMS);
|
||||
stream->read(&delayVariance);
|
||||
stream->read(&lifetimeMS);
|
||||
stream->read(&lifetimeVariance);
|
||||
stream->read(&width);
|
||||
stream->read(&numSegments);
|
||||
stream->read(&velocity);
|
||||
stream->read(&height);
|
||||
stream->read(&acceleration);
|
||||
stream->read(&texWrap);
|
||||
stream->read(&texFactor);
|
||||
stream->read(&ejectionFreq);
|
||||
stream->read(&ejectionAngle);
|
||||
stream->read(&ringLifetime);
|
||||
stream->read(&startRadius);
|
||||
|
||||
if( stream->readFlag() )
|
||||
{
|
||||
explosionId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
||||
}
|
||||
|
||||
U32 i;
|
||||
for( i=0; i<NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( stream->readFlag() )
|
||||
{
|
||||
emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
||||
}
|
||||
}
|
||||
|
||||
for( i=0; i<NUM_TIME_KEYS; i++ )
|
||||
{
|
||||
stream->read( &colors[i] );
|
||||
}
|
||||
|
||||
for( i=0; i<NUM_TIME_KEYS; i++ )
|
||||
{
|
||||
stream->read( ×[i] );
|
||||
}
|
||||
|
||||
for( i=0; i<NUM_TEX; i++ )
|
||||
{
|
||||
textureName[i] = stream->readSTString();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Preload data - load resources
|
||||
//--------------------------------------------------------------------------
|
||||
bool SplashData::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if (Parent::preload(server, errorBuffer) == false)
|
||||
return false;
|
||||
|
||||
if (!server)
|
||||
{
|
||||
S32 i;
|
||||
for( i=0; i<NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( !emitterList[i] && emitterIDList[i] != 0 )
|
||||
{
|
||||
if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
|
||||
{
|
||||
Con::errorf( ConsoleLogEntry::General, "SplashData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", emitterIDList[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( i=0; i<NUM_TEX; i++ )
|
||||
{
|
||||
if (textureName[i] && textureName[i][0])
|
||||
{
|
||||
textureHandle[i] = TextureHandle(textureName[i], MeshTexture );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !explosion && explosionId != 0 )
|
||||
{
|
||||
if( !Sim::findObject(explosionId, explosion) )
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "SplashData::preload: Invalid packet, bad datablockId(explosion): %d", explosionId);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Splash
|
||||
//--------------------------------------------------------------------------
|
||||
Splash::Splash()
|
||||
{
|
||||
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
|
||||
|
||||
mDelayMS = 0;
|
||||
mCurrMS = 0;
|
||||
mEndingMS = 1000;
|
||||
mActive = false;
|
||||
mRadius = 0.0;
|
||||
mVelocity = 1.0;
|
||||
mHeight = 0.0;
|
||||
mTimeSinceLastRing = 0.0;
|
||||
mDead = false;
|
||||
mElapsedTime = 0.0;
|
||||
|
||||
mInitialNormal.set( 0.0, 0.0, 1.0 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Destructor
|
||||
//--------------------------------------------------------------------------
|
||||
Splash::~Splash()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Set initial state
|
||||
//--------------------------------------------------------------------------
|
||||
void Splash::setInitialState(const Point3F& point, const Point3F& normal, const F32 fade)
|
||||
{
|
||||
mInitialPosition = point;
|
||||
mInitialNormal = normal;
|
||||
mFade = fade;
|
||||
mFog = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// OnAdd
|
||||
//--------------------------------------------------------------------------
|
||||
bool Splash::onAdd()
|
||||
{
|
||||
// first check if we have a server connection, if we dont then this is on the server
|
||||
// and we should exit, then check if the parent fails to add the object
|
||||
NetConnection* conn = NetConnection::getConnectionToServer();
|
||||
if(!conn || !Parent::onAdd())
|
||||
return false;
|
||||
|
||||
mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
|
||||
mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
|
||||
|
||||
mVelocity = mDataBlock->velocity;
|
||||
mHeight = mDataBlock->height;
|
||||
mTimeSinceLastRing = 1.0 / mDataBlock->ejectionFreq;
|
||||
|
||||
|
||||
if( isClientObject() )
|
||||
{
|
||||
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mDataBlock->emitterList[i] != NULL )
|
||||
{
|
||||
ParticleEmitter * pEmitter = new ParticleEmitter;
|
||||
pEmitter->onNewDataBlock( mDataBlock->emitterList[i] );
|
||||
if( !pEmitter->registerObject() )
|
||||
{
|
||||
Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
|
||||
delete pEmitter;
|
||||
pEmitter = NULL;
|
||||
}
|
||||
mEmitterList[i] = pEmitter;
|
||||
}
|
||||
}
|
||||
|
||||
spawnExplosion();
|
||||
}
|
||||
|
||||
|
||||
mObjBox.min = Point3F( -1, -1, -1 );
|
||||
mObjBox.max = Point3F( 1, 1, 1 );
|
||||
resetWorldBox();
|
||||
|
||||
gClientContainer.addObject(this);
|
||||
gClientSceneGraph->addObjectToScene(this);
|
||||
|
||||
removeFromProcessList();
|
||||
gClientProcessList.addObject(this);
|
||||
|
||||
conn->addObject(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// OnRemove
|
||||
//--------------------------------------------------------------------------
|
||||
void Splash::onRemove()
|
||||
{
|
||||
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mEmitterList[i] )
|
||||
{
|
||||
mEmitterList[i]->deleteWhenEmpty();
|
||||
mEmitterList[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ringList.free();
|
||||
|
||||
mSceneManager->removeObjectFromScene(this);
|
||||
getContainer()->removeObject(this);
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// On New Data Block
|
||||
//--------------------------------------------------------------------------
|
||||
bool Splash::onNewDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
mDataBlock = dynamic_cast<SplashData*>(dptr);
|
||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Prep render image
|
||||
//--------------------------------------------------------------------------
|
||||
bool Splash::prepRenderImage(SceneState* state, const U32 stateKey,
|
||||
const U32 /*startZone*/, const bool /*modifyBaseState*/)
|
||||
{
|
||||
if (isLastState(state, stateKey))
|
||||
return false;
|
||||
setLastState(state, stateKey);
|
||||
|
||||
// This should be sufficient for most objects that don't manage zones, and
|
||||
// don't need to return a specialized RenderImage...
|
||||
if (state->isObjectRendered(this))
|
||||
{
|
||||
mFog = 0.0;
|
||||
|
||||
SceneRenderImage* image = new SceneRenderImage;
|
||||
image->obj = this;
|
||||
image->isTranslucent = true;
|
||||
image->sortType = SceneRenderImage::Point;
|
||||
image->textureSortKey = (U32)(dsize_t)mDataBlock;
|
||||
state->setImageRefPoint(this, image);
|
||||
state->insertRenderImage(image);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Render
|
||||
//--------------------------------------------------------------------------
|
||||
void Splash::renderObject(SceneState* state, SceneRenderImage*)
|
||||
{
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
||||
|
||||
RectI viewport;
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
dglGetViewport(&viewport);
|
||||
|
||||
state->setupObjectProjection(this);
|
||||
|
||||
render();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
dglSetViewport(viewport);
|
||||
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Process tick
|
||||
//--------------------------------------------------------------------------
|
||||
void Splash::processTick(const Move*)
|
||||
{
|
||||
mCurrMS += TickMs;
|
||||
|
||||
if( isServerObject() )
|
||||
{
|
||||
if( mCurrMS >= mEndingMS )
|
||||
{
|
||||
mDead = true;
|
||||
if( mCurrMS >= (mEndingMS + mDataBlock->ringLifetime * 1000) )
|
||||
{
|
||||
deleteObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mCurrMS >= mEndingMS )
|
||||
{
|
||||
mDead = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Advance time
|
||||
//--------------------------------------------------------------------------
|
||||
void Splash::advanceTime(F32 dt)
|
||||
{
|
||||
if (dt == 0.0)
|
||||
return;
|
||||
|
||||
mElapsedTime += dt;
|
||||
|
||||
updateColor();
|
||||
updateWave( dt );
|
||||
updateEmitters( dt );
|
||||
updateRings( dt );
|
||||
|
||||
if( !mDead )
|
||||
{
|
||||
emitRings( dt );
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update emitters
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::updateEmitters( F32 dt )
|
||||
{
|
||||
Point3F pos = getPosition();
|
||||
|
||||
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mEmitterList[i] )
|
||||
{
|
||||
mEmitterList[i]->emitParticles( pos, pos, mInitialNormal, Point3F( 0.0, 0.0, 0.0 ), (S32) (dt * 1000) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update wave
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::updateWave( F32 dt )
|
||||
{
|
||||
mVelocity += mDataBlock->acceleration * dt;
|
||||
mRadius += mVelocity * dt;
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Render splash
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::render()
|
||||
{
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
|
||||
SplashRing *ring = NULL;
|
||||
|
||||
U32 i=0;
|
||||
while( bool( ring = ringList.next( ring ) ) )
|
||||
{
|
||||
SplashRing *next = ringList.next( ring );
|
||||
|
||||
if( !next )
|
||||
break;
|
||||
|
||||
renderSegment( *ring, *next );
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Render horizontal segment created from 2 sets of points that are the
|
||||
// top and bottom rings of the segment.
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::renderSegment( SplashRing &top, SplashRing &bottom )
|
||||
{
|
||||
F32 texFactor = mDataBlock->texWrap;
|
||||
|
||||
glBindTexture( GL_TEXTURE_2D, mDataBlock->textureHandle[0].getGLName() );
|
||||
|
||||
F32 topAlpha = top.elapsedTime / top.lifetime;
|
||||
F32 bottomAlpha = bottom.elapsedTime / bottom.lifetime;
|
||||
|
||||
if( topAlpha < 0.5 )
|
||||
topAlpha *= 2.0;
|
||||
else
|
||||
topAlpha = 1.0 - topAlpha;
|
||||
|
||||
if( bottomAlpha < 0.5 )
|
||||
bottomAlpha *= 2.0;
|
||||
else
|
||||
bottomAlpha = 1.0 - bottomAlpha;
|
||||
|
||||
top.color.alpha = topAlpha;
|
||||
bottom.color.alpha = bottomAlpha;
|
||||
|
||||
|
||||
glBegin( GL_QUAD_STRIP );
|
||||
{
|
||||
for( U32 i=0; i<top.points.size(); i++ )
|
||||
{
|
||||
F32 t = F32(i) / F32(top.points.size());
|
||||
|
||||
glTexCoord2f( t * texFactor, top.v );
|
||||
glColor4fv( top.color );
|
||||
glVertex3fv( top.points[i].position );
|
||||
|
||||
glTexCoord2f( t * texFactor, bottom.v );
|
||||
glColor4fv( bottom.color );
|
||||
glVertex3fv( bottom.points[i].position );
|
||||
}
|
||||
|
||||
glTexCoord2f( 1.0 * texFactor, top.v );
|
||||
glColor4fv( top.color );
|
||||
glVertex3fv( top.points[0].position );
|
||||
|
||||
glTexCoord2f( 1.0 * texFactor, bottom.v );
|
||||
glColor4fv( bottom.color );
|
||||
glVertex3fv( bottom.points[0].position );
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update color
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::updateColor()
|
||||
{
|
||||
SplashRing *ring = NULL;
|
||||
|
||||
while( bool( ring = ringList.next( ring ) ) )
|
||||
{
|
||||
F32 t = F32(ring->elapsedTime) / F32(ring->lifetime);
|
||||
|
||||
for( U32 i = 1; i < SplashData::NUM_TIME_KEYS; i++ )
|
||||
{
|
||||
if( mDataBlock->times[i] >= t )
|
||||
{
|
||||
F32 firstPart = t - mDataBlock->times[i-1];
|
||||
F32 total = (mDataBlock->times[i] -
|
||||
mDataBlock->times[i-1]);
|
||||
|
||||
firstPart /= total;
|
||||
|
||||
ring->color.interpolate( mDataBlock->colors[i-1],
|
||||
mDataBlock->colors[i],
|
||||
firstPart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Create ring
|
||||
//----------------------------------------------------------------------------
|
||||
SplashRing Splash::createRing()
|
||||
{
|
||||
SplashRing ring;
|
||||
U32 numPoints = mDataBlock->numSegments + 1;
|
||||
|
||||
Point3F ejectionAxis( 0.0, 0.0, 1.0 );
|
||||
|
||||
Point3F axisx;
|
||||
if (mFabs(ejectionAxis.z) < 0.999f)
|
||||
mCross(ejectionAxis, Point3F(0, 0, 1), &axisx);
|
||||
else
|
||||
mCross(ejectionAxis, Point3F(0, 1, 0), &axisx);
|
||||
axisx.normalize();
|
||||
|
||||
for( U32 i=0; i<numPoints; i++ )
|
||||
{
|
||||
F32 t = F32(i) / F32(numPoints);
|
||||
|
||||
AngAxisF thetaRot( axisx, mDataBlock->ejectionAngle * (M_PI / 180.0));
|
||||
AngAxisF phiRot( ejectionAxis, t * (M_PI * 2.0));
|
||||
|
||||
Point3F pointAxis = ejectionAxis;
|
||||
|
||||
MatrixF temp;
|
||||
thetaRot.setMatrix(&temp);
|
||||
temp.mulP(pointAxis);
|
||||
phiRot.setMatrix(&temp);
|
||||
temp.mulP(pointAxis);
|
||||
|
||||
Point3F startOffset = axisx;
|
||||
temp.mulV( startOffset );
|
||||
startOffset *= mDataBlock->startRadius;
|
||||
|
||||
SplashRingPoint point;
|
||||
point.position = getPosition() + startOffset;
|
||||
point.velocity = pointAxis * mDataBlock->velocity;
|
||||
|
||||
ring.points.push_back( point );
|
||||
}
|
||||
|
||||
ring.color = mDataBlock->colors[0];
|
||||
ring.lifetime = mDataBlock->ringLifetime;
|
||||
ring.elapsedTime = 0.0;
|
||||
ring.v = mDataBlock->texFactor * mFmod( mElapsedTime, 1.0 );
|
||||
|
||||
return ring;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Emit rings
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::emitRings( F32 dt )
|
||||
{
|
||||
mTimeSinceLastRing += dt;
|
||||
|
||||
S32 numNewRings = (S32) (mTimeSinceLastRing * F32(mDataBlock->ejectionFreq));
|
||||
|
||||
mTimeSinceLastRing -= numNewRings / mDataBlock->ejectionFreq;
|
||||
|
||||
for( S32 i=numNewRings-1; i>=0; i-- )
|
||||
{
|
||||
F32 t = F32(i) / F32(numNewRings);
|
||||
t *= dt;
|
||||
t += mTimeSinceLastRing;
|
||||
|
||||
SplashRing ring = createRing();
|
||||
updateRing( &ring, t );
|
||||
|
||||
ringList.link( ring );
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update rings
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::updateRings( F32 dt )
|
||||
{
|
||||
SplashRing *ring = NULL;
|
||||
while( bool( ring = ringList.next( ring ) ) )
|
||||
{
|
||||
ring->elapsedTime += dt;
|
||||
|
||||
if( !ring->isActive() )
|
||||
{
|
||||
SplashRing *inactiveRing = ring;
|
||||
ring = ringList.prev( ring );
|
||||
ringList.free( inactiveRing );
|
||||
}
|
||||
else
|
||||
{
|
||||
updateRing( ring, dt );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update ring
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::updateRing( SplashRing *ring, F32 dt )
|
||||
{
|
||||
for( U32 i=0; i<ring->points.size(); i++ )
|
||||
{
|
||||
if( mDead )
|
||||
{
|
||||
Point3F vel = ring->points[i].velocity;
|
||||
vel.normalize();
|
||||
vel *= mDataBlock->acceleration;
|
||||
ring->points[i].velocity += vel * dt;
|
||||
}
|
||||
|
||||
ring->points[i].velocity += Point3F( 0.0, 0.0, -9.8 ) * dt;
|
||||
ring->points[i].position += ring->points[i].velocity * dt;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Explode
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::spawnExplosion()
|
||||
{
|
||||
if( !mDataBlock->explosion ) return;
|
||||
|
||||
Explosion* pExplosion = new Explosion;
|
||||
pExplosion->onNewDataBlock(mDataBlock->explosion);
|
||||
|
||||
MatrixF trans = getTransform();
|
||||
trans.setPosition( getPosition() );
|
||||
|
||||
pExplosion->setTransform( trans );
|
||||
pExplosion->setInitialState( trans.getPosition(), VectorF(0,0,1), 1);
|
||||
if (!pExplosion->registerObject())
|
||||
delete pExplosion;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// packUpdate
|
||||
//--------------------------------------------------------------------------
|
||||
U32 Splash::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if( stream->writeFlag(mask & GameBase::InitialUpdateMask) )
|
||||
{
|
||||
mathWrite(*stream, mInitialPosition);
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// unpackUpdate
|
||||
//--------------------------------------------------------------------------
|
||||
void Splash::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
if( stream->readFlag() )
|
||||
{
|
||||
mathRead(*stream, &mInitialPosition);
|
||||
setPosition( mInitialPosition );
|
||||
}
|
||||
}
|
||||
183
engine/game/fx/splash.h
Executable file
183
engine/game/fx/splash.h
Executable file
@@ -0,0 +1,183 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SPLASH_H_
|
||||
#define _SPLASH_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _LLIST_H_
|
||||
#include "core/llist.h"
|
||||
#endif
|
||||
|
||||
class ParticleEmitter;
|
||||
class ParticleEmitterData;
|
||||
class AudioProfile;
|
||||
class ExplosionData;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Ring Point
|
||||
//--------------------------------------------------------------------------
|
||||
struct SplashRingPoint
|
||||
{
|
||||
Point3F position;
|
||||
Point3F velocity;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Splash Ring
|
||||
//--------------------------------------------------------------------------
|
||||
struct SplashRing
|
||||
{
|
||||
Vector <SplashRingPoint> points;
|
||||
ColorF color;
|
||||
F32 lifetime;
|
||||
F32 elapsedTime;
|
||||
F32 v;
|
||||
|
||||
SplashRing()
|
||||
{
|
||||
color.set( 0.0, 0.0, 0.0, 1.0 );
|
||||
lifetime = 0.0;
|
||||
elapsedTime = 0.0;
|
||||
v = 0.0;
|
||||
}
|
||||
|
||||
bool isActive()
|
||||
{
|
||||
return elapsedTime < lifetime;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Splash Data
|
||||
//--------------------------------------------------------------------------
|
||||
class SplashData : public GameBaseData
|
||||
{
|
||||
public:
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
enum Constants
|
||||
{
|
||||
NUM_EMITTERS = 3,
|
||||
NUM_TIME_KEYS = 4,
|
||||
NUM_TEX = 2,
|
||||
};
|
||||
|
||||
public:
|
||||
AudioProfile* soundProfile;
|
||||
S32 soundProfileId;
|
||||
|
||||
ParticleEmitterData* emitterList[NUM_EMITTERS];
|
||||
S32 emitterIDList[NUM_EMITTERS];
|
||||
|
||||
S32 delayMS;
|
||||
S32 delayVariance;
|
||||
S32 lifetimeMS;
|
||||
S32 lifetimeVariance;
|
||||
Point3F scale;
|
||||
F32 width;
|
||||
F32 height;
|
||||
U32 numSegments;
|
||||
F32 velocity;
|
||||
F32 acceleration;
|
||||
F32 texWrap;
|
||||
F32 texFactor;
|
||||
|
||||
F32 ejectionFreq;
|
||||
F32 ejectionAngle;
|
||||
F32 ringLifetime;
|
||||
F32 startRadius;
|
||||
|
||||
F32 times[ NUM_TIME_KEYS ];
|
||||
ColorF colors[ NUM_TIME_KEYS ];
|
||||
|
||||
StringTableEntry textureName[NUM_TEX];
|
||||
TextureHandle textureHandle[NUM_TEX];
|
||||
|
||||
ExplosionData* explosion;
|
||||
S32 explosionId;
|
||||
|
||||
SplashData();
|
||||
DECLARE_CONOBJECT(SplashData);
|
||||
bool onAdd();
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(SplashData)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Splash
|
||||
//--------------------------------------------------------------------------
|
||||
class Splash : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
private:
|
||||
SplashData* mDataBlock;
|
||||
|
||||
ParticleEmitter * mEmitterList[ SplashData::NUM_EMITTERS ];
|
||||
|
||||
LList <SplashRing> ringList;
|
||||
|
||||
U32 mCurrMS;
|
||||
U32 mEndingMS;
|
||||
F32 mRandAngle;
|
||||
F32 mRadius;
|
||||
F32 mVelocity;
|
||||
F32 mHeight;
|
||||
ColorF mColor;
|
||||
F32 mTimeSinceLastRing;
|
||||
bool mDead;
|
||||
F32 mElapsedTime;
|
||||
|
||||
protected:
|
||||
Point3F mInitialPosition;
|
||||
Point3F mInitialNormal;
|
||||
F32 mFade;
|
||||
F32 mFog;
|
||||
bool mActive;
|
||||
S32 mDelayMS;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void processTick(const Move *move);
|
||||
void advanceTime(F32 dt);
|
||||
void updateEmitters( F32 dt );
|
||||
void updateWave( F32 dt );
|
||||
void updateColor();
|
||||
SplashRing createRing();
|
||||
void updateRings( F32 dt );
|
||||
void updateRing( SplashRing *ring, F32 dt );
|
||||
void emitRings( F32 dt );
|
||||
void render();
|
||||
void renderSegment( SplashRing &top, SplashRing &bottom );
|
||||
void spawnExplosion();
|
||||
|
||||
// Rendering
|
||||
protected:
|
||||
bool prepRenderImage ( SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState=false);
|
||||
void renderObject ( SceneState *state, SceneRenderImage *image);
|
||||
|
||||
public:
|
||||
Splash();
|
||||
~Splash();
|
||||
void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0);
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream* stream);
|
||||
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
DECLARE_CONOBJECT(Splash);
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_SPLASH
|
||||
142
engine/game/fx/underLava.cc
Executable file
142
engine/game/fx/underLava.cc
Executable file
@@ -0,0 +1,142 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/fx/underLava.h"
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mRect.h"
|
||||
#include "dgl/gTexManager.h"
|
||||
#include "terrain/waterBlock.h"
|
||||
#include "math/mConstants.h"
|
||||
|
||||
UnderLavaFX gLavaFX;
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Under lava FX - "Lava - With pumice!"
|
||||
//**************************************************************************
|
||||
UnderLavaFX::UnderLavaFX()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Init
|
||||
//--------------------------------------------------------------------------
|
||||
void UnderLavaFX::init()
|
||||
{
|
||||
RectI viewport;
|
||||
dglGetViewport( &viewport );
|
||||
|
||||
mViewSize = viewport.extent;
|
||||
|
||||
mTexFrequency.x = F32(viewport.extent.x / viewport.extent.y);
|
||||
mTexFrequency.y = 1.0;
|
||||
|
||||
mNumPoints.x = (S32)(50 * F32(viewport.extent.x / viewport.extent.y));
|
||||
mNumPoints.y = 50;
|
||||
|
||||
mWave[0].amplitude = 0.02;
|
||||
mWave[0].frequency = 2.0;
|
||||
mWave[0].velocity = Sim::getCurrentTime() / 1000.0 * 2.0;
|
||||
|
||||
mMoveSpeed = Sim::getCurrentTime() / 1000.0 * 0.025;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Render
|
||||
//--------------------------------------------------------------------------
|
||||
void UnderLavaFX::render()
|
||||
{
|
||||
init();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
// TextureHandle lavaTex = WaterBlock::getSubmergeTexture(0);
|
||||
glBindTexture(GL_TEXTURE_2D, WaterBlock::getSubmergeTexture(0).getGLName());
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(GL_FALSE);
|
||||
glColor4f(1.0, 1.0, 1.0, 0.5);
|
||||
|
||||
if( WaterBlock::getSubmergeTexture(0) )
|
||||
{
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
|
||||
// render layer 1
|
||||
for( U32 i=0; i<mNumPoints.y; i++ )
|
||||
{
|
||||
renderRow( i, mNumPoints.y-1, mNumPoints.x-1 );
|
||||
}
|
||||
}
|
||||
|
||||
// give second layer a different phase
|
||||
mWave[0].velocity += 10.0;
|
||||
|
||||
// lavaTex = WaterBlock::getSubmergeTexture(1);
|
||||
|
||||
if( WaterBlock::getSubmergeTexture(1) )
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, WaterBlock::getSubmergeTexture(1).getGLName());
|
||||
|
||||
// render layer 2
|
||||
for( U32 i=0; i<mNumPoints.y; i++ )
|
||||
{
|
||||
renderRow( i, mNumPoints.y-1, mNumPoints.x-1 );
|
||||
}
|
||||
}
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Render row (triangle strip)
|
||||
//--------------------------------------------------------------------------
|
||||
void UnderLavaFX::renderRow( U32 row, U32 numRows, U32 numColumns )
|
||||
{
|
||||
|
||||
F32 xMult = F32(2.0) / F32(numColumns);
|
||||
F32 yMult = F32(2.0) / F32(numRows);
|
||||
|
||||
glBegin( GL_TRIANGLE_STRIP );
|
||||
|
||||
for( U32 i=0; i<numColumns+1; i++ )
|
||||
{
|
||||
F32 u = F32(i) / F32(numColumns) * mTexFrequency.x;
|
||||
F32 v = F32(row) / F32(numRows) * mTexFrequency.y;
|
||||
|
||||
u += mMoveSpeed + mWave[0].amplitude * mSin( u * M_2PI * mWave[0].frequency + mWave[0].velocity );
|
||||
v += mMoveSpeed + mWave[0].amplitude * mSin( v * M_2PI * mWave[0].frequency + mWave[0].velocity );
|
||||
|
||||
glTexCoord2f( u, v );
|
||||
glVertex2f( -1.0 + i * xMult, -1.0 + row * yMult );
|
||||
|
||||
v = F32(row+1) / F32(numRows) * mTexFrequency.y;
|
||||
v += mMoveSpeed + mWave[0].amplitude * mSin( v * M_2PI * mWave[0].frequency + mWave[0].velocity );
|
||||
|
||||
glTexCoord2f( u, v );
|
||||
glVertex2f( -1.0 + i * xMult, -1.0 + (row+1) * yMult );
|
||||
|
||||
}
|
||||
|
||||
glEnd();
|
||||
|
||||
}
|
||||
56
engine/game/fx/underLava.h
Executable file
56
engine/game/fx/underLava.h
Executable file
@@ -0,0 +1,56 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _UNDERLAVA_H_
|
||||
#define _UNDERLAVA_H_
|
||||
|
||||
#ifndef _MPOINT_H_
|
||||
#include "math/mPoint.h"
|
||||
#endif
|
||||
|
||||
//**************************************************************************
|
||||
// Data
|
||||
//**************************************************************************
|
||||
struct LavaVertex
|
||||
{
|
||||
Point2I pnt;
|
||||
Point2F texPnt;
|
||||
|
||||
};
|
||||
|
||||
struct LavaWave
|
||||
{
|
||||
F32 frequency;
|
||||
F32 amplitude;
|
||||
F32 velocity;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// Under lava FX
|
||||
//**************************************************************************
|
||||
class UnderLavaFX
|
||||
{
|
||||
private:
|
||||
Point2F mTexFrequency;
|
||||
Point2I mNumPoints;
|
||||
Point2I mViewSize;
|
||||
LavaWave mWave[2];
|
||||
F32 mMoveSpeed;
|
||||
|
||||
void renderRow( U32 row, U32 numRows, U32 numColumns );
|
||||
|
||||
public:
|
||||
UnderLavaFX();
|
||||
|
||||
void init();
|
||||
void render();
|
||||
|
||||
};
|
||||
|
||||
extern UnderLavaFX gLavaFX;
|
||||
|
||||
#endif
|
||||
715
engine/game/fx/weatherLightning.cpp
Executable file
715
engine/game/fx/weatherLightning.cpp
Executable file
@@ -0,0 +1,715 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "sceneGraph/sceneState.h"
|
||||
#include "audio/audioDataBlock.h"
|
||||
#include "game/fx/weatherLightning.h"
|
||||
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(WeatherLightningStrikeEvent);
|
||||
IMPLEMENT_CO_DATABLOCK_V1(WeatherLightningData);
|
||||
IMPLEMENT_CO_NETOBJECT_V1(WeatherLightning);
|
||||
|
||||
MRandomLCG sgRandomGen;
|
||||
|
||||
S32 QSORT_CALLBACK cmpWLSounds(const void* p1, const void* p2)
|
||||
{
|
||||
U32 i1 = *((const S32*)p1);
|
||||
U32 i2 = *((const S32*)p2);
|
||||
|
||||
if (i1 < i2) {
|
||||
return 1;
|
||||
} else if (i1 > i2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
S32 QSORT_CALLBACK cmpWLTextures(const void* t1, const void* t2)
|
||||
{
|
||||
StringTableEntry ta = *(StringTableEntry*)t1;
|
||||
StringTableEntry tb = *(StringTableEntry*)t2;
|
||||
|
||||
if(ta && ta[0] != '\0')
|
||||
{
|
||||
if(tb && tb[0] != '\0')
|
||||
return dStricmp(ta, tb);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(tb && tb[0] != '\0')
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
WeatherLightningStrikeEvent::WeatherLightningStrikeEvent()
|
||||
{
|
||||
mLightning = NULL;
|
||||
}
|
||||
|
||||
WeatherLightningStrikeEvent::~WeatherLightningStrikeEvent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WeatherLightningStrikeEvent::pack(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
if(!mLightning)
|
||||
{
|
||||
stream->writeFlag(false);
|
||||
return;
|
||||
}
|
||||
|
||||
S32 ghostIndex = con->getGhostIndex(mLightning);
|
||||
if(ghostIndex == -1)
|
||||
{
|
||||
stream->writeFlag(false);
|
||||
return;
|
||||
}
|
||||
|
||||
stream->writeFlag(true);
|
||||
stream->writeRangedU32(U32(ghostIndex), 0, NetConnection::MaxGhostCount);
|
||||
stream->writeFloat(mStart.x, PositionalBits);
|
||||
stream->writeFloat(mStart.y, PositionalBits);
|
||||
}
|
||||
|
||||
void WeatherLightningStrikeEvent::unpack(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
if(!stream->readFlag())
|
||||
return;
|
||||
S32 ghostIndex = stream->readRangedU32(0, NetConnection::MaxGhostCount);
|
||||
mLightning = NULL;
|
||||
NetObject* pObject = con->resolveGhost(ghostIndex);
|
||||
if(pObject)
|
||||
mLightning = dynamic_cast<WeatherLightning*>(pObject);
|
||||
|
||||
mStart.x = stream->readFloat(PositionalBits);
|
||||
mStart.y = stream->readFloat(PositionalBits);
|
||||
}
|
||||
|
||||
void WeatherLightningStrikeEvent::process(NetConnection*)
|
||||
{
|
||||
if (mLightning)
|
||||
mLightning->processEvent(this);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
WeatherLightningData::WeatherLightningData()
|
||||
{
|
||||
dMemset(strikeTextureNames, 0, sizeof(strikeTextureNames));
|
||||
dMemset(flashTextureNames, 0, sizeof(flashTextureNames));
|
||||
dMemset(fuzzyTextureNames, 0, sizeof(fuzzyTextureNames));
|
||||
|
||||
dMemset(strikeTextures, 0, sizeof(strikeTextures));
|
||||
dMemset(flashTextures, 0, sizeof(flashTextures));
|
||||
dMemset(fuzzyTextures, 0, sizeof(fuzzyTextures));
|
||||
|
||||
strikeSoundId = -1;
|
||||
strikeSound = NULL_AUDIOHANDLE;
|
||||
for(U32 i = 0; i < MaxSounds; i++)
|
||||
{
|
||||
thunderSoundIds[i] = -1;
|
||||
thunderSounds[i] = NULL_AUDIOHANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
WeatherLightningData::~WeatherLightningData()
|
||||
{
|
||||
//
|
||||
};
|
||||
|
||||
void WeatherLightningData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("strikeTextures", TypeFilename, Offset(strikeTextureNames, WeatherLightningData), MaxStrikeTextures);
|
||||
addField("flashTextures", TypeFilename, Offset(flashTextureNames, WeatherLightningData), MaxFlashTextures);
|
||||
addField("fuzzyTextures", TypeFilename, Offset(fuzzyTextureNames, WeatherLightningData), MaxFuzzyTextures);
|
||||
|
||||
addField("strikeSound", TypeAudioProfilePtr, Offset(strikeSound, WeatherLightningData));
|
||||
addField("thunderSounds", TypeAudioProfilePtr, Offset(thunderSounds, WeatherLightningData), MaxSounds);
|
||||
}
|
||||
|
||||
bool WeatherLightningData::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if(!strikeSound && strikeSoundId != -1)
|
||||
{
|
||||
if(Sim::findObject(strikeSoundId, strikeSound) == false)
|
||||
Con::errorf(ConsoleLogEntry::General, "WeatherLightningData::onAdd: Invalid packet, bad datablockId(sound: %d", strikeSound);
|
||||
}
|
||||
|
||||
for(U32 i = 0; i < MaxSounds; i++)
|
||||
{
|
||||
if(!thunderSounds[i] && thunderSoundIds[i] != -1)
|
||||
{
|
||||
if(Sim::findObject(thunderSoundIds[i], thunderSounds[i]) == false)
|
||||
Con::errorf(ConsoleLogEntry::General, "WeahterLightningData::onAdd: Invalid packet, bad datablockId(sound: %d", thunderSounds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WeatherLightningData::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if(Parent::preload(server, errorBuffer) == false)
|
||||
return false;
|
||||
|
||||
dQsort(strikeTextureNames, MaxStrikeTextures, sizeof(StringTableEntry), cmpWLTextures);
|
||||
dQsort(flashTextureNames, MaxFlashTextures, sizeof(StringTableEntry), cmpWLTextures);
|
||||
dQsort(fuzzyTextureNames, MaxFuzzyTextures, sizeof(StringTableEntry), cmpWLTextures);
|
||||
|
||||
if(!server)
|
||||
{
|
||||
for(numStrikes = 0; numStrikes < MaxStrikeTextures; numStrikes++)
|
||||
{
|
||||
if(strikeTextureNames[numStrikes] && strikeTextureNames[numStrikes][0] != '\0')
|
||||
strikeTextures[numStrikes] = TextureHandle(strikeTextureNames[numStrikes], MeshTexture);
|
||||
else
|
||||
break;
|
||||
}
|
||||
for(numFlashes = 0; numFlashes < MaxFlashTextures && flashTextureNames[numFlashes] != NULL; numFlashes++)
|
||||
{
|
||||
if(flashTextureNames[numFlashes] && flashTextureNames[numFlashes][0] != '\0')
|
||||
flashTextures[numFlashes] = TextureHandle(flashTextureNames[numFlashes], MeshTexture);
|
||||
else
|
||||
break;
|
||||
}
|
||||
for(numFuzzes = 0; numFuzzes < MaxFuzzyTextures && fuzzyTextureNames[numFuzzes] != NULL; numFuzzes++)
|
||||
{
|
||||
if(fuzzyTextureNames[numFuzzes] && fuzzyTextureNames[numFuzzes][0] != '\0')
|
||||
fuzzyTextures[numFuzzes] = TextureHandle(fuzzyTextureNames[numFuzzes], MeshTexture);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dQsort(thunderSounds, MaxSounds, sizeof(AudioProfile*), cmpWLSounds);
|
||||
for(numSounds = 0; numSounds < MaxSounds && thunderSounds[numSounds] != NULL_AUDIOHANDLE; numSounds++) {
|
||||
//
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WeatherLightningData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
U32 i;
|
||||
for (i = 0; i < MaxStrikeTextures; i++)
|
||||
stream->writeString(strikeTextureNames[i]);
|
||||
for(i = 0; i < MaxFlashTextures; i++)
|
||||
stream->writeString(flashTextureNames[i]);
|
||||
for(i = 0; i < MaxFuzzyTextures; i++)
|
||||
stream->writeString(fuzzyTextureNames[i]);
|
||||
|
||||
if(stream->writeFlag(strikeSound != NULL_AUDIOHANDLE))
|
||||
stream->writeRangedU32(strikeSound->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
for(i = 0; i < MaxSounds; i++)
|
||||
{
|
||||
if(stream->writeFlag(thunderSounds[i] != NULL_AUDIOHANDLE))
|
||||
stream->writeRangedU32(thunderSounds[i]->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
}
|
||||
}
|
||||
|
||||
void WeatherLightningData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
U32 i;
|
||||
for(i = 0; i < MaxStrikeTextures; i++)
|
||||
strikeTextureNames[i] = stream->readSTString();
|
||||
for(i = 0; i < MaxFlashTextures; i++)
|
||||
flashTextureNames[i] = stream->readSTString();
|
||||
for(i = 0; i < MaxFuzzyTextures; i++)
|
||||
fuzzyTextureNames[i] = stream->readSTString();
|
||||
|
||||
if(stream->readFlag())
|
||||
strikeSoundId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
||||
else
|
||||
strikeSoundId = -1;
|
||||
|
||||
for(i = 0; i < MaxSounds; i++)
|
||||
{
|
||||
if(stream->readFlag())
|
||||
thunderSoundIds[i] = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
||||
else
|
||||
thunderSoundIds[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
WeatherLightning::WeatherLightning()
|
||||
{
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
mTypeMask |= StaticObjectType|EnvironmentObjectType;
|
||||
|
||||
lastThink = 0;
|
||||
strikesPerMinute = 9;
|
||||
boltDeathAge = 1.5;
|
||||
}
|
||||
|
||||
WeatherLightning::~WeatherLightning()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void WeatherLightning::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addNamedField(strikesPerMinute, TypeS32, WeatherLightning);
|
||||
addNamedField(boltDeathAge, TypeF32, WeatherLightning);
|
||||
}
|
||||
|
||||
bool WeatherLightning::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
mObjBox.min.set( -0.5, -0.5, -0.5 );
|
||||
mObjBox.max.set( 0.5, 0.5, 0.5 );
|
||||
resetWorldBox();
|
||||
|
||||
addToScene();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WeatherLightning::onRemove()
|
||||
{
|
||||
while(mActiveBolts.size())
|
||||
{
|
||||
WeatherLightningBolt* bolt = mActiveBolts[0];
|
||||
delete bolt;
|
||||
mActiveBolts.erase_fast(U32(0));
|
||||
}
|
||||
while(mSoundEvents.size())
|
||||
mSoundEvents.erase_fast(U32(0));
|
||||
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
bool WeatherLightning::onNewDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
mDataBlock = dynamic_cast<WeatherLightningData*>(dptr);
|
||||
if(!mDataBlock || !Parent::onNewDataBlock(dptr))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WeatherLightning::prepRenderImage(SceneState* state, const U32 stateKey, const U32, const bool)
|
||||
{
|
||||
if(isLastState(state, stateKey))
|
||||
return false;
|
||||
setLastState(state, stateKey);
|
||||
|
||||
// This should be sufficient for most objects that don't manage zones, and
|
||||
// don't need to return a specialized RenderImage...
|
||||
if(state->isObjectRendered(this))
|
||||
{
|
||||
SceneRenderImage* image = new SceneRenderImage;
|
||||
image->obj = this;
|
||||
image->isTranslucent = true;
|
||||
image->sortType = SceneRenderImage::EndSort;
|
||||
state->insertRenderImage(image);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WeatherLightningBolt::render(const Point3F &camPos)
|
||||
{
|
||||
Point3F perpVec;
|
||||
Point3F lightUp = startPoint - endPoint;
|
||||
mCross(camPos - endPoint, lightUp, &perpVec);
|
||||
perpVec.normalize();
|
||||
|
||||
Point3F frontVec;
|
||||
mCross(perpVec, lightUp, &frontVec);
|
||||
frontVec.normalize();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
//
|
||||
// strike texture
|
||||
//
|
||||
|
||||
// setup alpha value
|
||||
F32 strikeAlpha;
|
||||
if(currentAge < (strikeTime / 3.0))
|
||||
{
|
||||
strikeAlpha = currentAge / (strikeTime / 3.0);
|
||||
strikeAlpha = mPow(strikeAlpha, F32(1.0 / 3.0));
|
||||
}
|
||||
else if(currentAge < (2.0 * strikeTime / 3.0))
|
||||
strikeAlpha = 1.0;
|
||||
else
|
||||
strikeAlpha = 1.0 - ((currentAge - (2.0 * strikeTime / 3.0)) / (strikeTime / 3.0));
|
||||
glColor4f(1.0f, 1.0f, 1.0f, strikeAlpha);
|
||||
|
||||
// generate texture coords
|
||||
Point3F points[4];
|
||||
F32 width = ((startPoint.z - endPoint.z) * 0.125f);
|
||||
points[0] = startPoint - perpVec * width;
|
||||
points[1] = startPoint + perpVec * width;
|
||||
points[2] = endPoint + perpVec * width;
|
||||
points[3] = endPoint - perpVec * width;
|
||||
|
||||
// bind and draw texture
|
||||
glBindTexture(GL_TEXTURE_2D, strikeTexture->getGLName());
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
||||
glTexCoord2f(0, 1); glVertex3fv(points[1]);
|
||||
glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
||||
glTexCoord2f(1, 0); glVertex3fv(points[3]);
|
||||
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
||||
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
|
||||
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
||||
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
|
||||
glEnd();
|
||||
|
||||
//
|
||||
// fuzzy texture
|
||||
//
|
||||
|
||||
// setup alpha value
|
||||
F32 constAlpha;
|
||||
if(currentAge < strikeTime / 2.0)
|
||||
constAlpha = currentAge / (strikeTime / 2.0);
|
||||
else if(currentAge < (2.0 * strikeTime / 3.0))
|
||||
constAlpha = 1.0 - ((currentAge - (strikeTime / 2.0)) / (strikeTime / 6.0));
|
||||
else
|
||||
constAlpha = 0.0;
|
||||
glColor4f(1.0, 1.0, 1.0, constAlpha);
|
||||
|
||||
// generate texture coords
|
||||
width *= 4;
|
||||
points[0] = startPoint - perpVec * width;
|
||||
points[1] = startPoint + perpVec * width;
|
||||
points[2] = endPoint + perpVec * width;
|
||||
points[3] = endPoint - perpVec * width;
|
||||
|
||||
if(constAlpha != 0.0)
|
||||
{
|
||||
// bind and draw texture
|
||||
glBindTexture(GL_TEXTURE_2D, fuzzyTexture->getGLName());
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
||||
glTexCoord2f(0, 1); glVertex3fv(points[1]);
|
||||
glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
||||
glTexCoord2f(1, 0); glVertex3fv(points[3]);
|
||||
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
||||
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
|
||||
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
||||
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
|
||||
glEnd();
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
//
|
||||
// flash texture
|
||||
//
|
||||
|
||||
// setup alpha value
|
||||
glColor4f(1.0f, 1.0f, 1.0f, strikeAlpha);
|
||||
|
||||
// generate texture coords
|
||||
points[0] = startPoint - perpVec * width + frontVec * width;
|
||||
points[1] = startPoint - perpVec * width - frontVec * width;
|
||||
points[2] = startPoint + perpVec * width - frontVec * width;
|
||||
points[3] = startPoint + perpVec * width + frontVec * width;
|
||||
|
||||
// bind and draw texture
|
||||
glBindTexture(GL_TEXTURE_2D, flashTexture->getGLName());
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
||||
glTexCoord2f(0, 1); glVertex3fv(points[1]);
|
||||
glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
||||
glTexCoord2f(1, 0); glVertex3fv(points[3]);
|
||||
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
||||
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
|
||||
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
||||
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
|
||||
glEnd();
|
||||
|
||||
//
|
||||
// finished
|
||||
//
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
void WeatherLightning::renderObject(SceneState* state, SceneRenderImage*)
|
||||
{
|
||||
if(mActiveBolts.size())
|
||||
{
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
||||
|
||||
RectI viewport;
|
||||
F64 farPlane;
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
|
||||
dglGetViewport(&viewport);
|
||||
farPlane = state->getFarPlane();
|
||||
const Point3F &camPos = state->getCameraPosition();
|
||||
|
||||
// adjust far clip plane
|
||||
F64 distance = (getPosition() - camPos).lenSquared();
|
||||
state->setFarPlane(getMax(farPlane, distance));
|
||||
|
||||
state->setupObjectProjection(this);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
|
||||
for(U32 i = 0; i < mActiveBolts.size(); i++)
|
||||
mActiveBolts[i]->render(camPos);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
state->setFarPlane(farPlane);
|
||||
dglSetViewport(viewport);
|
||||
|
||||
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
||||
}
|
||||
}
|
||||
|
||||
void WeatherLightning::processTick(const Move *move)
|
||||
{
|
||||
Parent::processTick(move);
|
||||
|
||||
if (isServerObject())
|
||||
{
|
||||
S32 msBetweenStrikes = (S32)(60.0 / strikesPerMinute * 1000.0);
|
||||
|
||||
lastThink += TickMs;
|
||||
if( lastThink > msBetweenStrikes )
|
||||
{
|
||||
strikeRandomPoint();
|
||||
lastThink -= msBetweenStrikes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WeatherLightning::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
|
||||
U32 i;
|
||||
|
||||
// loop through and erase any dead bolts
|
||||
for(i = 0; i < mActiveBolts.size();)
|
||||
{
|
||||
WeatherLightningBolt* bolt = mActiveBolts[i];
|
||||
bolt->currentAge += dt;
|
||||
if(bolt->currentAge > bolt->deathAge)
|
||||
{
|
||||
delete bolt;
|
||||
mActiveBolts.erase_fast(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// loop through and find any pending sound events
|
||||
for(i = 0; i < mSoundEvents.size();)
|
||||
{
|
||||
SoundEvent *sEvent = &mSoundEvents[i];
|
||||
sEvent->time -= dt;
|
||||
if(sEvent->time <= 0.0)
|
||||
{
|
||||
// fire off the sound
|
||||
if(sEvent->soundBlockId != -1)
|
||||
alxPlay(mDataBlock->thunderSounds[sEvent->soundBlockId], &sEvent->position);
|
||||
mSoundEvents.erase_fast(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void WeatherLightning::strikeRandomPoint()
|
||||
{
|
||||
// choose random strike point within object bounds
|
||||
Point2F strikePoint;
|
||||
strikePoint.x = sgRandomGen.randF( 0.0, 1.0 );
|
||||
strikePoint.y = sgRandomGen.randF( 0.0, 1.0 );
|
||||
|
||||
SimGroup* pClientGroup = Sim::getClientGroup();
|
||||
for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
|
||||
{
|
||||
NetConnection* nc = static_cast<NetConnection*>(*itr);
|
||||
|
||||
WeatherLightningStrikeEvent* wlEvent = new WeatherLightningStrikeEvent;
|
||||
wlEvent->mLightning = this;
|
||||
wlEvent->mStart = strikePoint;
|
||||
|
||||
nc->postNetEvent(wlEvent);
|
||||
}
|
||||
}
|
||||
|
||||
TextureHandle* WeatherLightning::getRandomStrike()
|
||||
{
|
||||
U32 strike = (U32)(mCeil(mDataBlock->numStrikes * sgRandomGen.randF()) - 1.0f);
|
||||
return &mDataBlock->strikeTextures[strike];
|
||||
}
|
||||
|
||||
TextureHandle* WeatherLightning::getRandomFlash()
|
||||
{
|
||||
U32 flash = (U32)(mCeil(mDataBlock->numFlashes * sgRandomGen.randF()) - 1.0f);
|
||||
return &mDataBlock->flashTextures[flash];
|
||||
}
|
||||
|
||||
TextureHandle* WeatherLightning::getRandomFuzzy()
|
||||
{
|
||||
U32 fuzzy = (U32)(mCeil(mDataBlock->numFuzzes * sgRandomGen.randF()) - 1.0f);
|
||||
return &mDataBlock->fuzzyTextures[fuzzy];
|
||||
}
|
||||
|
||||
S32 WeatherLightning::getRandomSound()
|
||||
{
|
||||
U32 sound = (U32)(mCeil(mDataBlock->numSounds * sgRandomGen.randF()) - 1.0f);
|
||||
if(mDataBlock->thunderSounds[sound] != NULL_AUDIOHANDLE)
|
||||
return sound;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void WeatherLightning::processEvent(WeatherLightningStrikeEvent* wlEvent)
|
||||
{
|
||||
AssertFatal(wlEvent->mStart.x >= 0 && wlEvent->mStart.x <= 1.0, "Out of bounds coord!");
|
||||
|
||||
mActiveBolts.push_back(new WeatherLightningBolt);
|
||||
WeatherLightningBolt* bolt = mActiveBolts.last();
|
||||
|
||||
Point3F strikePoint(0.0, 0.0, 0.0);
|
||||
strikePoint.x = wlEvent->mStart.x;
|
||||
strikePoint.y = wlEvent->mStart.y;
|
||||
strikePoint *= mObjScale;
|
||||
strikePoint += getPosition();
|
||||
strikePoint += Point3F( -mObjScale.x * 0.5, -mObjScale.y * 0.5, 0.0 );
|
||||
|
||||
RayInfo rayInfo;
|
||||
Point3F start = strikePoint;
|
||||
start.z = mObjScale.z * 0.5 + getPosition().z;
|
||||
strikePoint.z += -mObjScale.z * 0.5;
|
||||
bool rayHit = gClientContainer.castRay(start, strikePoint, (STATIC_COLLISION_MASK | WaterObjectType), &rayInfo);
|
||||
if(rayHit)
|
||||
strikePoint.z = rayInfo.point.z;
|
||||
|
||||
F32 height = mObjScale.z * 0.5 + getPosition().z;
|
||||
|
||||
bolt->startPoint = Point3F(strikePoint.x, strikePoint.y, height);
|
||||
bolt->endPoint = strikePoint;
|
||||
|
||||
bolt->currentAge = 0.0f;
|
||||
bolt->deathAge = boltDeathAge;
|
||||
bolt->strikeTime = 0.35;
|
||||
|
||||
bolt->strikeTexture = getRandomStrike();
|
||||
bolt->flashTexture = getRandomFlash();
|
||||
bolt->fuzzyTexture = getRandomFuzzy();
|
||||
|
||||
// setup a thunder sound event
|
||||
Point3F listener;
|
||||
alxGetListenerPoint3F(AL_POSITION, &listener);
|
||||
|
||||
mSoundEvents.increment();
|
||||
SoundEvent& sEvent = mSoundEvents.last();
|
||||
|
||||
// find the length to the closest point on the bolt
|
||||
Point3F dHat = bolt->startPoint - bolt->endPoint;
|
||||
F32 boltLength = dHat.len();
|
||||
dHat /= boltLength;
|
||||
F32 distAlong = mDot((listener - bolt->endPoint), dHat);
|
||||
|
||||
Point3F contactPoint;
|
||||
if(distAlong >= boltLength)
|
||||
contactPoint = bolt->startPoint;
|
||||
else if(distAlong <= 0.0)
|
||||
contactPoint = bolt->endPoint;
|
||||
else
|
||||
contactPoint = bolt->endPoint + dHat * distAlong;
|
||||
|
||||
F32 delayDist = (listener - contactPoint).len();
|
||||
U32 delayTime = U32((delayDist / 330.0f) * 100.0f);
|
||||
|
||||
MatrixF trans(true);
|
||||
trans.setPosition(contactPoint);
|
||||
|
||||
sEvent.soundBlockId = getRandomSound();
|
||||
sEvent.position = trans;
|
||||
sEvent.time = delayTime;
|
||||
|
||||
// play strike sound
|
||||
trans.setPosition(strikePoint);
|
||||
if(mDataBlock->strikeSound)
|
||||
alxPlay(mDataBlock->strikeSound, &trans);
|
||||
}
|
||||
|
||||
U32 WeatherLightning::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(conn, mask, stream);
|
||||
|
||||
// Only write data if this is the initial packet or we've been inspected.
|
||||
if (stream->writeFlag(mask & (InitialUpdateMask | ExtendedInfoMask)))
|
||||
{
|
||||
// Initial update
|
||||
mathWrite(*stream, getPosition());
|
||||
mathWrite(*stream, mObjScale);
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void WeatherLightning::unpackUpdate(NetConnection *conn, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(conn, stream);
|
||||
|
||||
if (stream->readFlag())
|
||||
{
|
||||
// Initial update
|
||||
Point3F pos;
|
||||
mathRead(*stream, &pos);
|
||||
setPosition( pos );
|
||||
|
||||
mathRead(*stream, &mObjScale);
|
||||
}
|
||||
}
|
||||
172
engine/game/fx/weatherLightning.h
Executable file
172
engine/game/fx/weatherLightning.h
Executable file
@@ -0,0 +1,172 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _WEATHER_LIGHTNING_H_
|
||||
#define _WEATHER_LIGHTNING_H_
|
||||
|
||||
#ifndef _NETCONNECTION_H_
|
||||
#include "sim/netConnection.h"
|
||||
#endif
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
#ifndef _LIGHTMANAGER_H_
|
||||
#include "sceneGraph/lightManager.h"
|
||||
#endif
|
||||
|
||||
class AudioProfile;
|
||||
class WeatherLightning;
|
||||
|
||||
class WeatherLightningStrikeEvent : public NetEvent
|
||||
{
|
||||
typedef NetEvent Parent;
|
||||
|
||||
public:
|
||||
enum Constants {
|
||||
PositionalBits = 10
|
||||
};
|
||||
|
||||
Point2F mStart;
|
||||
WeatherLightning* mLightning;
|
||||
|
||||
public:
|
||||
WeatherLightningStrikeEvent();
|
||||
~WeatherLightningStrikeEvent();
|
||||
|
||||
void pack(NetConnection*, BitStream*);
|
||||
void write(NetConnection*, BitStream*){}
|
||||
void unpack(NetConnection*, BitStream*);
|
||||
void process(NetConnection*);
|
||||
|
||||
DECLARE_CONOBJECT(WeatherLightningStrikeEvent);
|
||||
};
|
||||
|
||||
class WeatherLightningData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
|
||||
public:
|
||||
enum {
|
||||
MaxSounds = 4,
|
||||
MaxStrikeTextures = 6,//8,
|
||||
MaxFlashTextures = 4, //6,
|
||||
MaxFuzzyTextures = 2, //4,
|
||||
};
|
||||
|
||||
// primary strike texture
|
||||
U32 numStrikes;
|
||||
StringTableEntry strikeTextureNames[MaxStrikeTextures];
|
||||
TextureHandle strikeTextures[MaxStrikeTextures];
|
||||
|
||||
// flash texture
|
||||
U32 numFlashes;
|
||||
StringTableEntry flashTextureNames[MaxFlashTextures];
|
||||
TextureHandle flashTextures[MaxFlashTextures];
|
||||
|
||||
// fuzzy/stretch texture
|
||||
U32 numFuzzes;
|
||||
StringTableEntry fuzzyTextureNames[MaxFuzzyTextures];
|
||||
TextureHandle fuzzyTextures[MaxFuzzyTextures];
|
||||
|
||||
//strike sound
|
||||
S32 strikeSoundId;
|
||||
AudioProfile* strikeSound;
|
||||
|
||||
// thunder sounds
|
||||
U32 numSounds;
|
||||
S32 thunderSoundIds[MaxSounds];
|
||||
AudioProfile* thunderSounds[MaxSounds];
|
||||
|
||||
public:
|
||||
WeatherLightningData();
|
||||
~WeatherLightningData();
|
||||
|
||||
void packData(BitStream*);
|
||||
void unpackData(BitStream*);
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
|
||||
DECLARE_CONOBJECT(WeatherLightningData);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
struct WeatherLightningBolt
|
||||
{
|
||||
Point3F startPoint;
|
||||
Point3F endPoint;
|
||||
|
||||
F32 currentAge;
|
||||
F32 deathAge;
|
||||
F32 strikeTime;
|
||||
|
||||
TextureHandle* strikeTexture;
|
||||
TextureHandle* flashTexture;
|
||||
TextureHandle* fuzzyTexture;
|
||||
|
||||
void render(const Point3F &camPos);
|
||||
};
|
||||
|
||||
class WeatherLightning : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
// datablock
|
||||
WeatherLightningData* mDataBlock;
|
||||
|
||||
U32 lastThink;
|
||||
U32 strikesPerMinute;
|
||||
U32 boltDeathAge;
|
||||
|
||||
struct SoundEvent {
|
||||
S32 soundBlockId;
|
||||
MatrixF position;
|
||||
U32 time;
|
||||
};
|
||||
|
||||
// only active on client
|
||||
VectorPtr<WeatherLightningBolt*> mActiveBolts;
|
||||
Vector<SoundEvent> mSoundEvents;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
|
||||
// rendering
|
||||
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
void renderObject(SceneState *state, SceneRenderImage *image);
|
||||
|
||||
// simulation
|
||||
void processTick(const Move *move);
|
||||
void advanceTime(F32 dt);
|
||||
|
||||
// grab random textures
|
||||
TextureHandle* getRandomStrike();
|
||||
TextureHandle* getRandomFlash();
|
||||
TextureHandle* getRandomFuzzy();
|
||||
|
||||
// grab random sounds
|
||||
S32 getRandomSound();
|
||||
|
||||
public:
|
||||
WeatherLightning();
|
||||
~WeatherLightning();
|
||||
|
||||
// strike random point within object box
|
||||
void strikeRandomPoint();
|
||||
|
||||
// receive lightning event and create lightning bolt
|
||||
void processEvent(WeatherLightningStrikeEvent*);
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
DECLARE_CONOBJECT(WeatherLightning);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
#endif // _WEATHER_LIGHTNING_H_
|
||||
983
engine/game/game.cc
Executable file
983
engine/game/game.cc
Executable file
@@ -0,0 +1,983 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformVideo.h"
|
||||
#include "platform/platformAudio.h"
|
||||
#include "platform/platformInput.h"
|
||||
#include "core/findMatch.h"
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "game/game.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/console.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "terrain/terrRender.h"
|
||||
#include "terrain/waterBlock.h"
|
||||
#include "game/collisionTest.h"
|
||||
#include "game/showTSShape.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "gui/core/guiTSControl.h"
|
||||
#include "game/moveManager.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "game/shapeBase.h"
|
||||
#include "core/dnet.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "dgl/gTexManager.h"
|
||||
#include "sceneGraph/sceneLighting.h"
|
||||
#include "terrain/sky.h"
|
||||
#include "game/ambientAudioManager.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "sceneGraph/detailManager.h"
|
||||
#include "gui/controls/guiMLTextCtrl.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "game/fx/underLava.h"
|
||||
#include "core/iTickable.h"
|
||||
//this is an extra comment
|
||||
|
||||
static void cPanoramaScreenShot(SimObject *, S32, const char ** argv);
|
||||
|
||||
void wireCube(F32 size, Point3F pos);
|
||||
|
||||
CollisionTest collisionTest;
|
||||
|
||||
F32 gMovementSpeed = 1;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunctionGroupBegin( GameFunctions, "General game functionality.");
|
||||
|
||||
ConsoleFunction(screenShot, void, 3, 3, "(string file, string format)"
|
||||
"Take a screenshot.\n\n"
|
||||
"@param format One of JPEG or PNG.")
|
||||
{
|
||||
FileStream fStream;
|
||||
if(!fStream.open(argv[1], FileStream::Write))
|
||||
{
|
||||
Con::printf("Failed to open file '%s'.", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
glReadBuffer(GL_FRONT);
|
||||
|
||||
Point2I extent = Canvas->getExtent();
|
||||
U8 * pixels = new U8[extent.x * extent.y * 3];
|
||||
glReadPixels(0, 0, extent.x, extent.y, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
GBitmap * bitmap = new GBitmap;
|
||||
bitmap->allocateBitmap(U32(extent.x), U32(extent.y));
|
||||
|
||||
// flip the rows
|
||||
for(U32 y = 0; y < extent.y; y++)
|
||||
dMemcpy(bitmap->getAddress(0, extent.y - y - 1), pixels + y * extent.x * 3, U32(extent.x * 3));
|
||||
|
||||
if ( dStrcmp( argv[2], "JPEG" ) == 0 )
|
||||
bitmap->writeJPEG(fStream);
|
||||
else if( dStrcmp( argv[2], "PNG" ) == 0)
|
||||
bitmap->writePNG(fStream);
|
||||
else
|
||||
bitmap->writePNG(fStream);
|
||||
|
||||
fStream.close();
|
||||
delete [] pixels;
|
||||
delete bitmap;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static const U32 MaxPlayerNameLength = 16;
|
||||
ConsoleFunction( strToPlayerName, const char*, 2, 2, "strToPlayerName( string )" )
|
||||
{
|
||||
argc;
|
||||
|
||||
const char* ptr = argv[1];
|
||||
|
||||
// Strip leading spaces and underscores:
|
||||
while ( *ptr == ' ' || *ptr == '_' )
|
||||
ptr++;
|
||||
|
||||
U32 len = dStrlen( ptr );
|
||||
if ( len )
|
||||
{
|
||||
char* ret = Con::getReturnBuffer( MaxPlayerNameLength + 1 );
|
||||
char* rptr = ret;
|
||||
ret[MaxPlayerNameLength - 1] = '\0';
|
||||
ret[MaxPlayerNameLength] = '\0';
|
||||
bool space = false;
|
||||
|
||||
U8 ch;
|
||||
while ( *ptr && dStrlen( ret ) < MaxPlayerNameLength )
|
||||
{
|
||||
ch = (U8) *ptr;
|
||||
|
||||
// Strip all illegal characters:
|
||||
if ( ch < 32 || ch == ',' || ch == '.' || ch == '\'' || ch == '`' )
|
||||
{
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't allow double spaces or space-underline combinations:
|
||||
if ( ch == ' ' || ch == '_' )
|
||||
{
|
||||
if ( space )
|
||||
{
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
space = true;
|
||||
}
|
||||
else
|
||||
space = false;
|
||||
|
||||
*rptr++ = *ptr;
|
||||
ptr++;
|
||||
}
|
||||
*rptr = '\0';
|
||||
|
||||
//finally, strip out the ML text control chars...
|
||||
return GuiMLTextCtrl::stripControlChars(ret);
|
||||
return( ret );
|
||||
}
|
||||
|
||||
return( "" );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static U32 moveCount = 0;
|
||||
|
||||
bool GameGetCameraTransform(MatrixF *mat, Point3F *velocity)
|
||||
{
|
||||
// Return the position and velocity of the control object
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
return connection && connection->getControlCameraTransform(0, mat) &&
|
||||
connection->getControlCameraVelocity(velocity);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// Camera and FOV info
|
||||
namespace {
|
||||
|
||||
const U32 MaxZoomSpeed = 2000; ///< max number of ms to reach target FOV
|
||||
|
||||
static F32 sConsoleCameraFov = 90.f; ///< updated to camera FOV each frame
|
||||
static F32 sDefaultFov = 90.f; ///< normal FOV
|
||||
static F32 sCameraFov = 90.f; ///< current camera FOV
|
||||
static F32 sTargetFov = 90.f; ///< the desired FOV
|
||||
static F32 sLastCameraUpdateTime = 0; ///< last time camera was updated
|
||||
static S32 sZoomSpeed = 500; ///< ms per 90deg fov change
|
||||
|
||||
} // namespace {}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleFunctionGroupBegin( CameraFunctions, "Functions controlling the global camera properties defined in main.cc.");
|
||||
|
||||
ConsoleFunction(setDefaultFov, void, 2,2, "(defaultFov) - Set the default FOV for a camera.")
|
||||
{
|
||||
argc;
|
||||
sDefaultFov = mClampF(dAtof(argv[1]), MinCameraFov, MaxCameraFov);
|
||||
if(sCameraFov == sTargetFov)
|
||||
sTargetFov = sDefaultFov;
|
||||
}
|
||||
|
||||
ConsoleFunction(setZoomSpeed, void, 2,2, "(speed) - Set the zoom speed of the camera, in ms per 90deg FOV change.")
|
||||
{
|
||||
argc;
|
||||
sZoomSpeed = mClamp(dAtoi(argv[1]), 0, MaxZoomSpeed);
|
||||
}
|
||||
|
||||
ConsoleFunction(setFov, void, 2, 2, "(fov) - Set the FOV of the camera.")
|
||||
{
|
||||
argc;
|
||||
sTargetFov = mClampF(dAtof(argv[1]), MinCameraFov, MaxCameraFov);
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd( CameraFunctions );
|
||||
|
||||
F32 GameGetCameraFov()
|
||||
{
|
||||
return(sCameraFov);
|
||||
}
|
||||
|
||||
void GameSetCameraFov(F32 fov)
|
||||
{
|
||||
sTargetFov = sCameraFov = fov;
|
||||
}
|
||||
|
||||
void GameSetCameraTargetFov(F32 fov)
|
||||
{
|
||||
sTargetFov = fov;
|
||||
}
|
||||
|
||||
void GameUpdateCameraFov()
|
||||
{
|
||||
F32 time = F32(Platform::getVirtualMilliseconds());
|
||||
|
||||
// need to update fov?
|
||||
if(sTargetFov != sCameraFov)
|
||||
{
|
||||
F32 delta = time - sLastCameraUpdateTime;
|
||||
|
||||
// snap zoom?
|
||||
if((sZoomSpeed == 0) || (delta <= 0.f))
|
||||
sCameraFov = sTargetFov;
|
||||
else
|
||||
{
|
||||
// gZoomSpeed is time in ms to zoom 90deg
|
||||
F32 step = 90.f * (delta / F32(sZoomSpeed));
|
||||
|
||||
if(sCameraFov > sTargetFov)
|
||||
{
|
||||
sCameraFov -= step;
|
||||
if(sCameraFov < sTargetFov)
|
||||
sCameraFov = sTargetFov;
|
||||
}
|
||||
else
|
||||
{
|
||||
sCameraFov += step;
|
||||
if(sCameraFov > sTargetFov)
|
||||
sCameraFov = sTargetFov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the game connection controls the vertical and the horizontal
|
||||
GameConnection * connection = GameConnection::getConnectionToServer();
|
||||
if(connection)
|
||||
{
|
||||
// check if fov is valid on control object
|
||||
if(connection->isValidControlCameraFov(sCameraFov))
|
||||
connection->setControlCameraFov(sCameraFov);
|
||||
else
|
||||
{
|
||||
// will set to the closest fov (fails only on invalid control object)
|
||||
if(connection->setControlCameraFov(sCameraFov))
|
||||
{
|
||||
F32 setFov = sCameraFov;
|
||||
connection->getControlCameraFov(&setFov);
|
||||
sTargetFov = sCameraFov = setFov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the console variable
|
||||
sConsoleCameraFov = sCameraFov;
|
||||
sLastCameraUpdateTime = time;
|
||||
}
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// ConsoleFunction(dumpTSShapes, void, 1, 1, "dumpTSShapes();")
|
||||
// {
|
||||
// argc, argv;
|
||||
|
||||
// FindMatch match("*.dts", 4096);
|
||||
// ResourceManager->findMatches(&match);
|
||||
|
||||
// for (U32 i = 0; i < match.numMatches(); i++)
|
||||
// {
|
||||
// U32 j;
|
||||
// Resource<TSShape> shape = ResourceManager->load(match.matchList[i]);
|
||||
// if (bool(shape) == false)
|
||||
// Con::errorf(" aaa Couldn't load: %s", match.matchList[i]);
|
||||
|
||||
// U32 numMeshes = 0, numSkins = 0;
|
||||
// for (j = 0; j < shape->meshes.size(); j++)
|
||||
// if (shape->meshes[j])
|
||||
// numMeshes++;
|
||||
// for (j = 0; j < shape->skins.size(); j++)
|
||||
// if (shape->skins[j])
|
||||
// numSkins++;
|
||||
|
||||
// Con::printf(" aaa Shape: %s (%d meshes, %d skins)", match.matchList[i], numMeshes, numSkins);
|
||||
// Con::printf(" aaa Meshes");
|
||||
// for (j = 0; j < shape->meshes.size(); j++)
|
||||
// {
|
||||
// if (shape->meshes[j])
|
||||
// Con::printf(" aaa %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)",
|
||||
// shape->meshes[j]->meshType & TSMesh::TypeMask,
|
||||
// shape->meshes[j]->numFrames,
|
||||
// shape->meshes[j]->numMatFrames,
|
||||
// shape->meshes[j]->vertsPerFrame,
|
||||
// shape->meshes[j]->verts.size(),
|
||||
// shape->meshes[j]->norms.size(),
|
||||
// shape->meshes[j]->tverts.size(),
|
||||
// shape->meshes[j]->primitives.size(),
|
||||
// shape->meshes[j]->indices.size());
|
||||
// }
|
||||
// Con::printf(" aaa Skins");
|
||||
// for (j = 0; j < shape->skins.size(); j++)
|
||||
// {
|
||||
// if (shape->skins[j])
|
||||
// Con::printf(" aaa %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)",
|
||||
// shape->skins[j]->meshType & TSMesh::TypeMask,
|
||||
// shape->skins[j]->numFrames,
|
||||
// shape->skins[j]->numMatFrames,
|
||||
// shape->skins[j]->vertsPerFrame,
|
||||
// shape->skins[j]->verts.size(),
|
||||
// shape->skins[j]->norms.size(),
|
||||
// shape->skins[j]->tverts.size(),
|
||||
// shape->skins[j]->primitives.size(),
|
||||
// shape->skins[j]->indices.size());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
#endif
|
||||
|
||||
ConsoleFunction( getControlObjectAltitude, const char*, 1, 1, "Get distance from bottom of controlled object to terrain.")
|
||||
{
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
if (connection) {
|
||||
ShapeBase* pSB = connection->getControlObject();
|
||||
if (pSB != NULL && pSB->isClientObject())
|
||||
{
|
||||
Point3F pos(0.f, 0.f, 0.f);
|
||||
|
||||
// if this object is mounted, then get the bottom position of the mount's bbox
|
||||
if(pSB->getObjectMount())
|
||||
{
|
||||
static Point3F BoxPnts[] = {
|
||||
Point3F(0,0,0),
|
||||
Point3F(0,0,1),
|
||||
Point3F(0,1,0),
|
||||
Point3F(0,1,1),
|
||||
Point3F(1,0,0),
|
||||
Point3F(1,0,1),
|
||||
Point3F(1,1,0),
|
||||
Point3F(1,1,1)
|
||||
};
|
||||
|
||||
ShapeBase * mount = pSB->getObjectMount();
|
||||
Box3F box = mount->getObjBox();
|
||||
MatrixF mat = mount->getTransform();
|
||||
VectorF scale = mount->getScale();
|
||||
|
||||
Point3F projPnts[8];
|
||||
F32 minZ = 1e30;
|
||||
|
||||
for(U32 i = 0; i < 8; i++)
|
||||
{
|
||||
Point3F pnt(BoxPnts[i].x ? box.max.x : box.min.x,
|
||||
BoxPnts[i].y ? box.max.y : box.min.y,
|
||||
BoxPnts[i].z ? box.max.z : box.min.z);
|
||||
|
||||
pnt.convolve(scale);
|
||||
mat.mulP(pnt, &projPnts[i]);
|
||||
|
||||
if(projPnts[i].z < minZ)
|
||||
pos = projPnts[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
pSB->getTransform().getColumn(3, &pos);
|
||||
|
||||
TerrainBlock* pBlock = gClientSceneGraph->getCurrentTerrain();
|
||||
if (pBlock != NULL) {
|
||||
Point3F terrPos = pos;
|
||||
pBlock->getWorldTransform().mulP(terrPos);
|
||||
terrPos.convolveInverse(pBlock->getScale());
|
||||
|
||||
F32 height;
|
||||
if (pBlock->getHeight(Point2F(terrPos.x, terrPos.y), &height) == true) {
|
||||
terrPos.z = height;
|
||||
terrPos.convolve(pBlock->getScale());
|
||||
pBlock->getTransform().mulP(terrPos);
|
||||
|
||||
pos.z -= terrPos.z;
|
||||
}
|
||||
}
|
||||
|
||||
char* retBuf = Con::getReturnBuffer(128);
|
||||
dSprintf(retBuf, 128, "%g", mFloor(getMax(pos.z, 0.f)));
|
||||
return retBuf;
|
||||
}
|
||||
}
|
||||
|
||||
return "0";
|
||||
}
|
||||
|
||||
ConsoleFunction( getControlObjectSpeed, const char*, 1, 1, "Get speed (but not velocity) of controlled object.")
|
||||
{
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
if (connection)
|
||||
{
|
||||
ShapeBase* pSB = connection->getControlObject();
|
||||
if (pSB != NULL && pSB->isClientObject()) {
|
||||
Point3F vel = pSB->getVelocity();
|
||||
F32 speed = vel.len();
|
||||
|
||||
// We're going to force the formating to be what we want...
|
||||
F32 intPart = mFloor(speed);
|
||||
speed -= intPart;
|
||||
speed *= 10;
|
||||
speed = mFloor(speed);
|
||||
|
||||
char* retBuf = Con::getReturnBuffer(128);
|
||||
dSprintf(retBuf, 128, "%g.%g", intPart, speed);
|
||||
return retBuf;
|
||||
}
|
||||
}
|
||||
|
||||
return "0";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction( panoramaScreenShot, void, 3, 3, "(string file, string format)"
|
||||
"Take a panoramic screenshot.\n\n"
|
||||
"@param format This is either JPEG or PNG.")
|
||||
{
|
||||
S32 numShots = 3;
|
||||
if (argc == 3)
|
||||
numShots = dAtoi(argv[2]);
|
||||
|
||||
CameraQuery query;
|
||||
if (!GameProcessCameraQuery( &query ))
|
||||
return;
|
||||
|
||||
SceneObject *object = dynamic_cast<SceneObject*>(query.object);
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
F32 rotInc = query.fov * 0.75f;
|
||||
|
||||
FileStream fStream;
|
||||
GBitmap bitmap;
|
||||
Point2I extent = Canvas->getExtent();
|
||||
bitmap.allocateBitmap(U32(extent.x), U32(extent.y));
|
||||
U8 * pixels = new U8[extent.x * extent.y * 3];
|
||||
|
||||
|
||||
S32 start = -(numShots/2);
|
||||
for (S32 i=0; i<numShots; i++, start++)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
MatrixF rot( EulerF(0.0f, 0.0f, rotInc * F32(start)) );
|
||||
MatrixF result;
|
||||
result.mul(query.cameraMatrix, rot);
|
||||
|
||||
object->setTransform( result );
|
||||
Canvas->renderFrame(false);
|
||||
dSprintf(buffer, sizeof(buffer), "%s-%d.png", argv[1], i);
|
||||
|
||||
glReadBuffer(GL_FRONT);
|
||||
glReadPixels(0, 0, extent.x, extent.y, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
if(!fStream.open(buffer, FileStream::Write))
|
||||
{
|
||||
Con::printf("Failed to open file '%s'.", buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
// flip the rows
|
||||
for(U32 y = 0; y < extent.y; y++)
|
||||
dMemcpy(bitmap.getAddress(0, extent.y - y - 1), pixels + y * extent.x * 3, U32(extent.x * 3));
|
||||
|
||||
if ( dStrcmp( argv[2], "JPEG" ) == 0 )
|
||||
bitmap.writeJPEG(fStream);
|
||||
else if( dStrcmp( argv[2], "PNG" ) == 0)
|
||||
bitmap.writePNG(fStream);
|
||||
else
|
||||
bitmap.writePNG(fStream);
|
||||
|
||||
fStream.close();
|
||||
}
|
||||
|
||||
delete [] pixels;
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd( GameFunctions );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GameInit()
|
||||
{
|
||||
// Make sure the exporter draws from the correct directories...
|
||||
//
|
||||
Con::addVariable("movementSpeed", TypeF32, &gMovementSpeed);
|
||||
Con::addVariable("$pref::OpenGL::disableEXTPalettedTexture", TypeBool, &gOpenGLDisablePT);
|
||||
Con::addVariable("$pref::OpenGL::disableEXTCompiledVertexArray", TypeBool, &gOpenGLDisableCVA);
|
||||
Con::addVariable("$pref::OpenGL::disableARBMultitexture", TypeBool, &gOpenGLDisableARBMT);
|
||||
Con::addVariable("$pref::OpenGL::disableEXTFogCoord", TypeBool, &gOpenGLDisableFC);
|
||||
Con::addVariable("$pref::OpenGL::disableEXTTexEnvCombine", TypeBool, &gOpenGLDisableTEC);
|
||||
Con::addVariable("$pref::OpenGL::disableARBTextureCompression", TypeBool, &gOpenGLDisableTCompress);
|
||||
Con::addVariable("$pref::OpenGL::noEnvColor", TypeBool, &gOpenGLNoEnvColor);
|
||||
Con::addVariable("$pref::OpenGL::gammaCorrection", TypeF32, &gOpenGLGammaCorrection);
|
||||
Con::addVariable("$pref::OpenGL::noDrawArraysAlpha", TypeBool, &gOpenGLNoDrawArraysAlpha);
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
gOpenGLDisableFC = true;
|
||||
#else
|
||||
Con::addVariable("$pref::OpenGL::disableEXTFogCoord", TypeBool, &gOpenGLDisableFC);
|
||||
#endif
|
||||
|
||||
Con::addVariable("$pref::TS::autoDetail", TypeF32, &DetailManager::smDetailScale);
|
||||
Con::addVariable("$pref::visibleDistanceMod", TypeF32, &SceneGraph::smVisibleDistanceMod);
|
||||
|
||||
// updated every frame
|
||||
Con::addVariable("cameraFov", TypeF32, &sConsoleCameraFov);
|
||||
|
||||
// Initialize the collision testing script stuff.
|
||||
collisionTest.consoleInit();
|
||||
}
|
||||
|
||||
const U32 AudioUpdatePeriod = 125; ///< milliseconds between audio updates.
|
||||
|
||||
bool clientProcess(U32 timeDelta)
|
||||
{
|
||||
ShowTSShape::advanceTime(timeDelta);
|
||||
ITickable::advanceTime(timeDelta);
|
||||
|
||||
bool ret = gClientProcessList.advanceClientTime(timeDelta);
|
||||
|
||||
// Run the collision test and update the Audio system
|
||||
// by checking the controlObject
|
||||
MatrixF mat;
|
||||
Point3F velocity;
|
||||
|
||||
if (GameGetCameraTransform(&mat, &velocity))
|
||||
{
|
||||
alxListenerMatrixF(&mat);
|
||||
// alxListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z);
|
||||
collisionTest.collide(mat);
|
||||
}
|
||||
|
||||
// determine if were lagging
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
if(connection)
|
||||
connection->detectLag();
|
||||
|
||||
// alxUpdate is somewhat expensive and does not need to be updated constantly,
|
||||
// though it does need to be updated in real time
|
||||
static U32 lastAudioUpdate = 0;
|
||||
U32 realTime = Platform::getRealMilliseconds();
|
||||
if((realTime - lastAudioUpdate) >= AudioUpdatePeriod)
|
||||
{
|
||||
alxUpdate();
|
||||
gAmbientAudioManager.update();
|
||||
lastAudioUpdate = realTime;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool serverProcess(U32 timeDelta)
|
||||
{
|
||||
return gServerProcessList.advanceServerTime(timeDelta);
|
||||
}
|
||||
|
||||
static ColorF cubeColors[8] = {
|
||||
ColorF(0, 0, 0),
|
||||
ColorF(1, 0, 0),
|
||||
ColorF(0, 1, 0),
|
||||
ColorF(0, 0, 1),
|
||||
ColorF(1, 1, 0),
|
||||
ColorF(1, 0, 1),
|
||||
ColorF(0, 1, 1),
|
||||
ColorF(1, 1, 1)
|
||||
};
|
||||
|
||||
static Point3F cubePoints[8] = {
|
||||
Point3F(-1, -1, -1),
|
||||
Point3F(-1, -1, 1),
|
||||
Point3F(-1, 1, -1),
|
||||
Point3F(-1, 1, 1),
|
||||
Point3F( 1, -1, -1),
|
||||
Point3F( 1, -1, 1),
|
||||
Point3F( 1, 1, -1),
|
||||
Point3F( 1, 1, 1)
|
||||
};
|
||||
|
||||
static U32 cubeFaces[6][4] = {
|
||||
{ 0, 2, 6, 4 },
|
||||
{ 0, 2, 3, 1 },
|
||||
{ 0, 1, 5, 4 },
|
||||
{ 3, 2, 6, 7 },
|
||||
{ 7, 6, 4, 5 },
|
||||
{ 3, 7, 5, 1 }
|
||||
};
|
||||
|
||||
void wireCube(F32 size, Point3F pos)
|
||||
{
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
for (S32 i = 0; i < 6; i++)
|
||||
{
|
||||
glBegin(GL_LINE_LOOP);
|
||||
for(S32 vert = 0; vert < 4; vert++)
|
||||
{
|
||||
U32 idx = cubeFaces[i][vert];
|
||||
glColor3f(cubeColors[idx].red, cubeColors[idx].green, cubeColors[idx].blue);
|
||||
glVertex3f(cubePoints[idx].x * size + pos.x, cubePoints[idx].y * size + pos.y, cubePoints[idx].z * size + pos.z);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
bool GameProcessCameraQuery(CameraQuery *query)
|
||||
{
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
|
||||
if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix))
|
||||
{
|
||||
query->object = connection->getControlObject();
|
||||
query->nearPlane = 0.1f;
|
||||
|
||||
Sky* pSky = gClientSceneGraph->getCurrentSky();
|
||||
|
||||
if (pSky)
|
||||
query->farPlane = pSky->getVisibleDistance();
|
||||
else
|
||||
query->farPlane = 1000.0f;
|
||||
|
||||
F32 cameraFov;
|
||||
if(!connection->getControlCameraFov(&cameraFov))
|
||||
return false;
|
||||
|
||||
query->fov = mDegToRad(cameraFov);
|
||||
query->ortho = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
struct OutputPoint
|
||||
{
|
||||
Point3F point;
|
||||
U8 color[4];
|
||||
Point2F texCoord;
|
||||
Point2F fogCoord;
|
||||
};
|
||||
|
||||
#define USEOLDFILTERS 1
|
||||
|
||||
void GameRenderFilters(const CameraQuery& camq)
|
||||
{
|
||||
#if USEOLDFILTERS
|
||||
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
|
||||
F32 damageFlash = 0;
|
||||
F32 whiteOut = 0;
|
||||
F32 blackOut = 0;
|
||||
|
||||
if(connection)
|
||||
{
|
||||
damageFlash = connection->getDamageFlash();
|
||||
whiteOut = connection->getWhiteOut();
|
||||
blackOut = connection->getBlackOut();
|
||||
}
|
||||
|
||||
ShapeBase* psb = dynamic_cast<ShapeBase*>(camq.object);
|
||||
if (psb != NULL) {
|
||||
if (damageFlash > 0.0) {
|
||||
if (damageFlash > 0.76)
|
||||
damageFlash = 0.76f;
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(1, 0, 0, damageFlash);
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex3f(-1, -1, 0);
|
||||
glVertex3f(-1, 1, 0);
|
||||
glVertex3f( 1, 1, 0);
|
||||
glVertex3f( 1, -1, 0);
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
if (whiteOut > 0.0) {
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(1.0f, 1.0f, 0.92f, (whiteOut > 1.0f ? 1.0f : whiteOut));
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex3f(-1, -1, 0);
|
||||
glVertex3f(-1, 1, 0);
|
||||
glVertex3f( 1, 1, 0);
|
||||
glVertex3f( 1, -1, 0);
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
if (blackOut > 0.0) {
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, (blackOut > 1.0f ? 1.0f : blackOut));
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex3f(-1, -1, 0);
|
||||
glVertex3f(-1, 1, 0);
|
||||
glVertex3f( 1, 1, 0);
|
||||
glVertex3f( 1, -1, 0);
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
F32 invincible = psb->getInvincibleEffect();
|
||||
if (invincible > 0.0) {
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0, 0, 1, (invincible > 1.0f ? 1.0f : invincible));
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex3f(-1, -1, 0);
|
||||
glVertex3f(-1, 1, 0);
|
||||
glVertex3f( 1, 1, 0);
|
||||
glVertex3f( 1, -1, 0);
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
if (WaterBlock::mCameraSubmerged)
|
||||
{
|
||||
if (WaterBlock::isWater(WaterBlock::mSubmergedType))
|
||||
{
|
||||
// view filter for camera below the water surface
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(.2, .6, .6, .3);
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex3f(-1, -1, 0);
|
||||
glVertex3f(-1, 1, 0);
|
||||
glVertex3f( 1, 1, 0);
|
||||
glVertex3f( 1, -1, 0);
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
else if (WaterBlock::isLava(WaterBlock::mSubmergedType))
|
||||
{
|
||||
gLavaFX.render();
|
||||
}
|
||||
else if (WaterBlock::isQuicksand(WaterBlock::mSubmergedType))
|
||||
{
|
||||
}
|
||||
}
|
||||
WaterBlock::mCameraSubmerged = false;
|
||||
WaterBlock::mSubmergedType = 0;
|
||||
}
|
||||
#else
|
||||
|
||||
//
|
||||
// Need to build a filter for damage, invincibility, underwater, whiteout... ect
|
||||
//
|
||||
// Damage, Whiteout, and invincible effects have constant color with variable
|
||||
// alpha values. The water filter has a constant color and alpha value. This
|
||||
// looks kinda tricky, and it is. See Frohn for more details Jett-
|
||||
|
||||
// first get the game connection for this player
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
|
||||
bool addWaterFilter = false;
|
||||
bool addLavaFilter = false;
|
||||
F32 maxDamageFilter = 0.77;
|
||||
F32 damageFlash = 0.f; ColorF damageFilterColor(1.f, 0.f, 0.f, 0.f);
|
||||
F32 whiteOut = 0.f; ColorF whiteoutFilterColor(1.f, 1.f, 1.f, 0.f);
|
||||
F32 waterFilter = 0.f; ColorF waterFilterColor(0.2, 0.6, 0.6, 0.6);
|
||||
F32 invincible = 0.f; ColorF invincibleFilterColor(0.f, 0.f, 1.f, 0.f);
|
||||
|
||||
// final color and alpha of filter + an adder
|
||||
ColorF Xcolor(0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
if(connection)
|
||||
{
|
||||
// grab the damage flash alpha value
|
||||
damageFlash = connection->getDamageFlash();
|
||||
if( damageFlash > maxDamageFilter )
|
||||
damageFlash = maxDamageFilter;
|
||||
|
||||
damageFilterColor.alpha = damageFlash;
|
||||
|
||||
// grab the whiteout value
|
||||
whiteoutFilterColor.alpha = connection->getWhiteOut();
|
||||
|
||||
// need to grab the player obj to get inv. alpha value
|
||||
ShapeBase* psb = dynamic_cast<ShapeBase*>(camq.object);
|
||||
if(psb != NULL)
|
||||
invincibleFilterColor.alpha = psb->getInvincibleEffect();
|
||||
|
||||
// determine if we need to add in our water filter (constant color and alpha)
|
||||
if( WaterBlock::mCameraSubmerged )
|
||||
{
|
||||
if( WaterBlock::isWater( WaterBlock::mSubmergedType ) )
|
||||
addWaterFilter = true;
|
||||
else if( WaterBlock::isLava( WaterBlock::mSubmergedType))
|
||||
addLavaFilter = true;
|
||||
}
|
||||
|
||||
// compute the final color and alpha
|
||||
Xcolor = ( Xcolor * ( 1 - damageFilterColor.alpha ) ) + ( damageFilterColor * damageFilterColor.alpha );
|
||||
Xcolor.alpha = Xcolor.alpha * ( 1 - damageFilterColor.alpha );
|
||||
|
||||
Xcolor = ( Xcolor * ( 1 - whiteoutFilterColor.alpha ) ) + ( whiteoutFilterColor * whiteoutFilterColor.alpha );
|
||||
Xcolor.alpha = Xcolor.alpha * ( 1 - whiteoutFilterColor.alpha );
|
||||
|
||||
Xcolor = ( Xcolor * ( 1 - invincibleFilterColor.alpha ) ) + ( invincibleFilterColor * invincibleFilterColor.alpha );
|
||||
Xcolor.alpha = Xcolor.alpha * ( 1 - invincibleFilterColor.alpha );
|
||||
|
||||
// if were sitting in water, then add that filter in as well.
|
||||
if(addWaterFilter)
|
||||
{
|
||||
Xcolor = ( Xcolor * ( 1 - waterFilterColor.alpha ) ) + ( waterFilterColor * waterFilterColor.alpha );
|
||||
Xcolor.alpha = Xcolor.alpha * ( 1 - waterFilterColor.alpha );
|
||||
}
|
||||
|
||||
// draw our filter with final color
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
glColor4f(Xcolor.red, Xcolor.blue, Xcolor.blue, Xcolor.alpha);
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex3f(-1, -1, 0);
|
||||
glVertex3f(-1, 1, 0);
|
||||
glVertex3f( 1, 1, 0);
|
||||
glVertex3f( 1, -1, 0);
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
|
||||
|
||||
// if were under lava, apply appropriate texture
|
||||
if( addLavaFilter )
|
||||
{
|
||||
gLavaFX.render();
|
||||
}
|
||||
|
||||
WaterBlock::mCameraSubmerged = false;
|
||||
WaterBlock::mSubmergedType = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void GameRenderWorld()
|
||||
{
|
||||
PROFILE_START(GameRenderWorld);
|
||||
FrameAllocator::setWaterMark(0);
|
||||
|
||||
#if defined(TORQUE_GATHER_METRICS) && TORQUE_GATHER_METRICS > 1
|
||||
TextureManager::smTextureCacheMisses = 0;
|
||||
#endif
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
dglSetCanonicalState();
|
||||
gClientSceneGraph->renderScene();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
collisionTest.render();
|
||||
|
||||
#if defined(TORQUE_GATHER_METRICS) && TORQUE_GATHER_METRICS > 1
|
||||
Con::setFloatVariable("Video::texResidentPercentage",
|
||||
TextureManager::getResidentFraction());
|
||||
Con::setIntVariable("Video::textureCacheMisses",
|
||||
TextureManager::smTextureCacheMisses);
|
||||
#endif
|
||||
|
||||
AssertFatal(FrameAllocator::getWaterMark() == 0, "Error, someone didn't reset the water mark on the frame allocator!");
|
||||
FrameAllocator::setWaterMark(0);
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
39
engine/game/game.h
Executable file
39
engine/game/game.h
Executable file
@@ -0,0 +1,39 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAME_H_
|
||||
#define _GAME_H_
|
||||
|
||||
struct CameraQuery;
|
||||
|
||||
const F32 MinCameraFov = 1.f; ///< min camera FOV
|
||||
const F32 MaxCameraFov = 179.f; ///< max camera FOV
|
||||
|
||||
/// Actually renders the world. This is the function that will render the scene ONLY - new guis, no damage flashes.
|
||||
void GameRenderWorld();
|
||||
/// Renders overlays such as damage flashes, white outs, and water masks. These are usually a color applied over the entire screen.
|
||||
void GameRenderFilters(const CameraQuery& camq);
|
||||
/// Does the same thing as GameGetCameraTransform, but fills in other data including information about the far and near clipping planes.
|
||||
bool GameProcessCameraQuery(CameraQuery *query);
|
||||
/// Gets the position, rotation, and velocity of the camera.
|
||||
bool GameGetCameraTransform(MatrixF *mat, Point3F *velocity);
|
||||
/// Gets the camera field of view angle.
|
||||
F32 GameGetCameraFov();
|
||||
/// Sets the field of view angle of the camera.
|
||||
void GameSetCameraFov(F32 fov);
|
||||
/// Sets where the camera fov will be change to. This is for non-instantaneous zooms/retractions.
|
||||
void GameSetCameraTargetFov(F32 fov);
|
||||
/// Update the camera fov to be closer to the target fov.
|
||||
void GameUpdateCameraFov();
|
||||
/// Initializes graphics related console variables.
|
||||
void GameInit();
|
||||
|
||||
/// Processes the next frame, including gui, rendering, and tick interpolation.
|
||||
/// This function will only have an effect when executed on the client.
|
||||
bool clientProcess(U32 timeDelta);
|
||||
/// Processes the next cycle on the server. This function will only have an effect when executed on the server.
|
||||
bool serverProcess(U32 timeDelta);
|
||||
|
||||
#endif
|
||||
518
engine/game/gameBase.cc
Executable file
518
engine/game/gameBase.cc
Executable file
@@ -0,0 +1,518 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "game/gameBase.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "dgl/dgl.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Ghost update relative priority values
|
||||
|
||||
static F32 sUpFov = 1.0;
|
||||
static F32 sUpDistance = 0.4;
|
||||
static F32 sUpVelocity = 0.4;
|
||||
static F32 sUpSkips = 0.2;
|
||||
static F32 sUpOwnership = 0.2;
|
||||
static F32 sUpInterest = 0.2;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(GameBaseData);
|
||||
|
||||
GameBaseData::GameBaseData()
|
||||
{
|
||||
category = "";
|
||||
className = "";
|
||||
packed = false;
|
||||
}
|
||||
|
||||
bool GameBaseData::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// Link our object name to the datablock class name and
|
||||
// then onto our C++ class name.
|
||||
const char* name = getName();
|
||||
if (name && name[0] && getClassRep()) {
|
||||
bool linkSuccess = false;
|
||||
Namespace *parent = getClassRep()->getNameSpace();
|
||||
if (className && className[0] && dStricmp(className, parent->mName)) {
|
||||
linkSuccess = Con::linkNamespaces(parent->mName,className);
|
||||
if(linkSuccess)
|
||||
linkSuccess = Con::linkNamespaces(className,name);
|
||||
}
|
||||
else
|
||||
linkSuccess = Con::linkNamespaces(parent->mName,name);
|
||||
if(linkSuccess)
|
||||
mNameSpace = Con::lookupNamespace(name);
|
||||
}
|
||||
|
||||
// If no className was specified, set it to our C++ class name
|
||||
if (!className || !className[0])
|
||||
className = getClassRep()->getClassName();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBaseData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
addField("category", TypeCaseString, Offset(category, GameBaseData));
|
||||
addField("className", TypeString, Offset(className, GameBaseData));
|
||||
}
|
||||
|
||||
bool GameBaseData::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if (!Parent::preload(server, errorBuffer))
|
||||
return false;
|
||||
packed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBaseData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
packed = true;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool UNPACK_DB_ID(BitStream * stream, U32 & id)
|
||||
{
|
||||
if (stream->readFlag())
|
||||
{
|
||||
id = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PACK_DB_ID(BitStream * stream, U32 id)
|
||||
{
|
||||
if (stream->writeFlag(id))
|
||||
{
|
||||
stream->writeRangedU32(id,DataBlockObjectIdFirst,DataBlockObjectIdLast);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PRELOAD_DB(U32 & id, SimDataBlock ** data, bool server, const char * clientMissing, const char * serverMissing)
|
||||
{
|
||||
if (server)
|
||||
{
|
||||
if (*data)
|
||||
id = (*data)->getId();
|
||||
else if (server && serverMissing)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General,serverMissing);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (id && !Sim::findObject(id,*data) && clientMissing)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General,clientMissing);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool GameBase::gShowBoundingBox;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_NETOBJECT_V1(GameBase);
|
||||
|
||||
GameBase::GameBase()
|
||||
{
|
||||
mNetFlags.set(Ghostable);
|
||||
mTypeMask |= GameBaseObjectType;
|
||||
|
||||
mProcessLink.next = mProcessLink.prev = this;
|
||||
mAfterObject = 0;
|
||||
mProcessTag = 0;
|
||||
mLastDelta = 0;
|
||||
mDataBlock = 0;
|
||||
mProcessTick = true;
|
||||
mNameTag = "";
|
||||
mControllingClient = 0;
|
||||
}
|
||||
|
||||
GameBase::~GameBase()
|
||||
{
|
||||
plUnlink();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool GameBase::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd() || !mDataBlock)
|
||||
return false;
|
||||
|
||||
if (isClientObject()) {
|
||||
// Client datablock are initialized by the initial update
|
||||
gClientProcessList.addObject(this);
|
||||
}
|
||||
else {
|
||||
// Datablock must be initialized on the server
|
||||
if (!onNewDataBlock(mDataBlock))
|
||||
return false;
|
||||
gServerProcessList.addObject(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBase::onRemove()
|
||||
{
|
||||
plUnlink();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
bool GameBase::onNewDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
mDataBlock = dptr;
|
||||
|
||||
if (!mDataBlock)
|
||||
return false;
|
||||
|
||||
setMaskBits(DataBlockMask);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBase::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
setMaskBits(ExtendedInfoMask);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void GameBase::processTick(const Move*)
|
||||
{
|
||||
mLastDelta = 0;
|
||||
}
|
||||
|
||||
void GameBase::interpolateTick(F32 delta)
|
||||
{
|
||||
mLastDelta = delta;
|
||||
}
|
||||
|
||||
void GameBase::advanceTime(F32)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
|
||||
{
|
||||
updateMask;
|
||||
|
||||
// Calculate a priority used to decide if this object
|
||||
// will be updated on the client. All the weights
|
||||
// are calculated 0 -> 1 Then weighted together at the
|
||||
// end to produce a priority.
|
||||
Point3F pos;
|
||||
getWorldBox().getCenter(&pos);
|
||||
pos -= camInfo->pos;
|
||||
F32 dist = pos.len();
|
||||
if (dist == 0.0f) dist = 0.001f;
|
||||
pos *= 1.0f / dist;
|
||||
|
||||
// Weight based on linear distance, the basic stuff.
|
||||
F32 wDistance = (dist < camInfo->visibleDistance)?
|
||||
1.0f - (dist / camInfo->visibleDistance): 0.0f;
|
||||
|
||||
// Weight by field of view, objects directly in front
|
||||
// will be weighted 1, objects behind will be 0
|
||||
F32 dot = mDot(pos,camInfo->orientation);
|
||||
bool inFov = dot > camInfo->cosFov;
|
||||
F32 wFov = inFov? 1.0f: 0;
|
||||
|
||||
// Weight by linear velocity parallel to the viewing plane
|
||||
// (if it's the field of view, 0 if it's not).
|
||||
F32 wVelocity = 0.0f;
|
||||
if (inFov)
|
||||
{
|
||||
Point3F vec;
|
||||
mCross(camInfo->orientation,getVelocity(),&vec);
|
||||
wVelocity = (vec.len() * camInfo->fov) /
|
||||
(camInfo->fov * camInfo->visibleDistance);
|
||||
if (wVelocity > 1.0f)
|
||||
wVelocity = 1.0f;
|
||||
}
|
||||
|
||||
// Weight by interest.
|
||||
F32 wInterest;
|
||||
if (getType() & PlayerObjectType)
|
||||
wInterest = 0.75f;
|
||||
else if (getType() & ProjectileObjectType)
|
||||
{
|
||||
// Projectiles are more interesting if they
|
||||
// are heading for us.
|
||||
wInterest = 0.30f;
|
||||
F32 dot = -mDot(pos,getVelocity());
|
||||
if (dot > 0.0f)
|
||||
wInterest += 0.20 * dot;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getType() & ItemObjectType)
|
||||
wInterest = 0.25f;
|
||||
else
|
||||
// Everything else is less interesting.
|
||||
wInterest = 0.0f;
|
||||
}
|
||||
|
||||
// Weight by updateSkips
|
||||
F32 wSkips = updateSkips * 0.5;
|
||||
|
||||
// Calculate final priority, should total to about 1.0f
|
||||
//
|
||||
return
|
||||
wFov * sUpFov +
|
||||
wDistance * sUpDistance +
|
||||
wVelocity * sUpVelocity +
|
||||
wSkips * sUpSkips +
|
||||
wInterest * sUpInterest;
|
||||
}
|
||||
|
||||
void GameBase::drawBoundingBox(bool useRenderTransform)
|
||||
{
|
||||
const MatrixF & mat = useRenderTransform ? getRenderTransform() : getTransform();
|
||||
Box3F wbox = useRenderTransform ? getRenderWorldBox() : mWorldBox;
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
Point3F box;
|
||||
glPushMatrix();
|
||||
dglMultMatrix(&mat);
|
||||
box = (mObjBox.min + mObjBox.max) * 0.5;
|
||||
glTranslatef(box.x,box.y,box.z);
|
||||
box = (mObjBox.max - mObjBox.min) * 0.5;
|
||||
glScalef(box.x,box.y,box.z);
|
||||
glColor3f(1, 0, 1);
|
||||
dglWireCube(Point3F(1,1,1),Point3F(0,0,0));
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
box = (wbox.min + wbox.max) * 0.5;
|
||||
glTranslatef(box.x,box.y,box.z);
|
||||
box = (wbox.max - wbox.min) * 0.5;
|
||||
glScalef(box.x,box.y,box.z);
|
||||
glColor3f(0, 1, 1);
|
||||
dglWireCube(Point3F(1,1,1),Point3F(0,0,0));
|
||||
glPopMatrix();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool GameBase::setDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
if (isGhost() || isProperlyAdded()) {
|
||||
if (mDataBlock != dptr)
|
||||
return onNewDataBlock(dptr);
|
||||
}
|
||||
else
|
||||
mDataBlock = dptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void GameBase::scriptOnAdd()
|
||||
{
|
||||
// Script onAdd() must be called by the leaf class after
|
||||
// everything is ready.
|
||||
if (!isGhost())
|
||||
Con::executef(mDataBlock,2,"onAdd",scriptThis());
|
||||
}
|
||||
|
||||
void GameBase::scriptOnNewDataBlock()
|
||||
{
|
||||
// Script onNewDataBlock() must be called by the leaf class
|
||||
// after everything is loaded.
|
||||
if (!isGhost())
|
||||
Con::executef(mDataBlock,2,"onNewDataBlock",scriptThis());
|
||||
}
|
||||
|
||||
void GameBase::scriptOnRemove()
|
||||
{
|
||||
// Script onRemove() must be called by leaf class while
|
||||
// the object state is still valid.
|
||||
if (!isGhost() && mDataBlock)
|
||||
Con::executef(mDataBlock,2,"onRemove",scriptThis());
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void GameBase::plUnlink()
|
||||
{
|
||||
mProcessLink.next->mProcessLink.prev = mProcessLink.prev;
|
||||
mProcessLink.prev->mProcessLink.next = mProcessLink.next;
|
||||
mProcessLink.next = mProcessLink.prev = this;
|
||||
}
|
||||
|
||||
void GameBase::plLinkAfter(GameBase* obj)
|
||||
{
|
||||
// Link this after obj
|
||||
mProcessLink.next = obj->mProcessLink.next;
|
||||
mProcessLink.prev = obj;
|
||||
obj->mProcessLink.next = this;
|
||||
mProcessLink.next->mProcessLink.prev = this;
|
||||
}
|
||||
|
||||
void GameBase::plLinkBefore(GameBase* obj)
|
||||
{
|
||||
// Link this before obj
|
||||
mProcessLink.next = obj;
|
||||
mProcessLink.prev = obj->mProcessLink.prev;
|
||||
obj->mProcessLink.prev = this;
|
||||
mProcessLink.prev->mProcessLink.next = this;
|
||||
}
|
||||
|
||||
void GameBase::plJoin(GameBase* head)
|
||||
{
|
||||
GameBase *tail1 = head->mProcessLink.prev;
|
||||
GameBase *tail2 = mProcessLink.prev;
|
||||
tail1->mProcessLink.next = this;
|
||||
mProcessLink.prev = tail1;
|
||||
tail2->mProcessLink.next = head;
|
||||
head->mProcessLink.prev = tail2;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void GameBase::processAfter(GameBase* obj)
|
||||
{
|
||||
mAfterObject = obj;
|
||||
if ((const GameBase*)obj->mAfterObject == this)
|
||||
obj->mAfterObject = 0;
|
||||
if (isGhost())
|
||||
gClientProcessList.markDirty();
|
||||
else
|
||||
gServerProcessList.markDirty();
|
||||
}
|
||||
|
||||
void GameBase::clearProcessAfter()
|
||||
{
|
||||
mAfterObject = 0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GameBase::setControllingClient(GameConnection* client)
|
||||
{
|
||||
if (isClientObject())
|
||||
{
|
||||
if (mControllingClient)
|
||||
Con::executef(this, 3, "setControl","0");
|
||||
if (client)
|
||||
Con::executef(this, 3, "setControl","1");
|
||||
}
|
||||
|
||||
mControllingClient = client;
|
||||
setMaskBits(ControlMask);
|
||||
}
|
||||
|
||||
U32 GameBase::getPacketDataChecksum(GameConnection*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameBase::writePacketData(GameConnection*, BitStream*)
|
||||
{
|
||||
}
|
||||
|
||||
void GameBase::readPacketData(GameConnection*, BitStream*)
|
||||
{
|
||||
}
|
||||
|
||||
U32 GameBase::packUpdate(NetConnection *, U32 mask, BitStream *stream)
|
||||
{
|
||||
// Check the mask for the ScaleMask; if it's true, pass that in.
|
||||
if (stream->writeFlag( mask & ScaleMask ) ) {
|
||||
mathWrite( *stream, Parent::getScale() );
|
||||
}
|
||||
if (stream->writeFlag((mask & DataBlockMask) && mDataBlock != NULL)) {
|
||||
stream->writeRangedU32(mDataBlock->getId(),
|
||||
DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
}
|
||||
|
||||
// cafTODO: ControlMask
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameBase::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
if (stream->readFlag()) {
|
||||
VectorF scale;
|
||||
mathRead( *stream, &scale );
|
||||
setScale( scale );
|
||||
}
|
||||
if (stream->readFlag()) {
|
||||
GameBaseData* dptr = 0;
|
||||
SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
if (!Sim::findObject(id,dptr) || !setDataBlock(dptr))
|
||||
con->setLastError("Invalid packet GameBase::unpackUpdate()");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ConsoleMethod( GameBase, getDataBlock, S32, 2, 2, "()"
|
||||
"Return the datablock this GameBase is using.")
|
||||
{
|
||||
return object->getDataBlock()? object->getDataBlock()->getId(): 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ConsoleMethod(GameBase, setDataBlock, bool, 3, 3, "(DataBlock db)"
|
||||
"Assign this GameBase to use the specified datablock.")
|
||||
{
|
||||
GameBaseData* data;
|
||||
if (Sim::findObject(argv[2],data)) {
|
||||
return object->setDataBlock(data);
|
||||
}
|
||||
Con::errorf("Could not find data block \"%s\"",argv[2]);
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(GameBaseData)
|
||||
IMPLEMENT_GETDATATYPE(GameBaseData)
|
||||
IMPLEMENT_SETDATATYPE(GameBaseData)
|
||||
|
||||
void GameBase::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Misc");
|
||||
addField("nameTag", TypeCaseString, Offset(mNameTag, GameBase));
|
||||
addField("dataBlock", TypeGameBaseDataPtr, Offset(mDataBlock, GameBase));
|
||||
endGroup("Misc");
|
||||
}
|
||||
|
||||
void GameBase::consoleInit()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
Con::addVariable("GameBase::boundingBox", TypeBool, &gShowBoundingBox);
|
||||
#endif
|
||||
}
|
||||
398
engine/game/gameBase.h
Executable file
398
engine/game/gameBase.h
Executable file
@@ -0,0 +1,398 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#define _GAMEBASE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
#ifndef _GTEXMANAGER_H_
|
||||
#include "dgl/gTexManager.h"
|
||||
#endif
|
||||
|
||||
class NetConnection;
|
||||
class ProcessList;
|
||||
struct Move;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// Scriptable, demo-able datablock.
|
||||
///
|
||||
/// This variant of SimDataBlock performs these additional tasks:
|
||||
/// - Linking datablock's namepsaces to the namespace of their C++ class, so
|
||||
/// that datablocks can expose script functionality.
|
||||
/// - Linking datablocks to a user defined scripting namespace, by setting the
|
||||
/// className field at datablock definition time.
|
||||
/// - Adds a category field; this is used by the world creator in the editor to
|
||||
/// classify creatable shapes. Creatable shapes are placed under the Shapes
|
||||
/// node in the treeview for this; additional levels are created, named after
|
||||
/// the category fields.
|
||||
/// - Adds support for demo stream recording. This support takes the form
|
||||
/// of the member variable packed. When a demo is being recorded by a client,
|
||||
/// data is unpacked, then packed again to the data stream, then, in the case
|
||||
/// of datablocks, preload() is called to process the data. It is occasionally
|
||||
/// the case that certain references in the datablock stream cannot be resolved
|
||||
/// until preload is called, in which case a raw ID field is stored in the variable
|
||||
/// which will eventually be used to store a pointer to the object. However, if
|
||||
/// packData() is called before we resolve this ID, trying to call getID() on the
|
||||
/// objecct ID would be a fatal error. Therefore, in these cases, we test packed;
|
||||
/// if it is true, then we know we have to write the raw data, instead of trying
|
||||
/// to resolve an ID.
|
||||
///
|
||||
/// @see SimDataBlock for further details about datablocks.
|
||||
/// @see http://hosted.tribalwar.com/t2faq/datablocks.shtml for an excellent
|
||||
/// explanation of the basics of datablocks from a scripting perspective.
|
||||
/// @nosubgrouping
|
||||
struct GameBaseData : public SimDataBlock {
|
||||
private:
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
bool packed;
|
||||
StringTableEntry category;
|
||||
StringTableEntry className;
|
||||
|
||||
bool onAdd();
|
||||
|
||||
// The derived class should provide the following:
|
||||
DECLARE_CONOBJECT(GameBaseData);
|
||||
GameBaseData();
|
||||
static void initPersistFields();
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
DECLARE_CONSOLETYPE(GameBaseData)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// A few utility methods for sending datablocks over the net
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool UNPACK_DB_ID(BitStream *, U32 & id);
|
||||
bool PACK_DB_ID(BitStream *, U32 id);
|
||||
bool PRELOAD_DB(U32 & id, SimDataBlock **, bool server, const char * clientMissing = NULL, const char * serverMissing = NULL);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class GameConnection;
|
||||
|
||||
// For truly it is written: "The wise man extends GameBase for his purposes,
|
||||
// while the fool has the ability to eject shell casings from the belly of his
|
||||
// dragon." -- KillerBunny
|
||||
|
||||
/// Base class for game objects which use datablocks, networking, are editable,
|
||||
/// and need to process ticks.
|
||||
///
|
||||
/// @section GameBase_process GameBase and ProcessList
|
||||
///
|
||||
/// GameBase adds two kinds of time-based updates. Torque works off of a concept
|
||||
/// of ticks. Ticks are slices of time 32 milliseconds in length. There are three
|
||||
/// methods which are used to update GameBase objects that are registered with
|
||||
/// the ProcessLists:
|
||||
/// - processTick(Move*) is called on each object once for every tick, regardless
|
||||
/// of the "real" framerate.
|
||||
/// - interpolateTick(float) is called on client objects when they need to interpolate
|
||||
/// to match the next tick.
|
||||
/// - advanceTime(float) is called on client objects so they can do time-based behaviour,
|
||||
/// like updating animations.
|
||||
///
|
||||
/// Torque maintains a server and a client processing list; in a local game, both
|
||||
/// are populated, while in multiplayer situations, either one or the other is
|
||||
/// populated.
|
||||
///
|
||||
/// You can control whether an object is considered for ticking by means of the
|
||||
/// setProcessTick() method.
|
||||
///
|
||||
/// @section GameBase_datablock GameBase and Datablocks
|
||||
///
|
||||
/// GameBase adds support for datablocks. Datablocks are secondary classes which store
|
||||
/// static data for types of game elements. For instance, this means that all "light human
|
||||
/// male armor" type Players share the same datablock. Datablocks typically store not only
|
||||
/// raw data, but perform precalculations, like finding nodes in the game model, or
|
||||
/// validating movement parameters.
|
||||
///
|
||||
/// There are three parts to the datablock interface implemented in GameBase:
|
||||
/// - <b>getDataBlock()</b>, which gets a pointer to the current datablock. This is
|
||||
/// mostly for external use; for in-class use, it's better to directly access the
|
||||
/// mDataBlock member.
|
||||
/// - <b>setDataBlock()</b>, which sets mDataBlock to point to a new datablock; it
|
||||
/// uses the next part of the interface to inform subclasses of this.
|
||||
/// - <b>onNewDataBlock()</b> is called whenever a new datablock is assigned to a GameBase.
|
||||
///
|
||||
/// Datablocks are also usable through the scripting language.
|
||||
///
|
||||
/// @see SimDataBlock for more details.
|
||||
///
|
||||
/// @section GameBase_networking GameBase and Networking
|
||||
///
|
||||
/// writePacketData() and readPacketData() are called to transfer information needed for client
|
||||
/// side prediction. They are usually used when updating a client of its control object state.
|
||||
///
|
||||
/// Subclasses of GameBase usually transmit positional and basic status data in the packUpdate()
|
||||
/// functions, while giving velocity, momentum, and similar state information in the writePacketData().
|
||||
///
|
||||
/// writePacketData()/readPacketData() are called <i>in addition</i> to packUpdate/unpackUpdate().
|
||||
///
|
||||
/// @nosubgrouping
|
||||
class GameBase : public SceneObject
|
||||
{
|
||||
private:
|
||||
typedef SceneObject Parent;
|
||||
friend class ProcessList;
|
||||
|
||||
/// @name Datablock
|
||||
/// @{
|
||||
private:
|
||||
GameBaseData* mDataBlock;
|
||||
StringTableEntry mNameTag;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Tick Processing Internals
|
||||
/// @{
|
||||
private:
|
||||
void plUnlink();
|
||||
void plLinkAfter(GameBase*);
|
||||
void plLinkBefore(GameBase*);
|
||||
void plJoin(GameBase*);
|
||||
struct Link {
|
||||
GameBase *next;
|
||||
GameBase *prev;
|
||||
};
|
||||
U32 mProcessTag; ///< Tag used to sort objects for processing.
|
||||
Link mProcessLink; ///< Ordered process queue link.
|
||||
SimObjectPtr<GameBase> mAfterObject;
|
||||
/// @}
|
||||
|
||||
// Control interface
|
||||
GameConnection* mControllingClient;
|
||||
//GameBase* mControllingObject;
|
||||
|
||||
public:
|
||||
static bool gShowBoundingBox; ///< Should we render bounding boxes?
|
||||
protected:
|
||||
bool mProcessTick;
|
||||
F32 mLastDelta;
|
||||
F32 mCameraFov;
|
||||
|
||||
public:
|
||||
GameBase();
|
||||
virtual ~GameBase();
|
||||
|
||||
enum GameBaseMasks {
|
||||
InitialUpdateMask = Parent::NextFreeMask,
|
||||
DataBlockMask = InitialUpdateMask << 1,
|
||||
ExtendedInfoMask = DataBlockMask << 1,
|
||||
ControlMask = ExtendedInfoMask << 1,
|
||||
NextFreeMask = ControlMask << 1
|
||||
};
|
||||
|
||||
/// @name Inherited Functionality.
|
||||
/// @{
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void inspectPostApply();
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
/// @}
|
||||
|
||||
///@name Datablock
|
||||
///@{
|
||||
|
||||
/// Assigns this object a datablock and loads attributes with onNewDataBlock.
|
||||
///
|
||||
/// @see onNewDataBlock
|
||||
/// @param dptr Datablock
|
||||
bool setDataBlock(GameBaseData* dptr);
|
||||
|
||||
/// Returns the datablock for this object.
|
||||
GameBaseData* getDataBlock() { return mDataBlock; }
|
||||
|
||||
/// Called when a new datablock is set. This allows subclasses to
|
||||
/// appropriately handle new datablocks.
|
||||
///
|
||||
/// @see setDataBlock()
|
||||
/// @param dptr New datablock
|
||||
virtual bool onNewDataBlock(GameBaseData* dptr);
|
||||
///@}
|
||||
|
||||
/// @name Script
|
||||
/// The scriptOnXX methods are invoked by the leaf classes
|
||||
/// @{
|
||||
|
||||
/// Executes the 'onAdd' script function for this object.
|
||||
/// @note This must be called after everything is ready
|
||||
void scriptOnAdd();
|
||||
|
||||
/// Executes the 'onNewDataBlock' script function for this object.
|
||||
///
|
||||
/// @note This must be called after everything is loaded.
|
||||
void scriptOnNewDataBlock();
|
||||
|
||||
/// Executes the 'onRemove' script function for this object.
|
||||
/// @note This must be called while the object is still valid
|
||||
void scriptOnRemove();
|
||||
/// @}
|
||||
|
||||
/// @name Tick Processing
|
||||
/// @{
|
||||
|
||||
/// Set the status of tick processing.
|
||||
///
|
||||
/// If this is set to true, processTick will be called; if false,
|
||||
/// then it will be skipped.
|
||||
///
|
||||
/// @see processTick
|
||||
/// @param t If true, tick processing is enabled.
|
||||
void setProcessTick(bool t) { mProcessTick = t; }
|
||||
|
||||
/// Force this object to process after some other object.
|
||||
///
|
||||
/// For example, a player mounted to a vehicle would want to process after the vehicle,
|
||||
/// to prevent a visible "lagging" from occuring when the vehicle motions, so the player
|
||||
/// would be set to processAfter(theVehicle);
|
||||
///
|
||||
/// @param obj Object to process after
|
||||
void processAfter(GameBase *obj);
|
||||
|
||||
/// Clears the effects of a call to processAfter()
|
||||
void clearProcessAfter();
|
||||
|
||||
/// Returns the object that this processes after.
|
||||
///
|
||||
/// @see processAfter
|
||||
GameBase* getProcessAfter() { return mAfterObject; }
|
||||
|
||||
/// Removes this object from the tick-processing list
|
||||
void removeFromProcessList() { plUnlink(); }
|
||||
|
||||
/// Processes a move event and updates object state once every 32 milliseconds.
|
||||
///
|
||||
/// This takes place both on the client and server, every 32 milliseconds (1 tick).
|
||||
///
|
||||
/// @see ProcessList
|
||||
/// @param move Move event corresponding to this tick, or NULL.
|
||||
virtual void processTick(const Move *move);
|
||||
|
||||
/// Interpolates between tick events. This takes place on the CLIENT ONLY.
|
||||
///
|
||||
/// @param delta Time since last call to interpolate
|
||||
virtual void interpolateTick(F32 delta);
|
||||
|
||||
/// Advances simulation time for animations. This is called every frame.
|
||||
///
|
||||
/// @param dt Time since last advance call
|
||||
virtual void advanceTime(F32 dt);
|
||||
|
||||
/// This is a component system thing, gotta ask Clark about it
|
||||
virtual void preprocessMove(Move *move) {}
|
||||
/// @}
|
||||
|
||||
/// Draws a bounding box around this object
|
||||
void drawBoundingBox(bool useRenderTransform = false);
|
||||
|
||||
/// @name Network
|
||||
/// @see NetObject, NetConnection
|
||||
/// @{
|
||||
|
||||
F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips);
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
/// Write state information necessary to perform client side prediction of an object.
|
||||
///
|
||||
/// This information is sent only to the controling object. For example, if you are a client
|
||||
/// controlling a Player, the server uses writePacketData() instead of packUpdate() to
|
||||
/// generate the data you receive.
|
||||
///
|
||||
/// @param conn Connection for which we're generating this data.
|
||||
/// @param stream Bitstream for output.
|
||||
virtual void writePacketData(GameConnection *conn, BitStream *stream);
|
||||
|
||||
/// Read data written with writePacketData() and update the object state.
|
||||
///
|
||||
/// @param conn Connection for which we're generating this data.
|
||||
/// @param stream Bitstream to read.
|
||||
virtual void readPacketData(GameConnection *conn, BitStream *stream);
|
||||
|
||||
/// Gets the checksum for packet data.
|
||||
///
|
||||
/// Basically writes a packet, does a CRC check on it, and returns
|
||||
/// that CRC.
|
||||
///
|
||||
/// @see writePacketData
|
||||
/// @param conn Game connection
|
||||
virtual U32 getPacketDataChecksum(GameConnection *conn);
|
||||
///@}
|
||||
|
||||
/// @name User control
|
||||
/// @{
|
||||
|
||||
/// Returns the client controling this object
|
||||
GameConnection *getControllingClient() { return mControllingClient; }
|
||||
|
||||
/// Sets the client controling this object
|
||||
/// @param client Client that is now controling this object
|
||||
virtual void setControllingClient(GameConnection *client);
|
||||
/// @}
|
||||
|
||||
DECLARE_CONOBJECT(GameBase);
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define TickShift 5
|
||||
#define TickMs (1 << TickShift)
|
||||
#define TickSec (F32(TickMs) / 1000.0f)
|
||||
#define TickMask (TickMs - 1)
|
||||
|
||||
/// List to keep track of GameBases to process.
|
||||
class ProcessList
|
||||
{
|
||||
GameBase head;
|
||||
U32 mCurrentTag;
|
||||
SimTime mLastTick;
|
||||
SimTime mLastTime;
|
||||
SimTime mLastDelta;
|
||||
bool mIsServer;
|
||||
bool mDirty;
|
||||
static bool mDebugControlSync;
|
||||
|
||||
void orderList();
|
||||
void advanceObjects();
|
||||
|
||||
public:
|
||||
SimTime getLastTime() { return mLastTime; }
|
||||
ProcessList(bool isServer);
|
||||
void markDirty() { mDirty = true; }
|
||||
bool isDirty() { return mDirty; }
|
||||
void addObject(GameBase* obj) {
|
||||
obj->plLinkBefore(&head);
|
||||
}
|
||||
F32 getLastInterpDelta() { return mLastDelta / F32(TickMs); }
|
||||
|
||||
/// @name Advancing Time
|
||||
/// The advance time functions return true if a tick was processed.
|
||||
///
|
||||
/// These functions go through either gServerProcessList or gClientProcessList and
|
||||
/// call each GameBase's processTick().
|
||||
/// @{
|
||||
|
||||
bool advanceServerTime(SimTime timeDelta);
|
||||
bool advanceClientTime(SimTime timeDelta);
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
extern ProcessList gClientProcessList;
|
||||
extern ProcessList gServerProcessList;
|
||||
|
||||
#endif
|
||||
1485
engine/game/gameConnection.cc
Executable file
1485
engine/game/gameConnection.cc
Executable file
File diff suppressed because it is too large
Load Diff
310
engine/game/gameConnection.h
Executable file
310
engine/game/gameConnection.h
Executable file
@@ -0,0 +1,310 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMECONNECTION_H_
|
||||
#define _GAMECONNECTION_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "game/shapeBase.h"
|
||||
#endif
|
||||
#ifndef _NETCONNECTION_H_
|
||||
#include "sim/netConnection.h"
|
||||
#endif
|
||||
#ifndef _MOVEMANAGER_H_
|
||||
#include "game/moveManager.h"
|
||||
#endif
|
||||
#ifndef _BITVECTOR_H_
|
||||
#include "core/bitVector.h"
|
||||
#endif
|
||||
|
||||
enum GameConnectionConstants
|
||||
{
|
||||
MaxClients = 126,
|
||||
DataBlockQueueCount = 16
|
||||
};
|
||||
|
||||
class AudioProfile;
|
||||
class MatrixF;
|
||||
class MatrixF;
|
||||
class Point3F;
|
||||
class MoveManager;
|
||||
struct Move;
|
||||
struct AuthInfo;
|
||||
|
||||
#define GameString "Torque Game Engine Demo"
|
||||
|
||||
class GameConnection : public NetConnection
|
||||
{
|
||||
private:
|
||||
typedef NetConnection Parent;
|
||||
|
||||
enum PrivateConstants {
|
||||
MoveCountBits = 5,
|
||||
/// MaxMoveCount should not exceed the MoveManager's
|
||||
/// own maximum (MaxMoveQueueSize)
|
||||
MaxMoveCount = 30,
|
||||
};
|
||||
typedef Vector<Move> MoveList;
|
||||
|
||||
SimObjectPtr<ShapeBase> mControlObject;
|
||||
SimObjectPtr<ShapeBase> mCameraObject;
|
||||
U32 mDataBlockSequence;
|
||||
char mDisconnectReason[256];
|
||||
|
||||
U32 mMissionCRC; // crc of the current mission file from the server
|
||||
|
||||
private:
|
||||
U32 mLastControlRequestTime;
|
||||
S32 mDataBlockModifiedKey;
|
||||
S32 mMaxDataBlockModifiedKey;
|
||||
|
||||
/// @name Client side first/third person
|
||||
/// @{
|
||||
|
||||
///
|
||||
bool mFirstPerson; ///< Are we currently first person or not.
|
||||
bool mUpdateFirstPerson; ///< Set to notify client or server of first person change.
|
||||
bool mUpdateCameraFov; ///< Set to notify server of camera FOV change.
|
||||
F32 mCameraFov; ///< Current camera fov (in degrees).
|
||||
F32 mCameraPos; ///< Current camera pos (0-1).
|
||||
F32 mCameraSpeed; ///< Camera in/out speed.
|
||||
/// @}
|
||||
|
||||
/// @name Move Packets
|
||||
/// Write/read move data to the packet.
|
||||
/// @{
|
||||
|
||||
///
|
||||
void moveWritePacket(BitStream *bstream);
|
||||
void moveReadPacket(BitStream *bstream);
|
||||
/// @}
|
||||
public:
|
||||
|
||||
/// @name Protocol Versions
|
||||
///
|
||||
/// Protocol versions are used to indicated changes in network traffic.
|
||||
/// These could be changes in how any object transmits or processes
|
||||
/// network information. You can specify backwards compatibility by
|
||||
/// specifying a MinRequireProtocolVersion. If the client
|
||||
/// protocol is >= this min value, the connection is accepted.
|
||||
///
|
||||
/// Torque (V12) SDK 1.0 uses protocol = 1
|
||||
///
|
||||
/// Torque SDK 1.1 uses protocol = 2
|
||||
/// Torque SDK 1.4 uses protocol = 12
|
||||
/// @{
|
||||
static const U32 CurrentProtocolVersion;
|
||||
static const U32 MinRequiredProtocolVersion;
|
||||
/// @}
|
||||
|
||||
/// Configuration
|
||||
enum Constants {
|
||||
BlockTypeMove = NetConnectionBlockTypeCount,
|
||||
GameConnectionBlockTypeCount,
|
||||
MaxConnectArgs = 16,
|
||||
DataBlocksDone = NumConnectionMessages,
|
||||
DataBlocksDownloadDone,
|
||||
};
|
||||
|
||||
/// Set connection arguments; these are passed to the server when we connect.
|
||||
void setConnectArgs(U32 argc, const char **argv);
|
||||
|
||||
/// Set the server password to use when we join.
|
||||
void setJoinPassword(const char *password);
|
||||
|
||||
/// @name Event Handling
|
||||
/// @{
|
||||
|
||||
virtual void onTimedOut();
|
||||
virtual void onConnectTimedOut();
|
||||
virtual void onDisconnect(const char *reason);
|
||||
virtual void onConnectionRejected(const char *reason);
|
||||
virtual void onConnectionEstablished(bool isInitiator);
|
||||
virtual void handleStartupError(const char *errorString);
|
||||
/// @}
|
||||
|
||||
/// @name Packet I/O
|
||||
/// @{
|
||||
|
||||
virtual void writeConnectRequest(BitStream *stream);
|
||||
virtual bool readConnectRequest(BitStream *stream, const char **errorString);
|
||||
virtual void writeConnectAccept(BitStream *stream);
|
||||
virtual bool readConnectAccept(BitStream *stream, const char **errorString);
|
||||
/// @}
|
||||
|
||||
bool canRemoteCreate();
|
||||
|
||||
private:
|
||||
/// @name Connection State
|
||||
/// This data is set with setConnectArgs() and setJoinPassword(), and
|
||||
/// sent across the wire when we connect.
|
||||
/// @{
|
||||
|
||||
U32 mConnectArgc;
|
||||
char *mConnectArgv[MaxConnectArgs];
|
||||
char *mJoinPassword;
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
struct GamePacketNotify : public NetConnection::PacketNotify
|
||||
{
|
||||
S32 cameraFov;
|
||||
GamePacketNotify();
|
||||
};
|
||||
PacketNotify *allocNotify();
|
||||
|
||||
U32 mLastMoveAck;
|
||||
U32 mLastClientMove;
|
||||
U32 mFirstMoveIndex;
|
||||
U32 mMoveCredit;
|
||||
U32 mLastControlObjectChecksum;
|
||||
|
||||
Vector<SimDataBlock *> mDataBlockLoadList;
|
||||
|
||||
MoveList mMoveList;
|
||||
bool mAIControlled;
|
||||
AuthInfo * mAuthInfo;
|
||||
|
||||
static S32 mLagThresholdMS;
|
||||
S32 mLastPacketTime;
|
||||
bool mLagging;
|
||||
|
||||
/// @name Flashing
|
||||
////
|
||||
/// Note, these variables are not networked, they are for the local connection only.
|
||||
/// @{
|
||||
F32 mDamageFlash;
|
||||
F32 mWhiteOut;
|
||||
|
||||
F32 mBlackOut;
|
||||
S32 mBlackOutTimeMS;
|
||||
S32 mBlackOutStartTimeMS;
|
||||
bool mFadeToBlack;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Packet I/O
|
||||
/// @{
|
||||
|
||||
void readPacket (BitStream *bstream);
|
||||
void writePacket (BitStream *bstream, PacketNotify *note);
|
||||
void packetReceived (PacketNotify *note);
|
||||
void packetDropped (PacketNotify *note);
|
||||
void connectionError (const char *errorString);
|
||||
|
||||
void writeDemoStartBlock (ResizeBitStream *stream);
|
||||
bool readDemoStartBlock (BitStream *stream);
|
||||
void handleRecordedBlock (U32 type, U32 size, void *data);
|
||||
/// @}
|
||||
|
||||
public:
|
||||
|
||||
DECLARE_CONOBJECT(GameConnection);
|
||||
void handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount);
|
||||
void preloadDataBlock(SimDataBlock *block);
|
||||
void fileDownloadSegmentComplete();
|
||||
void preloadNextDataBlock(bool hadNew);
|
||||
static void consoleInit();
|
||||
|
||||
void setDisconnectReason(const char *reason);
|
||||
GameConnection();
|
||||
~GameConnection();
|
||||
|
||||
U32 getDataBlockSequence() { return mDataBlockSequence; }
|
||||
void setDataBlockSequence(U32 seq) { mDataBlockSequence = seq; }
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
static GameConnection *getConnectionToServer() { return dynamic_cast<GameConnection*>((NetConnection *) mServerConnection); }
|
||||
static GameConnection *getLocalClientConnection() { return dynamic_cast<GameConnection*>((NetConnection *) mLocalClientConnection); }
|
||||
|
||||
/// @name Control object
|
||||
/// @{
|
||||
|
||||
///
|
||||
void setControlObject(ShapeBase *co);
|
||||
ShapeBase* getControlObject() { return mControlObject; }
|
||||
void setCameraObject(ShapeBase *co);
|
||||
ShapeBase* getCameraObject();
|
||||
bool getControlCameraTransform(F32 dt,MatrixF* mat);
|
||||
bool getControlCameraVelocity(Point3F *vel);
|
||||
|
||||
bool getControlCameraFov(F32 * fov);
|
||||
bool setControlCameraFov(F32 fov);
|
||||
bool isValidControlCameraFov(F32 fov);
|
||||
|
||||
void setFirstPerson(bool firstPerson);
|
||||
|
||||
/// @}
|
||||
|
||||
void detectLag();
|
||||
|
||||
/// @name Datablock management
|
||||
/// @{
|
||||
|
||||
S32 getDataBlockModifiedKey () { return mDataBlockModifiedKey; }
|
||||
void setDataBlockModifiedKey (S32 key) { mDataBlockModifiedKey = key; }
|
||||
S32 getMaxDataBlockModifiedKey () { return mMaxDataBlockModifiedKey; }
|
||||
void setMaxDataBlockModifiedKey (S32 key) { mMaxDataBlockModifiedKey = key; }
|
||||
/// @}
|
||||
|
||||
/// @name Fade control
|
||||
/// @{
|
||||
|
||||
F32 getDamageFlash() { return mDamageFlash; }
|
||||
F32 getWhiteOut() { return mWhiteOut; }
|
||||
|
||||
void setBlackOut(bool fadeToBlack, S32 timeMS);
|
||||
F32 getBlackOut();
|
||||
/// @}
|
||||
|
||||
/// @name Move Management
|
||||
/// @{
|
||||
|
||||
void pushMove(const Move &mv);
|
||||
bool getNextMove(Move &curMove);
|
||||
bool isBacklogged();
|
||||
virtual void getMoveList(Move**,U32* numMoves);
|
||||
virtual void clearMoves(U32 count);
|
||||
void collectMove(U32 simTime);
|
||||
virtual bool areMovesPending();
|
||||
void incMoveCredit(U32 count);
|
||||
/// @}
|
||||
|
||||
/// @name Authentication
|
||||
///
|
||||
/// This is remnant code from Tribes 2.
|
||||
/// @{
|
||||
|
||||
void setAuthInfo(const AuthInfo *info);
|
||||
const AuthInfo *getAuthInfo();
|
||||
/// @}
|
||||
|
||||
/// @name Sound
|
||||
/// @{
|
||||
|
||||
void play2D(const AudioProfile *profile);
|
||||
void play3D(const AudioProfile *profile, const MatrixF *transform);
|
||||
/// @}
|
||||
|
||||
/// @name Misc.
|
||||
/// @{
|
||||
|
||||
bool isFirstPerson() { return mCameraPos == 0; }
|
||||
bool isAIControlled() { return mAIControlled; }
|
||||
|
||||
void doneScopingScene();
|
||||
void demoPlaybackComplete();
|
||||
|
||||
void setMissionCRC(U32 crc) { mMissionCRC = crc; }
|
||||
U32 getMissionCRC() { return(mMissionCRC); }
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif
|
||||
297
engine/game/gameConnectionEvents.cc
Executable file
297
engine/game/gameConnectionEvents.cc
Executable file
@@ -0,0 +1,297 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/dnet.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/simBase.h"
|
||||
#include "sim/pathManager.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "audio/audioDataBlock.h"
|
||||
#include "game/game.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/shapeBase.h"
|
||||
#include "game/gameConnectionEvents.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(SimDataBlockEvent);
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(Sim2DAudioEvent);
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(Sim3DAudioEvent);
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(SetMissionCRCEvent);
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
SimDataBlockEvent::~SimDataBlockEvent()
|
||||
{
|
||||
delete mObj;
|
||||
}
|
||||
SimDataBlockEvent::SimDataBlockEvent(SimDataBlock* obj, U32 index, U32 total, U32 missionSequence)
|
||||
{
|
||||
mObj = NULL;
|
||||
mIndex = index;
|
||||
mTotal = total;
|
||||
mMissionSequence = missionSequence;
|
||||
mProcess = false;
|
||||
|
||||
if(obj)
|
||||
{
|
||||
id = obj->getId();
|
||||
AssertFatal(id >= DataBlockObjectIdFirst && id <= DataBlockObjectIdLast,
|
||||
"Out of range event data block id... check simBase.h");
|
||||
// Con::printf("queuing data block: %d", mIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET
|
||||
const char *SimDataBlockEvent::getDebugName()
|
||||
{
|
||||
SimObject *obj = Sim::findObject(id);
|
||||
static char buffer[256];
|
||||
dSprintf(buffer, sizeof(buffer), "%s [%s - %s]",
|
||||
getClassName(),
|
||||
obj ? obj->getName() : "",
|
||||
obj ? obj->getClassName() : "NONE");
|
||||
return buffer;
|
||||
}
|
||||
#endif // TORQUE_DEBUG_NET
|
||||
|
||||
void SimDataBlockEvent::notifyDelivered(NetConnection *conn, bool )
|
||||
{
|
||||
// if the modified key for this event is not the current one,
|
||||
// we've already resorted and resent some blocks, so fall out.
|
||||
if(conn->isRemoved())
|
||||
return;
|
||||
GameConnection *gc = (GameConnection *) conn;
|
||||
if(gc->getDataBlockSequence() != mMissionSequence)
|
||||
return;
|
||||
|
||||
U32 nextIndex = mIndex + DataBlockQueueCount;
|
||||
SimDataBlockGroup *g = Sim::getDataBlockGroup();
|
||||
|
||||
if(mIndex == g->size() - 1)
|
||||
{
|
||||
gc->setDataBlockModifiedKey(gc->getMaxDataBlockModifiedKey());
|
||||
gc->sendConnectionMessage(GameConnection::DataBlocksDone, mMissionSequence);
|
||||
}
|
||||
if(g->size() <= nextIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SimDataBlock *blk = (SimDataBlock *) (*g)[nextIndex];
|
||||
gc->postNetEvent(new SimDataBlockEvent(blk, nextIndex, g->size(), mMissionSequence));
|
||||
}
|
||||
|
||||
void SimDataBlockEvent::pack(NetConnection *conn, BitStream *bstream)
|
||||
{
|
||||
SimDataBlock* obj;
|
||||
Sim::findObject(id,obj);
|
||||
GameConnection *gc = (GameConnection *) conn;
|
||||
if(bstream->writeFlag(gc->getDataBlockModifiedKey() < obj->getModifiedKey()))
|
||||
{
|
||||
if(obj->getModifiedKey() > gc->getMaxDataBlockModifiedKey())
|
||||
gc->setMaxDataBlockModifiedKey(obj->getModifiedKey());
|
||||
|
||||
AssertFatal(obj,
|
||||
"SimDataBlockEvent:: Data blocks cannot be deleted");
|
||||
bstream->writeInt(id - DataBlockObjectIdFirst,DataBlockObjectIdBitSize);
|
||||
|
||||
S32 classId = obj->getClassId(conn->getNetClassGroup());
|
||||
bstream->writeClassId(classId, NetClassTypeDataBlock, conn->getNetClassGroup());
|
||||
bstream->writeInt(mIndex, DataBlockObjectIdBitSize);
|
||||
bstream->writeInt(mTotal, DataBlockObjectIdBitSize + 1);
|
||||
obj->packData(bstream);
|
||||
}
|
||||
}
|
||||
|
||||
void SimDataBlockEvent::unpack(NetConnection *cptr, BitStream *bstream)
|
||||
{
|
||||
if(bstream->readFlag())
|
||||
{
|
||||
mProcess = true;
|
||||
id = bstream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst;
|
||||
S32 classId = bstream->readClassId(NetClassTypeDataBlock, cptr->getNetClassGroup());
|
||||
mIndex = bstream->readInt(DataBlockObjectIdBitSize);
|
||||
mTotal = bstream->readInt(DataBlockObjectIdBitSize + 1);
|
||||
|
||||
SimObject* ptr = (SimObject *) ConsoleObject::create(cptr->getNetClassGroup(), NetClassTypeDataBlock, classId);
|
||||
if ((mObj = dynamic_cast<SimDataBlock*>(ptr)) != 0) {
|
||||
//Con::printf(" - SimDataBlockEvent: unpacking event of type: %s", mObj->getClassName());
|
||||
mObj->unpackData(bstream);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Con::printf(" - SimDataBlockEvent: INVALID PACKET! Could not create class with classID: %d", classId);
|
||||
delete ptr;
|
||||
cptr->setLastError("Invalid packet in SimDataBlockEvent::unpack()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimDataBlockEvent::write(NetConnection *cptr, BitStream *bstream)
|
||||
{
|
||||
if(bstream->writeFlag(mProcess))
|
||||
{
|
||||
bstream->writeInt(id - DataBlockObjectIdFirst,DataBlockObjectIdBitSize);
|
||||
S32 classId = mObj->getClassId(cptr->getNetClassGroup());
|
||||
bstream->writeClassId(classId, NetClassTypeDataBlock, cptr->getNetClassGroup());
|
||||
bstream->writeInt(mIndex, DataBlockObjectIdBitSize);
|
||||
bstream->writeInt(mTotal, DataBlockObjectIdBitSize + 1);
|
||||
mObj->packData(bstream);
|
||||
}
|
||||
}
|
||||
|
||||
void SimDataBlockEvent::process(NetConnection *cptr)
|
||||
{
|
||||
if(mProcess)
|
||||
{
|
||||
//call the console function to set the number of blocks to be sent
|
||||
Con::executef(3, "onDataBlockObjectReceived", Con::getIntArg(mIndex), Con::getIntArg(mTotal));
|
||||
|
||||
SimDataBlock* obj = NULL;
|
||||
char *errorBuffer = NetConnection::getErrorBuffer();
|
||||
|
||||
if( Sim::findObject( id,obj ) && dStrcmp( obj->getClassName(),mObj->getClassName() ) == 0 )
|
||||
{
|
||||
U8 buf[1500];
|
||||
BitStream stream(buf, 1500);
|
||||
mObj->packData(&stream);
|
||||
stream.setPosition(0);
|
||||
obj->unpackData(&stream);
|
||||
obj->preload(false, errorBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( obj != NULL )
|
||||
{
|
||||
Con::warnf( "A '%s' datablock with id: %d already existed. Clobbering it with new '%s' datablock from server.", obj->getClassName(), id, mObj->getClassName() );
|
||||
obj->deleteObject();
|
||||
}
|
||||
|
||||
bool reg = mObj->registerObject(id);
|
||||
cptr->addObject(mObj);
|
||||
GameConnection *conn = dynamic_cast<GameConnection *>(cptr);
|
||||
if(conn)
|
||||
{
|
||||
conn->preloadDataBlock(mObj);
|
||||
mObj = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
Sim2DAudioEvent::Sim2DAudioEvent(const AudioProfile *profile)
|
||||
{
|
||||
mProfile = profile;
|
||||
}
|
||||
|
||||
void Sim2DAudioEvent::pack(NetConnection *, BitStream *bstream)
|
||||
{
|
||||
bstream->writeInt( mProfile->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize);
|
||||
}
|
||||
|
||||
void Sim2DAudioEvent::write(NetConnection *, BitStream *bstream)
|
||||
{
|
||||
bstream->writeInt( mProfile->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize);
|
||||
}
|
||||
|
||||
void Sim2DAudioEvent::unpack(NetConnection *, BitStream *bstream)
|
||||
{
|
||||
SimObjectId id = bstream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst;
|
||||
Sim::findObject(id, mProfile);
|
||||
}
|
||||
|
||||
void Sim2DAudioEvent::process(NetConnection *)
|
||||
{
|
||||
if (mProfile)
|
||||
alxPlay(mProfile);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static F32 SoundPosAccuracy = 0.5;
|
||||
static S32 SoundRotBits = 8;
|
||||
|
||||
|
||||
Sim3DAudioEvent::Sim3DAudioEvent(const AudioProfile *profile,const MatrixF* mat)
|
||||
{
|
||||
mProfile = profile;
|
||||
if (mat)
|
||||
mTransform = *mat;
|
||||
}
|
||||
|
||||
void Sim3DAudioEvent::pack(NetConnection *con, BitStream *bstream)
|
||||
{
|
||||
bstream->writeInt(mProfile->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize);
|
||||
|
||||
// If the sound has cone parameters, the orientation is
|
||||
// transmitted as well.
|
||||
Audio::Description* ad = &mProfile->mDescriptionObject->mDescription;
|
||||
if (bstream->writeFlag(ad->mConeInsideAngle || ad->mConeOutsideAngle)) {
|
||||
QuatF q(mTransform);
|
||||
q.normalize();
|
||||
|
||||
// LH - we can get a valid quat that's very slightly over 1 in and so
|
||||
// this fails (barely) check against zero. So use some error-
|
||||
AssertFatal((1.0 - ((q.x * q.x) + (q.y * q.y) + (q.z * q.z))) >= (0.0 - 0.001),
|
||||
"QuatF::normalize() is broken in Sim3DAudioEvent");
|
||||
|
||||
bstream->writeFloat(q.x,SoundRotBits);
|
||||
bstream->writeFloat(q.y,SoundRotBits);
|
||||
bstream->writeFloat(q.z,SoundRotBits);
|
||||
bstream->writeFlag(q.w < 0.0);
|
||||
}
|
||||
|
||||
Point3F pos;
|
||||
mTransform.getColumn(3,&pos);
|
||||
bstream->writeCompressedPoint(pos,SoundPosAccuracy);
|
||||
}
|
||||
|
||||
void Sim3DAudioEvent::write(NetConnection *con, BitStream *bstream)
|
||||
{
|
||||
// Just do the normal pack...
|
||||
pack(con,bstream);
|
||||
}
|
||||
|
||||
void Sim3DAudioEvent::unpack(NetConnection *con, BitStream *bstream)
|
||||
{
|
||||
SimObjectId id = bstream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst;
|
||||
Sim::findObject(id, mProfile);
|
||||
|
||||
if (bstream->readFlag()) {
|
||||
QuatF q;
|
||||
q.x = bstream->readFloat(SoundRotBits);
|
||||
q.y = bstream->readFloat(SoundRotBits);
|
||||
q.z = bstream->readFloat(SoundRotBits);
|
||||
F32 value = ((q.x * q.x) + (q.y * q.y) + (q.z * q.z));
|
||||
// #ifdef __linux
|
||||
// Hmm, this should never happen, but it does...
|
||||
if ( value > 1.f )
|
||||
value = 1.f;
|
||||
// #endif
|
||||
q.w = mSqrt(1.f - value);
|
||||
if (bstream->readFlag())
|
||||
q.w = -q.w;
|
||||
q.setMatrix(&mTransform);
|
||||
}
|
||||
else
|
||||
mTransform.identity();
|
||||
|
||||
Point3F pos;
|
||||
bstream->readCompressedPoint(&pos,SoundPosAccuracy);
|
||||
mTransform.setColumn(3, pos);
|
||||
}
|
||||
|
||||
void Sim3DAudioEvent::process(NetConnection *)
|
||||
{
|
||||
if (mProfile)
|
||||
alxPlay(mProfile, &mTransform);
|
||||
}
|
||||
|
||||
92
engine/game/gameConnectionEvents.h
Executable file
92
engine/game/gameConnectionEvents.h
Executable file
@@ -0,0 +1,92 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMECONNECTIONEVENTS_H_
|
||||
#define _GAMECONNECTIONEVENTS_H_
|
||||
|
||||
class QuitEvent : public SimEvent
|
||||
{
|
||||
void process(SimObject *object)
|
||||
{
|
||||
Platform::postQuitMessage(0);
|
||||
}
|
||||
};
|
||||
|
||||
class SimDataBlockEvent : public NetEvent
|
||||
{
|
||||
SimObjectId id;
|
||||
SimDataBlock *mObj;
|
||||
U32 mIndex;
|
||||
U32 mTotal;
|
||||
U32 mMissionSequence;
|
||||
bool mProcess;
|
||||
public:
|
||||
~SimDataBlockEvent();
|
||||
SimDataBlockEvent(SimDataBlock* obj = NULL, U32 index = 0, U32 total = 0, U32 missionSequence = 0);
|
||||
void pack(NetConnection *, BitStream *bstream);
|
||||
void write(NetConnection *, BitStream *bstream);
|
||||
void unpack(NetConnection *cptr, BitStream *bstream);
|
||||
void process(NetConnection*);
|
||||
void notifyDelivered(NetConnection *, bool);
|
||||
#ifdef TORQUE_DEBUG_NET
|
||||
const char *getDebugName();
|
||||
#endif
|
||||
DECLARE_CONOBJECT(SimDataBlockEvent);
|
||||
};
|
||||
|
||||
class Sim2DAudioEvent: public NetEvent
|
||||
{
|
||||
private:
|
||||
const AudioProfile *mProfile;
|
||||
|
||||
public:
|
||||
Sim2DAudioEvent(const AudioProfile *profile=NULL);
|
||||
void pack(NetConnection *, BitStream *bstream);
|
||||
void write(NetConnection *, BitStream *bstream);
|
||||
void unpack(NetConnection *, BitStream *bstream);
|
||||
void process(NetConnection *);
|
||||
DECLARE_CONOBJECT(Sim2DAudioEvent);
|
||||
};
|
||||
|
||||
class Sim3DAudioEvent: public NetEvent
|
||||
{
|
||||
private:
|
||||
const AudioProfile *mProfile;
|
||||
MatrixF mTransform;
|
||||
|
||||
public:
|
||||
Sim3DAudioEvent(const AudioProfile *profile=NULL,const MatrixF* mat=NULL);
|
||||
void pack(NetConnection *, BitStream *bstream);
|
||||
void write(NetConnection *, BitStream *bstream);
|
||||
void unpack(NetConnection *, BitStream *bstream);
|
||||
void process(NetConnection *);
|
||||
DECLARE_CONOBJECT(Sim3DAudioEvent);
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// used to set the crc for the current mission (mission lighting)
|
||||
//----------------------------------------------------------------------------
|
||||
class SetMissionCRCEvent : public NetEvent
|
||||
{
|
||||
private:
|
||||
U32 mCrc;
|
||||
|
||||
public:
|
||||
SetMissionCRCEvent(U32 crc = 0xffffffff)
|
||||
{ mCrc = crc; }
|
||||
void pack(NetConnection *, BitStream * bstream)
|
||||
{ bstream->write(mCrc); }
|
||||
void write(NetConnection * con, BitStream * bstream)
|
||||
{ pack(con, bstream); }
|
||||
void unpack(NetConnection *, BitStream * bstream)
|
||||
{ bstream->read(&mCrc); }
|
||||
void process(NetConnection * con)
|
||||
{ static_cast<GameConnection*>(con)->setMissionCRC(mCrc); }
|
||||
|
||||
DECLARE_CONOBJECT(SetMissionCRCEvent);
|
||||
};
|
||||
|
||||
#endif
|
||||
367
engine/game/gameConnectionMoves.cc
Executable file
367
engine/game/gameConnectionMoves.cc
Executable file
@@ -0,0 +1,367 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/dnet.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/simBase.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/shapeBase.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "sim/pathManager.h"
|
||||
#include "audio/audio.h"
|
||||
#include "game/game.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "game/gameConnectionEvents.h"
|
||||
|
||||
F32 MoveManager::mForwardAction = 0;
|
||||
F32 MoveManager::mBackwardAction = 0;
|
||||
F32 MoveManager::mUpAction = 0;
|
||||
F32 MoveManager::mDownAction = 0;
|
||||
F32 MoveManager::mLeftAction = 0;
|
||||
F32 MoveManager::mRightAction = 0;
|
||||
|
||||
bool MoveManager::mFreeLook = false;
|
||||
F32 MoveManager::mPitch = 0;
|
||||
F32 MoveManager::mYaw = 0;
|
||||
F32 MoveManager::mRoll = 0;
|
||||
|
||||
F32 MoveManager::mPitchUpSpeed = 0;
|
||||
F32 MoveManager::mPitchDownSpeed = 0;
|
||||
F32 MoveManager::mYawLeftSpeed = 0;
|
||||
F32 MoveManager::mYawRightSpeed = 0;
|
||||
F32 MoveManager::mRollLeftSpeed = 0;
|
||||
F32 MoveManager::mRollRightSpeed = 0;
|
||||
|
||||
U32 MoveManager::mTriggerCount[MaxTriggerKeys] = { 0, };
|
||||
U32 MoveManager::mPrevTriggerCount[MaxTriggerKeys] = { 0, };
|
||||
|
||||
#define MAX_MOVE_PACKET_SENDS 4
|
||||
|
||||
const Move NullMove =
|
||||
{
|
||||
16,16,16,
|
||||
0,0,0,
|
||||
0,0,0, // x,y,z
|
||||
0,0,0, // Yaw, pitch, roll,
|
||||
0,0,
|
||||
|
||||
false,
|
||||
false,false,false,false,false,false
|
||||
};
|
||||
|
||||
void MoveManager::init()
|
||||
{
|
||||
Con::addVariable("mvForwardAction", TypeF32, &mForwardAction);
|
||||
Con::addVariable("mvBackwardAction", TypeF32, &mBackwardAction);
|
||||
Con::addVariable("mvUpAction", TypeF32, &mUpAction);
|
||||
Con::addVariable("mvDownAction", TypeF32, &mDownAction);
|
||||
Con::addVariable("mvLeftAction", TypeF32, &mLeftAction);
|
||||
Con::addVariable("mvRightAction", TypeF32, &mRightAction);
|
||||
|
||||
Con::addVariable("mvFreeLook", TypeBool, &mFreeLook);
|
||||
Con::addVariable("mvPitch", TypeF32, &mPitch);
|
||||
Con::addVariable("mvYaw", TypeF32, &mYaw);
|
||||
Con::addVariable("mvRoll", TypeF32, &mRoll);
|
||||
Con::addVariable("mvPitchUpSpeed", TypeF32, &mPitchUpSpeed);
|
||||
Con::addVariable("mvPitchDownSpeed", TypeF32, &mPitchDownSpeed);
|
||||
Con::addVariable("mvYawLeftSpeed", TypeF32, &mYawLeftSpeed);
|
||||
Con::addVariable("mvYawRightSpeed", TypeF32, &mYawRightSpeed);
|
||||
Con::addVariable("mvRollLeftSpeed", TypeF32, &mRollLeftSpeed);
|
||||
Con::addVariable("mvRollRightSpeed", TypeF32, &mRollRightSpeed);
|
||||
|
||||
for(U32 i = 0; i < MaxTriggerKeys; i++)
|
||||
{
|
||||
char varName[256];
|
||||
dSprintf(varName, sizeof(varName), "mvTriggerCount%d", i);
|
||||
Con::addVariable(varName, TypeS32, &mTriggerCount[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static inline F32 clampFloatWrap(F32 val)
|
||||
{
|
||||
return val - F32(S32(val));
|
||||
}
|
||||
|
||||
static F32 clampFloatClamp(F32 val, U32 bits)
|
||||
{
|
||||
if(val < 0)
|
||||
val = 0;
|
||||
else if(val > 1)
|
||||
val = 1;
|
||||
F32 mask = (1 << bits);
|
||||
return U32(val * mask) / F32(mask);
|
||||
}
|
||||
|
||||
static inline S32 clampRangeClamp(F32 val)
|
||||
{
|
||||
if(val < -1)
|
||||
return 0;
|
||||
if(val > 1)
|
||||
return 32;
|
||||
return (S32)((val + 1) * 16);
|
||||
}
|
||||
|
||||
|
||||
#define FANG2IANG(x) ((U32)((S16)((F32(0x10000) / M_2PI) * x)) & 0xFFFF)
|
||||
#define IANG2FANG(x) (F32)((M_2PI / F32(0x10000)) * (F32)((S16)x))
|
||||
|
||||
void Move::unclamp()
|
||||
{
|
||||
yaw = IANG2FANG(pyaw);
|
||||
pitch = IANG2FANG(ppitch);
|
||||
roll = IANG2FANG(proll);
|
||||
|
||||
x = (px - 16) / F32(16);
|
||||
y = (py - 16) / F32(16);
|
||||
z = (pz - 16) / F32(16);
|
||||
}
|
||||
|
||||
void Move::clamp()
|
||||
{
|
||||
// angles are all 16 bit.
|
||||
pyaw = FANG2IANG(yaw);
|
||||
ppitch = FANG2IANG(pitch);
|
||||
proll = FANG2IANG(roll);
|
||||
|
||||
px = clampRangeClamp(x);
|
||||
py = clampRangeClamp(y);
|
||||
pz = clampRangeClamp(z);
|
||||
unclamp();
|
||||
}
|
||||
|
||||
void Move::pack(BitStream *stream)
|
||||
{
|
||||
if(stream->writeFlag(pyaw != 0))
|
||||
stream->writeInt(pyaw, 16);
|
||||
if(stream->writeFlag(ppitch != 0))
|
||||
stream->writeInt(ppitch, 16);
|
||||
if(stream->writeFlag(proll != 0))
|
||||
stream->writeInt(proll, 16);
|
||||
|
||||
stream->writeInt(px, 6);
|
||||
stream->writeInt(py, 6);
|
||||
stream->writeInt(pz, 6);
|
||||
stream->writeFlag(freeLook);
|
||||
|
||||
for(U32 i = 0; i < MaxTriggerKeys; i++)
|
||||
stream->writeFlag(trigger[i]);
|
||||
}
|
||||
|
||||
void Move::unpack(BitStream *stream)
|
||||
{
|
||||
if(stream->readFlag())
|
||||
pyaw = stream->readInt(16);
|
||||
else
|
||||
pyaw = 0;
|
||||
if(stream->readFlag())
|
||||
ppitch = stream->readInt(16);
|
||||
else
|
||||
ppitch = 0;
|
||||
if(stream->readFlag())
|
||||
proll = stream->readInt(16);
|
||||
else
|
||||
proll = 0;
|
||||
px = stream->readInt(6);
|
||||
py = stream->readInt(6);
|
||||
pz = stream->readInt(6);
|
||||
|
||||
unclamp();
|
||||
freeLook = stream->readFlag();
|
||||
|
||||
for(U32 i = 0; i < MaxTriggerKeys; i++)
|
||||
trigger[i] = stream->readFlag();
|
||||
}
|
||||
|
||||
bool GameConnection::getNextMove(Move &curMove)
|
||||
{
|
||||
if(mMoveList.size() > MaxMoveQueueSize)
|
||||
return false;
|
||||
|
||||
F32 pitchAdd = MoveManager::mPitchUpSpeed - MoveManager::mPitchDownSpeed;
|
||||
F32 yawAdd = MoveManager::mYawLeftSpeed - MoveManager::mYawRightSpeed;
|
||||
F32 rollAdd = MoveManager::mRollRightSpeed - MoveManager::mRollLeftSpeed;
|
||||
|
||||
curMove.pitch = MoveManager::mPitch + pitchAdd;
|
||||
curMove.yaw = MoveManager::mYaw + yawAdd;
|
||||
curMove.roll = MoveManager::mRoll + rollAdd;
|
||||
|
||||
MoveManager::mPitch = 0;
|
||||
MoveManager::mYaw = 0;
|
||||
MoveManager::mRoll = 0;
|
||||
|
||||
curMove.x = MoveManager::mRightAction - MoveManager::mLeftAction;
|
||||
curMove.y = MoveManager::mForwardAction - MoveManager::mBackwardAction;
|
||||
curMove.z = MoveManager::mUpAction - MoveManager::mDownAction;
|
||||
|
||||
curMove.freeLook = MoveManager::mFreeLook;
|
||||
|
||||
for(U32 i = 0; i < MaxTriggerKeys; i++)
|
||||
{
|
||||
curMove.trigger[i] = false;
|
||||
if(MoveManager::mTriggerCount[i] & 1)
|
||||
curMove.trigger[i] = true;
|
||||
else if(!(MoveManager::mPrevTriggerCount[i] & 1) && MoveManager::mPrevTriggerCount[i] != MoveManager::mTriggerCount[i])
|
||||
curMove.trigger[i] = true;
|
||||
MoveManager::mPrevTriggerCount[i] = MoveManager::mTriggerCount[i];
|
||||
}
|
||||
curMove.clamp(); // clamp for net traffic
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameConnection::pushMove(const Move &mv)
|
||||
{
|
||||
U32 id = mFirstMoveIndex + mMoveList.size();
|
||||
U32 sz = mMoveList.size();
|
||||
mMoveList.push_back(mv);
|
||||
mMoveList[sz].id = id;
|
||||
mMoveList[sz].sendCount = 0;
|
||||
}
|
||||
|
||||
void GameConnection::getMoveList(Move** movePtr,U32* numMoves)
|
||||
{
|
||||
if (isConnectionToServer())
|
||||
{
|
||||
// give back moves starting at the last client move...
|
||||
|
||||
AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request");
|
||||
AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveList.size(), "Desynched first and last move.");
|
||||
*numMoves = mMoveList.size() - mLastClientMove + mFirstMoveIndex;
|
||||
*movePtr = mMoveList.address() + mLastClientMove - mFirstMoveIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// On the server we keep our own move list.
|
||||
*numMoves = (mMoveList.size() < mMoveCredit)?
|
||||
mMoveList.size(): mMoveCredit;
|
||||
*movePtr = mMoveList.begin();
|
||||
//
|
||||
mMoveCredit -= *numMoves;
|
||||
mMoveList.setSize(*numMoves);
|
||||
}
|
||||
}
|
||||
|
||||
void GameConnection::collectMove(U32 time)
|
||||
{
|
||||
Move mv;
|
||||
if(!isPlayingBack() && getNextMove(mv))
|
||||
{
|
||||
pushMove(mv);
|
||||
recordBlock(BlockTypeMove, sizeof(Move), &mv);
|
||||
}
|
||||
}
|
||||
|
||||
void GameConnection::clearMoves(U32 count)
|
||||
{
|
||||
if (isConnectionToServer()) {
|
||||
mLastClientMove += count;
|
||||
}
|
||||
else {
|
||||
AssertFatal(count <= mMoveList.size(),"GameConnection: Clearing too many moves");
|
||||
if (count == mMoveList.size())
|
||||
mMoveList.clear();
|
||||
else
|
||||
while (count--)
|
||||
mMoveList.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void GameConnection::incMoveCredit(U32 ticks)
|
||||
{
|
||||
AssertFatal(!isConnectionToServer(), "Cannot inc move credit on the client.");
|
||||
// Game tick increment
|
||||
if ((mMoveCredit += ticks) > MaxMoveCount)
|
||||
mMoveCredit = MaxMoveCount;
|
||||
|
||||
// Clear pending moves for the elapsed time if there
|
||||
// is no control object.
|
||||
if (mControlObject.isNull())
|
||||
mMoveList.clear();
|
||||
}
|
||||
|
||||
bool GameConnection::areMovesPending()
|
||||
{
|
||||
return isConnectionToServer() ?
|
||||
mMoveList.size() - mLastClientMove + mFirstMoveIndex :
|
||||
mMoveList.size();
|
||||
}
|
||||
|
||||
bool GameConnection::isBacklogged()
|
||||
{
|
||||
// If there are no pending moves and the input queue is full,
|
||||
// then the connection to the server must be clogged.
|
||||
if(!isConnectionToServer())
|
||||
return false;
|
||||
return mLastClientMove - mFirstMoveIndex == mMoveList.size() &&
|
||||
mMoveList.size() >= MaxMoveCount;
|
||||
}
|
||||
|
||||
|
||||
void GameConnection::moveWritePacket(BitStream *bstream)
|
||||
{
|
||||
Move* move;
|
||||
U32 count;
|
||||
AssertFatal(mLastMoveAck == mFirstMoveIndex, "Invalid move index.");
|
||||
count = mMoveList.size();
|
||||
move = mMoveList.address();
|
||||
U32 start = mLastMoveAck;
|
||||
U32 offset;
|
||||
for(offset = 0; offset < count; offset++)
|
||||
if(move[offset].sendCount < MAX_MOVE_PACKET_SENDS)
|
||||
break;
|
||||
if(offset == count && count != 0)
|
||||
offset--;
|
||||
|
||||
start += offset;
|
||||
count -= offset;
|
||||
|
||||
if (count > MaxMoveCount)
|
||||
count = MaxMoveCount;
|
||||
bstream->writeInt(start,32);
|
||||
bstream->writeInt(count,MoveCountBits);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
move[offset + i].sendCount++;
|
||||
move[offset + i].pack(bstream);
|
||||
}
|
||||
}
|
||||
|
||||
void GameConnection::moveReadPacket(BitStream *bstream)
|
||||
{
|
||||
// Server side packet read.
|
||||
U32 start = bstream->readInt(32);
|
||||
U32 count = bstream->readInt(MoveCountBits);
|
||||
|
||||
// Skip forward (must be starting up), or over the moves
|
||||
// we already have.
|
||||
int skip = mLastMoveAck - start;
|
||||
if (skip < 0) {
|
||||
mLastMoveAck = start;
|
||||
//mMoveList.clear();
|
||||
}
|
||||
else {
|
||||
Move tmp;
|
||||
if (skip > count)
|
||||
skip = count;
|
||||
for (int i = 0; i < skip; i++)
|
||||
tmp.unpack(bstream);
|
||||
start += skip;
|
||||
count = count - skip;
|
||||
}
|
||||
|
||||
// Put the rest on the move list.
|
||||
int index = mMoveList.size();
|
||||
mMoveList.increment(count);
|
||||
while (index < mMoveList.size())
|
||||
{
|
||||
mMoveList[index].unpack(bstream);
|
||||
mMoveList[index].id = start++;
|
||||
index ++;
|
||||
}
|
||||
|
||||
mLastMoveAck += count;
|
||||
}
|
||||
|
||||
|
||||
69
engine/game/gameFunctions.cc
Executable file
69
engine/game/gameFunctions.cc
Executable file
@@ -0,0 +1,69 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "sim/sceneObject.h"
|
||||
#include "core/fileStream.h"
|
||||
|
||||
void RegisterGameFunctions();
|
||||
|
||||
// query
|
||||
SimpleQueryList gServerQueryList;
|
||||
U32 gServerQueryIndex = 0;
|
||||
|
||||
//SERVER FUNCTIONS ONLY
|
||||
ConsoleFunctionGroupBegin( Containers, "Spatial query functions. <b>Server side only!</b>");
|
||||
|
||||
ConsoleFunction(containerFindFirst, const char*, 6, 6, "(bitset type, Point3F point, float x, float y, float z)"
|
||||
"Find objects matching the bitmask type within a box centered at point, with extents x, y, z.\n\n"
|
||||
"Returns the first object found; thereafter, you can get more results using containerFindNext().")
|
||||
{
|
||||
//find out what we're looking for
|
||||
U32 typeMask = U32(dAtoi(argv[1]));
|
||||
|
||||
//find the center of the container volume
|
||||
Point3F origin(0, 0, 0);
|
||||
dSscanf(argv[2], "%g %g %g", &origin.x, &origin.y, &origin.z);
|
||||
|
||||
//find the box dimensions
|
||||
Point3F size(0, 0, 0);
|
||||
size.x = mFabs(dAtof(argv[3]));
|
||||
size.y = mFabs(dAtof(argv[4]));
|
||||
size.z = mFabs(dAtof(argv[5]));
|
||||
|
||||
//build the container volume
|
||||
Box3F queryBox;
|
||||
queryBox.min = origin;
|
||||
queryBox.max = origin;
|
||||
queryBox.min -= size;
|
||||
queryBox.max += size;
|
||||
|
||||
//initialize the list, and do the query
|
||||
gServerQueryList.mList.clear();
|
||||
gServerContainer.findObjects(queryBox, typeMask, SimpleQueryList::insertionCallback, &gServerQueryList);
|
||||
|
||||
//return the first element
|
||||
gServerQueryIndex = 0;
|
||||
char *buff = Con::getReturnBuffer(100);
|
||||
if (gServerQueryList.mList.size())
|
||||
dSprintf(buff, 100, "%d", gServerQueryList.mList[gServerQueryIndex++]->getId());
|
||||
else
|
||||
buff[0] = '\0';
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleFunction( containerFindNext, const char*, 1, 1, "Get more results from a previous call to containerFindFirst().")
|
||||
{
|
||||
//return the next element
|
||||
char *buff = Con::getReturnBuffer(100);
|
||||
if (gServerQueryIndex < gServerQueryList.mList.size())
|
||||
dSprintf(buff, 100, "%d", gServerQueryList.mList[gServerQueryIndex++]->getId());
|
||||
else
|
||||
buff[0] = '\0';
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd( Containers );
|
||||
11
engine/game/gameFunctions.h
Executable file
11
engine/game/gameFunctions.h
Executable file
@@ -0,0 +1,11 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMEFUNCTIONS_H_
|
||||
#define _GAMEFUNCTIONS_H_
|
||||
|
||||
void RegisterGameFunctions();
|
||||
|
||||
#endif
|
||||
245
engine/game/gameProcess.cc
Executable file
245
engine/game/gameProcess.cc
Executable file
@@ -0,0 +1,245 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "core/dnet.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/gameBase.h"
|
||||
#include "game/shapeBase.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ProcessList gClientProcessList(false);
|
||||
ProcessList gServerProcessList(true);
|
||||
|
||||
bool ProcessList::mDebugControlSync = false;
|
||||
|
||||
ProcessList::ProcessList(bool isServer)
|
||||
{
|
||||
mDirty = false;
|
||||
mCurrentTag = 0;
|
||||
mLastTick = 0;
|
||||
mLastTime = 0;
|
||||
mLastDelta = 0;
|
||||
mIsServer = isServer;
|
||||
// Con::addVariable("debugControlSync",TypeBool, &mDebugControlSync);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ProcessList::orderList()
|
||||
{
|
||||
// GameBase tags are intialized to 0, so current tag
|
||||
// should never be 0.
|
||||
if (!++mCurrentTag)
|
||||
mCurrentTag++;
|
||||
|
||||
// Install a temporary head node
|
||||
GameBase list;
|
||||
list.plLinkBefore(head.mProcessLink.next);
|
||||
head.plUnlink();
|
||||
|
||||
// Reverse topological sort into the orignal head node
|
||||
while (list.mProcessLink.next != &list) {
|
||||
GameBase* ptr = list.mProcessLink.next;
|
||||
ptr->mProcessTag = mCurrentTag;
|
||||
ptr->plUnlink();
|
||||
if (ptr->mAfterObject) {
|
||||
// Build chain "stack" of dependant objects and patch
|
||||
// it to the end of the current list.
|
||||
while (bool(ptr->mAfterObject) &&
|
||||
ptr->mAfterObject->mProcessTag != mCurrentTag) {
|
||||
ptr->mAfterObject->mProcessTag = mCurrentTag;
|
||||
ptr->mAfterObject->plUnlink();
|
||||
ptr->mAfterObject->plLinkBefore(ptr);
|
||||
ptr = ptr->mAfterObject;
|
||||
}
|
||||
ptr->plJoin(&head);
|
||||
}
|
||||
else
|
||||
ptr->plLinkBefore(&head);
|
||||
}
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool ProcessList::advanceServerTime(SimTime timeDelta)
|
||||
{
|
||||
PROFILE_START(AdvanceServerTime);
|
||||
|
||||
if (mDirty) orderList();
|
||||
|
||||
SimTime targetTime = mLastTime + timeDelta;
|
||||
SimTime targetTick = targetTime & ~TickMask;
|
||||
SimTime tickCount = (targetTick - mLastTick) >> TickShift;
|
||||
|
||||
bool ret = mLastTick != targetTick;
|
||||
// Advance all the objects
|
||||
for (; mLastTick != targetTick; mLastTick += TickMs)
|
||||
advanceObjects();
|
||||
|
||||
// Credit all the connections with the elapsed ticks.
|
||||
SimGroup *g = Sim::getClientGroup();
|
||||
for (SimGroup::iterator i = g->begin(); i != g->end(); i++)
|
||||
if (GameConnection *t = dynamic_cast<GameConnection *>(*i))
|
||||
t->incMoveCredit(tickCount);
|
||||
|
||||
mLastTime = targetTime;
|
||||
PROFILE_END();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool ProcessList::advanceClientTime(SimTime timeDelta)
|
||||
{
|
||||
PROFILE_START(AdvanceClientTime);
|
||||
|
||||
if (mDirty) orderList();
|
||||
|
||||
SimTime targetTime = mLastTime + timeDelta;
|
||||
SimTime targetTick = (targetTime + TickMask) & ~TickMask;
|
||||
SimTime tickCount = (targetTick - mLastTick) >> TickShift;
|
||||
|
||||
// See if the control object has pending moves.
|
||||
GameBase* control = 0;
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
if (connection)
|
||||
{
|
||||
// If the connection to the server is backlogged
|
||||
// the simulation is frozen.
|
||||
if (connection->isBacklogged()) {
|
||||
mLastTime = targetTime;
|
||||
mLastTick = targetTick;
|
||||
PROFILE_END();
|
||||
return false;
|
||||
}
|
||||
if (connection->areMovesPending())
|
||||
control = connection->getControlObject();
|
||||
}
|
||||
|
||||
// If we are going to tick, or have moves pending for the
|
||||
// control object, we need to reset everyone back to their
|
||||
// last full tick pos.
|
||||
if (mLastDelta && (tickCount || control))
|
||||
for (GameBase* obj = head.mProcessLink.next; obj != &head;
|
||||
obj = obj->mProcessLink.next)
|
||||
if (obj->mProcessTick)
|
||||
obj->interpolateTick(0);
|
||||
|
||||
// Produce new moves and advance all the objects
|
||||
if (tickCount)
|
||||
{
|
||||
for (; mLastTick != targetTick; mLastTick += TickMs)
|
||||
{
|
||||
if(connection)
|
||||
{
|
||||
// process any demo blocks that are NOT moves, and exactly one move
|
||||
// we advance time in the demo stream by a move inserted on
|
||||
// each tick. So before doing the tick processing we advance
|
||||
// the demo stream until a move is ready
|
||||
if(connection->isPlayingBack())
|
||||
{
|
||||
U32 blockType;
|
||||
do
|
||||
{
|
||||
blockType = connection->getNextBlockType();
|
||||
bool res = connection->processNextBlock();
|
||||
// if there are no more blocks, exit out of this function,
|
||||
// as no more client time needs to process right now - we'll
|
||||
// get it all on the next advanceClientTime()
|
||||
if(!res)
|
||||
{
|
||||
PROFILE_END();
|
||||
return true;
|
||||
}
|
||||
} while(blockType != GameConnection::BlockTypeMove);
|
||||
}
|
||||
connection->collectMove(mLastTick);
|
||||
}
|
||||
advanceObjects();
|
||||
}
|
||||
}
|
||||
else
|
||||
if (control)
|
||||
{
|
||||
// Sync up the control object with the latest client moves.
|
||||
Move* movePtr;
|
||||
U32 m = 0, numMoves;
|
||||
connection->getMoveList(&movePtr, &numMoves);
|
||||
while (m < numMoves)
|
||||
{
|
||||
control->processTick(&movePtr[m++]);
|
||||
}
|
||||
connection->clearMoves(m);
|
||||
}
|
||||
|
||||
mLastDelta = (TickMs - (targetTime & TickMask)) & TickMask;
|
||||
F32 dt = mLastDelta / F32(TickMs);
|
||||
for (GameBase* obj = head.mProcessLink.next; obj != &head;
|
||||
obj = obj->mProcessLink.next)
|
||||
if (obj->mProcessTick)
|
||||
obj->interpolateTick(dt);
|
||||
|
||||
// Inform objects of total elapsed delta so they can advance
|
||||
// client side animations.
|
||||
dt = F32(timeDelta) / 1000;
|
||||
for (GameBase* obj = head.mProcessLink.next; obj != &head;
|
||||
obj = obj->mProcessLink.next)
|
||||
obj->advanceTime(dt);
|
||||
|
||||
mLastTime = targetTime;
|
||||
PROFILE_END();
|
||||
return tickCount != 0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ProcessList::advanceObjects()
|
||||
{
|
||||
PROFILE_START(AdvanceObjects);
|
||||
|
||||
// A little link list shuffling is done here to avoid problems
|
||||
// with objects being deleted from within the process method.
|
||||
GameBase list;
|
||||
GameBase* obj;
|
||||
list.plLinkBefore(head.mProcessLink.next);
|
||||
head.plUnlink();
|
||||
while ((obj = list.mProcessLink.next) != &list) {
|
||||
obj->plUnlink();
|
||||
obj->plLinkBefore(&head);
|
||||
|
||||
// Each object is either advanced a single tick, or if it's
|
||||
// being controlled by a client, ticked once for each pending move.
|
||||
if (obj->mTypeMask & GameBaseObjectType) {
|
||||
|
||||
GameBase *pGB = static_cast<GameBase *>(obj);
|
||||
GameConnection* con = pGB->getControllingClient();
|
||||
|
||||
if (con && con->getControlObject() == pGB) {
|
||||
Move* movePtr;
|
||||
U32 m, numMoves;
|
||||
|
||||
con->getMoveList(&movePtr, &numMoves);
|
||||
|
||||
for (m = 0; m < numMoves && pGB->getControllingClient() == con; )
|
||||
obj->processTick(&movePtr[m++]);
|
||||
|
||||
con->clearMoves(m);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (obj->mProcessTick)
|
||||
obj->processTick(0);
|
||||
}
|
||||
PROFILE_END();
|
||||
}
|
||||
92
engine/game/gameTSCtrl.cc
Executable file
92
engine/game/gameTSCtrl.cc
Executable file
@@ -0,0 +1,92 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/gameTSCtrl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "game/projectile.h"
|
||||
#include "game/gameBase.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/shapeBase.h"
|
||||
#include "game/player.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Debug stuff:
|
||||
Point3F lineTestStart = Point3F(0, 0, 0);
|
||||
Point3F lineTestEnd = Point3F(0, 1000, 0);
|
||||
Point3F lineTestIntersect = Point3F(0, 0, 0);
|
||||
bool gSnapLine = false;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Class: GameTSCtrl
|
||||
//----------------------------------------------------------------------------
|
||||
IMPLEMENT_CONOBJECT(GameTSCtrl);
|
||||
|
||||
GameTSCtrl::GameTSCtrl()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
bool GameTSCtrl::processCameraQuery(CameraQuery *camq)
|
||||
{
|
||||
GameUpdateCameraFov();
|
||||
return GameProcessCameraQuery(camq);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void GameTSCtrl::renderWorld(const RectI &updateRect)
|
||||
{
|
||||
GameRenderWorld();
|
||||
dglSetClipRect(updateRect);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void GameTSCtrl::onMouseMove(const GuiEvent &evt)
|
||||
{
|
||||
if(gSnapLine)
|
||||
return;
|
||||
|
||||
MatrixF mat;
|
||||
Point3F vel;
|
||||
if ( GameGetCameraTransform(&mat, &vel) )
|
||||
{
|
||||
Point3F pos;
|
||||
mat.getColumn(3,&pos);
|
||||
const Point3F screenPoint(evt.mousePoint.x, evt.mousePoint.y, -1);
|
||||
Point3F worldPoint;
|
||||
|
||||
if (unproject(screenPoint, &worldPoint))
|
||||
{
|
||||
Point3F vec = worldPoint - pos;
|
||||
lineTestStart = pos;
|
||||
vec.normalizeSafe();
|
||||
lineTestEnd = pos + vec * 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameTSCtrl::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
// check if should bother with a render
|
||||
GameConnection * con = GameConnection::getConnectionToServer();
|
||||
bool skipRender = !con || (con->getWhiteOut() >= 1.f) || (con->getDamageFlash() >= 1.f) || (con->getBlackOut() >= 1.f);
|
||||
|
||||
if(!skipRender)
|
||||
Parent::onRender(offset, updateRect);
|
||||
|
||||
dglSetViewport(updateRect);
|
||||
CameraQuery camq = mLastCameraQuery;
|
||||
if(GameProcessCameraQuery(&camq))
|
||||
GameRenderFilters(camq);
|
||||
|
||||
// Draw controls after so they aren't affected by the filters. (If we're doing that.)
|
||||
if(!skipRender && !mApplyFilterToChildren)
|
||||
Parent::renderChildControls(offset, updateRect);}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction( snapToggle, void, 1, 1, "()" )
|
||||
{
|
||||
gSnapLine = !gSnapLine;
|
||||
}
|
||||
40
engine/game/gameTSCtrl.h
Executable file
40
engine/game/gameTSCtrl.h
Executable file
@@ -0,0 +1,40 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMETSCTRL_H_
|
||||
#define _GAMETSCTRL_H_
|
||||
|
||||
#ifndef _DGL_H_
|
||||
#include "dgl/dgl.h"
|
||||
#endif
|
||||
#ifndef _GAME_H_
|
||||
#include "game/game.h"
|
||||
#endif
|
||||
#ifndef _GUITSCONTROL_H_
|
||||
#include "gui/core/guiTSControl.h"
|
||||
#endif
|
||||
|
||||
class ProjectileData;
|
||||
class GameBase;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class GameTSCtrl : public GuiTSCtrl
|
||||
{
|
||||
private:
|
||||
typedef GuiTSCtrl Parent;
|
||||
|
||||
public:
|
||||
GameTSCtrl();
|
||||
|
||||
bool processCameraQuery(CameraQuery *query);
|
||||
void renderWorld(const RectI &updateRect);
|
||||
|
||||
void onMouseMove(const GuiEvent &evt);
|
||||
void onRender(Point2I offset, const RectI &updateRect);
|
||||
|
||||
DECLARE_CONOBJECT(GameTSCtrl);
|
||||
};
|
||||
|
||||
#endif
|
||||
18
engine/game/guiNoMouseCtrl.cc
Executable file
18
engine/game/guiNoMouseCtrl.cc
Executable file
@@ -0,0 +1,18 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "gui/core/guiControl.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class GuiNoMouseCtrl : public GuiControl
|
||||
{
|
||||
typedef GuiControl Parent;
|
||||
public:
|
||||
|
||||
// GuiControl
|
||||
bool pointInControl(const Point2I &) { return(false); }
|
||||
DECLARE_CONOBJECT(GuiNoMouseCtrl);
|
||||
};
|
||||
IMPLEMENT_CONOBJECT(GuiNoMouseCtrl);
|
||||
336
engine/game/guiPlayerView.cc
Executable file
336
engine/game/guiPlayerView.cc
Executable file
@@ -0,0 +1,336 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "game/guiPlayerView.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
static const F32 MaxOrbitDist = 5.0f;
|
||||
static const S32 MaxAnimations = 6;
|
||||
|
||||
IMPLEMENT_CONOBJECT( GuiPlayerView );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
GuiPlayerView::GuiPlayerView() : GuiTSCtrl()
|
||||
{
|
||||
mActive = true;
|
||||
mMouseState = None;
|
||||
mModel = NULL;
|
||||
mWeapon = NULL;
|
||||
mLastMousePoint.set( 0, 0 );
|
||||
lastRenderTime = 0;
|
||||
runThread = 0;
|
||||
wNode = -1;
|
||||
pNode = -1;
|
||||
mAnimationSeq = 0;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
GuiPlayerView::~GuiPlayerView()
|
||||
{
|
||||
if ( mModel )
|
||||
{
|
||||
delete mModel;
|
||||
mModel = NULL;
|
||||
}
|
||||
if ( mWeapon )
|
||||
{
|
||||
delete mWeapon;
|
||||
mWeapon = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleMethod( GuiPlayerView, setModel, void, 4, 4, "playerView.setModel( raceGender, skin )" )
|
||||
{
|
||||
argc;
|
||||
object->setPlayerModel( argv[2], argv[3] );
|
||||
}
|
||||
|
||||
ConsoleMethod( GuiPlayerView, setSeq, void, 3, 3, "playerView.setSeq( index )" )
|
||||
{
|
||||
argc;
|
||||
object->setPlayerSeq( dAtoi(argv[2]) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GuiPlayerView::onWake()
|
||||
{
|
||||
if ( !Parent::onWake() )
|
||||
return( false );
|
||||
|
||||
mCameraMatrix.identity();
|
||||
mCameraRot.set( 0, 0, 3.9 );
|
||||
mCameraPos.set( 0, 1.75, 1.25 );
|
||||
mCameraMatrix.setColumn( 3, mCameraPos );
|
||||
mOrbitPos.set( 0, 0, 0 );
|
||||
mOrbitDist = 3.5f;
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::onMouseDown( const GuiEvent &event )
|
||||
{
|
||||
if ( !mActive || !mVisible || !mAwake )
|
||||
return;
|
||||
|
||||
mMouseState = Rotating;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::onMouseUp( const GuiEvent &/*event*/ )
|
||||
{
|
||||
mouseUnlock();
|
||||
mMouseState = None;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::onMouseDragged( const GuiEvent &event )
|
||||
{
|
||||
if ( mMouseState != Rotating )
|
||||
return;
|
||||
|
||||
Point2I delta = event.mousePoint - mLastMousePoint;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
|
||||
mCameraRot.x += ( delta.y * 0.01 );
|
||||
mCameraRot.z += ( delta.x * 0.01 );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::onRightMouseDown( const GuiEvent &event )
|
||||
{
|
||||
mMouseState = Zooming;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::onRightMouseUp( const GuiEvent &/*event*/ )
|
||||
{
|
||||
mouseUnlock();
|
||||
mMouseState = None;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::onRightMouseDragged( const GuiEvent &event )
|
||||
{
|
||||
if ( mMouseState != Zooming )
|
||||
return;
|
||||
|
||||
S32 delta = event.mousePoint.y - mLastMousePoint.y;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
|
||||
mOrbitDist += ( delta * 0.01 );
|
||||
}
|
||||
|
||||
void GuiPlayerView::setPlayerSeq( S32 index )
|
||||
{
|
||||
if( index > MaxAnimations || index < 0 )
|
||||
return;
|
||||
|
||||
mAnimationSeq = index;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::setPlayerModel( const char* shape, const char* skin )
|
||||
{
|
||||
// Stuff random rotation values in...
|
||||
mCameraRot.z = gRandGen.randF(-4.14, -1);
|
||||
|
||||
if ( mModel )
|
||||
{
|
||||
delete mModel;
|
||||
mModel = NULL;
|
||||
}
|
||||
|
||||
if ( mWeapon )
|
||||
{
|
||||
delete mWeapon;
|
||||
mWeapon = NULL;
|
||||
}
|
||||
|
||||
runThread = 0;
|
||||
|
||||
Resource<TSShape> hShape = ResourceManager->load( shape);
|
||||
if ( !bool( hShape ) )
|
||||
return;
|
||||
|
||||
mModel = new TSShapeInstance( hShape, true );
|
||||
AssertFatal( mModel, "ERROR! Failed to load warrior model!" );
|
||||
|
||||
// Set the skin:
|
||||
if ( !mModel->ownMaterialList() )
|
||||
mModel->cloneMaterialList();
|
||||
|
||||
TSMaterialList* materialList = mModel->getMaterialList();
|
||||
for ( U32 i = 0; i < materialList->mMaterialNames.size(); i++ )
|
||||
{
|
||||
const char* name = materialList->mMaterialNames[i];
|
||||
if ( !name )
|
||||
continue;
|
||||
|
||||
const U32 len = dStrlen( name );
|
||||
AssertISV( len < 200, "GuiPlayerView::setPlayerModel - Skin name exceeds maximum length!" );
|
||||
if ( len < 6 )
|
||||
continue;
|
||||
|
||||
const char* replace = dStrstr( name, "base." );
|
||||
if ( !replace )
|
||||
continue;
|
||||
|
||||
char newName[256];
|
||||
dStrncpy( newName, name, replace - name );
|
||||
newName[replace - name] = 0;
|
||||
dStrcat( newName, skin );
|
||||
dStrcat( newName, "." );
|
||||
dStrcat( newName, replace + 5 );
|
||||
|
||||
TextureHandle test = TextureHandle( newName, MeshTexture, false );
|
||||
if ( test.getGLName() )
|
||||
materialList->mMaterials[i] = test;
|
||||
else
|
||||
materialList->mMaterials[i] = TextureHandle( name, MeshTexture, false );
|
||||
}
|
||||
|
||||
// Initialize camera values:
|
||||
mOrbitPos = mModel->getShape()->center;
|
||||
mMinOrbitDist = mModel->getShape()->radius;
|
||||
|
||||
// // initialize run thread
|
||||
// S32 sequence = hShape->findSequence("dummyRun");
|
||||
//
|
||||
// if( sequence != -1 )
|
||||
// {
|
||||
// runThread = mModel->addThread();
|
||||
// mModel->setPos( runThread, 0 );
|
||||
// mModel->setTimeScale( runThread, 1 );
|
||||
// mModel->setSequence( runThread, sequence, 0 );
|
||||
// }
|
||||
|
||||
// create a weapon for this dude
|
||||
Resource<TSShape> wShape;
|
||||
if( dStrcmp( shape, "heavy_male") == 0 || dStrcmp( shape, "bioderm_heavy") == 0 || dStrcmp( shape, "heavy_female") == 0 )
|
||||
{
|
||||
wShape = ResourceManager->load( "shapes/weapon_mortar.dts" );
|
||||
}
|
||||
else if( dStrcmp( shape, "medium_male") == 0 || dStrcmp( shape, "bioderm_medium") == 0 || dStrcmp( shape, "medium_female") == 0 )
|
||||
{
|
||||
wShape = ResourceManager->load( "shapes/weapon_plasma.dts" );
|
||||
}
|
||||
else
|
||||
{
|
||||
wShape = ResourceManager->load( "shapes/weapon_disc.dts" );
|
||||
}
|
||||
|
||||
if ( !bool( wShape ) )
|
||||
return;
|
||||
|
||||
pNode = hShape->findNode("mount0");
|
||||
wNode = wShape->findNode("mountPoint");
|
||||
|
||||
mWeapon = new TSShapeInstance( wShape, true );
|
||||
AssertFatal( mWeapon, "ERROR! Failed to load Weapon Model!" );
|
||||
|
||||
// the first time recording
|
||||
lastRenderTime = Platform::getVirtualMilliseconds();
|
||||
}
|
||||
|
||||
void GuiPlayerView::getWeaponTransform( MatrixF *mat )
|
||||
{
|
||||
MatrixF weapTrans = mWeapon->mNodeTransforms[wNode];
|
||||
Point3F weapOffset = -weapTrans.getPosition();
|
||||
MatrixF modelTrans = mModel->mNodeTransforms[pNode];
|
||||
modelTrans.mulP( weapOffset );
|
||||
modelTrans.setPosition( weapOffset );
|
||||
*mat = modelTrans;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool GuiPlayerView::processCameraQuery( CameraQuery* query )
|
||||
{
|
||||
// Make sure the orbit distance is within the acceptable range:
|
||||
mOrbitDist = ( mOrbitDist < mMinOrbitDist ) ? mMinOrbitDist : ( ( mOrbitDist > MaxOrbitDist ) ? MaxOrbitDist : mOrbitDist );
|
||||
|
||||
// Adjust the camera so that we are still facing the model:
|
||||
Point3F vec;
|
||||
MatrixF xRot, zRot;
|
||||
xRot.set( EulerF( mCameraRot.x, 0, 0 ) );
|
||||
zRot.set( EulerF( 0, 0, mCameraRot.z ) );
|
||||
|
||||
mCameraMatrix.mul( zRot, xRot );
|
||||
mCameraMatrix.getColumn( 1, &vec );
|
||||
vec *= mOrbitDist;
|
||||
mCameraPos = mOrbitPos - vec;
|
||||
|
||||
query->nearPlane = 0.1;
|
||||
query->farPlane = 2100.0;
|
||||
query->fov = 3.1415 / 3.5;
|
||||
mCameraMatrix.setColumn( 3, mCameraPos );
|
||||
query->cameraMatrix = mCameraMatrix;
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void GuiPlayerView::renderWorld( const RectI &updateRect )
|
||||
{
|
||||
if ( !(bool)mModel /* || !(bool)mWeapon */)
|
||||
return;
|
||||
|
||||
S32 time = Platform::getVirtualMilliseconds();
|
||||
S32 dt = time - lastRenderTime;
|
||||
lastRenderTime = time;
|
||||
|
||||
glClear( GL_DEPTH_BUFFER_BIT );
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
|
||||
glEnable( GL_DEPTH_TEST );
|
||||
glDepthFunc( GL_LEQUAL );
|
||||
|
||||
// animate and render in a run pose
|
||||
F32 fdt = dt;
|
||||
// mModel->advanceTime( fdt/1000.f, runThread );
|
||||
mModel->animate();
|
||||
mModel->render();
|
||||
|
||||
// render a weapon, if we have one
|
||||
if(mWeapon)
|
||||
{
|
||||
MatrixF mat;
|
||||
getWeaponTransform( &mat );
|
||||
glPushMatrix();
|
||||
dglMultMatrix( &mat );
|
||||
|
||||
mWeapon->render();
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
glDisable( GL_DEPTH_TEST );
|
||||
dglSetClipRect( updateRect );
|
||||
dglSetCanonicalState();
|
||||
}
|
||||
|
||||
void GuiPlayerView::onMouseEnter(const GuiEvent & event)
|
||||
{
|
||||
Con::executef(this, 1, "onMouseEnter");
|
||||
}
|
||||
|
||||
void GuiPlayerView::onMouseLeave(const GuiEvent & event)
|
||||
{
|
||||
Con::executef(this, 1, "onMouseLeave");
|
||||
}
|
||||
93
engine/game/guiPlayerView.h
Executable file
93
engine/game/guiPlayerView.h
Executable file
@@ -0,0 +1,93 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GUIPLAYERVIEW_H_
|
||||
#define _GUIPLAYERVIEW_H_
|
||||
|
||||
#ifndef _GUITSCONTROL_H_
|
||||
#include "gui/core/guiTSControl.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#endif
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "game/gameBase.h"
|
||||
#endif
|
||||
#ifndef _CAMERA_H_
|
||||
#include "game/camera.h"
|
||||
#endif
|
||||
|
||||
class TSThread;
|
||||
|
||||
class GuiPlayerView : public GuiTSCtrl
|
||||
{
|
||||
private:
|
||||
typedef GuiTSCtrl Parent;
|
||||
|
||||
enum
|
||||
{
|
||||
run = 0,
|
||||
hi,
|
||||
anim1,
|
||||
anim2,
|
||||
anim3,
|
||||
anim4,
|
||||
anim5
|
||||
};
|
||||
|
||||
protected:
|
||||
enum MouseState
|
||||
{
|
||||
None,
|
||||
Rotating,
|
||||
Zooming
|
||||
};
|
||||
|
||||
MouseState mMouseState;
|
||||
|
||||
TSShapeInstance* mModel;
|
||||
TSShapeInstance* mWeapon;
|
||||
U32 mSkinTag;
|
||||
|
||||
Point3F mCameraPos;
|
||||
MatrixF mCameraMatrix;
|
||||
EulerF mCameraRot;
|
||||
Point3F mOrbitPos;
|
||||
F32 mMinOrbitDist;
|
||||
F32 mOrbitDist;
|
||||
S32 wNode;
|
||||
S32 pNode;
|
||||
|
||||
TSThread *runThread;
|
||||
S32 lastRenderTime;
|
||||
S32 mAnimationSeq;
|
||||
|
||||
Point2I mLastMousePoint;
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT( GuiPlayerView );
|
||||
GuiPlayerView();
|
||||
~GuiPlayerView();
|
||||
|
||||
bool onWake();
|
||||
|
||||
void onMouseEnter(const GuiEvent &event);
|
||||
void onMouseLeave(const GuiEvent &event);
|
||||
void onMouseDown( const GuiEvent &event );
|
||||
void onMouseUp( const GuiEvent &event );
|
||||
void onMouseDragged( const GuiEvent &event );
|
||||
void onRightMouseDown( const GuiEvent &event );
|
||||
void onRightMouseUp( const GuiEvent &event );
|
||||
void onRightMouseDragged( const GuiEvent &event );
|
||||
|
||||
void setPlayerModel( const char* shape, const char* skin );
|
||||
void setPlayerSeq( S32 index );
|
||||
void getWeaponTransform( MatrixF *mat );
|
||||
|
||||
bool processCameraQuery( CameraQuery *query );
|
||||
void renderWorld( const RectI &updateRect );
|
||||
};
|
||||
|
||||
#endif // _GUI_PLAYERVIEW_H
|
||||
1119
engine/game/item.cc
Executable file
1119
engine/game/item.cc
Executable file
File diff suppressed because it is too large
Load Diff
157
engine/game/item.h
Executable file
157
engine/game/item.h
Executable file
@@ -0,0 +1,157 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ITEM_H_
|
||||
#define _ITEM_H_
|
||||
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "game/shapeBase.h"
|
||||
#endif
|
||||
#ifndef _LIGHTMANAGER_H_
|
||||
#include "sceneGraph/lightManager.h"
|
||||
#endif
|
||||
#ifndef _BOXCONVEX_H_
|
||||
#include "collision/boxConvex.h"
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct ItemData: public ShapeBaseData {
|
||||
typedef ShapeBaseData Parent;
|
||||
|
||||
F32 friction;
|
||||
F32 elasticity;
|
||||
|
||||
bool sticky;
|
||||
F32 gravityMod;
|
||||
F32 maxVelocity;
|
||||
|
||||
S32 dynamicTypeField;
|
||||
|
||||
StringTableEntry pickUpName;
|
||||
|
||||
bool lightOnlyStatic;
|
||||
S32 lightType;
|
||||
ColorF lightColor;
|
||||
S32 lightTime;
|
||||
F32 lightRadius;
|
||||
|
||||
ItemData();
|
||||
DECLARE_CONOBJECT(ItemData);
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class Item: public ShapeBase
|
||||
{
|
||||
typedef ShapeBase Parent;
|
||||
|
||||
/// Client interpolation data
|
||||
struct StateDelta
|
||||
{
|
||||
Point3F pos;
|
||||
VectorF posVec;
|
||||
S32 warpTicks;
|
||||
Point3F warpOffset;
|
||||
F32 dt;
|
||||
};
|
||||
StateDelta delta;
|
||||
|
||||
// Static attributes
|
||||
ItemData* mDataBlock;
|
||||
static F32 mGravity;
|
||||
bool mCollidable;
|
||||
bool mStatic;
|
||||
bool mRotate;
|
||||
|
||||
//
|
||||
VectorF mVelocity;
|
||||
bool mAtRest;
|
||||
|
||||
S32 mAtRestCounter;
|
||||
static const S32 csmAtRestTimer;
|
||||
|
||||
bool mInLiquid;
|
||||
|
||||
ShapeBase* mCollisionObject;
|
||||
U32 mCollisionTimeout;
|
||||
|
||||
public:
|
||||
|
||||
void registerLights(LightManager *lightManager, bool lightingScene);
|
||||
enum LightType
|
||||
{
|
||||
NoLight = 0,
|
||||
ConstantLight,
|
||||
PulsingLight,
|
||||
|
||||
NumLightTypes,
|
||||
};
|
||||
|
||||
private:
|
||||
S32 mDropTime;
|
||||
LightInfo mLight;
|
||||
|
||||
public:
|
||||
|
||||
Point3F mStickyCollisionPos;
|
||||
Point3F mStickyCollisionNormal;
|
||||
|
||||
//
|
||||
private:
|
||||
OrthoBoxConvex mConvex;
|
||||
Box3F mWorkingQueryBox;
|
||||
|
||||
void updateVelocity(const F32 dt);
|
||||
void updatePos(const U32 mask, const F32 dt);
|
||||
void updateWorkingCollisionSet(const U32 mask, const F32 dt);
|
||||
bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
|
||||
void buildConvex(const Box3F& box, Convex* convex);
|
||||
void onDeleteNotify(SimObject*);
|
||||
|
||||
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(Item);
|
||||
|
||||
enum MaskBits {
|
||||
HiddenMask = Parent::NextFreeMask,
|
||||
ThrowSrcMask = Parent::NextFreeMask << 1,
|
||||
PositionMask = Parent::NextFreeMask << 2,
|
||||
RotationMask = Parent::NextFreeMask << 3,
|
||||
NextFreeMask = Parent::NextFreeMask << 4
|
||||
};
|
||||
|
||||
Item();
|
||||
~Item();
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
|
||||
bool isStatic() { return mStatic; }
|
||||
bool isRotating() { return mRotate; }
|
||||
Point3F getVelocity() const;
|
||||
void setVelocity(const VectorF& vel);
|
||||
void applyImpulse(const Point3F& pos,const VectorF& vec);
|
||||
void setCollisionTimeout(ShapeBase* obj);
|
||||
ShapeBase* getCollisionObject() { return mCollisionObject; };
|
||||
|
||||
void processTick(const Move *move);
|
||||
void interpolateTick(F32 delta);
|
||||
void setTransform(const MatrixF &mat);
|
||||
void renderImage(SceneState *state, SceneRenderImage *image);
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
};
|
||||
|
||||
#endif
|
||||
718
engine/game/main.cc
Executable file
718
engine/game/main.cc
Executable file
@@ -0,0 +1,718 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformVideo.h"
|
||||
#include "platform/platformInput.h"
|
||||
#include "platform/platformAudio.h"
|
||||
#include "platform/event.h"
|
||||
#include "platform/gameInterface.h"
|
||||
#include "core/tVector.h"
|
||||
#include "core/chunkFile.h"
|
||||
#include "math/mMath.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "dgl/gBitmap.h"
|
||||
#include "core/resManager.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "dgl/gTexManager.h"
|
||||
#include "dgl/gFont.h"
|
||||
#include "console/console.h"
|
||||
#include "console/simBase.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "sim/actionMap.h"
|
||||
#include "core/dnet.h"
|
||||
#include "game/game.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "console/telnetConsole.h"
|
||||
#include "console/telnetDebugger.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "math/mathTypes.h"
|
||||
#include "dgl/gTexManager.h"
|
||||
#include "core/resManager.h"
|
||||
#include "interior/interiorRes.h"
|
||||
#include "interior/interiorInstance.h"
|
||||
#include "interior/interiorMapRes.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "terrain/terrRender.h"
|
||||
#include "editor/terraformer.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "dgl/materialList.h"
|
||||
#include "sceneGraph/sceneRoot.h"
|
||||
#include "game/moveManager.h"
|
||||
#include "platform/platformVideo.h"
|
||||
#include "dgl/materialPropertyMap.h"
|
||||
#include "sim/netStringTable.h"
|
||||
#include "sim/pathManager.h"
|
||||
#include "game/gameFunctions.h"
|
||||
#include "game/fx/particleEngine.h"
|
||||
#include "platform/platformRedBook.h"
|
||||
#include "game/demoGame.h"
|
||||
#include "sim/decalManager.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "sceneGraph/detailManager.h"
|
||||
#include "interior/interiorLMManager.h"
|
||||
#include "game/version.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "game/shapeBase.h"
|
||||
#include "game/objectTypes.h"
|
||||
#include "game/net/serverQuery.h"
|
||||
#include "game/badWordFilter.h"
|
||||
|
||||
#ifndef BUILD_TOOLS
|
||||
DemoGame GameObject;
|
||||
DemoNetInterface GameNetInterface;
|
||||
#endif
|
||||
|
||||
extern ResourceInstance *constructTerrainFile(Stream &stream);
|
||||
extern ResourceInstance *constructTSShape(Stream &);
|
||||
|
||||
|
||||
ConsoleFunctionGroupBegin( Platform , "General platform functions.");
|
||||
|
||||
ConsoleFunction( lockMouse, void, 2, 2, "(bool isLocked)"
|
||||
"Lock the mouse (or not, depending on the argument's value) to the window.")
|
||||
{
|
||||
Platform::setWindowLocked(dAtob(argv[1]));
|
||||
}
|
||||
|
||||
ConsoleFunction( setNetPort, bool, 2, 2, "(int port)"
|
||||
"Set the network port for the game to use.")
|
||||
{
|
||||
return Net::openPort(dAtoi(argv[1]));
|
||||
}
|
||||
|
||||
ConsoleFunction( saveJournal, void, 2, 2, "(string filename)"
|
||||
"Save the journal to the specified file.")
|
||||
{
|
||||
Game->saveJournal(argv[1]);
|
||||
}
|
||||
|
||||
ConsoleFunction( playJournal, void, 2, 3, "(string filename, bool break=false)"
|
||||
"Begin playback of a journal from a specified field, optionally breaking at the start.")
|
||||
{
|
||||
bool jBreak = (argc > 2)? dAtob(argv[2]): false;
|
||||
Game->playJournal(argv[1],jBreak);
|
||||
}
|
||||
|
||||
extern void netInit();
|
||||
extern void processConnectedReceiveEvent( ConnectedReceiveEvent * event );
|
||||
extern void processConnectedNotifyEvent( ConnectedNotifyEvent * event );
|
||||
extern void processConnectedAcceptEvent( ConnectedAcceptEvent * event );
|
||||
extern void ShowInit();
|
||||
|
||||
/// Initalizes the components of the game like the TextureManager, ResourceManager
|
||||
/// console...etc.
|
||||
static bool initLibraries()
|
||||
{
|
||||
if(!Net::init())
|
||||
{
|
||||
Platform::AlertOK("Network Error", "Unable to initialize the network... aborting.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// asserts should be created FIRST
|
||||
PlatformAssert::create();
|
||||
|
||||
FrameAllocator::init(3 << 20); // 3 meg frame allocator buffer
|
||||
|
||||
// // Cryptographic pool next
|
||||
// CryptRandomPool::init();
|
||||
|
||||
_StringTable::create();
|
||||
TextureManager::create();
|
||||
ResManager::create();
|
||||
|
||||
// Register known file types here
|
||||
ResourceManager->registerExtension(".jpg", constructBitmapJPEG);
|
||||
ResourceManager->registerExtension(".png", constructBitmapPNG);
|
||||
ResourceManager->registerExtension(".gif", constructBitmapGIF);
|
||||
ResourceManager->registerExtension(".dbm", constructBitmapDBM);
|
||||
ResourceManager->registerExtension(".bmp", constructBitmapBMP);
|
||||
ResourceManager->registerExtension(".bm8", constructBitmapBM8);
|
||||
ResourceManager->registerExtension(".uft", constructFont);
|
||||
ResourceManager->registerExtension(".dif", constructInteriorDIF);
|
||||
ResourceManager->registerExtension(".ter", constructTerrainFile);
|
||||
ResourceManager->registerExtension(".dts", constructTSShape);
|
||||
ResourceManager->registerExtension(".dml", constructMaterialList);
|
||||
ResourceManager->registerExtension(".map", constructInteriorMAP);
|
||||
|
||||
Con::init();
|
||||
NetStringTable::create();
|
||||
|
||||
TelnetConsole::create();
|
||||
TelnetDebugger::create();
|
||||
|
||||
Processor::init();
|
||||
Math::init();
|
||||
Platform::init(); // platform specific initialization
|
||||
InteriorLMManager::init();
|
||||
InteriorInstance::init();
|
||||
TSShapeInstance::init();
|
||||
RedBook::init();
|
||||
Platform::initConsole();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Destroys all the things initalized in initLibraries
|
||||
static void shutdownLibraries()
|
||||
{
|
||||
// Purge any resources on the timeout list...
|
||||
if (ResourceManager)
|
||||
ResourceManager->purge();
|
||||
|
||||
RedBook::destroy();
|
||||
TSShapeInstance::destroy();
|
||||
InteriorInstance::destroy();
|
||||
InteriorLMManager::destroy();
|
||||
|
||||
TextureManager::preDestroy();
|
||||
|
||||
Platform::shutdown();
|
||||
TelnetDebugger::destroy();
|
||||
TelnetConsole::destroy();
|
||||
|
||||
NetStringTable::destroy();
|
||||
Con::shutdown();
|
||||
|
||||
ResManager::destroy();
|
||||
TextureManager::destroy();
|
||||
|
||||
_StringTable::destroy();
|
||||
|
||||
// asserts should be destroyed LAST
|
||||
FrameAllocator::destroy();
|
||||
|
||||
PlatformAssert::destroy();
|
||||
Net::shutdown();
|
||||
}
|
||||
|
||||
ConsoleFunction( getSimTime, S32, 1, 1, "Return the current sim time in milliseconds.\n\n"
|
||||
"Sim time is time since the game started.")
|
||||
{
|
||||
return Sim::getCurrentTime();
|
||||
}
|
||||
|
||||
ConsoleFunction( getRealTime, S32, 1, 1, "Return the current real time in milliseconds.\n\n"
|
||||
"Real time is platform defined; typically time since the computer booted.")
|
||||
{
|
||||
return Platform::getRealMilliseconds();
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd(Platform);
|
||||
|
||||
static F32 gTimeScale = 1.0;
|
||||
static U32 gTimeAdvance = 0;
|
||||
static U32 gFrameSkip = 0;
|
||||
static U32 gFrameCount = 0;
|
||||
|
||||
// Executes an entry script; can be controlled by command-line options.
|
||||
bool runEntryScript (int argc, const char **argv)
|
||||
{
|
||||
// Executes an entry script file. This is "main.cs"
|
||||
// by default, but any file name (with no whitespace
|
||||
// in it) may be run if it is specified as the first
|
||||
// command-line parameter. The script used, default
|
||||
// or otherwise, is not compiled and is loaded here
|
||||
// directly because the resource system restricts
|
||||
// access to the "root" directory.
|
||||
|
||||
FileStream str; // The working filestream.
|
||||
const char* defaultScriptName = "main.cs";
|
||||
bool useDefaultScript = true;
|
||||
|
||||
// Check if any command-line parameters were passed (the first is just the app name).
|
||||
if (argc > 1)
|
||||
{
|
||||
// If so, check if the first parameter is a file to open.
|
||||
if ( (str.open(argv[1], FileStream::Read)) && (argv[1] != "") )
|
||||
{
|
||||
// If it opens, we assume it is the script to run.
|
||||
useDefaultScript = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (useDefaultScript)
|
||||
{
|
||||
if (!str.open(defaultScriptName, FileStream::Read))
|
||||
{
|
||||
char msg[1024];
|
||||
dSprintf(msg, sizeof(msg), "Failed to open \"%s\".", defaultScriptName);
|
||||
Platform::AlertOK("Error", msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
U32 size = str.getStreamSize();
|
||||
char *script = new char[size + 1];
|
||||
str.read(size, script);
|
||||
str.close();
|
||||
script[size] = 0;
|
||||
|
||||
Con::evaluate(script, false, useDefaultScript ? defaultScriptName : argv[1]);
|
||||
delete[] script;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Initalize game, run the specified startup script
|
||||
bool initGame(int argc, const char **argv)
|
||||
{
|
||||
Con::setFloatVariable("Video::texResidentPercentage", -1.0f);
|
||||
Con::setIntVariable("Video::textureCacheMisses", -1);
|
||||
Con::addVariable("timeScale", TypeF32, &gTimeScale);
|
||||
Con::addVariable("timeAdvance", TypeS32, &gTimeAdvance);
|
||||
Con::addVariable("frameSkip", TypeS32, &gFrameSkip);
|
||||
|
||||
// Stuff game types into the console
|
||||
Con::setIntVariable("$TypeMasks::StaticObjectType", StaticObjectType);
|
||||
Con::setIntVariable("$TypeMasks::EnvironmentObjectType", EnvironmentObjectType);
|
||||
Con::setIntVariable("$TypeMasks::TerrainObjectType", TerrainObjectType);
|
||||
Con::setIntVariable("$TypeMasks::InteriorObjectType", InteriorObjectType);
|
||||
Con::setIntVariable("$TypeMasks::WaterObjectType", WaterObjectType);
|
||||
Con::setIntVariable("$TypeMasks::TriggerObjectType", TriggerObjectType);
|
||||
Con::setIntVariable("$TypeMasks::MarkerObjectType", MarkerObjectType);
|
||||
Con::setIntVariable("$TypeMasks::GameBaseObjectType", GameBaseObjectType);
|
||||
Con::setIntVariable("$TypeMasks::ShapeBaseObjectType", ShapeBaseObjectType);
|
||||
Con::setIntVariable("$TypeMasks::CameraObjectType", CameraObjectType);
|
||||
Con::setIntVariable("$TypeMasks::StaticShapeObjectType", StaticShapeObjectType);
|
||||
Con::setIntVariable("$TypeMasks::PlayerObjectType", PlayerObjectType);
|
||||
Con::setIntVariable("$TypeMasks::ItemObjectType", ItemObjectType);
|
||||
Con::setIntVariable("$TypeMasks::VehicleObjectType", VehicleObjectType);
|
||||
Con::setIntVariable("$TypeMasks::VehicleBlockerObjectType", VehicleBlockerObjectType);
|
||||
Con::setIntVariable("$TypeMasks::ProjectileObjectType", ProjectileObjectType);
|
||||
Con::setIntVariable("$TypeMasks::ExplosionObjectType", ExplosionObjectType);
|
||||
Con::setIntVariable("$TypeMasks::CorpseObjectType", CorpseObjectType);
|
||||
Con::setIntVariable("$TypeMasks::DebrisObjectType", DebrisObjectType);
|
||||
Con::setIntVariable("$TypeMasks::PhysicalZoneObjectType", PhysicalZoneObjectType);
|
||||
Con::setIntVariable("$TypeMasks::StaticTSObjectType", StaticTSObjectType);
|
||||
Con::setIntVariable("$TypeMasks::StaticRenderedObjectType", StaticRenderedObjectType);
|
||||
Con::setIntVariable("$TypeMasks::DamagableItemObjectType", DamagableItemObjectType);
|
||||
Con::setIntVariable("$TypeMasks::InteriorMapObjectType", InteriorMapObjectType);
|
||||
|
||||
//
|
||||
#ifdef TORQUE_GATHER_METRICS
|
||||
Con::addVariable("Video::numTexelsLoaded", TypeS32, &TextureManager::smTextureSpaceLoaded);
|
||||
#else
|
||||
static U32 sBogusNTL = 0;
|
||||
Con::addVariable("Video::numTexelsLoaded", TypeS32, &sBogusNTL);
|
||||
#endif
|
||||
|
||||
TerrainRender::init();
|
||||
netInit();
|
||||
GameInit();
|
||||
ShowInit();
|
||||
MoveManager::init();
|
||||
|
||||
Sim::init();
|
||||
|
||||
ActionMap* globalMap = new ActionMap;
|
||||
globalMap->registerObject("GlobalActionMap");
|
||||
Sim::getActiveActionMapSet()->pushObject(globalMap);
|
||||
|
||||
MaterialPropertyMap *map = new MaterialPropertyMap;
|
||||
|
||||
map->registerObject("MaterialPropertyMap");
|
||||
Sim::getRootGroup()->addObject(map);
|
||||
|
||||
gClientSceneGraph = new SceneGraph(true);
|
||||
gClientSceneRoot = new SceneRoot;
|
||||
gClientSceneGraph->addObjectToScene(gClientSceneRoot);
|
||||
gServerSceneGraph = new SceneGraph(false);
|
||||
gServerSceneRoot = new SceneRoot;
|
||||
gServerSceneGraph->addObjectToScene(gServerSceneRoot);
|
||||
gDecalManager = new DecalManager;
|
||||
gClientContainer.addObject(gDecalManager);
|
||||
gClientSceneGraph->addObjectToScene(gDecalManager);
|
||||
|
||||
DetailManager::init();
|
||||
PathManager::init();
|
||||
ParticleEngine::init();
|
||||
BadWordFilter::create();
|
||||
|
||||
SimChunk::initChunkMappings();
|
||||
|
||||
// run the entry script and return.
|
||||
return runEntryScript(argc, argv);
|
||||
}
|
||||
|
||||
/// Shutdown the game and delete core objects
|
||||
void shutdownGame()
|
||||
{
|
||||
//exec the script onExit() function
|
||||
Con::executef(1, "onExit");
|
||||
|
||||
BadWordFilter::destroy();
|
||||
ParticleEngine::destroy();
|
||||
PathManager::destroy();
|
||||
DetailManager::shutdown();
|
||||
|
||||
// Note: tho the SceneGraphs are created after the Manager, delete them after, rather
|
||||
// than before to make sure that all the objects are removed from the graph.
|
||||
Sim::shutdown();
|
||||
|
||||
gClientSceneGraph->removeObjectFromScene(gDecalManager);
|
||||
gClientContainer.removeObject(gDecalManager);
|
||||
gClientSceneGraph->removeObjectFromScene(gClientSceneRoot);
|
||||
gServerSceneGraph->removeObjectFromScene(gServerSceneRoot);
|
||||
delete gClientSceneRoot;
|
||||
delete gServerSceneRoot;
|
||||
delete gClientSceneGraph;
|
||||
delete gServerSceneGraph;
|
||||
delete gDecalManager;
|
||||
gClientSceneRoot = NULL;
|
||||
gServerSceneRoot = NULL;
|
||||
gClientSceneGraph = NULL;
|
||||
gServerSceneGraph = NULL;
|
||||
gDecalManager = NULL;
|
||||
|
||||
TerrainRender::shutdown();
|
||||
}
|
||||
|
||||
extern bool gDGLRender;
|
||||
bool gShuttingDown = false;
|
||||
|
||||
/// Main loop of the game
|
||||
int DemoGame::main(int argc, const char **argv)
|
||||
{
|
||||
// if (argc == 1) {
|
||||
// static const char* argvFake[] = { "dtest.exe", "-jload", "test.jrn" };
|
||||
// argc = 3;
|
||||
// argv = argvFake;
|
||||
// }
|
||||
|
||||
// Memory::enableLogging("testMem.log");
|
||||
// Memory::setBreakAlloc(104717);
|
||||
|
||||
if(!initLibraries())
|
||||
return 0;
|
||||
|
||||
#ifdef IHVBUILD
|
||||
char* pVer = new char[sgVerStringLen + 1];
|
||||
U32 hi;
|
||||
for (hi = 0; hi < sgVerStringLen; hi++)
|
||||
pVer[hi] = sgVerString[hi] ^ 0xFF;
|
||||
pVer[hi] = '\0';
|
||||
|
||||
SHA1Context hashCTX;
|
||||
hashCTX.init();
|
||||
hashCTX.hashBytes(pVer, sgVerStringLen);
|
||||
hashCTX.finalize();
|
||||
|
||||
U8 hash[20];
|
||||
hashCTX.getHash(hash);
|
||||
|
||||
for (hi = 0; hi < 20; hi++)
|
||||
if (U8(hash[hi]) != U8(sgHashVer[hi]))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
// Set up the command line args for the console scripts...
|
||||
Con::setIntVariable("Game::argc", argc);
|
||||
U32 i;
|
||||
for (i = 0; i < argc; i++)
|
||||
Con::setVariable(avar("Game::argv%d", i), argv[i]);
|
||||
if (initGame(argc, argv) == false)
|
||||
{
|
||||
Platform::AlertOK("Error", "Failed to initialize game, shutting down.");
|
||||
shutdownGame();
|
||||
shutdownLibraries();
|
||||
gShuttingDown = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef IHVBUILD
|
||||
char* pPrint = new char[dStrlen(sgVerPrintString) + 1];
|
||||
for (U32 pi = 0; pi < dStrlen(sgVerPrintString); pi++)
|
||||
pPrint[pi] = sgVerPrintString[pi] ^ 0xff;
|
||||
pPrint[dStrlen(sgVerPrintString)] = '\0';
|
||||
|
||||
Con::printf("");
|
||||
Con::errorf(ConsoleLogEntry::General, pPrint, pVer);
|
||||
delete [] pVer;
|
||||
#endif
|
||||
|
||||
while(Game->isRunning())
|
||||
{
|
||||
PROFILE_START(MainLoop);
|
||||
PROFILE_START(JournalMain);
|
||||
Game->journalProcess();
|
||||
PROFILE_END();
|
||||
PROFILE_START(NetProcessMain);
|
||||
Net::process(); // read in all events
|
||||
PROFILE_END();
|
||||
PROFILE_START(PlatformProcessMain);
|
||||
Platform::process(); // keys, etc.
|
||||
PROFILE_END();
|
||||
PROFILE_START(TelconsoleProcessMain);
|
||||
TelConsole->process();
|
||||
PROFILE_END();
|
||||
PROFILE_START(TelDebuggerProcessMain);
|
||||
TelDebugger->process();
|
||||
PROFILE_END();
|
||||
PROFILE_START(TimeManagerProcessMain);
|
||||
TimeManager::process(); // guaranteed to produce an event
|
||||
PROFILE_END();
|
||||
PROFILE_END();
|
||||
}
|
||||
shutdownGame();
|
||||
shutdownLibraries();
|
||||
|
||||
gShuttingDown = true;
|
||||
|
||||
#if 0
|
||||
// tg: Argh! This OS version check should be part of Platform, not here...
|
||||
//
|
||||
// check os
|
||||
OSVERSIONINFO osInfo;
|
||||
dMemset(&osInfo, 0, sizeof(OSVERSIONINFO));
|
||||
osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
|
||||
// see if osversioninfoex fails
|
||||
if(!GetVersionEx((OSVERSIONINFO*)&osInfo))
|
||||
{
|
||||
osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
if(!GetVersionEx((OSVERSIONINFO*)&osInfo))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// terminate the process if win95 only!
|
||||
if((osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && // 95, 98, ME
|
||||
(osInfo.dwMajorVersion == 4) && // 95, 98, ME, NT
|
||||
(osInfo.dwMinorVersion == 0)) // 95
|
||||
{
|
||||
AssertWarn(0, "Forcing termination of app (Win95)! Upgrade your OS now!");
|
||||
TerminateProcess(GetCurrentProcess(), 0xffffffff);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool serverTick = false;
|
||||
|
||||
|
||||
static F32 fpsRealStart;
|
||||
static F32 fpsRealLast;
|
||||
//static F32 fpsRealTotal;
|
||||
static F32 fpsReal;
|
||||
static F32 fpsVirtualStart;
|
||||
static F32 fpsVirtualLast;
|
||||
//static F32 fpsVirtualTotal;
|
||||
static F32 fpsVirtual;
|
||||
static F32 fpsFrames;
|
||||
static F32 fpsNext;
|
||||
static bool fpsInit = false;
|
||||
const F32 UPDATE_INTERVAL = 0.25f;
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
/// Resets the FPS variables
|
||||
void fpsReset()
|
||||
{
|
||||
fpsRealStart = (F32)Platform::getRealMilliseconds()/1000.0f; // Real-World Tick Count
|
||||
fpsVirtualStart = (F32)Platform::getVirtualMilliseconds()/1000.0f; // Engine Tick Count (does not vary between frames)
|
||||
fpsNext = fpsRealStart + UPDATE_INTERVAL;
|
||||
|
||||
// fpsRealTotal= 0.0f;
|
||||
fpsRealLast = 0.0f;
|
||||
fpsReal = 0.0f;
|
||||
// fpsVirtualTotal = 0.0f;
|
||||
fpsVirtualLast = 0.0f;
|
||||
fpsVirtual = 0.0f;
|
||||
fpsFrames = 0;
|
||||
fpsInit = true;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
/// Updates the FPS variables
|
||||
void fpsUpdate()
|
||||
{
|
||||
if (!fpsInit)
|
||||
fpsReset();
|
||||
|
||||
const float alpha = 0.07f;
|
||||
F32 realSeconds = (F32)Platform::getRealMilliseconds()/1000.0f;
|
||||
F32 virtualSeconds = (F32)Platform::getVirtualMilliseconds()/1000.0f;
|
||||
|
||||
fpsFrames++;
|
||||
if (fpsFrames > 1)
|
||||
{
|
||||
fpsReal = fpsReal*(1.0-alpha) + (realSeconds-fpsRealLast)*alpha;
|
||||
fpsVirtual = fpsVirtual*(1.0-alpha) + (virtualSeconds-fpsVirtualLast)*alpha;
|
||||
}
|
||||
// fpsRealTotal = fpsFrames/(realSeconds - fpsRealStart);
|
||||
// fpsVirtualTotal = fpsFrames/(virtualSeconds - fpsVirtualStart);
|
||||
|
||||
fpsRealLast = realSeconds;
|
||||
fpsVirtualLast = virtualSeconds;
|
||||
|
||||
// update variables every few frames
|
||||
F32 update = fpsRealLast - fpsNext;
|
||||
if (update > 0.5f)
|
||||
{
|
||||
// Con::setVariable("fps::realTotal", avar("%4.1f", fpsRealTotal));
|
||||
// Con::setVariable("fps::virtualTotal", avar("%4.1f", fpsVirtualTotal));
|
||||
Con::setVariable("fps::real", avar("%4.1f", 1.0f/fpsReal));
|
||||
Con::setVariable("fps::virtual", avar("%4.1f", 1.0f/fpsVirtual));
|
||||
if (update > UPDATE_INTERVAL)
|
||||
fpsNext = fpsRealLast + UPDATE_INTERVAL;
|
||||
else
|
||||
fpsNext += UPDATE_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Process a mouse movement event, essentially pass to the canvas for handling
|
||||
void DemoGame::processMouseMoveEvent(MouseMoveEvent * mEvent)
|
||||
{
|
||||
if (Canvas)
|
||||
Canvas->processMouseMoveEvent(mEvent);
|
||||
}
|
||||
|
||||
/// Process an input event, pass to canvas for handling
|
||||
void DemoGame::processInputEvent(InputEvent *event)
|
||||
{
|
||||
PROFILE_START(ProcessInputEvent);
|
||||
if (!ActionMap::handleEventGlobal(event))
|
||||
{
|
||||
// Other input consumers here...
|
||||
if (!(Canvas && Canvas->processInputEvent(event)))
|
||||
ActionMap::handleEvent(event);
|
||||
}
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
/// Process a quit event
|
||||
void DemoGame::processQuitEvent()
|
||||
{
|
||||
setRunning(false);
|
||||
}
|
||||
|
||||
/// Refresh the game window, ask the canvas to set all regions to dirty (need to be updated)
|
||||
void DemoGame::refreshWindow()
|
||||
{
|
||||
if(Canvas)
|
||||
Canvas->resetUpdateRegions();
|
||||
}
|
||||
|
||||
/// Process a console event
|
||||
void DemoGame::processConsoleEvent(ConsoleEvent *event)
|
||||
{
|
||||
char *argv[2];
|
||||
argv[0] = "eval";
|
||||
argv[1] = event->data;
|
||||
Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, const_cast<const char**>(argv), false));
|
||||
}
|
||||
|
||||
/// Process a time event and update all sub-processes
|
||||
void DemoGame::processTimeEvent(TimeEvent *event)
|
||||
{
|
||||
PROFILE_START(ProcessTimeEvent);
|
||||
U32 elapsedTime = event->elapsedTime;
|
||||
// cap the elapsed time to one second
|
||||
// if it's more than that we're probably in a bad catch-up situation
|
||||
|
||||
if(elapsedTime > 1024)
|
||||
elapsedTime = 1024;
|
||||
|
||||
U32 timeDelta;
|
||||
|
||||
if(gTimeAdvance)
|
||||
timeDelta = gTimeAdvance;
|
||||
else
|
||||
timeDelta = (U32) (elapsedTime * gTimeScale);
|
||||
|
||||
Platform::advanceTime(elapsedTime);
|
||||
bool tickPass;
|
||||
PROFILE_START(ServerProcess);
|
||||
tickPass = serverProcess(timeDelta);
|
||||
PROFILE_END();
|
||||
PROFILE_START(ServerNetProcess);
|
||||
// only send packets if a tick happened
|
||||
if(tickPass)
|
||||
GNet->processServer();
|
||||
PROFILE_END();
|
||||
|
||||
PROFILE_START(SimAdvanceTime);
|
||||
Sim::advanceTime(timeDelta);
|
||||
PROFILE_END();
|
||||
|
||||
PROFILE_START(ClientProcess);
|
||||
tickPass = clientProcess(timeDelta);
|
||||
PROFILE_END();
|
||||
PROFILE_START(ClientNetProcess);
|
||||
if(tickPass)
|
||||
GNet->processClient();
|
||||
PROFILE_END();
|
||||
|
||||
if(Canvas && gDGLRender)
|
||||
{
|
||||
bool preRenderOnly = false;
|
||||
if(gFrameSkip && gFrameCount % gFrameSkip)
|
||||
preRenderOnly = true;
|
||||
|
||||
PROFILE_START(RenderFrame);
|
||||
ShapeBase::incRenderFrame();
|
||||
Canvas->renderFrame(preRenderOnly);
|
||||
PROFILE_END();
|
||||
gFrameCount++;
|
||||
}
|
||||
GNet->checkTimeouts();
|
||||
fpsUpdate();
|
||||
PROFILE_END();
|
||||
|
||||
// Update the console time
|
||||
Con::setFloatVariable("Sim::Time",F32(Platform::getVirtualMilliseconds()) / 1000);
|
||||
}
|
||||
|
||||
/// Re-activate the game from, say, a minimized state
|
||||
void GameReactivate()
|
||||
{
|
||||
if ( !Input::isEnabled() )
|
||||
Input::enable();
|
||||
|
||||
if ( !Input::isActive() )
|
||||
Input::reactivate();
|
||||
|
||||
gDGLRender = true;
|
||||
if ( Canvas )
|
||||
Canvas->resetUpdateRegions();
|
||||
}
|
||||
|
||||
/// De-activate the game in responce to, say, a minimize event
|
||||
void GameDeactivate( bool noRender )
|
||||
{
|
||||
if ( Input::isActive() )
|
||||
Input::deactivate();
|
||||
|
||||
if ( Input::isEnabled() )
|
||||
Input::disable();
|
||||
|
||||
if ( noRender )
|
||||
gDGLRender = false;
|
||||
}
|
||||
|
||||
/// Invalidate all the textures
|
||||
void DemoGame::textureKill()
|
||||
{
|
||||
TextureManager::makeZombie();
|
||||
}
|
||||
|
||||
/// Reaquire all textures
|
||||
void DemoGame::textureResurrect()
|
||||
{
|
||||
TextureManager::resurrect();
|
||||
}
|
||||
|
||||
/// Process recieved net-packets
|
||||
void DemoGame::processPacketReceiveEvent(PacketReceiveEvent * prEvent)
|
||||
{
|
||||
GNet->processPacketReceiveEvent(prEvent);
|
||||
}
|
||||
135
engine/game/missionArea.cc
Executable file
135
engine/game/missionArea.cc
Executable file
@@ -0,0 +1,135 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/missionArea.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "math/mathIO.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(MissionArea);
|
||||
|
||||
RectI MissionArea::smMissionArea(Point2I(768, 768), Point2I(512, 512));
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
MissionArea::MissionArea()
|
||||
{
|
||||
mArea.set(Point2I(768, 768), Point2I(512, 512));
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
mFlightCeiling = 2000;
|
||||
mFlightCeilingRange = 50;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MissionArea::setArea(const RectI & area)
|
||||
{
|
||||
// set it
|
||||
mArea = MissionArea::smMissionArea = area;
|
||||
|
||||
// pass along..
|
||||
if(isServerObject())
|
||||
mNetFlags.set(UpdateMask);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const MissionArea * MissionArea::getServerObject()
|
||||
{
|
||||
SimSet * scopeAlwaysSet = Sim::getGhostAlwaysSet();
|
||||
for(SimSet::iterator itr = scopeAlwaysSet->begin(); itr != scopeAlwaysSet->end(); itr++)
|
||||
{
|
||||
MissionArea * ma = dynamic_cast<MissionArea*>(*itr);
|
||||
if(ma)
|
||||
{
|
||||
AssertFatal(ma->isServerObject(), "MissionArea::getServerObject: found client object in ghost always set!");
|
||||
return(ma);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool MissionArea::onAdd()
|
||||
{
|
||||
if(isServerObject() && MissionArea::getServerObject())
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "MissionArea::onAdd - MissionArea already instantiated!");
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(!Parent::onAdd())
|
||||
return(false);
|
||||
|
||||
setArea(mArea);
|
||||
return(true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MissionArea::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Dimensions");
|
||||
addField("area", TypeRectI, Offset(mArea, MissionArea));
|
||||
addField("flightCeiling", TypeF32, Offset(mFlightCeiling, MissionArea));
|
||||
addField("flightCeilingRange", TypeF32, Offset(mFlightCeilingRange, MissionArea));
|
||||
endGroup("Dimensions");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ConsoleMethod( MissionArea, getArea, const char *, 2, 2, "()"
|
||||
"Returns 4 fields: starting x, starting y, extents x, extents y")
|
||||
{
|
||||
static char buf[48];
|
||||
|
||||
RectI area = object->getArea();
|
||||
dSprintf(buf, sizeof(buf), "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y);
|
||||
return(buf);
|
||||
}
|
||||
|
||||
ConsoleMethod( MissionArea, setArea, void, 6, 6, "(int x, int y, int width, int height)")
|
||||
{
|
||||
if(object->isClientObject())
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "MissionArea::cSetArea - cannot alter client object!");
|
||||
return;
|
||||
}
|
||||
|
||||
RectI rect;
|
||||
rect.point.x = dAtoi(argv[2]);
|
||||
rect.point.y = dAtoi(argv[3]);
|
||||
rect.extent.x = dAtoi(argv[4]);
|
||||
rect.extent.y = dAtoi(argv[5]);
|
||||
|
||||
object->setArea(rect);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void MissionArea::unpackUpdate(NetConnection *, BitStream * stream)
|
||||
{
|
||||
// ghost (initial) and regular updates share flag..
|
||||
if(stream->readFlag())
|
||||
{
|
||||
mathRead(*stream, &mArea);
|
||||
stream->read(&mFlightCeiling);
|
||||
stream->read(&mFlightCeilingRange);
|
||||
}
|
||||
}
|
||||
|
||||
U32 MissionArea::packUpdate(NetConnection *, U32 mask, BitStream * stream)
|
||||
{
|
||||
if(stream->writeFlag(mask & UpdateMask))
|
||||
{
|
||||
mathWrite(*stream, mArea);
|
||||
stream->write(mFlightCeiling);
|
||||
stream->write(mFlightCeilingRange);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
56
engine/game/missionArea.h
Executable file
56
engine/game/missionArea.h
Executable file
@@ -0,0 +1,56 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MISSIONAREA_H_
|
||||
#define _MISSIONAREA_H_
|
||||
|
||||
#ifndef _NETOBJECT_H_
|
||||
#include "sim/netObject.h"
|
||||
#endif
|
||||
|
||||
class MissionArea : public NetObject
|
||||
{
|
||||
private:
|
||||
typedef NetObject Parent;
|
||||
RectI mArea;
|
||||
|
||||
F32 mFlightCeiling;
|
||||
F32 mFlightCeilingRange;
|
||||
|
||||
public:
|
||||
MissionArea();
|
||||
|
||||
static RectI smMissionArea;
|
||||
|
||||
static const MissionArea * getServerObject();
|
||||
|
||||
F32 getFlightCeiling() const { return mFlightCeiling; }
|
||||
F32 getFlightCeilingRange() const { return mFlightCeilingRange; }
|
||||
|
||||
//
|
||||
const RectI & getArea(){return(mArea);}
|
||||
void setArea(const RectI & area);
|
||||
|
||||
/// @name SimObject Inheritance
|
||||
/// @{
|
||||
bool onAdd();
|
||||
|
||||
static void initPersistFields();
|
||||
/// @}
|
||||
|
||||
/// @name NetObject Inheritance
|
||||
/// @{
|
||||
enum NetMaskBits {
|
||||
UpdateMask = BIT(0)
|
||||
};
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
/// @}
|
||||
|
||||
DECLARE_CONOBJECT(MissionArea);
|
||||
};
|
||||
|
||||
#endif
|
||||
295
engine/game/missionMarker.cc
Executable file
295
engine/game/missionMarker.cc
Executable file
@@ -0,0 +1,295 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/missionMarker.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "core/color.h"
|
||||
|
||||
extern bool gEditingMission;
|
||||
IMPLEMENT_CO_DATABLOCK_V1(MissionMarkerData);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: MissionMarker
|
||||
//------------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_NETOBJECT_V1(MissionMarker);
|
||||
|
||||
MissionMarker::MissionMarker()
|
||||
{
|
||||
mTypeMask |= StaticShapeObjectType | StaticObjectType;
|
||||
mDataBlock = 0;
|
||||
mAddedToScene = false;
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
}
|
||||
|
||||
bool MissionMarker::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd() || !mDataBlock)
|
||||
return(false);
|
||||
|
||||
if(gEditingMission)
|
||||
{
|
||||
addToScene();
|
||||
mAddedToScene = true;
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void MissionMarker::onRemove()
|
||||
{
|
||||
if(gEditingMission)
|
||||
{
|
||||
removeFromScene();
|
||||
mAddedToScene = false;
|
||||
}
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void MissionMarker::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
|
||||
void MissionMarker::onEditorEnable()
|
||||
{
|
||||
if(!mAddedToScene)
|
||||
{
|
||||
addToScene();
|
||||
mAddedToScene = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MissionMarker::onEditorDisable()
|
||||
{
|
||||
if(mAddedToScene)
|
||||
{
|
||||
removeFromScene();
|
||||
mAddedToScene = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MissionMarker::onNewDataBlock(GameBaseData * dptr)
|
||||
{
|
||||
mDataBlock = dynamic_cast<MissionMarkerData*>(dptr);
|
||||
if(!mDataBlock || !Parent::onNewDataBlock(dptr))
|
||||
return(false);
|
||||
scriptOnNewDataBlock();
|
||||
return(true);
|
||||
}
|
||||
|
||||
void MissionMarker::setTransform(const MatrixF& mat)
|
||||
{
|
||||
Parent::setTransform(mat);
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
|
||||
U32 MissionMarker::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
if(stream->writeFlag(mask & PositionMask))
|
||||
{
|
||||
stream->writeAffineTransform(mObjToWorld);
|
||||
mathWrite(*stream, mObjScale);
|
||||
}
|
||||
|
||||
return(retMask);
|
||||
}
|
||||
|
||||
void MissionMarker::unpackUpdate(NetConnection * con, BitStream * stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
if(stream->readFlag())
|
||||
{
|
||||
MatrixF mat;
|
||||
stream->readAffineTransform(&mat);
|
||||
Parent::setTransform(mat);
|
||||
|
||||
Point3F scale;
|
||||
mathRead(*stream, &scale);
|
||||
setScale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
void MissionMarker::initPersistFields() {
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: WayPoint
|
||||
//------------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_NETOBJECT_V1(WayPoint);
|
||||
|
||||
WayPointTeam::WayPointTeam()
|
||||
{
|
||||
mTeamId = 0;
|
||||
mWayPoint = 0;
|
||||
}
|
||||
|
||||
WayPoint::WayPoint()
|
||||
{
|
||||
mName = StringTable->insert("");
|
||||
}
|
||||
|
||||
void WayPoint::setHidden(bool hidden)
|
||||
{
|
||||
if(isServerObject())
|
||||
setMaskBits(UpdateHiddenMask);
|
||||
mHidden = hidden;
|
||||
}
|
||||
|
||||
bool WayPoint::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return(false);
|
||||
|
||||
//
|
||||
if(isClientObject())
|
||||
Sim::getWayPointSet()->addObject(this);
|
||||
else
|
||||
{
|
||||
mTeam.mWayPoint = this;
|
||||
setMaskBits(UpdateNameMask|UpdateTeamMask);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
void WayPoint::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
if(!mName || !mName[0])
|
||||
mName = StringTable->insert("");
|
||||
setMaskBits(UpdateNameMask|UpdateTeamMask);
|
||||
}
|
||||
|
||||
U32 WayPoint::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
if(stream->writeFlag(mask & UpdateNameMask))
|
||||
stream->writeString(mName);
|
||||
if(stream->writeFlag(mask & UpdateTeamMask))
|
||||
stream->write(mTeam.mTeamId);
|
||||
if(stream->writeFlag(mask & UpdateHiddenMask))
|
||||
stream->writeFlag(mHidden);
|
||||
return(retMask);
|
||||
}
|
||||
|
||||
void WayPoint::unpackUpdate(NetConnection * con, BitStream * stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
if(stream->readFlag())
|
||||
mName = stream->readSTString(true);
|
||||
if(stream->readFlag())
|
||||
stream->read(&mTeam.mTeamId);
|
||||
if(stream->readFlag())
|
||||
mHidden = stream->readFlag();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeWayPointTeam
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( WayPointTeam, TypeWayPointTeam, sizeof(WayPointTeam) )
|
||||
|
||||
ConsoleGetType( TypeWayPointTeam )
|
||||
{
|
||||
char * buf = Con::getReturnBuffer(32);
|
||||
dSprintf(buf, 32, "%d", ((WayPointTeam*)dptr)->mTeamId);
|
||||
return(buf);
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeWayPointTeam )
|
||||
{
|
||||
WayPointTeam * pTeam = (WayPointTeam*)dptr;
|
||||
pTeam->mTeamId = dAtoi(argv[0]);
|
||||
|
||||
if(pTeam->mWayPoint && pTeam->mWayPoint->isServerObject())
|
||||
pTeam->mWayPoint->setMaskBits(WayPoint::UpdateTeamMask);
|
||||
}
|
||||
|
||||
void WayPoint::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Misc");
|
||||
addField("name", TypeCaseString, Offset(mName, WayPoint));
|
||||
addField("team", TypeWayPointTeam, Offset(mTeam, WayPoint));
|
||||
endGroup("Misc");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: SpawnSphere
|
||||
//------------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SpawnSphere);
|
||||
|
||||
Sphere SpawnSphere::smSphere(Sphere::Octahedron);
|
||||
|
||||
SpawnSphere::SpawnSphere()
|
||||
{
|
||||
mRadius = 100.f;
|
||||
mSphereWeight = 100.f;
|
||||
mIndoorWeight = 100.f;
|
||||
mOutdoorWeight = 100.f;
|
||||
}
|
||||
|
||||
bool SpawnSphere::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return(false);
|
||||
|
||||
if(!isClientObject())
|
||||
setMaskBits(UpdateSphereMask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpawnSphere::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
setMaskBits(UpdateSphereMask);
|
||||
}
|
||||
|
||||
U32 SpawnSphere::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
//
|
||||
if(stream->writeFlag(mask & UpdateSphereMask))
|
||||
{
|
||||
stream->write(mRadius);
|
||||
stream->write(mSphereWeight);
|
||||
stream->write(mIndoorWeight);
|
||||
stream->write(mOutdoorWeight);
|
||||
}
|
||||
return(retMask);
|
||||
}
|
||||
|
||||
void SpawnSphere::unpackUpdate(NetConnection * con, BitStream * stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
if(stream->readFlag())
|
||||
{
|
||||
stream->read(&mRadius);
|
||||
stream->read(&mSphereWeight);
|
||||
stream->read(&mIndoorWeight);
|
||||
stream->read(&mOutdoorWeight);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnSphere::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Dimensions");
|
||||
addField("radius", TypeF32, Offset(mRadius, SpawnSphere));
|
||||
endGroup("Dimensions");
|
||||
|
||||
addGroup("Weight");
|
||||
addField("sphereWeight", TypeF32, Offset(mSphereWeight, SpawnSphere));
|
||||
addField("indoorWeight", TypeF32, Offset(mIndoorWeight, SpawnSphere));
|
||||
addField("outdoorWeight", TypeF32, Offset(mOutdoorWeight, SpawnSphere));
|
||||
endGroup("Weight");
|
||||
}
|
||||
163
engine/game/missionMarker.h
Executable file
163
engine/game/missionMarker.h
Executable file
@@ -0,0 +1,163 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MISSIONMARKER_H_
|
||||
#define _MISSIONMARKER_H_
|
||||
|
||||
#ifndef _BITSTREAM_H_
|
||||
#include "core/bitStream.h"
|
||||
#endif
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "game/shapeBase.h"
|
||||
#endif
|
||||
#ifndef _MATHIO_H_
|
||||
#include "math/mathIO.h"
|
||||
#endif
|
||||
#ifndef _SPHERE_H_
|
||||
#include "game/sphere.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
class MissionMarkerData : public ShapeBaseData
|
||||
{
|
||||
private:
|
||||
typedef ShapeBaseData Parent;
|
||||
public:
|
||||
DECLARE_CONOBJECT(MissionMarkerData);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: MissionMarker
|
||||
//------------------------------------------------------------------------------
|
||||
class MissionMarker : public ShapeBase
|
||||
{
|
||||
private:
|
||||
typedef ShapeBase Parent;
|
||||
|
||||
protected:
|
||||
enum MaskBits {
|
||||
PositionMask = Parent::NextFreeMask,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
MissionMarkerData * mDataBlock;
|
||||
bool mAddedToScene;
|
||||
|
||||
public:
|
||||
MissionMarker();
|
||||
|
||||
// GameBase
|
||||
bool onNewDataBlock(GameBaseData *dptr);
|
||||
|
||||
// SceneObject
|
||||
void setTransform(const MatrixF &mat);
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void onEditorEnable();
|
||||
void onEditorDisable();
|
||||
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
DECLARE_CONOBJECT(MissionMarker);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: WayPoint
|
||||
//------------------------------------------------------------------------------
|
||||
class WayPoint;
|
||||
class WayPointTeam
|
||||
{
|
||||
public:
|
||||
WayPointTeam();
|
||||
|
||||
S32 mTeamId;
|
||||
WayPoint * mWayPoint;
|
||||
};
|
||||
DefineConsoleType( TypeWayPointTeam )
|
||||
|
||||
class WayPoint : public MissionMarker
|
||||
{
|
||||
private:
|
||||
typedef MissionMarker Parent;
|
||||
|
||||
public:
|
||||
enum WayPointMasks {
|
||||
UpdateNameMask = Parent::NextFreeMask,
|
||||
UpdateTeamMask = Parent::NextFreeMask << 1,
|
||||
UpdateHiddenMask = Parent::NextFreeMask << 2,
|
||||
NextFreeMask = Parent::NextFreeMask << 3
|
||||
};
|
||||
|
||||
WayPoint();
|
||||
|
||||
// ShapeBase: only ever added to scene if in the editor
|
||||
void setHidden(bool hidden);
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// field data
|
||||
StringTableEntry mName;
|
||||
WayPointTeam mTeam;
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
DECLARE_CONOBJECT(WayPoint);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: SpawnSphere
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class SpawnSphere : public MissionMarker
|
||||
{
|
||||
private:
|
||||
typedef MissionMarker Parent;
|
||||
static Sphere smSphere;
|
||||
|
||||
public:
|
||||
SpawnSphere();
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
enum SpawnSphereMasks {
|
||||
UpdateSphereMask = Parent::NextFreeMask,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// field data
|
||||
F32 mRadius;
|
||||
F32 mSphereWeight;
|
||||
F32 mIndoorWeight;
|
||||
F32 mOutdoorWeight;
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
DECLARE_CONOBJECT(SpawnSphere);
|
||||
};
|
||||
|
||||
#endif
|
||||
65
engine/game/moveManager.h
Executable file
65
engine/game/moveManager.h
Executable file
@@ -0,0 +1,65 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MOVEMANAGER_H_
|
||||
#define _MOVEMANAGER_H_
|
||||
|
||||
enum MoveConstants {
|
||||
MaxTriggerKeys = 6,
|
||||
MaxMoveQueueSize = 45,
|
||||
};
|
||||
|
||||
class BitStream;
|
||||
|
||||
struct Move
|
||||
{
|
||||
// packed storage rep, set in clamp
|
||||
S32 px, py, pz;
|
||||
U32 pyaw, ppitch, proll;
|
||||
F32 x, y, z; // float -1 to 1
|
||||
F32 yaw, pitch, roll; // 0-2PI
|
||||
U32 id; // sync'd between server & client - debugging tool.
|
||||
U32 sendCount;
|
||||
|
||||
bool freeLook;
|
||||
bool trigger[MaxTriggerKeys];
|
||||
|
||||
void pack(BitStream *stream);
|
||||
void unpack(BitStream *stream);
|
||||
void clamp();
|
||||
void unclamp();
|
||||
};
|
||||
|
||||
extern const Move NullMove;
|
||||
|
||||
class MoveManager
|
||||
{
|
||||
public:
|
||||
static F32 mForwardAction;
|
||||
static F32 mBackwardAction;
|
||||
static F32 mUpAction;
|
||||
static F32 mDownAction;
|
||||
static F32 mLeftAction;
|
||||
static F32 mRightAction;
|
||||
|
||||
static bool mFreeLook;
|
||||
static F32 mPitch;
|
||||
static F32 mYaw;
|
||||
static F32 mRoll;
|
||||
|
||||
static F32 mPitchUpSpeed;
|
||||
static F32 mPitchDownSpeed;
|
||||
static F32 mYawLeftSpeed;
|
||||
static F32 mYawRightSpeed;
|
||||
static F32 mRollLeftSpeed;
|
||||
static F32 mRollRightSpeed;
|
||||
|
||||
static U32 mTriggerCount[MaxTriggerKeys];
|
||||
static U32 mPrevTriggerCount[MaxTriggerKeys];
|
||||
|
||||
static void init();
|
||||
};
|
||||
|
||||
#endif
|
||||
309
engine/game/net/httpObject.cc
Executable file
309
engine/game/net/httpObject.cc
Executable file
@@ -0,0 +1,309 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/net/httpObject.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/event.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/consoleInternal.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(HTTPObject);
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
HTTPObject::HTTPObject()
|
||||
{
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
mPost = 0;
|
||||
mBufferSave = 0;
|
||||
}
|
||||
|
||||
HTTPObject::~HTTPObject()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
dFree(mPost);
|
||||
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
mPost = 0;
|
||||
dFree(mBufferSave);
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
//--------------------------------------
|
||||
void HTTPObject::get(const char *host, const char *path, const char *query)
|
||||
{
|
||||
if(mHostName)
|
||||
dFree(mHostName);
|
||||
if(mPath)
|
||||
dFree(mPath);
|
||||
if(mQuery)
|
||||
dFree(mQuery);
|
||||
if(mPost)
|
||||
dFree(mPost);
|
||||
if(mBufferSave)
|
||||
dFree(mBufferSave);
|
||||
|
||||
mBufferSave = 0;
|
||||
mHostName = dStrdup(host);
|
||||
mPath = dStrdup(path);
|
||||
if(query)
|
||||
mQuery = dStrdup(query);
|
||||
else
|
||||
mQuery = NULL;
|
||||
mPost = NULL;
|
||||
|
||||
connect(host);
|
||||
}
|
||||
|
||||
void HTTPObject::post(const char *host, const char *path, const char *query, const char *post)
|
||||
{
|
||||
if(mHostName)
|
||||
dFree(mHostName);
|
||||
if(mPath)
|
||||
dFree(mPath);
|
||||
if(mQuery)
|
||||
dFree(mQuery);
|
||||
if(mPost)
|
||||
dFree(mPost);
|
||||
if(mBufferSave)
|
||||
dFree(mBufferSave);
|
||||
|
||||
mBufferSave = 0;
|
||||
mHostName = dStrdup(host);
|
||||
mPath = dStrdup(path);
|
||||
if(query && query[0])
|
||||
mQuery = dStrdup(query);
|
||||
else
|
||||
mQuery = NULL;
|
||||
mPost = dStrdup(post);
|
||||
connect(host);
|
||||
}
|
||||
|
||||
static char getHex(char c)
|
||||
{
|
||||
if(c <= 9)
|
||||
return c + '0';
|
||||
return c - 10 + 'A';
|
||||
}
|
||||
|
||||
static S32 getHexVal(char c)
|
||||
{
|
||||
if(c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if(c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 10;
|
||||
else if(c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void HTTPObject::expandPath(char *dest, const char *path, U32 destSize)
|
||||
{
|
||||
static bool asciiEscapeTableBuilt = false;
|
||||
static bool asciiEscapeTable[256];
|
||||
if(!asciiEscapeTableBuilt)
|
||||
{
|
||||
asciiEscapeTableBuilt = true;
|
||||
U32 i;
|
||||
for(i = 0; i <= ' '; i++)
|
||||
asciiEscapeTable[i] = true;
|
||||
for(;i <= 0x7F; i++)
|
||||
asciiEscapeTable[i] = false;
|
||||
for(;i <= 0xFF; i++)
|
||||
asciiEscapeTable[i] = true;
|
||||
asciiEscapeTable['\"'] = true;
|
||||
asciiEscapeTable['_'] = true;
|
||||
asciiEscapeTable['\''] = true;
|
||||
asciiEscapeTable['#'] = true;
|
||||
asciiEscapeTable['$'] = true;
|
||||
asciiEscapeTable['%'] = true;
|
||||
asciiEscapeTable['&'] = true;
|
||||
asciiEscapeTable['+'] = true;
|
||||
asciiEscapeTable['-'] = true;
|
||||
asciiEscapeTable['~'] = true;
|
||||
}
|
||||
|
||||
U32 destIndex = 0;
|
||||
U32 srcIndex = 0;
|
||||
while(path[srcIndex] && destIndex < destSize - 3)
|
||||
{
|
||||
char c = path[srcIndex++];
|
||||
if(asciiEscapeTable[c])
|
||||
{
|
||||
dest[destIndex++] = '%';
|
||||
dest[destIndex++] = getHex((c >> 4) & 0xF);
|
||||
dest[destIndex++] = getHex(c & 0xF);
|
||||
}
|
||||
else
|
||||
dest[destIndex++] = c;
|
||||
}
|
||||
dest[destIndex] = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void HTTPObject::onConnected()
|
||||
{
|
||||
Parent::onConnected();
|
||||
char expPath[8192];
|
||||
char buffer[8192];
|
||||
|
||||
if(mQuery)
|
||||
{
|
||||
dSprintf(buffer, sizeof(buffer), "%s?%s", mPath, mQuery);
|
||||
expandPath(expPath, buffer, sizeof(expPath));
|
||||
}
|
||||
else
|
||||
expandPath(expPath, mPath, sizeof(expPath));
|
||||
|
||||
char *pt = dStrchr(mHostName, ':');
|
||||
if(pt)
|
||||
*pt = 0;
|
||||
dSprintf(buffer, sizeof(buffer), "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", expPath, mHostName);
|
||||
if(pt)
|
||||
*pt = ':';
|
||||
|
||||
send((U8*)buffer, dStrlen(buffer));
|
||||
mParseState = ParsingStatusLine;
|
||||
mChunkedEncoding = false;
|
||||
}
|
||||
|
||||
void HTTPObject::onConnectFailed()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
Parent::onConnectFailed();
|
||||
}
|
||||
|
||||
|
||||
void HTTPObject::onDisconnect()
|
||||
{
|
||||
dFree(mHostName);
|
||||
dFree(mPath);
|
||||
dFree(mQuery);
|
||||
mHostName = 0;
|
||||
mPath = 0;
|
||||
mQuery = 0;
|
||||
Parent::onDisconnect();
|
||||
}
|
||||
|
||||
bool HTTPObject::processLine(U8 *line)
|
||||
{
|
||||
if(mParseState == ParsingStatusLine)
|
||||
{
|
||||
mParseState = ParsingHeader;
|
||||
}
|
||||
else if(mParseState == ParsingHeader)
|
||||
{
|
||||
if(!dStricmp((char *) line, "transfer-encoding: chunked"))
|
||||
mChunkedEncoding = true;
|
||||
if(line[0] == 0)
|
||||
{
|
||||
if(mChunkedEncoding)
|
||||
mParseState = ParsingChunkHeader;
|
||||
else
|
||||
mParseState = ProcessingBody;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(mParseState == ParsingChunkHeader)
|
||||
{
|
||||
if(line[0]) // strip off the crlf if necessary
|
||||
{
|
||||
mChunkSize = 0;
|
||||
S32 hexVal;
|
||||
while((hexVal = getHexVal(*line++)) != -1)
|
||||
{
|
||||
mChunkSize *= 16;
|
||||
mChunkSize += hexVal;
|
||||
}
|
||||
if(mBufferSave)
|
||||
{
|
||||
mBuffer = mBufferSave;
|
||||
mBufferSize = mBufferSaveSize;
|
||||
mBufferSave = 0;
|
||||
}
|
||||
if(mChunkSize)
|
||||
mParseState = ProcessingBody;
|
||||
else
|
||||
{
|
||||
mParseState = ProcessingDone;
|
||||
finishLastLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Parent::processLine(line);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 HTTPObject::onDataReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
U32 HTTPObject::onReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
if(mParseState == ProcessingBody)
|
||||
{
|
||||
if(mChunkedEncoding && bufferLen >= mChunkSize)
|
||||
{
|
||||
U32 ret = onDataReceive(buffer, mChunkSize);
|
||||
mChunkSize -= ret;
|
||||
if(mChunkSize == 0)
|
||||
{
|
||||
if(mBuffer)
|
||||
{
|
||||
mBufferSaveSize = mBufferSize;
|
||||
mBufferSave = mBuffer;
|
||||
mBuffer = 0;
|
||||
mBufferSize = 0;
|
||||
}
|
||||
mParseState = ParsingChunkHeader;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
U32 ret = onDataReceive(buffer, bufferLen);
|
||||
mChunkSize -= ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else if(mParseState != ProcessingDone)
|
||||
{
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
return bufferLen;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
ConsoleMethod( HTTPObject, get, void, 4, 5, "(TransportAddress addr, string requirstURI, string query=NULL)")
|
||||
{
|
||||
object->get(argv[2], argv[3], argc == 4 ? NULL : argv[4]);
|
||||
}
|
||||
|
||||
ConsoleMethod( HTTPObject, post, void, 6, 6, "(TransportAddress addr, string requestURI, string query, string post)")
|
||||
{
|
||||
object->post(argv[2], argv[3], argv[4], argv[5]);
|
||||
}
|
||||
64
engine/game/net/httpObject.h
Executable file
64
engine/game/net/httpObject.h
Executable file
@@ -0,0 +1,64 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _HTTPOBJECT_H_
|
||||
#define _HTTPOBJECT_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _TCPOBJECT_H_
|
||||
#include "game/net/tcpObject.h"
|
||||
#endif
|
||||
|
||||
class HTTPObject : public TCPObject
|
||||
{
|
||||
private:
|
||||
typedef TCPObject Parent;
|
||||
protected:
|
||||
enum ParseState {
|
||||
ParsingStatusLine,
|
||||
ParsingHeader,
|
||||
ParsingChunkHeader,
|
||||
ProcessingBody,
|
||||
ProcessingDone,
|
||||
};
|
||||
ParseState mParseState;
|
||||
U32 mTotalBytes;
|
||||
U32 mBytesRemaining;
|
||||
public:
|
||||
U32 mStatus;
|
||||
F32 mVersion;
|
||||
U32 mContentLength;
|
||||
bool mChunkedEncoding;
|
||||
U32 mChunkSize;
|
||||
const char *mContentType;
|
||||
char *mHostName;
|
||||
char *mPath;
|
||||
char *mQuery;
|
||||
char *mPost;
|
||||
U8 *mBufferSave;
|
||||
U32 mBufferSaveSize;
|
||||
public:
|
||||
static void expandPath(char *dest, const char *path, U32 destSize);
|
||||
void get(const char *hostName, const char *urlName, const char *query);
|
||||
void post(const char *host, const char *path, const char *query, const char *post);
|
||||
HTTPObject();
|
||||
~HTTPObject();
|
||||
|
||||
//static HTTPObject *find(U32 tag);
|
||||
|
||||
virtual U32 onDataReceive(U8 *buffer, U32 bufferLen);
|
||||
virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data
|
||||
virtual void onConnected();
|
||||
virtual void onConnectFailed();
|
||||
virtual void onDisconnect();
|
||||
bool processLine(U8 *line);
|
||||
|
||||
DECLARE_CONOBJECT(HTTPObject);
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_HTTPOBJECT_
|
||||
277
engine/game/net/net.cc
Executable file
277
engine/game/net/net.cc
Executable file
@@ -0,0 +1,277 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/dnet.h"
|
||||
#include "core/idGenerator.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "sim/netObject.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "game/net/serverQuery.h"
|
||||
|
||||
StringTableEntry gMasterAddress;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// remote procedure call console functions
|
||||
//----------------------------------------------------------------
|
||||
|
||||
class RemoteCommandEvent : public NetEvent
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
MaxRemoteCommandArgs = 20,
|
||||
CommandArgsBits = 5
|
||||
};
|
||||
|
||||
private:
|
||||
S32 mArgc;
|
||||
char *mArgv[MaxRemoteCommandArgs + 1];
|
||||
StringHandle mTagv[MaxRemoteCommandArgs + 1];
|
||||
static char mBuf[1024];
|
||||
public:
|
||||
RemoteCommandEvent(S32 argc=0, const char **argv=NULL, NetConnection *conn = NULL)
|
||||
{
|
||||
mArgc = argc;
|
||||
for(S32 i = 0; i < argc; i++)
|
||||
{
|
||||
if(argv[i][0] == StringTagPrefixByte)
|
||||
{
|
||||
char buffer[256];
|
||||
mTagv[i+1] = StringHandle(dAtoi(argv[i]+1));
|
||||
if(conn)
|
||||
{
|
||||
dSprintf(buffer + 1, sizeof(buffer) - 1, "%d", conn->getNetSendId(mTagv[i+1]));
|
||||
buffer[0] = StringTagPrefixByte;
|
||||
mArgv[i+1] = dStrdup(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
mArgv[i+1] = dStrdup(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET
|
||||
const char *getDebugName()
|
||||
{
|
||||
static char buffer[256];
|
||||
dSprintf(buffer, sizeof(buffer), "%s [%s]", getClassName(), gNetStringTable->lookupString(dAtoi(mArgv[1] + 1)) );
|
||||
return buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
~RemoteCommandEvent()
|
||||
{
|
||||
for(S32 i = 0; i < mArgc; i++)
|
||||
dFree(mArgv[i+1]);
|
||||
}
|
||||
|
||||
virtual void pack(NetConnection* conn, BitStream *bstream)
|
||||
{
|
||||
bstream->writeInt(mArgc, CommandArgsBits);
|
||||
// write it out reversed... why?
|
||||
// automatic string substitution with later arguments -
|
||||
// handled automatically by the system.
|
||||
|
||||
for(S32 i = 0; i < mArgc; i++)
|
||||
conn->packString(bstream, mArgv[i+1]);
|
||||
}
|
||||
|
||||
virtual void write(NetConnection* conn, BitStream *bstream)
|
||||
{
|
||||
pack(conn, bstream);
|
||||
}
|
||||
|
||||
virtual void unpack(NetConnection* conn, BitStream *bstream)
|
||||
{
|
||||
|
||||
mArgc = bstream->readInt(CommandArgsBits);
|
||||
// read it out backwards
|
||||
for(S32 i = 0; i < mArgc; i++)
|
||||
{
|
||||
conn->unpackString(bstream, mBuf);
|
||||
mArgv[i+1] = dStrdup(mBuf);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void process(NetConnection *conn)
|
||||
{
|
||||
static char idBuf[10];
|
||||
|
||||
// de-tag the command name
|
||||
|
||||
for(S32 i = mArgc - 1; i >= 0; i--)
|
||||
{
|
||||
char *arg = mArgv[i+1];
|
||||
if(*arg == StringTagPrefixByte)
|
||||
{
|
||||
// it's a tag:
|
||||
U32 localTag = dAtoi(arg + 1);
|
||||
StringHandle tag = conn->translateRemoteStringId(localTag);
|
||||
NetStringTable::expandString( tag,
|
||||
mBuf,
|
||||
sizeof(mBuf),
|
||||
(mArgc - 1) - i,
|
||||
(const char**)(mArgv + i + 2) );
|
||||
dFree(mArgv[i+1]);
|
||||
mArgv[i+1] = dStrdup(mBuf);
|
||||
}
|
||||
}
|
||||
const char *rmtCommandName = dStrchr(mArgv[1], ' ') + 1;
|
||||
if(conn->isConnectionToServer())
|
||||
{
|
||||
dStrcpy(mBuf, "clientCmd");
|
||||
dStrcat(mBuf, rmtCommandName);
|
||||
|
||||
char *temp = mArgv[1];
|
||||
mArgv[1] = mBuf;
|
||||
|
||||
Con::execute(mArgc, (const char **) mArgv+1);
|
||||
mArgv[1] = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
dStrcpy(mBuf, "serverCmd");
|
||||
dStrcat(mBuf, rmtCommandName);
|
||||
char *temp = mArgv[1];
|
||||
|
||||
dSprintf(idBuf, sizeof(idBuf), "%d", conn->getId());
|
||||
mArgv[0] = mBuf;
|
||||
mArgv[1] = idBuf;
|
||||
|
||||
Con::execute(mArgc+1, (const char **) mArgv);
|
||||
mArgv[1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_CONOBJECT(RemoteCommandEvent);
|
||||
};
|
||||
char RemoteCommandEvent::mBuf[1024];
|
||||
|
||||
IMPLEMENT_CO_NETEVENT_V1(RemoteCommandEvent);
|
||||
|
||||
static void sendRemoteCommand(NetConnection *conn, S32 argc, const char **argv)
|
||||
{
|
||||
if(U8(argv[0][0]) != StringTagPrefixByte)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::Script, "Remote Command Error - command must be a tag.");
|
||||
return;
|
||||
}
|
||||
S32 i;
|
||||
for(i = argc - 1; i >= 0; i--)
|
||||
{
|
||||
if(argv[i][0] != 0)
|
||||
break;
|
||||
argc = i;
|
||||
}
|
||||
for(i = 0; i < argc; i++)
|
||||
conn->validateSendString(argv[i]);
|
||||
RemoteCommandEvent *cevt = new RemoteCommandEvent(argc, argv, conn);
|
||||
conn->postNetEvent(cevt);
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupBegin( Net, "Functions for use with the network; tagged strings and remote commands.");
|
||||
|
||||
ConsoleFunction( commandToServer, void, 2, RemoteCommandEvent::MaxRemoteCommandArgs + 1, "(string func, ...)"
|
||||
"Send a command to the server.")
|
||||
{
|
||||
NetConnection *conn = NetConnection::getConnectionToServer();
|
||||
if(!conn)
|
||||
return;
|
||||
sendRemoteCommand(conn, argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
ConsoleFunction( commandToClient, void, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(NetConnection client, string func, ...)")
|
||||
{
|
||||
NetConnection *conn;
|
||||
if(!Sim::findObject(argv[1], conn))
|
||||
return;
|
||||
sendRemoteCommand(conn, argc - 2, argv + 2);
|
||||
}
|
||||
|
||||
ConsoleFunction(removeTaggedString, void, 2, 2, "(int tag)")
|
||||
{
|
||||
gNetStringTable->removeString(dAtoi(argv[1]+1), true);
|
||||
}
|
||||
|
||||
ConsoleFunction( addTaggedString, const char*, 2, 2, "(string str)")
|
||||
{
|
||||
StringHandle s(argv[1]);
|
||||
gNetStringTable->incStringRefScript(s.getIndex());
|
||||
|
||||
char *ret = Con::getReturnBuffer(10);
|
||||
ret[0] = StringTagPrefixByte;
|
||||
dSprintf(ret + 1, 9, "%d", s.getIndex());
|
||||
return ret;
|
||||
}
|
||||
|
||||
ConsoleFunction( getTaggedString, const char*, 2, 2, "(int tag)")
|
||||
{
|
||||
const char *indexPtr = argv[1];
|
||||
if (*indexPtr == StringTagPrefixByte)
|
||||
indexPtr++;
|
||||
return gNetStringTable->lookupString(dAtoi(indexPtr));
|
||||
}
|
||||
|
||||
ConsoleFunction( buildTaggedString, const char*, 2, 11, "(string format, ...)")
|
||||
{
|
||||
const char *indexPtr = argv[1];
|
||||
if (*indexPtr == StringTagPrefixByte)
|
||||
indexPtr++;
|
||||
const char *fmtString = gNetStringTable->lookupString(dAtoi(indexPtr));
|
||||
char *strBuffer = Con::getReturnBuffer(512);
|
||||
const char *fmtStrPtr = fmtString;
|
||||
char *strBufPtr = strBuffer;
|
||||
S32 strMaxLength = 511;
|
||||
if (!fmtString)
|
||||
goto done;
|
||||
|
||||
//build the string
|
||||
while (*fmtStrPtr)
|
||||
{
|
||||
//look for an argument tag
|
||||
if (*fmtStrPtr == '%')
|
||||
{
|
||||
if (fmtStrPtr[1] >= '1' && fmtStrPtr[1] <= '9')
|
||||
{
|
||||
S32 argIndex = S32(fmtStrPtr[1] - '0') + 1;
|
||||
if (argIndex >= argc)
|
||||
goto done;
|
||||
const char *argStr = argv[argIndex];
|
||||
if (!argStr)
|
||||
goto done;
|
||||
S32 strLength = dStrlen(argStr);
|
||||
if (strLength > strMaxLength)
|
||||
goto done;
|
||||
dStrcpy(strBufPtr, argStr);
|
||||
strBufPtr += strLength;
|
||||
strMaxLength -= strLength;
|
||||
fmtStrPtr += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//if we don't continue, just copy the character
|
||||
if (strMaxLength <= 0)
|
||||
goto done;
|
||||
*strBufPtr++ = *fmtStrPtr++;
|
||||
strMaxLength--;
|
||||
}
|
||||
|
||||
done:
|
||||
*strBufPtr = '\0';
|
||||
return strBuffer;
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd( Net );
|
||||
|
||||
void netInit()
|
||||
{
|
||||
gMasterAddress = "";
|
||||
Con::addVariable( "MasterServerAddress", TypeString, &gMasterAddress );
|
||||
}
|
||||
84
engine/game/net/netTest.cc
Executable file
84
engine/game/net/netTest.cc
Executable file
@@ -0,0 +1,84 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/simBase.h"
|
||||
#include "platform/event.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "sim/netObject.h"
|
||||
|
||||
class SimpleMessageEvent : public NetEvent
|
||||
{
|
||||
typedef NetEvent Parent;
|
||||
char *msg;
|
||||
public:
|
||||
SimpleMessageEvent(const char *message = NULL)
|
||||
{
|
||||
if(message)
|
||||
msg = dStrdup(message);
|
||||
else
|
||||
msg = NULL;
|
||||
}
|
||||
~SimpleMessageEvent()
|
||||
{ dFree(msg); }
|
||||
|
||||
virtual void pack(NetConnection* /*ps*/, BitStream *bstream)
|
||||
{ bstream->writeString(msg); }
|
||||
virtual void write(NetConnection*, BitStream *bstream)
|
||||
{ bstream->writeString(msg); }
|
||||
virtual void unpack(NetConnection* /*ps*/, BitStream *bstream)
|
||||
{ char buf[256]; bstream->readString(buf); msg = dStrdup(buf); }
|
||||
virtual void process(NetConnection *)
|
||||
{ Con::printf("RMSG %d %s", mSourceId, msg); }
|
||||
|
||||
DECLARE_CONOBJECT(SimpleMessageEvent);
|
||||
};
|
||||
|
||||
IMPLEMENT_CO_NETEVENT_V1(SimpleMessageEvent);
|
||||
|
||||
class SimpleNetObject : public NetObject
|
||||
{
|
||||
typedef NetObject Parent;
|
||||
public:
|
||||
char message[256];
|
||||
SimpleNetObject()
|
||||
{
|
||||
mNetFlags.set(ScopeAlways | Ghostable);
|
||||
dStrcpy(message, "Hello World!");
|
||||
}
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
|
||||
{
|
||||
stream->writeString(message);
|
||||
return 0;
|
||||
}
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream)
|
||||
{
|
||||
stream->readString(message);
|
||||
Con::printf("Got message: %s", message);
|
||||
}
|
||||
void setMessage(const char *msg)
|
||||
{
|
||||
setMaskBits(1);
|
||||
dStrcpy(message, msg);
|
||||
}
|
||||
|
||||
DECLARE_CONOBJECT(SimpleNetObject);
|
||||
};
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject);
|
||||
|
||||
ConsoleMethod( SimpleNetObject, setMessage, void, 3, 3, "(string msg)")
|
||||
{
|
||||
object->setMessage(argv[2]);
|
||||
}
|
||||
|
||||
ConsoleFunction( msg, void, 3, 3, "(NetConnection id, string message)"
|
||||
"Send a SimpleNetObject message to the specified connection.")
|
||||
{
|
||||
NetConnection *con = (NetConnection *) Sim::findObject(argv[1]);
|
||||
if(con)
|
||||
con->postNetEvent(new SimpleMessageEvent(argv[2]));
|
||||
}
|
||||
2051
engine/game/net/serverQuery.cc
Executable file
2051
engine/game/net/serverQuery.cc
Executable file
File diff suppressed because it is too large
Load Diff
125
engine/game/net/serverQuery.h
Executable file
125
engine/game/net/serverQuery.h
Executable file
@@ -0,0 +1,125 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SERVERQUERY_H_
|
||||
#define _SERVERQUERY_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _EVENT_H_
|
||||
#include "platform/event.h"
|
||||
#endif
|
||||
#ifndef _BITSET_H_
|
||||
#include "core/bitSet.h"
|
||||
#endif
|
||||
|
||||
#include "sim/netConnection.h"
|
||||
#include "sim/netInterface.h"
|
||||
//-----------------------------------------------------------------------------
|
||||
// Master server information
|
||||
|
||||
class DemoNetInterface : public NetInterface
|
||||
{
|
||||
public:
|
||||
void handleInfoPacket(const NetAddress *address, U8 packetType, BitStream *stream);
|
||||
};
|
||||
|
||||
struct MasterInfo
|
||||
{
|
||||
NetAddress address;
|
||||
U32 region;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Game Server Information
|
||||
|
||||
struct ServerInfo
|
||||
{
|
||||
enum StatusFlags
|
||||
{
|
||||
// Info flags (0-7):
|
||||
Status_Dedicated = BIT(0),
|
||||
Status_Passworded = BIT(1),
|
||||
Status_Linux = BIT(2),
|
||||
|
||||
// Status flags:
|
||||
Status_New = 0,
|
||||
Status_Querying = BIT(28),
|
||||
Status_Updating = BIT(29),
|
||||
Status_Responded = BIT(30),
|
||||
Status_TimedOut = BIT(31),
|
||||
};
|
||||
|
||||
U8 numPlayers;
|
||||
U8 maxPlayers;
|
||||
U8 numBots;
|
||||
char* name;
|
||||
char* gameType;
|
||||
char* missionName;
|
||||
char* missionType;
|
||||
char* statusString;
|
||||
char* infoString;
|
||||
NetAddress address;
|
||||
U32 version;
|
||||
U32 ping;
|
||||
U32 cpuSpeed;
|
||||
bool isFavorite;
|
||||
BitSet32 status;
|
||||
|
||||
ServerInfo()
|
||||
{
|
||||
numPlayers = 0;
|
||||
maxPlayers = 0;
|
||||
numBots = 0;
|
||||
name = NULL;
|
||||
gameType = NULL;
|
||||
missionType = NULL;
|
||||
missionName = NULL;
|
||||
statusString = NULL;
|
||||
infoString = NULL;
|
||||
version = 0;
|
||||
ping = 0;
|
||||
cpuSpeed = 0;
|
||||
isFavorite = false;
|
||||
status = Status_New;
|
||||
}
|
||||
~ServerInfo();
|
||||
|
||||
bool isNew() { return( status == Status_New ); }
|
||||
bool isQuerying() { return( status.test( Status_Querying ) ); }
|
||||
bool isUpdating() { return( status.test( Status_Updating ) ); }
|
||||
bool hasResponded() { return( status.test( Status_Responded ) ); }
|
||||
bool isTimedOut() { return( status.test( Status_TimedOut ) ); }
|
||||
|
||||
bool isDedicated() { return( status.test( Status_Dedicated ) ); }
|
||||
bool isPassworded() { return( status.test( Status_Passworded ) ); }
|
||||
bool isLinux() { return( status.test( Status_Linux ) ); }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern Vector<ServerInfo> gServerList;
|
||||
extern bool gServerBrowserDirty;
|
||||
extern void clearServerList();
|
||||
extern void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType,
|
||||
U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
|
||||
U8 filterFlags);
|
||||
extern void queryMasterGameTypes();
|
||||
extern void queryMasterServer(U8 flags, const char* gameType, const char* missionType,
|
||||
U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU,
|
||||
U8 filterFlags, U8 buddyCount, U32* buddyList );
|
||||
extern void queryFavoriteServers( U8 flags );
|
||||
extern void querySingleServer(const NetAddress* addr, U8 flags);
|
||||
extern void startHeartbeat();
|
||||
extern void sendHeartbeat( U8 flags );
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
extern void addFakeServers( S32 howMany );
|
||||
#endif // DEBUG
|
||||
|
||||
#endif
|
||||
309
engine/game/net/tcpObject.cc
Executable file
309
engine/game/net/tcpObject.cc
Executable file
@@ -0,0 +1,309 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/net/tcpObject.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/event.h"
|
||||
#include "platform/gameInterface.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "game/demoGame.h"
|
||||
|
||||
TCPObject *TCPObject::table[TCPObject::TableSize] = {0, };
|
||||
|
||||
IMPLEMENT_CONOBJECT(TCPObject);
|
||||
|
||||
TCPObject *TCPObject::find(NetSocket tag)
|
||||
{
|
||||
for(TCPObject *walk = table[U32(tag) & TableMask]; walk; walk = walk->mNext)
|
||||
if(walk->mTag == tag)
|
||||
return walk;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TCPObject::addToTable(NetSocket newTag)
|
||||
{
|
||||
removeFromTable();
|
||||
mTag = newTag;
|
||||
mNext = table[U32(mTag) & TableMask];
|
||||
table[U32(mTag) & TableMask] = this;
|
||||
}
|
||||
|
||||
void TCPObject::removeFromTable()
|
||||
{
|
||||
for(TCPObject **walk = &table[U32(mTag) & TableMask]; *walk; walk = &((*walk)->mNext))
|
||||
{
|
||||
if(*walk == this)
|
||||
{
|
||||
*walk = mNext;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TCPObject::TCPObject()
|
||||
{
|
||||
mBuffer = NULL;
|
||||
mBufferSize = 0;
|
||||
mPort = 0;
|
||||
mTag = InvalidSocket;
|
||||
mNext = NULL;
|
||||
mState = Disconnected;
|
||||
}
|
||||
|
||||
TCPObject::~TCPObject()
|
||||
{
|
||||
disconnect();
|
||||
dFree(mBuffer);
|
||||
}
|
||||
|
||||
bool TCPObject::processArguments(S32 argc, const char **argv)
|
||||
{
|
||||
if(argc == 0)
|
||||
return true;
|
||||
else if(argc == 1)
|
||||
{
|
||||
addToTable(U32(dAtoi(argv[0])));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TCPObject::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
const char *name = getName();
|
||||
|
||||
if(name && name[0] && getClassRep())
|
||||
{
|
||||
Namespace *parent = getClassRep()->getNameSpace();
|
||||
Con::linkNamespaces(parent->mName, name);
|
||||
mNameSpace = Con::lookupNamespace(name);
|
||||
|
||||
}
|
||||
|
||||
Sim::getTCPGroup()->addObject(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 TCPObject::onReceive(U8 *buffer, U32 bufferLen)
|
||||
{
|
||||
// we got a raw buffer event
|
||||
// default action is to split the buffer into lines of text
|
||||
// and call processLine on each
|
||||
// for any incomplete lines we have mBuffer
|
||||
U32 start = 0;
|
||||
parseLine(buffer, &start, bufferLen);
|
||||
return start;
|
||||
}
|
||||
|
||||
void TCPObject::parseLine(U8 *buffer, U32 *start, U32 bufferLen)
|
||||
{
|
||||
// find the first \n in buffer
|
||||
U32 i;
|
||||
U8 *line = buffer + *start;
|
||||
|
||||
for(i = *start; i < bufferLen; i++)
|
||||
if(buffer[i] == '\n' || buffer[i] == 0)
|
||||
break;
|
||||
U32 len = i - *start;
|
||||
|
||||
if(i == bufferLen || mBuffer)
|
||||
{
|
||||
// we've hit the end with no newline
|
||||
mBuffer = (U8 *) dRealloc(mBuffer, mBufferSize + len + 1);
|
||||
dMemcpy(mBuffer + mBufferSize, line, len);
|
||||
mBufferSize += len;
|
||||
*start = i;
|
||||
|
||||
// process the line
|
||||
if(i != bufferLen)
|
||||
{
|
||||
mBuffer[mBufferSize] = 0;
|
||||
if(mBufferSize && mBuffer[mBufferSize-1] == '\r')
|
||||
mBuffer[mBufferSize - 1] = 0;
|
||||
U8 *temp = mBuffer;
|
||||
mBuffer = 0;
|
||||
mBufferSize = 0;
|
||||
|
||||
processLine(temp);
|
||||
dFree(temp);
|
||||
}
|
||||
}
|
||||
else if(i != bufferLen)
|
||||
{
|
||||
line[len] = 0;
|
||||
if(len && line[len-1] == '\r')
|
||||
line[len-1] = 0;
|
||||
processLine(line);
|
||||
}
|
||||
if(i != bufferLen)
|
||||
*start = i + 1;
|
||||
}
|
||||
|
||||
void TCPObject::onConnectionRequest(const NetAddress *addr, U32 connectId)
|
||||
{
|
||||
char idBuf[16];
|
||||
char addrBuf[256];
|
||||
Net::addressToString(addr, addrBuf);
|
||||
dSprintf(idBuf, sizeof(idBuf), "%d", connectId);
|
||||
Con::executef(this, 3, "onConnectRequest", addrBuf, idBuf);
|
||||
}
|
||||
|
||||
bool TCPObject::processLine(U8 *line)
|
||||
{
|
||||
Con::executef(this, 2, "onLine", line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCPObject::onDNSResolved()
|
||||
{
|
||||
mState = DNSResolved;
|
||||
Con::executef(this, 1, "onDNSResolved");
|
||||
}
|
||||
|
||||
void TCPObject::onDNSFailed()
|
||||
{
|
||||
mState = Disconnected;
|
||||
Con::executef(this, 1, "onDNSFailed");
|
||||
}
|
||||
|
||||
void TCPObject::onConnected()
|
||||
{
|
||||
mState = Connected;
|
||||
Con::executef(this, 1, "onConnected");
|
||||
}
|
||||
|
||||
void TCPObject::onConnectFailed()
|
||||
{
|
||||
mState = Disconnected;
|
||||
Con::executef(this, 1, "onConnectFailed");
|
||||
}
|
||||
|
||||
void TCPObject::finishLastLine()
|
||||
{
|
||||
if(mBufferSize)
|
||||
{
|
||||
mBuffer[mBufferSize] = 0;
|
||||
processLine(mBuffer);
|
||||
dFree(mBuffer);
|
||||
mBuffer = 0;
|
||||
mBufferSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TCPObject::onDisconnect()
|
||||
{
|
||||
finishLastLine();
|
||||
mState = Disconnected;
|
||||
Con::executef(this, 1, "onDisconnect");
|
||||
}
|
||||
|
||||
void TCPObject::listen(U16 port)
|
||||
{
|
||||
mState = Listening;
|
||||
U32 newTag = Net::openListenPort(port);
|
||||
addToTable(newTag);
|
||||
}
|
||||
|
||||
void TCPObject::connect(const char *address)
|
||||
{
|
||||
NetSocket newTag = Net::openConnectTo(address);
|
||||
addToTable(newTag);
|
||||
}
|
||||
|
||||
void TCPObject::disconnect()
|
||||
{
|
||||
if( mTag != InvalidSocket ) {
|
||||
Net::closeConnectTo(mTag);
|
||||
}
|
||||
removeFromTable();
|
||||
}
|
||||
|
||||
void TCPObject::send(const U8 *buffer, U32 len)
|
||||
{
|
||||
Net::sendtoSocket(mTag, buffer, S32(len));
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, send, void, 3, 0, "(...)"
|
||||
"Parameters are transmitted as strings, one at a time.")
|
||||
{
|
||||
for(S32 i = 2; i < argc; i++)
|
||||
object->send((const U8 *) argv[i], dStrlen(argv[i]));
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, listen, void, 3, 3, "(int port)"
|
||||
"Start listening on the specified ports for connections.")
|
||||
{
|
||||
object->listen(U32(dAtoi(argv[2])));
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, connect, void, 3, 3, "(string addr)"
|
||||
"Connect to the given address.")
|
||||
{
|
||||
object->connect(argv[2]);
|
||||
}
|
||||
|
||||
ConsoleMethod( TCPObject, disconnect, void, 2, 2, "Disconnect from whatever we're connected to, if anything.")
|
||||
{
|
||||
object->disconnect();
|
||||
}
|
||||
|
||||
void DemoGame::processConnectedReceiveEvent(ConnectedReceiveEvent* event )
|
||||
{
|
||||
TCPObject *tcpo = TCPObject::find(event->tag);
|
||||
if(!tcpo)
|
||||
{
|
||||
Con::printf("Got bad connected receive event.");
|
||||
return;
|
||||
}
|
||||
U32 size = U32(event->size - ConnectedReceiveEventHeaderSize);
|
||||
U8 *buffer = event->data;
|
||||
|
||||
while(size)
|
||||
{
|
||||
U32 ret = tcpo->onReceive(buffer, size);
|
||||
AssertFatal(ret <= size, "Invalid return size");
|
||||
size -= ret;
|
||||
buffer += ret;
|
||||
}
|
||||
}
|
||||
|
||||
void DemoGame::processConnectedAcceptEvent( ConnectedAcceptEvent* event )
|
||||
{
|
||||
TCPObject *tcpo = TCPObject::find(event->portTag);
|
||||
if(!tcpo)
|
||||
return;
|
||||
tcpo->onConnectionRequest(&event->address, event->connectionTag);
|
||||
}
|
||||
|
||||
void DemoGame::processConnectedNotifyEvent( ConnectedNotifyEvent* event )
|
||||
{
|
||||
TCPObject *tcpo = TCPObject::find(event->tag);
|
||||
if(!tcpo)
|
||||
return;
|
||||
switch(event->state)
|
||||
{
|
||||
case ConnectedNotifyEvent::DNSResolved:
|
||||
tcpo->onDNSResolved();
|
||||
break;
|
||||
case ConnectedNotifyEvent::DNSFailed:
|
||||
tcpo->onDNSFailed();
|
||||
break;
|
||||
case ConnectedNotifyEvent::Connected:
|
||||
tcpo->onConnected();
|
||||
break;
|
||||
case ConnectedNotifyEvent::ConnectFailed:
|
||||
tcpo->onConnectFailed();
|
||||
break;
|
||||
case ConnectedNotifyEvent::Disconnected:
|
||||
tcpo->onDisconnect();
|
||||
break;
|
||||
}
|
||||
}
|
||||
69
engine/game/net/tcpObject.h
Executable file
69
engine/game/net/tcpObject.h
Executable file
@@ -0,0 +1,69 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TCPOBJECT_H_
|
||||
#define _TCPOBJECT_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
class TCPObject : public SimObject
|
||||
{
|
||||
public:
|
||||
enum State {Disconnected, DNSResolved, Connected, Listening };
|
||||
|
||||
private:
|
||||
NetSocket mTag;
|
||||
TCPObject *mNext;
|
||||
enum { TableSize = 256, TableMask = 0xFF };
|
||||
static TCPObject *table[TableSize];
|
||||
State mState;
|
||||
|
||||
protected:
|
||||
typedef SimObject Parent;
|
||||
U8 *mBuffer;
|
||||
U32 mBufferSize;
|
||||
U16 mPort;
|
||||
|
||||
public:
|
||||
TCPObject();
|
||||
virtual ~TCPObject();
|
||||
|
||||
void parseLine(U8 *buffer, U32 *start, U32 bufferLen);
|
||||
void finishLastLine();
|
||||
|
||||
static TCPObject *find(NetSocket tag);
|
||||
|
||||
// onReceive gets called continuously until all bytes are processed
|
||||
// return # of bytes processed each time.
|
||||
virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data
|
||||
virtual bool processLine(U8 *line); // process a complete line of text... default action is to call into script
|
||||
virtual void onDNSResolved();
|
||||
virtual void onDNSFailed();
|
||||
virtual void onConnected();
|
||||
virtual void onConnectFailed();
|
||||
virtual void onConnectionRequest(const NetAddress *addr, U32 connectId);
|
||||
virtual void onDisconnect();
|
||||
void connect(const char *address);
|
||||
void listen(U16 port);
|
||||
void disconnect();
|
||||
State getState() { return mState; }
|
||||
|
||||
bool processArguments(S32 argc, const char **argv);
|
||||
void send(const U8 *buffer, U32 bufferLen);
|
||||
void addToTable(NetSocket newTag);
|
||||
void removeFromTable();
|
||||
|
||||
void setPort(U16 port) { mPort = port; }
|
||||
|
||||
bool onAdd();
|
||||
|
||||
DECLARE_CONOBJECT(TCPObject);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_TCPOBJECT_
|
||||
76
engine/game/objectTypes.h
Executable file
76
engine/game/objectTypes.h
Executable file
@@ -0,0 +1,76 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _OBJECTTYPES_H_
|
||||
#define _OBJECTTYPES_H_
|
||||
|
||||
#include "platform/types.h"
|
||||
|
||||
// Types used for SimObject type masks (SimObject::mTypeMask)
|
||||
//
|
||||
|
||||
/* NB! If a new object type is added, don't forget to add it to the
|
||||
* consoleInit function in simBase.cc
|
||||
*/
|
||||
|
||||
enum SimObjectTypes
|
||||
{
|
||||
/// @name Types used by the SceneObject class
|
||||
/// @{
|
||||
DefaultObjectType = 0,
|
||||
StaticObjectType = BIT(0),
|
||||
/// @}
|
||||
|
||||
/// @name Basic Engine Types
|
||||
/// @{
|
||||
|
||||
///
|
||||
EnvironmentObjectType = BIT(1),
|
||||
TerrainObjectType = BIT(2),
|
||||
InteriorObjectType = BIT(3),
|
||||
WaterObjectType = BIT(4),
|
||||
TriggerObjectType = BIT(5),
|
||||
MarkerObjectType = BIT(6),
|
||||
AtlasObjectType = BIT(7), // Buy TSE.
|
||||
InteriorMapObjectType = BIT(8),
|
||||
DecalManagerObjectType = BIT(9),
|
||||
/// @}
|
||||
|
||||
/// @name Game Types
|
||||
/// @{
|
||||
GameBaseObjectType = BIT(10),
|
||||
ShapeBaseObjectType = BIT(11),
|
||||
CameraObjectType = BIT(12),
|
||||
StaticShapeObjectType = BIT(13),
|
||||
PlayerObjectType = BIT(14),
|
||||
ItemObjectType = BIT(15),
|
||||
VehicleObjectType = BIT(16),
|
||||
VehicleBlockerObjectType = BIT(17),
|
||||
ProjectileObjectType = BIT(18),
|
||||
ExplosionObjectType = BIT(19),
|
||||
CorpseObjectType = BIT(20),
|
||||
DebrisObjectType = BIT(22),
|
||||
PhysicalZoneObjectType = BIT(23),
|
||||
StaticTSObjectType = BIT(24),
|
||||
AIObjectType = BIT(25),
|
||||
StaticRenderedObjectType = BIT(26),
|
||||
/// @}
|
||||
|
||||
/// @name Other
|
||||
/// The following are allowed types that can be set on datablocks for static shapes
|
||||
/// @{
|
||||
DamagableItemObjectType = BIT(27),
|
||||
/// @}
|
||||
};
|
||||
|
||||
#define STATIC_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticObjectType ) \
|
||||
|
||||
#define DAMAGEABLE_MASK ( PlayerObjectType | \
|
||||
VehicleObjectType | \
|
||||
DamagableItemObjectType ) \
|
||||
|
||||
#endif
|
||||
507
engine/game/pathCamera.cc
Executable file
507
engine/game/pathCamera.cc
Executable file
@@ -0,0 +1,507 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "core/dnet.h"
|
||||
#include "sim/pathManager.h"
|
||||
#include "game/game.h"
|
||||
#include "game/gameConnection.h"
|
||||
#include "editor/editor.h"
|
||||
|
||||
#include "game/pathCamera.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(PathCameraData);
|
||||
|
||||
void PathCameraData::consoleInit()
|
||||
{
|
||||
}
|
||||
|
||||
void PathCameraData::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void PathCameraData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
}
|
||||
|
||||
void PathCameraData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(PathCamera);
|
||||
|
||||
PathCamera::PathCamera()
|
||||
{
|
||||
mNetFlags.clear(Ghostable);
|
||||
mTypeMask |= CameraObjectType;
|
||||
delta.time = 0;
|
||||
delta.timeVec = 0;
|
||||
|
||||
mDataBlock = 0;
|
||||
mState = Forward;
|
||||
mNodeBase = 0;
|
||||
mNodeCount = 0;
|
||||
mPosition = 0;
|
||||
mTarget = 0;
|
||||
mTargetSet = false;
|
||||
|
||||
MatrixF mat(1);
|
||||
mat.setPosition(Point3F(0,0,700));
|
||||
Parent::setTransform(mat);
|
||||
}
|
||||
|
||||
PathCamera::~PathCamera()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool PathCamera::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// Initialize from the current transform.
|
||||
if (!mNodeCount) {
|
||||
QuatF rot(getTransform());
|
||||
Point3F pos = getPosition();
|
||||
mSpline.removeAll();
|
||||
mSpline.push_back(new CameraSpline::Knot(pos,rot,1,
|
||||
CameraSpline::Knot::NORMAL, CameraSpline::Knot::SPLINE));
|
||||
mNodeCount = 1;
|
||||
}
|
||||
|
||||
//
|
||||
mObjBox.max = mObjScale;
|
||||
mObjBox.min = mObjScale;
|
||||
mObjBox.min.neg();
|
||||
resetWorldBox();
|
||||
|
||||
if (getContainer())
|
||||
getContainer()->addObject(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PathCamera::onRemove()
|
||||
{
|
||||
if (getContainer())
|
||||
getContainer()->removeObject(this);
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
bool PathCamera::onNewDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
mDataBlock = dynamic_cast<PathCameraData*>(dptr);
|
||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::onEditorEnable()
|
||||
{
|
||||
mNetFlags.set(Ghostable);
|
||||
}
|
||||
|
||||
void PathCamera::onEditorDisable()
|
||||
{
|
||||
mNetFlags.clear(Ghostable);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void PathCamera::consoleInit()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::processTick(const Move* move)
|
||||
{
|
||||
// client and server
|
||||
Parent::processTick(move);
|
||||
|
||||
// Move to new time
|
||||
advancePosition(TickMs);
|
||||
|
||||
// Set new position
|
||||
MatrixF mat;
|
||||
interpolateMat(mPosition,&mat);
|
||||
Parent::setTransform(mat);
|
||||
}
|
||||
|
||||
void PathCamera::interpolateTick(F32 dt)
|
||||
{
|
||||
Parent::interpolateTick(dt);
|
||||
MatrixF mat;
|
||||
interpolateMat(delta.time + (delta.timeVec * dt),&mat);
|
||||
Parent::setRenderTransform(mat);
|
||||
}
|
||||
|
||||
void PathCamera::interpolateMat(F32 pos,MatrixF* mat)
|
||||
{
|
||||
CameraSpline::Knot knot;
|
||||
mSpline.value(pos - mNodeBase,&knot);
|
||||
knot.mRotation.setMatrix(mat);
|
||||
mat->setPosition(knot.mPosition);
|
||||
}
|
||||
|
||||
void PathCamera::advancePosition(S32 ms)
|
||||
{
|
||||
delta.timeVec = mPosition;
|
||||
|
||||
// Advance according to current speed
|
||||
if (mState == Forward) {
|
||||
mPosition = mSpline.advanceTime(mPosition - mNodeBase,ms);
|
||||
if (mPosition > mNodeCount - 1)
|
||||
mPosition = mNodeCount - 1;
|
||||
mPosition += mNodeBase;
|
||||
if (mTargetSet && mPosition >= mTarget) {
|
||||
mTargetSet = false;
|
||||
mPosition = mTarget;
|
||||
mState = Stop;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mState == Backward) {
|
||||
mPosition = mSpline.advanceTime(mPosition - mNodeBase,-ms);
|
||||
if (mPosition < 0)
|
||||
mPosition = 0;
|
||||
mPosition += mNodeBase;
|
||||
if (mTargetSet && mPosition <= mTarget) {
|
||||
mTargetSet = false;
|
||||
mPosition = mTarget;
|
||||
mState = Stop;
|
||||
}
|
||||
}
|
||||
|
||||
// Script callbacks
|
||||
if (int(mPosition) != int(delta.timeVec))
|
||||
onNode(int(mPosition));
|
||||
|
||||
// Set frame interpolation
|
||||
delta.time = mPosition;
|
||||
delta.timeVec -= mPosition;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::getCameraTransform(F32* pos, MatrixF* mat)
|
||||
{
|
||||
// Overide the ShapeBase method to skip all the first/third person support.
|
||||
getRenderEyeTransform(mat);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::setPosition(F32 pos)
|
||||
{
|
||||
mPosition = mClampF(pos,mNodeBase,mNodeBase + mNodeCount - 1);
|
||||
MatrixF mat;
|
||||
interpolateMat(mPosition,&mat);
|
||||
Parent::setTransform(mat);
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
|
||||
void PathCamera::setTarget(F32 pos)
|
||||
{
|
||||
mTarget = pos;
|
||||
mTargetSet = true;
|
||||
if (mTarget > mPosition)
|
||||
mState = Forward;
|
||||
else
|
||||
if (mTarget < mPosition)
|
||||
mState = Backward;
|
||||
else {
|
||||
mTargetSet = false;
|
||||
mState = Stop;
|
||||
}
|
||||
setMaskBits(TargetMask | StateMask);
|
||||
}
|
||||
|
||||
void PathCamera::setState(State s)
|
||||
{
|
||||
mState = s;
|
||||
setMaskBits(StateMask);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::reset(F32 speed)
|
||||
{
|
||||
CameraSpline::Knot *knot = new CameraSpline::Knot;
|
||||
mSpline.value(mPosition - mNodeBase,knot);
|
||||
if (speed)
|
||||
knot->mSpeed = speed;
|
||||
mSpline.removeAll();
|
||||
mSpline.push_back(knot);
|
||||
|
||||
mNodeBase = 0;
|
||||
mNodeCount = 1;
|
||||
mPosition = 0;
|
||||
mTargetSet = false;
|
||||
mState = Forward;
|
||||
setMaskBits(StateMask | PositionMask | WindowMask | TargetMask);
|
||||
}
|
||||
|
||||
void PathCamera::pushBack(CameraSpline::Knot *knot)
|
||||
{
|
||||
// Make room at the end
|
||||
if (mNodeCount == NodeWindow) {
|
||||
delete mSpline.remove(mSpline.getKnot(0));
|
||||
mNodeBase++;
|
||||
}
|
||||
else
|
||||
mNodeCount++;
|
||||
|
||||
// Fill in the new node
|
||||
mSpline.push_back(knot);
|
||||
setMaskBits(WindowMask);
|
||||
|
||||
// Make sure the position doesn't fall off
|
||||
if (mPosition < mNodeBase) {
|
||||
mPosition = mNodeBase;
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
}
|
||||
|
||||
void PathCamera::pushFront(CameraSpline::Knot *knot)
|
||||
{
|
||||
// Make room at the front
|
||||
if (mNodeCount == NodeWindow)
|
||||
delete mSpline.remove(mSpline.getKnot(mNodeCount));
|
||||
else
|
||||
mNodeCount++;
|
||||
mNodeBase--;
|
||||
|
||||
// Fill in the new node
|
||||
mSpline.push_front(knot);
|
||||
setMaskBits(WindowMask);
|
||||
|
||||
// Make sure the position doesn't fall off
|
||||
if (mPosition > mNodeBase + (NodeWindow - 1)) {
|
||||
mPosition = mNodeBase + (NodeWindow - 1);
|
||||
setMaskBits(PositionMask);
|
||||
}
|
||||
}
|
||||
|
||||
void PathCamera::popFront()
|
||||
{
|
||||
if (mNodeCount < 2)
|
||||
return;
|
||||
|
||||
// Remove the first node. Node base and position are unaffected.
|
||||
mNodeCount--;
|
||||
delete mSpline.remove(mSpline.getKnot(0));
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::onNode(S32 node)
|
||||
{
|
||||
if (!isGhost())
|
||||
Con::executef(mDataBlock,3,"onNode",scriptThis(), Con::getIntArg(node));
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PathCamera::renderImage(SceneState*, SceneRenderImage*)
|
||||
{
|
||||
if (gEditingMission)
|
||||
{
|
||||
glPushMatrix();
|
||||
dglMultMatrix(&mObjToWorld);
|
||||
glScalef(mObjScale.x,mObjScale.y,mObjScale.z);
|
||||
wireCube(Point3F(1, 1, 1),Point3F(0,0,0));
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
U32 PathCamera::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
Parent::packUpdate(con,mask,stream);
|
||||
|
||||
if (stream->writeFlag(mask & StateMask))
|
||||
stream->writeInt(mState,StateBits);
|
||||
|
||||
if (stream->writeFlag(mask & PositionMask))
|
||||
stream->write(mPosition);
|
||||
|
||||
if (stream->writeFlag(mask & TargetMask))
|
||||
if (stream->writeFlag(mTargetSet))
|
||||
stream->write(mTarget);
|
||||
|
||||
if (stream->writeFlag(mask & WindowMask)) {
|
||||
stream->write(mNodeBase);
|
||||
stream->write(mNodeCount);
|
||||
for (int i = 0; i < mNodeCount; i++) {
|
||||
CameraSpline::Knot *knot = mSpline.getKnot(i);
|
||||
mathWrite(*stream, knot->mPosition);
|
||||
mathWrite(*stream, knot->mRotation);
|
||||
stream->write(knot->mSpeed);
|
||||
stream->writeInt(knot->mType, CameraSpline::Knot::NUM_TYPE_BITS);
|
||||
stream->writeInt(knot->mPath, CameraSpline::Knot::NUM_PATH_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
// The rest of the data is part of the control object packet update.
|
||||
// If we're controlled by this client, we don't need to send it.
|
||||
if(stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PathCamera::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con,stream);
|
||||
|
||||
// StateMask
|
||||
if (stream->readFlag())
|
||||
mState = stream->readInt(StateBits);
|
||||
|
||||
// PositionMask
|
||||
if (stream->readFlag()) {
|
||||
stream->read(&mPosition);
|
||||
delta.time = mPosition;
|
||||
delta.timeVec = 0;
|
||||
}
|
||||
|
||||
// TargetMask
|
||||
if (stream->readFlag())
|
||||
if (mTargetSet = stream->readFlag())
|
||||
stream->read(&mTarget);
|
||||
|
||||
// WindowMask
|
||||
if (stream->readFlag()) {
|
||||
mSpline.removeAll();
|
||||
stream->read(&mNodeBase);
|
||||
stream->read(&mNodeCount);
|
||||
for (int i = 0; i < mNodeCount; i++) {
|
||||
CameraSpline::Knot *knot = new CameraSpline::Knot();
|
||||
mathRead(*stream, &knot->mPosition);
|
||||
mathRead(*stream, &knot->mRotation);
|
||||
stream->read(&knot->mSpeed);
|
||||
knot->mType = (CameraSpline::Knot::Type)stream->readInt(CameraSpline::Knot::NUM_TYPE_BITS);
|
||||
knot->mPath = (CameraSpline::Knot::Path)stream->readInt(CameraSpline::Knot::NUM_PATH_BITS);
|
||||
mSpline.push_back(knot);
|
||||
}
|
||||
}
|
||||
|
||||
// Controlled by the client?
|
||||
if (stream->readFlag())
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console access methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod(PathCamera,setPosition,void,3, 3,"PathCamera.setPosition(pos);")
|
||||
{
|
||||
object->setPosition(dAtof(argv[2]));
|
||||
}
|
||||
|
||||
ConsoleMethod(PathCamera,setTarget,void,3, 3,"PathCamera.setTarget(pos);")
|
||||
{
|
||||
object->setTarget(dAtof(argv[2]));
|
||||
}
|
||||
|
||||
ConsoleMethod(PathCamera,setState,void,3, 3,"PathCamera.setState({forward,backward,stop});")
|
||||
{
|
||||
if (!dStricmp(argv[2],"forward"))
|
||||
object->setState(PathCamera::Forward);
|
||||
else
|
||||
if (!dStricmp(argv[2],"backward"))
|
||||
object->setState(PathCamera::Backward);
|
||||
else
|
||||
object->setState(PathCamera::Stop);
|
||||
}
|
||||
|
||||
ConsoleMethod(PathCamera,reset,void,2,3,"PathCamera.reset(speed=0);")
|
||||
{
|
||||
object->reset((argc >= 3)? dAtof(argv[2]): 1);
|
||||
}
|
||||
|
||||
|
||||
static CameraSpline::Knot::Type resolveKnotType(const char *arg)
|
||||
{
|
||||
if (dStricmp(arg, "Position Only") == 0) return CameraSpline::Knot::POSITION_ONLY;
|
||||
if (dStricmp(arg, "Kink") == 0) return CameraSpline::Knot::KINK;
|
||||
return CameraSpline::Knot::NORMAL;
|
||||
}
|
||||
|
||||
static CameraSpline::Knot::Path resolveKnotPath(const char *arg)
|
||||
{
|
||||
if (dStricmp(arg, "Linear") == 0) return CameraSpline::Knot::LINEAR;
|
||||
return CameraSpline::Knot::SPLINE;
|
||||
}
|
||||
|
||||
ConsoleMethod(PathCamera,pushBack,void,6, 6,"PathCamera.pushBack(transform,speed,type,path);")
|
||||
{
|
||||
Point3F pos;
|
||||
AngAxisF aa(Point3F(0,0,0),0);
|
||||
dSscanf(argv[2], "%g %g %g %g %g %g %g", &pos.x, &pos.y, &pos.z, &aa.axis.x, &aa.axis.y, &aa.axis.z, &aa.angle);
|
||||
QuatF rot(aa);
|
||||
object->pushBack( new CameraSpline::Knot(pos, rot, dAtof(argv[3]), resolveKnotType(argv[4]), resolveKnotPath(argv[5])) );
|
||||
}
|
||||
|
||||
ConsoleMethod(PathCamera,pushFront,void,6, 6,"PathCamera.pushFront(transform,speed,type,path);")
|
||||
{
|
||||
Point3F pos;
|
||||
AngAxisF aa(Point3F(0,0,0),0);
|
||||
dSscanf(argv[2], "%g %g %g %g %g %g %g", &pos.x, &pos.y, &pos.z, &aa.axis.x, &aa.axis.y, &aa.axis.z, &aa.angle);
|
||||
QuatF rot(aa);
|
||||
object->pushFront( new CameraSpline::Knot(pos, rot, dAtof(argv[3]), resolveKnotType(argv[4]), resolveKnotPath(argv[5])) );
|
||||
}
|
||||
|
||||
ConsoleMethod(PathCamera,popFront,void,2, 2,"PathCamera.popFront();")
|
||||
{
|
||||
object->popFront();
|
||||
}
|
||||
|
||||
|
||||
|
||||
111
engine/game/pathCamera.h
Executable file
111
engine/game/pathCamera.h
Executable file
@@ -0,0 +1,111 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PATHCAMERA_H_
|
||||
#define _PATHCAMERA_H_
|
||||
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "game/shapeBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _CAMERASPLINE_H_
|
||||
#include "game/cameraSpline.h"
|
||||
#endif
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
struct PathCameraData: public ShapeBaseData {
|
||||
typedef ShapeBaseData Parent;
|
||||
|
||||
//
|
||||
DECLARE_CONOBJECT(PathCameraData);
|
||||
static void consoleInit();
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class PathCamera: public ShapeBase
|
||||
{
|
||||
public:
|
||||
enum State {
|
||||
Forward,
|
||||
Backward,
|
||||
Stop,
|
||||
StateBits = 3
|
||||
};
|
||||
|
||||
private:
|
||||
typedef ShapeBase Parent;
|
||||
|
||||
enum MaskBits {
|
||||
WindowMask = Parent::NextFreeMask,
|
||||
PositionMask = Parent::NextFreeMask + 1,
|
||||
TargetMask = Parent::NextFreeMask + 2,
|
||||
StateMask = Parent::NextFreeMask + 3,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
struct StateDelta {
|
||||
F32 time;
|
||||
F32 timeVec;
|
||||
};
|
||||
StateDelta delta;
|
||||
|
||||
enum Constants {
|
||||
NodeWindow = 20 // Maximum number of active nodes
|
||||
};
|
||||
|
||||
//
|
||||
PathCameraData* mDataBlock;
|
||||
CameraSpline mSpline;
|
||||
S32 mNodeBase;
|
||||
S32 mNodeCount;
|
||||
F32 mPosition;
|
||||
int mState;
|
||||
F32 mTarget;
|
||||
bool mTargetSet;
|
||||
|
||||
void interpolateMat(F32 pos,MatrixF* mat);
|
||||
void advancePosition(S32 ms);
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(PathCamera);
|
||||
|
||||
PathCamera();
|
||||
~PathCamera();
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
|
||||
void onEditorEnable();
|
||||
void onEditorDisable();
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
void onNode(S32 node);
|
||||
|
||||
void processTick(const Move*);
|
||||
void interpolateTick(F32 dt);
|
||||
void getCameraTransform(F32* pos,MatrixF* mat);
|
||||
void renderImage(SceneState* state, SceneRenderImage*);
|
||||
|
||||
U32 packUpdate(NetConnection *, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *, BitStream *stream);
|
||||
|
||||
void reset(F32 speed = 1);
|
||||
void pushFront(CameraSpline::Knot *knot);
|
||||
void pushBack(CameraSpline::Knot *knot);
|
||||
void popFront();
|
||||
|
||||
void setPosition(F32 pos);
|
||||
void setTarget(F32 pos);
|
||||
void setState(State s);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
328
engine/game/physicalZone.cc
Executable file
328
engine/game/physicalZone.cc
Executable file
@@ -0,0 +1,328 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "game/physicalZone.h"
|
||||
#include "core/bitStream.h"
|
||||
#include "collision/boxConvex.h"
|
||||
#include "collision/clippedPolyList.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "sceneGraph/sceneState.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(PhysicalZone);
|
||||
|
||||
ConsoleMethod(PhysicalZone, activate, void, 2, 2, "Activate the physical zone's effects.")
|
||||
{
|
||||
if (object->isClientObject())
|
||||
return;
|
||||
|
||||
object->activate();
|
||||
}
|
||||
|
||||
ConsoleMethod(PhysicalZone, deactivate, void, 2, 2, "Deactivate the physical zone's effects.")
|
||||
{
|
||||
if (object->isClientObject())
|
||||
return;
|
||||
|
||||
object->deactivate();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//--------------------------------------
|
||||
//
|
||||
PhysicalZone::PhysicalZone()
|
||||
{
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
mTypeMask |= PhysicalZoneObjectType;
|
||||
|
||||
mVelocityMod = 1.0f;
|
||||
mGravityMod = 1.0f;
|
||||
mAppliedForce.set(0, 0, 0);
|
||||
|
||||
mConvexList = new Convex;
|
||||
mActive = true;
|
||||
}
|
||||
|
||||
PhysicalZone::~PhysicalZone()
|
||||
{
|
||||
delete mConvexList;
|
||||
mConvexList = NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void PhysicalZone::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup("Misc");
|
||||
addField("velocityMod", TypeF32, Offset(mVelocityMod, PhysicalZone));
|
||||
addField("gravityMod", TypeF32, Offset(mGravityMod, PhysicalZone));
|
||||
addField("appliedForce", TypePoint3F, Offset(mAppliedForce, PhysicalZone));
|
||||
addField("polyhedron", TypeTriggerPolyhedron, Offset(mPolyhedron, PhysicalZone));
|
||||
endGroup("Misc");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool PhysicalZone::onAdd()
|
||||
{
|
||||
if(!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
if (mVelocityMod < -40.0f || mVelocityMod > 40.0f) {
|
||||
Con::errorf("PhysicalZone: velocity mod out of range. [-40, 40]");
|
||||
mVelocityMod = mVelocityMod < -40.0f ? -40.0f : 40.0f;
|
||||
}
|
||||
if (mGravityMod < -40.0f || mGravityMod > 40.0f) {
|
||||
Con::errorf("PhysicalZone: GravityMod out of range. [-40, 40]");
|
||||
mGravityMod = mGravityMod < -40.0f ? -40.0f : 40.0f;
|
||||
}
|
||||
static const char* coordString[] = { "x", "y", "z" };
|
||||
F32* p = mAppliedForce;
|
||||
for (U32 i = 0; i < 3; i++) {
|
||||
if (p[i] < -40000.0f || p[i] > 40000.0f) {
|
||||
Con::errorf("PhysicalZone: applied force: %s out of range. [-40000, 40000]", coordString[i]);
|
||||
p[i] = p[i] < -40000.0f ? -40000.0f : 40000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Polyhedron temp = mPolyhedron;
|
||||
setPolyhedron(temp);
|
||||
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PhysicalZone::onRemove()
|
||||
{
|
||||
mConvexList->nukeList();
|
||||
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void PhysicalZone::setTransform(const MatrixF & mat)
|
||||
{
|
||||
Parent::setTransform(mat);
|
||||
|
||||
MatrixF base(true);
|
||||
base.scale(Point3F(1.0/mObjScale.x,
|
||||
1.0/mObjScale.y,
|
||||
1.0/mObjScale.z));
|
||||
base.mul(mWorldToObj);
|
||||
mClippedList.setBaseTransform(base);
|
||||
|
||||
if (isServerObject())
|
||||
setMaskBits(InitialUpdateMask);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
U32 PhysicalZone::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 i;
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if (stream->writeFlag((mask & InitialUpdateMask) != 0)) {
|
||||
// Note that we don't really care about efficiency here, since this is an
|
||||
// edit-only ghost...
|
||||
mathWrite(*stream, mObjToWorld);
|
||||
mathWrite(*stream, mObjScale);
|
||||
|
||||
// Write the polyhedron
|
||||
stream->write(mPolyhedron.pointList.size());
|
||||
for (i = 0; i < mPolyhedron.pointList.size(); i++)
|
||||
mathWrite(*stream, mPolyhedron.pointList[i]);
|
||||
|
||||
stream->write(mPolyhedron.planeList.size());
|
||||
for (i = 0; i < mPolyhedron.planeList.size(); i++)
|
||||
mathWrite(*stream, mPolyhedron.planeList[i]);
|
||||
|
||||
stream->write(mPolyhedron.edgeList.size());
|
||||
for (i = 0; i < mPolyhedron.edgeList.size(); i++) {
|
||||
const Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i];
|
||||
|
||||
stream->write(rEdge.face[0]);
|
||||
stream->write(rEdge.face[1]);
|
||||
stream->write(rEdge.vertex[0]);
|
||||
stream->write(rEdge.vertex[1]);
|
||||
}
|
||||
|
||||
stream->write(mVelocityMod);
|
||||
stream->write(mGravityMod);
|
||||
mathWrite(*stream, mAppliedForce);
|
||||
stream->writeFlag(mActive);
|
||||
} else {
|
||||
stream->writeFlag(mActive);
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void PhysicalZone::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
if (stream->readFlag()) {
|
||||
U32 i, size;
|
||||
MatrixF temp;
|
||||
Point3F tempScale;
|
||||
Polyhedron tempPH;
|
||||
|
||||
// Transform
|
||||
mathRead(*stream, &temp);
|
||||
mathRead(*stream, &tempScale);
|
||||
|
||||
// Read the polyhedron
|
||||
stream->read(&size);
|
||||
tempPH.pointList.setSize(size);
|
||||
for (i = 0; i < tempPH.pointList.size(); i++)
|
||||
mathRead(*stream, &tempPH.pointList[i]);
|
||||
|
||||
stream->read(&size);
|
||||
tempPH.planeList.setSize(size);
|
||||
for (i = 0; i < tempPH.planeList.size(); i++)
|
||||
mathRead(*stream, &tempPH.planeList[i]);
|
||||
|
||||
stream->read(&size);
|
||||
tempPH.edgeList.setSize(size);
|
||||
for (i = 0; i < tempPH.edgeList.size(); i++) {
|
||||
Polyhedron::Edge& rEdge = tempPH.edgeList[i];
|
||||
|
||||
stream->read(&rEdge.face[0]);
|
||||
stream->read(&rEdge.face[1]);
|
||||
stream->read(&rEdge.vertex[0]);
|
||||
stream->read(&rEdge.vertex[1]);
|
||||
}
|
||||
|
||||
stream->read(&mVelocityMod);
|
||||
stream->read(&mGravityMod);
|
||||
mathRead(*stream, &mAppliedForce);
|
||||
|
||||
setPolyhedron(tempPH);
|
||||
setScale(tempScale);
|
||||
setTransform(temp);
|
||||
mActive = stream->readFlag();
|
||||
} else {
|
||||
mActive = stream->readFlag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void PhysicalZone::setPolyhedron(const Polyhedron& rPolyhedron)
|
||||
{
|
||||
mPolyhedron = rPolyhedron;
|
||||
|
||||
if (mPolyhedron.pointList.size() != 0) {
|
||||
mObjBox.min.set(1e10, 1e10, 1e10);
|
||||
mObjBox.max.set(-1e10, -1e10, -1e10);
|
||||
for (U32 i = 0; i < mPolyhedron.pointList.size(); i++) {
|
||||
mObjBox.min.setMin(mPolyhedron.pointList[i]);
|
||||
mObjBox.max.setMax(mPolyhedron.pointList[i]);
|
||||
}
|
||||
} else {
|
||||
mObjBox.min.set(-0.5, -0.5, -0.5);
|
||||
mObjBox.max.set( 0.5, 0.5, 0.5);
|
||||
}
|
||||
|
||||
MatrixF xform = getTransform();
|
||||
setTransform(xform);
|
||||
|
||||
mClippedList.clear();
|
||||
mClippedList.mPlaneList = mPolyhedron.planeList;
|
||||
|
||||
MatrixF base(true);
|
||||
base.scale(Point3F(1.0/mObjScale.x,
|
||||
1.0/mObjScale.y,
|
||||
1.0/mObjScale.z));
|
||||
base.mul(mWorldToObj);
|
||||
|
||||
mClippedList.setBaseTransform(base);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void PhysicalZone::buildConvex(const Box3F& box, Convex* convex)
|
||||
{
|
||||
// These should really come out of a pool
|
||||
mConvexList->collectGarbage();
|
||||
|
||||
Box3F realBox = box;
|
||||
mWorldToObj.mul(realBox);
|
||||
realBox.min.convolveInverse(mObjScale);
|
||||
realBox.max.convolveInverse(mObjScale);
|
||||
|
||||
if (realBox.isOverlapped(getObjBox()) == false)
|
||||
return;
|
||||
|
||||
// Just return a box convex for the entire shape...
|
||||
Convex* cc = 0;
|
||||
CollisionWorkingList& wl = convex->getWorkingList();
|
||||
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
|
||||
if (itr->mConvex->getType() == BoxConvexType &&
|
||||
itr->mConvex->getObject() == this) {
|
||||
cc = itr->mConvex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cc)
|
||||
return;
|
||||
|
||||
// Create a new convex.
|
||||
BoxConvex* cp = new BoxConvex;
|
||||
mConvexList->registerObject(cp);
|
||||
convex->addToWorkingList(cp);
|
||||
cp->init(this);
|
||||
|
||||
mObjBox.getCenter(&cp->mCenter);
|
||||
cp->mSize.x = mObjBox.len_x() / 2.0f;
|
||||
cp->mSize.y = mObjBox.len_y() / 2.0f;
|
||||
cp->mSize.z = mObjBox.len_z() / 2.0f;
|
||||
}
|
||||
|
||||
|
||||
bool PhysicalZone::testObject(SceneObject* enter)
|
||||
{
|
||||
if (mPolyhedron.pointList.size() == 0)
|
||||
return false;
|
||||
|
||||
mClippedList.clear();
|
||||
|
||||
SphereF sphere;
|
||||
sphere.center = (mWorldBox.min + mWorldBox.max) * 0.5;
|
||||
VectorF bv = mWorldBox.max - sphere.center;
|
||||
sphere.radius = bv.len();
|
||||
|
||||
enter->buildPolyList(&mClippedList, mWorldBox, sphere);
|
||||
return mClippedList.isEmpty() == false;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
void PhysicalZone::activate()
|
||||
{
|
||||
AssertFatal(isServerObject(), "Client objects not allowed in ForceFieldInstance::open()");
|
||||
|
||||
if (mActive != true)
|
||||
setMaskBits(ActiveMask);
|
||||
mActive = true;
|
||||
}
|
||||
|
||||
void PhysicalZone::deactivate()
|
||||
{
|
||||
AssertFatal(isServerObject(), "Client objects not allowed in ForceFieldInstance::close()");
|
||||
|
||||
if (mActive != false)
|
||||
setMaskBits(ActiveMask);
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
77
engine/game/physicalZone.h
Executable file
77
engine/game/physicalZone.h
Executable file
@@ -0,0 +1,77 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _H_PHYSICALZONE
|
||||
#define _H_PHYSICALZONE
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "sim/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _EARLYOUTPOLYLIST_H_
|
||||
#include "collision/earlyOutPolyList.h"
|
||||
#endif
|
||||
|
||||
#ifndef _H_TRIGGER
|
||||
#include "game/trigger.h"
|
||||
#endif
|
||||
|
||||
|
||||
class Convex;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
class PhysicalZone : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
enum UpdateMasks {
|
||||
InitialUpdateMask = 1 << 0,
|
||||
ActiveMask = 1 << 1
|
||||
};
|
||||
|
||||
public:
|
||||
void setTransform(const MatrixF &mat);
|
||||
|
||||
protected:
|
||||
F32 mVelocityMod;
|
||||
F32 mGravityMod;
|
||||
Point3F mAppliedForce;
|
||||
|
||||
// Basically ripped from trigger
|
||||
Polyhedron mPolyhedron;
|
||||
EarlyOutPolyList mClippedList;
|
||||
|
||||
bool mActive;
|
||||
|
||||
Convex* mConvexList;
|
||||
void buildConvex(const Box3F& box, Convex* convex);
|
||||
|
||||
public:
|
||||
PhysicalZone();
|
||||
~PhysicalZone();
|
||||
|
||||
F32 getVelocityMod() const { return mVelocityMod; }
|
||||
F32 getGravityMod() const { return mGravityMod; }
|
||||
const Point3F& getForce() const { return mAppliedForce; }
|
||||
|
||||
void setPolyhedron(const Polyhedron&);
|
||||
bool testObject(SceneObject*);
|
||||
|
||||
void activate();
|
||||
void deactivate();
|
||||
bool isActive() const { return mActive; }
|
||||
|
||||
DECLARE_CONOBJECT(PhysicalZone);
|
||||
static void initPersistFields();
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
};
|
||||
|
||||
#endif // _H_PHYSICALZONE
|
||||
|
||||
4264
engine/game/player.cc
Executable file
4264
engine/game/player.cc
Executable file
File diff suppressed because it is too large
Load Diff
523
engine/game/player.h
Executable file
523
engine/game/player.h
Executable file
@@ -0,0 +1,523 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLAYER_H_
|
||||
#define _PLAYER_H_
|
||||
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "game/shapeBase.h"
|
||||
#endif
|
||||
#ifndef _BOXCONVEX_H_
|
||||
#include "collision/boxConvex.h"
|
||||
#endif
|
||||
|
||||
class ParticleEmitter;
|
||||
class ParticleEmitterData;
|
||||
class DecalData;
|
||||
class SplashData;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct PlayerData: public ShapeBaseData {
|
||||
typedef ShapeBaseData Parent;
|
||||
enum Constants {
|
||||
RecoverDelayBits = 7,
|
||||
JumpDelayBits = 7,
|
||||
NumSpineNodes = 6,
|
||||
ImpactBits = 3,
|
||||
NUM_SPLASH_EMITTERS = 3,
|
||||
BUBBLE_EMITTER = 2,
|
||||
};
|
||||
bool renderFirstPerson; ///< Render the player shape in first person
|
||||
|
||||
F32 pickupRadius; ///< Radius around player for items (on server)
|
||||
F32 maxTimeScale; ///< Max timeScale for action animations
|
||||
|
||||
F32 minLookAngle; ///< Lowest angle (radians) the player can look
|
||||
F32 maxLookAngle; ///< Highest angle (radians) the player can look
|
||||
F32 maxFreelookAngle; ///< Max left/right angle the player can look
|
||||
|
||||
/// @name Physics constants
|
||||
/// @{
|
||||
|
||||
F32 runForce; ///< Force used to accelerate player
|
||||
F32 runEnergyDrain; ///< Energy drain/tick
|
||||
F32 minRunEnergy; ///< Minimum energy required to run
|
||||
F32 maxForwardSpeed; ///< Maximum forward speed when running
|
||||
F32 maxBackwardSpeed; ///< Maximum backward speed when running
|
||||
F32 maxSideSpeed; ///< Maximum side speed when running
|
||||
F32 maxUnderwaterForwardSpeed; ///< Maximum underwater forward speed when running
|
||||
F32 maxUnderwaterBackwardSpeed; ///< Maximum underwater backward speed when running
|
||||
F32 maxUnderwaterSideSpeed; ///< Maximum underwater side speed when running
|
||||
|
||||
F32 maxStepHeight; ///< Maximum height the player can step up
|
||||
F32 runSurfaceAngle; ///< Maximum angle from vertical in degrees the player can run up
|
||||
|
||||
F32 horizMaxSpeed; ///< Max speed attainable in the horizontal
|
||||
F32 horizResistSpeed; ///< Speed at which resistence will take place
|
||||
F32 horizResistFactor; ///< Factor of resistence once horizResistSpeed has been reached
|
||||
|
||||
F32 upMaxSpeed; ///< Max vertical speed attainable
|
||||
F32 upResistSpeed; ///< Speed at which resistence will take place
|
||||
F32 upResistFactor; ///< Factor of resistence once upResistSpeed has been reached
|
||||
|
||||
S32 recoverDelay; ///< # tick
|
||||
F32 recoverRunForceScale; ///< RunForce multiplier in recover state
|
||||
|
||||
F32 jumpForce; ///< Force exherted per jump
|
||||
F32 jumpEnergyDrain; ///< Energy drained per jump
|
||||
F32 minJumpEnergy; ///< Minimum energy required to jump
|
||||
F32 minJumpSpeed; ///< Minimum speed needed to jump
|
||||
F32 maxJumpSpeed; ///< Maximum speed before the player can no longer jump
|
||||
F32 jumpSurfaceAngle; ///< Angle from vertical in degrees where the player can jump
|
||||
S32 jumpDelay; ///< Delay time in ticks between jumps
|
||||
/// @}
|
||||
|
||||
/// @name Hitboxes
|
||||
/// @{
|
||||
|
||||
F32 boxHeadPercentage;
|
||||
F32 boxTorsoPercentage;
|
||||
|
||||
S32 boxHeadLeftPercentage;
|
||||
S32 boxHeadRightPercentage;
|
||||
S32 boxHeadBackPercentage;
|
||||
S32 boxHeadFrontPercentage;
|
||||
/// @}
|
||||
|
||||
F32 minImpactSpeed; ///< Minimum impact speed required to apply fall damage
|
||||
|
||||
F32 decalOffset;
|
||||
|
||||
F32 groundImpactMinSpeed; ///< Minimum impact speed required to apply fall damage with the ground
|
||||
VectorF groundImpactShakeFreq; ///< Frequency in each direction for the camera to shake
|
||||
VectorF groundImpactShakeAmp; ///< How much to shake
|
||||
F32 groundImpactShakeDuration; ///< How long to shake
|
||||
F32 groundImpactShakeFalloff; ///< How fast the shake disapates
|
||||
|
||||
/// Zounds!
|
||||
enum Sounds {
|
||||
FootSoft,
|
||||
FootHard,
|
||||
FootMetal,
|
||||
FootSnow,
|
||||
FootShallowSplash,
|
||||
FootWading,
|
||||
FootUnderWater,
|
||||
FootBubbles,
|
||||
MoveBubbles,
|
||||
WaterBreath,
|
||||
ImpactSoft,
|
||||
ImpactHard,
|
||||
ImpactMetal,
|
||||
ImpactSnow,
|
||||
ImpactWaterEasy,
|
||||
ImpactWaterMedium,
|
||||
ImpactWaterHard,
|
||||
ExitWater,
|
||||
MaxSounds
|
||||
};
|
||||
AudioProfile* sound[MaxSounds];
|
||||
|
||||
Point3F boxSize; ///< Width, depth, height
|
||||
|
||||
/// Animation and other data intialized in onAdd
|
||||
struct ActionAnimationDef {
|
||||
const char* name; ///< Sequence name
|
||||
struct Vector {
|
||||
F32 x,y,z;
|
||||
} dir; ///< Default direction
|
||||
};
|
||||
struct ActionAnimation {
|
||||
const char* name; ///< Sequence name
|
||||
S32 sequence; ///< Sequence index
|
||||
VectorF dir; ///< Dir of animation ground transform
|
||||
F32 speed; ///< Speed in m/s
|
||||
bool velocityScale; ///< Scale animation by velocity
|
||||
bool death; ///< Are we dying?
|
||||
};
|
||||
enum {
|
||||
// *** WARNING ***
|
||||
// These enum values are used to index the ActionAnimationList
|
||||
// array instantiated in player.cc
|
||||
// The first five are selected in the move state based on velocity
|
||||
RootAnim,
|
||||
RunForwardAnim,
|
||||
BackBackwardAnim,
|
||||
SideLeftAnim,
|
||||
|
||||
// These are set explicitly based on player actions
|
||||
FallAnim,
|
||||
JumpAnim,
|
||||
StandJumpAnim,
|
||||
LandAnim,
|
||||
|
||||
//
|
||||
NumMoveActionAnims = SideLeftAnim + 1,
|
||||
NumTableActionAnims = LandAnim + 1,
|
||||
NumExtraActionAnims = 512,
|
||||
NumActionAnims = NumTableActionAnims + NumExtraActionAnims,
|
||||
ActionAnimBits = 9,
|
||||
NullAnimation = (1 << ActionAnimBits) - 1
|
||||
};
|
||||
|
||||
static ActionAnimationDef ActionAnimationList[NumTableActionAnims];
|
||||
ActionAnimation actionList[NumActionAnims];
|
||||
U32 actionCount;
|
||||
U32 lookAction;
|
||||
S32 spineNode[NumSpineNodes];
|
||||
S32 pickupDelta; ///< Base off of pcikupRadius
|
||||
F32 runSurfaceCos; ///< Angle from vertical in cos(runSurfaceAngle)
|
||||
F32 jumpSurfaceCos; ///< Angle from vertical in cos(jumpSurfaceAngle)
|
||||
|
||||
enum Impacts {
|
||||
ImpactNone,
|
||||
ImpactNormal,
|
||||
};
|
||||
|
||||
enum Recoil {
|
||||
LightRecoil,
|
||||
MediumRecoil,
|
||||
HeavyRecoil,
|
||||
NumRecoilSequences
|
||||
};
|
||||
S32 recoilSequence[NumRecoilSequences];
|
||||
|
||||
/// @name Particles
|
||||
/// All of the data relating to environmental effects
|
||||
/// @{
|
||||
|
||||
ParticleEmitterData * footPuffEmitter;
|
||||
S32 footPuffID;
|
||||
S32 footPuffNumParts;
|
||||
F32 footPuffRadius;
|
||||
|
||||
DecalData* decalData;
|
||||
S32 decalID;
|
||||
|
||||
ParticleEmitterData * dustEmitter;
|
||||
S32 dustID;
|
||||
|
||||
SplashData* splash;
|
||||
S32 splashId;
|
||||
F32 splashVelocity;
|
||||
F32 splashAngle;
|
||||
F32 splashFreqMod;
|
||||
F32 splashVelEpsilon;
|
||||
F32 bubbleEmitTime;
|
||||
|
||||
F32 medSplashSoundVel;
|
||||
F32 hardSplashSoundVel;
|
||||
F32 exitSplashSoundVel;
|
||||
F32 footSplashHeight;
|
||||
|
||||
ParticleEmitterData* splashEmitterList[NUM_SPLASH_EMITTERS];
|
||||
S32 splashEmitterIDList[NUM_SPLASH_EMITTERS];
|
||||
/// @}
|
||||
|
||||
//
|
||||
DECLARE_CONOBJECT(PlayerData);
|
||||
PlayerData();
|
||||
bool preload(bool server, char errorBuffer[256]);
|
||||
void getGroundInfo(TSShapeInstance*,TSThread*,ActionAnimation*);
|
||||
bool isTableSequence(S32 seq);
|
||||
bool isJumpAction(U32 action);
|
||||
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class Player: public ShapeBase
|
||||
{
|
||||
typedef ShapeBase Parent;
|
||||
protected:
|
||||
/// Bit masks for different types of events
|
||||
enum MaskBits {
|
||||
ActionMask = Parent::NextFreeMask << 0,
|
||||
MoveMask = Parent::NextFreeMask << 1,
|
||||
ImpactMask = Parent::NextFreeMask << 2,
|
||||
NextFreeMask = Parent::NextFreeMask << 3
|
||||
};
|
||||
|
||||
struct Range {
|
||||
Range(F32 _min,F32 _max) {
|
||||
min = _min;
|
||||
max = _max;
|
||||
delta = _max - _min;
|
||||
};
|
||||
F32 min,max;
|
||||
F32 delta;
|
||||
};
|
||||
|
||||
ParticleEmitter *mSplashEmitter[PlayerData::NUM_SPLASH_EMITTERS];
|
||||
F32 mBubbleEmitterTime;
|
||||
|
||||
/// Client interpolation/warp data
|
||||
struct StateDelta {
|
||||
Move move; ///< Last move from server
|
||||
F32 dt; ///< Last interpolation time
|
||||
/// @name Interpolation data
|
||||
/// @{
|
||||
|
||||
Point3F pos;
|
||||
Point3F rot;
|
||||
Point3F head;
|
||||
VectorF posVec;
|
||||
VectorF rotVec;
|
||||
VectorF headVec;
|
||||
/// @}
|
||||
|
||||
/// @name Warp data
|
||||
/// @{
|
||||
|
||||
S32 warpTicks;
|
||||
Point3F warpOffset;
|
||||
Point3F rotOffset;
|
||||
/// @}
|
||||
};
|
||||
StateDelta delta; ///< Used for interpolation on the client. @see StateDelta
|
||||
S32 mPredictionCount; ///< Number of ticks to predict
|
||||
|
||||
// Current pos, vel etc.
|
||||
Point3F mHead; ///< Head rotation, uses only x & z
|
||||
Point3F mRot; ///< Body rotation, uses only z
|
||||
VectorF mVelocity; ///< Velocity
|
||||
Point3F mAnchorPoint; ///< Pos compression anchor
|
||||
static F32 mGravity; ///< Gravity
|
||||
S32 mImpactSound;
|
||||
|
||||
S32 mMountPending; ///< mMountPending suppresses tickDelay countdown so players will sit until
|
||||
///< their mount, or another animation, comes through (or 13 seconds elapses).
|
||||
|
||||
/// Main player state
|
||||
enum ActionState {
|
||||
NullState,
|
||||
MoveState,
|
||||
RecoverState,
|
||||
NumStateBits = 3
|
||||
};
|
||||
ActionState mState; ///< What is the player doing? @see ActionState
|
||||
bool mFalling; ///< Falling in mid-air?
|
||||
S32 mJumpDelay; ///< Delay till next jump
|
||||
S32 mContactTimer; ///< Ticks since last contact
|
||||
|
||||
Point3F mJumpSurfaceNormal; ///< Normal of the surface the player last jumped on
|
||||
U32 mJumpSurfaceLastContact; ///< How long it's been since the player landed (ticks)
|
||||
F32 mWeaponBackFraction; ///< Amount to slide the weapon back (if it's up against something)
|
||||
|
||||
AUDIOHANDLE mMoveBubbleHandle; ///< Audio handle for moving bubbles
|
||||
AUDIOHANDLE mWaterBreathHandle; ///< Audio handle for underwater breath
|
||||
|
||||
SimObjectPtr<ShapeBase> mControlObject; ///< Controlling object
|
||||
|
||||
/// @name Animation threads & data
|
||||
/// @{
|
||||
|
||||
struct ActionAnimation {
|
||||
U32 action;
|
||||
TSThread* thread;
|
||||
S32 delayTicks; // before picking another.
|
||||
bool forward;
|
||||
bool firstPerson;
|
||||
bool waitForEnd;
|
||||
bool holdAtEnd;
|
||||
bool animateOnServer;
|
||||
bool atEnd;
|
||||
} mActionAnimation;
|
||||
|
||||
struct ArmAnimation {
|
||||
U32 action;
|
||||
TSThread* thread;
|
||||
} mArmAnimation;
|
||||
TSThread* mArmThread;
|
||||
|
||||
TSThread* mHeadVThread;
|
||||
TSThread* mHeadHThread;
|
||||
TSThread* mRecoilThread;
|
||||
static Range mArmRange;
|
||||
static Range mHeadVRange;
|
||||
static Range mHeadHRange;
|
||||
/// @}
|
||||
|
||||
bool mInMissionArea; ///< Are we in the mission area?
|
||||
//
|
||||
U32 mRecoverTicks; ///< same as recoverTicks in the player datablock
|
||||
U32 mReversePending;
|
||||
|
||||
bool inLiquid; ///< Are we in liquid?
|
||||
//
|
||||
PlayerData* mDataBlock; ///< MMmmmmm...datablock...
|
||||
|
||||
Point3F mLastPos; ///< Holds the last position for physics updates
|
||||
Point3F mLastWaterPos; ///< Same as mLastPos, but for water
|
||||
|
||||
struct ContactInfo {
|
||||
bool contacted, jump, run;
|
||||
VectorF contactNormal;
|
||||
void clear() {contacted=jump=run=false; contactNormal.set(1,1,1);}
|
||||
ContactInfo() {clear();}
|
||||
} mContactInfo;
|
||||
|
||||
struct Death {
|
||||
F32 lastPos;
|
||||
Point3F posAdd;
|
||||
VectorF rotate;
|
||||
VectorF curNormal;
|
||||
F32 curSink;
|
||||
void clear() {dMemset(this, 0, sizeof(*this)); initFall();}
|
||||
VectorF getPosAdd() {VectorF ret(posAdd); posAdd.set(0,0,0); return ret;}
|
||||
bool haveVelocity() {return posAdd.x != 0 || posAdd.y != 0;}
|
||||
void initFall() {curNormal.set(0,0,1); curSink = 0;}
|
||||
Death() {clear();}
|
||||
MatrixF* fallToGround(F32 adjust, const Point3F& pos, F32 zrot, F32 boxRad);
|
||||
} mDeath;
|
||||
|
||||
// New collision
|
||||
public:
|
||||
OrthoBoxConvex mConvex;
|
||||
Box3F mWorkingQueryBox;
|
||||
|
||||
protected:
|
||||
void setState(ActionState state, U32 ticks=0);
|
||||
void updateState();
|
||||
|
||||
///Update the movement
|
||||
void updateMove(const Move *move);
|
||||
///Interpolate movement
|
||||
bool updatePos(const F32 travelTime = TickSec);
|
||||
///Update head animation
|
||||
void updateLookAnimation();
|
||||
///Update other animations
|
||||
void updateAnimation(F32 dt);
|
||||
void updateAnimationTree(bool firstPerson);
|
||||
bool step(Point3F *pos,F32 *maxStep,F32 time);
|
||||
///See if the player is still in the mission area
|
||||
void checkMissionArea();
|
||||
|
||||
virtual bool setArmThread(U32 action);
|
||||
virtual void setActionThread(U32 action,bool forward,bool hold = false,bool wait = false,bool fsp = false, bool forceSet = false);
|
||||
virtual void updateActionThread();
|
||||
virtual void pickActionAnimation();
|
||||
void onUnmount(ShapeBase* obj,S32 node);
|
||||
|
||||
void setPosition(const Point3F& pos,const Point3F& viewRot);
|
||||
void setRenderPosition(const Point3F& pos,const Point3F& viewRot,F32 dt=-1);
|
||||
void findContact(bool* run,bool* jump,VectorF* contactNormal);
|
||||
virtual void onImageRecoil(U32 imageSlot,ShapeBaseImageData::StateData::RecoilState);
|
||||
virtual void updateDamageLevel();
|
||||
virtual void updateDamageState();
|
||||
/// Set which client is controlling this player
|
||||
void setControllingClient(GameConnection* client);
|
||||
|
||||
void calcClassRenderData();
|
||||
/// Render the mounted image, usually the weapon
|
||||
void renderMountedImage(SceneState* state, ShapeImageRenderImage* image);
|
||||
/// Render the player
|
||||
void renderImage(SceneState *state, SceneRenderImage *image);
|
||||
/// Play a footstep sound
|
||||
void playFootstepSound(bool triggeredLeft, S32 sound);
|
||||
/// Play an impact sound
|
||||
void playImpactSound();
|
||||
|
||||
/// Are we in the process of dying?
|
||||
bool inDeathAnim();
|
||||
F32 deathDelta(Point3F &delta);
|
||||
void updateDeathOffsets();
|
||||
bool inSittingAnim();
|
||||
|
||||
/// @name Water
|
||||
/// @{
|
||||
|
||||
void updateSplash(); ///< Update the splash effect
|
||||
void updateFroth( F32 dt ); ///< Update any froth
|
||||
void updateWaterSounds( F32 dt ); ///< Update water sounds
|
||||
bool pointInWater( Point3F &point ); ///< Tests to see if a point is in water
|
||||
void createSplash( Point3F &pos, F32 speed ); ///< Creates a splash
|
||||
bool collidingWithWater( Point3F &waterHeight ); ///< Are we collising with water?
|
||||
/// @}
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(Player);
|
||||
|
||||
Player();
|
||||
~Player();
|
||||
static void consoleInit();
|
||||
|
||||
/// @name Transforms
|
||||
/// Transforms are all in object space
|
||||
/// @{
|
||||
|
||||
void setTransform(const MatrixF &mat);
|
||||
void getEyeTransform(MatrixF* mat);
|
||||
void getRenderEyeTransform(MatrixF* mat);
|
||||
void getCameraParameters(F32 *min, F32 *max, Point3F *offset, MatrixF *rot);
|
||||
void getMuzzleTransform(U32 imageSlot,MatrixF* mat);
|
||||
void getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat);
|
||||
/// @}
|
||||
|
||||
Point3F getVelocity() const;
|
||||
void setVelocity(const VectorF& vel);
|
||||
/// Apply an impulse at the given point, with magnitude/direction of vec
|
||||
void applyImpulse(const Point3F& pos,const VectorF& vec);
|
||||
/// Get the rotation of the player
|
||||
const Point3F& getRotation() { return mRot; }
|
||||
/// Get the rotation of the head of the player
|
||||
const Point3F& getHeadRotation() { return mHead; }
|
||||
void getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad);
|
||||
|
||||
bool canJump(); ///< Can the player jump?
|
||||
bool haveContact() {return !mContactTimer;} ///< Is it in contact with something
|
||||
void getMuzzlePointAI(U32 imageSlot, Point3F* point);
|
||||
/// duh
|
||||
float getMaxForwardVelocity() { return (mDataBlock != NULL ? mDataBlock->maxForwardSpeed : 0); }
|
||||
|
||||
virtual bool isDisplacable() const;
|
||||
virtual Point3F getMomentum() const;
|
||||
virtual void setMomentum(const Point3F &momentum);
|
||||
virtual F32 getMass() const;
|
||||
virtual bool displaceObject(const Point3F& displaceVector);
|
||||
virtual bool getAIMove(Move*);
|
||||
|
||||
bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here?
|
||||
|
||||
//
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock(GameBaseData* dptr);
|
||||
void onScaleChanged();
|
||||
Box3F mScaledBox;
|
||||
|
||||
// Animation
|
||||
const char* getStateName();
|
||||
bool setActionThread(const char* sequence,bool hold,bool wait,bool fsp = false);
|
||||
bool setArmThread(const char* sequence);
|
||||
|
||||
// Object control
|
||||
void setControlObject(ShapeBase *obj);
|
||||
ShapeBase* getControlObject();
|
||||
|
||||
//
|
||||
void updateWorkingCollisionSet();
|
||||
void processTick(const Move *move);
|
||||
void interpolateTick(F32 delta);
|
||||
void advanceTime(F32 dt);
|
||||
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
|
||||
bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
|
||||
void buildConvex(const Box3F& box, Convex* convex);
|
||||
bool isControlObject();
|
||||
|
||||
void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *);
|
||||
void writePacketData(GameConnection *conn, BitStream *stream);
|
||||
void readPacketData (GameConnection *conn, BitStream *stream);
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user