mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-10 14:14:33 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
582
Engine/source/T3D/aiClient.cpp
Normal file
582
Engine/source/T3D/aiClient.cpp
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/aiClient.h"
|
||||
|
||||
#include "math/mMatrix.h"
|
||||
#include "T3D/shapeBase.h"
|
||||
#include "T3D/player.h"
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#include "console/consoleInternal.h"
|
||||
|
||||
|
||||
IMPLEMENT_CONOBJECT( AIClient );
|
||||
|
||||
ConsoleDocClass( AIClient,
|
||||
"@brief Simulated client driven by AI commands.\n\n"
|
||||
|
||||
"This object is derived from the AIConnection class. It introduces its own Player object "
|
||||
"to solidify the purpose of this class: Simulated client connecting as a player\n\n"
|
||||
"To get more specific, if you want a strong alternative to AIPlayer (and wish to make use "
|
||||
"of the AIConnection structure), consider AIClient. AIClient inherits from AIConnection, "
|
||||
"contains quite a bit of functionality you will find in AIPlayer, and has its own Player "
|
||||
"object.\n\n"
|
||||
|
||||
"@note This is a legacy class, which you are discouraged from using as it will "
|
||||
"most likely be deprecated in a future version. For now it has been left in for "
|
||||
"backwards compatibility with TGE and the old RTS Kit. Use AIPlayer instead.\n\n"
|
||||
|
||||
"@see AIPlayer, AIConnection\n\n"
|
||||
|
||||
"@ingroup AI\n"
|
||||
"@ingroup Networking\n"
|
||||
);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
U32 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 = dynamic_cast<Player *>( getControlObject() );
|
||||
|
||||
// We got a something controling us?
|
||||
if( !mPlayer )
|
||||
return 1;
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
F32 moveSpeed = mMoveSpeed;
|
||||
|
||||
switch( mMoveMode ) {
|
||||
|
||||
case ModeStop:
|
||||
return 1; // 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( !mIsZero( xDiff ) || !mIsZero( yDiff ) ) {
|
||||
// use the cur yaw between -Pi and Pi
|
||||
while( curYaw > M_2PI_F )
|
||||
curYaw -= M_2PI_F;
|
||||
while( curYaw < -M_2PI_F )
|
||||
curYaw += M_2PI_F;
|
||||
|
||||
// find the new yaw
|
||||
newYaw = mAtan2( xDiff, yDiff );
|
||||
|
||||
// find the yaw diff
|
||||
F32 yawDiff = newYaw - curYaw;
|
||||
|
||||
// make it between 0 and 2PI
|
||||
if( yawDiff < 0.0f )
|
||||
yawDiff += M_2PI_F;
|
||||
else if( yawDiff >= M_2PI_F )
|
||||
yawDiff -= M_2PI_F;
|
||||
|
||||
// now make sure we take the short way around the circle
|
||||
if( yawDiff > M_2PI_F )
|
||||
yawDiff -= M_2PI_F;
|
||||
else if( yawDiff < -M_2PI_F )
|
||||
yawDiff += M_2PI_F;
|
||||
|
||||
mMove.yaw = yawDiff;
|
||||
|
||||
// set up the movement matrix
|
||||
moveMatrix.set( EulerF( 0.0f, 0.0f, newYaw ) );
|
||||
}
|
||||
else
|
||||
moveMatrix.set( EulerF( 0.0f, 0.0f, curYaw ) );
|
||||
|
||||
// next do pitch
|
||||
F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len();
|
||||
|
||||
if( !mIsZero( horzDist ) ) {
|
||||
//we shoot from the gun, not the eye...
|
||||
F32 vertDist = mAimLocation.z;
|
||||
|
||||
newPitch = mAtan2( horzDist, vertDist ) - ( M_2PI_F / 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 ) ) && ( !mIsZero( mMoveSpeed ) ) )
|
||||
{
|
||||
if( mIsZero( xDiff ) )
|
||||
mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed );
|
||||
else if( mIsZero( 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.0f ), &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;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, 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( AIClient, setMoveSpeed, void, 3, 3, "ai.setMoveSpeed( float );" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
ai->setMoveSpeed( dAtof( argv[2] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all AI movement, halt!
|
||||
*/
|
||||
ConsoleMethod( AIClient, 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( AIClient, 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( AIClient, 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( AIClient, 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( AIClient, 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( AIClient, 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( AIClient, getTargetObject, S32, 2, 2, "ai.getTargetObject();" ) {
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
|
||||
return ai->getTargetObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the bot the mission is cycling
|
||||
*/
|
||||
ConsoleMethod( AIClient, missionCycleCleanup, void, 2, 2, "ai.missionCycleCleanup();" ) {
|
||||
AIClient *ai = static_cast<AIClient*>( object );
|
||||
ai->missionCycleCleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the AI to run mode
|
||||
*/
|
||||
ConsoleMethod( AIClient, 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( AIClient, 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, "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( AIClient, moveForward, void, 2, 2, "ai.moveForward();" ) {
|
||||
|
||||
AIClient *ai = static_cast<AIClient *>( object );
|
||||
ShapeBase *player = dynamic_cast<ShapeBase*>(ai->getControlObject());
|
||||
Point3F location;
|
||||
MatrixF const &myTransform = player->getTransform();
|
||||
myTransform.getColumn( 3, &location );
|
||||
|
||||
location.y += 100.0f;
|
||||
|
||||
ai->setMoveDestination( location );
|
||||
} // *** /TEST FXN
|
||||
114
Engine/source/T3D/aiClient.h
Normal file
114
Engine/source/T3D/aiClient.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AICLIENT_H_
|
||||
#define _AICLIENT_H_
|
||||
|
||||
#ifndef _AICONNECTION_H_
|
||||
#include "T3D/aiConnection.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
class ShapeBase;
|
||||
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();
|
||||
|
||||
U32 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
|
||||
258
Engine/source/T3D/aiConnection.cpp
Normal file
258
Engine/source/T3D/aiConnection.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/aiConnection.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT( AIConnection );
|
||||
|
||||
ConsoleDocClass( AIConnection,
|
||||
"@brief Special client connection driven by an AI, rather than a human.\n\n"
|
||||
|
||||
"Unlike other net connections, AIConnection is intended to run unmanned. Rather than "
|
||||
"gathering input from a human using a device, move events, triggers, and look events "
|
||||
"are driven through functions like AIConnection::setMove.\n\n"
|
||||
|
||||
"In addition to having its own set of functions for managing client move events, "
|
||||
"a member variable inherited by GameConnection is toggle: mAIControlled. This is useful "
|
||||
"for a server to determine if a connection is AI driven via the function GameConnection::isAIControlled\n\n"
|
||||
|
||||
"AIConnection is an alternative to manually creating an AI driven game object. When you "
|
||||
"want the server to manage AI, you will create a specific one from script using a "
|
||||
"class like AIPlayer. If you do not want the server managing the AI and wish to simulate "
|
||||
"a complete client connection, you will use AIConnection\n\n."
|
||||
|
||||
"To get more specific, if you want a strong alternative to AIPlayer (and wish to make use "
|
||||
"of the AIConnection structure), consider AIClient. AIClient inherits from AIConnection, "
|
||||
"contains quite a bit of functionality you will find in AIPlayer, and has its own Player "
|
||||
"object.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Create a new AI client connection\n"
|
||||
"%botConnection = aiConnect(\"MasterBlaster\" @ %i, -1, 0.5, false, \"SDF\", 1.0);\n\n"
|
||||
"// In another area of the code, you can locate this and any other AIConnections\n"
|
||||
"// using the isAIControlled function\n"
|
||||
"for(%i = 0; %i < ClientGroup.getCount(); %i++)\n"
|
||||
"{\n"
|
||||
" %client = ClientGroup.getObject(%i);\n"
|
||||
" if(%client.isAIControlled())\n"
|
||||
" {\n"
|
||||
" // React to this AI controlled client\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@note This is a legacy class, which you are discouraged from using as it will "
|
||||
"most likely be deprecated in a future version. For now it has been left in for "
|
||||
"backwards compatibility with TGE and the old RTS Kit. Use GameConnection "
|
||||
"and AIPlayer instead.\n\n"
|
||||
|
||||
"@see GameConnection, NetConnection, AIClient\n\n"
|
||||
|
||||
"@ingroup AI\n"
|
||||
"@ingroup Networking\n"
|
||||
);
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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.
|
||||
*/
|
||||
U32 AIConnection::getMoveList( Move **lngMove, U32 *numMoves )
|
||||
{
|
||||
*numMoves = 1;
|
||||
*lngMove = &mMove;
|
||||
return *numMoves;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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_2PI_F, M_2PI_F);
|
||||
return (a < 0) ? a + M_2PI_F : a;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Construct and connect an AI connection object
|
||||
ConsoleFunction(aiConnect, S32 , 2, 20, "(...)"
|
||||
"@brief Creates a new AIConnection, and passes arguments to its onConnect script callback.\n\n"
|
||||
"@returns The newly created AIConnection\n"
|
||||
"@see GameConnection for parameter information\n"
|
||||
"@ingroup AI")
|
||||
{
|
||||
// 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";
|
||||
}
|
||||
56
Engine/source/T3D/aiConnection.h
Normal file
56
Engine/source/T3D/aiConnection.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AICONNECTION_H_
|
||||
#define _AICONNECTION_H_
|
||||
|
||||
#ifndef _GAMECONNECTION_H_
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#endif
|
||||
#ifndef _MOVEMANAGER_H_
|
||||
#include "T3D/gameBase/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 U32 getMoveList(Move **,U32 *numMoves);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
602
Engine/source/T3D/aiPlayer.cpp
Normal file
602
Engine/source/T3D/aiPlayer.cpp
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/aiPlayer.h"
|
||||
|
||||
#include "console/consoleInternal.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(AIPlayer);
|
||||
|
||||
ConsoleDocClass( AIPlayer,
|
||||
"@brief A Player object not controlled by conventional input, but by an AI engine.\n\n"
|
||||
|
||||
"The AIPlayer provides a Player object that may be controlled from script. You control "
|
||||
"where the player moves and how fast. You may also set where the AIPlayer is aiming at "
|
||||
"-- either a location or another game object.\n\n"
|
||||
|
||||
"The AIPlayer class does not have a datablock of its own. It makes use of the PlayerData "
|
||||
"datablock to define how it looks, etc. As the AIPlayer is an extension of the Player class "
|
||||
"it can mount objects and fire weapons, or mount vehicles and drive them.\n\n"
|
||||
|
||||
"While the PlayerData datablock is used, there are a number of additional callbacks that are "
|
||||
"implemented by AIPlayer on the datablock. These are listed here:\n\n"
|
||||
|
||||
"void onReachDestination(AIPlayer obj) \n"
|
||||
"Called when the player has reached its set destination using the setMoveDestination() method. "
|
||||
"The actual point at which this callback is called is when the AIPlayer is within the mMoveTolerance "
|
||||
"of the defined destination.\n\n"
|
||||
|
||||
"void onMoveStuck(AIPlayer obj) \n"
|
||||
"While in motion, if an AIPlayer has moved less than moveStuckTolerance within a single tick, this "
|
||||
"callback is called. From here you could choose an alternate destination to get the AIPlayer moving "
|
||||
"again.\n\n"
|
||||
|
||||
"void onTargetEnterLOS(AIPlayer obj) \n"
|
||||
"When an object is being aimed at (following a call to setAimObject()) and the targeted object enters "
|
||||
"the AIPlayer's line of sight, this callback is called. The LOS test is a ray from the AIPlayer's eye "
|
||||
"position to the center of the target's bounding box. The LOS ray test only checks against interiors, "
|
||||
"statis shapes, and terrain.\n\n"
|
||||
|
||||
"void onTargetExitLOS(AIPlayer obj) \n"
|
||||
"When an object is being aimed at (following a call to setAimObject()) and the targeted object leaves "
|
||||
"the AIPlayer's line of sight, this callback is called. The LOS test is a ray from the AIPlayer's eye "
|
||||
"position to the center of the target's bounding box. The LOS ray test only checks against interiors, "
|
||||
"statis shapes, and terrain.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Create the demo player object\n"
|
||||
"%player = new AiPlayer()\n"
|
||||
"{\n"
|
||||
" dataBlock = DemoPlayer;\n"
|
||||
" path = \"\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@see Player for a list of all inherited functions, variables, and base description\n"
|
||||
|
||||
"@ingroup AI\n"
|
||||
"@ingroup gameObjects\n");
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
AIPlayer::AIPlayer()
|
||||
{
|
||||
mMoveDestination.set( 0.0f, 0.0f, 0.0f );
|
||||
mMoveSpeed = 1.0f;
|
||||
mMoveTolerance = 0.25f;
|
||||
mMoveStuckTolerance = 0.01f;
|
||||
mMoveStuckTestDelay = 30;
|
||||
mMoveStuckTestCountdown = 0;
|
||||
mMoveSlowdown = true;
|
||||
mMoveState = ModeStop;
|
||||
|
||||
mAimObject = 0;
|
||||
mAimLocationSet = false;
|
||||
mTargetInLOS = false;
|
||||
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);
|
||||
|
||||
mIsAiControlled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
AIPlayer::~AIPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void AIPlayer::initPersistFields()
|
||||
{
|
||||
addGroup( "AI" );
|
||||
|
||||
addField( "mMoveTolerance", TypeF32, Offset( mMoveTolerance, AIPlayer ),
|
||||
"@brief Distance from destination before stopping.\n\n"
|
||||
"When the AIPlayer is moving to a given destination it will move to within "
|
||||
"this distance of the destination and then stop. By providing this tolerance "
|
||||
"it helps the AIPlayer from never reaching its destination due to minor obstacles, "
|
||||
"rounding errors on its position calculation, etc. By default it is set to 0.25.\n");
|
||||
|
||||
addField( "moveStuckTolerance", TypeF32, Offset( mMoveStuckTolerance, AIPlayer ),
|
||||
"@brief Distance tolerance on stuck check.\n\n"
|
||||
"When the AIPlayer is moving to a given destination, if it ever moves less than "
|
||||
"this tolerance during a single tick, the AIPlayer is considered stuck. At this point "
|
||||
"the onMoveStuck() callback is called on the datablock.\n");
|
||||
|
||||
addField( "moveStuckTestDelay", TypeS32, Offset( mMoveStuckTestDelay, AIPlayer ),
|
||||
"@brief The number of ticks to wait before testing if the AIPlayer is stuck.\n\n"
|
||||
"When the AIPlayer is asked to move, this property is the number of ticks to wait "
|
||||
"before the AIPlayer starts to check if it is stuck. This delay allows the AIPlayer "
|
||||
"to accelerate to full speed without its initial slow start being considered as stuck.\n"
|
||||
"@note Set to zero to have the stuck test start immediately.\n");
|
||||
|
||||
endGroup( "AI" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool AIPlayer::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// Use the eye as the current position (see getAIMove)
|
||||
MatrixF eye;
|
||||
getEyeTransform(&eye);
|
||||
mLastLocation = eye.getPosition();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
mMoveStuckTestCountdown = mMoveStuckTestDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 != ModeStop)
|
||||
{
|
||||
// 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 (!mIsZero(xDiff) || !mIsZero(yDiff))
|
||||
{
|
||||
// First do Yaw
|
||||
// use the cur yaw between -Pi and Pi
|
||||
F32 curYaw = rotation.z;
|
||||
while (curYaw > M_2PI_F)
|
||||
curYaw -= M_2PI_F;
|
||||
while (curYaw < -M_2PI_F)
|
||||
curYaw += M_2PI_F;
|
||||
|
||||
// find the yaw offset
|
||||
F32 newYaw = mAtan2( xDiff, yDiff );
|
||||
F32 yawDiff = newYaw - curYaw;
|
||||
|
||||
// make it between 0 and 2PI
|
||||
if( yawDiff < 0.0f )
|
||||
yawDiff += M_2PI_F;
|
||||
else if( yawDiff >= M_2PI_F )
|
||||
yawDiff -= M_2PI_F;
|
||||
|
||||
// now make sure we take the short way around the circle
|
||||
if( yawDiff > M_PI_F )
|
||||
yawDiff -= M_2PI_F;
|
||||
else if( yawDiff < -M_PI_F )
|
||||
yawDiff += M_2PI_F;
|
||||
|
||||
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 = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f );
|
||||
if (mFabs(newPitch) > 0.01f)
|
||||
{
|
||||
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 != ModeStop)
|
||||
{
|
||||
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 (mIsZero(xDiff))
|
||||
movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
|
||||
else
|
||||
if (mIsZero(yDiff))
|
||||
movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f;
|
||||
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.0f : 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 value = mFabs(xDiff / yDiff);
|
||||
movePtr->x = (location.x > mMoveDestination.x) ? -value : value;
|
||||
movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
// Rotate the move into object space (this really only needs
|
||||
// a 2D matrix)
|
||||
Point3F newMove;
|
||||
MatrixF moveMatrix;
|
||||
moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw)));
|
||||
moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &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.0f;
|
||||
if (dist < maxDist)
|
||||
speed *= dist / maxDist;
|
||||
movePtr->x *= speed;
|
||||
movePtr->y *= speed;
|
||||
|
||||
mMoveState = ModeSlowing;
|
||||
}
|
||||
else
|
||||
{
|
||||
movePtr->x *= mMoveSpeed;
|
||||
movePtr->y *= mMoveSpeed;
|
||||
|
||||
mMoveState = ModeMove;
|
||||
}
|
||||
|
||||
if (mMoveStuckTestCountdown > 0)
|
||||
--mMoveStuckTestCountdown;
|
||||
else
|
||||
{
|
||||
// We should check to see if we are stuck...
|
||||
F32 locationDelta = (location - mLastLocation).len();
|
||||
if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled)
|
||||
{
|
||||
// If we are slowing down, then it's likely that our location delta will be less than
|
||||
// our move stuck tolerance. Because we can be both slowing and stuck
|
||||
// we should TRY to check if we've moved. This could use better detection.
|
||||
if ( mMoveState != ModeSlowing || locationDelta == 0 )
|
||||
{
|
||||
mMoveState = ModeStuck;
|
||||
throwCallback("onMoveStuck");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
mLastLocation = location;
|
||||
|
||||
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(), name, getIdString());
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Console Functions
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( AIPlayer, stop, void, ( ),,
|
||||
"@brief Tells the AIPlayer to stop moving.\n\n")
|
||||
{
|
||||
object->stopMove();
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, clearAim, void, ( ),,
|
||||
"@brief Use this to stop aiming at an object or a point.\n\n"
|
||||
|
||||
"@see setAimLocation()\n"
|
||||
"@see setAimObject()\n")
|
||||
{
|
||||
object->clearAim();
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, setMoveSpeed, void, ( F32 speed ),,
|
||||
"@brief Sets the move speed for an AI object.\n\n"
|
||||
|
||||
"@param speed A speed multiplier between 0.0 and 1.0. "
|
||||
"This is multiplied by the AIPlayer's base movement rates (as defined in "
|
||||
"its PlayerData datablock)\n\n"
|
||||
|
||||
"@see getMoveDestination()\n")
|
||||
{
|
||||
object->setMoveSpeed(speed);
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, setMoveDestination, void, ( Point3F goal, bool slowDown ), ( true ),
|
||||
"@brief Tells the AI to move to the location provided\n\n"
|
||||
|
||||
"@param goal Coordinates in world space representing location to move to.\n"
|
||||
"@param slowDown A boolean value. If set to true, the bot will slow down "
|
||||
"when it gets within 5-meters of its move destination. If false, the bot "
|
||||
"will stop abruptly when it reaches the move destination. By default, this is true.\n\n"
|
||||
|
||||
"@note Upon reaching a move destination, the bot will clear its move destination and "
|
||||
"calls to getMoveDestination will return \"0 0 0\"."
|
||||
|
||||
"@see getMoveDestination()\n")
|
||||
{
|
||||
object->setMoveDestination( goal, slowDown);
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, getMoveDestination, Point3F, (),,
|
||||
"@brief Get the AIPlayer's current destination.\n\n"
|
||||
|
||||
"@return Returns a point containing the \"x y z\" position "
|
||||
"of the AIPlayer's current move destination. If no move destination "
|
||||
"has yet been set, this returns \"0 0 0\"."
|
||||
|
||||
"@see setMoveDestination()\n")
|
||||
{
|
||||
return object->getMoveDestination();
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, setAimLocation, void, ( Point3F target ),,
|
||||
"@brief Tells the AIPlayer to aim at the location provided.\n\n"
|
||||
|
||||
"@param target An \"x y z\" position in the game world to target.\n\n"
|
||||
|
||||
"@see getAimLocation()\n")
|
||||
{
|
||||
object->setAimLocation(target);
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, getAimLocation, Point3F, (),,
|
||||
"@brief Returns the point the AIPlayer is aiming at.\n\n"
|
||||
|
||||
"This will reflect the position set by setAimLocation(), "
|
||||
"or the position of the object that the bot is now aiming at. "
|
||||
"If the bot is not aiming at anything, this value will "
|
||||
"change to whatever point the bot's current line-of-sight intercepts."
|
||||
|
||||
"@return World space coordinates of the object AI is aiming at. Formatted as \"X Y Z\".\n\n"
|
||||
|
||||
"@see setAimLocation()\n"
|
||||
"@see setAimObject()\n")
|
||||
{
|
||||
return object->getAimLocation();
|
||||
}
|
||||
|
||||
ConsoleDocFragment _setAimObject(
|
||||
"@brief Sets the AIPlayer's target object. May optionally set an offset from target location\n\n"
|
||||
|
||||
"@param targetObject The object to target\n"
|
||||
"@param offset Optional three-element offset vector which will be added to the position of the aim object.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Without an offset\n"
|
||||
"%ai.setAimObject(%target);\n\n"
|
||||
"// With an offset\n"
|
||||
"// Cause our AI object to aim at the target\n"
|
||||
"// offset (0, 0, 1) so you don't aim at the target's feet\n"
|
||||
"%ai.setAimObject(%target, \"0 0 1\");\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@see getAimLocation()\n"
|
||||
"@see getAimObject()\n"
|
||||
"@see clearAim()\n",
|
||||
|
||||
"AIPlayer",
|
||||
"void setAimObject(GameBase targetObject, Point3F offset);"
|
||||
);
|
||||
ConsoleMethod( AIPlayer, setAimObject, void, 3, 4, "( GameBase obj, [Point3F offset] )"
|
||||
"Sets the bot's target object. Optionally set an offset from target location."
|
||||
"@hide")
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
DefineEngineMethod( AIPlayer, getAimObject, S32, (),,
|
||||
"@brief Gets the object the AIPlayer is targeting.\n\n"
|
||||
|
||||
"@return Returns -1 if no object is being aimed at, "
|
||||
"or the SimObjectID of the object the AIPlayer is aiming at.\n\n"
|
||||
|
||||
"@see setAimObject()\n")
|
||||
{
|
||||
GameBase* obj = object->getAimObject();
|
||||
return obj? obj->getId(): -1;
|
||||
}
|
||||
94
Engine/source/T3D/aiPlayer.h
Normal file
94
Engine/source/T3D/aiPlayer.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AIPLAYER_H_
|
||||
#define _AIPLAYER_H_
|
||||
|
||||
#ifndef _PLAYER_H_
|
||||
#include "T3D/player.h"
|
||||
#endif
|
||||
|
||||
|
||||
class AIPlayer : public Player {
|
||||
|
||||
typedef Player Parent;
|
||||
|
||||
public:
|
||||
enum MoveState {
|
||||
ModeStop, // AI has stopped moving.
|
||||
ModeMove, // AI is currently moving.
|
||||
ModeStuck, // AI is stuck, but wants to move.
|
||||
ModeSlowing, // AI is slowing down as it reaches it's destination.
|
||||
};
|
||||
|
||||
private:
|
||||
MoveState mMoveState;
|
||||
F32 mMoveSpeed;
|
||||
F32 mMoveTolerance; // Distance from destination before we stop
|
||||
Point3F mMoveDestination; // Destination for movement
|
||||
Point3F mLastLocation; // For stuck check
|
||||
F32 mMoveStuckTolerance; // Distance tolerance on stuck check
|
||||
S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck
|
||||
S32 mMoveStuckTestCountdown; // The current countdown until at AI starts to check if it is stuck
|
||||
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();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
bool onAdd();
|
||||
|
||||
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
|
||||
1978
Engine/source/T3D/camera.cpp
Normal file
1978
Engine/source/T3D/camera.cpp
Normal file
File diff suppressed because it is too large
Load diff
243
Engine/source/T3D/camera.h
Normal file
243
Engine/source/T3D/camera.h
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CAMERA_H_
|
||||
#define _CAMERA_H_
|
||||
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "T3D/shapeBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DYNAMIC_CONSOLETYPES_H_
|
||||
#include "console/dynamicTypes.h"
|
||||
#endif
|
||||
|
||||
|
||||
class CameraData: public ShapeBaseData
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ShapeBaseData Parent;
|
||||
|
||||
// ShapeBaseData.
|
||||
DECLARE_CONOBJECT( CameraData );
|
||||
DECLARE_CATEGORY( "Game" );
|
||||
DECLARE_DESCRIPTION( "A datablock that describes a camera." );
|
||||
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
|
||||
/// Implements a basic camera object.
|
||||
class Camera: public ShapeBase
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ShapeBase Parent;
|
||||
|
||||
/// Movement behavior type for camera.
|
||||
enum CameraMotionMode
|
||||
{
|
||||
StationaryMode = 0,
|
||||
|
||||
FreeRotateMode,
|
||||
FlyMode,
|
||||
OrbitObjectMode,
|
||||
OrbitPointMode,
|
||||
TrackObjectMode,
|
||||
OverheadMode,
|
||||
EditOrbitMode, ///< Used by the World Editor
|
||||
|
||||
CameraFirstMode = 0,
|
||||
CameraLastMode = EditOrbitMode
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
enum MaskBits
|
||||
{
|
||||
MoveMask = Parent::NextFreeMask,
|
||||
UpdateMask = Parent::NextFreeMask << 1,
|
||||
NewtonCameraMask = Parent::NextFreeMask << 2,
|
||||
EditOrbitMask = Parent::NextFreeMask << 3,
|
||||
NextFreeMask = Parent::NextFreeMask << 4
|
||||
};
|
||||
|
||||
struct StateDelta
|
||||
{
|
||||
Point3F pos;
|
||||
Point3F rot;
|
||||
VectorF posVec;
|
||||
VectorF rotVec;
|
||||
};
|
||||
|
||||
Point3F mRot;
|
||||
StateDelta mDelta;
|
||||
|
||||
Point3F mOffset;
|
||||
|
||||
static F32 smMovementSpeed;
|
||||
|
||||
SimObjectPtr<GameBase> mOrbitObject;
|
||||
F32 mMinOrbitDist;
|
||||
F32 mMaxOrbitDist;
|
||||
F32 mCurOrbitDist;
|
||||
Point3F mPosition;
|
||||
bool mObservingClientObject;
|
||||
|
||||
/// @name NewtonFlyMode
|
||||
/// @{
|
||||
|
||||
VectorF mAngularVelocity;
|
||||
F32 mAngularForce;
|
||||
F32 mAngularDrag;
|
||||
VectorF mVelocity;
|
||||
bool mNewtonMode;
|
||||
bool mNewtonRotation;
|
||||
F32 mMass;
|
||||
F32 mDrag;
|
||||
F32 mFlyForce;
|
||||
F32 mSpeedMultiplier;
|
||||
F32 mBrakeMultiplier;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name EditOrbitMode
|
||||
/// @{
|
||||
|
||||
bool mValidEditOrbitPoint;
|
||||
Point3F mEditOrbitPoint;
|
||||
F32 mCurrentEditOrbitDist;
|
||||
|
||||
/// @}
|
||||
|
||||
bool mLocked;
|
||||
|
||||
CameraMotionMode mMode;
|
||||
|
||||
void _setPosition(const Point3F& pos,const Point3F& viewRot);
|
||||
void _setRenderPosition(const Point3F& pos,const Point3F& viewRot);
|
||||
void _validateEyePoint( F32 pos, MatrixF* mat );
|
||||
|
||||
void _calcOrbitPoint( MatrixF* mat, const Point3F& rot );
|
||||
void _calcEditOrbitPoint( MatrixF *mat, const Point3F& rot );
|
||||
|
||||
static bool _setModeField( void *object, const char *index, const char *data );
|
||||
static bool _setNewtonField( void *object, const char *index, const char *data );
|
||||
|
||||
// ShapeBase.
|
||||
virtual F32 getCameraFov();
|
||||
virtual void setCameraFov( F32 fov );
|
||||
virtual F32 getDefaultCameraFov();
|
||||
virtual bool isValidCameraFov( F32 fov );
|
||||
virtual F32 getDamageFlash() const;
|
||||
virtual F32 getWhiteOut() const;
|
||||
virtual void setTransform( const MatrixF& mat );
|
||||
virtual void setRenderTransform( const MatrixF& mat );
|
||||
|
||||
public:
|
||||
|
||||
Camera();
|
||||
~Camera();
|
||||
|
||||
CameraMotionMode getMode() const { return mMode; }
|
||||
|
||||
Point3F getPosition();
|
||||
Point3F getRotation() { return mRot; };
|
||||
void setRotation( const Point3F& viewRot );
|
||||
|
||||
Point3F getOffset() { return mOffset; };
|
||||
void lookAt( const Point3F& pos);
|
||||
void setOffset( const Point3F& offset) { if( mOffset != offset ) mOffset = offset; setMaskBits( UpdateMask ); }
|
||||
void setFlyMode();
|
||||
void setOrbitMode( GameBase *obj, const Point3F& pos, const Point3F& rot, const Point3F& offset,
|
||||
F32 minDist, F32 maxDist, F32 curDist, bool ownClientObject, bool locked = false );
|
||||
void setTrackObject( GameBase *obj, const Point3F& offset);
|
||||
void onDeleteNotify( SimObject* obj );
|
||||
|
||||
GameBase* getOrbitObject() { return(mOrbitObject); }
|
||||
bool isObservingClientObject() { return(mObservingClientObject); }
|
||||
|
||||
/// @name NewtonFlyMode
|
||||
/// @{
|
||||
|
||||
void setNewtonFlyMode();
|
||||
VectorF getVelocity() const { return mVelocity; }
|
||||
void setVelocity( const VectorF& vel );
|
||||
VectorF getAngularVelocity() const { return mAngularVelocity; }
|
||||
void setAngularVelocity( const VectorF& vel );
|
||||
bool isRotationDamped() {return mNewtonRotation;}
|
||||
void setAngularForce( F32 force ) {mAngularForce = force; setMaskBits(NewtonCameraMask);}
|
||||
void setAngularDrag( F32 drag ) {mAngularDrag = drag; setMaskBits(NewtonCameraMask);}
|
||||
void setMass( F32 mass ) {mMass = mass; setMaskBits(NewtonCameraMask);}
|
||||
void setDrag( F32 drag ) {mDrag = drag; setMaskBits(NewtonCameraMask);}
|
||||
void setFlyForce( F32 force ) {mFlyForce = force; setMaskBits(NewtonCameraMask);}
|
||||
void setSpeedMultiplier( F32 mul ) {mSpeedMultiplier = mul; setMaskBits(NewtonCameraMask);}
|
||||
void setBrakeMultiplier( F32 mul ) {mBrakeMultiplier = mul; setMaskBits(NewtonCameraMask);}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name EditOrbitMode
|
||||
/// @{
|
||||
|
||||
void setEditOrbitMode();
|
||||
bool isEditOrbitMode() {return mMode == EditOrbitMode;}
|
||||
bool getValidEditOrbitPoint() { return mValidEditOrbitPoint; }
|
||||
void setValidEditOrbitPoint( bool state );
|
||||
Point3F getEditOrbitPoint() const;
|
||||
void setEditOrbitPoint( const Point3F& pnt );
|
||||
|
||||
/// Orient the camera to view the given radius. Requires that an
|
||||
/// edit orbit point has been set.
|
||||
void autoFitRadius( F32 radius );
|
||||
|
||||
/// @}
|
||||
|
||||
// ShapeBase.
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
|
||||
virtual void onEditorEnable();
|
||||
virtual void onEditorDisable();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
virtual void processTick( const Move* move );
|
||||
virtual void interpolateTick( F32 delta);
|
||||
virtual void getCameraTransform( F32* pos,MatrixF* mat );
|
||||
|
||||
virtual void writePacketData( GameConnection* conn, BitStream* stream );
|
||||
virtual void readPacketData( GameConnection* conn, BitStream* stream );
|
||||
virtual U32 packUpdate( NetConnection* conn, U32 mask, BitStream* stream );
|
||||
virtual void unpackUpdate( NetConnection* conn, BitStream* stream );
|
||||
|
||||
DECLARE_CONOBJECT( Camera );
|
||||
DECLARE_CATEGORY( "Game" );
|
||||
DECLARE_DESCRIPTION( "Represents a position, direction and field of view to render a scene from." );
|
||||
};
|
||||
|
||||
typedef Camera::CameraMotionMode CameraMotionMode;
|
||||
DefineEnumType( CameraMotionMode );
|
||||
|
||||
#endif
|
||||
387
Engine/source/T3D/cameraSpline.cpp
Normal file
387
Engine/source/T3D/cameraSpline.cpp
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include "T3D/cameraSpline.h"
|
||||
|
||||
#include "console/console.h"
|
||||
#include "gfx/gfxDevice.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;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static bool gBuilding = false;
|
||||
|
||||
void CameraSpline::buildTimeMap()
|
||||
{
|
||||
if (!mIsMapDirty)
|
||||
return;
|
||||
|
||||
gBuilding = true;
|
||||
|
||||
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) > F32(mSize - 1))
|
||||
time = (F32)mSize - 1.0f;
|
||||
|
||||
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;
|
||||
|
||||
gBuilding = false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CameraSpline::renderTimeMap()
|
||||
{
|
||||
buildTimeMap();
|
||||
|
||||
gBuilding = true;
|
||||
|
||||
// Build vertex buffer
|
||||
GFXVertexBufferHandle<GFXVertexPC> vb;
|
||||
vb.set(GFX, mTimeMap.size(), GFXBufferTypeVolatile);
|
||||
vb.lock();
|
||||
|
||||
MRandomLCG random(1376312589 * (U32)this);
|
||||
int index = 0;
|
||||
for(Vector<TimeMap>::iterator itr=mTimeMap.begin(); itr != mTimeMap.end(); itr++)
|
||||
{
|
||||
Knot a;
|
||||
value(itr->mTime, &a, true);
|
||||
|
||||
S32 cr = random.randI(0,255);
|
||||
S32 cg = random.randI(0,255);
|
||||
S32 cb = random.randI(0,255);
|
||||
vb[index].color.set(cr, cg, cb);
|
||||
vb[index].point.set(a.mPosition.x, a.mPosition.y, a.mPosition.z);
|
||||
index++;
|
||||
}
|
||||
|
||||
gBuilding = false;
|
||||
|
||||
vb.unlock();
|
||||
|
||||
// Render the buffer
|
||||
GFX->pushWorldMatrix();
|
||||
GFX->disableShaders();
|
||||
GFX->setVertexBuffer(vb);
|
||||
GFX->drawPrimitive(GFXLineStrip,0,index);
|
||||
GFX->popWorldMatrix();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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)
|
||||
{
|
||||
// Do some easing in and out for t.
|
||||
if(!gBuilding)
|
||||
{
|
||||
F32 oldT = t;
|
||||
if(oldT < 0.5f)
|
||||
{
|
||||
t = 0.5f - (mSin( (0.5 - oldT) * M_PI ) / 2.f);
|
||||
}
|
||||
|
||||
if((F32(size()) - 1.5f) > 0.f && oldT - (F32(size()) - 1.5f) > 0.f)
|
||||
{
|
||||
oldT -= (F32(size()) - 1.5f);
|
||||
t = (F32(size()) - 1.5f) + (mCos( (0.5f - oldT) * F32(M_PI) ) / 2.f);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that t is in range [0 >= t > size]
|
||||
// AssertFatal(t >= 0.0f && t < (F32)size(), "t out of range");
|
||||
Knot *p1 = getKnot((S32)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 = (S32)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((F32)start);
|
||||
F32 d2 = getDistance((F32)end);
|
||||
|
||||
if (d1 == d2)
|
||||
{
|
||||
result->mRotation = p2->mRotation;
|
||||
result->mSpeed = p2->mSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = (c-d1)/(d2-d1);
|
||||
|
||||
if(p1->mPath == Knot::SPLINE)
|
||||
{
|
||||
Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1);
|
||||
Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2);
|
||||
|
||||
F32 q,w,e;
|
||||
q = mCatmullrom(i, 0, 1, 1, 1);
|
||||
w = mCatmullrom(i, 0, 0, 0, 1);
|
||||
e = mCatmullrom(i, 0, 0, 1, 1);
|
||||
|
||||
QuatF a; a.interpolate(p0->mRotation, p1->mRotation, q);
|
||||
QuatF b; b.interpolate(p2->mRotation, p3->mRotation, w);
|
||||
|
||||
result->mRotation.interpolate(a, b, e);
|
||||
result->mSpeed = mCatmullrom(i, p0->mSpeed, p1->mSpeed, p2->mSpeed, p3->mSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
result->mRotation.interpolate(p1->mRotation, p2->mRotation, i);
|
||||
result->mSpeed = (p1->mSpeed * (1.0f-i)) + (p2->mSpeed * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
108
Engine/source/T3D/cameraSpline.h
Normal file
108
Engine/source/T3D/cameraSpline.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _CAMERASPLINE_H_
|
||||
#define _CAMERASPLINE_H_
|
||||
|
||||
#include "math/mMath.h"
|
||||
#include "core/util/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(const Knot &k);
|
||||
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
|
||||
97
Engine/source/T3D/containerQuery.cpp
Normal file
97
Engine/source/T3D/containerQuery.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/containerQuery.h"
|
||||
|
||||
#include "scene/sceneObject.h"
|
||||
#include "environment/waterObject.h"
|
||||
#include "T3D/physicalZone.h"
|
||||
|
||||
|
||||
void findRouter( SceneObject *obj, void *key )
|
||||
{
|
||||
if (obj->getTypeMask() & WaterObjectType)
|
||||
waterFind(obj, key);
|
||||
else if (obj->getTypeMask() & PhysicalZoneObjectType)
|
||||
physicalZoneFind(obj, key);
|
||||
else {
|
||||
AssertFatal(false, "Error, must be either water or physical zone here!");
|
||||
}
|
||||
}
|
||||
|
||||
void waterFind( SceneObject *obj, void *key )
|
||||
{
|
||||
PROFILE_SCOPE( waterFind );
|
||||
|
||||
// This is called for each WaterObject the ShapeBase object is overlapping.
|
||||
|
||||
ContainerQueryInfo *info = static_cast<ContainerQueryInfo*>(key);
|
||||
WaterObject *water = dynamic_cast<WaterObject*>(obj);
|
||||
AssertFatal( water != NULL, "containerQuery - waterFind(), passed object was not of class WaterObject!");
|
||||
|
||||
// Get point at the bottom/center of the box.
|
||||
Point3F testPnt = info->box.getCenter();
|
||||
testPnt.z = info->box.minExtents.z;
|
||||
|
||||
F32 coverage = water->getWaterCoverage(info->box);
|
||||
|
||||
// Since a WaterObject can have global bounds we may get this call
|
||||
// even though we have zero coverage. If so we want to early out and
|
||||
// not save the water properties.
|
||||
if ( coverage == 0.0f )
|
||||
return;
|
||||
|
||||
// Add in flow force. Would be appropriate to try scaling it by coverage
|
||||
// thought. Or perhaps have getFlow do that internally and take
|
||||
// the box parameter.
|
||||
info->appliedForce += water->getFlow( testPnt );
|
||||
|
||||
// Only save the following properties for the WaterObject with the
|
||||
// greatest water coverage for this ShapeBase object.
|
||||
if ( coverage < info->waterCoverage )
|
||||
return;
|
||||
|
||||
info->waterCoverage = coverage;
|
||||
info->liquidType = water->getLiquidType();
|
||||
info->waterViscosity = water->getViscosity();
|
||||
info->waterDensity = water->getDensity();
|
||||
info->waterHeight = water->getSurfaceHeight( Point2F(testPnt.x,testPnt.y) );
|
||||
info->waterObject = water;
|
||||
}
|
||||
|
||||
void physicalZoneFind(SceneObject* obj, void *key)
|
||||
{
|
||||
PROFILE_SCOPE( physicalZoneFind );
|
||||
|
||||
ContainerQueryInfo *info = static_cast<ContainerQueryInfo*>(key);
|
||||
PhysicalZone* pz = dynamic_cast<PhysicalZone*>(obj);
|
||||
AssertFatal(pz != NULL, "Error, not a physical zone!");
|
||||
if (pz == NULL || pz->testBox(info->box) == false)
|
||||
return;
|
||||
|
||||
if (pz->isActive()) {
|
||||
info->gravityScale *= pz->getGravityMod();
|
||||
info->appliedForce += pz->getForce();
|
||||
}
|
||||
}
|
||||
|
||||
71
Engine/source/T3D/containerQuery.h
Normal file
71
Engine/source/T3D/containerQuery.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONTAINERQUERY_H_
|
||||
#define _CONTAINERQUERY_H_
|
||||
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "core/stringTable.h"
|
||||
#endif
|
||||
#ifndef _MBOX_H_
|
||||
#include "math/mBox.h"
|
||||
#endif
|
||||
|
||||
class SceneObject;
|
||||
class WaterObject;
|
||||
|
||||
struct ContainerQueryInfo
|
||||
{
|
||||
ContainerQueryInfo()
|
||||
: waterCoverage(0.0f),
|
||||
waterHeight(0.0f),
|
||||
waterDensity(0.0f),
|
||||
waterViscosity(0.0f),
|
||||
gravityScale(1.0f),
|
||||
appliedForce(0,0,0),
|
||||
box(-1,-1,-1,1,1,1),
|
||||
mass(1.0f),
|
||||
waterObject(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
//SceneObject *sceneObject;
|
||||
Box3F box;
|
||||
F32 mass;
|
||||
F32 waterCoverage;
|
||||
F32 waterHeight;
|
||||
F32 waterDensity;
|
||||
F32 waterViscosity;
|
||||
String liquidType;
|
||||
F32 gravityScale;
|
||||
Point3F appliedForce;
|
||||
WaterObject *waterObject;
|
||||
};
|
||||
|
||||
extern void findRouter( SceneObject *obj, void *key );
|
||||
extern void waterFind( SceneObject *obj, void *key );
|
||||
extern void physicalZoneFind( SceneObject *obj, void *key );
|
||||
|
||||
#endif // _CONTAINERQUERY_H_
|
||||
1770
Engine/source/T3D/convexShape.cpp
Normal file
1770
Engine/source/T3D/convexShape.cpp
Normal file
File diff suppressed because it is too large
Load diff
248
Engine/source/T3D/convexShape.h
Normal file
248
Engine/source/T3D/convexShape.h
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONVEXSHAPE_H_
|
||||
#define _CONVEXSHAPE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
|
||||
class ConvexShape;
|
||||
|
||||
// Crap name, but whatcha gonna do.
|
||||
class ConvexShapeCollisionConvex : public Convex
|
||||
{
|
||||
typedef Convex Parent;
|
||||
friend class ConvexShape;
|
||||
|
||||
protected:
|
||||
|
||||
ConvexShape *pShape;
|
||||
|
||||
public:
|
||||
|
||||
ConvexShapeCollisionConvex() { mType = ConvexShapeCollisionConvexType; }
|
||||
|
||||
ConvexShapeCollisionConvex( const ConvexShapeCollisionConvex& cv ) {
|
||||
mType = ConvexShapeCollisionConvexType;
|
||||
mObject = cv.mObject;
|
||||
pShape = cv.pShape;
|
||||
}
|
||||
|
||||
Point3F support(const VectorF& vec) const;
|
||||
void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
|
||||
void getPolyList(AbstractPolyList* list);
|
||||
};
|
||||
|
||||
|
||||
GFXDeclareVertexFormat( ConvexVert )
|
||||
{
|
||||
Point3F point;
|
||||
GFXVertexColor color;
|
||||
Point3F normal;
|
||||
Point3F tangent;
|
||||
Point2F texCoord;
|
||||
};
|
||||
|
||||
class PhysicsBody;
|
||||
|
||||
// Define our vertex format here so we don't have to
|
||||
// change it in multiple spots later
|
||||
typedef ConvexVert VertexType;
|
||||
|
||||
class ConvexShape : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
friend class GuiConvexEditorCtrl;
|
||||
friend class GuiConvexEditorUndoAction;
|
||||
friend class ConvexShapeCollisionConvex;
|
||||
|
||||
public:
|
||||
|
||||
enum NetBits {
|
||||
TransformMask = Parent::NextFreeMask,
|
||||
UpdateMask = Parent::NextFreeMask << 1,
|
||||
NextFreeMask = Parent::NextFreeMask << 2
|
||||
};
|
||||
|
||||
// Declaring these structs directly within ConvexShape to prevent
|
||||
// the otherwise excessively deep scoping we had.
|
||||
// eg. ConvexShape::Face::Triangle ...
|
||||
|
||||
struct Edge
|
||||
{
|
||||
U32 p0;
|
||||
U32 p1;
|
||||
};
|
||||
|
||||
struct Triangle
|
||||
{
|
||||
U32 p0;
|
||||
U32 p1;
|
||||
U32 p2;
|
||||
|
||||
U32 operator []( U32 index ) const
|
||||
{
|
||||
AssertFatal( index >= 0 && index <= 2, "index out of range" );
|
||||
return *( (&p0) + index );
|
||||
}
|
||||
};
|
||||
|
||||
struct Face
|
||||
{
|
||||
Vector< Edge > edges;
|
||||
Vector< U32 > points;
|
||||
Vector< U32 > winding;
|
||||
Vector< Point2F > texcoords;
|
||||
Vector< Triangle > triangles;
|
||||
Point3F tangent;
|
||||
Point3F normal;
|
||||
Point3F centroid;
|
||||
F32 area;
|
||||
S32 id;
|
||||
};
|
||||
|
||||
struct Geometry
|
||||
{
|
||||
void generate( const Vector< PlaneF > &planes, const Vector< Point3F > &tangents );
|
||||
|
||||
Vector< Point3F > points;
|
||||
Vector< Face > faces;
|
||||
};
|
||||
|
||||
static bool smRenderEdges;
|
||||
|
||||
public:
|
||||
|
||||
ConvexShape();
|
||||
virtual ~ConvexShape();
|
||||
|
||||
DECLARE_CONOBJECT( ConvexShape );
|
||||
|
||||
// ConsoleObject
|
||||
static void initPersistFields();
|
||||
|
||||
// SimObject
|
||||
virtual void inspectPostApply();
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
virtual void writeFields(Stream &stream, U32 tabStop);
|
||||
virtual bool writeField( StringTableEntry fieldname, const char *value );
|
||||
|
||||
// NetObject
|
||||
virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
|
||||
virtual void unpackUpdate( NetConnection *conn, BitStream *stream );
|
||||
|
||||
// SceneObject
|
||||
virtual void onScaleChanged();
|
||||
virtual void setTransform( const MatrixF &mat );
|
||||
virtual void prepRenderImage( SceneRenderState *state );
|
||||
virtual void buildConvex( const Box3F &box, Convex *convex );
|
||||
virtual bool buildPolyList( PolyListContext context, AbstractPolyList *polyList, const Box3F &box, const SphereF &sphere );
|
||||
virtual bool castRay( const Point3F &start, const Point3F &end, RayInfo *info );
|
||||
virtual bool collideBox( const Point3F &start, const Point3F &end, RayInfo *info );
|
||||
|
||||
|
||||
void updateBounds( bool recenter );
|
||||
void recenter();
|
||||
|
||||
/// Geometry access.
|
||||
/// @{
|
||||
|
||||
MatrixF getSurfaceWorldMat( S32 faceid, bool scaled = false ) const;
|
||||
void cullEmptyPlanes( Vector< U32 > *removedPlanes );
|
||||
void exportToCollada();
|
||||
void resizePlanes( const Point3F &size );
|
||||
void getSurfaceLineList( S32 surfId, Vector< Point3F > &lineList );
|
||||
Geometry& getGeometry() { return mGeometry; }
|
||||
Vector<MatrixF>& getSurfaces() { return mSurfaces; }
|
||||
void getSurfaceTriangles( S32 surfId, Vector< Point3F > *outPoints, Vector< Point2F > *outCoords, bool worldSpace );
|
||||
|
||||
/// @}
|
||||
|
||||
/// Geometry Visualization.
|
||||
/// @{
|
||||
|
||||
void renderFaceEdges( S32 faceid, const ColorI &color = ColorI::WHITE, F32 lineWidth = 1.0f );
|
||||
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
|
||||
void _updateMaterial();
|
||||
void _updateGeometry( bool updateCollision = false );
|
||||
void _updateCollision();
|
||||
void _export( OptimizedPolyList *plist, const Box3F &box, const SphereF &sphere );
|
||||
|
||||
void _renderDebug( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mat );
|
||||
|
||||
static S32 QSORT_CALLBACK _comparePlaneDist( const void *a, const void *b );
|
||||
|
||||
static bool protectedSetSurface( void *object, const char *index, const char *data );
|
||||
|
||||
protected:
|
||||
|
||||
// The name of the Material we will use for rendering
|
||||
String mMaterialName;
|
||||
|
||||
// The actual Material instance
|
||||
BaseMatInstance* mMaterialInst;
|
||||
|
||||
// The GFX vertex and primitive buffers
|
||||
GFXVertexBufferHandle< VertexType > mVertexBuffer;
|
||||
GFXPrimitiveBufferHandle mPrimitiveBuffer;
|
||||
|
||||
U32 mVertCount;
|
||||
U32 mPrimCount;
|
||||
|
||||
Geometry mGeometry;
|
||||
|
||||
Vector< PlaneF > mPlanes;
|
||||
|
||||
Vector< MatrixF > mSurfaces;
|
||||
|
||||
Vector< Point3F > mFaceCenters;
|
||||
|
||||
Convex *mConvexList;
|
||||
|
||||
PhysicsBody *mPhysicsRep;
|
||||
|
||||
/// Geometry visualization
|
||||
/// @{
|
||||
|
||||
F32 mNormalLength;
|
||||
|
||||
/// @}
|
||||
|
||||
};
|
||||
|
||||
#endif // _CONVEXSHAPE_H_
|
||||
942
Engine/source/T3D/debris.cpp
Normal file
942
Engine/source/T3D/debris.cpp
Normal file
|
|
@ -0,0 +1,942 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/debris.h"
|
||||
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "math/mathUtils.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "ts/tsPartInstance.h"
|
||||
#include "T3D/fx/particleEmitter.h"
|
||||
#include "T3D/fx/explosion.h"
|
||||
#include "T3D/gameBase/gameProcess.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "lighting/lightQuery.h"
|
||||
|
||||
|
||||
const U32 csmStaticCollisionMask = TerrainObjectType |
|
||||
InteriorObjectType;
|
||||
|
||||
const U32 csmDynamicCollisionMask = StaticShapeObjectType;
|
||||
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(DebrisData);
|
||||
|
||||
ConsoleDocClass( DebrisData,
|
||||
"@brief Stores properties for an individual debris type.\n\n"
|
||||
|
||||
"DebrisData defines the base properties for a Debris object. Typically you'll want a Debris object to consist of "
|
||||
"a shape and possibly up to two particle emitters. The DebrisData datablock provides the definition for these items, "
|
||||
"along with physical properties and how a Debris object will react to other game objects, such as water and terrain.\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock DebrisData(GrenadeDebris)\n"
|
||||
"{\n"
|
||||
" shapeFile = \"art/shapes/weapons/ramrifle/debris.dts\";\n"
|
||||
" emitters[0] = GrenadeDebrisFireEmitter;\n"
|
||||
" elasticity = 0.4;\n"
|
||||
" friction = 0.25;\n"
|
||||
" numBounces = 3;\n"
|
||||
" bounceVariance = 1;\n"
|
||||
" explodeOnMaxBounce = false;\n"
|
||||
" staticOnMaxBounce = false;\n"
|
||||
" snapOnMaxBounce = false;\n"
|
||||
" minSpinSpeed = 200;\n"
|
||||
" maxSpinSpeed = 600;\n"
|
||||
" lifetime = 4;\n"
|
||||
" lifetimeVariance = 1.5;\n"
|
||||
" velocity = 15;\n"
|
||||
" velocityVariance = 5;\n"
|
||||
" fade = true;\n"
|
||||
" useRadiusMass = true;\n"
|
||||
" baseRadius = 0.3;\n"
|
||||
" gravModifier = 1.0;\n"
|
||||
" terminalVelocity = 20;\n"
|
||||
" ignoreWater = false;\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see Debris\n\n"
|
||||
"@ingroup FX\n"
|
||||
);
|
||||
|
||||
DebrisData::DebrisData()
|
||||
{
|
||||
dMemset( emitterList, 0, sizeof( emitterList ) );
|
||||
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
|
||||
|
||||
explosion = NULL;
|
||||
explosionId = 0;
|
||||
|
||||
velocity = 0.0f;
|
||||
velocityVariance = 0.0;
|
||||
elasticity = 0.3f;
|
||||
friction = 0.2f;
|
||||
numBounces = 0;
|
||||
bounceVariance = 0;
|
||||
minSpinSpeed = maxSpinSpeed = 0.0;
|
||||
staticOnMaxBounce = false;
|
||||
explodeOnMaxBounce = false;
|
||||
snapOnMaxBounce = false;
|
||||
lifetime = 3.0f;
|
||||
lifetimeVariance = 0.0f;
|
||||
minSpinSpeed = 0.0f;
|
||||
maxSpinSpeed = 0.0f;
|
||||
textureName = NULL;
|
||||
shapeName = NULL;
|
||||
fade = true;
|
||||
useRadiusMass = false;
|
||||
baseRadius = 1.0f;
|
||||
gravModifier = 1.0f;
|
||||
terminalVelocity = 0.0f;
|
||||
ignoreWater = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// validate data
|
||||
|
||||
if( velocityVariance > velocity )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName());
|
||||
velocityVariance = velocity;
|
||||
}
|
||||
if( friction < -10.0f || friction > 10.0f )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName());
|
||||
friction = 0.2f;
|
||||
}
|
||||
if( elasticity < -10.0f || elasticity > 10.0f )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName());
|
||||
elasticity = 0.2f;
|
||||
}
|
||||
if( lifetime < 0.0f || lifetime > 1000.0f )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName());
|
||||
lifetime = 3.0f;
|
||||
}
|
||||
if( lifetimeVariance < 0.0f || lifetimeVariance > lifetime )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName());
|
||||
lifetimeVariance = 0.0f;
|
||||
}
|
||||
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.0f || minSpinSpeed > 10000.0f || minSpinSpeed > maxSpinSpeed )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName());
|
||||
minSpinSpeed = maxSpinSpeed - 1.0f;
|
||||
}
|
||||
if( maxSpinSpeed < -10000.0f || maxSpinSpeed > 10000.0f )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName());
|
||||
maxSpinSpeed = 0.0f;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebrisData::preload(bool server, String &errorStr)
|
||||
{
|
||||
if (Parent::preload(server, errorStr) == false)
|
||||
return false;
|
||||
|
||||
if( server ) return true;
|
||||
|
||||
if( shapeName && shapeName[0] != '\0' && !bool(shape) )
|
||||
{
|
||||
shape = ResourceManager::get().load(shapeName);
|
||||
if( bool(shape) == false )
|
||||
{
|
||||
errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", shapeName);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
|
||||
delete pDummy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DebrisData::initPersistFields()
|
||||
{
|
||||
addGroup("Display");
|
||||
addField("texture", TypeString, Offset(textureName, DebrisData),
|
||||
"@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n");
|
||||
addField("shapeFile", TypeShapeFilename, Offset(shapeName, DebrisData),
|
||||
"@brief Object model to use for this debris object.\n\nThis shape is optional. You could have Debris made up of only particles.\n");
|
||||
endGroup("Display");
|
||||
|
||||
addGroup("Datablocks");
|
||||
addField("emitters", TYPEID< ParticleEmitterData >(), Offset(emitterList, DebrisData), DDC_NUM_EMITTERS,
|
||||
"@brief List of particle emitters to spawn along with this debris object.\n\nThese are optional. You could have Debris made up of only a shape.\n");
|
||||
addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, DebrisData),
|
||||
"@brief ExplosionData to spawn along with this debris object.\n\nThis is optional as not all Debris explode.\n");
|
||||
endGroup("Datablocks");
|
||||
|
||||
addGroup("Physical Properties");
|
||||
addField("elasticity", TypeF32, Offset(elasticity, DebrisData),
|
||||
"@brief A floating-point value specifying how 'bouncy' this object is.\n\nMust be in the range of -10 to 10.\n");
|
||||
addField("friction", TypeF32, Offset(friction, DebrisData),
|
||||
"@brief A floating-point value specifying how much velocity is lost to impact and sliding friction.\n\nMust be in the range of -10 to 10.\n");
|
||||
addField("numBounces", TypeS32, Offset(numBounces, DebrisData),
|
||||
"@brief How many times to allow this debris object to bounce until it either explodes, becomes static or snaps (defined in explodeOnMaxBounce, staticOnMaxBounce, snapOnMaxBounce).\n\n"
|
||||
"Must be within the range of 0 to 10000.\n"
|
||||
"@see bounceVariance\n");
|
||||
addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData),
|
||||
"@brief Allowed variance in the value of numBounces.\n\nMust be less than numBounces.\n@see numBounces\n");
|
||||
addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData),
|
||||
"@brief Minimum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 1000, and must be less than maxSpinSpeed.\n@see maxSpinSpeed\n");
|
||||
addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData),
|
||||
"@brief Maximum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 10000.\n@see minSpinSpeed\n");
|
||||
addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData), "How much gravity affects debris.");
|
||||
addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData), "Max velocity magnitude.");
|
||||
addField("velocity", TypeF32, Offset(velocity, DebrisData),
|
||||
"@brief Speed at which this debris object will move.\n\n@see velocityVariance\n");
|
||||
addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData),
|
||||
"@brief Allowed variance in the value of velocity\n\nMust be less than velocity.\n@see velocity\n");
|
||||
addField("lifetime", TypeF32, Offset(lifetime, DebrisData),
|
||||
"@brief Amount of time until this debris object is destroyed.\n\nMust be in the range of 0 to 1000.\n@see lifetimeVariance");
|
||||
addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData),
|
||||
"@brief Allowed variance in the value of lifetime.\n\nMust be less than lifetime.\n@see lifetime\n");
|
||||
addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData),
|
||||
"@brief Use mass calculations based on radius.\n\nAllows for the adjustment of elasticity and friction based on the Debris size.\n@see baseRadius\n");
|
||||
addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData),
|
||||
"@brief Radius at which the standard elasticity and friction apply.\n\nOnly used when useRaduisMass is true.\n@see useRadiusMass.\n");
|
||||
endGroup("Physical Properties");
|
||||
|
||||
addGroup("Behavior");
|
||||
addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData),
|
||||
"@brief If true, this debris object will explode after it has bounced max times.\n\nBe sure to provide an ExplosionData datablock for this to take effect.\n@see explosion\n");
|
||||
addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData), "If true, this debris object becomes static after it has bounced max times.");
|
||||
addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData), "If true, this debris object will snap into a resting position on the last bounce.");
|
||||
addField("fade", TypeBool, Offset(fade, DebrisData),
|
||||
"@brief If true, this debris object will fade out when destroyed.\n\nThis fade occurs over the last second of the Debris' lifetime.\n");
|
||||
addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there.");
|
||||
endGroup("Behavior");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
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(explodeOnMaxBounce);
|
||||
stream->write(staticOnMaxBounce);
|
||||
stream->write(snapOnMaxBounce);
|
||||
stream->write(lifetime);
|
||||
stream->write(lifetimeVariance);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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(&explodeOnMaxBounce);
|
||||
stream->read(&staticOnMaxBounce);
|
||||
stream->read(&snapOnMaxBounce);
|
||||
stream->read(&lifetime);
|
||||
stream->read(&lifetimeVariance);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Debris);
|
||||
|
||||
ConsoleDocClass( Debris,
|
||||
"@brief Base debris class. Uses the DebrisData datablock for properties of individual debris objects.\n\n"
|
||||
|
||||
"Debris is typically made up of a shape and up to two particle emitters. In most cases Debris objects are "
|
||||
"not created directly. They are usually produced automatically by other means, such as through the Explosion "
|
||||
"class. When an explosion goes off, its ExplosionData datablock determines what Debris to emit.\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock ExplosionData(GrenadeLauncherExplosion)\n"
|
||||
"{\n"
|
||||
" // Assiging debris data\n"
|
||||
" debris = GrenadeDebris;\n\n"
|
||||
" // Adjust how debris is ejected\n"
|
||||
" debrisThetaMin = 10;\n"
|
||||
" debrisThetaMax = 60;\n"
|
||||
" debrisNum = 4;\n"
|
||||
" debrisNumVariance = 2;\n"
|
||||
" debrisVelocity = 25;\n"
|
||||
" debrisVelocityVariance = 5;\n\n"
|
||||
" // Note: other ExplosionData properties are not listed for this example\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@note Debris are client side only objects.\n"
|
||||
|
||||
"@see DebrisData\n"
|
||||
"@see ExplosionData\n"
|
||||
"@see Explosion\n"
|
||||
|
||||
"@ingroup FX\n"
|
||||
);
|
||||
|
||||
DefineEngineMethod(Debris, init, bool, (const char* inputPosition, const char* inputVelocity),
|
||||
("1.0 1.0 1.0", "1.0 0.0 0.0"),
|
||||
"@brief Manually set this piece of debris at the given position with the given velocity.\n\n"
|
||||
|
||||
"Usually you do not manually create Debris objects as they are generated through other means, "
|
||||
"such as an Explosion. This method exists when you do manually create a Debris object and "
|
||||
"want to have it start moving.\n"
|
||||
|
||||
"@param inputPosition Position to place the debris.\n"
|
||||
"@param inputVelocity Velocity to move the debris after it has been placed.\n"
|
||||
"@return Always returns true.\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Define the position\n"
|
||||
"%position = \"1.0 1.0 1.0\";\n\n"
|
||||
"// Define the velocity\n"
|
||||
"%velocity = \"1.0 0.0 0.0\";\n\n"
|
||||
"// Inform the debris object of its new position and velocity\n"
|
||||
"%debris.init(%position,%velocity);\n"
|
||||
"@endtsexample\n")
|
||||
{
|
||||
Point3F pos;
|
||||
dSscanf( inputPosition, "%f %f %f", &pos.x, &pos.y, &pos.z );
|
||||
|
||||
Point3F vel;
|
||||
dSscanf( inputVelocity, "%f %f %f", &vel.x, &vel.y, &vel.z );
|
||||
|
||||
object->init( pos, vel );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Debris::Debris()
|
||||
{
|
||||
mTypeMask |= DebrisObjectType | DynamicShapeObjectType;
|
||||
|
||||
mVelocity = Point3F( 0.0f, 0.0f, 4.0f );
|
||||
mLifetime = gRandGen.randF( 1.0f, 10.0f );
|
||||
mLastPos = getPosition();
|
||||
mNumBounces = gRandGen.randI( 0, 1 );
|
||||
mSize = 2.0f;
|
||||
mElapsedTime = 0.0f;
|
||||
mShape = NULL;
|
||||
mPart = NULL;
|
||||
mDataBlock = NULL;
|
||||
mXRotSpeed = 0.0f;
|
||||
mZRotSpeed = 0.0f;
|
||||
mInitialTrans.identity();
|
||||
mRadius = 0.2f;
|
||||
mStatic = false;
|
||||
|
||||
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
|
||||
|
||||
// Only allocated client side.
|
||||
mNetFlags.set( IsGhost );
|
||||
}
|
||||
|
||||
Debris::~Debris()
|
||||
{
|
||||
if( mShape )
|
||||
{
|
||||
delete mShape;
|
||||
mShape = NULL;
|
||||
}
|
||||
|
||||
if( mPart )
|
||||
{
|
||||
delete mPart;
|
||||
mPart = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Debris::initPersistFields()
|
||||
{
|
||||
addGroup( "Debris" );
|
||||
|
||||
addField( "lifetime", TypeF32, Offset(mLifetime, Debris),
|
||||
"@brief Length of time for this debris object to exist. When expired, the object will be deleted.\n\n"
|
||||
"The initial lifetime value comes from the DebrisData datablock.\n"
|
||||
"@see DebrisData::lifetime\n"
|
||||
"@see DebrisData::lifetimeVariance\n");
|
||||
|
||||
endGroup( "Debris" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void Debris::init( const Point3F &position, const Point3F &velocity )
|
||||
{
|
||||
setPosition( position );
|
||||
setVelocity( velocity );
|
||||
}
|
||||
|
||||
bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||
{
|
||||
mDataBlock = dynamic_cast< DebrisData* >( dptr );
|
||||
if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Debris::onAdd()
|
||||
{
|
||||
if( !Parent::onAdd() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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], false );
|
||||
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[ParticleData::PDC_NUM_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 = (S32)mDataBlock->bounceVariance;
|
||||
bounceVar = gRandGen.randI( -bounceVar, bounceVar );
|
||||
mNumBounces = mDataBlock->numBounces + bounceVar;
|
||||
|
||||
F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance;
|
||||
mLifetime = mDataBlock->lifetime + lifeVar;
|
||||
|
||||
F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
|
||||
F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
|
||||
zRotSpeed *= gRandGen.randF( 0.1f, 0.5f );
|
||||
|
||||
mRotAngles.set( xRotSpeed, 0.0f, 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
|
||||
gClientSceneGraph->addObjectToScene(this);
|
||||
|
||||
removeFromProcessList();
|
||||
ClientProcessList::get()->addObject(this);
|
||||
|
||||
NetConnection* pNC = NetConnection::getConnectionToServer();
|
||||
AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
|
||||
pNC->addObject(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getSceneManager()->removeObjectFromScene(this);
|
||||
getContainer()->removeObject(this);
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void Debris::processTick(const Move*)
|
||||
{
|
||||
if (mLifetime <= 0.0)
|
||||
deleteObject();
|
||||
}
|
||||
|
||||
void Debris::advanceTime( F32 dt )
|
||||
{
|
||||
mElapsedTime += dt;
|
||||
|
||||
mLifetime -= dt;
|
||||
if( mLifetime <= 0.0 )
|
||||
{
|
||||
mLifetime = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
mLastPos = getPosition();
|
||||
|
||||
if( !mStatic )
|
||||
{
|
||||
rotate( dt );
|
||||
|
||||
Point3F nextPos = getPosition();
|
||||
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 = getTransform();
|
||||
|
||||
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.0f, 0.0f, 0.10f ) );
|
||||
|
||||
setTransform( newTrans );
|
||||
}
|
||||
if( mDataBlock->staticOnMaxBounce )
|
||||
{
|
||||
mStatic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setPosition( nextPos );
|
||||
}
|
||||
}
|
||||
|
||||
Point3F pos( getPosition( ) );
|
||||
updateEmitters( pos, mVelocity, (U32)(dt * 1000.0));
|
||||
|
||||
}
|
||||
|
||||
void Debris::rotate( F32 dt )
|
||||
{
|
||||
MatrixF curTrans = getTransform();
|
||||
curTrans.setPosition( Point3F(0.0f, 0.0f, 0.0f) );
|
||||
|
||||
Point3F curAngles = mRotAngles * dt * M_PI_F/180.0f;
|
||||
MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) );
|
||||
|
||||
curTrans.mul( rotMatrix );
|
||||
curTrans.setPosition( getPosition() );
|
||||
setTransform( curTrans );
|
||||
}
|
||||
|
||||
bool Debris::bounce( const Point3F &nextPos, F32 dt )
|
||||
{
|
||||
Point3F curPos = getPosition();
|
||||
|
||||
Point3F dir = nextPos - curPos;
|
||||
if( dir.magnitudeSafe() == 0.0f ) 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.0f);
|
||||
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;
|
||||
|
||||
setPosition( bouncePos );
|
||||
|
||||
mRotAngles *= mElasticity;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void Debris::explode()
|
||||
{
|
||||
|
||||
if( !mDataBlock->explosion ) return;
|
||||
|
||||
Point3F explosionPos = getPosition();
|
||||
|
||||
Explosion* pExplosion = new Explosion;
|
||||
pExplosion->onNewDataBlock(mDataBlock->explosion, false);
|
||||
|
||||
MatrixF trans( true );
|
||||
trans.setPosition( getPosition() );
|
||||
|
||||
pExplosion->setTransform( trans );
|
||||
pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1);
|
||||
if (!pExplosion->registerObject())
|
||||
delete pExplosion;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
if( newVel.magnitudeSafe() > mDataBlock->terminalVelocity )
|
||||
{
|
||||
newVel.normalizeSafe();
|
||||
newVel *= mDataBlock->terminalVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
newVel += force * dt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newVel += force * dt;
|
||||
}
|
||||
|
||||
newPos += newVel * dt;
|
||||
|
||||
}
|
||||
|
||||
void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms )
|
||||
{
|
||||
|
||||
Point3F axis = -vel;
|
||||
|
||||
if( axis.magnitudeSafe() == 0.0 )
|
||||
{
|
||||
axis = Point3F( 0.0, 0.0, 1.0 );
|
||||
}
|
||||
axis.normalizeSafe();
|
||||
|
||||
|
||||
Point3F lastPos = mLastPos;
|
||||
|
||||
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mEmitterList[i] )
|
||||
{
|
||||
mEmitterList[i]->emitParticles( lastPos, pos, axis, vel, ms );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Debris::prepRenderImage( SceneRenderState *state )
|
||||
{
|
||||
if( !mPart && !mShape )
|
||||
return;
|
||||
|
||||
Point3F cameraOffset;
|
||||
mObjToWorld.getColumn(3,&cameraOffset);
|
||||
cameraOffset -= state->getDiffuseCameraPosition();
|
||||
F32 dist = cameraOffset.len();
|
||||
F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
|
||||
|
||||
if( mShape )
|
||||
{
|
||||
mShape->setDetailFromDistance( state, dist * invScale );
|
||||
if( mShape->getCurrentDetail() < 0 )
|
||||
return;
|
||||
}
|
||||
|
||||
if( mPart )
|
||||
{
|
||||
// get the shapeInstance that we are using for the debris parts
|
||||
TSShapeInstance *si = mPart->getSourceShapeInstance();
|
||||
if ( si )
|
||||
si->setDetailFromDistance( state, dist * invScale );
|
||||
}
|
||||
|
||||
prepBatchRender( state );
|
||||
}
|
||||
|
||||
void Debris::prepBatchRender( SceneRenderState *state )
|
||||
{
|
||||
if ( !mShape && !mPart )
|
||||
return;
|
||||
|
||||
GFXTransformSaver saver;
|
||||
|
||||
F32 alpha = 1.0;
|
||||
if( mDataBlock->fade )
|
||||
{
|
||||
if( mLifetime < 1.0 ) alpha = mLifetime;
|
||||
}
|
||||
|
||||
Point3F cameraOffset;
|
||||
mObjToWorld.getColumn(3,&cameraOffset);
|
||||
cameraOffset -= state->getCameraPosition();
|
||||
|
||||
// Set up our TS render state.
|
||||
TSRenderState rdata;
|
||||
rdata.setSceneState( state );
|
||||
|
||||
// We might have some forward lit materials
|
||||
// so pass down a query to gather lights.
|
||||
LightQuery query;
|
||||
query.init( getWorldSphere() );
|
||||
rdata.setLightQuery( &query );
|
||||
|
||||
if( mShape )
|
||||
{
|
||||
MatrixF mat = getRenderTransform();
|
||||
GFX->setWorldMatrix( mat );
|
||||
|
||||
rdata.setFadeOverride( alpha );
|
||||
mShape->render( rdata );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mPart->getCurrentObjectDetail() != -1)
|
||||
{
|
||||
MatrixF mat = getRenderTransform();
|
||||
GFX->setWorldMatrix( mat );
|
||||
|
||||
rdata.setFadeOverride( alpha );
|
||||
mPart->render( rdata );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Debris::setSize( F32 size )
|
||||
{
|
||||
mSize = size;
|
||||
}
|
||||
173
Engine/source/T3D/debris.h
Normal file
173
Engine/source/T3D/debris.h
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DEBRIS_H_
|
||||
#define _DEBRIS_H_
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/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 Decs
|
||||
//-----------------------------------------------------------------------
|
||||
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 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;
|
||||
|
||||
|
||||
S32 explosionId;
|
||||
ExplosionData * explosion;
|
||||
ParticleEmitterData* emitterList[DDC_NUM_EMITTERS];
|
||||
S32 emitterIDList[DDC_NUM_EMITTERS];
|
||||
|
||||
DebrisData();
|
||||
|
||||
bool onAdd();
|
||||
bool preload( bool server, String &errorStr );
|
||||
static void initPersistFields();
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(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;
|
||||
|
||||
SimObjectPtr<ParticleEmitter> mEmitterList[ DebrisData::DDC_NUM_EMITTERS ];
|
||||
|
||||
/// Bounce the debris - returns true if debris bounces.
|
||||
bool bounce( const Point3F &nextPos, F32 dt );
|
||||
|
||||
/// Compute state of debris as if it hasn't collided with anything.
|
||||
void computeNewState( Point3F &newPos, Point3F &newVel, F32 dt );
|
||||
|
||||
void explode();
|
||||
void rotate( F32 dt );
|
||||
|
||||
protected:
|
||||
virtual void processTick(const Move* move);
|
||||
virtual void advanceTime( F32 dt );
|
||||
void prepRenderImage(SceneRenderState *state);
|
||||
void prepBatchRender(SceneRenderState *state);
|
||||
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void updateEmitters( Point3F &pos, Point3F &vel, U32 ms );
|
||||
|
||||
public:
|
||||
|
||||
Debris();
|
||||
~Debris();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
bool onNewDataBlock( GameBaseData *dptr, bool reload );
|
||||
|
||||
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
|
||||
481
Engine/source/T3D/decal/decalData.cpp
Normal file
481
Engine/source/T3D/decal/decalData.cpp
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/decal/decalData.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
#include "T3D/objectTypes.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
GFXImplementVertexFormat( DecalVertex )
|
||||
{
|
||||
addElement( "POSITION", GFXDeclType_Float3 );
|
||||
addElement( "NORMAL", GFXDeclType_Float3 );
|
||||
addElement( "TANGENT", GFXDeclType_Float3 );
|
||||
addElement( "COLOR", GFXDeclType_Color );
|
||||
addElement( "TEXCOORD", GFXDeclType_Float2, 0 );
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1( DecalData );
|
||||
|
||||
ConsoleDocClass( DecalData,
|
||||
"@brief A datablock describing an individual decal.\n\n"
|
||||
|
||||
"The textures defined by the decal Material can be divided into multiple "
|
||||
"rectangular sub-textures as shown below, with a different sub-texture "
|
||||
"selected by all decals using the same DecalData (via #frame) or each decal "
|
||||
"instance (via #randomize).\n"
|
||||
|
||||
"@image html images/decal_example.png \"Example of a Decal imagemap\"\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock DecalData(BulletHoleDecal)\n"
|
||||
"{\n"
|
||||
" material = \"DECAL_BulletHole\";\n"
|
||||
" size = \"5.0\";\n"
|
||||
" lifeSpan = \"50000\";\n"
|
||||
" randomize = \"1\";\n"
|
||||
" texRows = \"2\";\n"
|
||||
" texCols = \"2\";\n"
|
||||
" clippingAngle = \"60\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see Decals\n"
|
||||
"@ingroup Decals\n"
|
||||
"@ingroup FX\n"
|
||||
);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// DecalData
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
DecalData::DecalData()
|
||||
{
|
||||
size = 5;
|
||||
materialName = "";
|
||||
|
||||
lifeSpan = 5000;
|
||||
fadeTime = 1000;
|
||||
|
||||
frame = 0;
|
||||
randomize = false;
|
||||
texRows = 1;
|
||||
texCols = 1;
|
||||
|
||||
fadeStartPixelSize = -1.0f;
|
||||
fadeEndPixelSize = 200.0f;
|
||||
|
||||
material = NULL;
|
||||
matInst = NULL;
|
||||
|
||||
renderPriority = 10;
|
||||
clippingMasks = STATIC_COLLISION_TYPEMASK;
|
||||
clippingAngle = 89.0f;
|
||||
|
||||
texCoordCount = 1;
|
||||
|
||||
// TODO: We could in theory calculate if we can skip
|
||||
// normals on the decal by checking the material features.
|
||||
skipVertexNormals = false;
|
||||
|
||||
for ( S32 i = 0; i < 16; i++ )
|
||||
{
|
||||
texRect[i].point.set( 0.0f, 0.0f );
|
||||
texRect[i].extent.set( 1.0f, 1.0f );
|
||||
}
|
||||
}
|
||||
|
||||
DecalData::~DecalData()
|
||||
{
|
||||
SAFE_DELETE( matInst );
|
||||
}
|
||||
|
||||
bool DecalData::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
if (size < 0.0) {
|
||||
Con::warnf("DecalData::onAdd: size < 0");
|
||||
size = 0;
|
||||
}
|
||||
|
||||
getSet()->addObject( this );
|
||||
|
||||
if( texRows > 1 || texCols > 1 )
|
||||
reloadRects();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DecalData::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void DecalData::initPersistFields()
|
||||
{
|
||||
addGroup( "Decal" );
|
||||
|
||||
addField( "size", TypeF32, Offset( size, DecalData ),
|
||||
"Width and height of the decal in meters before scale is applied." );
|
||||
|
||||
addField( "material", TypeMaterialName, Offset( materialName, DecalData ),
|
||||
"Material to use for this decal." );
|
||||
|
||||
addField( "lifeSpan", TypeS32, Offset( lifeSpan, DecalData ),
|
||||
"Time (in milliseconds) before this decal will be automatically deleted." );
|
||||
|
||||
addField( "fadeTime", TypeS32, Offset( fadeTime, DecalData ),
|
||||
"@brief Time (in milliseconds) over which to fade out the decal before "
|
||||
"deleting it at the end of its lifetime.\n\n"
|
||||
"@see lifeSpan" );
|
||||
|
||||
endGroup( "Decal" );
|
||||
|
||||
addGroup( "Rendering" );
|
||||
|
||||
addField( "fadeStartPixelSize", TypeF32, Offset( fadeStartPixelSize, DecalData ),
|
||||
"@brief LOD value - size in pixels at which decals of this type begin "
|
||||
"to fade out.\n\n"
|
||||
"This should be a larger value than #fadeEndPixelSize. However, you may "
|
||||
"also set this to a negative value to disable lod-based fading." );
|
||||
|
||||
addField( "fadeEndPixelSize", TypeF32, Offset( fadeEndPixelSize, DecalData ),
|
||||
"@brief LOD value - size in pixels at which decals of this type are "
|
||||
"fully faded out.\n\n"
|
||||
"This should be a smaller value than #fadeStartPixelSize." );
|
||||
|
||||
addField( "renderPriority", TypeS8, Offset( renderPriority, DecalData ),
|
||||
"Default renderPriority for decals of this type (determines draw "
|
||||
"order when decals overlap)." );
|
||||
|
||||
addField( "clippingAngle", TypeF32, Offset( clippingAngle, DecalData ),
|
||||
"The angle in degrees used to clip geometry that faces away from the "
|
||||
"decal projection direction." );
|
||||
|
||||
endGroup( "Rendering" );
|
||||
|
||||
addGroup( "Texturing" );
|
||||
|
||||
addField( "frame", TypeS32, Offset( frame, DecalData ),
|
||||
"Index of the texture rectangle within the imagemap to use for this decal." );
|
||||
|
||||
addField( "randomize", TypeBool, Offset( randomize, DecalData ),
|
||||
"If true, a random frame from the imagemap is selected for each "
|
||||
"instance of the decal." );
|
||||
|
||||
addField( "textureCoordCount", TypeS32, Offset( texCoordCount, DecalData ),
|
||||
"Number of individual frames in the imagemap (maximum 16)." );
|
||||
|
||||
addField( "texRows", TypeS32, Offset( texRows, DecalData ),
|
||||
"@brief Number of rows in the supplied imagemap.\n\n"
|
||||
"Use #texRows and #texCols if the imagemap frames are arranged in a "
|
||||
"grid; use #textureCoords to manually specify UV coordinates for "
|
||||
"irregular sized frames." );
|
||||
|
||||
addField( "texCols", TypeS32, Offset( texCols, DecalData ),
|
||||
"@brief Number of columns in the supplied imagemap.\n\n"
|
||||
"Use #texRows and #texCols if the imagemap frames are arranged in a "
|
||||
"grid; use #textureCoords to manually specify UV coordinates for "
|
||||
"irregular sized frames." );
|
||||
|
||||
addField( "textureCoords", TypeRectF, Offset( texRect, DecalData ), MAX_TEXCOORD_COUNT,
|
||||
"@brief An array of RectFs (topleft.x topleft.y extent.x extent.y) "
|
||||
"representing the UV coordinates for each frame in the imagemap.\n\n"
|
||||
"@note This field should only be set if the imagemap frames are "
|
||||
"irregular in size. Otherwise use the #texRows and #texCols fields "
|
||||
"and the UV coordinates will be calculated automatically." );
|
||||
|
||||
endGroup( "Texturing" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void DecalData::onStaticModified( const char *slotName, const char *newValue )
|
||||
{
|
||||
Parent::onStaticModified( slotName, newValue );
|
||||
|
||||
if ( !isProperlyAdded() )
|
||||
return;
|
||||
|
||||
// To allow changing materials live.
|
||||
if ( dStricmp( slotName, "material" ) == 0 )
|
||||
{
|
||||
materialName = newValue;
|
||||
_updateMaterial();
|
||||
}
|
||||
// To allow changing name live.
|
||||
else if ( dStricmp( slotName, "name" ) == 0 )
|
||||
{
|
||||
lookupName = getName();
|
||||
}
|
||||
else if ( dStricmp( slotName, "renderPriority" ) == 0 )
|
||||
{
|
||||
renderPriority = getMax( renderPriority, (U8)1 );
|
||||
}
|
||||
}
|
||||
|
||||
bool DecalData::preload( bool server, String &errorStr )
|
||||
{
|
||||
if (Parent::preload(server, errorStr) == false)
|
||||
return false;
|
||||
|
||||
// Server assigns name to lookupName,
|
||||
// client assigns lookupName in unpack.
|
||||
if ( server )
|
||||
lookupName = getName();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DecalData::packData( BitStream *stream )
|
||||
{
|
||||
Parent::packData( stream );
|
||||
|
||||
stream->write( lookupName );
|
||||
stream->write( size );
|
||||
stream->write( materialName );
|
||||
stream->write( lifeSpan );
|
||||
stream->write( fadeTime );
|
||||
stream->write( texCoordCount );
|
||||
|
||||
for (S32 i = 0; i < texCoordCount; i++)
|
||||
mathWrite( *stream, texRect[i] );
|
||||
|
||||
stream->write( fadeStartPixelSize );
|
||||
stream->write( fadeEndPixelSize );
|
||||
stream->write( renderPriority );
|
||||
stream->write( clippingMasks );
|
||||
stream->write( clippingAngle );
|
||||
|
||||
stream->write( texRows );
|
||||
stream->write( texCols );
|
||||
stream->write( frame );
|
||||
stream->write( randomize );
|
||||
}
|
||||
|
||||
void DecalData::unpackData( BitStream *stream )
|
||||
{
|
||||
Parent::unpackData( stream );
|
||||
|
||||
stream->read( &lookupName );
|
||||
stream->read( &size );
|
||||
stream->read( &materialName );
|
||||
_updateMaterial();
|
||||
stream->read( &lifeSpan );
|
||||
stream->read( &fadeTime );
|
||||
stream->read( &texCoordCount );
|
||||
|
||||
for (S32 i = 0; i < texCoordCount; i++)
|
||||
mathRead(*stream, &texRect[i]);
|
||||
|
||||
stream->read( &fadeStartPixelSize );
|
||||
stream->read( &fadeEndPixelSize );
|
||||
stream->read( &renderPriority );
|
||||
stream->read( &clippingMasks );
|
||||
stream->read( &clippingAngle );
|
||||
|
||||
stream->read( &texRows );
|
||||
stream->read( &texCols );
|
||||
stream->read( &frame );
|
||||
stream->read( &randomize );
|
||||
}
|
||||
|
||||
void DecalData::_initMaterial()
|
||||
{
|
||||
SAFE_DELETE( matInst );
|
||||
|
||||
if ( material )
|
||||
matInst = material->createMatInstance();
|
||||
else
|
||||
matInst = MATMGR->createMatInstance( "WarningMaterial" );
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setZReadWrite( true, false );
|
||||
//desc.zFunc = GFXCmpLess;
|
||||
matInst->addStateBlockDesc( desc );
|
||||
|
||||
matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<DecalVertex>() );
|
||||
if( !matInst->isValid() )
|
||||
{
|
||||
Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", materialName.c_str() );
|
||||
SAFE_DELETE( matInst );
|
||||
matInst = MATMGR->createMatInstance( "WarningMaterial" );
|
||||
matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat< DecalVertex >() );
|
||||
}
|
||||
}
|
||||
|
||||
void DecalData::_updateMaterial()
|
||||
{
|
||||
if ( materialName.isEmpty() )
|
||||
return;
|
||||
|
||||
Material *pMat = NULL;
|
||||
if ( !Sim::findObject( materialName, pMat ) )
|
||||
{
|
||||
Con::printf( "DecalData::unpackUpdate, failed to find Material of name %s!", materialName.c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
material = pMat;
|
||||
|
||||
// Only update material instance if we have one allocated.
|
||||
if ( matInst )
|
||||
_initMaterial();
|
||||
}
|
||||
|
||||
Material* DecalData::getMaterial()
|
||||
{
|
||||
if ( !material )
|
||||
{
|
||||
_updateMaterial();
|
||||
if ( !material )
|
||||
material = static_cast<Material*>( Sim::findObject("WarningMaterial") );
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
BaseMatInstance* DecalData::getMaterialInstance()
|
||||
{
|
||||
if ( !material || !matInst || matInst->getMaterial() != material )
|
||||
_initMaterial();
|
||||
|
||||
return matInst;
|
||||
}
|
||||
|
||||
DecalData* DecalData::findDatablock( String searchName )
|
||||
{
|
||||
StringTableEntry className = DecalData::getStaticClassRep()->getClassName();
|
||||
DecalData *pData;
|
||||
SimSet *set = getSet();
|
||||
SimSetIterator iter( set );
|
||||
|
||||
for ( ; *iter; ++iter )
|
||||
{
|
||||
if ( (*iter)->getClassName() != className )
|
||||
{
|
||||
Con::errorf( "DecalData::findDatablock - found a class %s object in DecalDataSet!", (*iter)->getClassName() );
|
||||
continue;
|
||||
}
|
||||
|
||||
pData = static_cast<DecalData*>( *iter );
|
||||
if ( pData->lookupName.equal( searchName, String::NoCase ) )
|
||||
return pData;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DecalData::inspectPostApply()
|
||||
{
|
||||
reloadRects();
|
||||
}
|
||||
|
||||
void DecalData::reloadRects()
|
||||
{
|
||||
F32 rowsBase = 0;
|
||||
F32 colsBase = 0;
|
||||
bool canRenderRowsByFrame = false;
|
||||
bool canRenderColsByFrame = false;
|
||||
S32 id = 0;
|
||||
|
||||
texRect[id].point.x = 0.f;
|
||||
texRect[id].extent.x = 1.f;
|
||||
texRect[id].point.y = 0.f;
|
||||
texRect[id].extent.y = 1.f;
|
||||
|
||||
texCoordCount = (texRows * texCols) - 1;
|
||||
|
||||
if( texCoordCount > 16 )
|
||||
{
|
||||
Con::warnf("Coordinate max must be lower than 16 to be a valid decal !");
|
||||
texRows = 1;
|
||||
texCols = 1;
|
||||
texCoordCount = 1;
|
||||
}
|
||||
|
||||
// use current datablock information in order to build a template to extract
|
||||
// coordinates from.
|
||||
if( texRows > 1 )
|
||||
{
|
||||
rowsBase = ( 1.f / texRows );
|
||||
canRenderRowsByFrame = true;
|
||||
}
|
||||
if( texCols > 1 )
|
||||
{
|
||||
colsBase = ( 1.f / texCols );
|
||||
canRenderColsByFrame = true;
|
||||
}
|
||||
|
||||
// if were able, lets enter the loop
|
||||
if( frame >= 0 && (canRenderRowsByFrame || canRenderColsByFrame) )
|
||||
{
|
||||
// columns first then rows
|
||||
for ( S32 colId = 1; colId <= texCols; colId++ )
|
||||
{
|
||||
for ( S32 rowId = 1; rowId <= texRows; rowId++, id++ )
|
||||
{
|
||||
// if were over the coord count, lets go
|
||||
if(id > texCoordCount)
|
||||
return;
|
||||
|
||||
// keep our dimensions correct
|
||||
if(rowId > texRows)
|
||||
rowId = 1;
|
||||
|
||||
if(colId > texCols)
|
||||
colId = 1;
|
||||
|
||||
// start setting our rect values per frame
|
||||
if( canRenderRowsByFrame )
|
||||
{
|
||||
texRect[id].point.x = rowsBase * ( rowId - 1 );
|
||||
texRect[id].extent.x = rowsBase;
|
||||
}
|
||||
|
||||
if( canRenderColsByFrame )
|
||||
{
|
||||
texRect[id].point.y = colsBase * ( colId - 1 );
|
||||
texRect[id].extent.y = colsBase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefineEngineMethod(DecalData, postApply, void, (),,
|
||||
"Recompute the imagemap sub-texture rectangles for this DecalData.\n"
|
||||
"@tsexample\n"
|
||||
"// Inform the decal object to reload its imagemap and frame data.\n"
|
||||
"%decalData.texRows = 4;\n"
|
||||
"%decalData.postApply();\n"
|
||||
"@endtsexample\n")
|
||||
{
|
||||
object->inspectPostApply();
|
||||
}
|
||||
143
Engine/source/T3D/decal/decalData.h
Normal file
143
Engine/source/T3D/decal/decalData.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DECALDATA_H_
|
||||
#define _DECALDATA_H_
|
||||
|
||||
#ifndef _SIMDATABLOCK_H_
|
||||
#include "console/simDatablock.h"
|
||||
#endif
|
||||
#ifndef _MATERIALDEFINITION_H_
|
||||
#include "materials/materialDefinition.h"
|
||||
#endif
|
||||
#ifndef _MRECT_H_
|
||||
#include "math/mRect.h"
|
||||
#endif
|
||||
#ifndef _DYNAMIC_CONSOLETYPES_H_
|
||||
#include "console/dynamicTypes.h"
|
||||
#endif
|
||||
|
||||
GFXDeclareVertexFormat( DecalVertex )
|
||||
{
|
||||
// .xyz = coords
|
||||
Point3F point;
|
||||
Point3F normal;
|
||||
Point3F tangent;
|
||||
GFXVertexColor color;
|
||||
Point2F texCoord;
|
||||
};
|
||||
|
||||
/// DataBlock implementation for decals.
|
||||
class DecalData : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
|
||||
enum { MAX_TEXCOORD_COUNT = 16 };
|
||||
|
||||
F32 size;
|
||||
|
||||
/// Milliseconds for decal to expire.
|
||||
U32 lifeSpan;
|
||||
|
||||
/// Milliseconds for decal to fade after expiration.
|
||||
U32 fadeTime;
|
||||
|
||||
S32 texCoordCount;
|
||||
RectF texRect[MAX_TEXCOORD_COUNT];
|
||||
|
||||
///
|
||||
S32 frame;
|
||||
bool randomize;
|
||||
S32 texRows;
|
||||
S32 texCols;
|
||||
|
||||
F32 fadeStartPixelSize;
|
||||
F32 fadeEndPixelSize;
|
||||
|
||||
/// Name of material to use.
|
||||
String materialName;
|
||||
|
||||
/// Render material for decal.
|
||||
SimObjectPtr<Material> material;
|
||||
|
||||
/// Material instance for decal.
|
||||
BaseMatInstance *matInst;
|
||||
|
||||
String lookupName;
|
||||
|
||||
U8 renderPriority;
|
||||
|
||||
S32 clippingMasks;
|
||||
|
||||
/// The angle in degress used to clip geometry
|
||||
/// that faces away from the decal projection.
|
||||
F32 clippingAngle;
|
||||
|
||||
/// Skip generating and collecting vertex normals for decals.
|
||||
bool skipVertexNormals;
|
||||
|
||||
public:
|
||||
|
||||
DecalData();
|
||||
~DecalData();
|
||||
|
||||
DECLARE_CONOBJECT(DecalData);
|
||||
static void initPersistFields();
|
||||
virtual void onStaticModified( const char *slotName, const char *newValue = NULL );
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
virtual bool preload( bool server, String &errorStr );
|
||||
virtual void packData( BitStream* );
|
||||
virtual void unpackData( BitStream* );
|
||||
|
||||
Material* getMaterial();
|
||||
BaseMatInstance* getMaterialInstance();
|
||||
|
||||
static SimSet* getSet();
|
||||
static DecalData* findDatablock( String lookupName );
|
||||
|
||||
virtual void inspectPostApply();
|
||||
void reloadRects();
|
||||
|
||||
protected:
|
||||
|
||||
void _initMaterial();
|
||||
void _updateMaterial();
|
||||
};
|
||||
|
||||
inline SimSet* DecalData::getSet()
|
||||
{
|
||||
SimSet *set = NULL;
|
||||
if ( !Sim::findObject( "DecalDataSet", set ) )
|
||||
{
|
||||
set = new SimSet;
|
||||
set->registerObject( "DecalDataSet" );
|
||||
Sim::getRootGroup()->addObject( set );
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
#endif // _DECALDATA_H_
|
||||
407
Engine/source/T3D/decal/decalDataFile.cpp
Normal file
407
Engine/source/T3D/decal/decalDataFile.cpp
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "decalDataFile.h"
|
||||
|
||||
#include "math/mathIO.h"
|
||||
#include "core/tAlgorithm.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
|
||||
#include "T3D/decal/decalManager.h"
|
||||
#include "T3D/decal/decalData.h"
|
||||
|
||||
|
||||
template<>
|
||||
void* Resource<DecalDataFile>::create( const Torque::Path &path )
|
||||
{
|
||||
FileStream stream;
|
||||
stream.open( path.getFullPath(), Torque::FS::File::Read );
|
||||
if ( stream.getStatus() != Stream::Ok )
|
||||
return NULL;
|
||||
|
||||
DecalDataFile *file = new DecalDataFile();
|
||||
if ( !file->read( stream ) )
|
||||
{
|
||||
delete file;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
template<>
|
||||
ResourceBase::Signature Resource<DecalDataFile>::signature()
|
||||
{
|
||||
return MakeFourCC('d','e','c','f');
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DecalDataFile::DecalDataFile()
|
||||
: mIsDirty( false ),
|
||||
mSphereWithLastInsertion( NULL )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mSphereList );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DecalDataFile::~DecalDataFile()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DecalDataFile::clear()
|
||||
{
|
||||
for ( U32 i=0; i < mSphereList.size(); i++ )
|
||||
delete mSphereList[i];
|
||||
|
||||
mSphereList.clear();
|
||||
mSphereWithLastInsertion = NULL;
|
||||
mChunker.freeBlocks();
|
||||
|
||||
mIsDirty = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool DecalDataFile::write( Stream& stream )
|
||||
{
|
||||
// Write our identifier... so we have a better
|
||||
// idea if we're reading pure garbage.
|
||||
// This identifier stands for "Torque Decal Data File".
|
||||
stream.write( 4, "TDDF" );
|
||||
|
||||
// Now the version number.
|
||||
stream.write( (U8)FILE_VERSION );
|
||||
|
||||
Vector<DecalInstance*> allDecals;
|
||||
|
||||
// Gather all DecalInstances that should be saved.
|
||||
for ( U32 i = 0; i < mSphereList.size(); i++ )
|
||||
{
|
||||
Vector<DecalInstance*>::const_iterator item = mSphereList[i]->mItems.begin();
|
||||
for ( ; item != mSphereList[i]->mItems.end(); item++ )
|
||||
{
|
||||
if ( (*item)->mFlags & SaveDecal )
|
||||
allDecals.push_back( (*item) );
|
||||
}
|
||||
}
|
||||
|
||||
// Gather all the DecalData datablocks used.
|
||||
Vector<const DecalData*> allDatablocks;
|
||||
for ( U32 i = 0; i < allDecals.size(); i++ )
|
||||
allDatablocks.push_back_unique( allDecals[i]->mDataBlock );
|
||||
|
||||
// Write out datablock lookupNames.
|
||||
U32 count = allDatablocks.size();
|
||||
stream.write( count );
|
||||
for ( U32 i = 0; i < count; i++ )
|
||||
stream.write( allDatablocks[i]->lookupName );
|
||||
|
||||
Vector<const DecalData*>::iterator dataIter;
|
||||
|
||||
// Write out the DecalInstance list.
|
||||
count = allDecals.size();
|
||||
stream.write( count );
|
||||
for ( U32 i = 0; i < count; i++ )
|
||||
{
|
||||
DecalInstance *inst = allDecals[i];
|
||||
|
||||
dataIter = find( allDatablocks.begin(), allDatablocks.end(), inst->mDataBlock );
|
||||
U8 dataIndex = dataIter - allDatablocks.begin();
|
||||
|
||||
stream.write( dataIndex );
|
||||
mathWrite( stream, inst->mPosition );
|
||||
mathWrite( stream, inst->mNormal );
|
||||
mathWrite( stream, inst->mTangent );
|
||||
stream.write( inst->mTextureRectIdx );
|
||||
stream.write( inst->mSize );
|
||||
stream.write( inst->mRenderPriority );
|
||||
}
|
||||
|
||||
// Clear the dirty flag.
|
||||
mIsDirty = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool DecalDataFile::read( Stream &stream )
|
||||
{
|
||||
// NOTE: we are shortcutting by just saving out the DecalInst and
|
||||
// using regular addDecal methods to add them, which will end up
|
||||
// generating the DecalSphere(s) in the process.
|
||||
// It would be more efficient however to just save out all the data
|
||||
// and read it all in with no calculation required.
|
||||
|
||||
// Read our identifier... so we know we're
|
||||
// not reading in pure garbage.
|
||||
char id[4] = { 0 };
|
||||
stream.read( 4, id );
|
||||
if ( dMemcmp( id, "TDDF", 4 ) != 0 )
|
||||
{
|
||||
Con::errorf( "DecalDataFile::read() - This is not a Decal file!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Empty ourselves before we really begin reading.
|
||||
clear();
|
||||
|
||||
// Now the version number.
|
||||
U8 version;
|
||||
stream.read( &version );
|
||||
if ( version != (U8)FILE_VERSION )
|
||||
{
|
||||
Con::errorf( "DecalDataFile::read() - file versions do not match!" );
|
||||
Con::errorf( "You must manually delete the old .decals file before continuing!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in the lookupNames of the DecalData datablocks and recover the datablock.
|
||||
Vector<DecalData*> allDatablocks;
|
||||
U32 count;
|
||||
stream.read( &count );
|
||||
allDatablocks.setSize( count );
|
||||
for ( U32 i = 0; i < count; i++ )
|
||||
{
|
||||
String lookupName;
|
||||
stream.read( &lookupName );
|
||||
|
||||
DecalData *data = DecalData::findDatablock( lookupName );
|
||||
|
||||
if ( !data )
|
||||
{
|
||||
char name[512];
|
||||
dSprintf(name, 512, "%s_missing", lookupName.c_str());
|
||||
|
||||
DecalData *stubCheck = DecalData::findDatablock( name );
|
||||
if( !stubCheck )
|
||||
{
|
||||
data = new DecalData;
|
||||
data->lookupName = name;
|
||||
data->registerObject(name);
|
||||
Sim::getRootGroup()->addObject( data );
|
||||
data->materialName = "WarningMaterial";
|
||||
data->material = dynamic_cast<Material*>(Sim::findObject("WarningMaterial"));
|
||||
|
||||
Con::errorf( "DecalDataFile::read() - DecalData %s does not exist! Temporarily created %s_missing.", lookupName.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
allDatablocks[ i ] = data;
|
||||
}
|
||||
|
||||
U8 dataIndex;
|
||||
DecalData *data;
|
||||
|
||||
// Now read all the DecalInstance(s).
|
||||
stream.read( &count );
|
||||
for ( U32 i = 0; i < count; i++ )
|
||||
{
|
||||
DecalInstance *inst = _allocateInstance();
|
||||
|
||||
stream.read( &dataIndex );
|
||||
mathRead( stream, &inst->mPosition );
|
||||
mathRead( stream, &inst->mNormal );
|
||||
mathRead( stream, &inst->mTangent );
|
||||
stream.read( &inst->mTextureRectIdx );
|
||||
stream.read( &inst->mSize );
|
||||
stream.read( &inst->mRenderPriority );
|
||||
|
||||
inst->mVisibility = 1.0f;
|
||||
inst->mFlags = PermanentDecal | SaveDecal | ClipDecal;
|
||||
inst->mCreateTime = Sim::getCurrentTime();
|
||||
inst->mVerts = NULL;
|
||||
inst->mIndices = NULL;
|
||||
inst->mVertCount = 0;
|
||||
inst->mIndxCount = 0;
|
||||
|
||||
data = allDatablocks[ dataIndex ];
|
||||
|
||||
if ( data )
|
||||
{
|
||||
inst->mDataBlock = data;
|
||||
|
||||
_addDecalToSpheres( inst );
|
||||
|
||||
// onload set instances should get added to the appropriate vec
|
||||
inst->mId = gDecalManager->mDecalInstanceVec.size();
|
||||
gDecalManager->mDecalInstanceVec.push_back(inst);
|
||||
}
|
||||
else
|
||||
{
|
||||
_freeInstance( inst );
|
||||
Con::errorf( "DecalDataFile::read - cannot find DecalData for DecalInstance read from disk." );
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the dirty flag.
|
||||
mIsDirty = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DecalInstance* DecalDataFile::addDecal( const Point3F& pos, const Point3F& normal, const Point3F& tangent,
|
||||
DecalData* decalData, F32 decalScale, S32 decalTexIndex, U8 flags )
|
||||
{
|
||||
DecalInstance* newDecal = _allocateInstance();
|
||||
|
||||
newDecal->mRenderPriority = 0;
|
||||
newDecal->mCustomTex = NULL;
|
||||
newDecal->mId = -1;
|
||||
|
||||
newDecal->mPosition = pos;
|
||||
newDecal->mNormal = normal;
|
||||
newDecal->mTangent = tangent;
|
||||
|
||||
newDecal->mSize = decalData->size * decalScale;
|
||||
|
||||
newDecal->mDataBlock = decalData;
|
||||
|
||||
S32 frame = newDecal->mDataBlock->frame;
|
||||
// randomize the frame if the flag is set. this number is used directly below us
|
||||
// when calculating render coords
|
||||
if ( decalData->randomize )
|
||||
frame = gRandGen.randI();
|
||||
|
||||
frame %= getMax( decalData->texCoordCount, 0 ) + 1;
|
||||
|
||||
newDecal->mTextureRectIdx = frame;
|
||||
|
||||
newDecal->mVisibility = 1.0f;
|
||||
|
||||
newDecal->mLastAlpha = -1.0f;
|
||||
|
||||
newDecal->mCreateTime = Sim::getCurrentTime();
|
||||
|
||||
newDecal->mVerts = NULL;
|
||||
newDecal->mIndices = NULL;
|
||||
newDecal->mVertCount = 0;
|
||||
newDecal->mIndxCount = 0;
|
||||
|
||||
newDecal->mFlags = flags;
|
||||
newDecal->mFlags |= ClipDecal;
|
||||
|
||||
_addDecalToSpheres( newDecal );
|
||||
|
||||
return newDecal;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DecalDataFile::_addDecalToSpheres( DecalInstance* inst )
|
||||
{
|
||||
// First try the sphere we have last inserted an item into, if there is one.
|
||||
// Given a good spatial locality of insertions, this is a reasonable first
|
||||
// guess as a good candidate.
|
||||
|
||||
if( mSphereWithLastInsertion && mSphereWithLastInsertion->tryAddItem( inst ) )
|
||||
return;
|
||||
|
||||
// Otherwise, go through the list and try to find an existing sphere that meets
|
||||
// our tolerances.
|
||||
|
||||
for( U32 i = 0; i < mSphereList.size(); i++ )
|
||||
{
|
||||
DecalSphere* sphere = mSphereList[i];
|
||||
|
||||
if( sphere == mSphereWithLastInsertion )
|
||||
continue;
|
||||
|
||||
if( sphere->tryAddItem( inst ) )
|
||||
{
|
||||
mSphereWithLastInsertion = sphere;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find a suitable existing sphere, so create a new one.
|
||||
|
||||
DecalSphere* sphere = new DecalSphere( inst->mPosition, inst->mSize / 2.f );
|
||||
|
||||
mSphereList.push_back( sphere );
|
||||
mSphereWithLastInsertion = sphere;
|
||||
|
||||
sphere->mItems.push_back( inst );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DecalDataFile::removeDecal( DecalInstance *inst )
|
||||
{
|
||||
if( !_removeDecalFromSpheres( inst ) )
|
||||
return;
|
||||
|
||||
_freeInstance( inst );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool DecalDataFile::_removeDecalFromSpheres( DecalInstance *inst )
|
||||
{
|
||||
for( U32 i = 0; i < mSphereList.size(); i++ )
|
||||
{
|
||||
DecalSphere* sphere = mSphereList[ i ];
|
||||
Vector< DecalInstance* > &items = sphere->mItems;
|
||||
|
||||
// Try to remove the instance from the list of this sphere.
|
||||
// If that fails, the instance doesn't belong to this sphere
|
||||
// so continue.
|
||||
|
||||
if( !items.remove( inst ) )
|
||||
continue;
|
||||
|
||||
// If the sphere is now empty, remove it. Otherwise, update
|
||||
// it's bounds.
|
||||
|
||||
if( items.empty() )
|
||||
{
|
||||
if( mSphereWithLastInsertion == sphere )
|
||||
mSphereWithLastInsertion = NULL;
|
||||
|
||||
delete sphere;
|
||||
mSphereList.erase( i );
|
||||
}
|
||||
else
|
||||
sphere->updateWorldSphere();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DecalDataFile::notifyDecalModified( DecalInstance *inst )
|
||||
{
|
||||
_removeDecalFromSpheres( inst );
|
||||
_addDecalToSpheres( inst );
|
||||
}
|
||||
133
Engine/source/T3D/decal/decalDataFile.h
Normal file
133
Engine/source/T3D/decal/decalDataFile.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DECALDATAFILE_H_
|
||||
#define _DECALDATAFILE_H_
|
||||
|
||||
#ifndef _DATACHUNKER_H_
|
||||
#include "core/dataChunker.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DECALSPHERE_H_
|
||||
#include "T3D/decal/decalSphere.h"
|
||||
#endif
|
||||
|
||||
|
||||
class Stream;
|
||||
class DecalData;
|
||||
|
||||
|
||||
|
||||
/// This is the data file for decals.
|
||||
/// Not intended to be used directly, do your work with decals
|
||||
/// via the DecalManager.
|
||||
class DecalDataFile
|
||||
{
|
||||
protected:
|
||||
|
||||
enum { FILE_VERSION = 5 };
|
||||
|
||||
/// Set to true if the file is dirty and
|
||||
/// needs to be saved before being destroyed.
|
||||
bool mIsDirty;
|
||||
|
||||
/// @name Memory Management
|
||||
/// @{
|
||||
|
||||
/// Allocator for DecalInstances.
|
||||
FreeListChunker< DecalInstance > mChunker;
|
||||
|
||||
/// Allocate a new, uninitialized DecalInstance.
|
||||
DecalInstance* _allocateInstance() { return mChunker.alloc(); }
|
||||
|
||||
/// Free the memory of the given DecalInstance.
|
||||
void _freeInstance( DecalInstance *decal ) { mChunker.free( decal ); }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Instance Management
|
||||
/// @{
|
||||
|
||||
/// The decal sphere that we have last insert an item into. This sphere
|
||||
/// is most likely to be a good candidate for the next insertion so
|
||||
/// test this sphere first.
|
||||
DecalSphere* mSphereWithLastInsertion;
|
||||
|
||||
/// List of bounding sphere shapes that contain and organize
|
||||
/// DecalInstances for optimized culling and lookup.
|
||||
Vector< DecalSphere* > mSphereList;
|
||||
|
||||
/// Add the given decal to the sphere list.
|
||||
void _addDecalToSpheres( DecalInstance *inst );
|
||||
|
||||
/// Remove the decal from the sphere list.
|
||||
bool _removeDecalFromSpheres( DecalInstance *inst );
|
||||
|
||||
/// @}
|
||||
|
||||
public:
|
||||
|
||||
DecalDataFile();
|
||||
virtual ~DecalDataFile();
|
||||
|
||||
Vector< DecalSphere* >& getSphereList() { return mSphereList; }
|
||||
const Vector< DecalSphere* >& getSphereList() const { return mSphereList; }
|
||||
|
||||
/// Return true if the decal data has been modified since the last save or load.
|
||||
bool isDirty() const { return mIsDirty; }
|
||||
|
||||
/// Deletes all the data and resets the
|
||||
/// file to an empty state.
|
||||
void clear();
|
||||
|
||||
/// @name I/O
|
||||
/// @{
|
||||
|
||||
/// Write the decal data to the given stream.
|
||||
bool write( Stream& stream );
|
||||
|
||||
/// Read the decal data from the given stream.
|
||||
bool read( Stream& stream );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Decal Management
|
||||
/// @{
|
||||
|
||||
/// Create a new decal in this file using the given data.
|
||||
DecalInstance* addDecal( const Point3F& pos, const Point3F& normal, const Point3F& tangent,
|
||||
DecalData* decalData, F32 decalScale, S32 decalTexIndex, U8 flags );
|
||||
|
||||
/// Remove a decal from the file.
|
||||
void removeDecal( DecalInstance *inst );
|
||||
|
||||
/// Let the file know that the data of the given decal has changed.
|
||||
void notifyDecalModified( DecalInstance *inst );
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // _DECALDATAFILE_H_
|
||||
73
Engine/source/T3D/decal/decalInstance.cpp
Normal file
73
Engine/source/T3D/decal/decalInstance.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/decal/decalInstance.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
|
||||
void DecalInstance::getWorldMatrix( MatrixF *outMat, bool flip )
|
||||
{
|
||||
outMat->setPosition( mPosition );
|
||||
|
||||
Point3F fvec;
|
||||
mCross( mNormal, mTangent, &fvec );
|
||||
|
||||
outMat->setColumn( 0, mTangent );
|
||||
outMat->setColumn( 1, fvec );
|
||||
outMat->setColumn( 2, mNormal );
|
||||
}
|
||||
|
||||
F32 DecalInstance::calcPixelSize( U32 viewportHeight, const Point3F &cameraPos, F32 worldToScreenScaleY ) const
|
||||
{
|
||||
// If fadeStartPixelSize is set less than zero this is interpreted to mean
|
||||
// pixelSize based fading is disabled and the decal always renders.
|
||||
// Returning a value of F32_MAX should be treated by the caller as
|
||||
// meaning "big enough to render at normal quality, dont LOD me.".
|
||||
if ( mDataBlock->fadeStartPixelSize < 0.0f )
|
||||
return F32_MAX;
|
||||
|
||||
// This is an approximation since mSize is actually the xyz size of the
|
||||
// cube we use to clip geometry for the decal. In this case we are assuming
|
||||
// it is appropriate to use the radius of the sphere 'inscribed' in that
|
||||
// cube as the equivalent of mShape->radius for purposes of calculating
|
||||
// the pixelScale ( see TSShapeInstance::setDetailFromDistance ).
|
||||
const F32 radius = mSize * 0.5f;
|
||||
|
||||
// Approximate distance to the decal.
|
||||
const F32 distance = ( cameraPos - mPosition ).len() - radius;
|
||||
|
||||
// We are inside the decal's volume. There is no useful pixelSize for us
|
||||
// to return, it is essentially the entire screen.
|
||||
if ( distance <= 0.0f )
|
||||
return F32_MAX;
|
||||
|
||||
// Lod is relative to a viewport with a heigh of 300. Could prescale this into decalSize...
|
||||
const F32 pixelScale = viewportHeight / 300.0f;
|
||||
|
||||
// Inline of SceneState::projectRadius.
|
||||
const F32 pixelSize = mSize / distance * worldToScreenScaleY * pixelScale;
|
||||
|
||||
// Optionally scale pixelSize by a detail adjust value here...
|
||||
// eg. pixelSize *= TSDetailAdjust...
|
||||
|
||||
return pixelSize;
|
||||
}
|
||||
93
Engine/source/T3D/decal/decalInstance.h
Normal file
93
Engine/source/T3D/decal/decalInstance.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DECALINSTANCE_H_
|
||||
#define _DECALINSTANCE_H_
|
||||
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DECALDATA_H_
|
||||
#include "T3D/decal/decalData.h"
|
||||
#endif
|
||||
|
||||
struct DecalVertex;
|
||||
class SceneRenderState;
|
||||
|
||||
/// DecalInstance represents a rendering decal in the scene.
|
||||
/// You should not allocate this yourself, add new decals to the scene
|
||||
/// via the DecalManager.
|
||||
/// All data is public, change it if you wish, but be sure to inform
|
||||
/// the DecalManager.
|
||||
class DecalInstance
|
||||
{
|
||||
public:
|
||||
|
||||
DecalData *mDataBlock;
|
||||
|
||||
Point3F mPosition;
|
||||
Point3F mNormal;
|
||||
Point3F mTangent;
|
||||
F32 mRotAroundNormal;
|
||||
F32 mSize;
|
||||
|
||||
U32 mCreateTime;
|
||||
F32 mVisibility;
|
||||
|
||||
F32 mLastAlpha;
|
||||
|
||||
U32 mTextureRectIdx;
|
||||
|
||||
DecalVertex *mVerts;
|
||||
U16 *mIndices;
|
||||
|
||||
U32 mVertCount;
|
||||
U32 mIndxCount;
|
||||
|
||||
U8 mFlags;
|
||||
|
||||
U8 mRenderPriority;
|
||||
|
||||
S32 mId;
|
||||
|
||||
GFXTexHandle *mCustomTex;
|
||||
|
||||
void getWorldMatrix( MatrixF *outMat, bool flip = false );
|
||||
|
||||
Box3F getWorldBox() const
|
||||
{
|
||||
return Box3F( mPosition - Point3F( mSize / 2.f ), mPosition + Point3F( mSize / 2.f ) );
|
||||
}
|
||||
|
||||
U8 getRenderPriority() const
|
||||
{
|
||||
return mRenderPriority == 0 ? mDataBlock->renderPriority : mRenderPriority;
|
||||
}
|
||||
|
||||
/// Calculates the size of this decal onscreen in pixels, used for LOD.
|
||||
F32 calcPixelSize( U32 viewportHeight, const Point3F &cameraPos, F32 worldToScreenScaleY ) const;
|
||||
|
||||
DecalInstance() : mId(-1) {}
|
||||
};
|
||||
|
||||
#endif // _DECALINSTANCE_H_
|
||||
1707
Engine/source/T3D/decal/decalManager.cpp
Normal file
1707
Engine/source/T3D/decal/decalManager.cpp
Normal file
File diff suppressed because it is too large
Load diff
278
Engine/source/T3D/decal/decalManager.h
Normal file
278
Engine/source/T3D/decal/decalManager.h
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DECALMANAGER_H_
|
||||
#define _DECALMANAGER_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MATHUTIL_FRUSTUM_H_
|
||||
#include "math/util/frustum.h"
|
||||
#endif
|
||||
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
|
||||
#ifndef _GFXDEVICE_H_
|
||||
#include "gfx/gfxDevice.h"
|
||||
#endif
|
||||
|
||||
#ifndef _CLIPPEDPOLYLIST_H_
|
||||
#include "collision/clippedPolyList.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DECALDATAFILE_H_
|
||||
#include "decalDataFile.h"
|
||||
#endif
|
||||
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DECALINSTANCE_H_
|
||||
#include "decalInstance.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TSIGNAL_H_
|
||||
#include "core/util/tSignal.h"
|
||||
#endif
|
||||
|
||||
#ifndef _DATACHUNKER_H_
|
||||
#include "core/dataChunker.h"
|
||||
#endif
|
||||
|
||||
|
||||
//#define DECALMANAGER_DEBUG
|
||||
|
||||
|
||||
struct ObjectRenderInst;
|
||||
class Material;
|
||||
|
||||
|
||||
enum DecalFlags
|
||||
{
|
||||
PermanentDecal = 1 << 0,
|
||||
SaveDecal = 1 << 1,
|
||||
ClipDecal = 1 << 2,
|
||||
CustomDecal = 1 << 3 // DecalManager will not attempt to clip or remove this decal
|
||||
// it is managed by someone else.
|
||||
};
|
||||
|
||||
|
||||
/// Manage decals in the scene.
|
||||
class DecalManager : public SceneObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SceneObject Parent;
|
||||
|
||||
// [rene, 11-Mar-11] This vector is very poorly managed; the logic is spread all over the place
|
||||
Vector<DecalInstance *> mDecalInstanceVec;
|
||||
|
||||
protected:
|
||||
|
||||
/// The clipper we keep around between decal updates
|
||||
/// to avoid excessive memory allocations.
|
||||
ClippedPolyList mClipper;
|
||||
|
||||
Vector<DecalInstance*> mDecalQueue;
|
||||
|
||||
StringTableEntry mDataFileName;
|
||||
Resource<DecalDataFile> mData;
|
||||
|
||||
Signal< void() > mClearDataSignal;
|
||||
|
||||
Vector< GFXVertexBufferHandle<DecalVertex>* > mVBs;
|
||||
Vector< GFXPrimitiveBufferHandle* > mPBs;
|
||||
|
||||
Vector< GFXVertexBufferHandle<DecalVertex>* > mVBPool;
|
||||
Vector< GFXPrimitiveBufferHandle* > mPBPool;
|
||||
|
||||
FreeListChunkerUntyped *mChunkers[3];
|
||||
|
||||
#ifdef DECALMANAGER_DEBUG
|
||||
Vector<PlaneF> mDebugPlanes;
|
||||
#endif
|
||||
|
||||
bool mDirty;
|
||||
|
||||
struct DecalBatch
|
||||
{
|
||||
U32 startDecal;
|
||||
U32 decalCount;
|
||||
U32 iCount;
|
||||
U32 vCount;
|
||||
U8 priority;
|
||||
Material *mat;
|
||||
BaseMatInstance *matInst;
|
||||
bool dynamic;
|
||||
};
|
||||
|
||||
/// Whether to render visualizations for debugging in the editor.
|
||||
static bool smDebugRender;
|
||||
|
||||
static bool smDecalsOn;
|
||||
static F32 smDecalLifeTimeScale;
|
||||
static bool smPoolBuffers;
|
||||
static const U32 smMaxVerts;
|
||||
static const U32 smMaxIndices;
|
||||
|
||||
// Assume that a class is already given for the object:
|
||||
// Point with coordinates {float x, y;}
|
||||
//===================================================================
|
||||
|
||||
// isLeft(): tests if a point is Left|On|Right of an infinite line.
|
||||
// Input: three points P0, P1, and P2
|
||||
// Return: >0 for P2 left of the line through P0 and P1
|
||||
// =0 for P2 on the line
|
||||
// <0 for P2 right of the line
|
||||
// See: the January 2001 Algorithm on Area of Triangles
|
||||
inline F32 isLeft( const Point3F &P0, const Point3F &P1, const Point3F &P2 )
|
||||
{
|
||||
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y);
|
||||
}
|
||||
|
||||
U32 _generateConvexHull( const Vector<Point3F> &points, Vector<Point3F> *outPoints );
|
||||
|
||||
// Rendering
|
||||
void prepRenderImage( SceneRenderState *state );
|
||||
|
||||
void _generateWindingOrder( const Point3F &cornerPoint, Vector<Point3F> *sortPoints );
|
||||
|
||||
// Helpers for creating and deleting the vert and index arrays
|
||||
// held by DecalInstance.
|
||||
void _allocBuffers( DecalInstance *inst );
|
||||
void _freeBuffers( DecalInstance *inst );
|
||||
void _freePools();
|
||||
|
||||
/// Returns index used to index into the correct sized FreeListChunker for
|
||||
/// allocating vertex and index arrays.
|
||||
S32 _getSizeClass( DecalInstance *inst ) const;
|
||||
|
||||
// Hide this from Doxygen
|
||||
/// @cond
|
||||
bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event);
|
||||
/// @endcond
|
||||
|
||||
void _renderDecalSpheres( ObjectRenderInst* inst, SceneRenderState* state, BaseMatInstance* overrideMat );
|
||||
|
||||
///
|
||||
void _handleZoningChangedEvent( SceneZoneSpaceManager* zoneManager );
|
||||
|
||||
bool _createDataFile();
|
||||
|
||||
// SceneObject.
|
||||
virtual bool onSceneAdd();
|
||||
virtual void onSceneRemove(); public:
|
||||
|
||||
public:
|
||||
|
||||
DecalManager();
|
||||
~DecalManager();
|
||||
|
||||
/// @name Decal Addition
|
||||
/// @{
|
||||
|
||||
/// Adds a decal using a normal and a rotation.
|
||||
///
|
||||
/// @param pos The 3d position for the decal center.
|
||||
/// @param normal The decal up vector.
|
||||
/// @param rotAroundNormal The decal rotation around the normal.
|
||||
/// @param decalData The datablock which defines this decal.
|
||||
/// @param decalScale A scalar for adjusting the default size of the decal.
|
||||
/// @param decalTexIndex Selects the texture coord index within the decal
|
||||
/// data to use. If it is less than zero then a random
|
||||
/// coord is selected.
|
||||
/// @param flags The decal flags. @see DecalFlags
|
||||
///
|
||||
DecalInstance* addDecal( const Point3F &pos,
|
||||
const Point3F &normal,
|
||||
F32 rotAroundNormal,
|
||||
DecalData *decalData,
|
||||
F32 decalScale = 1.0f,
|
||||
S32 decalTexIndex = 0,
|
||||
U8 flags = 0x000 );
|
||||
|
||||
/// Adds a decal using a normal and a tangent.
|
||||
///
|
||||
/// @param pos The 3d position for the decal center.
|
||||
/// @param normal The decal up vector.
|
||||
/// @param tanget The decal right vector.
|
||||
/// @param decalData The datablock which defines this decal.
|
||||
/// @param decalScale A scalar for adjusting the default size of the decal.
|
||||
/// @param decalTexIndex Selects the texture coord index within the decal
|
||||
/// data to use. If it is less than zero then a random
|
||||
/// coord is selected.
|
||||
/// @param flags The decal flags. @see DecalFlags
|
||||
///
|
||||
DecalInstance* addDecal( const Point3F &pos,
|
||||
const Point3F &normal,
|
||||
const Point3F &tangent,
|
||||
DecalData *decalData,
|
||||
F32 decalScale = 1.0f,
|
||||
S32 decalTexIndex = 0,
|
||||
U8 flags = 0 );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Decal Removal
|
||||
/// @{
|
||||
|
||||
void removeDecal( DecalInstance *inst );
|
||||
|
||||
/// @}
|
||||
|
||||
DecalInstance* getDecal( S32 id );
|
||||
|
||||
DecalInstance* getClosestDecal( const Point3F &pos );
|
||||
|
||||
/// Return the closest DecalInstance hit by a ray.
|
||||
DecalInstance* raycast( const Point3F &start, const Point3F &end, bool savedDecalsOnly = true );
|
||||
|
||||
//void dataDeleted( DecalData *data );
|
||||
|
||||
void saveDecals( const UTF8 *fileName );
|
||||
bool loadDecals( const UTF8 *fileName );
|
||||
void clearData();
|
||||
|
||||
/// Returns true if changes have been made since the last load/save
|
||||
bool isDirty() const { return mDirty; }
|
||||
|
||||
bool clipDecal( DecalInstance *decal, Vector<Point3F> *edgeVerts = NULL, const Point2F *clipDepth = NULL );
|
||||
|
||||
void notifyDecalModified( DecalInstance *inst );
|
||||
|
||||
Signal< void() >& getClearDataSignal() { return mClearDataSignal; }
|
||||
|
||||
Resource<DecalDataFile> getDecalDataFile() { return mData; }
|
||||
|
||||
// SimObject.
|
||||
DECLARE_CONOBJECT( DecalManager );
|
||||
static void consoleInit();
|
||||
};
|
||||
|
||||
extern DecalManager* gDecalManager;
|
||||
|
||||
#endif // _DECALMANAGER_H_
|
||||
109
Engine/source/T3D/decal/decalSphere.cpp
Normal file
109
Engine/source/T3D/decal/decalSphere.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/decal/decalSphere.h"
|
||||
|
||||
#include "scene/zones/sceneZoneSpaceManager.h"
|
||||
#include "T3D/decal/decalInstance.h"
|
||||
|
||||
|
||||
F32 DecalSphere::smDistanceTolerance = 30.0f;
|
||||
F32 DecalSphere::smRadiusTolerance = 40.0f;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool DecalSphere::tryAddItem( DecalInstance* inst )
|
||||
{
|
||||
// If we are further away from the center than our tolerance
|
||||
// allows, don't use this sphere.
|
||||
//
|
||||
// Note that we take the distance from the nearest point on the
|
||||
// bounding sphere rather than the distance to the center of the
|
||||
// decal. This takes decal sizes into account and generally works
|
||||
// better.
|
||||
|
||||
const F32 distCenterToCenter = ( mWorldSphere.center - inst->mPosition ).len();
|
||||
const F32 distBoundsToCenter = distCenterToCenter - inst->mSize / 2.f;
|
||||
|
||||
if( distBoundsToCenter > smDistanceTolerance )
|
||||
return false;
|
||||
|
||||
// Also, if adding the current decal to the sphere would make
|
||||
// it larger than our radius tolerance, don't use it.
|
||||
|
||||
const F32 newRadius = distCenterToCenter + inst->mSize / 2.f + 0.5f;
|
||||
if( newRadius > mWorldSphere.radius && newRadius > smRadiusTolerance )
|
||||
return false;
|
||||
|
||||
// Otherwise, go with this sphere and add the item to it.
|
||||
|
||||
mItems.push_back( inst );
|
||||
|
||||
// Update the sphere bounds, if necessary.
|
||||
|
||||
if( newRadius > mWorldSphere.radius )
|
||||
updateWorldSphere();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DecalSphere::updateWorldSphere()
|
||||
{
|
||||
AssertFatal( mItems.size() >= 1, "DecalSphere::updateWorldSphere - Sphere is empty!" );
|
||||
|
||||
Box3F aabb( mItems[ 0 ]->getWorldBox() );
|
||||
|
||||
const U32 numItems = mItems.size();
|
||||
for( U32 i = 1; i < numItems; ++ i )
|
||||
aabb.intersect( mItems[ i ]->getWorldBox() );
|
||||
|
||||
mWorldSphere = aabb.getBoundingSphere();
|
||||
|
||||
// Clear the zoning data so that it gets recomputed.
|
||||
mZones.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void DecalSphere::updateZoning( SceneZoneSpaceManager* zoneManager )
|
||||
{
|
||||
mZones.clear();
|
||||
|
||||
// If there's only a single zone in the world, it must be the
|
||||
// outdoor zone, so there's no point maintaining zoning information
|
||||
// on all the decalspheres.
|
||||
|
||||
if( zoneManager->getNumZones() == 1 )
|
||||
return;
|
||||
|
||||
// Otherwise query the scene graph for all zones that intersect with the
|
||||
// AABB around our world sphere.
|
||||
|
||||
Box3F aabb( mWorldSphere.radius );
|
||||
aabb.setCenter( mWorldSphere.center );
|
||||
|
||||
zoneManager->findZones( aabb, mZones );
|
||||
}
|
||||
83
Engine/source/T3D/decal/decalSphere.h
Normal file
83
Engine/source/T3D/decal/decalSphere.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DECALSPHERE_H_
|
||||
#define _DECALSPHERE_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
#ifndef _MSPHERE_H_
|
||||
#include "math/mSphere.h"
|
||||
#endif
|
||||
|
||||
|
||||
class DecalInstance;
|
||||
class SceneZoneSpaceManager;
|
||||
|
||||
|
||||
/// A bounding sphere in world space and a list of DecalInstance(s)
|
||||
/// contained by it. DecalInstance(s) are organized/binned in this fashion
|
||||
/// as a lookup and culling optimization.
|
||||
class DecalSphere
|
||||
{
|
||||
public:
|
||||
|
||||
static F32 smDistanceTolerance;
|
||||
static F32 smRadiusTolerance;
|
||||
|
||||
DecalSphere()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mItems );
|
||||
VECTOR_SET_ASSOCIATION( mZones );
|
||||
}
|
||||
DecalSphere( const Point3F &position, F32 radius )
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION( mItems );
|
||||
VECTOR_SET_ASSOCIATION( mZones );
|
||||
|
||||
mWorldSphere.center = position;
|
||||
mWorldSphere.radius = radius;
|
||||
}
|
||||
|
||||
/// Recompute #mWorldSphere from the current instance list.
|
||||
void updateWorldSphere();
|
||||
|
||||
/// Recompute the zoning information from the current bounds.
|
||||
void updateZoning( SceneZoneSpaceManager* zoneManager );
|
||||
|
||||
/// Decal instances in this sphere.
|
||||
Vector< DecalInstance* > mItems;
|
||||
|
||||
/// Zones that intersect with this sphere. If this is empty, the zoning
|
||||
/// state of the sphere is uninitialized.
|
||||
Vector< U32 > mZones;
|
||||
|
||||
/// World-space sphere corresponding to this DecalSphere.
|
||||
SphereF mWorldSphere;
|
||||
|
||||
///
|
||||
bool tryAddItem( DecalInstance* inst );
|
||||
};
|
||||
|
||||
#endif // !_DECALSPHERE_H_
|
||||
346
Engine/source/T3D/examples/renderMeshExample.cpp
Normal file
346
Engine/source/T3D/examples/renderMeshExample.cpp
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/examples/renderMeshExample.h"
|
||||
|
||||
#include "math/mathIO.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "lighting/lightQuery.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(RenderMeshExample);
|
||||
|
||||
ConsoleDocClass( RenderMeshExample,
|
||||
"@brief An example scene object which renders a mesh.\n\n"
|
||||
"This class implements a basic SceneObject that can exist in the world at a "
|
||||
"3D position and render itself. There are several valid ways to render an "
|
||||
"object in Torque. This class implements the preferred rendering method which "
|
||||
"is to submit a MeshRenderInst along with a Material, vertex buffer, "
|
||||
"primitive buffer, and transform and allow the RenderMeshMgr handle the "
|
||||
"actual setup and rendering for you.\n\n"
|
||||
"See the C++ code for implementation details.\n\n"
|
||||
"@ingroup Examples\n" );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object setup and teardown
|
||||
//-----------------------------------------------------------------------------
|
||||
RenderMeshExample::RenderMeshExample()
|
||||
{
|
||||
// Flag this object so that it will always
|
||||
// be sent across the network to clients
|
||||
mNetFlags.set( Ghostable | ScopeAlways );
|
||||
|
||||
// Set it as a "static" object that casts shadows
|
||||
mTypeMask |= StaticObjectType | StaticShapeObjectType;
|
||||
|
||||
// Make sure we the Material instance to NULL
|
||||
// so we don't try to access it incorrectly
|
||||
mMaterialInst = NULL;
|
||||
}
|
||||
|
||||
RenderMeshExample::~RenderMeshExample()
|
||||
{
|
||||
if ( mMaterialInst )
|
||||
SAFE_DELETE( mMaterialInst );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object Editing
|
||||
//-----------------------------------------------------------------------------
|
||||
void RenderMeshExample::initPersistFields()
|
||||
{
|
||||
addGroup( "Rendering" );
|
||||
addField( "material", TypeMaterialName, Offset( mMaterialName, RenderMeshExample ),
|
||||
"The name of the material used to render the mesh." );
|
||||
endGroup( "Rendering" );
|
||||
|
||||
// SceneObject already handles exposing the transform
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void RenderMeshExample::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
|
||||
// Flag the network mask to send the updates
|
||||
// to the client object
|
||||
setMaskBits( UpdateMask );
|
||||
}
|
||||
|
||||
bool RenderMeshExample::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
// Set up a 1x1x1 bounding box
|
||||
mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ),
|
||||
Point3F( 0.5f, 0.5f, 0.5f ) );
|
||||
|
||||
resetWorldBox();
|
||||
|
||||
// Add this object to the scene
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderMeshExample::onRemove()
|
||||
{
|
||||
// Remove this object from the scene
|
||||
removeFromScene();
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void RenderMeshExample::setTransform(const MatrixF & mat)
|
||||
{
|
||||
// Let SceneObject handle all of the matrix manipulation
|
||||
Parent::setTransform( mat );
|
||||
|
||||
// Dirty our network mask so that the new transform gets
|
||||
// transmitted to the client object
|
||||
setMaskBits( TransformMask );
|
||||
}
|
||||
|
||||
U32 RenderMeshExample::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
|
||||
{
|
||||
// Allow the Parent to get a crack at writing its info
|
||||
U32 retMask = Parent::packUpdate( conn, mask, stream );
|
||||
|
||||
// Write our transform information
|
||||
if ( stream->writeFlag( mask & TransformMask ) )
|
||||
{
|
||||
mathWrite(*stream, getTransform());
|
||||
mathWrite(*stream, getScale());
|
||||
}
|
||||
|
||||
// Write out any of the updated editable properties
|
||||
if ( stream->writeFlag( mask & UpdateMask ) )
|
||||
stream->write( mMaterialName );
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void RenderMeshExample::unpackUpdate(NetConnection *conn, BitStream *stream)
|
||||
{
|
||||
// Let the Parent read any info it sent
|
||||
Parent::unpackUpdate(conn, stream);
|
||||
|
||||
if ( stream->readFlag() ) // TransformMask
|
||||
{
|
||||
mathRead(*stream, &mObjToWorld);
|
||||
mathRead(*stream, &mObjScale);
|
||||
|
||||
setTransform( mObjToWorld );
|
||||
}
|
||||
|
||||
if ( stream->readFlag() ) // UpdateMask
|
||||
{
|
||||
stream->read( &mMaterialName );
|
||||
|
||||
if ( isProperlyAdded() )
|
||||
updateMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object Rendering
|
||||
//-----------------------------------------------------------------------------
|
||||
void RenderMeshExample::createGeometry()
|
||||
{
|
||||
static const 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 const Point3F cubeNormals[6] =
|
||||
{
|
||||
Point3F( 1, 0, 0), Point3F(-1, 0, 0), Point3F( 0, 1, 0),
|
||||
Point3F( 0, -1, 0), Point3F( 0, 0, 1), Point3F( 0, 0, -1)
|
||||
};
|
||||
|
||||
static const Point2F cubeTexCoords[4] =
|
||||
{
|
||||
Point2F( 0, 0), Point2F( 0, -1),
|
||||
Point2F( 1, 0), Point2F( 1, -1)
|
||||
};
|
||||
|
||||
static const U32 cubeFaces[36][3] =
|
||||
{
|
||||
{ 3, 0, 3 }, { 0, 0, 0 }, { 1, 0, 1 },
|
||||
{ 2, 0, 2 }, { 0, 0, 0 }, { 3, 0, 3 },
|
||||
{ 7, 1, 1 }, { 4, 1, 2 }, { 5, 1, 0 },
|
||||
{ 6, 1, 3 }, { 4, 1, 2 }, { 7, 1, 1 },
|
||||
{ 3, 2, 1 }, { 5, 2, 2 }, { 2, 2, 0 },
|
||||
{ 7, 2, 3 }, { 5, 2, 2 }, { 3, 2, 1 },
|
||||
{ 1, 3, 3 }, { 4, 3, 0 }, { 6, 3, 1 },
|
||||
{ 0, 3, 2 }, { 4, 3, 0 }, { 1, 3, 3 },
|
||||
{ 3, 4, 3 }, { 6, 4, 0 }, { 7, 4, 1 },
|
||||
{ 1, 4, 2 }, { 6, 4, 0 }, { 3, 4, 3 },
|
||||
{ 2, 5, 1 }, { 4, 5, 2 }, { 0, 5, 0 },
|
||||
{ 5, 5, 3 }, { 4, 5, 2 }, { 2, 5, 1 }
|
||||
};
|
||||
|
||||
// Fill the vertex buffer
|
||||
VertexType *pVert = NULL;
|
||||
|
||||
mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic );
|
||||
pVert = mVertexBuffer.lock();
|
||||
|
||||
Point3F halfSize = getObjBox().getExtents() * 0.5f;
|
||||
|
||||
for (U32 i = 0; i < 36; i++)
|
||||
{
|
||||
const U32& vdx = cubeFaces[i][0];
|
||||
const U32& ndx = cubeFaces[i][1];
|
||||
const U32& tdx = cubeFaces[i][2];
|
||||
|
||||
pVert[i].point = cubePoints[vdx] * halfSize;
|
||||
pVert[i].normal = cubeNormals[ndx];
|
||||
pVert[i].texCoord = cubeTexCoords[tdx];
|
||||
}
|
||||
|
||||
mVertexBuffer.unlock();
|
||||
|
||||
// Fill the primitive buffer
|
||||
U16 *pIdx = NULL;
|
||||
|
||||
mPrimitiveBuffer.set( GFX, 36, 12, GFXBufferTypeStatic );
|
||||
|
||||
mPrimitiveBuffer.lock(&pIdx);
|
||||
|
||||
for (U16 i = 0; i < 36; i++)
|
||||
pIdx[i] = i;
|
||||
|
||||
mPrimitiveBuffer.unlock();
|
||||
}
|
||||
|
||||
void RenderMeshExample::updateMaterial()
|
||||
{
|
||||
if ( mMaterialName.isEmpty() )
|
||||
return;
|
||||
|
||||
// If the material name matches then don't bother updating it.
|
||||
if ( mMaterialInst && mMaterialName.equal( mMaterialInst->getMaterial()->getName(), String::NoCase ) )
|
||||
return;
|
||||
|
||||
SAFE_DELETE( mMaterialInst );
|
||||
|
||||
mMaterialInst = MATMGR->createMatInstance( mMaterialName, getGFXVertexFormat< VertexType >() );
|
||||
if ( !mMaterialInst )
|
||||
Con::errorf( "RenderMeshExample::updateMaterial - no Material called '%s'", mMaterialName.c_str() );
|
||||
}
|
||||
|
||||
void RenderMeshExample::prepRenderImage( SceneRenderState *state )
|
||||
{
|
||||
// Do a little prep work if needed
|
||||
if ( mVertexBuffer.isNull() )
|
||||
createGeometry();
|
||||
|
||||
// If we have no material then skip out.
|
||||
if ( !mMaterialInst )
|
||||
return;
|
||||
|
||||
// If we don't have a material instance after the override then
|
||||
// we can skip rendering all together.
|
||||
BaseMatInstance *matInst = state->getOverrideMaterial( mMaterialInst );
|
||||
if ( !matInst )
|
||||
return;
|
||||
|
||||
// Get a handy pointer to our RenderPassmanager
|
||||
RenderPassManager *renderPass = state->getRenderPass();
|
||||
|
||||
// Allocate an MeshRenderInst so that we can submit it to the RenderPassManager
|
||||
MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
|
||||
|
||||
// Set our RenderInst as a standard mesh render
|
||||
ri->type = RenderPassManager::RIT_Mesh;
|
||||
|
||||
// Calculate our sorting point
|
||||
if ( state )
|
||||
{
|
||||
// Calculate our sort point manually.
|
||||
const Box3F& rBox = getRenderWorldBox();
|
||||
ri->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );
|
||||
}
|
||||
else
|
||||
ri->sortDistSq = 0.0f;
|
||||
|
||||
// Set up our transforms
|
||||
MatrixF objectToWorld = getRenderTransform();
|
||||
objectToWorld.scale( getScale() );
|
||||
|
||||
ri->objectToWorld = renderPass->allocUniqueXform( objectToWorld );
|
||||
ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
|
||||
ri->projection = renderPass->allocSharedXform(RenderPassManager::Projection);
|
||||
|
||||
// If our material needs lights then fill the RIs
|
||||
// light vector with the best lights.
|
||||
if ( matInst->isForwardLit() )
|
||||
{
|
||||
LightQuery query;
|
||||
query.init( getWorldSphere() );
|
||||
query.getLights( ri->lights, 8 );
|
||||
}
|
||||
|
||||
// Make sure we have an up-to-date backbuffer in case
|
||||
// our Material would like to make use of it
|
||||
// NOTICE: SFXBB is removed and refraction is disabled!
|
||||
//ri->backBuffTex = GFX->getSfxBackBuffer();
|
||||
|
||||
// Set our Material
|
||||
ri->matInst = matInst;
|
||||
|
||||
// Set up our vertex buffer and primitive buffer
|
||||
ri->vertBuff = &mVertexBuffer;
|
||||
ri->primBuff = &mPrimitiveBuffer;
|
||||
|
||||
ri->prim = renderPass->allocPrim();
|
||||
ri->prim->type = GFXTriangleList;
|
||||
ri->prim->minIndex = 0;
|
||||
ri->prim->startIndex = 0;
|
||||
ri->prim->numPrimitives = 12;
|
||||
ri->prim->startVertex = 0;
|
||||
ri->prim->numVertices = 36;
|
||||
|
||||
// We sort by the material then vertex buffer
|
||||
ri->defaultKey = matInst->getStateHint();
|
||||
ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe!
|
||||
|
||||
// Submit our RenderInst to the RenderPassManager
|
||||
state->getRenderPass()->addInst( ri );
|
||||
}
|
||||
|
||||
DefineEngineMethod( RenderMeshExample, postApply, void, (),,
|
||||
"A utility method for forcing a network update.\n")
|
||||
{
|
||||
object->inspectPostApply();
|
||||
}
|
||||
134
Engine/source/T3D/examples/renderMeshExample.h
Normal file
134
Engine/source/T3D/examples/renderMeshExample.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _RENDERMESHEXAMPLE_H_
|
||||
#define _RENDERMESHEXAMPLE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This class implements a basic SceneObject that can exist in the world at a
|
||||
// 3D position and render itself. There are several valid ways to render an
|
||||
// object in Torque. This class implements the preferred rendering method which
|
||||
// is to submit a MeshRenderInst along with a Material, vertex buffer,
|
||||
// primitive buffer, and transform and allow the RenderMeshMgr handle the
|
||||
// actual setup and rendering for you.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class RenderMeshExample : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
|
||||
// Networking masks
|
||||
// We need to implement a mask specifically to handle
|
||||
// updating our transform from the server object to its
|
||||
// client-side "ghost". We also need to implement a
|
||||
// maks for handling editor updates to our properties
|
||||
// (like material).
|
||||
enum MaskBits
|
||||
{
|
||||
TransformMask = Parent::NextFreeMask << 0,
|
||||
UpdateMask = Parent::NextFreeMask << 1,
|
||||
NextFreeMask = Parent::NextFreeMask << 2
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Rendering variables
|
||||
//--------------------------------------------------------------------------
|
||||
// The name of the Material we will use for rendering
|
||||
String mMaterialName;
|
||||
// The actual Material instance
|
||||
BaseMatInstance* mMaterialInst;
|
||||
|
||||
// Define our vertex format here so we don't have to
|
||||
// change it in multiple spots later
|
||||
typedef GFXVertexPNT VertexType;
|
||||
|
||||
// The GFX vertex and primitive buffers
|
||||
GFXVertexBufferHandle< VertexType > mVertexBuffer;
|
||||
GFXPrimitiveBufferHandle mPrimitiveBuffer;
|
||||
|
||||
public:
|
||||
RenderMeshExample();
|
||||
virtual ~RenderMeshExample();
|
||||
|
||||
// Declare this object as a ConsoleObject so that we can
|
||||
// instantiate it into the world and network it
|
||||
DECLARE_CONOBJECT(RenderMeshExample);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Object Editing
|
||||
// Since there is always a server and a client object in Torque and we
|
||||
// actually edit the server object we need to implement some basic
|
||||
// networking functions
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up any fields that we want to be editable (like position)
|
||||
static void initPersistFields();
|
||||
|
||||
// Allows the object to update its editable settings
|
||||
// from the server object to the client
|
||||
virtual void inspectPostApply();
|
||||
|
||||
// Handle when we are added to the scene and removed from the scene
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
// Override this so that we can dirty the network flag when it is called
|
||||
void setTransform( const MatrixF &mat );
|
||||
|
||||
// This function handles sending the relevant data from the server
|
||||
// object to the client object
|
||||
U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
|
||||
// This function handles receiving relevant data from the server
|
||||
// object and applying it to the client object
|
||||
void unpackUpdate( NetConnection *conn, BitStream *stream );
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Object Rendering
|
||||
// Torque utilizes a "batch" rendering system. This means that it builds a
|
||||
// list of objects that need to render (via RenderInst's) and then renders
|
||||
// them all in one batch. This allows it to optimized on things like
|
||||
// minimizing texture, state, and shader switching by grouping objects that
|
||||
// use the same Materials.
|
||||
//--------------------------------------------------------------------------
|
||||
// Create the geometry for rendering
|
||||
void createGeometry();
|
||||
|
||||
// Get the Material instance
|
||||
void updateMaterial();
|
||||
|
||||
// This is the function that allows this object to submit itself for rendering
|
||||
void prepRenderImage( SceneRenderState *state );
|
||||
};
|
||||
|
||||
#endif // _RENDERMESHEXAMPLE_H_
|
||||
280
Engine/source/T3D/examples/renderObjectExample.cpp
Normal file
280
Engine/source/T3D/examples/renderObjectExample.cpp
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/examples/renderObjectExample.h"
|
||||
|
||||
#include "math/mathIO.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "materials/sceneData.h"
|
||||
#include "gfx/gfxDebugEvent.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(RenderObjectExample);
|
||||
|
||||
ConsoleDocClass( RenderObjectExample,
|
||||
"@brief An example scene object which renders using a callback.\n\n"
|
||||
"This class implements a basic SceneObject that can exist in the world at a "
|
||||
"3D position and render itself. Note that RenderObjectExample handles its own "
|
||||
"rendering by submitting itself as an ObjectRenderInst (see "
|
||||
"renderInstance\renderPassmanager.h) along with a delegate for its render() "
|
||||
"function. However, the preffered rendering method in the engine is to submit "
|
||||
"a MeshRenderInst along with a Material, vertex buffer, primitive buffer, and "
|
||||
"transform and allow the RenderMeshMgr handle the actual rendering. You can "
|
||||
"see this implemented in RenderMeshExample.\n\n"
|
||||
"See the C++ code for implementation details.\n\n"
|
||||
"@ingroup Examples\n" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object setup and teardown
|
||||
//-----------------------------------------------------------------------------
|
||||
RenderObjectExample::RenderObjectExample()
|
||||
{
|
||||
// Flag this object so that it will always
|
||||
// be sent across the network to clients
|
||||
mNetFlags.set( Ghostable | ScopeAlways );
|
||||
|
||||
// Set it as a "static" object
|
||||
mTypeMask |= StaticObjectType | StaticShapeObjectType;
|
||||
}
|
||||
|
||||
RenderObjectExample::~RenderObjectExample()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object Editing
|
||||
//-----------------------------------------------------------------------------
|
||||
void RenderObjectExample::initPersistFields()
|
||||
{
|
||||
// SceneObject already handles exposing the transform
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool RenderObjectExample::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
// Set up a 1x1x1 bounding box
|
||||
mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ),
|
||||
Point3F( 0.5f, 0.5f, 0.5f ) );
|
||||
|
||||
resetWorldBox();
|
||||
|
||||
// Add this object to the scene
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderObjectExample::onRemove()
|
||||
{
|
||||
// Remove this object from the scene
|
||||
removeFromScene();
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void RenderObjectExample::setTransform(const MatrixF & mat)
|
||||
{
|
||||
// Let SceneObject handle all of the matrix manipulation
|
||||
Parent::setTransform( mat );
|
||||
|
||||
// Dirty our network mask so that the new transform gets
|
||||
// transmitted to the client object
|
||||
setMaskBits( TransformMask );
|
||||
}
|
||||
|
||||
U32 RenderObjectExample::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
|
||||
{
|
||||
// Allow the Parent to get a crack at writing its info
|
||||
U32 retMask = Parent::packUpdate( conn, mask, stream );
|
||||
|
||||
// Write our transform information
|
||||
if ( stream->writeFlag( mask & TransformMask ) )
|
||||
{
|
||||
mathWrite(*stream, getTransform());
|
||||
mathWrite(*stream, getScale());
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void RenderObjectExample::unpackUpdate(NetConnection *conn, BitStream *stream)
|
||||
{
|
||||
// Let the Parent read any info it sent
|
||||
Parent::unpackUpdate(conn, stream);
|
||||
|
||||
if ( stream->readFlag() ) // TransformMask
|
||||
{
|
||||
mathRead(*stream, &mObjToWorld);
|
||||
mathRead(*stream, &mObjScale);
|
||||
|
||||
setTransform( mObjToWorld );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object Rendering
|
||||
//-----------------------------------------------------------------------------
|
||||
void RenderObjectExample::createGeometry()
|
||||
{
|
||||
static const Point3F cubePoints[8] =
|
||||
{
|
||||
Point3F( 1.0f, -1.0f, -1.0f), Point3F( 1.0f, -1.0f, 1.0f),
|
||||
Point3F( 1.0f, 1.0f, -1.0f), Point3F( 1.0f, 1.0f, 1.0f),
|
||||
Point3F(-1.0f, -1.0f, -1.0f), Point3F(-1.0f, 1.0f, -1.0f),
|
||||
Point3F(-1.0f, -1.0f, 1.0f), Point3F(-1.0f, 1.0f, 1.0f)
|
||||
};
|
||||
|
||||
static const Point3F cubeNormals[6] =
|
||||
{
|
||||
Point3F( 1.0f, 0.0f, 0.0f), Point3F(-1.0f, 0.0f, 0.0f),
|
||||
Point3F( 0.0f, 1.0f, 0.0f), Point3F( 0.0f, -1.0f, 0.0f),
|
||||
Point3F( 0.0f, 0.0f, 1.0f), Point3F( 0.0f, 0.0f, -1.0f)
|
||||
};
|
||||
|
||||
static const ColorI cubeColors[3] =
|
||||
{
|
||||
ColorI( 255, 0, 0, 255 ),
|
||||
ColorI( 0, 255, 0, 255 ),
|
||||
ColorI( 0, 0, 255, 255 )
|
||||
};
|
||||
|
||||
static const U32 cubeFaces[36][3] =
|
||||
{
|
||||
{ 3, 0, 0 }, { 0, 0, 0 }, { 1, 0, 0 },
|
||||
{ 2, 0, 0 }, { 0, 0, 0 }, { 3, 0, 0 },
|
||||
{ 7, 1, 0 }, { 4, 1, 0 }, { 5, 1, 0 },
|
||||
{ 6, 1, 0 }, { 4, 1, 0 }, { 7, 1, 0 },
|
||||
{ 3, 2, 1 }, { 5, 2, 1 }, { 2, 2, 1 },
|
||||
{ 7, 2, 1 }, { 5, 2, 1 }, { 3, 2, 1 },
|
||||
{ 1, 3, 1 }, { 4, 3, 1 }, { 6, 3, 1 },
|
||||
{ 0, 3, 1 }, { 4, 3, 1 }, { 1, 3, 1 },
|
||||
{ 3, 4, 2 }, { 6, 4, 2 }, { 7, 4, 2 },
|
||||
{ 1, 4, 2 }, { 6, 4, 2 }, { 3, 4, 2 },
|
||||
{ 2, 5, 2 }, { 4, 5, 2 }, { 0, 5, 2 },
|
||||
{ 5, 5, 2 }, { 4, 5, 2 }, { 2, 5, 2 }
|
||||
};
|
||||
|
||||
// Fill the vertex buffer
|
||||
VertexType *pVert = NULL;
|
||||
|
||||
mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic );
|
||||
pVert = mVertexBuffer.lock();
|
||||
|
||||
Point3F halfSize = getObjBox().getExtents() * 0.5f;
|
||||
|
||||
for (U32 i = 0; i < 36; i++)
|
||||
{
|
||||
const U32& vdx = cubeFaces[i][0];
|
||||
const U32& ndx = cubeFaces[i][1];
|
||||
const U32& cdx = cubeFaces[i][2];
|
||||
|
||||
pVert[i].point = cubePoints[vdx] * halfSize;
|
||||
pVert[i].normal = cubeNormals[ndx];
|
||||
pVert[i].color = cubeColors[cdx];
|
||||
}
|
||||
|
||||
mVertexBuffer.unlock();
|
||||
|
||||
// Set up our normal and reflection StateBlocks
|
||||
GFXStateBlockDesc desc;
|
||||
|
||||
// The normal StateBlock only needs a default StateBlock
|
||||
mNormalSB = GFX->createStateBlock( desc );
|
||||
|
||||
// The reflection needs its culling reversed
|
||||
desc.cullDefined = true;
|
||||
desc.cullMode = GFXCullCW;
|
||||
mReflectSB = GFX->createStateBlock( desc );
|
||||
}
|
||||
|
||||
void RenderObjectExample::prepRenderImage( SceneRenderState *state )
|
||||
{
|
||||
// Do a little prep work if needed
|
||||
if ( mVertexBuffer.isNull() )
|
||||
createGeometry();
|
||||
|
||||
// Allocate an ObjectRenderInst so that we can submit it to the RenderPassManager
|
||||
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
||||
|
||||
// Now bind our rendering function so that it will get called
|
||||
ri->renderDelegate.bind( this, &RenderObjectExample::render );
|
||||
|
||||
// Set our RenderInst as a standard object render
|
||||
ri->type = RenderPassManager::RIT_Object;
|
||||
|
||||
// Set our sorting keys to a default value
|
||||
ri->defaultKey = 0;
|
||||
ri->defaultKey2 = 0;
|
||||
|
||||
// Submit our RenderInst to the RenderPassManager
|
||||
state->getRenderPass()->addInst( ri );
|
||||
}
|
||||
|
||||
void RenderObjectExample::render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
|
||||
{
|
||||
if ( overrideMat )
|
||||
return;
|
||||
|
||||
if ( mVertexBuffer.isNull() )
|
||||
return;
|
||||
|
||||
PROFILE_SCOPE(RenderObjectExample_Render);
|
||||
|
||||
// Set up a GFX debug event (this helps with debugging rendering events in external tools)
|
||||
GFXDEBUGEVENT_SCOPE( RenderObjectExample_Render, ColorI::RED );
|
||||
|
||||
// GFXTransformSaver is a handy helper class that restores
|
||||
// the current GFX matrices to their original values when
|
||||
// it goes out of scope at the end of the function
|
||||
GFXTransformSaver saver;
|
||||
|
||||
// Calculate our object to world transform matrix
|
||||
MatrixF objectToWorld = getRenderTransform();
|
||||
objectToWorld.scale( getScale() );
|
||||
|
||||
// Apply our object transform
|
||||
GFX->multWorld( objectToWorld );
|
||||
|
||||
// Deal with reflect pass otherwise
|
||||
// set the normal StateBlock
|
||||
if ( state->isReflectPass() )
|
||||
GFX->setStateBlock( mReflectSB );
|
||||
else
|
||||
GFX->setStateBlock( mNormalSB );
|
||||
|
||||
// Set up the "generic" shaders
|
||||
// These handle rendering on GFX layers that don't support
|
||||
// fixed function. Otherwise they disable shaders.
|
||||
GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
|
||||
|
||||
// Set the vertex buffer
|
||||
GFX->setVertexBuffer( mVertexBuffer );
|
||||
|
||||
// Draw our triangles
|
||||
GFX->drawPrimitive( GFXTriangleList, 0, 12 );
|
||||
}
|
||||
136
Engine/source/T3D/examples/renderObjectExample.h
Normal file
136
Engine/source/T3D/examples/renderObjectExample.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _RENDEROBJECTEXAMPLE_H_
|
||||
#define _RENDEROBJECTEXAMPLE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _GFXSTATEBLOCK_H_
|
||||
#include "gfx/gfxStateBlock.h"
|
||||
#endif
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This class implements a basic SceneObject that can exist in the world at a
|
||||
// 3D position and render itself. Note that RenderObjectExample handles its own
|
||||
// rendering by submitting itself as an ObjectRenderInst (see
|
||||
// renderInstance\renderPassmanager.h) along with a delegate for its render()
|
||||
// function. However, the preffered rendering method in the engine is to submit
|
||||
// a MeshRenderInst along with a Material, vertex buffer, primitive buffer, and
|
||||
// transform and allow the RenderMeshMgr handle the actual rendering. You can
|
||||
// see this implemented in RenderMeshExample.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class RenderObjectExample : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
|
||||
// Networking masks
|
||||
// We need to implement at least one of these to allow
|
||||
// the client version of the object to receive updates
|
||||
// from the server version (like if it has been moved
|
||||
// or edited)
|
||||
enum MaskBits
|
||||
{
|
||||
TransformMask = Parent::NextFreeMask << 0,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Rendering variables
|
||||
//--------------------------------------------------------------------------
|
||||
// Define our vertex format here so we don't have to
|
||||
// change it in multiple spots later
|
||||
typedef GFXVertexPCN VertexType;
|
||||
|
||||
// The handles for our StateBlocks
|
||||
GFXStateBlockRef mNormalSB;
|
||||
GFXStateBlockRef mReflectSB;
|
||||
|
||||
// The GFX vertex and primitive buffers
|
||||
GFXVertexBufferHandle< VertexType > mVertexBuffer;
|
||||
|
||||
public:
|
||||
RenderObjectExample();
|
||||
virtual ~RenderObjectExample();
|
||||
|
||||
// Declare this object as a ConsoleObject so that we can
|
||||
// instantiate it into the world and network it
|
||||
DECLARE_CONOBJECT(RenderObjectExample);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Object Editing
|
||||
// Since there is always a server and a client object in Torque and we
|
||||
// actually edit the server object we need to implement some basic
|
||||
// networking functions
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up any fields that we want to be editable (like position)
|
||||
static void initPersistFields();
|
||||
|
||||
// Handle when we are added to the scene and removed from the scene
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
// Override this so that we can dirty the network flag when it is called
|
||||
void setTransform( const MatrixF &mat );
|
||||
|
||||
// This function handles sending the relevant data from the server
|
||||
// object to the client object
|
||||
U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
|
||||
// This function handles receiving relevant data from the server
|
||||
// object and applying it to the client object
|
||||
void unpackUpdate( NetConnection *conn, BitStream *stream );
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Object Rendering
|
||||
// Torque utilizes a "batch" rendering system. This means that it builds a
|
||||
// list of objects that need to render (via RenderInst's) and then renders
|
||||
// them all in one batch. This allows it to optimized on things like
|
||||
// minimizing texture, state, and shader switching by grouping objects that
|
||||
// use the same Materials. For this example, however, we are just going to
|
||||
// get this object added to the list of objects that handle their own
|
||||
// rendering.
|
||||
//--------------------------------------------------------------------------
|
||||
// Create the geometry for rendering
|
||||
void createGeometry();
|
||||
|
||||
// This is the function that allows this object to submit itself for rendering
|
||||
void prepRenderImage( SceneRenderState *state );
|
||||
|
||||
// This is the function that actually gets called to do the rendering
|
||||
// Note that there is no longer a predefined name for this function.
|
||||
// Instead, when we submit our ObjectRenderInst in prepRenderImage(),
|
||||
// we bind this function as our rendering delegate function
|
||||
void render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
|
||||
};
|
||||
|
||||
#endif // _RENDEROBJECTEXAMPLE_H_
|
||||
273
Engine/source/T3D/examples/renderShapeExample.cpp
Normal file
273
Engine/source/T3D/examples/renderShapeExample.cpp
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/examples/renderShapeExample.h"
|
||||
|
||||
#include "math/mathIO.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "lighting/lightQuery.h"
|
||||
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(RenderShapeExample);
|
||||
|
||||
ConsoleDocClass( RenderShapeExample,
|
||||
"@brief An example scene object which renders a DTS.\n\n"
|
||||
"This class implements a basic SceneObject that can exist in the world at a "
|
||||
"3D position and render itself. There are several valid ways to render an "
|
||||
"object in Torque. This class makes use of the 'TS' (three space) shape "
|
||||
"system. TS manages loading the various mesh formats supported by Torque as "
|
||||
"well was rendering those meshes (including LOD and animation...though this "
|
||||
"example doesn't include any animation over time).\n\n"
|
||||
"See the C++ code for implementation details.\n\n"
|
||||
"@ingroup Examples\n" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object setup and teardown
|
||||
//-----------------------------------------------------------------------------
|
||||
RenderShapeExample::RenderShapeExample()
|
||||
{
|
||||
// Flag this object so that it will always
|
||||
// be sent across the network to clients
|
||||
mNetFlags.set( Ghostable | ScopeAlways );
|
||||
|
||||
// Set it as a "static" object.
|
||||
mTypeMask |= StaticObjectType | StaticShapeObjectType;
|
||||
|
||||
// Make sure to initialize our TSShapeInstance to NULL
|
||||
mShapeInstance = NULL;
|
||||
}
|
||||
|
||||
RenderShapeExample::~RenderShapeExample()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object Editing
|
||||
//-----------------------------------------------------------------------------
|
||||
void RenderShapeExample::initPersistFields()
|
||||
{
|
||||
addGroup( "Rendering" );
|
||||
addField( "shapeFile", TypeStringFilename, Offset( mShapeFile, RenderShapeExample ),
|
||||
"The path to the DTS shape file." );
|
||||
endGroup( "Rendering" );
|
||||
|
||||
// SceneObject already handles exposing the transform
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void RenderShapeExample::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
|
||||
// Flag the network mask to send the updates
|
||||
// to the client object
|
||||
setMaskBits( UpdateMask );
|
||||
}
|
||||
|
||||
bool RenderShapeExample::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
// Set up a 1x1x1 bounding box
|
||||
mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ),
|
||||
Point3F( 0.5f, 0.5f, 0.5f ) );
|
||||
|
||||
resetWorldBox();
|
||||
|
||||
// Add this object to the scene
|
||||
addToScene();
|
||||
|
||||
// Setup the shape.
|
||||
createShape();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderShapeExample::onRemove()
|
||||
{
|
||||
// Remove this object from the scene
|
||||
removeFromScene();
|
||||
|
||||
// Remove our TSShapeInstance
|
||||
if ( mShapeInstance )
|
||||
SAFE_DELETE( mShapeInstance );
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void RenderShapeExample::setTransform(const MatrixF & mat)
|
||||
{
|
||||
// Let SceneObject handle all of the matrix manipulation
|
||||
Parent::setTransform( mat );
|
||||
|
||||
// Dirty our network mask so that the new transform gets
|
||||
// transmitted to the client object
|
||||
setMaskBits( TransformMask );
|
||||
}
|
||||
|
||||
U32 RenderShapeExample::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
|
||||
{
|
||||
// Allow the Parent to get a crack at writing its info
|
||||
U32 retMask = Parent::packUpdate( conn, mask, stream );
|
||||
|
||||
// Write our transform information
|
||||
if ( stream->writeFlag( mask & TransformMask ) )
|
||||
{
|
||||
mathWrite(*stream, getTransform());
|
||||
mathWrite(*stream, getScale());
|
||||
}
|
||||
|
||||
// Write out any of the updated editable properties
|
||||
if ( stream->writeFlag( mask & UpdateMask ) )
|
||||
{
|
||||
stream->write( mShapeFile );
|
||||
|
||||
// Allow the server object a chance to handle a new shape
|
||||
createShape();
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void RenderShapeExample::unpackUpdate(NetConnection *conn, BitStream *stream)
|
||||
{
|
||||
// Let the Parent read any info it sent
|
||||
Parent::unpackUpdate(conn, stream);
|
||||
|
||||
if ( stream->readFlag() ) // TransformMask
|
||||
{
|
||||
mathRead(*stream, &mObjToWorld);
|
||||
mathRead(*stream, &mObjScale);
|
||||
|
||||
setTransform( mObjToWorld );
|
||||
}
|
||||
|
||||
if ( stream->readFlag() ) // UpdateMask
|
||||
{
|
||||
stream->read( &mShapeFile );
|
||||
|
||||
if ( isProperlyAdded() )
|
||||
createShape();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object Rendering
|
||||
//-----------------------------------------------------------------------------
|
||||
void RenderShapeExample::createShape()
|
||||
{
|
||||
if ( mShapeFile.isEmpty() )
|
||||
return;
|
||||
|
||||
// If this is the same shape then no reason to update it
|
||||
if ( mShapeInstance && mShapeFile.equal( mShape.getPath().getFullPath(), String::NoCase ) )
|
||||
return;
|
||||
|
||||
// Clean up our previous shape
|
||||
if ( mShapeInstance )
|
||||
SAFE_DELETE( mShapeInstance );
|
||||
mShape = NULL;
|
||||
|
||||
// Attempt to get the resource from the ResourceManager
|
||||
mShape = ResourceManager::get().load( mShapeFile );
|
||||
|
||||
if ( !mShape )
|
||||
{
|
||||
Con::errorf( "RenderShapeExample::createShape() - Unable to load shape: %s", mShapeFile.c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt to preload the Materials for this shape
|
||||
if ( isClientObject() &&
|
||||
!mShape->preloadMaterialList( mShape.getPath() ) &&
|
||||
NetConnection::filesWereDownloaded() )
|
||||
{
|
||||
mShape = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the bounding box
|
||||
mObjBox = mShape->bounds;
|
||||
resetWorldBox();
|
||||
setRenderTransform(mObjToWorld);
|
||||
|
||||
// Create the TSShapeInstance
|
||||
mShapeInstance = new TSShapeInstance( mShape, isClientObject() );
|
||||
}
|
||||
|
||||
void RenderShapeExample::prepRenderImage( SceneRenderState *state )
|
||||
{
|
||||
// Make sure we have a TSShapeInstance
|
||||
if ( !mShapeInstance )
|
||||
return;
|
||||
|
||||
// Calculate the distance of this object from the camera
|
||||
Point3F cameraOffset;
|
||||
getRenderTransform().getColumn( 3, &cameraOffset );
|
||||
cameraOffset -= state->getDiffuseCameraPosition();
|
||||
F32 dist = cameraOffset.len();
|
||||
if ( dist < 0.01f )
|
||||
dist = 0.01f;
|
||||
|
||||
// Set up the LOD for the shape
|
||||
F32 invScale = ( 1.0f / getMax( getMax( mObjScale.x, mObjScale.y ), mObjScale.z ) );
|
||||
|
||||
mShapeInstance->setDetailFromDistance( state, dist * invScale );
|
||||
|
||||
// Make sure we have a valid level of detail
|
||||
if ( mShapeInstance->getCurrentDetail() < 0 )
|
||||
return;
|
||||
|
||||
// GFXTransformSaver is a handy helper class that restores
|
||||
// the current GFX matrices to their original values when
|
||||
// it goes out of scope at the end of the function
|
||||
GFXTransformSaver saver;
|
||||
|
||||
// Set up our TS render state
|
||||
TSRenderState rdata;
|
||||
rdata.setSceneState( state );
|
||||
rdata.setFadeOverride( 1.0f );
|
||||
|
||||
// We might have some forward lit materials
|
||||
// so pass down a query to gather lights.
|
||||
LightQuery query;
|
||||
query.init( getWorldSphere() );
|
||||
rdata.setLightQuery( &query );
|
||||
|
||||
// Set the world matrix to the objects render transform
|
||||
MatrixF mat = getRenderTransform();
|
||||
mat.scale( mObjScale );
|
||||
GFX->setWorldMatrix( mat );
|
||||
|
||||
// Animate the the shape
|
||||
mShapeInstance->animate();
|
||||
|
||||
// Allow the shape to submit the RenderInst(s) for itself
|
||||
mShapeInstance->render( rdata );
|
||||
}
|
||||
119
Engine/source/T3D/examples/renderShapeExample.h
Normal file
119
Engine/source/T3D/examples/renderShapeExample.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _RENDERSHAPEEXAMPLE_H_
|
||||
#define _RENDERSHAPEEXAMPLE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This class implements a basic SceneObject that can exist in the world at a
|
||||
// 3D position and render itself. There are several valid ways to render an
|
||||
// object in Torque. This class makes use of the "TS" (three space) shape
|
||||
// system. TS manages loading the various mesh formats supported by Torque as
|
||||
// well was rendering those meshes (including LOD and animation...though this
|
||||
// example doesn't include any animation over time).
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class RenderShapeExample : public SceneObject
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
|
||||
// Networking masks
|
||||
// We need to implement a mask specifically to handle
|
||||
// updating our transform from the server object to its
|
||||
// client-side "ghost". We also need to implement a
|
||||
// maks for handling editor updates to our properties
|
||||
// (like material).
|
||||
enum MaskBits
|
||||
{
|
||||
TransformMask = Parent::NextFreeMask << 0,
|
||||
UpdateMask = Parent::NextFreeMask << 1,
|
||||
NextFreeMask = Parent::NextFreeMask << 2
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Rendering variables
|
||||
//--------------------------------------------------------------------------
|
||||
// The name of the shape file we will use for rendering
|
||||
String mShapeFile;
|
||||
// The actual shape instance
|
||||
TSShapeInstance* mShapeInstance;
|
||||
// Store the resource so we can access the filename later
|
||||
Resource<TSShape> mShape;
|
||||
|
||||
public:
|
||||
RenderShapeExample();
|
||||
virtual ~RenderShapeExample();
|
||||
|
||||
// Declare this object as a ConsoleObject so that we can
|
||||
// instantiate it into the world and network it
|
||||
DECLARE_CONOBJECT(RenderShapeExample);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Object Editing
|
||||
// Since there is always a server and a client object in Torque and we
|
||||
// actually edit the server object we need to implement some basic
|
||||
// networking functions
|
||||
//--------------------------------------------------------------------------
|
||||
// Set up any fields that we want to be editable (like position)
|
||||
static void initPersistFields();
|
||||
|
||||
// Allows the object to update its editable settings
|
||||
// from the server object to the client
|
||||
virtual void inspectPostApply();
|
||||
|
||||
// Handle when we are added to the scene and removed from the scene
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
// Override this so that we can dirty the network flag when it is called
|
||||
void setTransform( const MatrixF &mat );
|
||||
|
||||
// This function handles sending the relevant data from the server
|
||||
// object to the client object
|
||||
U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
|
||||
// This function handles receiving relevant data from the server
|
||||
// object and applying it to the client object
|
||||
void unpackUpdate( NetConnection *conn, BitStream *stream );
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Object Rendering
|
||||
// Torque utilizes a "batch" rendering system. This means that it builds a
|
||||
// list of objects that need to render (via RenderInst's) and then renders
|
||||
// them all in one batch. This allows it to optimized on things like
|
||||
// minimizing texture, state, and shader switching by grouping objects that
|
||||
// use the same Materials.
|
||||
//--------------------------------------------------------------------------
|
||||
// Create the geometry for rendering
|
||||
void createShape();
|
||||
|
||||
// This is the function that allows this object to submit itself for rendering
|
||||
void prepRenderImage( SceneRenderState *state );
|
||||
};
|
||||
|
||||
#endif // _RENDERSHAPEEXAMPLE_H_
|
||||
197
Engine/source/T3D/fps/guiClockHud.cpp
Normal file
197
Engine/source/T3D/fps/guiClockHud.cpp
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "T3D/shapeBase.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "console/engineAPI.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;
|
||||
bool mTimeReversed;
|
||||
|
||||
ColorF mFillColor;
|
||||
ColorF mFrameColor;
|
||||
ColorF mTextColor;
|
||||
|
||||
S32 mTimeOffset;
|
||||
|
||||
public:
|
||||
GuiClockHud();
|
||||
|
||||
void setTime(F32 newTime);
|
||||
void setReverseTime(F32 reverseTime);
|
||||
F32 getTime();
|
||||
|
||||
void onRender( Point2I, const RectI &);
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT( GuiClockHud );
|
||||
DECLARE_CATEGORY( "Gui Game" );
|
||||
DECLARE_DESCRIPTION( "Basic HUD clock. Displays the current simulation time offset from some base." );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT( GuiClockHud );
|
||||
|
||||
ConsoleDocClass( GuiClockHud,
|
||||
"@brief Basic HUD clock. Displays the current simulation time offset from some base.\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"\n new GuiClockHud()"
|
||||
"{\n"
|
||||
" fillColor = \"0.0 1.0 0.0 1.0\"; // Fills with a solid green color\n"
|
||||
" frameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n"
|
||||
" textColor = \"1.0 1.0 1.0 1.0\"; // Solid white text Color\n"
|
||||
" showFill = \"true\";\n"
|
||||
" showFrame = \"true\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@ingroup GuiGame\n"
|
||||
);
|
||||
|
||||
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()
|
||||
{
|
||||
addGroup("Misc");
|
||||
addField( "showFill", TypeBool, Offset( mShowFill, GuiClockHud ), "If true, draws a background color behind the control.");
|
||||
addField( "showFrame", TypeBool, Offset( mShowFrame, GuiClockHud ), "If true, draws a frame around the control." );
|
||||
addField( "fillColor", TypeColorF, Offset( mFillColor, GuiClockHud ), "Standard color for the background of the control." );
|
||||
addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiClockHud ), "Color for the control's frame." );
|
||||
addField( "textColor", TypeColorF, Offset( mTextColor, GuiClockHud ), "Color for the text on this control." );
|
||||
endGroup("Misc");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void GuiClockHud::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
// Background first
|
||||
if (mShowFill)
|
||||
GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor);
|
||||
|
||||
// Convert ms time into hours, minutes and seconds.
|
||||
S32 time = S32(getTime());
|
||||
S32 secs = time % 60;
|
||||
S32 mins = (time % 3600) / 60;
|
||||
|
||||
// Currently only displays min/sec
|
||||
char buf[256];
|
||||
dSprintf(buf,sizeof(buf), "%02d:%02d",mins,secs);
|
||||
|
||||
// Center the text
|
||||
offset.x += (getWidth() - mProfile->mFont->getStrWidth((const UTF8 *)buf)) / 2;
|
||||
offset.y += (getHeight() - mProfile->mFont->getHeight()) / 2;
|
||||
GFX->getDrawUtil()->setBitmapModulation(mTextColor);
|
||||
GFX->getDrawUtil()->drawText(mProfile->mFont, offset, buf);
|
||||
GFX->getDrawUtil()->clearBitmapModulation();
|
||||
|
||||
// Border last
|
||||
if (mShowFrame)
|
||||
GFX->getDrawUtil()->drawRect(updateRect, mFrameColor);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void GuiClockHud::setReverseTime(F32 time)
|
||||
{
|
||||
// Set the current time in seconds.
|
||||
mTimeReversed = true;
|
||||
mTimeOffset = S32(time * 1000) + Platform::getVirtualMilliseconds();
|
||||
}
|
||||
|
||||
void GuiClockHud::setTime(F32 time)
|
||||
{
|
||||
// Set the current time in seconds.
|
||||
mTimeReversed = false;
|
||||
mTimeOffset = S32(time * 1000) - Platform::getVirtualMilliseconds();
|
||||
}
|
||||
|
||||
F32 GuiClockHud::getTime()
|
||||
{
|
||||
// Return elapsed time in seconds.
|
||||
if(mTimeReversed)
|
||||
return F32(mTimeOffset - Platform::getVirtualMilliseconds()) / 1000;
|
||||
else
|
||||
return F32(mTimeOffset + Platform::getVirtualMilliseconds()) / 1000;
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiClockHud, setTime, void, (F32 timeInSeconds),(60), "Sets the current base time for the clock.\n"
|
||||
"@param timeInSeconds Time to set the clock, in seconds (IE: 00:02 would be 120)\n"
|
||||
"@tsexample\n"
|
||||
"// Define the time, in seconds\n"
|
||||
"%timeInSeconds = 120;\n\n"
|
||||
"// Change the time on the GuiClockHud control\n"
|
||||
"%guiClockHud.setTime(%timeInSeconds);\n"
|
||||
"@endtsexample\n"
|
||||
)
|
||||
{
|
||||
object->setTime(timeInSeconds);
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiClockHud, setReverseTime, void, (F32 timeInSeconds),(60), "@brief Sets a time for a countdown clock.\n\n"
|
||||
"Setting the time like this will cause the clock to count backwards from the specified time.\n\n"
|
||||
"@param timeInSeconds Time to set the clock, in seconds (IE: 00:02 would be 120)\n\n"
|
||||
"@see setTime\n"
|
||||
)
|
||||
{
|
||||
object->setReverseTime(timeInSeconds);
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiClockHud, getTime, F32, (),, "Returns the current time, in seconds.\n"
|
||||
"@return timeInseconds Current time, in seconds\n"
|
||||
"@tsexample\n"
|
||||
"// Get the current time from the GuiClockHud control\n"
|
||||
"%timeInSeconds = %guiClockHud.getTime();\n"
|
||||
"@endtsexample\n"
|
||||
)
|
||||
{
|
||||
return object->getTime();
|
||||
}
|
||||
191
Engine/source/T3D/fps/guiCrossHairHud.cpp
Normal file
191
Engine/source/T3D/fps/guiCrossHairHud.cpp
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "gui/controls/guiBitmapCtrl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/shapeBase.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "console/engineAPI.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 );
|
||||
DECLARE_CATEGORY( "Gui Game" );
|
||||
DECLARE_DESCRIPTION( "Basic cross hair hud. Reacts to state of control object.\n"
|
||||
"Also displays health bar for named objects under the cross hair." );
|
||||
};
|
||||
|
||||
/// 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 );
|
||||
|
||||
ConsoleDocClass( GuiCrossHairHud,
|
||||
"@brief Basic cross hair hud. Reacts to state of control object. Also displays health bar for named objects under the cross hair.\n\n"
|
||||
"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.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"\n new GuiCrossHairHud()"
|
||||
"{\n"
|
||||
" damageFillColor = \"1.0 0.0 0.0 1.0\"; // Fills with a solid red color\n"
|
||||
" damageFrameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n"
|
||||
" damageRect = \"15 5\";\n"
|
||||
" damageOffset = \"0 -10\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n"
|
||||
|
||||
"@ingroup GuiGame\n"
|
||||
);
|
||||
|
||||
GuiCrossHairHud::GuiCrossHairHud()
|
||||
{
|
||||
mDamageFillColor.set( 0.0f, 1.0f, 0.0f, 1.0f );
|
||||
mDamageFrameColor.set( 1.0f, 0.6f, 0.0f, 1.0f );
|
||||
mDamageRectSize.set(50, 4);
|
||||
mDamageOffset.set(0,32);
|
||||
}
|
||||
|
||||
void GuiCrossHairHud::initPersistFields()
|
||||
{
|
||||
addGroup("Damage");
|
||||
addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiCrossHairHud ), "As the health bar depletes, this color will represent the health loss amount." );
|
||||
addField( "damageFrameColor", TypeColorF, Offset( mDamageFrameColor, GuiCrossHairHud ), "Color for the health bar's frame." );
|
||||
addField( "damageRect", TypePoint2I, Offset( mDamageRectSize, GuiCrossHairHud ), "Size for the health bar portion of the control." );
|
||||
addField( "damageOffset", TypePoint2I, Offset( mDamageOffset, GuiCrossHairHud ), "Offset for drawing the damage portion of the health control." );
|
||||
endGroup("Damage");
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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 = dynamic_cast<ShapeBase*>(conn->getControlObject());
|
||||
if (!control || !(control->getTypeMask() & 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
|
||||
GFX->getDrawUtil()->drawRect(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)
|
||||
GFX->getDrawUtil()->drawRectFill(rect, mDamageFillColor);
|
||||
}
|
||||
190
Engine/source/T3D/fps/guiHealthBarHud.cpp
Normal file
190
Engine/source/T3D/fps/guiHealthBarHud.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/shapeBase.h"
|
||||
#include "gfx/gfxDrawUtil.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;
|
||||
|
||||
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 );
|
||||
DECLARE_CATEGORY( "Gui Game" );
|
||||
DECLARE_DESCRIPTION( "A basic health bar. Shows the damage value of the current\n"
|
||||
"PlayerObjectType control object." );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT( GuiHealthBarHud );
|
||||
|
||||
ConsoleDocClass( GuiHealthBarHud,
|
||||
"@brief A basic health bar. Shows the damage value of the current PlayerObjectType control object.\n\n"
|
||||
"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.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"\n new GuiHealthBarHud()"
|
||||
"{\n"
|
||||
" fillColor = \"0.0 1.0 0.0 1.0\"; // Fills with a solid green color\n"
|
||||
" frameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n"
|
||||
" damageFillColor = \"1.0 0.0 0.0 1.0\"; // Fills with a solid red color\n"
|
||||
" pulseRate = \"500\";\n"
|
||||
" pulseThreshold = \"0.25\";\n"
|
||||
" showFill = \"true\";\n"
|
||||
" showFrame = \"true\";\n"
|
||||
" displayEnergy = \"false\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@ingroup GuiGame\n"
|
||||
);
|
||||
|
||||
|
||||
GuiHealthBarHud::GuiHealthBarHud()
|
||||
{
|
||||
mShowFrame = mShowFill = true;
|
||||
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()
|
||||
{
|
||||
addGroup("Colors");
|
||||
addField( "fillColor", TypeColorF, Offset( mFillColor, GuiHealthBarHud ), "Standard color for the background of the control." );
|
||||
addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiHealthBarHud ), "Color for the control's frame." );
|
||||
addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiHealthBarHud ), "As the health bar depletes, this color will represent the health loss amount." );
|
||||
endGroup("Colors");
|
||||
|
||||
addGroup("Pulse");
|
||||
addField( "pulseRate", TypeS32, Offset( mPulseRate, GuiHealthBarHud ), "Speed at which the control will pulse." );
|
||||
addField( "pulseThreshold", TypeF32, Offset( mPulseThreshold, GuiHealthBarHud ), "Health level the control must be under before the control will pulse." );
|
||||
endGroup("Pulse");
|
||||
|
||||
addGroup("Misc");
|
||||
addField( "showFill", TypeBool, Offset( mShowFill, GuiHealthBarHud ), "If true, we draw the background color of the control." );
|
||||
addField( "showFrame", TypeBool, Offset( mShowFrame, GuiHealthBarHud ), "If true, we draw the frame of the control." );
|
||||
addField( "displayEnergy", TypeBool, Offset( mDisplayEnergy, GuiHealthBarHud ), "If true, display the energy value rather than the damage value." );
|
||||
endGroup("Misc");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
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 = dynamic_cast<ShapeBase*>(conn->getControlObject());
|
||||
if (!control || !(control->getTypeMask() & 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)
|
||||
GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor);
|
||||
|
||||
// Pulse the damage fill if it's below the threshold
|
||||
if (mPulseRate != 0)
|
||||
if (mValue < mPulseThreshold)
|
||||
{
|
||||
U32 time = Platform::getVirtualMilliseconds();
|
||||
F32 alpha = 2.0f * F32(time % mPulseRate) / F32(mPulseRate);
|
||||
mDamageFillColor.alpha = (alpha > 1.0f)? 2.0f - alpha: alpha;
|
||||
}
|
||||
else
|
||||
mDamageFillColor.alpha = 1;
|
||||
|
||||
// Render damage fill %
|
||||
RectI rect(updateRect);
|
||||
if(getWidth() > getHeight())
|
||||
rect.extent.x = (S32)(rect.extent.x * 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;
|
||||
}
|
||||
GFX->getDrawUtil()->drawRectFill(rect, mDamageFillColor);
|
||||
|
||||
// Border last
|
||||
if (mShowFrame)
|
||||
GFX->getDrawUtil()->drawRect(updateRect, mFrameColor);
|
||||
}
|
||||
287
Engine/source/T3D/fps/guiShapeNameHud.cpp
Normal file
287
Engine/source/T3D/fps/guiShapeNameHud.cpp
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "gui/core/guiControl.h"
|
||||
#include "gui/3d/guiTSControl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/shapeBase.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "console/engineAPI.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 );
|
||||
DECLARE_CATEGORY( "Gui Game" );
|
||||
DECLARE_DESCRIPTION( "Displays name and damage of ShapeBase objects in its bounds.\n"
|
||||
"Must be a child of a GuiTSCtrl and a server connection must be present." );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiShapeNameHud);
|
||||
|
||||
ConsoleDocClass( GuiShapeNameHud,
|
||||
"@brief Displays name and damage of ShapeBase objects in its bounds. Must be a child of a GuiTSCtrl and a server connection must be present.\n\n"
|
||||
"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.\n\n"
|
||||
"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.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"\n new GuiShapeNameHud()"
|
||||
"{\n"
|
||||
" fillColor = \"0.0 1.0 0.0 1.0\"; // Fills with a solid green color\n"
|
||||
" frameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n"
|
||||
" textColor = \"1.0 1.0 1.0 1.0\"; // Solid white text Color\n"
|
||||
" showFill = \"true\";\n"
|
||||
" showFrame = \"true\";\n"
|
||||
" verticalOffset = \"0.15\";\n"
|
||||
" distanceFade = \"15.0\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@ingroup GuiGame\n"
|
||||
);
|
||||
|
||||
/// Default distance for object's information to be displayed.
|
||||
static const F32 cDefaultVisibleDistance = 500.0f;
|
||||
|
||||
GuiShapeNameHud::GuiShapeNameHud()
|
||||
{
|
||||
mFillColor.set( 0.25f, 0.25f, 0.25f, 0.25f );
|
||||
mFrameColor.set( 0, 1, 0, 1 );
|
||||
mTextColor.set( 0, 1, 0, 1 );
|
||||
mShowFrame = mShowFill = true;
|
||||
mVerticalOffset = 0.5f;
|
||||
mDistanceFade = 0.1f;
|
||||
}
|
||||
|
||||
void GuiShapeNameHud::initPersistFields()
|
||||
{
|
||||
addGroup("Colors");
|
||||
addField( "fillColor", TypeColorF, Offset( mFillColor, GuiShapeNameHud ), "Standard color for the background of the control." );
|
||||
addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiShapeNameHud ), "Color for the control's frame." );
|
||||
addField( "textColor", TypeColorF, Offset( mTextColor, GuiShapeNameHud ), "Color for the text on this control." );
|
||||
endGroup("Colors");
|
||||
|
||||
addGroup("Misc");
|
||||
addField( "showFill", TypeBool, Offset( mShowFill, GuiShapeNameHud ), "If true, we draw the background color of the control." );
|
||||
addField( "showFrame", TypeBool, Offset( mShowFrame, GuiShapeNameHud ), "If true, we draw the frame of the control." );
|
||||
addField( "verticalOffset", TypeF32, Offset( mVerticalOffset, GuiShapeNameHud ), "Amount to vertically offset the control in relation to the ShapeBase object in focus." );
|
||||
addField( "distanceFade", TypeF32, Offset( mDistanceFade, GuiShapeNameHud ), "Visibility distance (how far the player must be from the ShapeBase object in focus) for this control to render." );
|
||||
endGroup("Misc");
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// 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)
|
||||
GFX->getDrawUtil()->drawRectFill(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;
|
||||
GameBase * control = dynamic_cast<GameBase*>(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) {
|
||||
ShapeBase* shape = dynamic_cast< ShapeBase* >(*itr);
|
||||
if ( shape ) {
|
||||
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->getTypeMask() & 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();
|
||||
SceneObject *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)
|
||||
GFX->getDrawUtil()->drawRect(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;
|
||||
GFX->getDrawUtil()->setBitmapModulation(mTextColor);
|
||||
GFX->getDrawUtil()->drawText(mProfile->mFont, offset, name);
|
||||
GFX->getDrawUtil()->clearBitmapModulation();
|
||||
}
|
||||
|
||||
208
Engine/source/T3D/fx/cameraFXMgr.cpp
Normal file
208
Engine/source/T3D/fx/cameraFXMgr.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/fx/cameraFXMgr.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "math/mMatrix.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;
|
||||
remoteControlled = false;
|
||||
isAdded = false;
|
||||
}
|
||||
|
||||
bool CameraShake::isExpired()
|
||||
{
|
||||
if ( remoteControlled )
|
||||
return false;
|
||||
|
||||
return Parent::isExpired();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Update
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraShake::update( F32 dt )
|
||||
{
|
||||
Parent::update( dt );
|
||||
|
||||
if ( !remoteControlled )
|
||||
fadeAmplitude();
|
||||
else
|
||||
mAmp = mStartAmp;
|
||||
|
||||
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.pushFront( newFX );
|
||||
}
|
||||
|
||||
void CameraFXManager::removeFX( CameraFX *fx )
|
||||
{
|
||||
CamFXList::Iterator itr = mFXList.begin();
|
||||
for ( ; itr != mFXList.end(); itr++ )
|
||||
{
|
||||
if ( *itr == fx )
|
||||
{
|
||||
mFXList.erase( itr );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Clear all currently running camera effects
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraFXManager::clear()
|
||||
{
|
||||
for(CamFXList::Iterator i = mFXList.begin(); i != mFXList.end(); ++i)
|
||||
{
|
||||
delete *i;
|
||||
}
|
||||
|
||||
mFXList.clear();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Update camera effects
|
||||
//--------------------------------------------------------------------------
|
||||
void CameraFXManager::update( F32 dt )
|
||||
{
|
||||
PROFILE_SCOPE( CameraFXManager_update );
|
||||
|
||||
mCamFXTrans.identity();
|
||||
|
||||
CamFXList::Iterator cur;
|
||||
for(CamFXList::Iterator i = mFXList.begin(); i != mFXList.end(); /*Trickiness*/)
|
||||
{
|
||||
// Store previous iterator and increment while iterator is still valid.
|
||||
cur = i;
|
||||
++i;
|
||||
CameraFX * curFX = *cur;
|
||||
curFX->update( dt );
|
||||
MatrixF fxTrans = curFX->getTrans();
|
||||
|
||||
mCamFXTrans.mul( fxTrans );
|
||||
|
||||
if( curFX->isExpired() )
|
||||
{
|
||||
delete curFX;
|
||||
mFXList.erase( cur );
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Engine/source/T3D/fx/cameraFXMgr.h
Normal file
113
Engine/source/T3D/fx/cameraFXMgr.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CAMERAFXMGR_H_
|
||||
#define _CAMERAFXMGR_H_
|
||||
|
||||
#ifndef _TORQUE_LIST_
|
||||
#include "core/util/tList.h"
|
||||
#endif
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
|
||||
//**************************************************************************
|
||||
// Abstract camera effect template
|
||||
//**************************************************************************
|
||||
class CameraFX
|
||||
{
|
||||
protected:
|
||||
MatrixF mCamFXTrans;
|
||||
F32 mElapsedTime;
|
||||
F32 mDuration;
|
||||
|
||||
public:
|
||||
CameraFX();
|
||||
|
||||
MatrixF & getTrans(){ return mCamFXTrans; }
|
||||
virtual 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:
|
||||
|
||||
/// Is controlled by someone else, ignore duration and do not delete.
|
||||
bool remoteControlled;
|
||||
bool isAdded;
|
||||
|
||||
CameraShake();
|
||||
|
||||
void init();
|
||||
void fadeAmplitude();
|
||||
void setFalloff( F32 falloff ){ mFalloff = falloff; }
|
||||
void setFrequency( VectorF &freq ){ mFreq = freq; }
|
||||
void setAmplitude( VectorF & ){ mStartAmp = amp; }
|
||||
bool isExpired();
|
||||
|
||||
virtual void update( F32 dt );
|
||||
};
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// CameraFXManager
|
||||
//**************************************************************************
|
||||
class CameraFXManager
|
||||
{
|
||||
typedef CameraFX * CameraFXPtr;
|
||||
|
||||
MatrixF mCamFXTrans;
|
||||
typedef Torque::List<CameraFXPtr> CamFXList;
|
||||
CamFXList mFXList;
|
||||
|
||||
public:
|
||||
void addFX( CameraFX *newFX );
|
||||
void removeFX( CameraFX *fx );
|
||||
void clear();
|
||||
MatrixF & getTrans(){ return mCamFXTrans; }
|
||||
void update( F32 dt );
|
||||
|
||||
CameraFXManager();
|
||||
~CameraFXManager();
|
||||
};
|
||||
|
||||
extern CameraFXManager gCamFXMgr;
|
||||
|
||||
|
||||
#endif
|
||||
1269
Engine/source/T3D/fx/explosion.cpp
Normal file
1269
Engine/source/T3D/fx/explosion.cpp
Normal file
File diff suppressed because it is too large
Load diff
199
Engine/source/T3D/fx/explosion.h
Normal file
199
Engine/source/T3D/fx/explosion.h
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _EXPLOSION_H_
|
||||
#define _EXPLOSION_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPE_H_
|
||||
#include "ts/tsShape.h"
|
||||
#endif
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
#ifndef _LIGHTINFO_H_
|
||||
#include "lighting/lightInfo.h"
|
||||
#endif
|
||||
|
||||
class ParticleEmitter;
|
||||
class ParticleEmitterData;
|
||||
class TSThread;
|
||||
class SFXTrack;
|
||||
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;
|
||||
|
||||
SFXTrack* soundProfile;
|
||||
ParticleEmitterData* particleEmitter;
|
||||
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;
|
||||
F32 lightStartBrightness;
|
||||
F32 lightEndBrightness;
|
||||
F32 lightNormalOffset;
|
||||
|
||||
ExplosionData();
|
||||
DECLARE_CONOBJECT(ExplosionData);
|
||||
bool onAdd();
|
||||
bool preload(bool server, String &errorStr);
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class Explosion : public GameBase, public ISceneLight
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
private:
|
||||
ExplosionData* mDataBlock;
|
||||
|
||||
TSShapeInstance* mExplosionInstance;
|
||||
TSThread* mExplosionThread;
|
||||
|
||||
SimObjectPtr<ParticleEmitter> mEmitterList[ ExplosionData::EC_NUM_EMITTERS ];
|
||||
SimObjectPtr<ParticleEmitter> mMainEmitter;
|
||||
|
||||
U32 mCurrMS;
|
||||
U32 mEndingMS;
|
||||
F32 mRandAngle;
|
||||
LightInfo* mLight;
|
||||
|
||||
protected:
|
||||
Point3F mInitialNormal;
|
||||
F32 mFade;
|
||||
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:
|
||||
void prepRenderImage( SceneRenderState *state );
|
||||
void prepBatchRender(SceneRenderState *state);
|
||||
void prepModelView(SceneRenderState*);
|
||||
|
||||
public:
|
||||
Explosion();
|
||||
~Explosion();
|
||||
void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0);
|
||||
|
||||
// ISceneLight
|
||||
virtual void submitLights( LightManager *lm, bool staticLighting );
|
||||
virtual LightInfo* getLight() { return mLight; }
|
||||
|
||||
bool onNewDataBlock( GameBaseData *dptr, bool reload );
|
||||
void setCollideType( U32 cType ){ mCollideType = cType; }
|
||||
|
||||
DECLARE_CONOBJECT(Explosion);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
#endif // _H_EXPLOSION
|
||||
|
||||
1818
Engine/source/T3D/fx/fxFoliageReplicator.cpp
Normal file
1818
Engine/source/T3D/fx/fxFoliageReplicator.cpp
Normal file
File diff suppressed because it is too large
Load diff
392
Engine/source/T3D/fx/fxFoliageReplicator.h
Normal file
392
Engine/source/T3D/fx/fxFoliageReplicator.h
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOLIAGEREPLICATOR_H_
|
||||
#define _FOLIAGEREPLICATOR_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _GFXTEXTUREHANDLE_H_
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _GBITMAP_H_
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#endif
|
||||
#ifndef _RENDERPASSMANAGER_H_
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#endif
|
||||
#ifndef _MATHUTIL_FRUSTUM_H_
|
||||
#include "math/util/frustum.h"
|
||||
#endif
|
||||
|
||||
#pragma warning( push, 4 )
|
||||
|
||||
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
|
||||
|
||||
#define FXFOLIAGEREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticShapeObjectType | \
|
||||
WaterObjectType )
|
||||
|
||||
#define FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticShapeObjectType )
|
||||
|
||||
|
||||
#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;
|
||||
// Used in DrawIndexPrimitive call.
|
||||
U32 startIndex;
|
||||
U32 primitiveCount;
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxFoliageRenderList
|
||||
//------------------------------------------------------------------------------
|
||||
class fxFoliageRenderList
|
||||
{
|
||||
public:
|
||||
|
||||
Box3F mBox; // Clipping Box.
|
||||
Frustum mFrustum; // View frustum.
|
||||
|
||||
Vector<fxFoliageItem*> mVisObjectSet; // Visible Object Set.
|
||||
F32 mHeightLerp; // Height Lerp.
|
||||
|
||||
public:
|
||||
bool IsQuadrantVisible(const Box3F VisBox, const MatrixF& RenderTransform);
|
||||
void SetupClipPlanes(SceneRenderState* state, const F32 FarClipPlane);
|
||||
void DrawQuadBox(const Box3F& QuadBox, const ColorF Colour);
|
||||
};
|
||||
|
||||
|
||||
// Define a vertex
|
||||
GFXDeclareVertexFormat( GFXVertexFoliage )
|
||||
{
|
||||
Point3F point;
|
||||
Point3F normal;
|
||||
Point2F texCoord;
|
||||
Point2F texCoord2;
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxFoliageReplicator
|
||||
//------------------------------------------------------------------------------
|
||||
class fxFoliageReplicator : public SceneObject
|
||||
{
|
||||
private:
|
||||
typedef SceneObject Parent;
|
||||
|
||||
protected:
|
||||
|
||||
void CreateFoliage(void);
|
||||
void DestroyFoliage(void);
|
||||
void DestroyFoliageItems();
|
||||
|
||||
|
||||
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;
|
||||
U32 mCurrentFoliageCount;
|
||||
|
||||
Vector<fxFoliageQuadrantNode*> mFoliageQuadTree;
|
||||
Vector<fxFoliageItem*> mReplicatedFoliage;
|
||||
fxFoliageRenderList mFrustumRenderSet;
|
||||
|
||||
GFXVertexBufferHandle<GFXVertexFoliage> mVertexBuffer;
|
||||
GFXPrimitiveBufferHandle mPrimBuffer;
|
||||
GFXShaderRef mShader;
|
||||
ShaderData* mShaderData;
|
||||
GBitmap* mAlphaLookup;
|
||||
|
||||
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.
|
||||
|
||||
// Used for alpha lookup in the pixel shader
|
||||
GFXTexHandle mAlphaTexture;
|
||||
|
||||
GFXStateBlockRef mPlacementSB;
|
||||
GFXStateBlockRef mRenderSB;
|
||||
GFXStateBlockRef mDebugSB;
|
||||
|
||||
GFXShaderConstBufferRef mFoliageShaderConsts;
|
||||
|
||||
GFXShaderConstHandle* mFoliageShaderProjectionSC;
|
||||
GFXShaderConstHandle* mFoliageShaderWorldSC;
|
||||
GFXShaderConstHandle* mFoliageShaderGlobalSwayPhaseSC;
|
||||
GFXShaderConstHandle* mFoliageShaderSwayMagnitudeSideSC;
|
||||
GFXShaderConstHandle* mFoliageShaderSwayMagnitudeFrontSC;
|
||||
GFXShaderConstHandle* mFoliageShaderGlobalLightPhaseSC;
|
||||
GFXShaderConstHandle* mFoliageShaderLuminanceMagnitudeSC;
|
||||
GFXShaderConstHandle* mFoliageShaderLuminanceMidpointSC;
|
||||
GFXShaderConstHandle* mFoliageShaderDistanceRangeSC;
|
||||
GFXShaderConstHandle* mFoliageShaderCameraPosSC;
|
||||
GFXShaderConstHandle* mFoliageShaderTrueBillboardSC;
|
||||
|
||||
//pixel shader
|
||||
GFXShaderConstHandle* mFoliageShaderGroundAlphaSC;
|
||||
GFXShaderConstHandle* mFoliageShaderAmbientColorSC;
|
||||
GFXShaderConstHandle* mDiffuseTextureSC;
|
||||
GFXShaderConstHandle* mAlphaMapTextureSC;
|
||||
|
||||
|
||||
|
||||
bool mDirty;
|
||||
|
||||
void SetupShader();
|
||||
void SetupBuffers();
|
||||
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance*);
|
||||
void renderBuffers(SceneRenderState* state);
|
||||
void renderArc(const F32 fRadiusX, const F32 fRadiusY);
|
||||
void renderPlacementArea(const F32 ElapsedTime);
|
||||
void renderQuad(fxFoliageQuadrantNode* quadNode, const MatrixF& RenderTransform, const bool UseDebug);
|
||||
void computeAlphaTex();
|
||||
public:
|
||||
fxFoliageReplicator();
|
||||
~fxFoliageReplicator();
|
||||
|
||||
void StartUp(void);
|
||||
void ShowReplication(void);
|
||||
void HideReplication(void);
|
||||
|
||||
// SceneObject
|
||||
virtual void prepRenderImage( SceneRenderState *state );
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// Editor
|
||||
void onGhostAlwaysDone();
|
||||
|
||||
// ConObject.
|
||||
static void initPersistFields();
|
||||
|
||||
// Field Data.
|
||||
class tagFieldData
|
||||
{
|
||||
public:
|
||||
|
||||
bool mUseDebugInfo;
|
||||
F32 mDebugBoxHeight;
|
||||
U32 mSeed;
|
||||
StringTableEntry mFoliageFile;
|
||||
GFXTexHandle 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 mUseTrueBillboards;
|
||||
|
||||
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 = GFXTexHandle();
|
||||
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;
|
||||
mUseTrueBillboards = false;
|
||||
|
||||
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);
|
||||
};
|
||||
#pragma warning( pop )
|
||||
#endif // _FOLIAGEREPLICATOR_H_
|
||||
764
Engine/source/T3D/fx/fxShapeReplicator.cpp
Normal file
764
Engine/source/T3D/fx/fxShapeReplicator.cpp
Normal file
|
|
@ -0,0 +1,764 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/fx/fxShapeReplicator.h"
|
||||
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "gfx/primBuilder.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "console/engineAPI.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);
|
||||
|
||||
ConsoleDocClass( fxShapeReplicator,
|
||||
"@brief An emitter for objects to replicate across an area.\n"
|
||||
"@ingroup Foliage\n"
|
||||
);
|
||||
|
||||
ConsoleDocClass( fxShapeReplicatedStatic,
|
||||
"@brief The object definition for shapes that will be replicated across an area using an fxShapeReplicator.\n"
|
||||
"@ingroup Foliage\n"
|
||||
);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Class: fxShapeReplicator
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fxShapeReplicator::fxShapeReplicator()
|
||||
{
|
||||
// Setup NetObject.
|
||||
mTypeMask |= StaticObjectType;
|
||||
mNetFlags.set(Ghostable | ScopeAlways);
|
||||
|
||||
// Reset Shape Count.
|
||||
mCurrentShapeCount = 0;
|
||||
|
||||
// Reset Creation Area Angle Animation.
|
||||
mCreationAreaAngle = 0;
|
||||
|
||||
// Reset Last Render Time.
|
||||
mLastRenderTime = 0;
|
||||
|
||||
mPlacementSB = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fxShapeReplicator::~fxShapeReplicator()
|
||||
{
|
||||
mPlacementSB = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::initPersistFields()
|
||||
{
|
||||
// Add out own persistent fields.
|
||||
addGroup( "Debugging" ); // MM: Added Group Header.
|
||||
addField( "HideReplications", TypeBool, Offset( mFieldData.mHideReplications, fxShapeReplicator ), "Replicated shapes are hidden when set to true." );
|
||||
addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxShapeReplicator ), "Draw placement rings when set to true." );
|
||||
addField( "PlacementAreaHeight", TypeS32, Offset( mFieldData.mPlacementBandHeight, fxShapeReplicator ), "Height of the placement ring in world units." );
|
||||
addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxShapeReplicator ), "Color of the placement ring." );
|
||||
endGroup( "Debugging" ); // MM: Added Group Footer.
|
||||
|
||||
addGroup( "Media" ); // MM: Added Group Header.
|
||||
addField( "ShapeFile", TypeShapeFilename, Offset( mFieldData.mShapeFile, fxShapeReplicator ), "Filename of shape to replicate." );
|
||||
endGroup( "Media" ); // MM: Added Group Footer.
|
||||
|
||||
addGroup( "Replications" ); // MM: Added Group Header.
|
||||
addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxShapeReplicator ), "Random seed for shape placement." );
|
||||
addField( "ShapeCount", TypeS32, Offset( mFieldData.mShapeCount, fxShapeReplicator ), "Maximum shape instance count." );
|
||||
addField( "ShapeRetries", TypeS32, Offset( mFieldData.mShapeRetries, fxShapeReplicator ), "Number of times to try placing a shape instance before giving up." );
|
||||
endGroup( "Replications" ); // MM: Added Group Footer.
|
||||
|
||||
addGroup( "Placement Radius" ); // MM: Added Group Header.
|
||||
addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxShapeReplicator ), "Placement area inner radius on the X axis" );
|
||||
addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxShapeReplicator ), "Placement area inner radius on the Y axis" );
|
||||
addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxShapeReplicator ), "Placement area outer radius on the X axis" );
|
||||
addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxShapeReplicator ), "Placement area outer radius on the Y axis" );
|
||||
endGroup( "Placement Radius" ); // MM: Added Group Footer.
|
||||
|
||||
addGroup( "Restraints" ); // MM: Added Group Header.
|
||||
addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxShapeReplicator ), "Shapes will be placed on terrain when set." );
|
||||
addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxShapeReplicator ), "Shapes will be placed on InteriorInstances when set." );
|
||||
addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxShapeReplicator ), "Shapes will be placed on Static shapes when set." );
|
||||
addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxShapeReplicator ), "Shapes will be placed on/under water when set." );
|
||||
addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxShapeReplicator ), "Shapes will be placed on water when set. Requires AllowOnWater." );
|
||||
addField( "AlignToTerrain", TypeBool, Offset( mFieldData.mAlignToTerrain, fxShapeReplicator ), "Align shapes to surface normal when set." );
|
||||
addField( "Interactions", TypeBool, Offset( mFieldData.mInteractions, fxShapeReplicator ), "Allow physics interactions with shapes." );
|
||||
addField( "AllowedTerrainSlope", TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxShapeReplicator ), "Maximum surface angle allowed for shape instances." );
|
||||
addField( "TerrainAlignment", TypePoint3F, Offset( mFieldData.mTerrainAlignment, fxShapeReplicator ), "Surface normals will be multiplied by these values when AlignToTerrain is enabled." );
|
||||
endGroup( "Restraints" ); // MM: Added Group Footer.
|
||||
|
||||
addGroup( "Object Transforms" ); // MM: Added Group Header.
|
||||
addField( "ShapeScaleMin", TypePoint3F, Offset( mFieldData.mShapeScaleMin, fxShapeReplicator ), "Minimum shape scale." );
|
||||
addField( "ShapeScaleMax", TypePoint3F, Offset( mFieldData.mShapeScaleMax, fxShapeReplicator ), "Maximum shape scale." );
|
||||
addField( "ShapeRotateMin", TypePoint3F, Offset( mFieldData.mShapeRotateMin, fxShapeReplicator ), "Minimum shape rotation angles.");
|
||||
addField( "ShapeRotateMax", TypePoint3F, Offset( mFieldData.mShapeRotateMax, fxShapeReplicator ), "Maximum shape rotation angles." );
|
||||
addField( "OffsetZ", TypeS32, Offset( mFieldData.mOffsetZ, fxShapeReplicator ), "Offset shapes by this amount vertically." );
|
||||
endGroup( "Object Transforms" ); // MM: Added Group Footer.
|
||||
|
||||
// Initialise parents' persistent fields.
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
|
||||
|
||||
// Don't create shapes if we are hiding replications.
|
||||
if (mFieldData.mHideReplications) return;
|
||||
|
||||
// Cannot continue without shapes!
|
||||
if (dStrcmp(mFieldData.mShapeFile, "") == 0) 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.clear();
|
||||
|
||||
// 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
|
||||
{
|
||||
// Get the Replicator Position.
|
||||
ShapePosition = getPosition();
|
||||
|
||||
// Calculate a random offset
|
||||
HypX = RandomGen.randF(mFieldData.mInnerRadiusX, mFieldData.mOuterRadiusX);
|
||||
HypY = RandomGen.randF(mFieldData.mInnerRadiusY, mFieldData.mOuterRadiusY);
|
||||
Angle = RandomGen.randF(0, (F32)M_2PI);
|
||||
|
||||
// Calcualte the new position.
|
||||
ShapePosition.x += HypX * mCos(Angle);
|
||||
ShapePosition.y += HypY * mSin(Angle);
|
||||
|
||||
|
||||
// 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 & StaticShapeObjectType) && !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.
|
||||
ShapeScale.set( RandomGen.randF(mFieldData.mShapeScaleMin.x, mFieldData.mShapeScaleMax.x),
|
||||
RandomGen.randF(mFieldData.mShapeScaleMin.y, mFieldData.mShapeScaleMax.y),
|
||||
RandomGen.randF(mFieldData.mShapeScaleMin.z, mFieldData.mShapeScaleMax.z));
|
||||
|
||||
// Set Shape Scale.
|
||||
fxStatic->setScale(ShapeScale);
|
||||
|
||||
// Lock it.
|
||||
fxStatic->setLocked(true);
|
||||
|
||||
// Store Shape in Replicated Shapes Vector.
|
||||
//mReplicatedShapes[mCurrentShapeCount++] = fxStatic;
|
||||
mReplicatedShapes.push_back(fxStatic);
|
||||
|
||||
}
|
||||
|
||||
mCurrentShapeCount = mReplicatedShapes.size();
|
||||
|
||||
// Take first Timestamp.
|
||||
mLastRenderTime = Platform::getVirtualMilliseconds();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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.clear();
|
||||
|
||||
// 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)
|
||||
{
|
||||
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.minExtents.set( -0.5, -0.5, -0.5 );
|
||||
mObjBox.maxExtents.set( 0.5, 0.5, 0.5 );
|
||||
resetWorldBox();
|
||||
|
||||
// Add to Scene.
|
||||
setRenderTransform(mObjToWorld);
|
||||
addToScene();
|
||||
|
||||
// Register for notification when GhostAlways objects are done loading
|
||||
NetConnection::smGhostAlwaysDone.notify( this, &fxShapeReplicator::onGhostAlwaysDone );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::onRemove()
|
||||
{
|
||||
// Remove the Replicator from the Replicator Set.
|
||||
dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"))->removeObject(this);
|
||||
|
||||
NetConnection::smGhostAlwaysDone.remove( this, &fxShapeReplicator::onGhostAlwaysDone );
|
||||
|
||||
removeFromScene();
|
||||
|
||||
// Destroy Shapes.
|
||||
DestroyShapes();
|
||||
|
||||
// Do Parent.
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::onGhostAlwaysDone()
|
||||
{
|
||||
RenewShapes();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::inspectPostApply()
|
||||
{
|
||||
// Set Parent.
|
||||
Parent::inspectPostApply();
|
||||
|
||||
// Renew Shapes.
|
||||
RenewShapes();
|
||||
|
||||
// Set Replication Mask.
|
||||
setMaskBits(ReplicationMask);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(StartClientReplication, void, (),, "Activates the shape replicator.\n"
|
||||
"@tsexample\n"
|
||||
"// Call the function\n"
|
||||
"StartClientReplication()\n"
|
||||
"@endtsexample\n"
|
||||
"@ingroup Foliage"
|
||||
)
|
||||
{
|
||||
// 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!");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::prepRenderImage( SceneRenderState* state )
|
||||
{
|
||||
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
|
||||
ri->renderDelegate.bind(this, &fxShapeReplicator::renderObject);
|
||||
// The fxShapeReplicator isn't technically foliage but our debug
|
||||
// effect seems to render best as a Foliage type (translucent,
|
||||
// renders itself, no sorting)
|
||||
ri->type = RenderPassManager::RIT_Foliage;
|
||||
state->getRenderPass()->addInst( ri );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Renders a triangle stripped oval
|
||||
void fxShapeReplicator::renderArc(const F32 fRadiusX, const F32 fRadiusY)
|
||||
{
|
||||
PrimBuild::begin(GFXTriangleStrip, 720);
|
||||
for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++)
|
||||
{
|
||||
F32 XPos, YPos;
|
||||
|
||||
// Calculate Position.
|
||||
XPos = fRadiusX * mCos(mDegToRad(-(F32)Angle));
|
||||
YPos = fRadiusY * mSin(mDegToRad(-(F32)Angle));
|
||||
|
||||
// Set Colour.
|
||||
PrimBuild::color4f(mFieldData.mPlaceAreaColour.red,
|
||||
mFieldData.mPlaceAreaColour.green,
|
||||
mFieldData.mPlaceAreaColour.blue,
|
||||
AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle));
|
||||
|
||||
PrimBuild::vertex3f(XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f);
|
||||
PrimBuild::vertex3f(XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f);
|
||||
}
|
||||
PrimBuild::end();
|
||||
}
|
||||
|
||||
// This currently uses the primbuilder, could convert out, but why allocate the buffer if we
|
||||
// never edit the misison?
|
||||
void fxShapeReplicator::renderPlacementArea(const F32 ElapsedTime)
|
||||
{
|
||||
if (gEditingMission && mFieldData.mShowPlacementArea)
|
||||
{
|
||||
GFX->pushWorldMatrix();
|
||||
GFX->multWorld(getTransform());
|
||||
|
||||
if (!mPlacementSB)
|
||||
{
|
||||
GFXStateBlockDesc transparent;
|
||||
transparent.setCullMode(GFXCullNone);
|
||||
transparent.alphaTestEnable = true;
|
||||
transparent.setZReadWrite(true);
|
||||
transparent.zWriteEnable = false;
|
||||
transparent.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
|
||||
mPlacementSB = GFX->createStateBlock( transparent );
|
||||
}
|
||||
|
||||
GFX->setStateBlock(mPlacementSB);
|
||||
|
||||
// Do we need to draw the Outer Radius?
|
||||
if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY)
|
||||
renderArc((F32) mFieldData.mOuterRadiusX, (F32) mFieldData.mOuterRadiusY);
|
||||
// Inner radius?
|
||||
if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY)
|
||||
renderArc((F32) mFieldData.mInnerRadiusX, (F32) mFieldData.mInnerRadiusY);
|
||||
|
||||
GFX->popWorldMatrix();
|
||||
mCreationAreaAngle = (U32)(mCreationAreaAngle + (1000 * ElapsedTime));
|
||||
mCreationAreaAngle = mCreationAreaAngle % 360;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void fxShapeReplicator::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
|
||||
{
|
||||
if (overrideMat)
|
||||
return;
|
||||
|
||||
// Return if placement area not needed.
|
||||
if (!mFieldData.mShowPlacementArea)
|
||||
return;
|
||||
|
||||
// Calculate Elapsed Time and take new Timestamp.
|
||||
S32 Time = Platform::getVirtualMilliseconds();
|
||||
F32 ElapsedTime = (Time - mLastRenderTime) * 0.001f;
|
||||
mLastRenderTime = Time;
|
||||
|
||||
renderPlacementArea(ElapsedTime);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
|
||||
RenewShapes();
|
||||
}
|
||||
}
|
||||
|
||||
196
Engine/source/T3D/fx/fxShapeReplicator.h
Normal file
196
Engine/source/T3D/fx/fxShapeReplicator.h
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SHAPEREPLICATOR_H_
|
||||
#define _SHAPEREPLICATOR_H_
|
||||
|
||||
#ifndef _TSSTATIC_H_
|
||||
#include "T3D/tsStatic.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#endif
|
||||
#ifndef _RENDERPASSMANAGER_H_
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#endif
|
||||
|
||||
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
|
||||
|
||||
#define FXREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticShapeObjectType | \
|
||||
WaterObjectType )
|
||||
|
||||
#define FXREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
|
||||
InteriorObjectType | \
|
||||
StaticShapeObjectType )
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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;
|
||||
U32 mCurrentShapeCount;
|
||||
Vector<fxShapeReplicatedStatic*> mReplicatedShapes;
|
||||
MRandomLCG RandomGen;
|
||||
S32 mLastRenderTime;
|
||||
|
||||
|
||||
public:
|
||||
fxShapeReplicator();
|
||||
~fxShapeReplicator();
|
||||
|
||||
|
||||
void StartUp(void);
|
||||
void ShowReplication(void);
|
||||
void HideReplication(void);
|
||||
|
||||
GFXStateBlockRef mPlacementSB;
|
||||
|
||||
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance*);
|
||||
void renderArc(const F32 fRadiusX, const F32 fRadiusY);
|
||||
void renderPlacementArea(const F32 ElapsedTime);
|
||||
|
||||
// SceneObject
|
||||
virtual void prepRenderImage( SceneRenderState *state );
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void inspectPostApply();
|
||||
|
||||
// NetObject
|
||||
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
|
||||
// Editor
|
||||
void onGhostAlwaysDone();
|
||||
|
||||
// ConObject.
|
||||
static void initPersistFields();
|
||||
|
||||
// Field Data.
|
||||
class tagFieldData
|
||||
{
|
||||
public:
|
||||
|
||||
U32 mSeed;
|
||||
StringTableEntry mShapeFile;
|
||||
U32 mShapeCount;
|
||||
U32 mShapeRetries;
|
||||
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);
|
||||
|
||||
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_
|
||||
1694
Engine/source/T3D/fx/groundCover.cpp
Normal file
1694
Engine/source/T3D/fx/groundCover.cpp
Normal file
File diff suppressed because it is too large
Load diff
391
Engine/source/T3D/fx/groundCover.h
Normal file
391
Engine/source/T3D/fx/groundCover.h
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GROUNDCOVER_H_
|
||||
#define _GROUNDCOVER_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _MATHUTIL_FRUSTUM_H_
|
||||
#include "math/util/frustum.h"
|
||||
#endif
|
||||
#ifndef _GFXTEXTUREHANDLE_H_
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
#endif
|
||||
#ifndef _GFX_GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _RENDERPASSMANAGER_H_
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#endif
|
||||
#ifndef _MATTEXTURETARGET_H_
|
||||
#include "materials/matTextureTarget.h"
|
||||
#endif
|
||||
#ifndef _SHADERFEATURE_H_
|
||||
#include "shaderGen/shaderFeature.h"
|
||||
#endif
|
||||
|
||||
class TerrainBlock;
|
||||
class GroundCoverCell;
|
||||
class TSShapeInstance;
|
||||
class Material;
|
||||
class MaterialParameters;
|
||||
class MaterialParameterHandle;
|
||||
|
||||
|
||||
///
|
||||
#define MAX_COVERTYPES 8
|
||||
|
||||
|
||||
GFXDeclareVertexFormat( GCVertex )
|
||||
{
|
||||
Point3F point;
|
||||
|
||||
Point3F normal;
|
||||
|
||||
// .rgb = ambient
|
||||
// .a = corner index
|
||||
GFXVertexColor ambient;
|
||||
|
||||
// .x = size x
|
||||
// .y = size y
|
||||
// .z = type
|
||||
// .w = wind amplitude
|
||||
Point4F params;
|
||||
};
|
||||
|
||||
struct GroundCoverShaderConstData
|
||||
{
|
||||
Point2F fadeInfo;
|
||||
Point3F gustInfo;
|
||||
Point2F turbInfo;
|
||||
Point3F camRight;
|
||||
Point3F camUp;
|
||||
};
|
||||
|
||||
class GroundCover;
|
||||
|
||||
class GroundCoverShaderConstHandles : public ShaderFeatureConstHandles
|
||||
{
|
||||
public:
|
||||
|
||||
GroundCoverShaderConstHandles();
|
||||
|
||||
virtual void init( GFXShader *shader );
|
||||
|
||||
virtual void setConsts( SceneRenderState *state,
|
||||
const SceneData &sgData,
|
||||
GFXShaderConstBuffer *buffer );
|
||||
|
||||
GroundCover *mGroundCover;
|
||||
|
||||
GFXShaderConstHandle *mTypeRectsSC;
|
||||
GFXShaderConstHandle *mFadeSC;
|
||||
GFXShaderConstHandle *mWindDirSC;
|
||||
GFXShaderConstHandle *mGustInfoSC;
|
||||
GFXShaderConstHandle *mTurbInfoSC;
|
||||
GFXShaderConstHandle *mCamRightSC;
|
||||
GFXShaderConstHandle *mCamUpSC;
|
||||
};
|
||||
|
||||
|
||||
class GroundCover : public SceneObject
|
||||
{
|
||||
friend class GroundCoverShaderConstHandles;
|
||||
friend class GroundCoverCell;
|
||||
typedef SceneObject Parent;
|
||||
|
||||
public:
|
||||
|
||||
GroundCover();
|
||||
~GroundCover();
|
||||
|
||||
DECLARE_CONOBJECT(GroundCover);
|
||||
|
||||
static void consoleInit();
|
||||
static void initPersistFields();
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
void inspectPostApply();
|
||||
|
||||
// Network
|
||||
U32 packUpdate( NetConnection *, U32 mask, BitStream *stream );
|
||||
void unpackUpdate( NetConnection *, BitStream *stream );
|
||||
|
||||
// Rendering
|
||||
void prepRenderImage( SceneRenderState *state );
|
||||
|
||||
// Editor
|
||||
void onTerrainUpdated( U32 flags, TerrainBlock *tblock, const Point2I& min, const Point2I& max );
|
||||
|
||||
// Misc
|
||||
const GroundCoverShaderConstData& getShaderConstData() const { return mShaderConstData; }
|
||||
|
||||
/// Sets the global ground cover LOD scalar which controls
|
||||
/// the percentage of the maximum designed cover to put down.
|
||||
/// It scales both rendering cost and placement CPU performance.
|
||||
/// Returns the actual value set.
|
||||
static F32 setQualityScale( F32 scale ) { return smDensityScale = mClampF( scale, 0.0f, 1.0f ); }
|
||||
|
||||
/// Returns the current quality scale... see above.
|
||||
static F32 getQualityScale() { return smDensityScale; }
|
||||
|
||||
protected:
|
||||
|
||||
enum MaskBits
|
||||
{
|
||||
TerrainBlockMask = Parent::NextFreeMask << 0,
|
||||
NextFreeMask = Parent::NextFreeMask << 1
|
||||
};
|
||||
|
||||
MaterialParameters *mMatParams;
|
||||
MaterialParameterHandle *mTypeRectsParam;
|
||||
MaterialParameterHandle *mFadeParams;
|
||||
MaterialParameterHandle *mWindDirParam;
|
||||
MaterialParameterHandle *mGustInfoParam;
|
||||
MaterialParameterHandle *mTurbInfoParam;
|
||||
MaterialParameterHandle *mCamRightParam;
|
||||
MaterialParameterHandle *mCamUpParam;
|
||||
|
||||
/// This RNG seed is saved and sent to clients
|
||||
/// for generating the same cover.
|
||||
S32 mRandomSeed;
|
||||
|
||||
/// This is the outer generation radius from
|
||||
/// the current camera position.
|
||||
F32 mRadius;
|
||||
|
||||
// Offset along the Z axis to render the ground cover.
|
||||
F32 mZOffset;
|
||||
|
||||
/// This is less than or equal to mRadius and
|
||||
/// defines when fading of cover elements begins.
|
||||
F32 mFadeRadius;
|
||||
|
||||
/// This is the distance at which DTS elements are
|
||||
/// completely culled out.
|
||||
F32 mShapeCullRadius;
|
||||
|
||||
/// Whether shapes rendered by the GroundCover should cast shadows.
|
||||
bool mShapesCastShadows;
|
||||
|
||||
/// This is used to scale the various culling radii
|
||||
/// when rendering a reflection... typically for water.
|
||||
F32 mReflectRadiusScale;
|
||||
|
||||
/// This is the number of cells per axis in the grid.
|
||||
U32 mGridSize;
|
||||
|
||||
typedef Vector<GroundCoverCell*> CellVector;
|
||||
|
||||
/// This is the allocator for GridCell chunks.
|
||||
CellVector mAllocCellList;
|
||||
CellVector mFreeCellList;
|
||||
|
||||
/// This is the grid of active cells.
|
||||
CellVector mCellGrid;
|
||||
|
||||
/// This is a scratch grid used while updating
|
||||
/// the cell grid.
|
||||
CellVector mScratchGrid;
|
||||
|
||||
/// This is the index to the first grid cell.
|
||||
Point2I mGridIndex;
|
||||
|
||||
/// The maximum amount of cover elements to include in
|
||||
/// the grid at any one time. The actual amount may be
|
||||
/// less than this based on randomization.
|
||||
S32 mMaxPlacement;
|
||||
|
||||
/// Used to detect changes in cell placement count from
|
||||
/// the global quality scale so we can regen the cells.
|
||||
S32 mLastPlacementCount;
|
||||
|
||||
/// Used for culling cells to update and render.
|
||||
Frustum mCuller;
|
||||
|
||||
/// Debug parameter for displaying the grid cells.
|
||||
bool mDebugRenderCells;
|
||||
|
||||
/// Debug parameter for turning off billboard rendering.
|
||||
bool mDebugNoBillboards;
|
||||
|
||||
/// Debug parameter for turning off shape rendering.
|
||||
bool mDebugNoShapes;
|
||||
|
||||
/// Debug parameter for locking the culling frustum which
|
||||
/// will freeze the cover generation.
|
||||
bool mDebugLockFrustum;
|
||||
|
||||
/// Stat for number of rendered cells.
|
||||
static U32 smStatRenderedCells;
|
||||
|
||||
/// Stat for number of rendered billboards.
|
||||
static U32 smStatRenderedBillboards;
|
||||
|
||||
/// Stat for number of rendered billboard batches.
|
||||
static U32 smStatRenderedBatches;
|
||||
|
||||
/// Stat for number of rendered shapes.
|
||||
static U32 smStatRenderedShapes;
|
||||
|
||||
/// The global ground cover LOD scalar which controls
|
||||
/// the percentage of the maximum amount of cover to put
|
||||
/// down. It scales both rendering cost and placement
|
||||
/// CPU performance.
|
||||
static F32 smDensityScale;
|
||||
|
||||
String mMaterialName;
|
||||
Material *mMaterial;
|
||||
BaseMatInstance *mMatInst;
|
||||
|
||||
GroundCoverShaderConstData mShaderConstData;
|
||||
|
||||
/// This is the maximum amout of degrees the billboard will
|
||||
/// tilt down to match the camera.
|
||||
F32 mMaxBillboardTiltAngle;
|
||||
|
||||
/// The probability of one cover type verses another.
|
||||
F32 mProbability[MAX_COVERTYPES];
|
||||
|
||||
/// The minimum random size for each cover type.
|
||||
F32 mSizeMin[MAX_COVERTYPES];
|
||||
|
||||
/// The maximum random size of this cover type.
|
||||
F32 mSizeMax[MAX_COVERTYPES];
|
||||
|
||||
/// An exponent used to bias between the minimum
|
||||
/// and maximum random sizes.
|
||||
F32 mSizeExponent[MAX_COVERTYPES];
|
||||
|
||||
/// The wind effect scale.
|
||||
F32 mWindScale[MAX_COVERTYPES];
|
||||
|
||||
/// The maximum slope angle in degrees for placement.
|
||||
F32 mMaxSlope[MAX_COVERTYPES];
|
||||
|
||||
/// The minimum world space elevation for placement.
|
||||
F32 mMinElevation[MAX_COVERTYPES];
|
||||
|
||||
/// The maximum world space elevation for placement.
|
||||
F32 mMaxElevation[MAX_COVERTYPES];
|
||||
|
||||
/// Terrain material name to limit coverage to, or
|
||||
/// left empty to cover entire terrain.
|
||||
StringTableEntry mLayer[MAX_COVERTYPES];
|
||||
|
||||
/// Inverts the data layer test making the
|
||||
/// layer an exclusion mask.
|
||||
bool mInvertLayer[MAX_COVERTYPES];
|
||||
|
||||
/// The minimum amount of elements in a clump.
|
||||
S32 mMinClumpCount[MAX_COVERTYPES];
|
||||
|
||||
/// The maximum amount of elements in a clump.
|
||||
S32 mMaxClumpCount[MAX_COVERTYPES];
|
||||
|
||||
/// An exponent used to bias between the minimum
|
||||
/// and maximum clump counts for a particular clump.
|
||||
F32 mClumpCountExponent[MAX_COVERTYPES];
|
||||
|
||||
/// The maximum clump radius.
|
||||
F32 mClumpRadius[MAX_COVERTYPES];
|
||||
|
||||
/// This is a cached array of billboard aspect scales
|
||||
/// used to avoid some calculations when generating cells.
|
||||
F32 mBillboardAspectScales[MAX_COVERTYPES];
|
||||
|
||||
RectF mBillboardRects[MAX_COVERTYPES];
|
||||
|
||||
/// The cover shape filenames.
|
||||
StringTableEntry mShapeFilenames[MAX_COVERTYPES];
|
||||
|
||||
/// The cover shape instances.
|
||||
TSShapeInstance* mShapeInstances[MAX_COVERTYPES];
|
||||
|
||||
/// This is the same as mProbability, but normalized for use
|
||||
/// during the cover placement process.
|
||||
F32 mNormalizedProbability[MAX_COVERTYPES];
|
||||
|
||||
/// A shared primitive buffer setup for drawing the maximum amount
|
||||
/// of billboards you could possibly have in a single cell.
|
||||
GFXPrimitiveBufferHandle mPrimBuffer;
|
||||
|
||||
/// The length in meters between peaks in the wind gust.
|
||||
F32 mWindGustLength;
|
||||
|
||||
/// Controls how often the wind gust peaks per second.
|
||||
F32 mWindGustFrequency;
|
||||
|
||||
/// The maximum distance in meters that the peak wind
|
||||
/// gust will displace an element.
|
||||
F32 mWindGustStrength;
|
||||
|
||||
/// The direction of the wind.
|
||||
Point2F mWindDirection;
|
||||
|
||||
/// Controls the overall rapidity of the wind turbulence.
|
||||
F32 mWindTurbulenceFrequency;
|
||||
|
||||
/// The maximum distance in meters that the turbulence can
|
||||
/// displace a ground cover element.
|
||||
F32 mWindTurbulenceStrength;
|
||||
|
||||
void _initMaterial();
|
||||
|
||||
bool _initShader();
|
||||
|
||||
void _initShapes();
|
||||
|
||||
void _deleteShapes();
|
||||
|
||||
/// Called when GroundCover parameters are changed and
|
||||
/// things need to be reinitialized to continue.
|
||||
void _initialize( U32 cellCount, U32 cellPlacementCount );
|
||||
|
||||
/// Updates the cover grid by removing cells that
|
||||
/// have fallen outside of mRadius and adding new
|
||||
/// ones that have come into view.
|
||||
void _updateCoverGrid( const Frustum &culler );
|
||||
|
||||
/// Clears the cell grid, moves all the allocated cells to
|
||||
/// the free list, and deletes excess free cells.
|
||||
void _freeCells();
|
||||
|
||||
/// Clears the cell grid and deletes all the free cells.
|
||||
void _deleteCells();
|
||||
|
||||
/// Returns a cell to the free list.
|
||||
void _recycleCell( GroundCoverCell* cell );
|
||||
|
||||
/// Generates a new cell using the recycle list when possible.
|
||||
GroundCoverCell* _generateCell( const Point2I& index,
|
||||
const Box3F& bounds,
|
||||
U32 placementCount,
|
||||
S32 randSeed );
|
||||
|
||||
void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
|
||||
};
|
||||
|
||||
#endif // _GROUNDCOVER_H_
|
||||
1248
Engine/source/T3D/fx/lightning.cpp
Normal file
1248
Engine/source/T3D/fx/lightning.cpp
Normal file
File diff suppressed because it is too large
Load diff
241
Engine/source/T3D/fx/lightning.h
Normal file
241
Engine/source/T3D/fx/lightning.h
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _LIGHTNING_H_
|
||||
#define _LIGHTNING_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
#ifndef _TORQUE_LIST_
|
||||
#include "core/util/tList.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
#ifndef _RENDERPASSMANAGER_H_
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#endif
|
||||
#ifndef _ENGINEAPI_H_
|
||||
#include "console/engineAPI.h"
|
||||
#endif
|
||||
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
|
||||
|
||||
|
||||
class ShapeBase;
|
||||
class LightningStrikeEvent;
|
||||
class SFXTrack;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
class LightningData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
public:
|
||||
enum Constants {
|
||||
MaxThunders = 8,
|
||||
MaxTextures = 8
|
||||
};
|
||||
|
||||
//-------------------------------------- Console set variables
|
||||
public:
|
||||
SFXTrack* thunderSounds[MaxThunders];
|
||||
SFXTrack* strikeSound;
|
||||
StringTableEntry strikeTextureNames[MaxTextures];
|
||||
|
||||
//-------------------------------------- load set variables
|
||||
public:
|
||||
|
||||
GFXTexHandle strikeTextures[MaxTextures];
|
||||
U32 numThunders;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
|
||||
public:
|
||||
LightningData();
|
||||
~LightningData();
|
||||
|
||||
void packData(BitStream*);
|
||||
void unpackData(BitStream*);
|
||||
bool preload(bool server, String &errorStr);
|
||||
|
||||
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;
|
||||
|
||||
typedef Torque::List<LightningBolt> LightingBoltList;
|
||||
LightingBoltList 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( const Point3F &startPoint, const Point3F &endPoint, U32 depth, F32 width );
|
||||
F32 findHeight( Point3F &point, SceneManager* 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, bool reload );
|
||||
|
||||
DECLARE_CALLBACK( void, applyDamage, ( const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject ));
|
||||
|
||||
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;
|
||||
|
||||
GFXStateBlockRef mLightningSB;
|
||||
|
||||
protected:
|
||||
|
||||
// Rendering
|
||||
void prepRenderImage(SceneRenderState *state);
|
||||
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* );
|
||||
|
||||
// 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 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
|
||||
|
||||
633
Engine/source/T3D/fx/particle.cpp
Normal file
633
Engine/source/T3D/fx/particle.cpp
Normal file
|
|
@ -0,0 +1,633 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "particle.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1( ParticleData );
|
||||
|
||||
ConsoleDocClass( ParticleData,
|
||||
"@brief Contains information for how specific particles should look and react "
|
||||
"including particle colors, particle imagemap, acceleration value for individual "
|
||||
"particles and spin information.\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock ParticleData( GLWaterExpSmoke )\n"
|
||||
"{\n"
|
||||
" textureName = \"art/shapes/particles/smoke\";\n"
|
||||
" dragCoefficient = 0.4;\n"
|
||||
" gravityCoefficient = -0.25;\n"
|
||||
" inheritedVelFactor = 0.025;\n"
|
||||
" constantAcceleration = -1.1;\n"
|
||||
" lifetimeMS = 1250;\n"
|
||||
" lifetimeVarianceMS = 0;\n"
|
||||
" useInvAlpha = false;\n"
|
||||
" spinSpeed = 1;\n"
|
||||
" spinRandomMin = -200.0;\n"
|
||||
" spinRandomMax = 200.0;\n\n"
|
||||
" colors[0] = \"0.1 0.1 1.0 1.0\";\n"
|
||||
" colors[1] = \"0.4 0.4 1.0 1.0\";\n"
|
||||
" colors[2] = \"0.4 0.4 1.0 0.0\";\n\n"
|
||||
" sizes[0] = 2.0;\n"
|
||||
" sizes[1] = 6.0;\n"
|
||||
" sizes[2] = 2.0;\n\n"
|
||||
" times[0] = 0.0;\n"
|
||||
" times[1] = 0.5;\n"
|
||||
" times[2] = 1.0;\n"
|
||||
"};\n"
|
||||
"@endtsexample\n"
|
||||
|
||||
"@ingroup FX\n"
|
||||
"@see ParticleEmitter\n"
|
||||
"@see ParticleEmitterData\n"
|
||||
"@see ParticleEmitterNode\n"
|
||||
);
|
||||
|
||||
static const float sgDefaultWindCoefficient = 0.0f;
|
||||
static const float sgDefaultConstantAcceleration = 0.f;
|
||||
static const float sgDefaultSpinSpeed = 1.f;
|
||||
static const float sgDefaultSpinRandomMin = 0.f;
|
||||
static const float sgDefaultSpinRandomMax = 0.f;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
ParticleData::ParticleData()
|
||||
{
|
||||
dragCoefficient = 0.0f;
|
||||
windCoefficient = sgDefaultWindCoefficient;
|
||||
gravityCoefficient = 0.0f;
|
||||
inheritedVelFactor = 0.0f;
|
||||
constantAcceleration = sgDefaultConstantAcceleration;
|
||||
lifetimeMS = 1000;
|
||||
lifetimeVarianceMS = 0;
|
||||
spinSpeed = sgDefaultSpinSpeed;
|
||||
spinRandomMin = sgDefaultSpinRandomMin;
|
||||
spinRandomMax = sgDefaultSpinRandomMax;
|
||||
useInvAlpha = false;
|
||||
animateTexture = false;
|
||||
|
||||
numFrames = 1;
|
||||
framesPerSec = numFrames;
|
||||
|
||||
S32 i;
|
||||
for( i=0; i<PDC_NUM_KEYS; i++ )
|
||||
{
|
||||
colors[i].set( 1.0, 1.0, 1.0, 1.0 );
|
||||
sizes[i] = 1.0;
|
||||
}
|
||||
|
||||
times[0] = 0.0f;
|
||||
times[1] = 0.33f;
|
||||
times[2] = 0.66f;
|
||||
times[3] = 1.0f;
|
||||
|
||||
texCoords[0].set(0.0,0.0); // texture coords at 4 corners
|
||||
texCoords[1].set(0.0,1.0); // of particle quad
|
||||
texCoords[2].set(1.0,1.0); // (defaults to entire particle)
|
||||
texCoords[3].set(1.0,0.0);
|
||||
animTexTiling.set(0,0); // tiling dimensions
|
||||
animTexFramesString = NULL; // string of animation frame indices
|
||||
animTexUVs = NULL; // array of tile vertex UVs
|
||||
textureName = NULL; // texture filename
|
||||
textureHandle = NULL; // loaded texture handle
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
ParticleData::~ParticleData()
|
||||
{
|
||||
if (animTexUVs)
|
||||
{
|
||||
delete [] animTexUVs;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// initPersistFields
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleData::initPersistFields()
|
||||
{
|
||||
addField( "dragCoefficient", TYPEID< F32 >(), Offset(dragCoefficient, ParticleData),
|
||||
"Particle physics drag amount." );
|
||||
addField( "windCoefficient", TYPEID< F32 >(), Offset(windCoefficient, ParticleData),
|
||||
"Strength of wind on the particles." );
|
||||
addField( "gravityCoefficient", TYPEID< F32 >(), Offset(gravityCoefficient, ParticleData),
|
||||
"Strength of gravity on the particles." );
|
||||
addField( "inheritedVelFactor", TYPEID< F32 >(), Offset(inheritedVelFactor, ParticleData),
|
||||
"Amount of emitter velocity to add to particle initial velocity." );
|
||||
addField( "constantAcceleration", TYPEID< F32 >(), Offset(constantAcceleration, ParticleData),
|
||||
"Constant acceleration to apply to this particle." );
|
||||
addField( "lifetimeMS", TYPEID< S32 >(), Offset(lifetimeMS, ParticleData),
|
||||
"Time in milliseconds before this particle is destroyed." );
|
||||
addField( "lifetimeVarianceMS", TYPEID< S32 >(), Offset(lifetimeVarianceMS, ParticleData),
|
||||
"Variance in lifetime of particle, from 0 - lifetimeMS." );
|
||||
addField( "spinSpeed", TYPEID< F32 >(), Offset(spinSpeed, ParticleData),
|
||||
"Speed at which to spin the particle." );
|
||||
addField( "spinRandomMin", TYPEID< F32 >(), Offset(spinRandomMin, ParticleData),
|
||||
"Minimum allowed spin speed of this particle, between -10000 and spinRandomMax." );
|
||||
addField( "spinRandomMax", TYPEID< F32 >(), Offset(spinRandomMax, ParticleData),
|
||||
"Maximum allowed spin speed of this particle, between spinRandomMin and 10000." );
|
||||
addField( "useInvAlpha", TYPEID< bool >(), Offset(useInvAlpha, ParticleData),
|
||||
"@brief Controls how particles blend with the scene.\n\n"
|
||||
"If true, particles blend like ParticleBlendStyle NORMAL, if false, "
|
||||
"blend like ParticleBlendStyle ADDITIVE.\n"
|
||||
"@note If ParticleEmitterData::blendStyle is set, it will override this value." );
|
||||
addField( "animateTexture", TYPEID< bool >(), Offset(animateTexture, ParticleData),
|
||||
"If true, allow the particle texture to be an animated sprite." );
|
||||
addField( "framesPerSec", TYPEID< S32 >(), Offset(framesPerSec, ParticleData),
|
||||
"If animateTexture is true, this defines the frames per second of the "
|
||||
"sprite animation." );
|
||||
|
||||
addField( "textureCoords", TYPEID< Point2F >(), Offset(texCoords, ParticleData), 4,
|
||||
"@brief 4 element array defining the UV coords into textureName to use "
|
||||
"for this particle.\n\n"
|
||||
"Coords should be set for the first tile only when using animTexTiling; "
|
||||
"coordinates for other tiles will be calculated automatically. \"0 0\" is "
|
||||
"top left and \"1 1\" is bottom right." );
|
||||
addField( "animTexTiling", TYPEID< Point2I >(), Offset(animTexTiling, ParticleData),
|
||||
"@brief The number of frames, in rows and columns stored in textureName "
|
||||
"(when animateTexture is true).\n\n"
|
||||
"A maximum of 256 frames can be stored in a single texture when using "
|
||||
"animTexTiling. Value should be \"NumColumns NumRows\", for example \"4 4\"." );
|
||||
addField( "animTexFrames", TYPEID< StringTableEntry >(), Offset(animTexFramesString,ParticleData),
|
||||
"@brief A list of frames and/or frame ranges to use for particle "
|
||||
"animation if animateTexture is true.\n\n"
|
||||
"Each frame token must be separated by whitespace. A frame token must be "
|
||||
"a positive integer frame number or a range of frame numbers separated "
|
||||
"with a '-'. The range separator, '-', cannot have any whitspace around "
|
||||
"it.\n\n"
|
||||
"Ranges can be specified to move through the frames in reverse as well "
|
||||
"as forward (eg. 19-14). Frame numbers exceeding the number of tiles will "
|
||||
"wrap.\n"
|
||||
"@tsexample\n"
|
||||
"animTexFrames = \"0-16 20 19 18 17 31-21\";\n"
|
||||
"@endtsexample\n" );
|
||||
|
||||
addField( "textureName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
|
||||
"Texture file to use for this particle." );
|
||||
addField( "animTexName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
|
||||
"@brief Texture file to use for this particle if animateTexture is true.\n\n"
|
||||
"Deprecated. Use textureName instead." );
|
||||
|
||||
// Interpolation variables
|
||||
addField( "colors", TYPEID< ColorF >(), Offset(colors, ParticleData), PDC_NUM_KEYS,
|
||||
"@brief Particle RGBA color keyframe values.\n\n"
|
||||
"The particle color will linearly interpolate between the color/time keys "
|
||||
"over the lifetime of the particle." );
|
||||
addField( "sizes", TYPEID< F32 >(), Offset(sizes, ParticleData), PDC_NUM_KEYS,
|
||||
"@brief Particle size keyframe values.\n\n"
|
||||
"The particle size will linearly interpolate between the size/time keys "
|
||||
"over the lifetime of the particle." );
|
||||
addProtectedField( "times", TYPEID< F32 >(), Offset(times, ParticleData), &protectedSetTimes,
|
||||
&defaultProtectedGetFn, PDC_NUM_KEYS,
|
||||
"@brief Time keys used with the colors and sizes keyframes.\n\n"
|
||||
"Values are from 0.0 (particle creation) to 1.0 (end of lifespace)." );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Pack data
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
stream->writeFloat(dragCoefficient / 5, 10);
|
||||
if( stream->writeFlag(windCoefficient != sgDefaultWindCoefficient ) )
|
||||
stream->write(windCoefficient);
|
||||
if (stream->writeFlag(gravityCoefficient != 0.0f))
|
||||
stream->writeSignedFloat(gravityCoefficient / 10, 12);
|
||||
stream->writeFloat(inheritedVelFactor, 9);
|
||||
if( stream->writeFlag( constantAcceleration != sgDefaultConstantAcceleration ) )
|
||||
stream->write(constantAcceleration);
|
||||
|
||||
stream->write( lifetimeMS );
|
||||
stream->write( lifetimeVarianceMS );
|
||||
|
||||
if( stream->writeFlag( spinSpeed != sgDefaultSpinSpeed ) )
|
||||
stream->write(spinSpeed);
|
||||
if(stream->writeFlag(spinRandomMin != sgDefaultSpinRandomMin || spinRandomMax != sgDefaultSpinRandomMax))
|
||||
{
|
||||
stream->writeInt((S32)(spinRandomMin + 1000), 11);
|
||||
stream->writeInt((S32)(spinRandomMax + 1000), 11);
|
||||
}
|
||||
stream->writeFlag(useInvAlpha);
|
||||
|
||||
S32 i, count;
|
||||
|
||||
// see how many frames there are:
|
||||
for(count = 0; count < 3; count++)
|
||||
if(times[count] >= 1)
|
||||
break;
|
||||
|
||||
count++;
|
||||
|
||||
stream->writeInt(count-1, 2);
|
||||
|
||||
for( i=0; i<count; i++ )
|
||||
{
|
||||
stream->writeFloat( colors[i].red, 7);
|
||||
stream->writeFloat( colors[i].green, 7);
|
||||
stream->writeFloat( colors[i].blue, 7);
|
||||
stream->writeFloat( colors[i].alpha, 7);
|
||||
stream->writeFloat( sizes[i]/MaxParticleSize, 14);
|
||||
stream->writeFloat( times[i], 8);
|
||||
}
|
||||
|
||||
if (stream->writeFlag(textureName && textureName[0]))
|
||||
stream->writeString(textureName);
|
||||
for (i = 0; i < 4; i++)
|
||||
mathWrite(*stream, texCoords[i]);
|
||||
if (stream->writeFlag(animateTexture))
|
||||
{
|
||||
if (stream->writeFlag(animTexFramesString && animTexFramesString[0]))
|
||||
{
|
||||
stream->writeString(animTexFramesString);
|
||||
}
|
||||
mathWrite(*stream, animTexTiling);
|
||||
stream->writeInt(framesPerSec, 8);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unpack data
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
dragCoefficient = stream->readFloat(10) * 5;
|
||||
if(stream->readFlag())
|
||||
stream->read(&windCoefficient);
|
||||
else
|
||||
windCoefficient = sgDefaultWindCoefficient;
|
||||
if (stream->readFlag())
|
||||
gravityCoefficient = stream->readSignedFloat(12)*10;
|
||||
else
|
||||
gravityCoefficient = 0.0f;
|
||||
inheritedVelFactor = stream->readFloat(9);
|
||||
if(stream->readFlag())
|
||||
stream->read(&constantAcceleration);
|
||||
else
|
||||
constantAcceleration = sgDefaultConstantAcceleration;
|
||||
|
||||
stream->read( &lifetimeMS );
|
||||
stream->read( &lifetimeVarianceMS );
|
||||
|
||||
if(stream->readFlag())
|
||||
stream->read(&spinSpeed);
|
||||
else
|
||||
spinSpeed = sgDefaultSpinSpeed;
|
||||
|
||||
if(stream->readFlag())
|
||||
{
|
||||
spinRandomMin = (F32)(stream->readInt(11) - 1000);
|
||||
spinRandomMax = (F32)(stream->readInt(11) - 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
spinRandomMin = sgDefaultSpinRandomMin;
|
||||
spinRandomMax = sgDefaultSpinRandomMax;
|
||||
}
|
||||
|
||||
useInvAlpha = stream->readFlag();
|
||||
|
||||
S32 i;
|
||||
S32 count = stream->readInt(2) + 1;
|
||||
for(i = 0;i < count; i++)
|
||||
{
|
||||
colors[i].red = stream->readFloat(7);
|
||||
colors[i].green = stream->readFloat(7);
|
||||
colors[i].blue = stream->readFloat(7);
|
||||
colors[i].alpha = stream->readFloat(7);
|
||||
sizes[i] = stream->readFloat(14) * MaxParticleSize;
|
||||
times[i] = stream->readFloat(8);
|
||||
}
|
||||
textureName = (stream->readFlag()) ? stream->readSTString() : 0;
|
||||
for (i = 0; i < 4; i++)
|
||||
mathRead(*stream, &texCoords[i]);
|
||||
|
||||
animateTexture = stream->readFlag();
|
||||
if (animateTexture)
|
||||
{
|
||||
animTexFramesString = (stream->readFlag()) ? stream->readSTString() : 0;
|
||||
mathRead(*stream, &animTexTiling);
|
||||
framesPerSec = stream->readInt(8);
|
||||
}
|
||||
}
|
||||
|
||||
bool ParticleData::protectedSetTimes( void *object, const char *index, const char *data)
|
||||
{
|
||||
ParticleData *pData = static_cast<ParticleData*>( object );
|
||||
F32 val = dAtof(data);
|
||||
U32 i;
|
||||
|
||||
if (!index)
|
||||
i = 0;
|
||||
else
|
||||
i = dAtoui(index);
|
||||
|
||||
pData->times[i] = mClampF( val, 0.f, 1.f );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// onAdd
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ParticleData::onAdd()
|
||||
{
|
||||
if (Parent::onAdd() == false)
|
||||
return false;
|
||||
|
||||
if (dragCoefficient < 0.0) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) drag coeff less than 0", getName());
|
||||
dragCoefficient = 0.0f;
|
||||
}
|
||||
if (lifetimeMS < 1) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetime < 1 ms", getName());
|
||||
lifetimeMS = 1;
|
||||
}
|
||||
if (lifetimeVarianceMS >= lifetimeMS) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetimeVariance >= lifetime", getName());
|
||||
lifetimeVarianceMS = lifetimeMS - 1;
|
||||
}
|
||||
if (spinSpeed > 10000.0 || spinSpeed < -10000.0) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinSpeed invalid", getName());
|
||||
return false;
|
||||
}
|
||||
if (spinRandomMin > 10000.0 || spinRandomMin < -10000.0) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin invalid", getName());
|
||||
spinRandomMin = -360.0;
|
||||
return false;
|
||||
}
|
||||
if (spinRandomMin > spinRandomMax) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin greater than spinRandomMax", getName());
|
||||
spinRandomMin = spinRandomMax - (spinRandomMin - spinRandomMax );
|
||||
return false;
|
||||
}
|
||||
if (spinRandomMax > 10000.0 || spinRandomMax < -10000.0) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMax invalid", getName());
|
||||
spinRandomMax = 360.0;
|
||||
return false;
|
||||
}
|
||||
if (framesPerSec > 255)
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) framesPerSec > 255, too high", getName());
|
||||
framesPerSec = 255;
|
||||
return false;
|
||||
}
|
||||
|
||||
times[0] = 0.0f;
|
||||
for (U32 i = 1; i < 4; i++) {
|
||||
if (times[i] < times[i-1]) {
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) times[%d] < times[%d]", getName(), i, i-1);
|
||||
times[i] = times[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
// Here we validate parameters
|
||||
if (animateTexture)
|
||||
{
|
||||
// Tiling dimensions must be positive and non-zero
|
||||
if (animTexTiling.x <= 0 || animTexTiling.y <= 0)
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General,
|
||||
"ParticleData(%s) bad value(s) for animTexTiling [%d or %d <= 0], invalid datablock",
|
||||
animTexTiling.x, animTexTiling.y, getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Indices must fit into a byte so these are also bad
|
||||
if (animTexTiling.x * animTexTiling.y > 256)
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General,
|
||||
"ParticleData(%s) bad values for animTexTiling [%d*%d > %d], invalid datablock",
|
||||
animTexTiling.x, animTexTiling.y, 256, getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// A list of frames is required
|
||||
if (!animTexFramesString || !animTexFramesString[0])
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) no animTexFrames, invalid datablock", getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// The frame list cannot be too long.
|
||||
if (animTexFramesString && dStrlen(animTexFramesString) > 255)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "ParticleData(%s) animTexFrames string too long [> 255 chars]", getName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// preload
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ParticleData::preload(bool server, String &errorStr)
|
||||
{
|
||||
if (Parent::preload(server, errorStr) == false)
|
||||
return false;
|
||||
|
||||
bool error = false;
|
||||
if(!server)
|
||||
{
|
||||
// Here we attempt to load the particle's texture if specified. An undefined
|
||||
// texture is *not* an error since the emitter may provide one.
|
||||
if (textureName && textureName[0])
|
||||
{
|
||||
textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
|
||||
if (!textureHandle)
|
||||
{
|
||||
errorStr = String::ToString("Missing particle texture: %s", textureName);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (animateTexture)
|
||||
{
|
||||
// Here we parse animTexFramesString into byte-size frame numbers in animTexFrames.
|
||||
// Each frame token must be separated by whitespace.
|
||||
// A frame token must be a positive integer frame number or a range of frame numbers
|
||||
// separated with a '-'.
|
||||
// The range separator, '-', cannot have any whitspace around it.
|
||||
// Ranges can be specified to move through the frames in reverse as well as forward.
|
||||
// Frame numbers exceeding the number of tiles will wrap.
|
||||
// example:
|
||||
// "0-16 20 19 18 17 31-21"
|
||||
|
||||
S32 n_tiles = animTexTiling.x * animTexTiling.y;
|
||||
AssertFatal(n_tiles > 0 && n_tiles <= 256, "Error, bad animTexTiling setting." );
|
||||
|
||||
animTexFrames.clear();
|
||||
|
||||
char* tokCopy = new char[dStrlen(animTexFramesString) + 1];
|
||||
dStrcpy(tokCopy, animTexFramesString);
|
||||
|
||||
char* currTok = dStrtok(tokCopy, " \t");
|
||||
while (currTok != NULL)
|
||||
{
|
||||
char* minus = dStrchr(currTok, '-');
|
||||
if (minus)
|
||||
{
|
||||
// add a range of frames
|
||||
*minus = '\0';
|
||||
S32 range_a = dAtoi(currTok);
|
||||
S32 range_b = dAtoi(minus+1);
|
||||
if (range_b < range_a)
|
||||
{
|
||||
// reverse frame range
|
||||
for (S32 i = range_a; i >= range_b; i--)
|
||||
animTexFrames.push_back((U8)(i % n_tiles));
|
||||
}
|
||||
else
|
||||
{
|
||||
// forward frame range
|
||||
for (S32 i = range_a; i <= range_b; i++)
|
||||
animTexFrames.push_back((U8)(i % n_tiles));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add one frame
|
||||
animTexFrames.push_back((U8)(dAtoi(currTok) % n_tiles));
|
||||
}
|
||||
currTok = dStrtok(NULL, " \t");
|
||||
}
|
||||
|
||||
// Here we pre-calculate the UVs for each frame tile, which are
|
||||
// tiled inside the UV region specified by texCoords. Since the
|
||||
// UVs are calculated using bilinear interpolation, the texCoords
|
||||
// region does *not* have to be an axis-aligned rectangle.
|
||||
|
||||
if (animTexUVs)
|
||||
delete [] animTexUVs;
|
||||
|
||||
animTexUVs = new Point2F[(animTexTiling.x+1)*(animTexTiling.y+1)];
|
||||
|
||||
// interpolate points on the left and right edge of the uv quadrangle
|
||||
Point2F lf_pt = texCoords[0];
|
||||
Point2F rt_pt = texCoords[3];
|
||||
|
||||
// per-row delta for left and right interpolated points
|
||||
Point2F lf_d = (texCoords[1] - texCoords[0])/(F32)animTexTiling.y;
|
||||
Point2F rt_d = (texCoords[2] - texCoords[3])/(F32)animTexTiling.y;
|
||||
|
||||
S32 idx = 0;
|
||||
for (S32 yy = 0; yy <= animTexTiling.y; yy++)
|
||||
{
|
||||
Point2F p = lf_pt;
|
||||
Point2F dp = (rt_pt - lf_pt)/(F32)animTexTiling.x;
|
||||
for (S32 xx = 0; xx <= animTexTiling.x; xx++)
|
||||
{
|
||||
animTexUVs[idx++] = p;
|
||||
p += dp;
|
||||
}
|
||||
lf_pt += lf_d;
|
||||
rt_pt += rt_d;
|
||||
}
|
||||
|
||||
// cleanup
|
||||
delete [] tokCopy;
|
||||
numFrames = animTexFrames.size();
|
||||
}
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Initialize particle
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelocity)
|
||||
{
|
||||
init->dataBlock = this;
|
||||
|
||||
// Calculate the constant accleration...
|
||||
init->vel += inheritVelocity * inheritedVelFactor;
|
||||
init->acc = init->vel * constantAcceleration;
|
||||
|
||||
// Calculate this instance's lifetime...
|
||||
init->totalLifetime = lifetimeMS;
|
||||
if (lifetimeVarianceMS != 0)
|
||||
init->totalLifetime += S32(gRandGen.randI() % (2 * lifetimeVarianceMS + 1)) - S32(lifetimeVarianceMS);
|
||||
|
||||
// assign spin amount
|
||||
init->spinSpeed = spinSpeed * gRandGen.randF( spinRandomMin, spinRandomMax );
|
||||
}
|
||||
|
||||
bool ParticleData::reload(char errorBuffer[256])
|
||||
{
|
||||
bool error = false;
|
||||
if (textureName && textureName[0])
|
||||
{
|
||||
textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
|
||||
if (!textureHandle)
|
||||
{
|
||||
dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureName);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
numFrames = 0;
|
||||
for( int i=0; i<PDC_MAX_TEX; i++ )
|
||||
{
|
||||
if( textureNameList[i] && textureNameList[i][0] )
|
||||
{
|
||||
textureList[i] = TextureHandle( textureNameList[i], MeshTexture );
|
||||
if (!textureList[i].getName())
|
||||
{
|
||||
dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureNameList[i]);
|
||||
error = true;
|
||||
}
|
||||
numFrames++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
return !error;
|
||||
}
|
||||
|
||||
DefineEngineMethod(ParticleData, reload, void, (),,
|
||||
"Reloads this particle.\n"
|
||||
"@tsexample\n"
|
||||
"// Get the editor's current particle\n"
|
||||
"%particle = PE_ParticleEditor.currParticle\n\n"
|
||||
"// Change a particle value\n"
|
||||
"%particle.setFieldValue( %propertyField, %value );\n\n"
|
||||
"// Reload it\n"
|
||||
"%particle.reload();\n"
|
||||
"@endtsexample\n" )
|
||||
{
|
||||
char errorBuffer[256];
|
||||
object->reload(errorBuffer);
|
||||
}
|
||||
128
Engine/source/T3D/fx/particle.h
Normal file
128
Engine/source/T3D/fx/particle.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PARTICLE_H_
|
||||
#define _PARTICLE_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
#ifndef _GFXTEXTUREHANDLE_H_
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
#endif
|
||||
|
||||
#define MaxParticleSize 50.0
|
||||
|
||||
struct Particle;
|
||||
|
||||
//*****************************************************************************
|
||||
// Particle Data
|
||||
//*****************************************************************************
|
||||
class ParticleData : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
enum PDConst
|
||||
{
|
||||
PDC_NUM_KEYS = 4,
|
||||
};
|
||||
|
||||
F32 dragCoefficient;
|
||||
F32 windCoefficient;
|
||||
F32 gravityCoefficient;
|
||||
|
||||
F32 inheritedVelFactor;
|
||||
F32 constantAcceleration;
|
||||
|
||||
S32 lifetimeMS;
|
||||
S32 lifetimeVarianceMS;
|
||||
|
||||
F32 spinSpeed; // degrees per second
|
||||
F32 spinRandomMin;
|
||||
F32 spinRandomMax;
|
||||
|
||||
bool useInvAlpha;
|
||||
|
||||
bool animateTexture;
|
||||
U32 numFrames;
|
||||
U32 framesPerSec;
|
||||
|
||||
ColorF colors[ PDC_NUM_KEYS ];
|
||||
F32 sizes[ PDC_NUM_KEYS ];
|
||||
F32 times[ PDC_NUM_KEYS ];
|
||||
|
||||
Point2F* animTexUVs;
|
||||
Point2F texCoords[4]; // default: {{0.0,0.0}, {0.0,1.0}, {1.0,1.0}, {1.0,0.0}}
|
||||
Point2I animTexTiling;
|
||||
StringTableEntry animTexFramesString;
|
||||
Vector<U8> animTexFrames;
|
||||
StringTableEntry textureName;
|
||||
GFXTexHandle textureHandle;
|
||||
|
||||
static bool protectedSetTimes( void *object, const char *index, const char *data );
|
||||
|
||||
public:
|
||||
ParticleData();
|
||||
~ParticleData();
|
||||
|
||||
// move this procedure to Particle
|
||||
void initializeParticle(Particle*, const Point3F&);
|
||||
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
bool onAdd();
|
||||
bool preload(bool server, String &errorStr);
|
||||
DECLARE_CONOBJECT(ParticleData);
|
||||
static void initPersistFields();
|
||||
|
||||
bool reload(char errorBuffer[256]);
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// Particle
|
||||
//
|
||||
// This structure should be as small as possible.
|
||||
//*****************************************************************************
|
||||
struct Particle
|
||||
{
|
||||
Point3F pos; // current instantaneous position
|
||||
Point3F vel; // " " velocity
|
||||
Point3F acc; // Constant acceleration
|
||||
Point3F orientDir; // direction particle should go if using oriented particles
|
||||
|
||||
U32 totalLifetime; // Total ms that this instance should be "live"
|
||||
ParticleData* dataBlock; // datablock that contains global parameters for
|
||||
// this instance
|
||||
U32 currentAge;
|
||||
|
||||
|
||||
// are these necessary to store here? - they are interpolated in real time
|
||||
ColorF color;
|
||||
F32 size;
|
||||
|
||||
F32 spinSpeed;
|
||||
Particle * next;
|
||||
};
|
||||
|
||||
|
||||
#endif // _PARTICLE_H_
|
||||
2006
Engine/source/T3D/fx/particleEmitter.cpp
Normal file
2006
Engine/source/T3D/fx/particleEmitter.cpp
Normal file
File diff suppressed because it is too large
Load diff
287
Engine/source/T3D/fx/particleEmitter.h
Normal file
287
Engine/source/T3D/fx/particleEmitter.h
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _H_PARTICLE_EMITTER
|
||||
#define _H_PARTICLE_EMITTER
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _PARTICLE_H_
|
||||
#include "T3D/fx/particle.h"
|
||||
#endif
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
#include "gfx/D3D9/360/gfx360MemVertexBuffer.h"
|
||||
#endif
|
||||
|
||||
class RenderPassManager;
|
||||
class ParticleData;
|
||||
|
||||
//*****************************************************************************
|
||||
// Particle Emitter Data
|
||||
//*****************************************************************************
|
||||
class ParticleEmitterData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
static bool _setAlignDirection( void *object, const char *index, const char *data );
|
||||
|
||||
public:
|
||||
|
||||
ParticleEmitterData();
|
||||
DECLARE_CONOBJECT(ParticleEmitterData);
|
||||
static void initPersistFields();
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
bool preload(bool server, String &errorStr);
|
||||
bool onAdd();
|
||||
void allocPrimBuffer( S32 overrideSize = -1 );
|
||||
|
||||
public:
|
||||
S32 ejectionPeriodMS; ///< Time, in Milliseconds, 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
|
||||
|
||||
F32 softnessDistance; ///< For soft particles, the distance (in meters) where particles will be faded
|
||||
///< based on the difference in depth between the particle and the scene geometry.
|
||||
|
||||
/// A scalar value used to influence the effect
|
||||
/// of the ambient color on the particle.
|
||||
F32 ambientFactor;
|
||||
|
||||
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
|
||||
bool alignParticles; ///< Particles always face along a particular axis
|
||||
Point3F alignDirection; ///< The direction aligned particles should face
|
||||
|
||||
StringTableEntry particleString; ///< Used to load particle data directly from a string
|
||||
|
||||
Vector<ParticleData*> particleDataBlocks; ///< Particle Datablocks
|
||||
Vector<U32> dataBlockIds; ///< Datablock IDs (parellel array to particleDataBlocks)
|
||||
|
||||
U32 partListInitSize; /// initial size of particle list calc'd from datablock info
|
||||
|
||||
GFXPrimitiveBufferHandle primBuff;
|
||||
|
||||
S32 blendStyle; ///< Pre-define blend factor setting
|
||||
bool sortParticles; ///< Particles are sorted back-to-front
|
||||
bool reverseOrder; ///< reverses draw order
|
||||
StringTableEntry textureName; ///< Emitter texture file to override particle textures
|
||||
GFXTexHandle textureHandle; ///< Emitter texture handle from txrName
|
||||
bool highResOnly; ///< This particle system should not use the mixed-resolution particle rendering
|
||||
bool renderReflection; ///< Enables this emitter to render into reflection passes.
|
||||
|
||||
bool reload();
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// Particle Emitter
|
||||
//*****************************************************************************
|
||||
class ParticleEmitter : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
public:
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
typedef GFXVertexPCTT ParticleVertexType;
|
||||
#else
|
||||
typedef GFXVertexPCT ParticleVertexType;
|
||||
#endif
|
||||
|
||||
ParticleEmitter();
|
||||
~ParticleEmitter();
|
||||
|
||||
DECLARE_CONOBJECT(ParticleEmitter);
|
||||
|
||||
static Point3F mWindVelocity;
|
||||
static void setWindVelocity( const Point3F &vel ){ mWindVelocity = vel; }
|
||||
|
||||
ColorF getCollectiveColor();
|
||||
|
||||
/// 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, bool reload );
|
||||
|
||||
/// 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);
|
||||
/// @}
|
||||
|
||||
bool mDead;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
inline void setupBillboard( Particle *part,
|
||||
Point3F *basePts,
|
||||
const MatrixF &camView,
|
||||
const ColorF &ambientColor,
|
||||
ParticleVertexType *lVerts );
|
||||
|
||||
inline void setupOriented( Particle *part,
|
||||
const Point3F &camPos,
|
||||
const ColorF &ambientColor,
|
||||
ParticleVertexType *lVerts );
|
||||
|
||||
inline void setupAligned( const Particle *part,
|
||||
const ColorF &ambientColor,
|
||||
ParticleVertexType *lVerts );
|
||||
|
||||
/// Updates the bounding box for the particle system
|
||||
void updateBBox();
|
||||
|
||||
/// @}
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
void processTick(const Move *move);
|
||||
void advanceTime(F32 dt);
|
||||
|
||||
// Rendering
|
||||
protected:
|
||||
void prepRenderImage( SceneRenderState *state );
|
||||
void copyToVB( const Point3F &camPos, const ColorF &ambientColor );
|
||||
|
||||
// PEngine interface
|
||||
private:
|
||||
|
||||
void update( U32 ms );
|
||||
inline void updateKeyData( Particle *part );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/// Constant used to calculate particle
|
||||
/// rotation from spin and age.
|
||||
static const F32 AgedSpinToRadians;
|
||||
|
||||
ParticleEmitterData* mDataBlock;
|
||||
|
||||
U32 mInternalClock;
|
||||
|
||||
U32 mNextParticleTime;
|
||||
|
||||
Point3F mLastPosition;
|
||||
bool mHasLastPosition;
|
||||
MatrixF mBBObjToWorld;
|
||||
|
||||
bool mDeleteWhenEmpty;
|
||||
bool mDeleteOnTick;
|
||||
|
||||
S32 mLifetimeMS;
|
||||
S32 mElapsedTimeMS;
|
||||
|
||||
F32 sizes[ ParticleData::PDC_NUM_KEYS ];
|
||||
ColorF colors[ ParticleData::PDC_NUM_KEYS ];
|
||||
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
GFX360MemVertexBufferHandle<ParticleVertexType> mVertBuff;
|
||||
#else
|
||||
GFXVertexBufferHandle<ParticleVertexType> mVertBuff;
|
||||
#endif
|
||||
|
||||
// These members are for implementing a link-list of the active emitter
|
||||
// particles. Member part_store contains blocks of particles that can be
|
||||
// chained in a link-list. Usually the first part_store block is large
|
||||
// enough to contain all the particles but it can be expanded in emergency
|
||||
// circumstances.
|
||||
Vector <Particle*> part_store;
|
||||
Particle* part_freelist;
|
||||
Particle part_list_head;
|
||||
S32 n_part_capacity;
|
||||
S32 n_parts;
|
||||
S32 mCurBuffSize;
|
||||
|
||||
};
|
||||
|
||||
#endif // _H_PARTICLE_EMITTER
|
||||
|
||||
419
Engine/source/T3D/fx/particleEmitterNode.cpp
Normal file
419
Engine/source/T3D/fx/particleEmitterNode.cpp
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "particleEmitterNode.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "T3D/fx/particleEmitter.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(ParticleEmitterNodeData);
|
||||
IMPLEMENT_CO_NETOBJECT_V1(ParticleEmitterNode);
|
||||
|
||||
ConsoleDocClass( ParticleEmitterNodeData,
|
||||
"@brief Contains additional data to be associated with a ParticleEmitterNode."
|
||||
"@ingroup FX\n"
|
||||
);
|
||||
|
||||
ConsoleDocClass( ParticleEmitterNode,
|
||||
"@brief A particle emitter object that can be positioned in the world and "
|
||||
"dynamically enabled or disabled.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock ParticleEmitterNodeData( SimpleEmitterNodeData )\n"
|
||||
"{\n"
|
||||
" timeMultiple = 1.0;\n"
|
||||
"};\n\n"
|
||||
|
||||
"%emitter = new ParticleEmitterNode()\n"
|
||||
"{\n"
|
||||
" datablock = SimpleEmitterNodeData;\n"
|
||||
" active = true;\n"
|
||||
" emitter = FireEmitterData;\n"
|
||||
" velocity = 3.5;\n"
|
||||
"};\n\n"
|
||||
|
||||
"// Dynamically change emitter datablock\n"
|
||||
"%emitter.setEmitterDataBlock( DustEmitterData );\n"
|
||||
"@endtsexample\n"
|
||||
|
||||
"@note To change the emitter field dynamically (after the ParticleEmitterNode "
|
||||
"object has been created) you must use the setEmitterDataBlock() method or the "
|
||||
"change will not be replicated to other clients in the game.\n"
|
||||
"Similarly, use the setActive() method instead of changing the active field "
|
||||
"directly. When changing velocity, you need to toggle setActive() on and off "
|
||||
"to force the state change to be transmitted to other clients.\n\n"
|
||||
|
||||
"@ingroup FX\n"
|
||||
"@see ParticleEmitterNodeData\n"
|
||||
"@see ParticleEmitterData\n"
|
||||
);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ParticleEmitterNodeData
|
||||
//-----------------------------------------------------------------------------
|
||||
ParticleEmitterNodeData::ParticleEmitterNodeData()
|
||||
{
|
||||
timeMultiple = 1.0;
|
||||
}
|
||||
|
||||
ParticleEmitterNodeData::~ParticleEmitterNodeData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// initPersistFields
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNodeData::initPersistFields()
|
||||
{
|
||||
addField( "timeMultiple", TYPEID< F32 >(), Offset(timeMultiple, ParticleEmitterNodeData),
|
||||
"@brief Time multiplier for particle emitter nodes.\n\n"
|
||||
"Increasing timeMultiple is like running the emitter at a faster rate - single-shot "
|
||||
"emitters will complete in a shorter time, and continuous emitters will generate "
|
||||
"particles more quickly.\n\n"
|
||||
"Valid range is 0.01 - 100." );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// onAdd
|
||||
//-----------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// preload
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ParticleEmitterNodeData::preload(bool server, String &errorStr)
|
||||
{
|
||||
if( Parent::preload(server, errorStr) == false )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// packData
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNodeData::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
stream->write(timeMultiple);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// unpackData
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNodeData::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
stream->read(&timeMultiple);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ParticleEmitterNode
|
||||
//-----------------------------------------------------------------------------
|
||||
ParticleEmitterNode::ParticleEmitterNode()
|
||||
{
|
||||
// Todo: ScopeAlways?
|
||||
mNetFlags.set(Ghostable);
|
||||
mTypeMask |= EnvironmentObjectType;
|
||||
|
||||
mActive = true;
|
||||
|
||||
mDataBlock = NULL;
|
||||
mEmitterDatablock = NULL;
|
||||
mEmitterDatablockId = 0;
|
||||
mEmitter = NULL;
|
||||
mVelocity = 1.0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
ParticleEmitterNode::~ParticleEmitterNode()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// initPersistFields
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNode::initPersistFields()
|
||||
{
|
||||
addField( "active", TYPEID< bool >(), Offset(mActive,ParticleEmitterNode),
|
||||
"Controls whether particles are emitted from this node." );
|
||||
addField( "emitter", TYPEID< ParticleEmitterData >(), Offset(mEmitterDatablock, ParticleEmitterNode),
|
||||
"Datablock to use when emitting particles." );
|
||||
addField( "velocity", TYPEID< F32 >(), Offset(mVelocity, ParticleEmitterNode),
|
||||
"Velocity to use when emitting particles (in the direction of the "
|
||||
"ParticleEmitterNode object's up (Z) axis)." );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// onAdd
|
||||
//-----------------------------------------------------------------------------
|
||||
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( isClientObject() )
|
||||
{
|
||||
setEmitterDataBlock( mEmitterDatablock );
|
||||
}
|
||||
else
|
||||
{
|
||||
setMaskBits( StateMask | EmitterDBMask );
|
||||
}
|
||||
|
||||
mObjBox.minExtents.set(-0.5, -0.5, -0.5);
|
||||
mObjBox.maxExtents.set( 0.5, 0.5, 0.5);
|
||||
resetWorldBox();
|
||||
addToScene();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// onRemove
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNode::onRemove()
|
||||
{
|
||||
removeFromScene();
|
||||
if( isClientObject() )
|
||||
{
|
||||
if( mEmitter )
|
||||
{
|
||||
mEmitter->deleteWhenEmpty();
|
||||
mEmitter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// onNewDataBlock
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ParticleEmitterNode::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||
{
|
||||
mDataBlock = dynamic_cast<ParticleEmitterNodeData*>( dptr );
|
||||
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
|
||||
return false;
|
||||
|
||||
// Todo: Uncomment if this is a "leaf" class
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNode::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
setMaskBits(StateMask | EmitterDBMask);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// advanceTime
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNode::processTick(const Move* move)
|
||||
{
|
||||
Parent::processTick(move);
|
||||
|
||||
if ( isMounted() )
|
||||
{
|
||||
MatrixF mat;
|
||||
mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
|
||||
setTransform( mat );
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEmitterNode::advanceTime(F32 dt)
|
||||
{
|
||||
Parent::advanceTime(dt);
|
||||
|
||||
if(!mActive || mEmitter.isNull() || !mDataBlock)
|
||||
return;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// packUpdate
|
||||
//-----------------------------------------------------------------------------
|
||||
U32 ParticleEmitterNode::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
|
||||
if ( stream->writeFlag( mask & InitialUpdateMask ) )
|
||||
{
|
||||
mathWrite(*stream, getTransform());
|
||||
mathWrite(*stream, getScale());
|
||||
}
|
||||
|
||||
if ( stream->writeFlag( mask & EmitterDBMask ) )
|
||||
{
|
||||
if( stream->writeFlag(mEmitterDatablock != NULL) )
|
||||
{
|
||||
stream->writeRangedU32(mEmitterDatablock->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
}
|
||||
}
|
||||
|
||||
if ( stream->writeFlag( mask & StateMask ) )
|
||||
{
|
||||
stream->writeFlag( mActive );
|
||||
stream->write( mVelocity );
|
||||
}
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// unpackUpdate
|
||||
//-----------------------------------------------------------------------------
|
||||
void ParticleEmitterNode::unpackUpdate(NetConnection* con, BitStream* stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
|
||||
if ( stream->readFlag() )
|
||||
{
|
||||
MatrixF temp;
|
||||
Point3F tempScale;
|
||||
mathRead(*stream, &temp);
|
||||
mathRead(*stream, &tempScale);
|
||||
|
||||
setScale(tempScale);
|
||||
setTransform(temp);
|
||||
}
|
||||
|
||||
if ( stream->readFlag() )
|
||||
{
|
||||
mEmitterDatablockId = stream->readFlag() ?
|
||||
stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast) : 0;
|
||||
|
||||
ParticleEmitterData *emitterDB = NULL;
|
||||
Sim::findObject( mEmitterDatablockId, emitterDB );
|
||||
if ( isProperlyAdded() )
|
||||
setEmitterDataBlock( emitterDB );
|
||||
}
|
||||
|
||||
if ( stream->readFlag() )
|
||||
{
|
||||
mActive = stream->readFlag();
|
||||
stream->read( &mVelocity );
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEmitterNode::setEmitterDataBlock(ParticleEmitterData* data)
|
||||
{
|
||||
if ( isServerObject() )
|
||||
{
|
||||
setMaskBits( EmitterDBMask );
|
||||
}
|
||||
else
|
||||
{
|
||||
ParticleEmitter* pEmitter = NULL;
|
||||
if ( data )
|
||||
{
|
||||
// Create emitter with new datablock
|
||||
pEmitter = new ParticleEmitter;
|
||||
pEmitter->onNewDataBlock( data, false );
|
||||
if( pEmitter->registerObject() == false )
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", data->getName() ? data->getName() : data->getIdString() );
|
||||
delete pEmitter;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace emitter
|
||||
if ( mEmitter )
|
||||
mEmitter->deleteWhenEmpty();
|
||||
|
||||
mEmitter = pEmitter;
|
||||
}
|
||||
|
||||
mEmitterDatablock = data;
|
||||
}
|
||||
|
||||
DefineEngineMethod(ParticleEmitterNode, setEmitterDataBlock, void, (ParticleEmitterData* emitterDatablock), (0),
|
||||
"Assigns the datablock for this emitter node.\n"
|
||||
"@param emitterDatablock ParticleEmitterData datablock to assign\n"
|
||||
"@tsexample\n"
|
||||
"// Assign a new emitter datablock\n"
|
||||
"%emitter.setEmitterDatablock( %emitterDatablock );\n"
|
||||
"@endtsexample\n" )
|
||||
{
|
||||
if ( !emitterDatablock )
|
||||
{
|
||||
Con::errorf("ParticleEmitterData datablock could not be found when calling setEmitterDataBlock in particleEmitterNode.");
|
||||
return;
|
||||
}
|
||||
|
||||
object->setEmitterDataBlock(emitterDatablock);
|
||||
}
|
||||
|
||||
DefineEngineMethod(ParticleEmitterNode, setActive, void, (bool active),,
|
||||
"Turns the emitter on or off.\n"
|
||||
"@param active New emitter state\n" )
|
||||
{
|
||||
object->setActive( active );
|
||||
}
|
||||
118
Engine/source/T3D/fx/particleEmitterNode.h
Normal file
118
Engine/source/T3D/fx/particleEmitterNode.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PARTICLEEMITTERDUMMY_H_
|
||||
#define _PARTICLEEMITTERDUMMY_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
|
||||
class ParticleEmitterData;
|
||||
class ParticleEmitter;
|
||||
|
||||
//*****************************************************************************
|
||||
// ParticleEmitterNodeData
|
||||
//*****************************************************************************
|
||||
class ParticleEmitterNodeData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
|
||||
//-------------------------------------- Console set variables
|
||||
public:
|
||||
F32 timeMultiple;
|
||||
|
||||
//-------------------------------------- load set variables
|
||||
public:
|
||||
|
||||
public:
|
||||
ParticleEmitterNodeData();
|
||||
~ParticleEmitterNodeData();
|
||||
|
||||
void packData(BitStream*);
|
||||
void unpackData(BitStream*);
|
||||
bool preload(bool server, String &errorStr);
|
||||
|
||||
DECLARE_CONOBJECT(ParticleEmitterNodeData);
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
|
||||
//*****************************************************************************
|
||||
// ParticleEmitterNode
|
||||
//*****************************************************************************
|
||||
class ParticleEmitterNode : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
enum MaskBits
|
||||
{
|
||||
StateMask = Parent::NextFreeMask << 0,
|
||||
EmitterDBMask = Parent::NextFreeMask << 1,
|
||||
NextFreeMask = Parent::NextFreeMask << 2,
|
||||
};
|
||||
|
||||
private:
|
||||
ParticleEmitterNodeData* mDataBlock;
|
||||
|
||||
protected:
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock( GameBaseData *dptr, bool reload );
|
||||
void inspectPostApply();
|
||||
|
||||
ParticleEmitterData* mEmitterDatablock;
|
||||
S32 mEmitterDatablockId;
|
||||
|
||||
bool mActive;
|
||||
|
||||
SimObjectPtr<ParticleEmitter> mEmitter;
|
||||
F32 mVelocity;
|
||||
|
||||
public:
|
||||
ParticleEmitterNode();
|
||||
~ParticleEmitterNode();
|
||||
|
||||
ParticleEmitter *getParticleEmitter() {return mEmitter;}
|
||||
|
||||
// Time/Move Management
|
||||
public:
|
||||
void processTick(const Move* move);
|
||||
void advanceTime(F32 dt);
|
||||
|
||||
DECLARE_CONOBJECT(ParticleEmitterNode);
|
||||
static void initPersistFields();
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream* stream);
|
||||
|
||||
inline bool getActive( void ) { return mActive; };
|
||||
inline void setActive( bool active ) { mActive = active; setMaskBits( StateMask ); };
|
||||
|
||||
void setEmitterDataBlock(ParticleEmitterData* data);
|
||||
};
|
||||
|
||||
#endif // _H_PARTICLEEMISSIONDUMMY
|
||||
|
||||
1867
Engine/source/T3D/fx/precipitation.cpp
Normal file
1867
Engine/source/T3D/fx/precipitation.cpp
Normal file
File diff suppressed because it is too large
Load diff
287
Engine/source/T3D/fx/precipitation.h
Normal file
287
Engine/source/T3D/fx/precipitation.h
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PRECIPITATION_H_
|
||||
#define _PRECIPITATION_H_
|
||||
|
||||
#include "gfx/gfxDevice.h"
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
#ifndef _RENDERPASSMANAGER_H_
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#endif
|
||||
|
||||
class SFXTrack;
|
||||
class SFXSource;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Precipitation datablock.
|
||||
class PrecipitationData : public GameBaseData
|
||||
{
|
||||
typedef GameBaseData Parent;
|
||||
|
||||
public:
|
||||
SFXTrack* soundProfile;
|
||||
|
||||
StringTableEntry mDropName; ///< Texture filename for drop particles
|
||||
StringTableEntry mDropShaderName; ///< The name of the shader used for raindrops
|
||||
StringTableEntry mSplashName; ///< Texture filename for splash particles
|
||||
StringTableEntry mSplashShaderName; ///< The name of the shader used for raindrops
|
||||
|
||||
S32 mDropsPerSide; ///< How many drops are on a side of the raindrop texture.
|
||||
S32 mSplashesPerSide; ///< How many splash are on a side of the splash texture.
|
||||
|
||||
PrecipitationData();
|
||||
DECLARE_CONOBJECT(PrecipitationData);
|
||||
bool preload( bool server, String& errorStr );
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
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
|
||||
U32 hitType; ///< What kind of object the drop will hit
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
hitType = 0;
|
||||
hitPos = Point3F(0,0,0);
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class Precipitation : public GameBase
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef GameBase Parent;
|
||||
PrecipitationData* mDataBlock;
|
||||
|
||||
Raindrop *mDropHead; ///< Drop linked list head
|
||||
Raindrop *mSplashHead; ///< Splash linked list head
|
||||
|
||||
Point2F* mTexCoords; ///< texture coords for rain texture
|
||||
Point2F* mSplashCoords; ///< texture coordinates for splash texture
|
||||
|
||||
SFXSource* mAmbientSound; ///< Ambient sound
|
||||
|
||||
GFXShaderRef mDropShader; ///< The shader used for raindrops
|
||||
GFXTexHandle mDropHandle; ///< Texture handle for raindrop
|
||||
GFXShaderRef mSplashShader; ///< The shader used for splashes
|
||||
GFXTexHandle mSplashHandle; ///< Texture handle for splash
|
||||
|
||||
U32 mLastRenderFrame; ///< Used to skip processTick when we haven't been visible.
|
||||
|
||||
U32 mDropHitMask; ///< Stores the current drop hit mask.
|
||||
|
||||
//console exposed variables
|
||||
bool mFollowCam; ///< Does the system follow the camera or stay where it's placed.
|
||||
|
||||
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
|
||||
bool mAnimateSplashes; ///< Animate the splashes using the frames in the texture.
|
||||
|
||||
S32 mDropAnimateMS; ///< If greater than zero, will animate the drops from
|
||||
///< the frames in the texture
|
||||
|
||||
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 mUseLighting; ///< This enables shading of the drops and splashes
|
||||
///< by the sun color.
|
||||
|
||||
ColorF mGlowIntensity; ///< Set it to 0 to disable the glow or use it to control
|
||||
///< the intensity of each channel.
|
||||
|
||||
bool mReflect; ///< This enables the precipitation to be rendered
|
||||
///< during reflection passes. This is expensive.
|
||||
|
||||
bool mUseWind; ///< This enables the wind from the sky SceneObject
|
||||
///< to effect the velocitiy of the drops.
|
||||
|
||||
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
|
||||
bool mDropHitPlayers; ///< Should drops collide with players
|
||||
bool mDropHitVehicles; ///< Should drops collide with vehicles
|
||||
|
||||
F32 mFadeDistance; ///< The distance at which fading of the particles begins.
|
||||
F32 mFadeDistanceEnd; ///< The distance at which fading of the particles ends.
|
||||
|
||||
U32 mMaxVBDrops; ///< The maximum drops allowed in one render batch.
|
||||
|
||||
GFXStateBlockRef mDefaultSB;
|
||||
GFXStateBlockRef mDistantSB;
|
||||
|
||||
GFXShaderConstBufferRef mDropShaderConsts;
|
||||
|
||||
GFXShaderConstHandle* mDropShaderModelViewSC;
|
||||
GFXShaderConstHandle* mDropShaderFadeStartEndSC;
|
||||
GFXShaderConstHandle* mDropShaderCameraPosSC;
|
||||
GFXShaderConstHandle* mDropShaderAmbientSC;
|
||||
|
||||
GFXShaderConstBufferRef mSplashShaderConsts;
|
||||
|
||||
GFXShaderConstHandle* mSplashShaderModelViewSC;
|
||||
GFXShaderConstHandle* mSplashShaderFadeStartEndSC;
|
||||
GFXShaderConstHandle* mSplashShaderCameraPosSC;
|
||||
GFXShaderConstHandle* mSplashShaderAmbientSC;
|
||||
|
||||
struct
|
||||
{
|
||||
bool valid;
|
||||
U32 startTime;
|
||||
U32 totalTime;
|
||||
F32 startPct;
|
||||
F32 endPct;
|
||||
|
||||
} mStormData;
|
||||
|
||||
struct
|
||||
{
|
||||
bool valid;
|
||||
U32 startTime;
|
||||
U32 totalTime;
|
||||
F32 startMax;
|
||||
F32 startSpeed;
|
||||
F32 endMax;
|
||||
F32 endSpeed;
|
||||
|
||||
} mTurbulenceData;
|
||||
|
||||
//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 initRenderObjects(); ///< Re-inits the texture coord lookup tables
|
||||
void initMaterials(); ///< Re-inits the textures and shaders
|
||||
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, const Box3F &box, const VectorF &windVel); ///< Casts a ray to see if/when a drop will collide
|
||||
void wrapDrop(Raindrop *drop, const Box3F &box, const U32 currTime, const VectorF &windVel); ///< 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
|
||||
|
||||
GFXPrimitiveBufferHandle mRainIB;
|
||||
GFXVertexBufferHandle<GFXVertexPT> mRainVB;
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
// Rendering
|
||||
void prepRenderImage( SceneRenderState* state );
|
||||
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* );
|
||||
|
||||
void setTransform(const MatrixF &mat);
|
||||
|
||||
public:
|
||||
|
||||
Precipitation();
|
||||
~Precipitation();
|
||||
void inspectPostApply();
|
||||
|
||||
enum
|
||||
{
|
||||
DataMask = Parent::NextFreeMask << 0,
|
||||
PercentageMask = Parent::NextFreeMask << 1,
|
||||
StormMask = Parent::NextFreeMask << 2,
|
||||
TransformMask = Parent::NextFreeMask << 3,
|
||||
TurbulenceMask = Parent::NextFreeMask << 4,
|
||||
NextFreeMask = Parent::NextFreeMask << 5
|
||||
};
|
||||
|
||||
bool onNewDataBlock( GameBaseData *dptr, bool reload );
|
||||
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);
|
||||
|
||||
/// This is used to smoothly change the turbulence
|
||||
/// over a desired time period. Setting ms to zero
|
||||
/// will cause the change to be instantaneous. Setting
|
||||
/// max zero will disable turbulence.
|
||||
void setTurbulence(F32 max, F32 speed, U32 ms);
|
||||
};
|
||||
|
||||
#endif // PRECIPITATION_H_
|
||||
|
||||
698
Engine/source/T3D/fx/splash.cpp
Normal file
698
Engine/source/T3D/fx/splash.cpp
Normal file
|
|
@ -0,0 +1,698 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/fx/splash.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gfx/primBuilder.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "sfx/sfxSystem.h"
|
||||
#include "sfx/sfxProfile.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "T3D/fx/explosion.h"
|
||||
#include "T3D/fx/particle.h"
|
||||
#include "T3D/fx/particleEmitter.h"
|
||||
#include "T3D/fx/particleEmitterNode.h"
|
||||
#include "T3D/gameBase/gameProcess.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
MRandomLCG sgRandom(0xdeadbeef);
|
||||
|
||||
} // namespace {}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(SplashData);
|
||||
IMPLEMENT_CO_NETOBJECT_V1(Splash);
|
||||
|
||||
ConsoleDocClass( SplashData,
|
||||
"@brief Acts as the physical point in space in white a Splash is created from.\n"
|
||||
"@ingroup FX\n"
|
||||
);
|
||||
|
||||
ConsoleDocClass( Splash,
|
||||
"@brief Manages the ring used for a Splash effect.\n"
|
||||
"@ingroup FX\n"
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// 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 );
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Init fields
|
||||
//--------------------------------------------------------------------------
|
||||
void SplashData::initPersistFields()
|
||||
{
|
||||
addField("soundProfile", TYPEID< SFXProfile >(), Offset(soundProfile, SplashData), "SFXProfile effect to play.\n");
|
||||
addField("scale", TypePoint3F, Offset(scale, SplashData), "The scale of this splashing effect, defined as the F32 points X, Y, Z.\n");
|
||||
addField("emitter", TYPEID< ParticleEmitterData >(), Offset(emitterList, SplashData), NUM_EMITTERS, "List of particle emitters to create at the point of this Splash effect.\n");
|
||||
addField("delayMS", TypeS32, Offset(delayMS, SplashData), "Time to delay, in milliseconds, before actually starting this effect.\n");
|
||||
addField("delayVariance", TypeS32, Offset(delayVariance, SplashData), "Time variance for delayMS.\n");
|
||||
addField("lifetimeMS", TypeS32, Offset(lifetimeMS, SplashData), "Lifetime for this effect, in milliseconds.\n");
|
||||
addField("lifetimeVariance", TypeS32, Offset(lifetimeVariance, SplashData), "Time variance for lifetimeMS.\n");
|
||||
addField("width", TypeF32, Offset(width, SplashData), "Width for the X and Y coordinates to create this effect within.");
|
||||
addField("numSegments", TypeS32, Offset(numSegments, SplashData), "Number of ejection points in the splash ring.\n");
|
||||
addField("velocity", TypeF32, Offset(velocity, SplashData), "Velocity for the splash effect to travel.\n");
|
||||
addField("height", TypeF32, Offset(height, SplashData), "Height for the splash to reach.\n");
|
||||
addField("acceleration", TypeF32, Offset(acceleration, SplashData), "Constant acceleration value to place upon the splash effect.\n");
|
||||
addField("times", TypeF32, Offset(times, SplashData), NUM_TIME_KEYS, "Times to transition through the splash effect. Up to 4 allowed. Values are 0.0 - 1.0, and corrispond to the life of the particle where 0 is first created and 1 is end of lifespace.\n" );
|
||||
addField("colors", TypeColorF, Offset(colors, SplashData), NUM_TIME_KEYS, "Color values to set the splash effect, rgba. Up to 4 allowed. Will transition through colors based on values set in the times value. Example: colors[0] = \"0.6 1.0 1.0 0.5\".\n" );
|
||||
addField("texture", TypeFilename, Offset(textureName, SplashData), NUM_TEX, "Imagemap file to use as the texture for the splash effect.\n");
|
||||
addField("texWrap", TypeF32, Offset(texWrap, SplashData), "Amount to wrap the texture around the splash ring, 0.0f - 1.0f.\n");
|
||||
addField("texFactor", TypeF32, Offset(texFactor, SplashData), "Factor in which to apply the texture to the splash ring, 0.0f - 1.0f.\n");
|
||||
addField("ejectionFreq", TypeF32, Offset(ejectionFreq, SplashData), "Frequency in which to emit splash rings.\n");
|
||||
addField("ejectionAngle", TypeF32, Offset(ejectionAngle, SplashData), "Rotational angle to create a splash ring.\n");
|
||||
addField("ringLifetime", TypeF32, Offset(ringLifetime, SplashData), "Lifetime, in milliseconds, for a splash ring.\n");
|
||||
addField("startRadius", TypeF32, Offset(startRadius, SplashData), "Starting radius size of a splash ring.\n");
|
||||
addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, SplashData), "ExplosionData object to create at the creation position of this splash effect.\n");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// 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, String &errorStr)
|
||||
{
|
||||
if (Parent::preload(server, errorStr) == 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] = GFXTexHandle(textureName[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle[%d] (line %d)", __FUNCTION__, i, __LINE__) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
// Only allocated client side.
|
||||
mNetFlags.set( IsGhost );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
|
||||
{
|
||||
if( mDataBlock->emitterList[i] != NULL )
|
||||
{
|
||||
ParticleEmitter * pEmitter = new ParticleEmitter;
|
||||
pEmitter->onNewDataBlock( mDataBlock->emitterList[i], false );
|
||||
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.minExtents = Point3F( -1, -1, -1 );
|
||||
mObjBox.maxExtents = Point3F( 1, 1, 1 );
|
||||
resetWorldBox();
|
||||
|
||||
gClientSceneGraph->addObjectToScene(this);
|
||||
|
||||
removeFromProcessList();
|
||||
ClientProcessList::get()->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.clear();
|
||||
|
||||
getSceneManager()->removeObjectFromScene(this);
|
||||
getContainer()->removeObject(this);
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// On New Data Block
|
||||
//--------------------------------------------------------------------------
|
||||
bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||
{
|
||||
mDataBlock = dynamic_cast<SplashData*>(dptr);
|
||||
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
|
||||
return false;
|
||||
|
||||
scriptOnNewDataBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update color
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::updateColor()
|
||||
{
|
||||
for(SplashRingList::Iterator ring = ringList.begin(); ring != ringList.end(); ++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.pushBack( ring );
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Update rings
|
||||
//----------------------------------------------------------------------------
|
||||
void Splash::updateRings( F32 dt )
|
||||
{
|
||||
SplashRingList::Iterator ring;
|
||||
for(SplashRingList::Iterator i = ringList.begin(); i != ringList.end(); /*Trickiness*/)
|
||||
{
|
||||
ring = i++;
|
||||
ring->elapsedTime += dt;
|
||||
|
||||
if( !ring->isActive() )
|
||||
{
|
||||
ringList.erase( ring );
|
||||
}
|
||||
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.0f, 0.0f, -9.8f ) * 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, false);
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
195
Engine/source/T3D/fx/splash.h
Normal file
195
Engine/source/T3D/fx/splash.h
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _SPLASH_H_
|
||||
#define _SPLASH_H_
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TORQUE_LIST_
|
||||
#include "core/util/tList.h"
|
||||
#endif
|
||||
|
||||
#include "gfx/gfxTextureHandle.h"
|
||||
|
||||
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];
|
||||
GFXTexHandle textureHandle[NUM_TEX];
|
||||
|
||||
ExplosionData* explosion;
|
||||
S32 explosionId;
|
||||
|
||||
SplashData();
|
||||
DECLARE_CONOBJECT(SplashData);
|
||||
bool onAdd();
|
||||
bool preload(bool server, String &errorStr);
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Splash
|
||||
//--------------------------------------------------------------------------
|
||||
class Splash : public GameBase
|
||||
{
|
||||
typedef GameBase Parent;
|
||||
|
||||
private:
|
||||
SplashData* mDataBlock;
|
||||
|
||||
SimObjectPtr<ParticleEmitter> mEmitterList[ SplashData::NUM_EMITTERS ];
|
||||
|
||||
typedef Torque::List<SplashRing> SplashRingList;
|
||||
SplashRingList 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 spawnExplosion();
|
||||
|
||||
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, bool reload );
|
||||
DECLARE_CONOBJECT(Splash);
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_SPLASH
|
||||
136
Engine/source/T3D/fx/windEmitter.cpp
Normal file
136
Engine/source/T3D/fx/windEmitter.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/fx/windEmitter.h"
|
||||
|
||||
#include "math/mBox.h"
|
||||
#include "core/tAlgorithm.h"
|
||||
#include "platform/profiler.h"
|
||||
|
||||
|
||||
Vector<WindEmitter*> WindEmitter::smAllEmitters;
|
||||
|
||||
|
||||
WindEmitter::WindEmitter()
|
||||
{
|
||||
smAllEmitters.push_back( this );
|
||||
|
||||
mEnabled = true;
|
||||
mScore = 0.0f;
|
||||
mSphere.center.zero();
|
||||
mSphere.radius = 0.0f;
|
||||
mStrength = 0.0f;
|
||||
mTurbulenceFrequency = 0.0f;
|
||||
mTurbulenceStrength = 0.0f;
|
||||
mVelocity.zero();
|
||||
}
|
||||
|
||||
WindEmitter::~WindEmitter()
|
||||
{
|
||||
WindEmitterList::iterator iter = find( smAllEmitters.begin(), smAllEmitters.end(), this );
|
||||
smAllEmitters.erase( iter );
|
||||
}
|
||||
|
||||
void WindEmitter::setPosition( const Point3F& pos )
|
||||
{
|
||||
mSphere.center = pos;
|
||||
}
|
||||
|
||||
void WindEmitter::update( const Point3F& pos, const VectorF& velocity )
|
||||
{
|
||||
mSphere.center = pos;
|
||||
mVelocity = velocity;
|
||||
}
|
||||
|
||||
void WindEmitter::setRadius( F32 radius )
|
||||
{
|
||||
mSphere.radius = radius;
|
||||
}
|
||||
|
||||
void WindEmitter::setStrength( F32 strength )
|
||||
{
|
||||
mStrength = strength;
|
||||
}
|
||||
|
||||
void WindEmitter::setTurbulency( F32 frequency, F32 strength )
|
||||
{
|
||||
mTurbulenceFrequency = frequency;
|
||||
mTurbulenceStrength = strength;
|
||||
}
|
||||
|
||||
S32 QSORT_CALLBACK WindEmitter::_sortByScore(const void* a, const void* b)
|
||||
{
|
||||
return mSign((*(WindEmitter**)b)->mScore - (*(WindEmitter**)a)->mScore);
|
||||
}
|
||||
|
||||
bool WindEmitter::findBest( const Point3F& cameraPos,
|
||||
const VectorF& cameraDir,
|
||||
F32 viewDistance,
|
||||
U32 maxResults,
|
||||
WindEmitterList* results )
|
||||
{
|
||||
PROFILE_START(WindEmitter_findBest);
|
||||
|
||||
// Build a sphere from the camera point.
|
||||
SphereF cameraSphere;
|
||||
cameraSphere.center = cameraPos;
|
||||
cameraSphere.radius = viewDistance;
|
||||
|
||||
// Collect the active spheres within the camera space and score them.
|
||||
WindEmitterList best;
|
||||
WindEmitterList::iterator iter = smAllEmitters.begin();
|
||||
for ( ; iter != smAllEmitters.end(); iter++ )
|
||||
{
|
||||
const SphereF& sphere = *(*iter);
|
||||
|
||||
// Skip any spheres outside of our camera range or that are disabled.
|
||||
if ( !(*iter)->mEnabled || !cameraSphere.isIntersecting( sphere ) )
|
||||
continue;
|
||||
|
||||
// Simple score calculation...
|
||||
//
|
||||
// score = ( radius / distance to camera ) * dot( cameraDir, vector from camera to sphere )
|
||||
//
|
||||
Point3F vect = sphere.center - cameraSphere.center;
|
||||
F32 dist = vect.len();
|
||||
(*iter)->mScore = dist * sphere.radius;
|
||||
vect /= getMax( dist, 0.001f );
|
||||
(*iter)->mScore *= mDot( vect, cameraDir );
|
||||
|
||||
best.push_back( *iter );
|
||||
}
|
||||
|
||||
// Sort the results by score!
|
||||
dQsort( best.address(), best.size(), sizeof(WindEmitter*), &WindEmitter::_sortByScore );
|
||||
|
||||
// Clip the results to the max requested.
|
||||
if ( best.size() > maxResults )
|
||||
best.setSize( maxResults );
|
||||
|
||||
// Merge the results and return.
|
||||
results->merge( best );
|
||||
|
||||
PROFILE_END(); // WindEmitter_findBest
|
||||
|
||||
return best.size() > 0;
|
||||
}
|
||||
96
Engine/source/T3D/fx/windEmitter.h
Normal file
96
Engine/source/T3D/fx/windEmitter.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _WINDEMITTER_H_
|
||||
#define _WINDEMITTER_H_
|
||||
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
#ifndef _MSPHERE_H_
|
||||
#include "math/mSphere.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
class WindEmitter;
|
||||
|
||||
/// A vector of WindEmitter pointers.
|
||||
typedef Vector<WindEmitter*> WindEmitterList;
|
||||
|
||||
|
||||
class WindEmitter
|
||||
{
|
||||
public:
|
||||
WindEmitter();
|
||||
~WindEmitter();
|
||||
|
||||
operator const SphereF&() const { return mSphere; }
|
||||
|
||||
void update( const Point3F& pos, const VectorF& velocity );
|
||||
|
||||
void setPosition( const Point3F& pos );
|
||||
|
||||
void setRadius( F32 radius );
|
||||
|
||||
void setStrength( F32 strength );
|
||||
|
||||
void setTurbulency( F32 frequency, F32 strength );
|
||||
|
||||
const Point3F& getCenter() const { return mSphere.center; }
|
||||
|
||||
F32 getRadius() const { return mSphere.radius; }
|
||||
|
||||
F32 getStrength() const { return mStrength; }
|
||||
|
||||
F32 getTurbulenceFrequency() const { return mTurbulenceFrequency; }
|
||||
|
||||
F32 getTurbulenceStrength() const { return mTurbulenceStrength; }
|
||||
|
||||
const VectorF& getVelocity() const { return mVelocity; }
|
||||
|
||||
|
||||
static bool findBest( const Point3F& cameraPos,
|
||||
const VectorF& cameraDir,
|
||||
F32 viewDistance,
|
||||
U32 maxResults,
|
||||
WindEmitterList* results );
|
||||
|
||||
protected:
|
||||
SphereF mSphere;
|
||||
|
||||
VectorF mVelocity;
|
||||
|
||||
F32 mStrength;
|
||||
F32 mTurbulenceFrequency;
|
||||
F32 mTurbulenceStrength;
|
||||
F32 mScore;
|
||||
|
||||
bool mEnabled;
|
||||
|
||||
static WindEmitterList smAllEmitters;
|
||||
|
||||
static S32 QSORT_CALLBACK _sortByScore( const void* a, const void* b );
|
||||
};
|
||||
|
||||
#endif // _WINDEMITTER_H_
|
||||
703
Engine/source/T3D/gameBase/gameBase.cpp
Normal file
703
Engine/source/T3D/gameBase/gameBase.cpp
Normal file
|
|
@ -0,0 +1,703 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#include "T3D/gameBase/gameProcess.h"
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
#include "T3D/aiConnection.h"
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Ghost update relative priority values
|
||||
|
||||
static F32 sUpFov = 1.0;
|
||||
static F32 sUpDistance = 0.4f;
|
||||
static F32 sUpVelocity = 0.4f;
|
||||
static F32 sUpSkips = 0.2f;
|
||||
static F32 sUpInterest = 0.2f;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1(GameBaseData);
|
||||
|
||||
ConsoleDocClass( GameBaseData,
|
||||
"@brief Scriptable, demo-able datablock. Used by GameBase objects.\n\n"
|
||||
"@see GameBase\n"
|
||||
"@ingroup gameObjects\n"
|
||||
);
|
||||
|
||||
IMPLEMENT_CALLBACK( GameBaseData, onAdd, void, ( GameBase* obj ), ( obj ),
|
||||
"@brief Called when the object is added to the scene.\n\n"
|
||||
|
||||
"@param obj the GameBase object\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock GameBaseData(MyObjectData)\n"
|
||||
"{\n"
|
||||
" category = \"Misc\";\n"
|
||||
"};\n\n"
|
||||
"function MyObjectData::onAdd( %this, %obj )\n"
|
||||
"{\n"
|
||||
" echo( \"Added \" @ %obj.getName() @ \" to the scene.\" );\n"
|
||||
"}\n\n"
|
||||
"function MyObjectData::onNewDataBlock( %this, %obj )\n"
|
||||
"{\n"
|
||||
" echo( \"Assign \" @ %this.getName() @ \" datablock to \" %obj.getName() );\n"
|
||||
"}\n\n"
|
||||
"function MyObjectData::onRemove( %this, %obj )\n"
|
||||
"{\n"
|
||||
" echo( \"Removed \" @ %obj.getName() @ \" to the scene.\" );\n"
|
||||
"}\n\n"
|
||||
"function MyObjectData::onMount( %this, %obj, %mountObj, %node )\n"
|
||||
"{\n"
|
||||
" echo( %obj.getName() @ \" mounted to \" @ %mountObj.getName() );\n"
|
||||
"}\n\n"
|
||||
"function MyObjectData::onUnmount( %this, %obj, %mountObj, %node )\n"
|
||||
"{\n"
|
||||
" echo( %obj.getName() @ \" unmounted from \" @ %mountObj.getName() );\n"
|
||||
"}\n\n"
|
||||
"@endtsexample\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj ), ( obj ),
|
||||
"@brief Called when the object has a new datablock assigned.\n\n"
|
||||
"@param obj the GameBase object\n\n"
|
||||
"@see onAdd for an example\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( GameBaseData, onRemove, void, ( GameBase* obj ), ( obj ),
|
||||
"@brief Called when the object is removed from the scene.\n\n"
|
||||
"@param obj the GameBase object\n\n"
|
||||
"@see onAdd for an example\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( GameBaseData, onMount, void, ( GameBase* obj, SceneObject* mountObj, S32 node ), ( obj, mountObj, node ),
|
||||
"@brief Called when the object is mounted to another object in the scene.\n\n"
|
||||
"@param obj the GameBase object being mounted\n"
|
||||
"@param mountObj the object we are mounted to\n"
|
||||
"@param node the mountObj node we are mounted to\n\n"
|
||||
"@see onAdd for an example\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( GameBaseData, onUnmount, void, ( GameBase* obj, SceneObject* mountObj, S32 node ), ( obj, mountObj, node ),
|
||||
"@brief Called when the object is unmounted from another object in the scene.\n\n"
|
||||
"@param obj the GameBase object being unmounted\n"
|
||||
"@param mountObj the object we are unmounted from\n"
|
||||
"@param node the mountObj node we are unmounted from\n\n"
|
||||
"@see onAdd for an example\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( GameBase, setControl, void, ( bool controlled ), ( controlled ),
|
||||
"@brief Called when the client controlling the object changes.\n\n"
|
||||
"@param controlled true if a client now controls this object, false if no "
|
||||
"client controls this object.\n" );
|
||||
|
||||
|
||||
GameBaseData::GameBaseData()
|
||||
{
|
||||
category = "";
|
||||
packed = false;
|
||||
}
|
||||
|
||||
void GameBaseData::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
|
||||
// Tell interested parties ( like objects referencing this datablock )
|
||||
// that we have been modified and they might want to rebuild...
|
||||
mReloadSignal.trigger();
|
||||
}
|
||||
|
||||
bool GameBaseData::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBaseData::initPersistFields()
|
||||
{
|
||||
addGroup("Scripting");
|
||||
|
||||
addField( "category", TypeCaseString, Offset( category, GameBaseData ),
|
||||
"The group that this datablock will show up in under the \"Scripted\" "
|
||||
"tab in the World Editor Library." );
|
||||
|
||||
endGroup("Scripting");
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool GameBaseData::preload(bool server, String &errorStr)
|
||||
{
|
||||
if (!Parent::preload(server, errorStr))
|
||||
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 = false;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_NETOBJECT_V1(GameBase);
|
||||
|
||||
ConsoleDocClass( GameBase,
|
||||
"@brief Base class for game objects which use datablocks, networking, are "
|
||||
"editable, and need to process ticks.\n\n"
|
||||
"@ingroup gameObjects\n"
|
||||
);
|
||||
|
||||
GameBase::GameBase()
|
||||
: mDataBlock( NULL ),
|
||||
mControllingClient( NULL ),
|
||||
mCurrentWaterObject( NULL )
|
||||
{
|
||||
mNetFlags.set(Ghostable);
|
||||
mTypeMask |= GameBaseObjectType;
|
||||
mProcessTag = 0;
|
||||
|
||||
// From ProcessObject
|
||||
mIsGameBase = true;
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
mLastMoveId = 0;
|
||||
mTicksSinceLastMove = 0;
|
||||
mIsAiControlled = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
GameBase::~GameBase()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool GameBase::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
// Datablock must be initialized on the server.
|
||||
// Client datablock are initialized by the initial update.
|
||||
if ( isServerObject() && mDataBlock && !onNewDataBlock( mDataBlock, false ) )
|
||||
return false;
|
||||
|
||||
setProcessTick( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBase::onRemove()
|
||||
{
|
||||
// EDITOR FEATURE: Remove us from the reload signal of our datablock.
|
||||
if ( mDataBlock )
|
||||
mDataBlock->mReloadSignal.remove( this, &GameBase::_onDatablockModified );
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
bool GameBase::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||
{
|
||||
// EDITOR FEATURE: Remove us from old datablock's reload signal and
|
||||
// add us to the new one.
|
||||
if ( !reload )
|
||||
{
|
||||
if ( mDataBlock )
|
||||
mDataBlock->mReloadSignal.remove( this, &GameBase::_onDatablockModified );
|
||||
if ( dptr )
|
||||
dptr->mReloadSignal.notify( this, &GameBase::_onDatablockModified );
|
||||
}
|
||||
|
||||
|
||||
mDataBlock = dptr;
|
||||
|
||||
if ( !mDataBlock )
|
||||
return false;
|
||||
|
||||
setMaskBits(DataBlockMask);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameBase::_onDatablockModified()
|
||||
{
|
||||
AssertFatal( mDataBlock, "GameBase::onDatablockModified - mDataBlock is NULL." );
|
||||
onNewDataBlock( mDataBlock, true );
|
||||
}
|
||||
|
||||
void GameBase::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
setMaskBits(ExtendedInfoMask);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GameBase::processTick(const Move * move)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
if (!move)
|
||||
mTicksSinceLastMove++;
|
||||
|
||||
const char * srv = isClientObject() ? "client" : "server";
|
||||
const char * who = "";
|
||||
if (isClientObject())
|
||||
{
|
||||
if (this == (GameBase*)GameConnection::getConnectionToServer()->getControlObject())
|
||||
who = " player";
|
||||
else
|
||||
who = " ghost";
|
||||
if (mIsAiControlled)
|
||||
who = " ai";
|
||||
}
|
||||
if (isServerObject())
|
||||
{
|
||||
if (dynamic_cast<AIConnection*>(getControllingClient()))
|
||||
{
|
||||
who = " ai";
|
||||
mIsAiControlled = true;
|
||||
}
|
||||
else if (getControllingClient())
|
||||
{
|
||||
who = " player";
|
||||
mIsAiControlled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
who = "";
|
||||
mIsAiControlled = false;
|
||||
}
|
||||
}
|
||||
U32 moveid = mLastMoveId+mTicksSinceLastMove;
|
||||
if (move)
|
||||
moveid = move->id;
|
||||
|
||||
if (getTypeMask() & GameBaseHiFiObjectType)
|
||||
{
|
||||
if (move)
|
||||
Con::printf("Processing (%s%s id %i) move %i",srv,who,getId(), move->id);
|
||||
else
|
||||
Con::printf("Processing (%s%s id %i) move %i (%i)",srv,who,getId(),mLastMoveId+mTicksSinceLastMove,mTicksSinceLastMove);
|
||||
}
|
||||
|
||||
if (move)
|
||||
{
|
||||
mLastMoveId = move->id;
|
||||
mTicksSinceLastMove=0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
|
||||
{
|
||||
TORQUE_UNUSED(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 (getTypeMask() & PlayerObjectType)
|
||||
wInterest = 0.75f;
|
||||
else if (getTypeMask() & 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 (getTypeMask() & 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;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool GameBase::setDataBlock(GameBaseData* dptr)
|
||||
{
|
||||
if (isGhost() || isProperlyAdded()) {
|
||||
if (mDataBlock != dptr)
|
||||
return onNewDataBlock(dptr,false);
|
||||
}
|
||||
else
|
||||
mDataBlock = dptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void GameBase::scriptOnAdd()
|
||||
{
|
||||
// Script onAdd() must be called by the leaf class after
|
||||
// everything is ready.
|
||||
if (mDataBlock && !isGhost())
|
||||
mDataBlock->onAdd_callback( this );
|
||||
}
|
||||
|
||||
void GameBase::scriptOnNewDataBlock()
|
||||
{
|
||||
// Script onNewDataBlock() must be called by the leaf class
|
||||
// after everything is loaded.
|
||||
if (mDataBlock && !isGhost())
|
||||
mDataBlock->onNewDataBlock_callback( this );
|
||||
}
|
||||
|
||||
void GameBase::scriptOnRemove()
|
||||
{
|
||||
// Script onRemove() must be called by leaf class while
|
||||
// the object state is still valid.
|
||||
if (!isGhost() && mDataBlock)
|
||||
mDataBlock->onRemove_callback( this );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GameBase::setControllingClient(GameConnection* client)
|
||||
{
|
||||
if (isClientObject())
|
||||
{
|
||||
if (mControllingClient)
|
||||
setControl_callback( 0 );
|
||||
if (client)
|
||||
setControl_callback( 1 );
|
||||
}
|
||||
|
||||
mControllingClient = client;
|
||||
}
|
||||
|
||||
U32 GameBase::getPacketDataChecksum(GameConnection * connection)
|
||||
{
|
||||
// just write the packet data into a buffer
|
||||
// then we can CRC the buffer. This should always let us
|
||||
// know when there is a checksum problem.
|
||||
|
||||
static U8 buffer[1500] = { 0, };
|
||||
BitStream stream(buffer, sizeof(buffer));
|
||||
|
||||
writePacketData(connection, &stream);
|
||||
U32 byteCount = stream.getPosition();
|
||||
U32 ret = CRC::calculateCRC(buffer, byteCount, 0xFFFFFFFF);
|
||||
dMemset(buffer, 0, byteCount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GameBase::writePacketData(GameConnection*, BitStream*)
|
||||
{
|
||||
}
|
||||
|
||||
void GameBase::readPacketData(GameConnection*, BitStream*)
|
||||
{
|
||||
}
|
||||
|
||||
U32 GameBase::packUpdate( NetConnection *connection, U32 mask, BitStream *stream )
|
||||
{
|
||||
U32 retMask = Parent::packUpdate( connection, mask, stream );
|
||||
|
||||
if ( stream->writeFlag( mask & ScaleMask ) )
|
||||
{
|
||||
// Only write one bit if the scale is one.
|
||||
if ( stream->writeFlag( mObjScale != Point3F::One ) )
|
||||
mathWrite( *stream, mObjScale );
|
||||
}
|
||||
|
||||
if ( stream->writeFlag( ( mask & DataBlockMask ) && mDataBlock != NULL ) )
|
||||
{
|
||||
stream->writeRangedU32( mDataBlock->getId(),
|
||||
DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast );
|
||||
if ( stream->writeFlag( mNetFlags.test( NetOrdered ) ) )
|
||||
stream->writeInt( mOrderGUID, 16 );
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
stream->write(mLastMoveId);
|
||||
stream->writeFlag(mIsAiControlled);
|
||||
#endif
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void GameBase::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate( con, stream );
|
||||
|
||||
// ScaleMask
|
||||
if ( stream->readFlag() )
|
||||
{
|
||||
if ( stream->readFlag() )
|
||||
{
|
||||
VectorF scale;
|
||||
mathRead( *stream, &scale );
|
||||
setScale( scale );
|
||||
}
|
||||
else
|
||||
setScale( Point3F::One );
|
||||
}
|
||||
|
||||
// DataBlockMask
|
||||
if ( stream->readFlag() )
|
||||
{
|
||||
GameBaseData *dptr = 0;
|
||||
SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast );
|
||||
if ( stream->readFlag() )
|
||||
mOrderGUID = stream->readInt( 16 );
|
||||
|
||||
if ( !Sim::findObject( id, dptr ) || !setDataBlock( dptr ) )
|
||||
con->setLastError( "Invalid packet GameBase::unpackUpdate()" );
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
stream->read(&mLastMoveId);
|
||||
mTicksSinceLastMove = 0;
|
||||
mIsAiControlled = stream->readFlag();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GameBase::onMount( SceneObject *obj, S32 node )
|
||||
{
|
||||
deleteNotify( obj );
|
||||
|
||||
// Are we mounting to a GameBase object?
|
||||
GameBase *gbaseObj = dynamic_cast<GameBase*>( obj );
|
||||
|
||||
if ( gbaseObj && gbaseObj->getControlObject() != this )
|
||||
processAfter( gbaseObj );
|
||||
|
||||
if (!isGhost()) {
|
||||
setMaskBits(MountedMask);
|
||||
mDataBlock->onMount_callback( this, obj, node );
|
||||
}
|
||||
}
|
||||
|
||||
void GameBase::onUnmount( SceneObject *obj, S32 node )
|
||||
{
|
||||
clearNotify(obj);
|
||||
|
||||
GameBase *gbaseObj = dynamic_cast<GameBase*>( obj );
|
||||
|
||||
if ( gbaseObj && gbaseObj->getControlObject() != this )
|
||||
clearProcessAfter();
|
||||
|
||||
if (!isGhost()) {
|
||||
setMaskBits(MountedMask);
|
||||
mDataBlock->onUnmount_callback( this, obj, node );
|
||||
}
|
||||
}
|
||||
|
||||
bool GameBase::setDataBlockProperty( void *obj, const char *index, const char *db)
|
||||
{
|
||||
if( db == NULL || !db || !db[ 0 ] )
|
||||
{
|
||||
Con::errorf( "GameBase::setDataBlockProperty - Can't unset datablock on GameBase objects" );
|
||||
return false;
|
||||
}
|
||||
|
||||
GameBase* object = static_cast< GameBase* >( obj );
|
||||
GameBaseData* data;
|
||||
if( Sim::findObject( db, data ) )
|
||||
return object->setDataBlock( data );
|
||||
|
||||
Con::errorf( "GameBase::setDatablockProperty - Could not find data block \"%s\"", db );
|
||||
return false;
|
||||
}
|
||||
|
||||
MoveList* GameBase::getMoveList()
|
||||
{
|
||||
return mControllingClient ? mControllingClient->mMoveList : NULL;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
DefineEngineMethod( GameBase, getDataBlock, S32, (),,
|
||||
"@brief Get the datablock used by this object.\n\n"
|
||||
"@return the datablock this GameBase is using."
|
||||
"@see setDataBlock()\n")
|
||||
{
|
||||
return object->getDataBlock()? object->getDataBlock()->getId(): 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
DefineEngineMethod( GameBase, setDataBlock, bool, ( GameBaseData* data ),,
|
||||
"@brief Assign this GameBase to use the specified datablock.\n\n"
|
||||
"@param data new datablock to use\n"
|
||||
"@return true if successful, false if failed."
|
||||
"@see getDataBlock()\n")
|
||||
{
|
||||
return ( data && object->setDataBlock(data) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GameBase::initPersistFields()
|
||||
{
|
||||
addGroup( "Game" );
|
||||
|
||||
addProtectedField( "dataBlock", TYPEID< GameBaseData >(), Offset(mDataBlock, GameBase),
|
||||
&setDataBlockProperty, &defaultProtectedGetFn,
|
||||
"Script datablock used for game objects." );
|
||||
|
||||
endGroup( "Game" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
void GameBase::consoleInit()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
Con::addVariable( "GameBase::boundingBox", TypeBool, &gShowBoundingBox,
|
||||
"@brief Toggles on the rendering of the bounding boxes for certain types of objects in scene.\n\n"
|
||||
"@ingroup GameBase" );
|
||||
#endif
|
||||
}
|
||||
|
||||
DefineEngineMethod( GameBase, applyImpulse, bool, ( Point3F pos, VectorF vel ),,
|
||||
"@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n"
|
||||
|
||||
"@param pos impulse world position\n"
|
||||
"@param vel impulse velocity (impulse force F = m * v)\n"
|
||||
"@return Always true\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
object->applyImpulse(pos,vel);
|
||||
return true;
|
||||
}
|
||||
|
||||
DefineEngineMethod( GameBase, applyRadialImpulse, void, ( Point3F origin, F32 radius, F32 magnitude ),,
|
||||
"@brief Applies a radial impulse to the object using the given origin and force.\n\n"
|
||||
|
||||
"@param origin World point of origin of the radial impulse.\n"
|
||||
"@param radius The radius of the impulse area.\n"
|
||||
"@param magnitude The strength of the impulse.\n"
|
||||
|
||||
"@note Not all objects that derrive from GameBase have this defined.\n")
|
||||
{
|
||||
object->applyRadialImpulse( origin, radius, magnitude );
|
||||
}
|
||||
451
Engine/source/T3D/gameBase/gameBase.h
Normal file
451
Engine/source/T3D/gameBase/gameBase.h
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMEBASE_H_
|
||||
#define _GAMEBASE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _PROCESSLIST_H_
|
||||
#include "T3D/gameBase/processList.h"
|
||||
#endif
|
||||
#ifndef _TICKCACHE_H_
|
||||
#include "T3D/gameBase/tickCache.h"
|
||||
#endif
|
||||
#ifndef _DYNAMIC_CONSOLETYPES_H_
|
||||
#include "console/dynamicTypes.h"
|
||||
#endif
|
||||
|
||||
class NetConnection;
|
||||
class ProcessList;
|
||||
class GameBase;
|
||||
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
|
||||
/// 'class' 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;
|
||||
|
||||
// Signal triggered when this datablock is modified.
|
||||
// GameBase objects referencing this datablock notify with this signal.
|
||||
Signal<void(void)> mReloadSignal;
|
||||
|
||||
// Triggers the reload signal.
|
||||
void inspectPostApply();
|
||||
|
||||
bool onAdd();
|
||||
|
||||
// The derived class should provide the following:
|
||||
DECLARE_CONOBJECT(GameBaseData);
|
||||
GameBaseData();
|
||||
static void initPersistFields();
|
||||
bool preload(bool server, String &errorStr);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
/// @name Callbacks
|
||||
/// @{
|
||||
DECLARE_CALLBACK( void, onAdd, ( GameBase* obj ) );
|
||||
DECLARE_CALLBACK( void, onRemove, ( GameBase* obj ) );
|
||||
DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj ) );
|
||||
DECLARE_CALLBACK( void, onMount, ( GameBase* obj, SceneObject* mountObj, S32 node ) );
|
||||
DECLARE_CALLBACK( void, onUnmount, ( GameBase* obj, SceneObject* mountObj, S32 node ) );
|
||||
/// @}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// 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;
|
||||
class WaterObject;
|
||||
class MoveList;
|
||||
|
||||
// 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
|
||||
{
|
||||
typedef SceneObject Parent;
|
||||
|
||||
/// @name Datablock
|
||||
/// @{
|
||||
|
||||
GameBaseData* mDataBlock;
|
||||
|
||||
/// @}
|
||||
|
||||
TickCache mTickCache;
|
||||
|
||||
// Control interface
|
||||
GameConnection* mControllingClient;
|
||||
|
||||
public:
|
||||
|
||||
static bool gShowBoundingBox; ///< Should we render bounding boxes?
|
||||
|
||||
protected:
|
||||
|
||||
F32 mCameraFov;
|
||||
|
||||
/// The WaterObject we are currently within.
|
||||
WaterObject *mCurrentWaterObject;
|
||||
|
||||
static bool setDataBlockProperty( void *object, const char *index, const char *data );
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
U32 mLastMoveId;
|
||||
U32 mTicksSinceLastMove;
|
||||
bool mIsAiControlled;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
GameBase();
|
||||
~GameBase();
|
||||
|
||||
enum GameBaseMasks {
|
||||
DataBlockMask = Parent::NextFreeMask << 0,
|
||||
ExtendedInfoMask = Parent::NextFreeMask << 1,
|
||||
NextFreeMask = Parent::NextFreeMask << 2
|
||||
};
|
||||
|
||||
// net flags added by game base
|
||||
enum
|
||||
{
|
||||
NetOrdered = BIT(Parent::MaxNetFlagBit+1), /// Process in same order on client and server.
|
||||
NetNearbyAdded = BIT(Parent::MaxNetFlagBit+2), /// Is set during client catchup when neighbors have been checked.
|
||||
GhostUpdated = BIT(Parent::MaxNetFlagBit+3), /// Is set whenever ghost updated (and reset) on the client, for hifi objects.
|
||||
TickLast = BIT(Parent::MaxNetFlagBit+4), /// Tick this object after all others.
|
||||
NewGhost = BIT(Parent::MaxNetFlagBit+5), /// This ghost was just added during the last update.
|
||||
HiFiPassive = BIT(Parent::MaxNetFlagBit+6), /// Do not interact with other hifi passive objects.
|
||||
MaxNetFlagBit = Parent::MaxNetFlagBit+6
|
||||
};
|
||||
|
||||
/// @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
|
||||
/// @param reload Is this a new datablock or are we reloading one
|
||||
/// we already had.
|
||||
virtual bool onNewDataBlock( GameBaseData *dptr, bool reload );
|
||||
///@}
|
||||
|
||||
/// @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();
|
||||
|
||||
/// @}
|
||||
|
||||
// ProcessObject override
|
||||
void processTick( const Move *move );
|
||||
|
||||
/// @name GameBase NetFlags & Hifi-Net Interface
|
||||
/// @{
|
||||
|
||||
/// Set or clear the GhostUpdated bit in our NetFlags.
|
||||
/// @see GhostUpdated
|
||||
void setGhostUpdated( bool b ) { if (b) mNetFlags.set(GhostUpdated); else mNetFlags.clear(GhostUpdated); }
|
||||
|
||||
/// Returns true if the GhostUpdated bit in our NetFlags is set.
|
||||
/// @see GhostUpdated
|
||||
bool isGhostUpdated() const { return mNetFlags.test(GhostUpdated); }
|
||||
|
||||
/// Set or clear the NewGhost bit in our NetFlags.
|
||||
/// @see NewGhost
|
||||
void setNewGhost( bool n ) { if (n) mNetFlags.set(NewGhost); else mNetFlags.clear(NewGhost); }
|
||||
|
||||
/// Returns true if the NewGhost bit in out NetFlags is set.
|
||||
/// @see NewGhost
|
||||
bool isNewGhost() const { return mNetFlags.test(NewGhost); }
|
||||
|
||||
/// Set or clear the NetNearbyAdded bit in our NetFlags.
|
||||
/// @see NetNearbyAdded
|
||||
void setNetNearbyAdded( bool b ) { if (b) mNetFlags.set(NetNearbyAdded); else mNetFlags.clear(NetNearbyAdded); }
|
||||
|
||||
/// Returns true if the NetNearby bit in our NetFlags is set.
|
||||
/// @see NetNearbyAdded
|
||||
bool isNetNearbyAdded() const { return mNetFlags.test(NetNearbyAdded); }
|
||||
|
||||
/// Returns true if the HiFiPassive bit in our NetFlags is set.
|
||||
/// @see HiFiPassive
|
||||
bool isHifiPassive() const { return mNetFlags.test(HiFiPassive); }
|
||||
|
||||
/// Returns true if the TickLast bit in our NetFlags is set.
|
||||
/// @see TickLast
|
||||
bool isTickLast() const { return mNetFlags.test(TickLast); }
|
||||
|
||||
/// Returns true if the NetOrdered bit in our NetFlags is set.
|
||||
/// @see NetOrdered
|
||||
bool isNetOrdered() const { return mNetFlags.test(NetOrdered); }
|
||||
|
||||
/// Called during client catchup under the hifi-net model.
|
||||
virtual void computeNetSmooth( F32 backDelta ) {}
|
||||
|
||||
/// Returns TickCache used under the hifi-net model.
|
||||
TickCache& getTickCache() { return mTickCache; }
|
||||
/// @}
|
||||
|
||||
/// @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 controlling 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 Mounted objects ( overrides )
|
||||
/// @{
|
||||
|
||||
public:
|
||||
|
||||
virtual void onMount( SceneObject *obj, S32 node );
|
||||
virtual void onUnmount( SceneObject *obj,S32 node );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name User control
|
||||
/// @{
|
||||
|
||||
/// Returns the client controlling this object
|
||||
GameConnection *getControllingClient() { return mControllingClient; }
|
||||
const GameConnection *getControllingClient() const { return mControllingClient; }
|
||||
|
||||
/// Returns the MoveList of the client controlling this object.
|
||||
/// If there is no client it returns NULL;
|
||||
MoveList* getMoveList();
|
||||
|
||||
/// Sets the client controlling this object
|
||||
/// @param client Client that is now controlling this object
|
||||
virtual void setControllingClient( GameConnection *client );
|
||||
|
||||
virtual GameBase * getControllingObject() { return NULL; }
|
||||
virtual GameBase * getControlObject() { return NULL; }
|
||||
virtual void setControlObject( GameBase * ) { }
|
||||
/// @}
|
||||
|
||||
virtual F32 getDefaultCameraFov() { return 90.f; }
|
||||
virtual F32 getCameraFov() { return 90.f; }
|
||||
virtual void setCameraFov( F32 fov ) { }
|
||||
virtual bool isValidCameraFov( F32 fov ) { return true; }
|
||||
virtual bool useObjsEyePoint() const { return false; }
|
||||
virtual bool onlyFirstPerson() const { return false; }
|
||||
virtual F32 getDamageFlash() const { return 1.0f; }
|
||||
virtual F32 getWhiteOut() const { return 1.0f; }
|
||||
|
||||
// Not implemented here, but should return the Camera to world transformation matrix
|
||||
virtual void getCameraTransform (F32 *pos, MatrixF *mat ) { *mat = MatrixF::Identity; }
|
||||
|
||||
/// Returns the water object we are colliding with, it is up to derived
|
||||
/// classes to actually set this object.
|
||||
virtual WaterObject* getCurrentWaterObject() { return mCurrentWaterObject; }
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
bool isAIControlled() const { return mIsAiControlled; }
|
||||
#endif
|
||||
|
||||
DECLARE_CONOBJECT (GameBase );
|
||||
|
||||
/// @name Callbacks
|
||||
/// @{
|
||||
DECLARE_CALLBACK( void, setControl, ( bool controlled ) );
|
||||
/// @}
|
||||
|
||||
private:
|
||||
|
||||
/// This is called by the reload signal in our datablock when it is
|
||||
/// modified in the editor.
|
||||
///
|
||||
/// This method is private and is not virtual. To handle a datablock-modified
|
||||
/// even in a child-class specific way you should override onNewDatablock
|
||||
/// and handle the reload( true ) case.
|
||||
///
|
||||
/// Warning: For local-client, editor situations only.
|
||||
///
|
||||
/// Warning: Do not attempt to call .remove or .notify on mDataBlock->mReloadSignal
|
||||
/// within this callback.
|
||||
///
|
||||
void _onDatablockModified();
|
||||
};
|
||||
|
||||
|
||||
#endif // _GAMEBASE_H_
|
||||
2105
Engine/source/T3D/gameBase/gameConnection.cpp
Normal file
2105
Engine/source/T3D/gameBase/gameConnection.cpp
Normal file
File diff suppressed because it is too large
Load diff
345
Engine/source/T3D/gameBase/gameConnection.h
Normal file
345
Engine/source/T3D/gameBase/gameConnection.h
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMECONNECTION_H_
|
||||
#define _GAMECONNECTION_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _GAMEBASE_H_
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#endif
|
||||
#ifndef _NETCONNECTION_H_
|
||||
#include "sim/netConnection.h"
|
||||
#endif
|
||||
#ifndef _MOVEMANAGER_H_
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#endif
|
||||
#ifndef _BITVECTOR_H_
|
||||
#include "core/bitVector.h"
|
||||
#endif
|
||||
|
||||
enum GameConnectionConstants
|
||||
{
|
||||
MaxClients = 126,
|
||||
DataBlockQueueCount = 16
|
||||
};
|
||||
|
||||
class SFXProfile;
|
||||
class MatrixF;
|
||||
class MatrixF;
|
||||
class Point3F;
|
||||
class MoveManager;
|
||||
class MoveList;
|
||||
struct Move;
|
||||
struct AuthInfo;
|
||||
|
||||
#define GameString TORQUE_APP_NAME
|
||||
|
||||
const F32 MinCameraFov = 1.f; ///< min camera FOV
|
||||
const F32 MaxCameraFov = 179.f; ///< max camera FOV
|
||||
|
||||
class GameConnection : public NetConnection
|
||||
{
|
||||
private:
|
||||
typedef NetConnection Parent;
|
||||
|
||||
SimObjectPtr<GameBase> mControlObject;
|
||||
SimObjectPtr<GameBase> 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.
|
||||
/// @}
|
||||
|
||||
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();
|
||||
|
||||
bool mControlForceMismatch;
|
||||
|
||||
Vector<SimDataBlock *> mDataBlockLoadList;
|
||||
|
||||
public:
|
||||
|
||||
MoveList *mMoveList;
|
||||
|
||||
protected:
|
||||
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);
|
||||
/// @}
|
||||
void ghostWriteExtra(NetObject *,BitStream *);
|
||||
void ghostReadExtra(NetObject *,BitStream *, bool newGhost);
|
||||
void ghostPreRead(NetObject *, bool newGhost);
|
||||
|
||||
virtual void onEndGhosting();
|
||||
|
||||
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();
|
||||
|
||||
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(GameBase *);
|
||||
GameBase* getControlObject() { return mControlObject; }
|
||||
const GameBase* getControlObject() const { return mControlObject; }
|
||||
|
||||
void setCameraObject(GameBase *);
|
||||
GameBase* getCameraObject();
|
||||
|
||||
bool getControlCameraTransform(F32 dt,MatrixF* mat);
|
||||
bool getControlCameraVelocity(Point3F *vel);
|
||||
|
||||
bool getControlCameraDefaultFov(F32 *fov);
|
||||
bool getControlCameraFov(F32 *fov);
|
||||
bool setControlCameraFov(F32 fov);
|
||||
bool isValidControlCameraFov(F32 fov);
|
||||
|
||||
// Used by editor
|
||||
bool isControlObjectRotDampedCamera();
|
||||
|
||||
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; }
|
||||
|
||||
/// Return the datablock sequence number that this game connection is on.
|
||||
/// The datablock sequence number is synchronized to the mission sequence number
|
||||
/// on each datablock transmission.
|
||||
U32 getDataBlockSequence() { return mDataBlockSequence; }
|
||||
|
||||
/// Set the datablock sequence number.
|
||||
void setDataBlockSequence(U32 seq) { mDataBlockSequence = seq; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Fade control
|
||||
/// @{
|
||||
|
||||
F32 getDamageFlash() const { return mDamageFlash; }
|
||||
F32 getWhiteOut() const { return mWhiteOut; }
|
||||
|
||||
void setBlackOut(bool fadeToBlack, S32 timeMS);
|
||||
F32 getBlackOut();
|
||||
/// @}
|
||||
|
||||
/// @name Authentication
|
||||
///
|
||||
/// This is remnant code from Tribes 2.
|
||||
/// @{
|
||||
|
||||
void setAuthInfo(const AuthInfo *info);
|
||||
const AuthInfo *getAuthInfo();
|
||||
/// @}
|
||||
|
||||
/// @name Sound
|
||||
/// @{
|
||||
|
||||
void play2D(SFXProfile *profile);
|
||||
void play3D(SFXProfile *profile, const MatrixF *transform);
|
||||
/// @}
|
||||
|
||||
/// @name Misc.
|
||||
/// @{
|
||||
|
||||
bool isFirstPerson() const { return mCameraPos == 0; }
|
||||
bool isAIControlled() { return mAIControlled; }
|
||||
|
||||
void doneScopingScene();
|
||||
void demoPlaybackComplete();
|
||||
|
||||
void setMissionCRC(U32 crc) { mMissionCRC = crc; }
|
||||
U32 getMissionCRC() { return(mMissionCRC); }
|
||||
/// @}
|
||||
|
||||
static Signal<void(F32)> smFovUpdate;
|
||||
static Signal<void()> smPlayingDemo;
|
||||
|
||||
protected:
|
||||
DECLARE_CALLBACK( void, onConnectionTimedOut, () );
|
||||
DECLARE_CALLBACK( void, onConnectionAccepted, () );
|
||||
DECLARE_CALLBACK( void, onConnectRequestTimedOut, () );
|
||||
DECLARE_CALLBACK( void, onConnectionDropped, (const char* reason) );
|
||||
DECLARE_CALLBACK( void, onConnectRequestRejected, (const char* reason) );
|
||||
DECLARE_CALLBACK( void, onConnectionError, (const char* errorString) );
|
||||
DECLARE_CALLBACK( void, onDrop, (const char* disconnectReason) );
|
||||
DECLARE_CALLBACK( void, initialControlSet, () );
|
||||
DECLARE_CALLBACK( void, onControlObjectChange, () );
|
||||
DECLARE_CALLBACK( void, setLagIcon, (bool state) );
|
||||
DECLARE_CALLBACK( void, onDataBlocksDone, (U32 sequence) );
|
||||
DECLARE_CALLBACK( void, onFlash, (bool state) );
|
||||
};
|
||||
|
||||
#endif
|
||||
382
Engine/source/T3D/gameBase/gameConnectionEvents.cpp
Normal file
382
Engine/source/T3D/gameBase/gameConnectionEvents.cpp
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/dnet.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/simBase.h"
|
||||
#include "scene/pathManager.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "sfx/sfxSystem.h"
|
||||
#include "sfx/sfxDescription.h"
|
||||
#include "app/game.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/gameBase/gameConnectionEvents.h"
|
||||
|
||||
#define DebugChecksum 0xF00DBAAD
|
||||
|
||||
|
||||
//#define DEBUG_SPEW
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(SimDataBlockEvent);
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(Sim2DAudioEvent);
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(Sim3DAudioEvent);
|
||||
IMPLEMENT_CO_CLIENTEVENT_V1(SetMissionCRCEvent);
|
||||
|
||||
ConsoleDocClass( SimDataBlockEvent,
|
||||
"@brief Use by GameConnection to process incoming datablocks.\n\n"
|
||||
"Not intended for game development, internal use only, but does expose onDataBlockObjectReceived.\n\n "
|
||||
"@internal");
|
||||
|
||||
ConsoleDocClass( Sim2DAudioEvent,
|
||||
"@brief Use by GameConnection to send a 2D sound event over the network.\n\n"
|
||||
"Not intended for game development, internal use only, but does expose GameConnection::play2D.\n\n "
|
||||
"@internal");
|
||||
|
||||
ConsoleDocClass( Sim3DAudioEvent,
|
||||
"@brief Use by GameConnection to send a 3D sound event over the network.\n\n"
|
||||
"Not intended for game development, internal use only, but does expose GameConnection::play3D.\n\n "
|
||||
"@internal");
|
||||
|
||||
ConsoleDocClass( SetMissionCRCEvent,
|
||||
"@brief Use by GameConnection to send a 3D sound event over the network.\n\n"
|
||||
"Not intended for game development, internal use only, but does expose GameConnection::setMissionCRC.\n\n "
|
||||
"@internal");
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
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");
|
||||
|
||||
#ifdef DEBUG_SPEW
|
||||
Con::printf("queuing data block: %d", mIndex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
SimDataBlockEvent::~SimDataBlockEvent()
|
||||
{
|
||||
if( mObj )
|
||||
delete mObj;
|
||||
}
|
||||
|
||||
#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);
|
||||
#ifdef TORQUE_DEBUG_NET
|
||||
bstream->writeInt(classId ^ DebugChecksum, 32);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if( Sim::findObject( id, ptr ) )
|
||||
{
|
||||
// An object with the given ID already exists. Make sure it has the right class.
|
||||
|
||||
AbstractClassRep* classRep = AbstractClassRep::findClassRep( cptr->getNetClassGroup(), NetClassTypeDataBlock, classId );
|
||||
if( classRep && dStrcmp( classRep->getClassName(), ptr->getClassName() ) != 0 )
|
||||
{
|
||||
Con::warnf( "A '%s' datablock with id: %d already existed. "
|
||||
"Clobbering it with new '%s' datablock from server.",
|
||||
ptr->getClassName(), id, classRep->getClassName() );
|
||||
ptr->deleteObject();
|
||||
ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if( !ptr )
|
||||
ptr = ( SimObject* ) ConsoleObject::create( cptr->getNetClassGroup(), NetClassTypeDataBlock, classId );
|
||||
|
||||
mObj = dynamic_cast< SimDataBlock* >( ptr );
|
||||
if( mObj != NULL )
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Con::printf(" - SimDataBlockEvent: unpacking event of type: %s", mObj->getClassName());
|
||||
#endif
|
||||
|
||||
mObj->unpackData( bstream );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_SPEW
|
||||
Con::printf(" - SimDataBlockEvent: INVALID PACKET! Could not create class with classID: %d", classId);
|
||||
#endif
|
||||
|
||||
delete ptr;
|
||||
cptr->setLastError("Invalid packet in SimDataBlockEvent::unpack()");
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET
|
||||
U32 checksum = bstream->readInt(32);
|
||||
AssertISV( (checksum ^ DebugChecksum) == (U32)classId,
|
||||
avar("unpack did not match pack for event of class %s.",
|
||||
mObj->getClassName()) );
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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("onDataBlockObjectReceived", Con::getIntArg(mIndex), Con::getIntArg(mTotal));
|
||||
|
||||
String &errorBuffer = NetConnection::getErrorBuffer();
|
||||
|
||||
// Register the datablock object if this is a new DB
|
||||
// and not for a modified datablock event.
|
||||
|
||||
if( !mObj->isProperlyAdded() )
|
||||
{
|
||||
// This is a fresh datablock object.
|
||||
// Perform preload on datablock and register
|
||||
// the object.
|
||||
|
||||
GameConnection* conn = dynamic_cast< GameConnection* >( cptr );
|
||||
if( conn )
|
||||
conn->preloadDataBlock( mObj );
|
||||
|
||||
if( mObj->registerObject(id) )
|
||||
{
|
||||
cptr->addObject( mObj );
|
||||
mObj = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an update to an existing datablock. Preload
|
||||
// to finish this.
|
||||
|
||||
mObj->preload( false, errorBuffer );
|
||||
mObj = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
Sim2DAudioEvent::Sim2DAudioEvent(SFXProfile *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)
|
||||
SFX->playOnce( mProfile );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static F32 SoundPosAccuracy = 0.5;
|
||||
static S32 SoundRotBits = 8;
|
||||
|
||||
Sim3DAudioEvent::Sim3DAudioEvent(SFXProfile *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.
|
||||
SFXDescription* ad = mProfile->getDescription();
|
||||
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)
|
||||
SFX->playOnce( mProfile, &mTransform );
|
||||
}
|
||||
|
||||
161
Engine/source/T3D/gameBase/gameConnectionEvents.h
Normal file
161
Engine/source/T3D/gameBase/gameConnectionEvents.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMECONNECTIONEVENTS_H_
|
||||
#define _GAMECONNECTIONEVENTS_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _GAMECONNECTION_H_
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SFXPROFILE_H_
|
||||
#include "sfx/sfxProfile.h"
|
||||
#endif
|
||||
|
||||
#ifndef _BITSTREAM_H_
|
||||
#include "core/stream/bitStream.h"
|
||||
#endif
|
||||
|
||||
|
||||
class QuitEvent : public SimEvent
|
||||
{
|
||||
void process(SimObject *object)
|
||||
{
|
||||
Platform::postQuitMessage(0);
|
||||
}
|
||||
};
|
||||
|
||||
/// Event for sending a datablock over the net from the server to the client.
|
||||
///
|
||||
/// Datablock events are GuaranteedOrdered client events.
|
||||
///
|
||||
class SimDataBlockEvent : public NetEvent
|
||||
{
|
||||
public:
|
||||
|
||||
typedef NetEvent Parent;
|
||||
|
||||
protected:
|
||||
|
||||
/// Id of the datablock object to be sent. This must be a datablock ID
|
||||
/// (as opposed to a normal object ID).
|
||||
SimObjectId id;
|
||||
|
||||
///
|
||||
U32 mIndex;
|
||||
|
||||
/// Total number of datablocks that are part of this datablock transmission.
|
||||
/// Each datablock is transmitted in an independent datablock event.
|
||||
U32 mTotal;
|
||||
|
||||
/// The mission sequence number to which this datablock transmission
|
||||
/// belongs.
|
||||
///
|
||||
/// @see GameConnection::getDataBlockSequence
|
||||
U32 mMissionSequence;
|
||||
|
||||
/// Datablock object constructed on the client side.
|
||||
SimDataBlock *mObj;
|
||||
|
||||
///
|
||||
bool mProcess;
|
||||
|
||||
public:
|
||||
|
||||
SimDataBlockEvent(SimDataBlock* obj = NULL, U32 index = 0, U32 total = 0, U32 missionSequence = 0);
|
||||
~SimDataBlockEvent();
|
||||
|
||||
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 );
|
||||
DECLARE_CATEGORY( "Game Networking" );
|
||||
};
|
||||
|
||||
class Sim2DAudioEvent: public NetEvent
|
||||
{
|
||||
private:
|
||||
SFXProfile *mProfile;
|
||||
|
||||
public:
|
||||
typedef NetEvent Parent;
|
||||
Sim2DAudioEvent(SFXProfile *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:
|
||||
SFXProfile *mProfile;
|
||||
MatrixF mTransform;
|
||||
|
||||
public:
|
||||
typedef NetEvent Parent;
|
||||
Sim3DAudioEvent(SFXProfile *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:
|
||||
typedef NetEvent Parent;
|
||||
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
|
||||
184
Engine/source/T3D/gameBase/gameProcess.cpp
Normal file
184
Engine/source/T3D/gameBase/gameProcess.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameBase/gameProcess.h"
|
||||
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/gameBase/moveList.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ClientProcessList* ClientProcessList::smClientProcessList = NULL;
|
||||
ServerProcessList* ServerProcessList::smServerProcessList = NULL;
|
||||
static U32 gNetOrderNextId = 0;
|
||||
|
||||
ConsoleFunction( dumpProcessList, void, 1, 1,
|
||||
"Dumps all ProcessObjects in ServerProcessList and ClientProcessList to the console." )
|
||||
{
|
||||
Con::printf( "client process list:" );
|
||||
ClientProcessList::get()->dumpToConsole();
|
||||
Con::printf( "server process list:" );
|
||||
ServerProcessList::get()->dumpToConsole();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ClientProcessList
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ClientProcessList::ClientProcessList()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientProcessList::addObject( ProcessObject *pobj )
|
||||
{
|
||||
AssertFatal( static_cast<SceneObject*>( pobj )->isClientObject(), "Tried to add server object to ClientProcessList." );
|
||||
|
||||
GameBase *obj = getGameBase( pobj );
|
||||
|
||||
if ( obj && obj->isNetOrdered() )
|
||||
{
|
||||
if ( ( gNetOrderNextId & 0xFFFF ) == 0 )
|
||||
// don't let it be zero
|
||||
gNetOrderNextId++;
|
||||
|
||||
pobj->mOrderGUID = ( gNetOrderNextId++ ) & 0xFFFF; // 16 bits should be enough
|
||||
pobj->plLinkBefore( &mHead );
|
||||
mDirty = true;
|
||||
}
|
||||
else if ( obj && obj->isTickLast() )
|
||||
{
|
||||
pobj->mOrderGUID = 0xFFFFFFFF;
|
||||
pobj->plLinkBefore( &mHead );
|
||||
// not dirty
|
||||
}
|
||||
else
|
||||
{
|
||||
pobj->plLinkAfter( &mHead );
|
||||
// not dirty
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientProcessList::doBacklogged( SimTime timeDelta )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
static bool backlogged = false;
|
||||
static U32 backloggedTime = 0;
|
||||
#endif
|
||||
|
||||
// See if the control object has pending moves.
|
||||
GameConnection *connection = GameConnection::getConnectionToServer();
|
||||
|
||||
if ( connection )
|
||||
{
|
||||
// If the connection to the server is backlogged
|
||||
// the simulation is frozen.
|
||||
if ( connection->mMoveList->isBacklogged() )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG
|
||||
if ( !backlogged )
|
||||
{
|
||||
Con::printf( "client is backlogged, time is frozen" );
|
||||
backlogged = true;
|
||||
}
|
||||
|
||||
backloggedTime += timeDelta;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
if ( backlogged )
|
||||
{
|
||||
Con::printf( "client is no longer backlogged, time is unfrozen (%i ms elapsed)", backloggedTime );
|
||||
backlogged = false;
|
||||
backloggedTime = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientProcessList::onPreTickObject( ProcessObject *pobj )
|
||||
{
|
||||
// reset to tick boundary
|
||||
pobj->interpolateTick( 0.0f );
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ServerProcessList
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ServerProcessList::ServerProcessList()
|
||||
{
|
||||
}
|
||||
|
||||
void ServerProcessList::addObject( ProcessObject *pobj )
|
||||
{
|
||||
AssertFatal( static_cast<SceneObject*>( pobj )->isServerObject(), "Tried to add client object to ServerProcessList." );
|
||||
|
||||
GameBase *obj = getGameBase( pobj );
|
||||
|
||||
if ( obj && obj->isNetOrdered() )
|
||||
{
|
||||
if ( ( gNetOrderNextId & 0xFFFF ) == 0)
|
||||
// don't let it be zero
|
||||
gNetOrderNextId++;
|
||||
|
||||
pobj->mOrderGUID = ( gNetOrderNextId++ ) & 0xFFFF; // 16 bits should be enough
|
||||
pobj->plLinkBefore( &mHead );
|
||||
mDirty = true;
|
||||
}
|
||||
else if ( obj && obj->isTickLast() )
|
||||
{
|
||||
pobj->mOrderGUID = 0xFFFFFFFF;
|
||||
pobj->plLinkBefore( &mHead );
|
||||
// not dirty
|
||||
}
|
||||
else
|
||||
{
|
||||
pobj->plLinkAfter( &mHead );
|
||||
// not dirty
|
||||
}
|
||||
}
|
||||
|
||||
void ServerProcessList::advanceObjects()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("Advance server time...");
|
||||
#endif
|
||||
|
||||
Parent::advanceObjects();
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("---------");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ServerProcessList::onPreTickObject( ProcessObject *pobj )
|
||||
{
|
||||
}
|
||||
|
||||
96
Engine/source/T3D/gameBase/gameProcess.h
Normal file
96
Engine/source/T3D/gameBase/gameProcess.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMEPROCESS_H_
|
||||
#define _GAMEPROCESS_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PROCESSLIST_H_
|
||||
#include "T3D/gameBase/processList.h"
|
||||
#endif
|
||||
|
||||
|
||||
class GameBase;
|
||||
class GameConnection;
|
||||
struct Move;
|
||||
|
||||
|
||||
class ClientProcessList : public ProcessList
|
||||
{
|
||||
typedef ProcessList Parent;
|
||||
|
||||
public:
|
||||
|
||||
ClientProcessList();
|
||||
|
||||
// ProcessList
|
||||
void addObject( ProcessObject *pobj );
|
||||
|
||||
/// Called after a correction packet is received from the server.
|
||||
/// If the control object was corrected it will now play back any moves
|
||||
/// which were rolled back.
|
||||
virtual void clientCatchup( GameConnection *conn ) {}
|
||||
|
||||
static ClientProcessList* get() { return smClientProcessList; }
|
||||
|
||||
protected:
|
||||
|
||||
// ProcessList
|
||||
void onPreTickObject( ProcessObject *pobj );
|
||||
|
||||
/// Returns true if backlogged.
|
||||
bool doBacklogged( SimTime timeDelta );
|
||||
|
||||
protected:
|
||||
|
||||
static ClientProcessList* smClientProcessList;
|
||||
};
|
||||
|
||||
|
||||
class ServerProcessList : public ProcessList
|
||||
{
|
||||
typedef ProcessList Parent;
|
||||
|
||||
public:
|
||||
|
||||
ServerProcessList();
|
||||
|
||||
// ProcessList
|
||||
void addObject( ProcessObject *pobj );
|
||||
|
||||
static ServerProcessList* get() { return smServerProcessList; }
|
||||
|
||||
protected:
|
||||
|
||||
// ProcessList
|
||||
void onPreTickObject( ProcessObject *pobj );
|
||||
void advanceObjects();
|
||||
|
||||
protected:
|
||||
|
||||
static ServerProcessList* smServerProcessList;
|
||||
};
|
||||
|
||||
|
||||
#endif // _GAMEPROCESS_H_
|
||||
607
Engine/source/T3D/gameBase/hifi/hifiGameProcess.cpp
Normal file
607
Engine/source/T3D/gameBase/hifi/hifiGameProcess.cpp
Normal file
|
|
@ -0,0 +1,607 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameBase/hifi/hifiGameProcess.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "math/mathUtils.h"
|
||||
#include "T3D/gameBase/hifi/hifiMoveList.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/gameFunctions.h"
|
||||
|
||||
|
||||
MODULE_BEGIN( ProcessList )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
HifiServerProcessList::init();
|
||||
HifiClientProcessList::init();
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
HifiServerProcessList::shutdown();
|
||||
HifiClientProcessList::shutdown();
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
void HifiServerProcessList::init()
|
||||
{
|
||||
smServerProcessList = new HifiServerProcessList();
|
||||
}
|
||||
|
||||
void HifiServerProcessList::shutdown()
|
||||
{
|
||||
delete smServerProcessList;
|
||||
}
|
||||
|
||||
void HifiClientProcessList::init()
|
||||
{
|
||||
smClientProcessList = new HifiClientProcessList();
|
||||
}
|
||||
|
||||
void HifiClientProcessList::shutdown()
|
||||
{
|
||||
delete smClientProcessList;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
F32 gMaxHiFiVelSq = 100 * 100;
|
||||
|
||||
namespace
|
||||
{
|
||||
inline GameBase * GetGameBase(ProcessObject * obj)
|
||||
{
|
||||
return static_cast<GameBase*>(obj);
|
||||
}
|
||||
|
||||
// local work class
|
||||
struct GameBaseListNode
|
||||
{
|
||||
GameBaseListNode()
|
||||
{
|
||||
mPrev=this;
|
||||
mNext=this;
|
||||
mObject=NULL;
|
||||
}
|
||||
|
||||
GameBaseListNode * mPrev;
|
||||
GameBaseListNode * mNext;
|
||||
GameBase * mObject;
|
||||
|
||||
void linkBefore(GameBaseListNode * obj)
|
||||
{
|
||||
// Link this before obj
|
||||
mNext = obj;
|
||||
mPrev = obj->mPrev;
|
||||
obj->mPrev = this;
|
||||
mPrev->mNext = this;
|
||||
}
|
||||
};
|
||||
|
||||
// Structure used for synchronizing move lists on client/server
|
||||
struct MoveSync
|
||||
{
|
||||
enum { ActionCount = 4 };
|
||||
|
||||
S32 moveDiff;
|
||||
S32 moveDiffSteadyCount;
|
||||
S32 moveDiffSameSignCount;
|
||||
|
||||
bool doAction() { return moveDiffSteadyCount>=ActionCount || moveDiffSameSignCount>=4*ActionCount; }
|
||||
void reset() { moveDiff=0; moveDiffSteadyCount=0; moveDiffSameSignCount=0; }
|
||||
void update(S32 diff);
|
||||
} moveSync;
|
||||
|
||||
void MoveSync::update(S32 diff)
|
||||
{
|
||||
if (diff && diff==moveDiff)
|
||||
{
|
||||
moveDiffSteadyCount++;
|
||||
moveDiffSameSignCount++;
|
||||
}
|
||||
else if (diff*moveDiff>0)
|
||||
{
|
||||
moveDiffSteadyCount = 0;
|
||||
moveDiffSameSignCount++;
|
||||
}
|
||||
else
|
||||
reset();
|
||||
moveDiff = diff;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// HifiClientProcessList
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
HifiClientProcessList::HifiClientProcessList()
|
||||
{
|
||||
mSkipAdvanceObjectsMs = 0;
|
||||
mForceHifiReset = false;
|
||||
mCatchup = 0;
|
||||
}
|
||||
|
||||
bool HifiClientProcessList::advanceTime( SimTime timeDelta )
|
||||
{
|
||||
PROFILE_SCOPE( AdvanceClientTime );
|
||||
|
||||
if ( mSkipAdvanceObjectsMs && timeDelta > mSkipAdvanceObjectsMs )
|
||||
{
|
||||
timeDelta -= mSkipAdvanceObjectsMs;
|
||||
advanceTime( mSkipAdvanceObjectsMs );
|
||||
AssertFatal( !mSkipAdvanceObjectsMs, "mSkipAdvanceObjectsMs must always be positive." );
|
||||
}
|
||||
|
||||
if ( doBacklogged( timeDelta ) )
|
||||
return false;
|
||||
|
||||
// remember interpolation value because we might need to set it back
|
||||
F32 oldLastDelta = mLastDelta;
|
||||
|
||||
bool ret = Parent::advanceTime( timeDelta );
|
||||
|
||||
if ( !mSkipAdvanceObjectsMs )
|
||||
{
|
||||
AssertFatal( mLastDelta >= 0.0f && mLastDelta <= 1.0f, "mLastDelta must always be zero to one." );
|
||||
for ( ProcessObject *pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next )
|
||||
{
|
||||
if ( pobj->isTicking() )
|
||||
pobj->interpolateTick( mLastDelta );
|
||||
}
|
||||
|
||||
// Inform objects of total elapsed delta so they can advance
|
||||
// client side animations.
|
||||
F32 dt = F32( timeDelta ) / 1000;
|
||||
for ( ProcessObject *pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next)
|
||||
{
|
||||
pobj->advanceTime( dt );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mSkipAdvanceObjectsMs -= timeDelta;
|
||||
mLastDelta = oldLastDelta;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HifiClientProcessList::onAdvanceObjects()
|
||||
{
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
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)
|
||||
return;
|
||||
}
|
||||
while(blockType != GameConnection::BlockTypeMove);
|
||||
}
|
||||
if (!mSkipAdvanceObjectsMs)
|
||||
{
|
||||
connection->mMoveList->collectMove();
|
||||
advanceObjects();
|
||||
}
|
||||
connection->mMoveList->onAdvanceObjects();
|
||||
}
|
||||
}
|
||||
|
||||
void HifiClientProcessList::onTickObject(ProcessObject * pobj)
|
||||
{
|
||||
// Each object is advanced a single tick
|
||||
// If it's controlled by a client, tick using a move.
|
||||
|
||||
Move *movePtr;
|
||||
U32 numMoves;
|
||||
GameConnection *con = pobj->getControllingClient();
|
||||
SimObjectPtr<GameBase> obj = getGameBase( pobj );
|
||||
|
||||
if ( obj && con && con->getControlObject() == obj && con->mMoveList->getMoves( &movePtr, &numMoves) )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() );
|
||||
#endif
|
||||
|
||||
obj->processTick( movePtr );
|
||||
|
||||
if ( bool(obj) && obj->getControllingClient() )
|
||||
{
|
||||
U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() );
|
||||
|
||||
// set checksum if not set or check against stored value if set
|
||||
movePtr->checksum = newsum;
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf( "move checksum: %i, (start %i), (move %f %f %f)",
|
||||
movePtr->checksum,sum,movePtr->yaw,movePtr->y,movePtr->z );
|
||||
#endif
|
||||
}
|
||||
con->mMoveList->clearMoves( 1 );
|
||||
}
|
||||
else if ( pobj->isTicking() )
|
||||
pobj->processTick( 0 );
|
||||
|
||||
if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) )
|
||||
{
|
||||
GameConnection * serverConnection = GameConnection::getConnectionToServer();
|
||||
TickCacheEntry * tce = obj->getTickCache().addCacheEntry();
|
||||
BitStream bs( tce->packetData, TickCacheEntry::MaxPacketSize );
|
||||
obj->writePacketData( serverConnection, &bs );
|
||||
|
||||
Point3F vel = obj->getVelocity();
|
||||
F32 velSq = mDot( vel, vel );
|
||||
gMaxHiFiVelSq = getMax( gMaxHiFiVelSq, velSq );
|
||||
}
|
||||
}
|
||||
|
||||
void HifiClientProcessList::advanceObjects()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("Advance client time...");
|
||||
#endif
|
||||
|
||||
// client re-computes this each time objects are advanced
|
||||
gMaxHiFiVelSq = 0;
|
||||
Parent::advanceObjects();
|
||||
|
||||
// We need to consume a move on the connections whether
|
||||
// there is a control object to consume the move or not,
|
||||
// otherwise client and server can get out of sync move-wise
|
||||
// during startup. If there is a control object, we cleared
|
||||
// a move above. Handle case where no control object here.
|
||||
// Note that we might consume an extra move here and there when
|
||||
// we had a control object in above loop but lost it during tick.
|
||||
// That is no big deal so we don't bother trying to carefully
|
||||
// track it.
|
||||
GameConnection * client = GameConnection::getConnectionToServer();
|
||||
if (client && client->getControlObject() == NULL)
|
||||
client->mMoveList->clearMoves(1);
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("---------");
|
||||
#endif
|
||||
}
|
||||
|
||||
void HifiClientProcessList::ageTickCache(S32 numToAge, S32 len)
|
||||
{
|
||||
for (ProcessObject * pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next)
|
||||
{
|
||||
GameBase *obj = getGameBase(pobj);
|
||||
if ( obj && obj->getTypeMask() & GameBaseHiFiObjectType )
|
||||
obj->getTickCache().ageCache(numToAge,len);
|
||||
}
|
||||
}
|
||||
|
||||
void HifiClientProcessList::updateMoveSync(S32 moveDiff)
|
||||
{
|
||||
moveSync.update(moveDiff);
|
||||
if (moveSync.doAction() && moveDiff<0)
|
||||
{
|
||||
skipAdvanceObjects(TickMs * -moveDiff);
|
||||
moveSync.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void HifiClientProcessList::clientCatchup(GameConnection * connection)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("client catching up... (%i)%s", mCatchup, mForceHifiReset ? " reset" : "");
|
||||
#endif
|
||||
|
||||
if (connection->getControlObject() && connection->getControlObject()->isGhostUpdated())
|
||||
// if control object is reset, make sure moves are reset too
|
||||
connection->mMoveList->resetCatchup();
|
||||
|
||||
const F32 maxVel = mSqrt(gMaxHiFiVelSq) * 1.25f;
|
||||
F32 dt = F32(mCatchup+1) * TickSec;
|
||||
Point3F bigDelta(maxVel*dt,maxVel*dt,maxVel*dt);
|
||||
|
||||
// walk through all process objects looking for ones which were updated
|
||||
// -- during first pass merely collect neighbors which need to be reset and updated in unison
|
||||
ProcessObject * pobj;
|
||||
if (mCatchup && !mForceHifiReset)
|
||||
{
|
||||
for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next)
|
||||
{
|
||||
GameBase *obj = getGameBase( pobj );
|
||||
static SimpleQueryList nearby;
|
||||
nearby.mList.clear();
|
||||
// check for nearby objects which need to be reset and then caught up
|
||||
// note the funky loop logic -- first time through obj is us, then
|
||||
// we start iterating through nearby list (to look for objects nearby
|
||||
// the nearby objects), which is why index starts at -1
|
||||
// [objects nearby the nearby objects also get added to the nearby list]
|
||||
for (S32 i=-1; obj; obj = ++i<nearby.mList.size() ? (GameBase*)nearby.mList[i] : NULL)
|
||||
{
|
||||
if (obj->isGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType) && !obj->isNetNearbyAdded())
|
||||
{
|
||||
Point3F start = obj->getWorldSphere().center;
|
||||
Point3F end = start + 1.1f * dt * obj->getVelocity();
|
||||
F32 rad = 1.5f * obj->getWorldSphere().radius;
|
||||
|
||||
// find nearby items not updated but are hi fi, mark them as updated (and restore old loc)
|
||||
// check to see if added items have neighbors that need updating
|
||||
Box3F box;
|
||||
Point3F rads(rad,rad,rad);
|
||||
box.minExtents = box.maxExtents = start;
|
||||
box.minExtents -= bigDelta + rads;
|
||||
box.maxExtents += bigDelta + rads;
|
||||
|
||||
// CodeReview - this is left in for MBU, but also so we can deal with the issue later.
|
||||
// add marble blast hack so hifi networking can see hidden objects
|
||||
// (since hidden is under control of hifi networking)
|
||||
// gForceNotHidden = true;
|
||||
|
||||
S32 j = nearby.mList.size();
|
||||
gClientContainer.findObjects(box, GameBaseHiFiObjectType, SimpleQueryList::insertionCallback, &nearby);
|
||||
|
||||
// CodeReview - this is left in for MBU, but also so we can deal with the issue later.
|
||||
// disable above hack
|
||||
// gForceNotHidden = false;
|
||||
|
||||
// drop anyone not heading toward us or already checked
|
||||
for (; j<nearby.mList.size(); j++)
|
||||
{
|
||||
GameBase * obj2 = (GameBase*)nearby.mList[j];
|
||||
// if both passive, these guys don't interact with each other
|
||||
bool passive = obj->isHifiPassive() && obj2->isHifiPassive();
|
||||
if (!obj2->isGhostUpdated() && !passive)
|
||||
{
|
||||
// compare swept spheres of obj and obj2
|
||||
// if collide, reset obj2, setGhostUpdated(true), and continue
|
||||
Point3F end2 = obj2->getWorldSphere().center;
|
||||
Point3F start2 = end2 - 1.1f * dt * obj2->getVelocity();
|
||||
F32 rad2 = 1.5f * obj->getWorldSphere().radius;
|
||||
if (MathUtils::capsuleCapsuleOverlap(start,end,rad,start2,end2,rad2))
|
||||
{
|
||||
// better add obj2
|
||||
obj2->getTickCache().beginCacheList();
|
||||
TickCacheEntry * tce = obj2->getTickCache().incCacheList();
|
||||
BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
|
||||
obj2->readPacketData(connection,&bs);
|
||||
obj2->setGhostUpdated(true);
|
||||
|
||||
// continue so we later add the neighbors too
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// didn't pass above test...so don't add it or nearby objects
|
||||
nearby.mList[j] = nearby.mList.last();
|
||||
nearby.mList.decrement();
|
||||
j--;
|
||||
}
|
||||
obj->setNetNearbyAdded(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save water mark -- for game base list
|
||||
FrameAllocatorMarker mark;
|
||||
|
||||
// build ordered list of client objects which need to be caught up
|
||||
GameBaseListNode list;
|
||||
for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next)
|
||||
{
|
||||
GameBase *obj = getGameBase( pobj );
|
||||
//GameBase *obj = dynamic_cast<GameBase*>( pobj );
|
||||
//GameBase *obj = (GameBase*)pobj;
|
||||
|
||||
// Not a GameBase object so nothing to do.
|
||||
if ( !obj )
|
||||
continue;
|
||||
|
||||
if (obj->isGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType))
|
||||
{
|
||||
// construct process object and add it to the list
|
||||
// hold pointer to our object in mAfterObject
|
||||
GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode));
|
||||
po->mObject = obj;
|
||||
po->linkBefore(&list);
|
||||
|
||||
// begin iterating through tick list (skip first tick since that is the state we've been reset to)
|
||||
obj->getTickCache().beginCacheList();
|
||||
obj->getTickCache().incCacheList();
|
||||
}
|
||||
else if (mForceHifiReset && (obj->getTypeMask() & GameBaseHiFiObjectType))
|
||||
{
|
||||
// add all hifi objects
|
||||
obj->getTickCache().beginCacheList();
|
||||
TickCacheEntry * tce = obj->getTickCache().incCacheList();
|
||||
BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
|
||||
obj->readPacketData(connection,&bs);
|
||||
obj->setGhostUpdated(true);
|
||||
|
||||
// construct process object and add it to the list
|
||||
// hold pointer to our object in mAfterObject
|
||||
GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode));
|
||||
po->mObject = obj;
|
||||
po->linkBefore(&list);
|
||||
}
|
||||
else if (obj == connection->getControlObject() && obj->isGhostUpdated())
|
||||
{
|
||||
// construct process object and add it to the list
|
||||
// hold pointer to our object in mAfterObject
|
||||
// .. but this is not a hi fi object, so don't mess with tick cache
|
||||
GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode));
|
||||
po->mObject = obj;
|
||||
po->linkBefore(&list);
|
||||
}
|
||||
else if (obj->isGhostUpdated())
|
||||
{
|
||||
// not hifi but we were updated, so perform net smooth now
|
||||
obj->computeNetSmooth(mLastDelta);
|
||||
}
|
||||
|
||||
// clear out work flags
|
||||
obj->setNetNearbyAdded(false);
|
||||
obj->setGhostUpdated(false);
|
||||
}
|
||||
|
||||
// run through all the moves in the move list so we can play them with our control object
|
||||
Move* movePtr;
|
||||
U32 numMoves;
|
||||
connection->mMoveList->resetClientMoves();
|
||||
connection->mMoveList->getMoves(&movePtr, &numMoves);
|
||||
AssertFatal(mCatchup<=numMoves,"doh");
|
||||
|
||||
// tick catchup time
|
||||
for (U32 m=0; m<mCatchup; m++)
|
||||
{
|
||||
for (GameBaseListNode * walk = list.mNext; walk != &list; walk = walk->mNext)
|
||||
{
|
||||
// note that we get object from after object not getGameBase function
|
||||
// this is because we are an on the fly linked list which uses mAfterObject
|
||||
// rather than the linked list embedded in GameBase (clean this up?)
|
||||
GameBase * obj = walk->mObject;
|
||||
|
||||
// it's possible for a non-hifi object to get in here, but
|
||||
// only if it is a control object...make sure we don't do any
|
||||
// of the tick cache stuff if we are not hifi.
|
||||
bool hifi = obj->getTypeMask() & GameBaseHiFiObjectType;
|
||||
TickCacheEntry * tce = hifi ? obj->getTickCache().incCacheList() : NULL;
|
||||
|
||||
// tick object
|
||||
if (obj==connection->getControlObject())
|
||||
{
|
||||
obj->processTick(movePtr);
|
||||
movePtr->checksum = obj->getPacketDataChecksum(connection);
|
||||
movePtr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertFatal(tce && hifi,"Should not get in here unless a hi fi object!!!");
|
||||
obj->processTick(tce->move);
|
||||
}
|
||||
|
||||
if (hifi)
|
||||
{
|
||||
BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
|
||||
obj->writePacketData(connection,&bs);
|
||||
}
|
||||
}
|
||||
if (connection->getControlObject() == NULL)
|
||||
movePtr++;
|
||||
}
|
||||
connection->mMoveList->clearMoves(mCatchup);
|
||||
|
||||
// Handle network error smoothing here...but only for control object
|
||||
GameBase * control = connection->getControlObject();
|
||||
if (control && !control->isNewGhost())
|
||||
{
|
||||
control->computeNetSmooth(mLastDelta);
|
||||
control->setNewGhost(false);
|
||||
}
|
||||
|
||||
if (moveSync.doAction() && moveSync.moveDiff>0)
|
||||
{
|
||||
S32 moveDiff = moveSync.moveDiff;
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("client timewarping to catchup %i moves",moveDiff);
|
||||
#endif
|
||||
while (moveDiff--)
|
||||
advanceObjects();
|
||||
moveSync.reset();
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("---------");
|
||||
#endif
|
||||
|
||||
// all caught up
|
||||
mCatchup = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// HifiServerProcessList
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void HifiServerProcessList::onTickObject(ProcessObject * pobj)
|
||||
{
|
||||
// Each object is advanced a single tick
|
||||
// If it's controlled by a client, tick using a move.
|
||||
|
||||
Move *movePtr;
|
||||
U32 numMoves;
|
||||
GameConnection *con = pobj->getControllingClient();
|
||||
SimObjectPtr<GameBase> obj = getGameBase( pobj );
|
||||
|
||||
if ( obj && con && con->getControlObject() == obj && con->mMoveList->getMoves( &movePtr, &numMoves ) )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() );
|
||||
#endif
|
||||
|
||||
obj->processTick(movePtr);
|
||||
|
||||
if ( bool(obj) && obj->getControllingClient() )
|
||||
{
|
||||
U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() );
|
||||
|
||||
// check move checksum
|
||||
if ( movePtr->checksum != newsum )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
if ( !obj->mIsAiControlled )
|
||||
Con::printf( "move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)",
|
||||
movePtr->id, movePtr->checksum, newsum, sum, movePtr->yaw, movePtr->y, movePtr->z );
|
||||
#endif
|
||||
movePtr->checksum = Move::ChecksumMismatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf( "move %i checksum agree: %i == %i, (start %i), (move %f %f %f)",
|
||||
movePtr->id, movePtr->checksum, newsum, sum, movePtr->yaw, movePtr->y, movePtr->z );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Adding this seems to fix constant corrections, but is it
|
||||
// really a sound fix?
|
||||
con->mMoveList->clearMoves( 1 );
|
||||
}
|
||||
}
|
||||
else if ( pobj->isTicking() )
|
||||
pobj->processTick( 0 );
|
||||
}
|
||||
91
Engine/source/T3D/gameBase/hifi/hifiGameProcess.h
Normal file
91
Engine/source/T3D/gameBase/hifi/hifiGameProcess.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMEPROCESS_HIFI_H_
|
||||
#define _GAMEPROCESS_HIFI_H_
|
||||
|
||||
#ifndef _GAMEPROCESS_H_
|
||||
#include "T3D/gameBase/gameProcess.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// List to keep track of GameBases to process.
|
||||
class HifiClientProcessList : public ClientProcessList
|
||||
{
|
||||
typedef ClientProcessList Parent;
|
||||
friend class HifiMoveList;
|
||||
|
||||
public:
|
||||
|
||||
HifiClientProcessList();
|
||||
|
||||
// ProcessList
|
||||
bool advanceTime(SimTime timeDelta);
|
||||
|
||||
// ClientProcessList
|
||||
void clientCatchup(GameConnection*);
|
||||
|
||||
static void init();
|
||||
static void shutdown();
|
||||
|
||||
protected:
|
||||
|
||||
// tick cache functions -- client only
|
||||
void ageTickCache(S32 numToAge, S32 len);
|
||||
void forceHifiReset(bool reset) { mForceHifiReset=reset; }
|
||||
U32 getTotalTicks() { return mTotalTicks; }
|
||||
void updateMoveSync(S32 moveDiff);
|
||||
void skipAdvanceObjects(U32 ms) { mSkipAdvanceObjectsMs += ms; }
|
||||
|
||||
// ProcessList
|
||||
void onTickObject(ProcessObject *);
|
||||
void advanceObjects();
|
||||
void onAdvanceObjects();
|
||||
|
||||
void setCatchup(U32 catchup) { mCatchup = catchup; }
|
||||
|
||||
protected:
|
||||
|
||||
U32 mSkipAdvanceObjectsMs;
|
||||
bool mForceHifiReset;
|
||||
U32 mCatchup;
|
||||
};
|
||||
|
||||
|
||||
class HifiServerProcessList : public ServerProcessList
|
||||
{
|
||||
typedef ServerProcessList Parent;
|
||||
|
||||
public:
|
||||
|
||||
HifiServerProcessList() {}
|
||||
|
||||
static void init();
|
||||
static void shutdown();
|
||||
|
||||
protected:
|
||||
|
||||
// ProcessList
|
||||
void onTickObject(ProcessObject *);
|
||||
};
|
||||
|
||||
#endif // _GAMEPROCESS_HIFI_H_
|
||||
443
Engine/source/T3D/gameBase/hifi/hifiMoveList.cpp
Normal file
443
Engine/source/T3D/gameBase/hifi/hifiMoveList.cpp
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/gameBase/hifi/hifiMoveList.h"
|
||||
#include "T3D/gameBase/hifi/hifiGameProcess.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
|
||||
#define MAX_MOVE_PACKET_SENDS 4
|
||||
|
||||
const U32 DefaultTargetMoveListSize = 3;
|
||||
const U32 DefaultMaxMoveSizeList = 5;
|
||||
const F32 DefaultSmoothMoveAvg = 0.15f;
|
||||
const F32 DefaultMoveListSizeSlack = 1.0f;
|
||||
|
||||
HifiMoveList::HifiMoveList()
|
||||
{
|
||||
mLastSentMove = 0;
|
||||
mAvgMoveQueueSize = DefaultTargetMoveListSize ;
|
||||
mTargetMoveListSize = DefaultTargetMoveListSize;
|
||||
mMaxMoveListSize = DefaultMaxMoveSizeList;
|
||||
mSmoothMoveAvg = DefaultSmoothMoveAvg;
|
||||
mMoveListSizeSlack = DefaultMoveListSizeSlack;
|
||||
mTotalServerTicks = ServerTicksUninitialized;
|
||||
mSuppressMove = false;
|
||||
}
|
||||
|
||||
void HifiMoveList::updateClientServerTickDiff(S32 & tickDiff)
|
||||
{
|
||||
if (mLastMoveAck==0)
|
||||
tickDiff=0;
|
||||
|
||||
// Make adjustments to move list to account for tick mis-matches between client and server.
|
||||
if (tickDiff>0)
|
||||
{
|
||||
// Server ticked more than client. Adjust for this by reseting all hifi objects
|
||||
// to a later position in the tick cache (see ageTickCache below) and at the same
|
||||
// time pulling back some moves we thought we had made (so that time on client
|
||||
// doesn't change).
|
||||
S32 dropTicks = tickDiff;
|
||||
while (dropTicks)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("dropping move%s",mLastClientMove>mFirstMoveIndex ? "" : " but none there");
|
||||
#endif
|
||||
if (mLastClientMove>mFirstMoveIndex)
|
||||
mLastClientMove--;
|
||||
else
|
||||
tickDiff--;
|
||||
dropTicks--;
|
||||
}
|
||||
AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request");
|
||||
AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveVec.size(), "Desynched first and last move.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client ticked more than server. Adjust for this by taking extra moves
|
||||
// (either adding back moves that were dropped above, or taking new ones).
|
||||
for (S32 i=0; i<-tickDiff; i++)
|
||||
{
|
||||
if (mMoveVec.size() > mLastClientMove - mFirstMoveIndex)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("add back move");
|
||||
#endif
|
||||
mLastClientMove++;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("add back move -- create one");
|
||||
#endif
|
||||
collectMove();
|
||||
mLastClientMove++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// drop moves that are not made yet (because we rolled them back) and not yet sent
|
||||
U32 len = getMax(mLastClientMove-mFirstMoveIndex,mLastSentMove-mFirstMoveIndex);
|
||||
mMoveVec.setSize(len);
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("move list size: %i, last move: %i, last sent: %i",mMoveVec.size(),mLastClientMove-mFirstMoveIndex,mLastSentMove-mFirstMoveIndex);
|
||||
#endif
|
||||
}
|
||||
|
||||
S32 HifiMoveList::getServerTicks(U32 serverTickNum)
|
||||
{
|
||||
S32 serverTicks=0;
|
||||
if (serverTicksInitialized())
|
||||
{
|
||||
// handle tick wrapping...
|
||||
const S32 MaxTickCount = (1<<TotalTicksBits);
|
||||
const S32 HalfMaxTickCount = MaxTickCount>>1;
|
||||
U32 prevTickNum = mTotalServerTicks & TotalTicksMask;
|
||||
serverTicks = serverTickNum-prevTickNum;
|
||||
if (serverTicks>HalfMaxTickCount)
|
||||
serverTicks -= MaxTickCount;
|
||||
else if (-serverTicks>HalfMaxTickCount)
|
||||
serverTicks += MaxTickCount;
|
||||
AssertFatal(serverTicks>=0,"Server can't tick backwards!!!");
|
||||
if (serverTicks<0)
|
||||
serverTicks=0;
|
||||
}
|
||||
mTotalServerTicks = serverTickNum;
|
||||
return serverTicks;
|
||||
}
|
||||
|
||||
void HifiMoveList::markControlDirty()
|
||||
{
|
||||
mLastClientMove = mLastMoveAck;
|
||||
|
||||
// save state for future update
|
||||
GameBase *obj = mConnection->getControlObject();
|
||||
AssertFatal(obj,"ClientProcessList::markControlDirty: no control object");
|
||||
obj->setGhostUpdated(true);
|
||||
obj->getTickCache().beginCacheList();
|
||||
TickCacheEntry * tce = obj->getTickCache().incCacheList();
|
||||
BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
|
||||
obj->writePacketData( mConnection, &bs );
|
||||
}
|
||||
|
||||
void HifiMoveList::resetMoveList()
|
||||
{
|
||||
mMoveVec.clear();
|
||||
mLastMoveAck = 0;
|
||||
mLastClientMove = 0;
|
||||
mFirstMoveIndex = 0;
|
||||
mLastSentMove = 0;
|
||||
}
|
||||
|
||||
U32 HifiMoveList::getMoves(Move** movePtr,U32* numMoves)
|
||||
{
|
||||
if (mConnection->isConnectionToServer())
|
||||
// give back moves starting at the last client move...
|
||||
return Parent::getMoves(movePtr,numMoves);
|
||||
|
||||
if (mSuppressMove || mMoveVec.size()==0)
|
||||
{
|
||||
*numMoves=0;
|
||||
*movePtr=NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*numMoves=1;
|
||||
*movePtr=mMoveVec.begin();
|
||||
}
|
||||
|
||||
return *numMoves;
|
||||
}
|
||||
|
||||
void HifiMoveList::advanceMove()
|
||||
{
|
||||
S32 numMoves = mMoveVec.size();
|
||||
mAvgMoveQueueSize *= (1.0f-mSmoothMoveAvg);
|
||||
mAvgMoveQueueSize += mSmoothMoveAvg * F32(numMoves);
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("moves remaining: %i, running avg: %f",numMoves,mAvgMoveQueueSize);
|
||||
#endif
|
||||
|
||||
if (mAvgMoveQueueSize<mTargetMoveListSize-mMoveListSizeSlack && numMoves<mTargetMoveListSize && numMoves)
|
||||
{
|
||||
numMoves=0;
|
||||
mAvgMoveQueueSize = (F32)getMax(S32(mAvgMoveQueueSize + mMoveListSizeSlack + 0.5f),numMoves);
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("too few moves on server, padding with null move");
|
||||
#endif
|
||||
}
|
||||
if (numMoves)
|
||||
numMoves=1;
|
||||
|
||||
if ( mMoveVec.size()>mMaxMoveListSize || (mAvgMoveQueueSize>mTargetMoveListSize+mMoveListSizeSlack && mMoveVec.size()>mTargetMoveListSize) )
|
||||
{
|
||||
U32 drop = mMoveVec.size()-mTargetMoveListSize;
|
||||
clearMoves(drop);
|
||||
mAvgMoveQueueSize = (F32)mTargetMoveListSize;
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("too many moves on server, dropping moves (%i)",drop);
|
||||
#endif
|
||||
}
|
||||
|
||||
mSuppressMove = numMoves == 0;
|
||||
|
||||
// now clear move
|
||||
if (areMovesPending())
|
||||
clearMoves(1);
|
||||
}
|
||||
|
||||
void HifiMoveList::clientWriteMovePacket(BitStream *bstream)
|
||||
{
|
||||
if (!serverTicksInitialized())
|
||||
resetMoveList();
|
||||
|
||||
AssertFatal(mLastMoveAck == mFirstMoveIndex, "Invalid move index.");
|
||||
|
||||
// enforce limit on number of moves sent
|
||||
if (mLastSentMove<mFirstMoveIndex)
|
||||
mLastSentMove=mFirstMoveIndex;
|
||||
U32 count = mLastSentMove-mFirstMoveIndex;
|
||||
|
||||
Move * move = mMoveVec.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);
|
||||
Move * prevMove = NULL;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
move[offset + i].sendCount++;
|
||||
move[offset + i].pack(bstream,prevMove);
|
||||
bstream->writeInt(move[offset + i].checksum,Move::ChecksumBits);
|
||||
prevMove = &move[offset+i];
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMoveList::serverReadMovePacket(BitStream *bstream)
|
||||
{
|
||||
// Server side packet read.
|
||||
U32 start = bstream->readInt(32);
|
||||
U32 count = bstream->readInt(MoveCountBits);
|
||||
|
||||
Move * prevMove = NULL;
|
||||
Move prevMoveHolder;
|
||||
|
||||
// Skip forward (must be starting up), or over the moves
|
||||
// we already have.
|
||||
int skip = mLastMoveAck - start;
|
||||
if (skip < 0)
|
||||
{
|
||||
mLastMoveAck = start;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (skip > count)
|
||||
skip = count;
|
||||
for (int i = 0; i < skip; i++)
|
||||
{
|
||||
prevMoveHolder.unpack(bstream,prevMove);
|
||||
prevMoveHolder.checksum = bstream->readInt(Move::ChecksumBits);
|
||||
prevMove = &prevMoveHolder;
|
||||
S32 idx = mMoveVec.size()-skip+i;
|
||||
if (idx>=0)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
if (mMoveVec[idx].checksum != prevMoveHolder.checksum)
|
||||
Con::printf("updated checksum on move %i from %i to %i",mMoveVec[idx].id,mMoveVec[idx].checksum,prevMoveHolder.checksum);
|
||||
#endif
|
||||
mMoveVec[idx].checksum = prevMoveHolder.checksum;
|
||||
}
|
||||
}
|
||||
start += skip;
|
||||
count = count - skip;
|
||||
}
|
||||
|
||||
// Put the rest on the move list.
|
||||
int index = mMoveVec.size();
|
||||
mMoveVec.increment(count);
|
||||
while (index < mMoveVec.size())
|
||||
{
|
||||
mMoveVec[index].unpack(bstream,prevMove);
|
||||
mMoveVec[index].checksum = bstream->readInt(Move::ChecksumBits);
|
||||
prevMove = &mMoveVec[index];
|
||||
mMoveVec[index].id = start++;
|
||||
index ++;
|
||||
}
|
||||
|
||||
mLastMoveAck += count;
|
||||
|
||||
if (mMoveVec.size()>mMaxMoveListSize)
|
||||
{
|
||||
U32 drop = mMoveVec.size()-mTargetMoveListSize;
|
||||
clearMoves(drop);
|
||||
mAvgMoveQueueSize = (F32)mTargetMoveListSize;
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("too many moves on server, dropping moves (%i)",drop);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMoveList::serverWriteMovePacket(BitStream * bstream)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("ack %i minus %i",mLastMoveAck,mMoveVec.size());
|
||||
#endif
|
||||
|
||||
// acknowledge only those moves that have been ticked
|
||||
bstream->writeInt(mLastMoveAck - mMoveVec.size(),32);
|
||||
|
||||
// send over the current tick count on the server...
|
||||
bstream->writeInt(ServerProcessList::get()->getTotalTicks() & TotalTicksMask, TotalTicksBits);
|
||||
}
|
||||
|
||||
void HifiMoveList::clientReadMovePacket(BitStream * bstream)
|
||||
{
|
||||
if (!serverTicksInitialized())
|
||||
resetMoveList();
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("pre move ack: %i", mLastMoveAck);
|
||||
#endif
|
||||
|
||||
mLastMoveAck = bstream->readInt(32);
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("post move ack %i, first move %i, last move %i", mLastMoveAck, mFirstMoveIndex, mLastClientMove);
|
||||
#endif
|
||||
|
||||
// This is how many times we've ticked since last ack -- before adjustments below
|
||||
S32 ourTicks = mLastMoveAck - mFirstMoveIndex;
|
||||
|
||||
if (mLastMoveAck < mFirstMoveIndex)
|
||||
mLastMoveAck = mFirstMoveIndex;
|
||||
|
||||
if(mLastMoveAck > mLastClientMove)
|
||||
{
|
||||
ourTicks -= mLastMoveAck-mLastClientMove;
|
||||
mLastClientMove = mLastMoveAck;
|
||||
}
|
||||
while(mFirstMoveIndex < mLastMoveAck)
|
||||
{
|
||||
if (mMoveVec.size())
|
||||
{
|
||||
mMoveVec.pop_front();
|
||||
mFirstMoveIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertWarn(1, "Popping off too many moves!");
|
||||
mFirstMoveIndex = mLastMoveAck;
|
||||
}
|
||||
}
|
||||
|
||||
// get server ticks using total number of ticks on server to date...
|
||||
U32 serverTickNum = bstream->readInt(TotalTicksBits);
|
||||
S32 serverTicks = getServerTicks(serverTickNum);
|
||||
S32 tickDiff = serverTicks - ourTicks;
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("server ticks: %i, client ticks: %i, diff: %i%s", serverTicks, ourTicks, tickDiff, !tickDiff ? "" : " (ticks mis-match)");
|
||||
#endif
|
||||
|
||||
|
||||
// Apply the first (of two) client-side synchronization mechanisms. Key is that
|
||||
// we need to both synchronize client/server move streams (so first move in list is made
|
||||
// at same "time" on both client and server) and maintain the "time" at which the most
|
||||
// recent move was made on the server. In both cases, "time" is the number of ticks
|
||||
// it took to get to that move.
|
||||
updateClientServerTickDiff(tickDiff);
|
||||
|
||||
// Apply the second (and final) client-side synchronization mechanism. The tickDiff adjustments above
|
||||
// make sure time is preserved on client. But that assumes that a future (or previous) update will adjust
|
||||
// time in the other direction, so that we don't get too far behind or ahead of the server. The updateMoveSync
|
||||
// mechanism tracks us over time to make sure we eventually return to be in sync, and makes adjustments
|
||||
// if we don't after a certain time period (number of updates). Unlike the tickDiff mechanism, when
|
||||
// the updateMoveSync acts time is not preserved on the client.
|
||||
HifiClientProcessList * processList = dynamic_cast<HifiClientProcessList*>(ClientProcessList::get());
|
||||
if (processList)
|
||||
{
|
||||
processList->updateMoveSync(mLastSentMove-mLastClientMove);
|
||||
|
||||
// set catchup parameters...
|
||||
U32 totalCatchup = mLastClientMove - mFirstMoveIndex;
|
||||
|
||||
processList->ageTickCache(ourTicks + (tickDiff>0 ? tickDiff : 0), totalCatchup+1);
|
||||
processList->forceHifiReset(tickDiff!=0);
|
||||
processList->setCatchup(totalCatchup);
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMoveList::ghostPreRead(NetObject * nobj, bool newGhost)
|
||||
{
|
||||
GameBase* obj = dynamic_cast<GameBase*>(nobj);
|
||||
if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) && !newGhost )
|
||||
{
|
||||
// set next cache entry to start
|
||||
obj->getTickCache().beginCacheList();
|
||||
|
||||
// reset to old state because we are about to unpack (and then tick forward)
|
||||
TickCacheEntry * tce = obj->getTickCache().incCacheList(false);
|
||||
if (tce)
|
||||
{
|
||||
BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
|
||||
obj->readPacketData(mConnection, &bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HifiMoveList::ghostReadExtra(NetObject * nobj, BitStream * bstream, bool newGhost)
|
||||
{
|
||||
// Receive additional per ghost information.
|
||||
// Get pending moves for ghosts that have them and add the moves to
|
||||
// the tick cache.
|
||||
GameBase* obj = dynamic_cast<GameBase*>(nobj);
|
||||
if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) )
|
||||
{
|
||||
// mark ghost so that it updates correctly
|
||||
obj->setGhostUpdated(true);
|
||||
obj->setNewGhost(newGhost);
|
||||
|
||||
// set next cache entry to start
|
||||
obj->getTickCache().beginCacheList();
|
||||
|
||||
// save state for future update
|
||||
TickCacheEntry * tce = obj->getTickCache().incCacheList();
|
||||
BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
|
||||
obj->writePacketData(mConnection, &bs);
|
||||
}
|
||||
}
|
||||
76
Engine/source/T3D/gameBase/hifi/hifiMoveList.h
Normal file
76
Engine/source/T3D/gameBase/hifi/hifiMoveList.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MOVELIST_HIFI_H_
|
||||
#define _MOVELIST_HIFI_H_
|
||||
|
||||
#ifndef _MOVELIST_H_
|
||||
#include "T3D/gameBase/moveList.h"
|
||||
#endif
|
||||
|
||||
class HifiMoveList : public MoveList
|
||||
{
|
||||
typedef MoveList Parent;
|
||||
|
||||
public:
|
||||
|
||||
HifiMoveList();
|
||||
|
||||
void init() { mTotalServerTicks = ServerTicksUninitialized; }
|
||||
|
||||
void ghostReadExtra(NetObject *,BitStream *, bool newGhost);
|
||||
void ghostPreRead(NetObject *, bool newGhost);
|
||||
|
||||
void clientWriteMovePacket(BitStream *bstream);
|
||||
void clientReadMovePacket(BitStream *);
|
||||
void serverWriteMovePacket(BitStream *);
|
||||
void serverReadMovePacket(BitStream *bstream);
|
||||
|
||||
void markControlDirty();
|
||||
U32 getMoves(Move**,U32* numMoves);
|
||||
void onAdvanceObjects() { if (mMoveVec.size() > mLastSentMove-mFirstMoveIndex) mLastSentMove++; }
|
||||
|
||||
void advanceMove();
|
||||
|
||||
protected:
|
||||
void resetMoveList();
|
||||
S32 getServerTicks(U32 serverTickNum);
|
||||
void updateClientServerTickDiff(S32 & tickDiff);
|
||||
bool serverTicksInitialized() { return mTotalServerTicks!=ServerTicksUninitialized; }
|
||||
|
||||
protected:
|
||||
U32 mLastSentMove;
|
||||
F32 mAvgMoveQueueSize;
|
||||
|
||||
// server side move list management
|
||||
U32 mTargetMoveListSize; // Target size of move buffer on server
|
||||
U32 mMaxMoveListSize; // Max size move buffer allowed to grow to
|
||||
F32 mSmoothMoveAvg; // Smoothing parameter for move list size running average
|
||||
F32 mMoveListSizeSlack; // Amount above/below target size move list running average allowed to diverge
|
||||
bool mSuppressMove; // If true, don't return move on server
|
||||
|
||||
// client side tracking of server ticks
|
||||
enum { TotalTicksBits=10, TotalTicksMask = (1<<TotalTicksBits)-1, ServerTicksUninitialized=0xFFFFFFFF };
|
||||
U32 mTotalServerTicks;
|
||||
};
|
||||
|
||||
#endif // _MOVELIST_HIFI_H_
|
||||
223
Engine/source/T3D/gameBase/moveList.cpp
Normal file
223
Engine/source/T3D/gameBase/moveList.cpp
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/gameBase/moveList.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
|
||||
MoveList::MoveList()
|
||||
{
|
||||
mControlMismatch = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
void MoveList::reset()
|
||||
{
|
||||
mLastMoveAck = 0;
|
||||
mLastClientMove = 0;
|
||||
mFirstMoveIndex = 0;
|
||||
mMoveVec.clear();
|
||||
}
|
||||
|
||||
bool MoveList::getNextMove(Move &curMove)
|
||||
{
|
||||
if ( mMoveVec.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 + MoveManager::mXAxis_L;
|
||||
curMove.y = MoveManager::mForwardAction - MoveManager::mBackwardAction + MoveManager::mYAxis_L;
|
||||
curMove.z = MoveManager::mUpAction - MoveManager::mDownAction;
|
||||
|
||||
curMove.freeLook = MoveManager::mFreeLook;
|
||||
curMove.deviceIsKeyboardMouse = MoveManager::mDeviceIsKeyboardMouse;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
if (mConnection->getControlObject())
|
||||
mConnection->getControlObject()->preprocessMove(&curMove);
|
||||
|
||||
curMove.clamp(); // clamp for net traffic
|
||||
return true;
|
||||
}
|
||||
|
||||
void MoveList::pushMove(const Move &mv)
|
||||
{
|
||||
U32 id = mFirstMoveIndex + mMoveVec.size();
|
||||
U32 sz = mMoveVec.size();
|
||||
mMoveVec.push_back(mv);
|
||||
mMoveVec[sz].id = id;
|
||||
mMoveVec[sz].sendCount = 0;
|
||||
}
|
||||
|
||||
U32 MoveList::getMoves(Move** movePtr,U32* numMoves)
|
||||
{
|
||||
if (mConnection->isConnectionToServer())
|
||||
{
|
||||
// give back moves starting at the last client move...
|
||||
|
||||
AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request");
|
||||
AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveVec.size(), "Desynched first and last move.");
|
||||
*numMoves = mMoveVec.size() - mLastClientMove + mFirstMoveIndex;
|
||||
*movePtr = mMoveVec.address() + mLastClientMove - mFirstMoveIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// return the full list
|
||||
*numMoves = mMoveVec.size();
|
||||
*movePtr = mMoveVec.begin();
|
||||
}
|
||||
|
||||
return *numMoves;
|
||||
}
|
||||
|
||||
void MoveList::collectMove()
|
||||
{
|
||||
Move mv;
|
||||
if (mConnection)
|
||||
{
|
||||
if(!mConnection->isPlayingBack() && getNextMove(mv))
|
||||
{
|
||||
mv.checksum=Move::ChecksumMismatch;
|
||||
pushMove(mv);
|
||||
mConnection->recordBlock(GameConnection::BlockTypeMove, sizeof(Move), &mv);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(getNextMove(mv))
|
||||
{
|
||||
mv.checksum=Move::ChecksumMismatch;
|
||||
pushMove(mv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoveList::clearMoves(U32 count)
|
||||
{
|
||||
if (mConnection->isConnectionToServer())
|
||||
{
|
||||
mLastClientMove += count;
|
||||
AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request");
|
||||
AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveVec.size(), "Desynched first and last move.");
|
||||
if (!mConnection)
|
||||
// drop right away if no connection
|
||||
ackMoves(count);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertFatal(count <= mMoveVec.size(),"GameConnection: Clearing too many moves");
|
||||
for (S32 i=0; i<count; i++)
|
||||
if (mMoveVec[i].checksum == Move::ChecksumMismatch)
|
||||
mControlMismatch = true;
|
||||
else
|
||||
mControlMismatch = false;
|
||||
if (count == mMoveVec.size())
|
||||
mMoveVec.clear();
|
||||
else
|
||||
while (count--)
|
||||
mMoveVec.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
bool MoveList::areMovesPending()
|
||||
{
|
||||
return mConnection->isConnectionToServer() ?
|
||||
mMoveVec.size() - mLastClientMove + mFirstMoveIndex :
|
||||
mMoveVec.size();
|
||||
}
|
||||
|
||||
bool MoveList::isBacklogged()
|
||||
{
|
||||
if ( !mConnection->isConnectionToServer() )
|
||||
return false;
|
||||
|
||||
return mLastClientMove - mFirstMoveIndex == mMoveVec.size() &&
|
||||
mMoveVec.size() >= MaxMoveCount;
|
||||
}
|
||||
|
||||
void MoveList::ackMoves(U32 count)
|
||||
{
|
||||
mLastMoveAck += count;
|
||||
if(mLastMoveAck > mLastClientMove)
|
||||
mLastClientMove = mLastMoveAck;
|
||||
while(mFirstMoveIndex < mLastMoveAck)
|
||||
{
|
||||
if (mMoveVec.size())
|
||||
{
|
||||
mMoveVec.pop_front();
|
||||
mFirstMoveIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertWarn(1, "Popping off too many moves!");
|
||||
mFirstMoveIndex = mLastMoveAck;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoveList::writeDemoStartBlock(ResizeBitStream *stream)
|
||||
{
|
||||
stream->write(mLastMoveAck);
|
||||
stream->write(mLastClientMove);
|
||||
stream->write(mFirstMoveIndex);
|
||||
|
||||
stream->write(U32(mMoveVec.size()));
|
||||
for(U32 j = 0; j < mMoveVec.size(); j++)
|
||||
mMoveVec[j].pack(stream);
|
||||
}
|
||||
|
||||
void MoveList::readDemoStartBlock(BitStream *stream)
|
||||
{
|
||||
stream->read(&mLastMoveAck);
|
||||
stream->read(&mLastClientMove);
|
||||
stream->read(&mFirstMoveIndex);
|
||||
|
||||
U32 size;
|
||||
Move mv;
|
||||
stream->read(&size);
|
||||
mMoveVec.clear();
|
||||
while(size--)
|
||||
{
|
||||
mv.unpack(stream);
|
||||
pushMove(mv);
|
||||
}
|
||||
}
|
||||
122
Engine/source/T3D/gameBase/moveList.h
Normal file
122
Engine/source/T3D/gameBase/moveList.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MOVELIST_H_
|
||||
#define _MOVELIST_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
#ifndef _MOVEMANAGER_H_
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#endif
|
||||
|
||||
class BitStream;
|
||||
class ResizeBitStream;
|
||||
class NetObject;
|
||||
class GameConnection;
|
||||
class PlayerRep;
|
||||
class ProcessList;
|
||||
|
||||
class MoveList
|
||||
{
|
||||
public:
|
||||
|
||||
MoveList();
|
||||
virtual ~MoveList() {}
|
||||
|
||||
virtual void init() {}
|
||||
|
||||
void setConnection( GameConnection *connection) { mConnection = connection; }
|
||||
|
||||
/// @name Move Packets
|
||||
/// Write/read move data to the packet.
|
||||
/// @{
|
||||
|
||||
virtual void ghostReadExtra( NetObject *, BitStream *, bool newGhost) {};
|
||||
virtual void ghostWriteExtra( NetObject *,BitStream * ) {};
|
||||
virtual void ghostPreRead( NetObject *, bool newGhost ) {};
|
||||
|
||||
virtual void clientWriteMovePacket( BitStream *bstream ) = 0;
|
||||
virtual void clientReadMovePacket( BitStream * ) = 0;
|
||||
|
||||
virtual void serverWriteMovePacket( BitStream * ) = 0;
|
||||
virtual void serverReadMovePacket( BitStream *bstream ) = 0;
|
||||
|
||||
virtual void writeDemoStartBlock( ResizeBitStream *stream );
|
||||
virtual void readDemoStartBlock( BitStream *stream );
|
||||
/// @}
|
||||
|
||||
virtual void advanceMove() = 0;
|
||||
virtual void onAdvanceObjects() = 0;
|
||||
virtual U32 getMoves( Move**, U32 *numMoves );
|
||||
|
||||
/// Reset to beginning of client move list.
|
||||
void resetClientMoves() { mLastClientMove = mFirstMoveIndex; }
|
||||
|
||||
/// Reset move list back to last acknowledged move.
|
||||
void resetCatchup() { mLastClientMove = mLastMoveAck; }
|
||||
|
||||
void collectMove();
|
||||
void pushMove( const Move &mv );
|
||||
virtual void clearMoves( U32 count );
|
||||
|
||||
virtual void markControlDirty() { mLastClientMove = mLastMoveAck; }
|
||||
bool isMismatch() { return mControlMismatch; }
|
||||
void clearMismatch() { mControlMismatch = false; }
|
||||
|
||||
/// Clear out all moves in the list and reset to initial state.
|
||||
void reset();
|
||||
|
||||
/// If there are no pending moves and the input queue is full,
|
||||
/// then the connection to the server must be clogged.
|
||||
bool isBacklogged();
|
||||
|
||||
bool areMovesPending();
|
||||
|
||||
void ackMoves( U32 count );
|
||||
|
||||
protected:
|
||||
|
||||
bool getNextMove( Move &curMove );
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
MoveCountBits = 5,
|
||||
/// MaxMoveCount should not exceed the MoveManager's
|
||||
/// own maximum (MaxMoveQueueSize)
|
||||
MaxMoveCount = 30,
|
||||
};
|
||||
|
||||
U32 mLastMoveAck;
|
||||
U32 mLastClientMove;
|
||||
U32 mFirstMoveIndex;
|
||||
bool mControlMismatch;
|
||||
|
||||
GameConnection *mConnection;
|
||||
|
||||
Vector<Move> mMoveVec;
|
||||
};
|
||||
|
||||
#endif // _MOVELIST_H_
|
||||
302
Engine/source/T3D/gameBase/moveManager.cpp
Normal file
302
Engine/source/T3D/gameBase/moveManager.cpp
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "core/module.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "math/mConstants.h"
|
||||
|
||||
|
||||
MODULE_BEGIN( MoveManager )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
MoveManager::init();
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
bool MoveManager::mDeviceIsKeyboardMouse = false;
|
||||
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;
|
||||
|
||||
F32 MoveManager::mXAxis_L = 0;
|
||||
F32 MoveManager::mYAxis_L = 0;
|
||||
F32 MoveManager::mXAxis_R = 0;
|
||||
F32 MoveManager::mYAxis_R = 0;
|
||||
|
||||
U32 MoveManager::mTriggerCount[MaxTriggerKeys] = { 0, };
|
||||
U32 MoveManager::mPrevTriggerCount[MaxTriggerKeys] = { 0, };
|
||||
|
||||
const Move NullMove =
|
||||
{
|
||||
/*px=*/16, /*py=*/16, /*pz=*/16,
|
||||
/*pyaw=*/0, /*ppitch=*/0, /*proll=*/0,
|
||||
/*x=*/0, /*y=*/0,/*z=*/0,
|
||||
/*yaw=*/0, /*pitch=*/0, /*roll=*/0,
|
||||
/*id=*/0,
|
||||
/*sendCount=*/0,
|
||||
|
||||
/*checksum=*/false,
|
||||
/*deviceIsKeyboardMouse=*/false,
|
||||
/*freeLook=*/false,
|
||||
/*triggers=*/{false,false,false,false,false,false}
|
||||
};
|
||||
|
||||
void MoveManager::init()
|
||||
{
|
||||
Con::addVariable("mvForwardAction", TypeF32, &mForwardAction,
|
||||
"Forwards movement speed for the active player.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvBackwardAction", TypeF32, &mBackwardAction,
|
||||
"Backwards movement speed for the active player.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvUpAction", TypeF32, &mUpAction,
|
||||
"Upwards movement speed for the active player.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvDownAction", TypeF32, &mDownAction,
|
||||
"Downwards movement speed for the active player.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvLeftAction", TypeF32, &mLeftAction,
|
||||
"Left movement speed for the active player.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvRightAction", TypeF32, &mRightAction,
|
||||
"Right movement speed for the active player.\n"
|
||||
"@ingroup Game");
|
||||
|
||||
Con::addVariable("mvFreeLook", TypeBool, &mFreeLook,
|
||||
"Boolean state for if freelook is active or not.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvDeviceIsKeyboardMouse", TypeBool, &mDeviceIsKeyboardMouse,
|
||||
"Boolean state for it the system is using a keyboard and mouse or not.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvPitch", TypeF32, &mPitch,
|
||||
"Current pitch value, typically applied through input devices, such as a mouse.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvYaw", TypeF32, &mYaw,
|
||||
"Current yaw value, typically applied through input devices, such as a mouse.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvRoll", TypeF32, &mRoll,
|
||||
"Current roll value, typically applied through input devices, such as a mouse.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvPitchUpSpeed", TypeF32, &mPitchUpSpeed,
|
||||
"Upwards pitch speed.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvPitchDownSpeed", TypeF32, &mPitchDownSpeed,
|
||||
"Downwards pitch speed.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvYawLeftSpeed", TypeF32, &mYawLeftSpeed,
|
||||
"Left Yaw speed.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvYawRightSpeed", TypeF32, &mYawRightSpeed,
|
||||
"Right Yaw speed.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvRollLeftSpeed", TypeF32, &mRollLeftSpeed,
|
||||
"Left roll speed.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("mvRollRightSpeed", TypeF32, &mRollRightSpeed,
|
||||
"Right roll speed.\n"
|
||||
"@ingroup Game");
|
||||
|
||||
// Dual-analog
|
||||
Con::addVariable( "mvXAxis_L", TypeF32, &mXAxis_L,
|
||||
"Left thumbstick X axis position on a dual-analog gamepad.\n"
|
||||
"@ingroup Game" );
|
||||
Con::addVariable( "mvYAxis_L", TypeF32, &mYAxis_L,
|
||||
"Left thumbstick Y axis position on a dual-analog gamepad.\n"
|
||||
"@ingroup Game" );
|
||||
|
||||
Con::addVariable( "mvXAxis_R", TypeF32, &mXAxis_R,
|
||||
"Right thumbstick X axis position on a dual-analog gamepad.\n"
|
||||
"@ingroup Game" );
|
||||
Con::addVariable( "mvYAxis_R", TypeF32, &mYAxis_R,
|
||||
"Right thumbstick Y axis position on a dual-analog gamepad.\n"
|
||||
"@ingroup Game");
|
||||
|
||||
for(U32 i = 0; i < MaxTriggerKeys; i++)
|
||||
{
|
||||
char varName[256];
|
||||
dSprintf(varName, sizeof(varName), "mvTriggerCount%d", i);
|
||||
Con::addVariable(varName, TypeS32, &mTriggerCount[i],
|
||||
"Used to determine the trigger counts of buttons. Namely used for input actions such as jumping and weapons firing.\n"
|
||||
"@ingroup Game");
|
||||
}
|
||||
}
|
||||
|
||||
static inline F32 clampFloatWrap(F32 val)
|
||||
{
|
||||
return val - F32(S32(val));
|
||||
}
|
||||
|
||||
static inline S32 clampRangeClamp(F32 val)
|
||||
{
|
||||
if(val < -1)
|
||||
return 0;
|
||||
if(val > 1)
|
||||
return 32;
|
||||
|
||||
// 0.5 / 16 = 0.03125 ... this forces a round up to
|
||||
// make the precision near zero equal in the negative
|
||||
// and positive directions. See...
|
||||
//
|
||||
// http://www.garagegames.com/community/forums/viewthread/49714
|
||||
|
||||
return (S32)((val + 1.03125) * 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);
|
||||
}
|
||||
|
||||
static inline F32 clampAngleClamp( F32 angle )
|
||||
{
|
||||
const F32 limit = ( M_PI_F / 180.0f ) * 179.999f;
|
||||
if ( angle < -limit )
|
||||
return -limit;
|
||||
if ( angle > limit )
|
||||
return limit;
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
void Move::clamp()
|
||||
{
|
||||
// If yaw/pitch/roll goes equal or greater than -PI/+PI it
|
||||
// flips the direction of the rotation... we protect against
|
||||
// that by clamping before the conversion.
|
||||
|
||||
yaw = clampAngleClamp( yaw );
|
||||
pitch = clampAngleClamp( pitch );
|
||||
roll = clampAngleClamp( roll );
|
||||
|
||||
// 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, const Move * basemove)
|
||||
{
|
||||
bool alwaysWriteAll = basemove!=NULL;
|
||||
if (!basemove)
|
||||
basemove = &NullMove;
|
||||
|
||||
S32 i;
|
||||
bool triggerDifferent = false;
|
||||
for (i=0; i < MaxTriggerKeys; i++)
|
||||
if (trigger[i] != basemove->trigger[i])
|
||||
triggerDifferent = true;
|
||||
bool somethingDifferent = (pyaw!=basemove->pyaw) ||
|
||||
(ppitch!=basemove->ppitch) ||
|
||||
(proll!=basemove->proll) ||
|
||||
(px!=basemove->px) ||
|
||||
(py!=basemove->py) ||
|
||||
(pz!=basemove->pz) ||
|
||||
(deviceIsKeyboardMouse!=basemove->deviceIsKeyboardMouse) ||
|
||||
(freeLook!=basemove->freeLook) ||
|
||||
triggerDifferent;
|
||||
|
||||
if (alwaysWriteAll || stream->writeFlag(somethingDifferent))
|
||||
{
|
||||
if(stream->writeFlag(pyaw != basemove->pyaw))
|
||||
stream->writeInt(pyaw, 16);
|
||||
if(stream->writeFlag(ppitch != basemove->ppitch))
|
||||
stream->writeInt(ppitch, 16);
|
||||
if(stream->writeFlag(proll != basemove->proll))
|
||||
stream->writeInt(proll, 16);
|
||||
|
||||
if (stream->writeFlag(px != basemove->px))
|
||||
stream->writeInt(px, 6);
|
||||
if (stream->writeFlag(py != basemove->py))
|
||||
stream->writeInt(py, 6);
|
||||
if (stream->writeFlag(pz != basemove->pz))
|
||||
stream->writeInt(pz, 6);
|
||||
stream->writeFlag(freeLook);
|
||||
stream->writeFlag(deviceIsKeyboardMouse);
|
||||
|
||||
if (stream->writeFlag(triggerDifferent))
|
||||
for(i = 0; i < MaxTriggerKeys; i++)
|
||||
stream->writeFlag(trigger[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Move::unpack(BitStream *stream, const Move * basemove)
|
||||
{
|
||||
bool alwaysReadAll = basemove!=NULL;
|
||||
if (!basemove)
|
||||
basemove=&NullMove;
|
||||
|
||||
if (alwaysReadAll || stream->readFlag())
|
||||
{
|
||||
pyaw = stream->readFlag() ? stream->readInt(16) : basemove->pyaw;
|
||||
ppitch = stream->readFlag() ? stream->readInt(16) : basemove->ppitch;
|
||||
proll = stream->readFlag() ? stream->readInt(16) : basemove->proll;
|
||||
|
||||
px = stream->readFlag() ? stream->readInt(6) : basemove->px;
|
||||
py = stream->readFlag() ? stream->readInt(6) : basemove->py;
|
||||
pz = stream->readFlag() ? stream->readInt(6) : basemove->pz;
|
||||
freeLook = stream->readFlag();
|
||||
deviceIsKeyboardMouse = stream->readFlag();
|
||||
|
||||
bool triggersDiffer = stream->readFlag();
|
||||
for (S32 i = 0; i< MaxTriggerKeys; i++)
|
||||
trigger[i] = triggersDiffer ? stream->readFlag() : basemove->trigger[i];
|
||||
unclamp();
|
||||
}
|
||||
else
|
||||
*this = *basemove;
|
||||
}
|
||||
95
Engine/source/T3D/gameBase/moveManager.h
Normal file
95
Engine/source/T3D/gameBase/moveManager.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MOVEMANAGER_H_
|
||||
#define _MOVEMANAGER_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
|
||||
enum MoveConstants {
|
||||
MaxTriggerKeys = 6,
|
||||
MaxMoveQueueSize = 45,
|
||||
};
|
||||
|
||||
class BitStream;
|
||||
|
||||
struct Move
|
||||
{
|
||||
enum { ChecksumBits = 16, ChecksumMask = ((1<<ChecksumBits)-1), ChecksumMismatch = U32(-1) };
|
||||
|
||||
// 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;
|
||||
U32 checksum;
|
||||
|
||||
bool deviceIsKeyboardMouse;
|
||||
bool freeLook;
|
||||
bool trigger[MaxTriggerKeys];
|
||||
|
||||
void pack(BitStream *stream, const Move * move = NULL);
|
||||
void unpack(BitStream *stream, const Move * move = NULL);
|
||||
void clamp();
|
||||
void unclamp();
|
||||
};
|
||||
|
||||
extern const Move NullMove;
|
||||
|
||||
class MoveManager
|
||||
{
|
||||
public:
|
||||
static bool mDeviceIsKeyboardMouse;
|
||||
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 F32 mXAxis_L;
|
||||
static F32 mYAxis_L;
|
||||
static F32 mXAxis_R;
|
||||
static F32 mYAxis_R;
|
||||
|
||||
static U32 mTriggerCount[MaxTriggerKeys];
|
||||
static U32 mPrevTriggerCount[MaxTriggerKeys];
|
||||
|
||||
static void init();
|
||||
};
|
||||
|
||||
#endif
|
||||
277
Engine/source/T3D/gameBase/processList.cpp
Normal file
277
Engine/source/T3D/gameBase/processList.cpp
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameBase/processList.h"
|
||||
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#include "platform/profiler.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ProcessObject::ProcessObject()
|
||||
: mProcessTag( 0 ),
|
||||
mOrderGUID( 0 ),
|
||||
mProcessTick( false ),
|
||||
mIsGameBase( false )
|
||||
{
|
||||
mProcessLink.next = mProcessLink.prev = this;
|
||||
}
|
||||
|
||||
void ProcessObject::plUnlink()
|
||||
{
|
||||
mProcessLink.next->mProcessLink.prev = mProcessLink.prev;
|
||||
mProcessLink.prev->mProcessLink.next = mProcessLink.next;
|
||||
mProcessLink.next = mProcessLink.prev = this;
|
||||
}
|
||||
|
||||
void ProcessObject::plLinkAfter(ProcessObject * obj)
|
||||
{
|
||||
AssertFatal(mProcessLink.next == this && mProcessLink.prev == this,"ProcessObject::plLinkAfter: must be unlinked before calling this");
|
||||
#ifdef TORQUE_DEBUG
|
||||
ProcessObject * test1 = obj;
|
||||
ProcessObject * test2 = obj->mProcessLink.next;
|
||||
ProcessObject * test3 = obj->mProcessLink.prev;
|
||||
ProcessObject * test4 = this;
|
||||
#endif
|
||||
|
||||
// Link this after obj
|
||||
mProcessLink.next = obj->mProcessLink.next;
|
||||
mProcessLink.prev = obj;
|
||||
obj->mProcessLink.next = this;
|
||||
mProcessLink.next->mProcessLink.prev = this;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
AssertFatal(test1->mProcessLink.next->mProcessLink.prev==test1 && test1->mProcessLink.prev->mProcessLink.next==test1,"Doh!");
|
||||
AssertFatal(test2->mProcessLink.next->mProcessLink.prev==test2 && test2->mProcessLink.prev->mProcessLink.next==test2,"Doh!");
|
||||
AssertFatal(test3->mProcessLink.next->mProcessLink.prev==test3 && test3->mProcessLink.prev->mProcessLink.next==test3,"Doh!");
|
||||
AssertFatal(test4->mProcessLink.next->mProcessLink.prev==test4 && test4->mProcessLink.prev->mProcessLink.next==test4,"Doh!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProcessObject::plLinkBefore(ProcessObject * obj)
|
||||
{
|
||||
AssertFatal(mProcessLink.next == this && mProcessLink.prev == this,"ProcessObject::plLinkBefore: must be unlinked before calling this");
|
||||
#ifdef TORQUE_DEBUG
|
||||
ProcessObject * test1 = obj;
|
||||
ProcessObject * test2 = obj->mProcessLink.next;
|
||||
ProcessObject * test3 = obj->mProcessLink.prev;
|
||||
ProcessObject * test4 = this;
|
||||
#endif
|
||||
|
||||
// Link this before obj
|
||||
mProcessLink.next = obj;
|
||||
mProcessLink.prev = obj->mProcessLink.prev;
|
||||
obj->mProcessLink.prev = this;
|
||||
mProcessLink.prev->mProcessLink.next = this;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
AssertFatal(test1->mProcessLink.next->mProcessLink.prev==test1 && test1->mProcessLink.prev->mProcessLink.next==test1,"Doh!");
|
||||
AssertFatal(test2->mProcessLink.next->mProcessLink.prev==test2 && test2->mProcessLink.prev->mProcessLink.next==test2,"Doh!");
|
||||
AssertFatal(test3->mProcessLink.next->mProcessLink.prev==test3 && test3->mProcessLink.prev->mProcessLink.next==test3,"Doh!");
|
||||
AssertFatal(test4->mProcessLink.next->mProcessLink.prev==test4 && test4->mProcessLink.prev->mProcessLink.next==test4,"Doh!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProcessObject::plJoin(ProcessObject * head)
|
||||
{
|
||||
ProcessObject * tail1 = head->mProcessLink.prev;
|
||||
ProcessObject * tail2 = mProcessLink.prev;
|
||||
tail1->mProcessLink.next = this;
|
||||
mProcessLink.prev = tail1;
|
||||
tail2->mProcessLink.next = head;
|
||||
head->mProcessLink.prev = tail2;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
ProcessList::ProcessList()
|
||||
{
|
||||
mCurrentTag = 0;
|
||||
mDirty = false;
|
||||
|
||||
mTotalTicks = 0;
|
||||
mLastTick = 0;
|
||||
mLastTime = 0;
|
||||
mLastDelta = 0.0f;
|
||||
}
|
||||
|
||||
void ProcessList::addObject( ProcessObject *obj )
|
||||
{
|
||||
obj->plLinkAfter(&mHead);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ProcessList::orderList()
|
||||
{
|
||||
// ProcessObject tags are initialized to 0, so current tag should never be 0.
|
||||
if (++mCurrentTag == 0)
|
||||
mCurrentTag++;
|
||||
|
||||
// Install a temporary head node
|
||||
ProcessObject list;
|
||||
list.plLinkBefore(mHead.mProcessLink.next);
|
||||
mHead.plUnlink();
|
||||
|
||||
// start out by (bubble) sorting list by GUID
|
||||
for (ProcessObject * cur = list.mProcessLink.next; cur != &list; cur = cur->mProcessLink.next)
|
||||
{
|
||||
if (cur->mOrderGUID == 0)
|
||||
// special case -- can be no lower, so accept as lowest (this is also
|
||||
// a common value since it is what non ordered objects have)
|
||||
continue;
|
||||
|
||||
for (ProcessObject * walk = cur->mProcessLink.next; walk != &list; walk = walk->mProcessLink.next)
|
||||
{
|
||||
if (walk->mOrderGUID < cur->mOrderGUID)
|
||||
{
|
||||
// swap walk and cur -- need to be careful because walk might be just after cur
|
||||
// so insert after item before cur and before item after walk
|
||||
ProcessObject * before = cur->mProcessLink.prev;
|
||||
ProcessObject * after = walk->mProcessLink.next;
|
||||
cur->plUnlink();
|
||||
walk->plUnlink();
|
||||
cur->plLinkBefore(after);
|
||||
walk->plLinkAfter(before);
|
||||
ProcessObject * swap = walk;
|
||||
walk = cur;
|
||||
cur = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse topological sort into the original head node
|
||||
while (list.mProcessLink.next != &list)
|
||||
{
|
||||
ProcessObject * ptr = list.mProcessLink.next;
|
||||
ProcessObject * afterObject = ptr->getAfterObject();
|
||||
ptr->mProcessTag = mCurrentTag;
|
||||
ptr->plUnlink();
|
||||
if (afterObject)
|
||||
{
|
||||
// Build chain "stack" of dependent objects and patch
|
||||
// it to the end of the current list.
|
||||
while (afterObject && afterObject->mProcessTag != mCurrentTag)
|
||||
{
|
||||
afterObject->mProcessTag = mCurrentTag;
|
||||
afterObject->plUnlink();
|
||||
afterObject->plLinkBefore(ptr);
|
||||
ptr = afterObject;
|
||||
afterObject = ptr->getAfterObject();
|
||||
}
|
||||
ptr->plJoin(&mHead);
|
||||
}
|
||||
else
|
||||
ptr->plLinkBefore(&mHead);
|
||||
}
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
GameBase* ProcessList::getGameBase( ProcessObject *obj )
|
||||
{
|
||||
if ( !obj->mIsGameBase )
|
||||
return NULL;
|
||||
|
||||
return static_cast< GameBase* >( obj );
|
||||
}
|
||||
|
||||
|
||||
void ProcessList::dumpToConsole()
|
||||
{
|
||||
for (ProcessObject * pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next)
|
||||
{
|
||||
SimObject * obj = dynamic_cast<SimObject*>(pobj);
|
||||
if (obj)
|
||||
Con::printf("id %i, order guid %i, type %s", obj->getId(), pobj->mOrderGUID, obj->getClassName());
|
||||
else
|
||||
Con::printf("---unknown object type, order guid %i", pobj->mOrderGUID);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool ProcessList::advanceTime(SimTime timeDelta)
|
||||
{
|
||||
PROFILE_START(ProcessList_AdvanceTime);
|
||||
|
||||
// some drivers change the FPU control state, which will break our control object simulation
|
||||
// (leading to packet mismatch errors due to small FP differences). So set it to the known
|
||||
// state before advancing.
|
||||
U32 mathState = Platform::getMathControlState();
|
||||
Platform::setMathControlStateKnown();
|
||||
|
||||
if (mDirty)
|
||||
orderList();
|
||||
|
||||
SimTime targetTime = mLastTime + timeDelta;
|
||||
SimTime targetTick = targetTime - (targetTime % TickMs);
|
||||
SimTime tickDelta = targetTick - mLastTick;
|
||||
bool tickPass = mLastTick != targetTick;
|
||||
|
||||
if ( tickPass )
|
||||
mPreTick.trigger();
|
||||
|
||||
// Advance all the objects.
|
||||
for (; mLastTick != targetTick; mLastTick += TickMs)
|
||||
onAdvanceObjects();
|
||||
|
||||
mLastTime = targetTime;
|
||||
mLastDelta = ((TickMs - ((targetTime+1) % TickMs)) % TickMs) / F32(TickMs);
|
||||
|
||||
if ( tickPass )
|
||||
mPostTick.trigger( tickDelta );
|
||||
|
||||
// restore math control state in case others are relying on it being a certain value
|
||||
Platform::setMathControlState(mathState);
|
||||
|
||||
PROFILE_END();
|
||||
return tickPass;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ProcessList::advanceObjects()
|
||||
{
|
||||
PROFILE_START(ProcessList_AdvanceObjects);
|
||||
|
||||
// A little link list shuffling is done here to avoid problems
|
||||
// with objects being deleted from within the process method.
|
||||
ProcessObject list;
|
||||
list.plLinkBefore(mHead.mProcessLink.next);
|
||||
mHead.plUnlink();
|
||||
for (ProcessObject * pobj = list.mProcessLink.next; pobj != &list; pobj = list.mProcessLink.next)
|
||||
{
|
||||
pobj->plUnlink();
|
||||
pobj->plLinkBefore(&mHead);
|
||||
|
||||
onTickObject(pobj);
|
||||
}
|
||||
|
||||
mTotalTicks++;
|
||||
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
|
||||
|
||||
193
Engine/source/T3D/gameBase/processList.h
Normal file
193
Engine/source/T3D/gameBase/processList.h
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PROCESSLIST_H_
|
||||
#define _PROCESSLIST_H_
|
||||
|
||||
#ifndef _SIM_H_
|
||||
#include "console/sim.h"
|
||||
#endif
|
||||
#ifndef _TSIGNAL_H_
|
||||
#include "core/util/tSignal.h"
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#define TickMs 32
|
||||
#define TickSec (F32(TickMs) / 1000.0f)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class GameConnection;
|
||||
struct Move;
|
||||
|
||||
|
||||
class ProcessObject
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
ProcessObject();
|
||||
virtual ~ProcessObject() { removeFromProcessList(); }
|
||||
|
||||
/// Removes this object from the tick-processing list
|
||||
void removeFromProcessList() { plUnlink(); }
|
||||
|
||||
/// Set the status of tick processing.
|
||||
///
|
||||
/// Set true to receive processTick, advanceTime, and interpolateTick calls.
|
||||
///
|
||||
/// @see processTick
|
||||
/// @param t If true, tick processing is enabled.
|
||||
virtual void setProcessTick( bool t ) { mProcessTick = t; }
|
||||
|
||||
/// Returns true if this object processes ticks.
|
||||
bool isTicking() const { return mProcessTick; }
|
||||
|
||||
/// This is really implemented in GameBase and is only here to avoid
|
||||
/// casts within ProcessList.
|
||||
virtual GameConnection* getControllingClient() { return NULL; }
|
||||
|
||||
/// This is really implemented in GameBase and is only here to avoid
|
||||
/// casts within ProcessList.
|
||||
virtual U32 getPacketDataChecksum( GameConnection *conn ) { return -1; }
|
||||
|
||||
/// 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 occurring when the
|
||||
/// vehicle moves. So the player would be set to processAfter(theVehicle).
|
||||
///
|
||||
/// @param obj Object to process after
|
||||
virtual void processAfter( ProcessObject *obj ) {}
|
||||
|
||||
/// Clears the effects of a call to processAfter()
|
||||
virtual void clearProcessAfter() {}
|
||||
|
||||
/// Returns the object that this processes after.
|
||||
///
|
||||
/// @see processAfter
|
||||
virtual ProcessObject* getAfterObject() const { return NULL; }
|
||||
|
||||
/// 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 ) {}
|
||||
|
||||
/// Allow object to modify the Move before it is ticked or sent to the server.
|
||||
/// This is only called for the control object on the client-side.
|
||||
virtual void preprocessMove( Move *move ) {}
|
||||
|
||||
//protected:
|
||||
|
||||
struct Link
|
||||
{
|
||||
ProcessObject *next;
|
||||
ProcessObject *prev;
|
||||
};
|
||||
|
||||
// Processing interface
|
||||
void plUnlink();
|
||||
void plLinkAfter(ProcessObject*);
|
||||
void plLinkBefore(ProcessObject*);
|
||||
void plJoin(ProcessObject*);
|
||||
|
||||
U32 mProcessTag; // Tag used during sort
|
||||
U32 mOrderGUID; // UID for keeping order synced (e.g., across network or runs of sim)
|
||||
Link mProcessLink; // Ordered process queue
|
||||
|
||||
bool mProcessTick;
|
||||
|
||||
bool mIsGameBase;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
typedef Signal<void()> PreTickSignal;
|
||||
typedef Signal<void(SimTime)> PostTickSignal;
|
||||
class GameBase;
|
||||
|
||||
/// List of ProcessObjects.
|
||||
class ProcessList
|
||||
{
|
||||
public:
|
||||
|
||||
ProcessList();
|
||||
virtual ~ProcessList() {}
|
||||
|
||||
void markDirty() { mDirty = true; }
|
||||
bool isDirty() { return mDirty; }
|
||||
|
||||
SimTime getLastTime() { return mLastTime; }
|
||||
F32 getLastDelta() { return mLastDelta; }
|
||||
F32 getLastInterpDelta() { return mLastDelta / F32(TickMs); }
|
||||
U32 getTotalTicks() { return mTotalTicks; }
|
||||
void dumpToConsole();
|
||||
|
||||
PreTickSignal& preTickSignal() { return mPreTick; }
|
||||
PostTickSignal& postTickSignal() { return mPostTick; }
|
||||
|
||||
virtual void addObject( ProcessObject *obj );
|
||||
|
||||
/// Returns true if a tick was processed.
|
||||
virtual bool advanceTime( SimTime timeDelta );
|
||||
|
||||
protected:
|
||||
|
||||
void orderList();
|
||||
GameBase* getGameBase( ProcessObject *obj );
|
||||
|
||||
virtual void advanceObjects();
|
||||
virtual void onAdvanceObjects() { advanceObjects(); }
|
||||
virtual void onPreTickObject( ProcessObject* ) {}
|
||||
virtual void onTickObject( ProcessObject* ) {}
|
||||
|
||||
protected:
|
||||
|
||||
ProcessObject mHead;
|
||||
|
||||
U32 mCurrentTag;
|
||||
bool mDirty;
|
||||
|
||||
U32 mTotalTicks;
|
||||
SimTime mLastTick;
|
||||
SimTime mLastTime;
|
||||
F32 mLastDelta;
|
||||
|
||||
PreTickSignal mPreTick;
|
||||
PostTickSignal mPostTick;
|
||||
};
|
||||
|
||||
#endif // _PROCESSLIST_H_
|
||||
400
Engine/source/T3D/gameBase/std/stdGameProcess.cpp
Normal file
400
Engine/source/T3D/gameBase/std/stdGameProcess.cpp
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameBase/std/stdGameProcess.h"
|
||||
|
||||
#include "platform/profiler.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/dnet.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "core/frameAllocator.h"
|
||||
#include "core/util/refBase.h"
|
||||
#include "math/mPoint3.h"
|
||||
#include "math/mMatrix.h"
|
||||
#include "math/mathUtils.h"
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/gameBase/std/stdMoveList.h"
|
||||
#include "T3D/fx/cameraFXMgr.h"
|
||||
|
||||
MODULE_BEGIN( ProcessList )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
StdServerProcessList::init();
|
||||
StdClientProcessList::init();
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
StdServerProcessList::shutdown();
|
||||
StdClientProcessList::shutdown();
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
void StdServerProcessList::init()
|
||||
{
|
||||
smServerProcessList = new StdServerProcessList();
|
||||
}
|
||||
|
||||
void StdServerProcessList::shutdown()
|
||||
{
|
||||
delete smServerProcessList;
|
||||
}
|
||||
|
||||
void StdClientProcessList::init()
|
||||
{
|
||||
smClientProcessList = new StdClientProcessList();
|
||||
}
|
||||
|
||||
void StdClientProcessList::shutdown()
|
||||
{
|
||||
delete smClientProcessList;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
// local work class
|
||||
struct GameBaseListNode
|
||||
{
|
||||
GameBaseListNode()
|
||||
{
|
||||
mPrev=this;
|
||||
mNext=this;
|
||||
mObject=NULL;
|
||||
}
|
||||
|
||||
GameBaseListNode * mPrev;
|
||||
GameBaseListNode * mNext;
|
||||
GameBase * mObject;
|
||||
|
||||
void linkBefore(GameBaseListNode * obj)
|
||||
{
|
||||
// Link this before obj
|
||||
mNext = obj;
|
||||
mPrev = obj->mPrev;
|
||||
obj->mPrev = this;
|
||||
mPrev->mNext = this;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ClientProcessList
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
StdClientProcessList::StdClientProcessList()
|
||||
{
|
||||
}
|
||||
|
||||
bool StdClientProcessList::advanceTime( SimTime timeDelta )
|
||||
{
|
||||
PROFILE_SCOPE( StdClientProcessList_AdvanceTime );
|
||||
|
||||
if ( doBacklogged( timeDelta ) )
|
||||
return false;
|
||||
|
||||
bool ret = Parent::advanceTime( timeDelta );
|
||||
ProcessObject *obj = NULL;
|
||||
|
||||
AssertFatal( mLastDelta >= 0.0f && mLastDelta <= 1.0f, "mLastDelta is not zero to one.");
|
||||
|
||||
obj = mHead.mProcessLink.next;
|
||||
while ( obj != &mHead )
|
||||
{
|
||||
if ( obj->isTicking() )
|
||||
obj->interpolateTick( mLastDelta );
|
||||
|
||||
obj = obj->mProcessLink.next;
|
||||
}
|
||||
|
||||
// Inform objects of total elapsed delta so they can advance
|
||||
// client side animations.
|
||||
F32 dt = F32(timeDelta) / 1000;
|
||||
|
||||
// Update camera FX.
|
||||
gCamFXMgr.update( dt );
|
||||
|
||||
obj = mHead.mProcessLink.next;
|
||||
while ( obj != &mHead )
|
||||
{
|
||||
obj->advanceTime( dt );
|
||||
obj = obj->mProcessLink.next;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void StdClientProcessList::onAdvanceObjects()
|
||||
{
|
||||
PROFILE_SCOPE( StdClientProcessList_OnAdvanceObjects );
|
||||
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
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)
|
||||
return;
|
||||
}
|
||||
while ( blockType != GameConnection::BlockTypeMove );
|
||||
}
|
||||
|
||||
connection->mMoveList->collectMove();
|
||||
advanceObjects();
|
||||
}
|
||||
else
|
||||
advanceObjects();
|
||||
}
|
||||
|
||||
void StdClientProcessList::onTickObject( ProcessObject *obj )
|
||||
{
|
||||
PROFILE_SCOPE( StdClientProcessList_OnTickObject );
|
||||
|
||||
// In case the object deletes itself during its processTick.
|
||||
SimObjectPtr<SceneObject> safePtr = static_cast<SceneObject*>( obj );
|
||||
|
||||
// Each object is either advanced a single tick, or if it's
|
||||
// being controlled by a client, ticked once for each pending move.
|
||||
Move* movePtr;
|
||||
U32 numMoves;
|
||||
GameConnection* con = obj->getControllingClient();
|
||||
if ( con && con->getControlObject() == obj )
|
||||
{
|
||||
con->mMoveList->getMoves( &movePtr, &numMoves );
|
||||
if ( numMoves )
|
||||
{
|
||||
// Note: should only have a single move at this point
|
||||
AssertFatal(numMoves==1,"ClientProccessList::onTickObject: more than one move in queue");
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient());
|
||||
#endif
|
||||
|
||||
if ( obj->isTicking() )
|
||||
obj->processTick( movePtr );
|
||||
|
||||
if ( bool(safePtr) && obj->getControllingClient() )
|
||||
{
|
||||
U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() );
|
||||
|
||||
// set checksum if not set or check against stored value if set
|
||||
movePtr->checksum = newsum;
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("move checksum: %i, (start %i), (move %f %f %f)",
|
||||
movePtr->checksum,sum,movePtr->yaw,movePtr->y,movePtr->z);
|
||||
#endif
|
||||
}
|
||||
con->mMoveList->clearMoves( 1 );
|
||||
}
|
||||
}
|
||||
else if ( obj->isTicking() )
|
||||
obj->processTick( 0 );
|
||||
}
|
||||
|
||||
void StdClientProcessList::advanceObjects()
|
||||
{
|
||||
PROFILE_SCOPE( StdClientProcessList_AdvanceObjects );
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("Advance client time...");
|
||||
#endif
|
||||
|
||||
Parent::advanceObjects();
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("---------");
|
||||
#endif
|
||||
}
|
||||
|
||||
void StdClientProcessList::clientCatchup( GameConnection * connection )
|
||||
{
|
||||
SimObjectPtr<GameBase> control = connection->getControlObject();
|
||||
if ( control )
|
||||
{
|
||||
Move * movePtr;
|
||||
U32 numMoves;
|
||||
U32 m = 0;
|
||||
connection->mMoveList->getMoves( &movePtr, &numMoves );
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("client catching up... (%i)", numMoves);
|
||||
#endif
|
||||
|
||||
preTickSignal().trigger();
|
||||
|
||||
if ( control->isTicking() )
|
||||
for ( m = 0; m < numMoves; m++ )
|
||||
control->processTick( movePtr++ );
|
||||
|
||||
connection->mMoveList->clearMoves( m );
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("---------");
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// ServerProcessList
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
StdServerProcessList::StdServerProcessList()
|
||||
{
|
||||
}
|
||||
|
||||
void StdServerProcessList::onPreTickObject( ProcessObject *pobj )
|
||||
{
|
||||
if ( pobj->mIsGameBase )
|
||||
{
|
||||
SimObjectPtr<GameBase> obj = getGameBase( pobj );
|
||||
|
||||
// Each object is either advanced a single tick, or if it's
|
||||
// being controlled by a client, ticked once for each pending move.
|
||||
GameConnection *con = obj->getControllingClient();
|
||||
|
||||
if ( con && con->getControlObject() == obj )
|
||||
{
|
||||
Move* movePtr;
|
||||
U32 numMoves;
|
||||
con->mMoveList->getMoves( &movePtr, &numMoves );
|
||||
|
||||
if ( numMoves == 0 )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("no moves on object %i, skip tick",obj->getId());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Parent::onPreTickObject (pobj );
|
||||
}
|
||||
|
||||
void StdServerProcessList::onTickObject( ProcessObject *pobj )
|
||||
{
|
||||
PROFILE_SCOPE( StdServerProcessList_OnTickObject );
|
||||
|
||||
// Each object is either advanced a single tick, or if it's
|
||||
// being controlled by a client, ticked once for each pending move.
|
||||
|
||||
GameConnection *con = pobj->getControllingClient();
|
||||
|
||||
if ( pobj->mIsGameBase && con && con->getControlObject() == pobj )
|
||||
{
|
||||
// In case the object is deleted during its own tick.
|
||||
SimObjectPtr<GameBase> obj = getGameBase( pobj );
|
||||
|
||||
Move* movePtr;
|
||||
U32 m, numMoves;
|
||||
con->mMoveList->getMoves( &movePtr, &numMoves );
|
||||
|
||||
// For debugging it can be useful to know when this happens.
|
||||
//if ( numMoves > 1 )
|
||||
// Con::printf( "numMoves: %i", numMoves );
|
||||
|
||||
// Do we really need to test the control object each iteration? Does it change?
|
||||
for ( m = 0; m < numMoves && con && con->getControlObject() == obj; m++, movePtr++ )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient());
|
||||
#endif
|
||||
|
||||
if ( obj->isTicking() )
|
||||
obj->processTick( movePtr );
|
||||
|
||||
if ( con && con->getControlObject() == obj )
|
||||
{
|
||||
U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() );
|
||||
|
||||
// check move checksum
|
||||
if ( movePtr->checksum != newsum )
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
if( !obj->isAIControlled() )
|
||||
Con::printf("move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)",
|
||||
movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z);
|
||||
#endif
|
||||
|
||||
movePtr->checksum = Move::ChecksumMismatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("move %i checksum agree: %i == %i, (start %i), (move %f %f %f)",
|
||||
movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
con->mMoveList->clearMoves( m );
|
||||
}
|
||||
else if ( pobj->isTicking() )
|
||||
pobj->processTick( 0 );
|
||||
}
|
||||
|
||||
void StdServerProcessList::advanceObjects()
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("Advance server time...");
|
||||
#endif
|
||||
|
||||
Parent::advanceObjects();
|
||||
|
||||
// Credit all connections with the elapsed tick
|
||||
SimGroup *clientGroup = Sim::getClientGroup();
|
||||
for(SimGroup::iterator i = clientGroup->begin(); i != clientGroup->end(); i++)
|
||||
{
|
||||
if (GameConnection *con = dynamic_cast<GameConnection *>(*i))
|
||||
{
|
||||
con->mMoveList->advanceMove();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("---------");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
82
Engine/source/T3D/gameBase/std/stdGameProcess.h
Normal file
82
Engine/source/T3D/gameBase/std/stdGameProcess.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMEPROCESS_STD_H_
|
||||
#define _GAMEPROCESS_STD_H_
|
||||
|
||||
//#include "T3D/gameBase/processList.h"
|
||||
#ifndef _GAMEPROCESS_H_
|
||||
#include "T3D/gameBase/gameProcess.h"
|
||||
#endif
|
||||
|
||||
class GameBase;
|
||||
class GameConnection;
|
||||
struct Move;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// List to keep track of GameBases to process.
|
||||
class StdClientProcessList : public ClientProcessList
|
||||
{
|
||||
typedef ClientProcessList Parent;
|
||||
|
||||
protected:
|
||||
|
||||
// ProcessList
|
||||
void onTickObject(ProcessObject *);
|
||||
void advanceObjects();
|
||||
void onAdvanceObjects();
|
||||
|
||||
public:
|
||||
|
||||
StdClientProcessList();
|
||||
|
||||
// ProcessList
|
||||
bool advanceTime( SimTime timeDelta );
|
||||
|
||||
// ClientProcessList
|
||||
void clientCatchup( GameConnection *conn );
|
||||
|
||||
static void init();
|
||||
static void shutdown();
|
||||
};
|
||||
|
||||
class StdServerProcessList : public ServerProcessList
|
||||
{
|
||||
typedef ServerProcessList Parent;
|
||||
|
||||
protected:
|
||||
|
||||
// ProcessList
|
||||
void onPreTickObject( ProcessObject *pobj );
|
||||
void onTickObject( ProcessObject *pobj );
|
||||
void advanceObjects();
|
||||
|
||||
public:
|
||||
|
||||
StdServerProcessList();
|
||||
|
||||
static void init();
|
||||
static void shutdown();
|
||||
};
|
||||
|
||||
#endif // _GAMEPROCESS_STD_H_
|
||||
198
Engine/source/T3D/gameBase/std/stdMoveList.cpp
Normal file
198
Engine/source/T3D/gameBase/std/stdMoveList.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameBase/std/stdMoveList.h"
|
||||
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
|
||||
#define MAX_MOVE_PACKET_SENDS 4
|
||||
|
||||
|
||||
StdMoveList::StdMoveList()
|
||||
{
|
||||
mMoveCredit = MaxMoveCount;
|
||||
}
|
||||
|
||||
U32 StdMoveList::getMoves(Move** movePtr,U32* numMoves)
|
||||
{
|
||||
if (!mConnection->isConnectionToServer())
|
||||
{
|
||||
if (mMoveVec.size() > mMoveCredit)
|
||||
mMoveVec.setSize(mMoveCredit);
|
||||
}
|
||||
return Parent::getMoves(movePtr,numMoves);
|
||||
}
|
||||
|
||||
void StdMoveList::clearMoves(U32 count)
|
||||
{
|
||||
if (!mConnection->isConnectionToServer())
|
||||
{
|
||||
count = mClamp(count,0,mMoveCredit);
|
||||
mMoveCredit -= count;
|
||||
}
|
||||
|
||||
Parent::clearMoves(count);
|
||||
}
|
||||
|
||||
void StdMoveList::advanceMove()
|
||||
{
|
||||
AssertFatal(!mConnection->isConnectionToServer(), "Cannot inc move credit on the client.");
|
||||
|
||||
// Game tick increment
|
||||
mMoveCredit++;
|
||||
if (mMoveCredit > MaxMoveCount)
|
||||
mMoveCredit = MaxMoveCount;
|
||||
|
||||
// Clear pending moves for the elapsed time if there
|
||||
// is no control object.
|
||||
if ( mConnection->getControlObject() == NULL )
|
||||
mMoveVec.clear();
|
||||
}
|
||||
|
||||
void StdMoveList::clientWriteMovePacket(BitStream *bstream)
|
||||
{
|
||||
AssertFatal(mLastMoveAck == mFirstMoveIndex, "Invalid move index.");
|
||||
U32 count = mMoveVec.size();
|
||||
|
||||
Move * move = mMoveVec.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);
|
||||
Move * prevMove = NULL;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
move[offset + i].sendCount++;
|
||||
move[offset + i].pack(bstream,prevMove);
|
||||
bstream->writeInt(move[offset + i].checksum,Move::ChecksumBits);
|
||||
prevMove = &move[offset+i];
|
||||
}
|
||||
}
|
||||
|
||||
void StdMoveList::serverReadMovePacket(BitStream *bstream)
|
||||
{
|
||||
// Server side packet read.
|
||||
U32 start = bstream->readInt(32);
|
||||
U32 count = bstream->readInt(MoveCountBits);
|
||||
|
||||
Move * prevMove = NULL;
|
||||
Move prevMoveHolder;
|
||||
|
||||
// Skip forward (must be starting up), or over the moves
|
||||
// we already have.
|
||||
int skip = mLastMoveAck - start;
|
||||
if (skip < 0)
|
||||
{
|
||||
mLastMoveAck = start;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (skip > count)
|
||||
skip = count;
|
||||
for (int i = 0; i < skip; i++)
|
||||
{
|
||||
prevMoveHolder.unpack(bstream,prevMove);
|
||||
prevMoveHolder.checksum = bstream->readInt(Move::ChecksumBits);
|
||||
prevMove = &prevMoveHolder;
|
||||
S32 idx = mMoveVec.size()-skip+i;
|
||||
if (idx>=0)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
if (mMoveVec[idx].checksum != prevMoveHolder.checksum)
|
||||
Con::printf("updated checksum on move %i from %i to %i",mMoveVec[idx].id,mMoveVec[idx].checksum,prevMoveHolder.checksum);
|
||||
#endif
|
||||
mMoveVec[idx].checksum = prevMoveHolder.checksum;
|
||||
}
|
||||
}
|
||||
start += skip;
|
||||
count = count - skip;
|
||||
}
|
||||
|
||||
// Put the rest on the move list.
|
||||
int index = mMoveVec.size();
|
||||
mMoveVec.increment(count);
|
||||
while (index < mMoveVec.size())
|
||||
{
|
||||
mMoveVec[index].unpack(bstream,prevMove);
|
||||
mMoveVec[index].checksum = bstream->readInt(Move::ChecksumBits);
|
||||
prevMove = &mMoveVec[index];
|
||||
mMoveVec[index].id = start++;
|
||||
index ++;
|
||||
}
|
||||
|
||||
mLastMoveAck += count;
|
||||
}
|
||||
|
||||
void StdMoveList::serverWriteMovePacket(BitStream * bstream)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("ack %i minus %i",mLastMoveAck,mMoveVec.size());
|
||||
#endif
|
||||
|
||||
// acknowledge only those moves that have been ticked
|
||||
bstream->writeInt(mLastMoveAck - mMoveVec.size(),32);
|
||||
}
|
||||
|
||||
void StdMoveList::clientReadMovePacket(BitStream * bstream)
|
||||
{
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("pre move ack: %i", mLastMoveAck);
|
||||
#endif
|
||||
|
||||
mLastMoveAck = bstream->readInt(32);
|
||||
|
||||
#ifdef TORQUE_DEBUG_NET_MOVES
|
||||
Con::printf("post move ack %i, first move %i, last move %i", mLastMoveAck, mFirstMoveIndex, mLastClientMove);
|
||||
#endif
|
||||
|
||||
if (mLastMoveAck < mFirstMoveIndex)
|
||||
mLastMoveAck = mFirstMoveIndex;
|
||||
|
||||
if(mLastMoveAck > mLastClientMove)
|
||||
mLastClientMove = mLastMoveAck;
|
||||
while(mFirstMoveIndex < mLastMoveAck)
|
||||
{
|
||||
if (mMoveVec.size())
|
||||
{
|
||||
mMoveVec.pop_front();
|
||||
mFirstMoveIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertWarn(1, "Popping off too many moves!");
|
||||
mFirstMoveIndex = mLastMoveAck;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Engine/source/T3D/gameBase/std/stdMoveList.h
Normal file
55
Engine/source/T3D/gameBase/std/stdMoveList.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _MOVELIST_STD_H_
|
||||
#define _MOVELIST_STD_H_
|
||||
|
||||
#ifndef _MOVELIST_H_
|
||||
#include "T3D/gameBase/moveList.h"
|
||||
#endif
|
||||
|
||||
class StdMoveList : public MoveList
|
||||
{
|
||||
typedef MoveList Parent;
|
||||
|
||||
public:
|
||||
|
||||
StdMoveList();
|
||||
|
||||
void clientWriteMovePacket(BitStream *);
|
||||
void clientReadMovePacket(BitStream *);
|
||||
|
||||
void serverWriteMovePacket(BitStream *);
|
||||
void serverReadMovePacket(BitStream *);
|
||||
|
||||
U32 getMoves(Move**,U32* numMoves);
|
||||
void clearMoves(U32 count);
|
||||
|
||||
void advanceMove();
|
||||
void onAdvanceObjects() {}
|
||||
|
||||
protected:
|
||||
|
||||
U32 mMoveCredit;
|
||||
};
|
||||
|
||||
#endif // _MOVELIST_STD_H_
|
||||
174
Engine/source/T3D/gameBase/tickCache.cpp
Normal file
174
Engine/source/T3D/gameBase/tickCache.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "T3D/gameBase/moveManager.h"
|
||||
#include "T3D/gameBase/gameProcess.h"
|
||||
|
||||
struct TickCacheHead
|
||||
{
|
||||
TickCacheEntry * oldest;
|
||||
TickCacheEntry * newest;
|
||||
TickCacheEntry * next;
|
||||
U32 numEntry;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
FreeListChunker<TickCacheHead> sgTickCacheHeadStore;
|
||||
FreeListChunker<TickCacheEntry> sgTickCacheEntryStore;
|
||||
FreeListChunker<Move> sgMoveStore;
|
||||
|
||||
static TickCacheHead * allocHead() { return sgTickCacheHeadStore.alloc(); }
|
||||
static void freeHead(TickCacheHead * head) { sgTickCacheHeadStore.free(head); }
|
||||
|
||||
static TickCacheEntry * allocEntry() { return sgTickCacheEntryStore.alloc(); }
|
||||
static void freeEntry(TickCacheEntry * entry) { sgTickCacheEntryStore.free(entry); }
|
||||
|
||||
static Move * allocMove() { return sgMoveStore.alloc(); }
|
||||
static void freeMove(Move * move) { sgMoveStore.free(move); }
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
TickCache::~TickCache()
|
||||
{
|
||||
if (mTickCacheHead)
|
||||
{
|
||||
setCacheSize(0);
|
||||
freeHead(mTickCacheHead);
|
||||
mTickCacheHead = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Move * TickCacheEntry::allocateMove()
|
||||
{
|
||||
return allocMove();
|
||||
}
|
||||
|
||||
TickCacheEntry * TickCache::addCacheEntry()
|
||||
{
|
||||
// Add a new entry, creating head if needed
|
||||
if (!mTickCacheHead)
|
||||
{
|
||||
mTickCacheHead = allocHead();
|
||||
mTickCacheHead->newest = mTickCacheHead->oldest = mTickCacheHead->next = NULL;
|
||||
mTickCacheHead->numEntry = 0;
|
||||
}
|
||||
if (!mTickCacheHead->newest)
|
||||
{
|
||||
mTickCacheHead->newest = mTickCacheHead->oldest = allocEntry();
|
||||
}
|
||||
else
|
||||
{
|
||||
mTickCacheHead->newest->next = allocEntry();
|
||||
mTickCacheHead->newest = mTickCacheHead->newest->next;
|
||||
}
|
||||
mTickCacheHead->newest->next = NULL;
|
||||
mTickCacheHead->newest->move = NULL;
|
||||
mTickCacheHead->numEntry++;
|
||||
return mTickCacheHead->newest;
|
||||
}
|
||||
|
||||
void TickCache::setCacheSize(S32 len)
|
||||
{
|
||||
// grow cache to len size, adding to newest side of the list
|
||||
while (!mTickCacheHead || mTickCacheHead->numEntry < len)
|
||||
addCacheEntry();
|
||||
// shrink tick cache down to given size, popping off oldest entries first
|
||||
while (mTickCacheHead && mTickCacheHead->numEntry > len)
|
||||
dropOldest();
|
||||
}
|
||||
|
||||
void TickCache::dropOldest()
|
||||
{
|
||||
AssertFatal(mTickCacheHead->oldest,"Popping off too many tick cache entries");
|
||||
TickCacheEntry * oldest = mTickCacheHead->oldest;
|
||||
mTickCacheHead->oldest = oldest->next;
|
||||
if (oldest->move)
|
||||
freeMove(oldest->move);
|
||||
freeEntry(oldest);
|
||||
mTickCacheHead->numEntry--;
|
||||
if (mTickCacheHead->numEntry < 2)
|
||||
mTickCacheHead->newest = mTickCacheHead->oldest;
|
||||
}
|
||||
|
||||
void TickCache::dropNextOldest()
|
||||
{
|
||||
AssertFatal(mTickCacheHead->oldest && mTickCacheHead->numEntry>1,"Popping off too many tick cache entries");
|
||||
TickCacheEntry * oldest = mTickCacheHead->oldest;
|
||||
TickCacheEntry * nextoldest = mTickCacheHead->oldest->next;
|
||||
oldest->next = nextoldest->next;
|
||||
if (nextoldest->move)
|
||||
freeMove(nextoldest->move);
|
||||
freeEntry(nextoldest);
|
||||
mTickCacheHead->numEntry--;
|
||||
if (mTickCacheHead->numEntry==1)
|
||||
mTickCacheHead->newest = mTickCacheHead->oldest;
|
||||
}
|
||||
|
||||
void TickCache::ageCache(S32 numToAge, S32 len)
|
||||
{
|
||||
AssertFatal(mTickCacheHead,"No tick cache head");
|
||||
AssertFatal(mTickCacheHead->numEntry>=numToAge,"Too few entries!");
|
||||
AssertFatal(mTickCacheHead->numEntry>numToAge,"Too few entries!");
|
||||
|
||||
while (numToAge--)
|
||||
dropOldest();
|
||||
while (mTickCacheHead->numEntry>len)
|
||||
dropNextOldest();
|
||||
while (mTickCacheHead->numEntry<len)
|
||||
addCacheEntry();
|
||||
}
|
||||
|
||||
void TickCache::beginCacheList()
|
||||
{
|
||||
// get ready iterate from oldest to newest entry
|
||||
if (mTickCacheHead)
|
||||
mTickCacheHead->next = mTickCacheHead->oldest;
|
||||
// if no head, that's ok, we'll just add entries as we go
|
||||
}
|
||||
|
||||
TickCacheEntry * TickCache::incCacheList(bool addIfNeeded)
|
||||
{
|
||||
// continue iterating through cache, returning current entry
|
||||
// we'll add new entries if need be
|
||||
TickCacheEntry * ret = NULL;
|
||||
if (mTickCacheHead && mTickCacheHead->next)
|
||||
{
|
||||
ret = mTickCacheHead->next;
|
||||
mTickCacheHead->next = mTickCacheHead->next->next;
|
||||
}
|
||||
else if (addIfNeeded)
|
||||
{
|
||||
addCacheEntry();
|
||||
ret = mTickCacheHead->newest;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
69
Engine/source/T3D/gameBase/tickCache.h
Normal file
69
Engine/source/T3D/gameBase/tickCache.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TICKCACHE_H_
|
||||
#define _TICKCACHE_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
|
||||
struct Move;
|
||||
|
||||
struct TickCacheEntry
|
||||
{
|
||||
enum { MaxPacketSize=140 };
|
||||
|
||||
U8 packetData[MaxPacketSize];
|
||||
TickCacheEntry * next;
|
||||
Move * move;
|
||||
|
||||
// If you want to assign moves to tick cache for later playback, allocate them here
|
||||
Move * allocateMove();
|
||||
};
|
||||
|
||||
struct TickCacheHead;
|
||||
|
||||
class TickCache
|
||||
{
|
||||
public:
|
||||
TickCache();
|
||||
~TickCache();
|
||||
|
||||
TickCacheEntry * addCacheEntry();
|
||||
void dropOldest();
|
||||
void dropNextOldest();
|
||||
void ageCache(S32 numToAge, S32 len);
|
||||
void setCacheSize(S32 len);
|
||||
void beginCacheList();
|
||||
TickCacheEntry * incCacheList(bool addIfNeeded=true);
|
||||
|
||||
private:
|
||||
TickCacheHead * mTickCacheHead;
|
||||
};
|
||||
|
||||
inline TickCache::TickCache()
|
||||
{
|
||||
mTickCacheHead = NULL;
|
||||
}
|
||||
|
||||
#endif // _TICKCACHE_H_
|
||||
470
Engine/source/T3D/gameFunctions.cpp
Normal file
470
Engine/source/T3D/gameFunctions.cpp
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/gameFunctions.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/camera.h"
|
||||
#include "T3D/sfx/sfx3DWorld.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gui/3d/guiTSControl.h"
|
||||
#include "core/util/journal/process.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "math/mEase.h"
|
||||
#include "core/module.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
|
||||
static void RegisterGameFunctions();
|
||||
static void Process3D();
|
||||
|
||||
MODULE_BEGIN( 3D )
|
||||
|
||||
MODULE_INIT_AFTER( Process )
|
||||
MODULE_INIT_AFTER( Scene )
|
||||
|
||||
MODULE_SHUTDOWN_BEFORE( Process )
|
||||
MODULE_SHUTDOWN_BEFORE( Sim )
|
||||
MODULE_SHUTDOWN_AFTER( Scene )
|
||||
|
||||
MODULE_INIT
|
||||
{
|
||||
Process::notify(Process3D, PROCESS_TIME_ORDER);
|
||||
|
||||
GameConnection::smFovUpdate.notify(GameSetCameraFov);
|
||||
|
||||
RegisterGameFunctions();
|
||||
}
|
||||
|
||||
MODULE_SHUTDOWN
|
||||
{
|
||||
GameConnection::smFovUpdate.remove(GameSetCameraFov);
|
||||
|
||||
Process::remove(Process3D);
|
||||
}
|
||||
|
||||
MODULE_END;
|
||||
|
||||
|
||||
static S32 gEaseInOut = Ease::InOut;
|
||||
static S32 gEaseIn = Ease::In;
|
||||
static S32 gEaseOut = Ease::Out;
|
||||
|
||||
static S32 gEaseLinear = Ease::Linear;
|
||||
static S32 gEaseQuadratic= Ease::Quadratic;
|
||||
static S32 gEaseCubic= Ease::Cubic;
|
||||
static S32 gEaseQuartic = Ease::Quartic;
|
||||
static S32 gEaseQuintic = Ease::Quintic;
|
||||
static S32 gEaseSinusoidal= Ease::Sinusoidal;
|
||||
static S32 gEaseExponential = Ease::Exponential;
|
||||
static S32 gEaseCircular = Ease::Circular;
|
||||
static S32 gEaseElastic = Ease::Elastic;
|
||||
static S32 gEaseBack = Ease::Back;
|
||||
static S32 gEaseBounce = Ease::Bounce;
|
||||
|
||||
|
||||
extern void ShowInit();
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// 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
|
||||
|
||||
/// A scale to apply to the normal visible distance
|
||||
/// typically used for tuning performance.
|
||||
static F32 sVisDistanceScale = 1.0f;
|
||||
|
||||
} // namespace {}
|
||||
|
||||
// query
|
||||
static SimpleQueryList sgServerQueryList;
|
||||
static U32 sgServerQueryIndex = 0;
|
||||
|
||||
//SERVER FUNCTIONS ONLY
|
||||
ConsoleFunctionGroupBegin( Containers, "Spatial query functions. <b>Server side only!</b>");
|
||||
|
||||
ConsoleFunction(containerFindFirst, const char*, 6, 6, "(int mask, Point3F point, float x, float y, float z)"
|
||||
"@brief Find objects matching the bitmask type within a box centered at point, with extents x, y, z.\n\n"
|
||||
"@returns The first object found, or an empty string if nothing was found. Thereafter, you can get more "
|
||||
"results using containerFindNext()."
|
||||
"@see containerFindNext\n"
|
||||
"@ingroup Game")
|
||||
{
|
||||
//find out what we're looking for
|
||||
U32 typeMask = U32(dAtoi(argv[1]));
|
||||
|
||||
//find the center of the container volume
|
||||
Point3F origin(0.0f, 0.0f, 0.0f);
|
||||
dSscanf(argv[2], "%g %g %g", &origin.x, &origin.y, &origin.z);
|
||||
|
||||
//find the box dimensions
|
||||
Point3F size(0.0f, 0.0f, 0.0f);
|
||||
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.minExtents = origin;
|
||||
queryBox.maxExtents = origin;
|
||||
queryBox.minExtents -= size;
|
||||
queryBox.maxExtents += size;
|
||||
|
||||
//initialize the list, and do the query
|
||||
sgServerQueryList.mList.clear();
|
||||
gServerContainer.findObjects(queryBox, typeMask, SimpleQueryList::insertionCallback, &sgServerQueryList);
|
||||
|
||||
//return the first element
|
||||
sgServerQueryIndex = 0;
|
||||
char *buff = Con::getReturnBuffer(100);
|
||||
if (sgServerQueryList.mList.size())
|
||||
dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
|
||||
else
|
||||
buff[0] = '\0';
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleFunction( containerFindNext, const char*, 1, 1, "()"
|
||||
"@brief Get more results from a previous call to containerFindFirst().\n\n"
|
||||
"@note You must call containerFindFirst() to begin the search.\n"
|
||||
"@returns The next object found, or an empty string if nothing else was found.\n"
|
||||
"@see containerFindFirst()\n"
|
||||
"@ingroup Game")
|
||||
{
|
||||
//return the next element
|
||||
char *buff = Con::getReturnBuffer(100);
|
||||
if (sgServerQueryIndex < sgServerQueryList.mList.size())
|
||||
dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
|
||||
else
|
||||
buff[0] = '\0';
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd( Containers );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
DefineEngineFunction( setDefaultFov, void, ( F32 defaultFOV ),,
|
||||
"@brief Set the default FOV for a camera.\n"
|
||||
"@param defaultFOV The default field of view in degrees\n"
|
||||
"@ingroup CameraSystem")
|
||||
{
|
||||
sDefaultFov = mClampF(defaultFOV, MinCameraFov, MaxCameraFov);
|
||||
if(sCameraFov == sTargetFov)
|
||||
sTargetFov = sDefaultFov;
|
||||
}
|
||||
|
||||
DefineEngineFunction( setZoomSpeed, void, ( S32 speed ),,
|
||||
"@brief Set the zoom speed of the camera.\n"
|
||||
"This affects how quickly the camera changes from one field of view "
|
||||
"to another.\n"
|
||||
"@param speed The camera's zoom speed in ms per 90deg FOV change\n"
|
||||
"@ingroup CameraSystem")
|
||||
{
|
||||
sZoomSpeed = mClamp(speed, 0, MaxZoomSpeed);
|
||||
}
|
||||
|
||||
DefineEngineFunction( setFov, void, ( F32 FOV ),,
|
||||
"@brief Set the FOV of the camera.\n"
|
||||
"@param FOV The camera's new FOV in degrees\n"
|
||||
"@ingroup CameraSystem")
|
||||
{
|
||||
sTargetFov = mClampF(FOV, MinCameraFov, MaxCameraFov);
|
||||
}
|
||||
|
||||
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);
|
||||
// gResourceManager->findMatches(&match);
|
||||
|
||||
// for (U32 i = 0; i < match.numMatches(); i++)
|
||||
// {
|
||||
// U32 j;
|
||||
// Resource<TSShape> shape = ResourceManager::get().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
|
||||
|
||||
bool GameProcessCameraQuery(CameraQuery *query)
|
||||
{
|
||||
GameConnection* connection = GameConnection::getConnectionToServer();
|
||||
|
||||
if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix))
|
||||
{
|
||||
query->object = dynamic_cast<ShapeBase*>(connection->getControlObject());
|
||||
query->nearPlane = gClientSceneGraph->getNearClip();
|
||||
|
||||
// Scale the normal visible distance by the performance
|
||||
// tuning scale which we never let over 1.
|
||||
sVisDistanceScale = mClampF( sVisDistanceScale, 0.01f, 1.0f );
|
||||
query->farPlane = gClientSceneGraph->getVisibleDistance() * sVisDistanceScale;
|
||||
|
||||
F32 cameraFov;
|
||||
if(!connection->getControlCameraFov(&cameraFov))
|
||||
return false;
|
||||
|
||||
query->fov = mDegToRad(cameraFov);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameRenderWorld()
|
||||
{
|
||||
PROFILE_START(GameRenderWorld);
|
||||
FrameAllocator::setWaterMark(0);
|
||||
|
||||
gClientSceneGraph->renderScene( SPT_Diffuse );
|
||||
|
||||
// renderScene leaves some states dirty, which causes problems if GameTSCtrl is the last Gui object rendered
|
||||
GFX->updateStates();
|
||||
|
||||
AssertFatal(FrameAllocator::getWaterMark() == 0,
|
||||
"Error, someone didn't reset the water mark on the frame allocator!");
|
||||
FrameAllocator::setWaterMark(0);
|
||||
PROFILE_END();
|
||||
}
|
||||
|
||||
|
||||
static void Process3D()
|
||||
{
|
||||
MATMGR->updateTime();
|
||||
|
||||
// Update the SFX world, if there is one.
|
||||
|
||||
if( gSFX3DWorld )
|
||||
gSFX3DWorld->update();
|
||||
}
|
||||
|
||||
static void RegisterGameFunctions()
|
||||
{
|
||||
Con::addVariable( "$pref::Camera::distanceScale", TypeF32, &sVisDistanceScale,
|
||||
"A scale to apply to the normal visible distance, typically used for tuning performance.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable( "$cameraFov", TypeF32, &sConsoleCameraFov,
|
||||
"The camera's Field of View.\n\n"
|
||||
"@ingroup Game" );
|
||||
|
||||
// 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::DynamicShapeObjectType", DynamicShapeObjectType);
|
||||
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::LightObjectType", LightObjectType);
|
||||
|
||||
Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut,
|
||||
"InOut ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::In", TypeS32, &gEaseIn,
|
||||
"In ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Out", TypeS32, &gEaseOut,
|
||||
"Out ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
|
||||
Con::addVariable("Ease::Linear", TypeS32, &gEaseLinear,
|
||||
"Linear ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Quadratic", TypeS32, &gEaseQuadratic,
|
||||
"Quadratic ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Cubic", TypeS32, &gEaseCubic,
|
||||
"Cubic ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Quartic", TypeS32, &gEaseQuartic,
|
||||
"Quartic ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Quintic", TypeS32, &gEaseQuintic,
|
||||
"Quintic ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Sinusoidal", TypeS32, &gEaseSinusoidal,
|
||||
"Sinusoidal ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Exponential", TypeS32, &gEaseExponential,
|
||||
"Exponential ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Circular", TypeS32, &gEaseCircular,
|
||||
"Circular ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Elastic", TypeS32, &gEaseElastic,
|
||||
"Elastic ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Back", TypeS32, &gEaseBack,
|
||||
"Backwards ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
Con::addVariable("Ease::Bounce", TypeS32, &gEaseBounce,
|
||||
"Bounce ease for curve movement.\n"
|
||||
"@ingroup Game");
|
||||
}
|
||||
64
Engine/source/T3D/gameFunctions.h
Normal file
64
Engine/source/T3D/gameFunctions.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMEFUNCTIONS_H_
|
||||
#define _GAMEFUNCTIONS_H_
|
||||
|
||||
#ifndef _MPOINT3_H_
|
||||
#include "math/mPoint3.h"
|
||||
#endif
|
||||
#ifndef _MMATRIX_H_
|
||||
#include "math/mMatrix.h"
|
||||
#endif
|
||||
|
||||
struct CameraQuery;
|
||||
|
||||
|
||||
/// 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();
|
||||
|
||||
#endif // _GAMEFUNCTIONS_H_
|
||||
209
Engine/source/T3D/gameTSCtrl.cpp
Normal file
209
Engine/source/T3D/gameTSCtrl.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/gameTSCtrl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "T3D/gameBase/gameBase.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/shapeBase.h"
|
||||
#include "T3D/gameFunctions.h"
|
||||
#include "console/engineAPI.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);
|
||||
|
||||
// See Torque manual (.CHM) for more information
|
||||
ConsoleDocClass( GameTSCtrl,
|
||||
"@brief The main 3D viewport for a Torque 3D game.\n\n"
|
||||
"@ingroup Gui3D\n");
|
||||
|
||||
GameTSCtrl::GameTSCtrl()
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
bool GameTSCtrl::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
#ifdef TORQUE_DEMO_WATERMARK
|
||||
mWatermark.init();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
bool GameTSCtrl::processCameraQuery(CameraQuery *camq)
|
||||
{
|
||||
GameUpdateCameraFov();
|
||||
return GameProcessCameraQuery(camq);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void GameTSCtrl::renderWorld(const RectI &updateRect)
|
||||
{
|
||||
GameRenderWorld();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void GameTSCtrl::makeScriptCall(const char *func, const GuiEvent &evt) const
|
||||
{
|
||||
// write screen position
|
||||
char *sp = Con::getArgBuffer(32);
|
||||
dSprintf(sp, 32, "%d %d", evt.mousePoint.x, evt.mousePoint.y);
|
||||
|
||||
// write world position
|
||||
char *wp = Con::getArgBuffer(32);
|
||||
Point3F camPos;
|
||||
mLastCameraQuery.cameraMatrix.getColumn(3, &camPos);
|
||||
dSprintf(wp, 32, "%g %g %g", camPos.x, camPos.y, camPos.z);
|
||||
|
||||
// write click vector
|
||||
char *vec = Con::getArgBuffer(32);
|
||||
Point3F fp(evt.mousePoint.x, evt.mousePoint.y, 1.0);
|
||||
Point3F ray;
|
||||
unproject(fp, &ray);
|
||||
ray -= camPos;
|
||||
ray.normalizeSafe();
|
||||
dSprintf(vec, 32, "%g %g %g", ray.x, ray.y, ray.z);
|
||||
|
||||
Con::executef( (SimObject*)this, func, sp, wp, vec );
|
||||
}
|
||||
|
||||
void GameTSCtrl::onMouseDown(const GuiEvent &evt)
|
||||
{
|
||||
Parent::onMouseDown(evt);
|
||||
if( isMethod( "onMouseDown" ) )
|
||||
makeScriptCall( "onMouseDown", evt );
|
||||
}
|
||||
|
||||
void GameTSCtrl::onRightMouseDown(const GuiEvent &evt)
|
||||
{
|
||||
Parent::onRightMouseDown(evt);
|
||||
if( isMethod( "onRightMouseDown" ) )
|
||||
makeScriptCall( "onRightMouseDown", evt );
|
||||
}
|
||||
|
||||
void GameTSCtrl::onMiddleMouseDown(const GuiEvent &evt)
|
||||
{
|
||||
Parent::onMiddleMouseDown(evt);
|
||||
if( isMethod( "onMiddleMouseDown" ) )
|
||||
makeScriptCall( "onMiddleMouseDown", evt );
|
||||
}
|
||||
|
||||
void GameTSCtrl::onMouseUp(const GuiEvent &evt)
|
||||
{
|
||||
Parent::onMouseUp(evt);
|
||||
if( isMethod( "onMouseUp" ) )
|
||||
makeScriptCall( "onMouseUp", evt );
|
||||
}
|
||||
|
||||
void GameTSCtrl::onRightMouseUp(const GuiEvent &evt)
|
||||
{
|
||||
Parent::onRightMouseUp(evt);
|
||||
if( isMethod( "onRightMouseUp" ) )
|
||||
makeScriptCall( "onRightMouseUp", evt );
|
||||
}
|
||||
|
||||
void GameTSCtrl::onMiddleMouseUp(const GuiEvent &evt)
|
||||
{
|
||||
Parent::onMiddleMouseUp(evt);
|
||||
if( isMethod( "onMiddleMouseUp" ) )
|
||||
makeScriptCall( "onMiddleMouseUp", evt );
|
||||
}
|
||||
|
||||
void GameTSCtrl::onMouseMove(const GuiEvent &evt)
|
||||
{
|
||||
if(gSnapLine)
|
||||
return;
|
||||
|
||||
MatrixF mat;
|
||||
Point3F vel;
|
||||
if ( GameGetCameraTransform(&mat, &vel) )
|
||||
{
|
||||
Point3F pos;
|
||||
mat.getColumn(3,&pos);
|
||||
Point3F screenPoint((F32)evt.mousePoint.x, (F32)evt.mousePoint.y, -1.0f);
|
||||
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 || true)
|
||||
Parent::onRender(offset, updateRect);
|
||||
|
||||
#ifdef TORQUE_DEMO_WATERMARK
|
||||
mWatermark.render(getExtent());
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(snapToggle, void, (),,
|
||||
"@brief Prevents mouse movement from being processed\n\n"
|
||||
|
||||
"In the source, whenever a mouse move event occurs "
|
||||
"GameTSCtrl::onMouseMove() is called. Whenever snapToggle() "
|
||||
"is called, it will flag a variable that can prevent this "
|
||||
"from happening: gSnapLine. This variable is not exposed to "
|
||||
"script, so you need to call this function to trigger it.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"// Snapping is off by default, so we will toggle\n"
|
||||
"// it on first:\n"
|
||||
"PlayGui.snapToggle();\n\n"
|
||||
"// Mouse movement should be disabled\n"
|
||||
"// Let's turn it back on\n"
|
||||
"PlayGui.snapToggle();\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@ingroup GuiGame")
|
||||
{
|
||||
gSnapLine = !gSnapLine;
|
||||
}
|
||||
//
|
||||
//ConsoleFunction( snapToggle, void, 1, 1, "()" )
|
||||
//{
|
||||
// gSnapLine = !gSnapLine;
|
||||
//}
|
||||
78
Engine/source/T3D/gameTSCtrl.h
Normal file
78
Engine/source/T3D/gameTSCtrl.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GAMETSCTRL_H_
|
||||
#define _GAMETSCTRL_H_
|
||||
|
||||
#ifndef _GAME_H_
|
||||
#include "app/game.h"
|
||||
#endif
|
||||
#ifndef _GUITSCONTROL_H_
|
||||
#include "gui/3d/guiTSControl.h"
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_DEMO_WATERMARK
|
||||
#ifndef _WATERMARK_H_
|
||||
#include "demo/watermark/watermark.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class ProjectileData;
|
||||
class GameBase;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class GameTSCtrl : public GuiTSCtrl
|
||||
{
|
||||
private:
|
||||
typedef GuiTSCtrl Parent;
|
||||
|
||||
#ifdef TORQUE_DEMO_WATERMARK
|
||||
Watermark mWatermark;
|
||||
#endif
|
||||
|
||||
void makeScriptCall(const char *func, const GuiEvent &evt) const;
|
||||
|
||||
public:
|
||||
GameTSCtrl();
|
||||
|
||||
DECLARE_CONOBJECT(GameTSCtrl);
|
||||
DECLARE_DESCRIPTION( "A control that renders a 3D view from the current control object." );
|
||||
|
||||
bool processCameraQuery(CameraQuery *query);
|
||||
void renderWorld(const RectI &updateRect);
|
||||
|
||||
// GuiControl
|
||||
virtual void onMouseDown(const GuiEvent &evt);
|
||||
virtual void onRightMouseDown(const GuiEvent &evt);
|
||||
virtual void onMiddleMouseDown(const GuiEvent &evt);
|
||||
|
||||
virtual void onMouseUp(const GuiEvent &evt);
|
||||
virtual void onRightMouseUp(const GuiEvent &evt);
|
||||
virtual void onMiddleMouseUp(const GuiEvent &evt);
|
||||
|
||||
void onMouseMove(const GuiEvent &evt);
|
||||
void onRender(Point2I offset, const RectI &updateRect);
|
||||
|
||||
virtual bool onAdd();
|
||||
};
|
||||
|
||||
#endif
|
||||
556
Engine/source/T3D/groundPlane.cpp
Normal file
556
Engine/source/T3D/groundPlane.cpp
Normal file
|
|
@ -0,0 +1,556 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/groundPlane.h"
|
||||
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "materials/sceneData.h"
|
||||
#include "materials/materialDefinition.h"
|
||||
#include "materials/materialManager.h"
|
||||
#include "materials/baseMatInstance.h"
|
||||
#include "math/util/frustum.h"
|
||||
#include "math/mPlane.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "collision/boxConvex.h"
|
||||
#include "collision/abstractPolyList.h"
|
||||
#include "T3D/physics/physicsPlugin.h"
|
||||
#include "T3D/physics/physicsBody.h"
|
||||
#include "T3D/physics/physicsCollision.h"
|
||||
|
||||
|
||||
/// Minimum square size allowed. This is a cheap way to limit the amount
|
||||
/// of geometry possibly generated by the GroundPlane (vertex buffers have a
|
||||
/// limit, too). Dynamically clipping extents into range is a problem since the
|
||||
/// location of the horizon depends on the camera orientation. Just shifting
|
||||
/// squareSize as needed also doesn't work as that causes different geometry to
|
||||
/// be generated depending on the viewpoint and orientation which affects the
|
||||
/// texturing.
|
||||
static const F32 sMIN_SQUARE_SIZE = 16;
|
||||
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1( GroundPlane );
|
||||
|
||||
ConsoleDocClass( GroundPlane,
|
||||
"@brief An infinite plane extending in all direction.\n\n"
|
||||
|
||||
"%GroundPlane is useful for setting up simple testing scenes, or it can be "
|
||||
"placed under an existing scene to keep objects from falling into 'nothing'.\n\n"
|
||||
|
||||
"%GroundPlane may not be moved or rotated, it is always at the world origin.\n\n"
|
||||
|
||||
"@ingroup Terrain"
|
||||
);
|
||||
|
||||
GroundPlane::GroundPlane()
|
||||
: mSquareSize( 128.0f ),
|
||||
mScaleU( 1.0f ),
|
||||
mScaleV( 1.0f ),
|
||||
mMaterial( NULL ),
|
||||
mMin( 0.0f, 0.0f ),
|
||||
mMax( 0.0f, 0.0f ),
|
||||
mPhysicsRep( NULL )
|
||||
{
|
||||
mTypeMask |= StaticObjectType | StaticShapeObjectType;
|
||||
mNetFlags.set( Ghostable | ScopeAlways );
|
||||
|
||||
mConvexList = new Convex;
|
||||
}
|
||||
|
||||
GroundPlane::~GroundPlane()
|
||||
{
|
||||
if( mMaterial )
|
||||
SAFE_DELETE( mMaterial );
|
||||
|
||||
mConvexList->nukeList();
|
||||
SAFE_DELETE( mConvexList );
|
||||
}
|
||||
|
||||
void GroundPlane::initPersistFields()
|
||||
{
|
||||
addGroup( "Plane" );
|
||||
|
||||
addField( "squareSize", TypeF32, Offset( mSquareSize, GroundPlane ), "Square size in meters to which %GroundPlane subdivides its geometry." );
|
||||
addField( "scaleU", TypeF32, Offset( mScaleU, GroundPlane ), "Scale of texture repeat in the U direction." );
|
||||
addField( "scaleV", TypeF32, Offset( mScaleV, GroundPlane ), "Scale of texture repeat in the V direction." );
|
||||
addField( "material", TypeMaterialName, Offset( mMaterialName, GroundPlane ), "Name of Material used to render %GroundPlane's surface." );
|
||||
|
||||
endGroup( "Plane" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
|
||||
removeField( "scale" );
|
||||
removeField( "position" );
|
||||
removeField( "rotation" );
|
||||
}
|
||||
|
||||
bool GroundPlane::onAdd()
|
||||
{
|
||||
if( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
if( isClientObject() )
|
||||
_updateMaterial();
|
||||
|
||||
if( mSquareSize < sMIN_SQUARE_SIZE )
|
||||
{
|
||||
Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
|
||||
mSquareSize = sMIN_SQUARE_SIZE;
|
||||
}
|
||||
|
||||
Parent::setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
|
||||
Parent::setTransform( MatrixF::Identity );
|
||||
setGlobalBounds();
|
||||
resetWorldBox();
|
||||
|
||||
addToScene();
|
||||
|
||||
if ( PHYSICSMGR )
|
||||
{
|
||||
PhysicsCollision *colShape = PHYSICSMGR->createCollision();
|
||||
colShape->addPlane( PlaneF( Point3F::Zero, Point3F( 0, 0, 1 ) ) );
|
||||
|
||||
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
|
||||
mPhysicsRep = PHYSICSMGR->createBody();
|
||||
mPhysicsRep->init( colShape, 0, 0, this, world );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GroundPlane::onRemove()
|
||||
{
|
||||
SAFE_DELETE( mPhysicsRep );
|
||||
|
||||
removeFromScene();
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void GroundPlane::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
setMaskBits( U32( -1 ) );
|
||||
|
||||
if( mSquareSize < sMIN_SQUARE_SIZE )
|
||||
{
|
||||
Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
|
||||
mSquareSize = sMIN_SQUARE_SIZE;
|
||||
}
|
||||
|
||||
setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
|
||||
}
|
||||
|
||||
void GroundPlane::setTransform( const MatrixF &mat )
|
||||
{
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
void GroundPlane::setScale( const Point3F& scale )
|
||||
{
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
U32 GroundPlane::packUpdate( NetConnection* connection, U32 mask, BitStream* stream )
|
||||
{
|
||||
U32 retMask = Parent::packUpdate( connection, mask, stream );
|
||||
|
||||
stream->write( mSquareSize );
|
||||
stream->write( mScaleU );
|
||||
stream->write( mScaleV );
|
||||
stream->write( mMaterialName );
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void GroundPlane::unpackUpdate( NetConnection* connection, BitStream* stream )
|
||||
{
|
||||
Parent::unpackUpdate( connection, stream );
|
||||
|
||||
stream->read( &mSquareSize );
|
||||
stream->read( &mScaleU );
|
||||
stream->read( &mScaleV );
|
||||
stream->read( &mMaterialName );
|
||||
|
||||
// If we're added then something possibly changed in
|
||||
// the editor... do an update of the material and the
|
||||
// geometry.
|
||||
if ( isProperlyAdded() )
|
||||
{
|
||||
_updateMaterial();
|
||||
mVertexBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GroundPlane::_updateMaterial()
|
||||
{
|
||||
if( mMaterialName.isEmpty() )
|
||||
{
|
||||
Con::warnf( "GroundPlane::_updateMaterial - no material set; defaulting to 'WarningMaterial'" );
|
||||
mMaterialName = "WarningMaterial";
|
||||
}
|
||||
|
||||
// If the material name matches then don't
|
||||
// bother updating it.
|
||||
if ( mMaterial &&
|
||||
mMaterialName.compare( mMaterial->getMaterial()->getName() ) == 0 )
|
||||
return;
|
||||
|
||||
SAFE_DELETE( mMaterial );
|
||||
|
||||
mMaterial = MATMGR->createMatInstance( mMaterialName, getGFXVertexFormat< VertexType >() );
|
||||
if ( !mMaterial )
|
||||
Con::errorf( "GroundPlane::_updateMaterial - no material called '%s'", mMaterialName.c_str() );
|
||||
}
|
||||
|
||||
bool GroundPlane::castRay( const Point3F& start, const Point3F& end, RayInfo* info )
|
||||
{
|
||||
PlaneF plane( Point3F( 0.0f, 0.0f, 0.0f ), Point3F( 0.0f, 0.0f, 1.0f ) );
|
||||
|
||||
F32 t = plane.intersect( start, end );
|
||||
if( t >= 0.0 && t <= 1.0 )
|
||||
{
|
||||
info->t = t;
|
||||
info->setContactPoint( start, end );
|
||||
info->normal.set( 0, 0, 1 );
|
||||
info->material = mMaterial;
|
||||
info->object = this;
|
||||
info->distance = 0;
|
||||
info->faceDot = 0;
|
||||
info->texCoord.set( 0, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GroundPlane::buildConvex( const Box3F& box, Convex* convex )
|
||||
{
|
||||
mConvexList->collectGarbage();
|
||||
|
||||
Box3F planeBox = getPlaneBox();
|
||||
if ( !box.isOverlapped( planeBox ) )
|
||||
return;
|
||||
|
||||
// See if we already have a convex in the working set.
|
||||
BoxConvex *boxConvex = NULL;
|
||||
CollisionWorkingList &wl = convex->getWorkingList();
|
||||
CollisionWorkingList *itr = wl.wLink.mNext;
|
||||
for ( ; itr != &wl; itr = itr->wLink.mNext )
|
||||
{
|
||||
if ( itr->mConvex->getType() == BoxConvexType &&
|
||||
itr->mConvex->getObject() == this )
|
||||
{
|
||||
boxConvex = (BoxConvex*)itr->mConvex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !boxConvex )
|
||||
{
|
||||
boxConvex = new BoxConvex;
|
||||
mConvexList->registerObject( boxConvex );
|
||||
boxConvex->init( this );
|
||||
|
||||
convex->addToWorkingList( boxConvex );
|
||||
}
|
||||
|
||||
// Update our convex to best match the queried box
|
||||
if ( boxConvex )
|
||||
{
|
||||
Point3F queryCenter = box.getCenter();
|
||||
|
||||
boxConvex->mCenter = Point3F( queryCenter.x, queryCenter.y, -GROUND_PLANE_BOX_HEIGHT_HALF );
|
||||
boxConvex->mSize = Point3F( box.getExtents().x,
|
||||
box.getExtents().y,
|
||||
GROUND_PLANE_BOX_HEIGHT_HALF );
|
||||
}
|
||||
}
|
||||
|
||||
bool GroundPlane::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F&, const SphereF& )
|
||||
{
|
||||
polyList->setObject( this );
|
||||
polyList->setTransform( &MatrixF::Identity, Point3F( 1.0f, 1.0f, 1.0f ) );
|
||||
|
||||
Box3F planeBox = getPlaneBox();
|
||||
polyList->addBox( planeBox, mMaterial );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GroundPlane::prepRenderImage( SceneRenderState* state )
|
||||
{
|
||||
PROFILE_SCOPE( GroundPlane_prepRenderImage );
|
||||
|
||||
// TODO: Should we skip rendering the ground plane into
|
||||
// the shadows? Its not like you can ever get under it.
|
||||
|
||||
if ( !mMaterial )
|
||||
return;
|
||||
|
||||
// If we don't have a material instance after the override then
|
||||
// we can skip rendering all together.
|
||||
BaseMatInstance *matInst = state->getOverrideMaterial( mMaterial );
|
||||
if ( !matInst )
|
||||
return;
|
||||
|
||||
PROFILE_SCOPE( GroundPlane_prepRender );
|
||||
|
||||
// Update the geometry.
|
||||
createGeometry( state->getFrustum() );
|
||||
if( mVertexBuffer.isNull() )
|
||||
return;
|
||||
|
||||
// Add a render instance.
|
||||
|
||||
RenderPassManager* pass = state->getRenderPass();
|
||||
MeshRenderInst* ri = pass->allocInst< MeshRenderInst >();
|
||||
|
||||
ri->type = RenderPassManager::RIT_Mesh;
|
||||
ri->vertBuff = &mVertexBuffer;
|
||||
ri->primBuff = &mPrimitiveBuffer;
|
||||
ri->prim = &mPrimitive;
|
||||
ri->matInst = matInst;
|
||||
ri->objectToWorld = pass->allocUniqueXform( MatrixF::Identity );
|
||||
ri->worldToCamera = pass->allocSharedXform( RenderPassManager::View );
|
||||
ri->projection = pass->allocSharedXform( RenderPassManager::Projection );
|
||||
ri->visibility = 1.0f;
|
||||
ri->translucentSort = matInst->getMaterial()->isTranslucent();
|
||||
ri->defaultKey = matInst->getStateHint();
|
||||
|
||||
if( ri->translucentSort )
|
||||
ri->type = RenderPassManager::RIT_Translucent;
|
||||
|
||||
// If we need lights then set them up.
|
||||
if ( matInst->isForwardLit() )
|
||||
{
|
||||
LightQuery query;
|
||||
query.init( getWorldSphere() );
|
||||
query.getLights( ri->lights, 8 );
|
||||
}
|
||||
|
||||
pass->addInst( ri );
|
||||
}
|
||||
|
||||
/// Generate a subset of the ground plane matching the given frustum.
|
||||
|
||||
void GroundPlane::createGeometry( const Frustum& frustum )
|
||||
{
|
||||
PROFILE_SCOPE( GroundPlane_createGeometry );
|
||||
|
||||
enum { MAX_WIDTH = 256, MAX_HEIGHT = 256 };
|
||||
|
||||
// Project the frustum onto the XY grid.
|
||||
|
||||
Point2F min;
|
||||
Point2F max;
|
||||
|
||||
projectFrustum( frustum, mSquareSize, min, max );
|
||||
|
||||
// Early out if the grid projection hasn't changed.
|
||||
|
||||
if( mVertexBuffer.isValid() &&
|
||||
min == mMin &&
|
||||
max == mMax )
|
||||
return;
|
||||
|
||||
mMin = min;
|
||||
mMax = max;
|
||||
|
||||
// Determine the grid extents and allocate the buffers.
|
||||
// Adjust square size permanently if with the given frustum,
|
||||
// we end up producing more than a certain limit of geometry.
|
||||
// This is to prevent this code from causing trouble with
|
||||
// long viewing distances.
|
||||
// This only affects the client object, of course, and thus
|
||||
// has no permanent effect.
|
||||
|
||||
U32 width = mCeil( ( max.x - min.x ) / mSquareSize );
|
||||
if( width > MAX_WIDTH )
|
||||
{
|
||||
mSquareSize = mCeil( ( max.x - min.x ) / MAX_WIDTH );
|
||||
width = MAX_WIDTH;
|
||||
}
|
||||
else if( !width )
|
||||
width = 1;
|
||||
|
||||
U32 height = mCeil( ( max.y - min.y ) / mSquareSize );
|
||||
if( height > MAX_HEIGHT )
|
||||
{
|
||||
mSquareSize = mCeil( ( max.y - min.y ) / MAX_HEIGHT );
|
||||
height = MAX_HEIGHT;
|
||||
}
|
||||
else if( !height )
|
||||
height = 1;
|
||||
|
||||
const U32 numVertices = ( width + 1 ) * ( height + 1 );
|
||||
const U32 numTriangles = width * height * 2;
|
||||
|
||||
// Only reallocate if the buffers are too small.
|
||||
if ( mVertexBuffer.isNull() || numVertices > mVertexBuffer->mNumVerts )
|
||||
{
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
mVertexBuffer.set( GFX, numVertices, GFXBufferTypeVolatile );
|
||||
#else
|
||||
mVertexBuffer.set( GFX, numVertices, GFXBufferTypeDynamic );
|
||||
#endif
|
||||
}
|
||||
if ( mPrimitiveBuffer.isNull() || numTriangles > mPrimitiveBuffer->mPrimitiveCount )
|
||||
{
|
||||
#if defined(TORQUE_OS_XENON)
|
||||
mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeVolatile );
|
||||
#else
|
||||
mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeDynamic );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Generate the grid.
|
||||
|
||||
generateGrid( width, height, mSquareSize, min, max, mVertexBuffer, mPrimitiveBuffer );
|
||||
|
||||
// Set up GFX primitive.
|
||||
|
||||
mPrimitive.type = GFXTriangleList;
|
||||
mPrimitive.numPrimitives = numTriangles;
|
||||
mPrimitive.numVertices = numVertices;
|
||||
}
|
||||
|
||||
/// Project the given frustum onto the ground plane and return the XY bounds in world space.
|
||||
|
||||
void GroundPlane::projectFrustum( const Frustum& frustum, F32 squareSize, Point2F& outMin, Point2F& outMax )
|
||||
{
|
||||
// Get the frustum's min and max XY coordinates.
|
||||
|
||||
const Box3F bounds = frustum.getBounds();
|
||||
|
||||
Point2F minPt( bounds.minExtents.x, bounds.minExtents.y );
|
||||
Point2F maxPt( bounds.maxExtents.x, bounds.maxExtents.y );
|
||||
|
||||
// Round the min and max coordinates so they align on the grid.
|
||||
|
||||
minPt.x -= mFmod( minPt.x, squareSize );
|
||||
minPt.y -= mFmod( minPt.y, squareSize );
|
||||
|
||||
F32 maxDeltaX = mFmod( maxPt.x, squareSize );
|
||||
F32 maxDeltaY = mFmod( maxPt.y, squareSize );
|
||||
|
||||
if( maxDeltaX != 0.0f )
|
||||
maxPt.x += ( squareSize - maxDeltaX );
|
||||
if( maxDeltaY != 0.0f )
|
||||
maxPt.y += ( squareSize - maxDeltaY );
|
||||
|
||||
// Add a safezone, so we don't touch the clipping planes.
|
||||
|
||||
minPt.x -= squareSize; minPt.y -= squareSize;
|
||||
maxPt.x += squareSize; maxPt.y += squareSize;
|
||||
|
||||
outMin = minPt;
|
||||
outMax = maxPt;
|
||||
}
|
||||
|
||||
/// Generate a triangulated grid spanning the given bounds into the given buffers.
|
||||
|
||||
void GroundPlane::generateGrid( U32 width, U32 height, F32 squareSize,
|
||||
const Point2F& min, const Point2F& max,
|
||||
GFXVertexBufferHandle< VertexType >& outVertices,
|
||||
GFXPrimitiveBufferHandle& outPrimitives )
|
||||
{
|
||||
// Generate the vertices.
|
||||
|
||||
VertexType* vertices = outVertices.lock();
|
||||
for( F32 y = min.y; y <= max.y; y += squareSize )
|
||||
for( F32 x = min.x; x <= max.x; x += squareSize )
|
||||
{
|
||||
vertices->point.x = x;
|
||||
vertices->point.y = y;
|
||||
vertices->point.z = 0.0;
|
||||
|
||||
vertices->texCoord.x = ( x / squareSize ) * mScaleU;
|
||||
vertices->texCoord.y = ( y / squareSize ) * -mScaleV;
|
||||
|
||||
vertices->normal.x = 0.0f;
|
||||
vertices->normal.y = 0.0f;
|
||||
vertices->normal.z = 1.0f;
|
||||
|
||||
vertices->tangent.x = 1.0f;
|
||||
vertices->tangent.y = 0.0f;
|
||||
vertices->tangent.z = 0.0f;
|
||||
|
||||
vertices->binormal.x = 0.0f;
|
||||
vertices->binormal.y = 1.0f;
|
||||
vertices->binormal.z = 0.0f;
|
||||
|
||||
vertices++;
|
||||
}
|
||||
outVertices.unlock();
|
||||
|
||||
// Generate the indices.
|
||||
|
||||
U16* indices;
|
||||
outPrimitives.lock( &indices );
|
||||
|
||||
U16 corner1 = 0;
|
||||
U16 corner2 = 1;
|
||||
U16 corner3 = width + 1;
|
||||
U16 corner4 = width + 2;
|
||||
|
||||
for( U32 y = 0; y < height; ++ y )
|
||||
{
|
||||
for( U32 x = 0; x < width; ++ x )
|
||||
{
|
||||
indices[ 0 ] = corner3;
|
||||
indices[ 1 ] = corner2;
|
||||
indices[ 2 ] = corner1;
|
||||
|
||||
indices += 3;
|
||||
|
||||
indices[ 0 ] = corner3;
|
||||
indices[ 1 ] = corner4;
|
||||
indices[ 2 ] = corner2;
|
||||
|
||||
indices += 3;
|
||||
|
||||
corner1 ++;
|
||||
corner2 ++;
|
||||
corner3 ++;
|
||||
corner4 ++;
|
||||
}
|
||||
|
||||
corner1 ++;
|
||||
corner2 ++;
|
||||
corner3 ++;
|
||||
corner4 ++;
|
||||
}
|
||||
|
||||
outPrimitives.unlock();
|
||||
}
|
||||
|
||||
DefineEngineMethod( GroundPlane, postApply, void, (),,
|
||||
"Intended as a helper to developers and editor scripts.\n"
|
||||
"Force trigger an inspectPostApply. This will transmit "
|
||||
"material and other fields to client objects."
|
||||
)
|
||||
{
|
||||
object->inspectPostApply();
|
||||
}
|
||||
138
Engine/source/T3D/groundPlane.h
Normal file
138
Engine/source/T3D/groundPlane.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TORQUE_T3D_GROUNDPLANE_H_
|
||||
#define _TORQUE_T3D_GROUNDPLANE_H_
|
||||
|
||||
#ifndef _SCENEOBJECT_H_
|
||||
#include "scene/sceneObject.h"
|
||||
#endif
|
||||
#ifndef _GFXVERTEXBUFFER_H_
|
||||
#include "gfx/gfxVertexBuffer.h"
|
||||
#endif
|
||||
#ifndef _GFXPRIMITIVEBUFFER_H_
|
||||
#include "gfx/gfxPrimitiveBuffer.h"
|
||||
#endif
|
||||
|
||||
class PhysicsBody;
|
||||
class BaseMatInstance;
|
||||
|
||||
|
||||
/// A virtually infinite XY ground plane primitive.
|
||||
///
|
||||
/// For rendering, a subset of the plane spanning the view frustum is generated
|
||||
/// and rendered. Tesselation is determined by the given squareSize property.
|
||||
///
|
||||
/// For collision detection, a finite bounding box is used to deal with finite
|
||||
/// precision of floating-point operations (we can't use floating-point infinity
|
||||
/// as infinity*0 is undefined.)
|
||||
///
|
||||
/// The ground plane can be textured like regular geometry by assigning a material
|
||||
/// name to its 'material' property. UVs mirror grid coordinates so that when
|
||||
/// using UV wrapping, textures will tile nicely.
|
||||
|
||||
|
||||
class GroundPlane : public SceneObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef SceneObject Parent;
|
||||
|
||||
DECLARE_CONOBJECT( GroundPlane );
|
||||
|
||||
GroundPlane();
|
||||
virtual ~GroundPlane();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream );
|
||||
virtual void unpackUpdate( NetConnection* connection, BitStream* stream );
|
||||
virtual void prepRenderImage( SceneRenderState* state );
|
||||
virtual bool castRay( const Point3F& start, const Point3F& end, RayInfo* info );
|
||||
virtual void buildConvex( const Box3F& box, Convex* convex );
|
||||
virtual bool buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere );
|
||||
virtual void inspectPostApply();
|
||||
virtual void setTransform( const MatrixF &mat );
|
||||
virtual void setScale( const Point3F& scale );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
protected:
|
||||
|
||||
typedef GFXVertexPNTBT VertexType;
|
||||
|
||||
void _updateMaterial();
|
||||
|
||||
void createGeometry( const Frustum& frustum );
|
||||
void projectFrustum( const Frustum& frustum, F32 squareSize,
|
||||
Point2F& outMin, Point2F& outMax );
|
||||
void generateGrid( U32 width, U32 height, F32 squareSize,
|
||||
const Point2F& min, const Point2F& max,
|
||||
GFXVertexBufferHandle< VertexType >& outVertices,
|
||||
GFXPrimitiveBufferHandle& outPrimitives );
|
||||
|
||||
Box3F getPlaneBox();
|
||||
|
||||
private:
|
||||
|
||||
typedef GFXVertexBufferHandle< VertexType > VertexBuffer;
|
||||
typedef GFXPrimitiveBufferHandle PrimitiveBuffer;
|
||||
|
||||
F32 mSquareSize; ///< World units per grid cell edge.
|
||||
F32 mScaleU; ///< Scale factor for U texture coordinates.
|
||||
F32 mScaleV; ///< Scale factor for V texture coordinates.
|
||||
String mMaterialName; ///< Object name of material to use.
|
||||
BaseMatInstance* mMaterial; ///< Instantiated material based on given material name.
|
||||
|
||||
PhysicsBody *mPhysicsRep;
|
||||
|
||||
/// @name Rendering State
|
||||
/// @{
|
||||
|
||||
Point2F mMin;
|
||||
Point2F mMax;
|
||||
VertexBuffer mVertexBuffer;
|
||||
PrimitiveBuffer mPrimitiveBuffer;
|
||||
GFXPrimitive mPrimitive;
|
||||
|
||||
/// @}
|
||||
|
||||
Convex* mConvexList; ///< List of collision convexes we have created; for cleanup.
|
||||
};
|
||||
|
||||
static const F32 GROUND_PLANE_BOX_HEIGHT_HALF = 1.0f;
|
||||
static const F32 GROUND_PLANE_BOX_EXTENT_HALF = 16000.0f;
|
||||
|
||||
inline Box3F GroundPlane::getPlaneBox()
|
||||
{
|
||||
Box3F planeBox;
|
||||
|
||||
planeBox.minExtents = Point3F( - GROUND_PLANE_BOX_EXTENT_HALF,
|
||||
- GROUND_PLANE_BOX_EXTENT_HALF,
|
||||
- 0.05f );
|
||||
planeBox.maxExtents = Point3F( GROUND_PLANE_BOX_EXTENT_HALF,
|
||||
GROUND_PLANE_BOX_EXTENT_HALF,
|
||||
0.05f );
|
||||
return planeBox;
|
||||
}
|
||||
|
||||
#endif // _TORQUE_T3D_GROUNDPLANE_H_
|
||||
496
Engine/source/T3D/guiMaterialPreview.cpp
Normal file
496
Engine/source/T3D/guiMaterialPreview.cpp
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Original header:
|
||||
// GuiMaterialPreview Control for Material Editor Written by Travis Vroman of Gaslight Studios
|
||||
// Updated 2-14-09
|
||||
// Portions based off Constructor viewport code.
|
||||
|
||||
#include "console/engineAPI.h"
|
||||
#include "T3D/guiMaterialPreview.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "lighting/lightManager.h"
|
||||
#include "lighting/lightInfo.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
|
||||
// GuiMaterialPreview
|
||||
GuiMaterialPreview::GuiMaterialPreview()
|
||||
: mMaxOrbitDist(5.0f),
|
||||
mMinOrbitDist(0.0f),
|
||||
mOrbitDist(5.0f),
|
||||
mMouseState(None),
|
||||
mModel(NULL),
|
||||
mLastMousePoint(0, 0),
|
||||
lastRenderTime(0),
|
||||
runThread(0),
|
||||
mFakeSun(NULL)
|
||||
{
|
||||
mActive = true;
|
||||
mCameraMatrix.identity();
|
||||
mCameraRot.set( mDegToRad(30.0f), 0, mDegToRad(-30.0f) );
|
||||
mCameraPos.set(0.0f, 1.75f, 1.25f);
|
||||
mCameraMatrix.setColumn(3, mCameraPos);
|
||||
mOrbitPos.set(0.0f, 0.0f, 0.0f);
|
||||
mTransStep = 0.01f;
|
||||
mTranMult = 4.0;
|
||||
mLightTransStep = 0.01f;
|
||||
mLightTranMult = 4.0;
|
||||
mOrbitRelPos = Point3F(0,0,0);
|
||||
|
||||
// By default don't do dynamic reflection
|
||||
// updates for this viewport.
|
||||
mReflectPriority = 0.0f;
|
||||
}
|
||||
|
||||
GuiMaterialPreview::~GuiMaterialPreview()
|
||||
{
|
||||
SAFE_DELETE(mModel);
|
||||
SAFE_DELETE(mFakeSun);
|
||||
}
|
||||
|
||||
bool GuiMaterialPreview::onWake()
|
||||
{
|
||||
if( !Parent::onWake() )
|
||||
return false;
|
||||
|
||||
if (!mFakeSun)
|
||||
mFakeSun = LightManager::createLightInfo();
|
||||
|
||||
mFakeSun->setColor( ColorF( 1.0f, 1.0f, 1.0f ) );
|
||||
mFakeSun->setAmbient( ColorF( 0.5f, 0.5f, 0.5f ) );
|
||||
mFakeSun->setDirection( VectorF( 0.0f, 0.707f, -0.707f ) );
|
||||
mFakeSun->setPosition( mFakeSun->getDirection() * -10000.0f );
|
||||
mFakeSun->setRange( 2000000.0f );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function allows the viewport's ambient color to be changed. This is exposed to script below.
|
||||
void GuiMaterialPreview::setAmbientLightColor( F32 r, F32 g, F32 b )
|
||||
{
|
||||
ColorF temp(r, g, b);
|
||||
temp.clamp();
|
||||
GuiMaterialPreview::mFakeSun->setAmbient( temp );
|
||||
}
|
||||
|
||||
// This function allows the light's color to be changed. This is exposed to script below.
|
||||
void GuiMaterialPreview::setLightColor( F32 r, F32 g, F32 b )
|
||||
{
|
||||
ColorF temp(r, g, b);
|
||||
temp.clamp();
|
||||
GuiMaterialPreview::mFakeSun->setColor( temp );
|
||||
}
|
||||
|
||||
// This function is for moving the light in the scene. This needs to be adjusted to keep the light
|
||||
// from getting all out of whack. For now, we'll just rely on the reset function if we need it
|
||||
// fixed.
|
||||
void GuiMaterialPreview::setLightTranslate(S32 modifier, F32 xstep, F32 ystep)
|
||||
{
|
||||
F32 _lighttransstep = (modifier & SI_SHIFT ? mLightTransStep : (mLightTransStep*mLightTranMult));
|
||||
|
||||
Point3F relativeLightDirection = GuiMaterialPreview::mFakeSun->getDirection();
|
||||
// May be able to get rid of this. For now, it helps to fix the position of the light if i gets messed up.
|
||||
if (modifier & SI_PRIMARY_CTRL)
|
||||
{
|
||||
relativeLightDirection.x += ( xstep * _lighttransstep * -1 );//need to invert this for some reason. Otherwise, it's backwards.
|
||||
relativeLightDirection.y += ( ystep * _lighttransstep );
|
||||
GuiMaterialPreview::mFakeSun->setDirection(relativeLightDirection);
|
||||
}
|
||||
// Default action taken by mouse wheel clicking.
|
||||
else
|
||||
{
|
||||
relativeLightDirection.x += ( xstep * _lighttransstep * -1 ); //need to invert this for some reason. Otherwise, it's backwards.
|
||||
relativeLightDirection.z += ( ystep * _lighttransstep );
|
||||
GuiMaterialPreview::mFakeSun->setDirection(relativeLightDirection);
|
||||
}
|
||||
}
|
||||
|
||||
// This is for panning the viewport camera.
|
||||
void GuiMaterialPreview::setTranslate(S32 modifier, F32 xstep, F32 ystep)
|
||||
{
|
||||
F32 transstep = (modifier & SI_SHIFT ? mTransStep : (mTransStep*mTranMult));
|
||||
|
||||
F32 nominalDistance = 20.0;
|
||||
Point3F vec = mCameraPos;
|
||||
vec -= mOrbitPos;
|
||||
transstep *= vec.len() / nominalDistance;
|
||||
|
||||
if (modifier & SI_PRIMARY_CTRL)
|
||||
{
|
||||
mOrbitRelPos.x += ( xstep * transstep );
|
||||
mOrbitRelPos.y += ( ystep * transstep );
|
||||
}
|
||||
else
|
||||
{
|
||||
mOrbitRelPos.x += ( xstep * transstep );
|
||||
mOrbitRelPos.z += ( ystep * transstep );
|
||||
}
|
||||
}
|
||||
|
||||
// Left Click
|
||||
void GuiMaterialPreview::onMouseDown(const GuiEvent &event)
|
||||
{
|
||||
mMouseState = MovingLight;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
// Left Click Release
|
||||
void GuiMaterialPreview::onMouseUp(const GuiEvent &event)
|
||||
{
|
||||
mouseUnlock();
|
||||
mMouseState = None;
|
||||
}
|
||||
|
||||
// Left Click Drag
|
||||
void GuiMaterialPreview::onMouseDragged(const GuiEvent &event)
|
||||
{
|
||||
if(mMouseState != MovingLight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If we are MovingLight...
|
||||
else
|
||||
{
|
||||
Point2I delta = event.mousePoint - mLastMousePoint;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
setLightTranslate(event.modifier, delta.x, delta.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Right Click
|
||||
void GuiMaterialPreview::onRightMouseDown(const GuiEvent &event)
|
||||
{
|
||||
mMouseState = Rotating;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
// Right Click Release
|
||||
void GuiMaterialPreview::onRightMouseUp(const GuiEvent &event)
|
||||
{
|
||||
mouseUnlock();
|
||||
mMouseState = None;
|
||||
}
|
||||
|
||||
// Right Click Drag
|
||||
void GuiMaterialPreview::onRightMouseDragged(const GuiEvent &event)
|
||||
{
|
||||
if (mMouseState != Rotating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Point2I delta = event.mousePoint - mLastMousePoint;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mCameraRot.x += (delta.y * 0.01f);
|
||||
mCameraRot.z += (delta.x * 0.01f);
|
||||
}
|
||||
|
||||
// Mouse Wheel Scroll Up
|
||||
bool GuiMaterialPreview::onMouseWheelUp(const GuiEvent &event)
|
||||
{
|
||||
mOrbitDist = (mOrbitDist - 0.10f);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mouse Wheel Scroll Down
|
||||
bool GuiMaterialPreview::onMouseWheelDown(const GuiEvent &event)
|
||||
{
|
||||
mOrbitDist = (mOrbitDist + 0.10f);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mouse Wheel Click
|
||||
void GuiMaterialPreview::onMiddleMouseDown(const GuiEvent &event)
|
||||
{
|
||||
if (!mActive || !mVisible || !mAwake)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mMouseState = Panning;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
// Mouse Wheel Click Release
|
||||
void GuiMaterialPreview::onMiddleMouseUp(const GuiEvent &event)
|
||||
{
|
||||
mouseUnlock();
|
||||
mMouseState = None;
|
||||
}
|
||||
|
||||
// Mouse Wheel Click Drag
|
||||
void GuiMaterialPreview::onMiddleMouseDragged(const GuiEvent &event)
|
||||
{
|
||||
if (mMouseState != Panning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Point2I delta = event.mousePoint - mLastMousePoint;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
setTranslate(event.modifier, delta.x, delta.y);
|
||||
}
|
||||
|
||||
// This is used to set the model we want to view in the control object.
|
||||
void GuiMaterialPreview::setObjectModel(const char* modelName)
|
||||
{
|
||||
deleteModel();
|
||||
|
||||
Resource<TSShape> model = ResourceManager::get().load(modelName);
|
||||
if (! bool(model))
|
||||
{
|
||||
Con::warnf(avar("GuiMaterialPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName));
|
||||
return;
|
||||
}
|
||||
|
||||
mModel = new TSShapeInstance(model, true);
|
||||
AssertFatal(mModel, avar("GuiMaterialPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName));
|
||||
|
||||
// Initialize camera values:
|
||||
mOrbitPos = mModel->getShape()->center;
|
||||
mMinOrbitDist = mModel->getShape()->radius;
|
||||
|
||||
lastRenderTime = Platform::getVirtualMilliseconds();
|
||||
}
|
||||
|
||||
void GuiMaterialPreview::deleteModel()
|
||||
{
|
||||
SAFE_DELETE(mModel);
|
||||
runThread = 0;
|
||||
}
|
||||
|
||||
// This is called whenever there is a change in the camera.
|
||||
bool GuiMaterialPreview::processCameraQuery(CameraQuery* query)
|
||||
{
|
||||
MatrixF xRot, zRot;
|
||||
Point3F vecf, vecu, vecr;;
|
||||
xRot.set(EulerF(mCameraRot.x, 0.0f, 0.0f));
|
||||
zRot.set(EulerF(0.0f, 0.0f, mCameraRot.z));
|
||||
|
||||
if(mMouseState != Panning)
|
||||
{
|
||||
// Adjust the camera so that we are still facing the model:
|
||||
Point3F vec;
|
||||
|
||||
mCameraMatrix.mul(zRot, xRot);
|
||||
mCameraMatrix.getColumn(1, &vec);
|
||||
vec *= mOrbitDist;
|
||||
mCameraPos = mOrbitPos - vec;
|
||||
|
||||
query->farPlane = 2100.0f;
|
||||
query->nearPlane = query->farPlane / 5000.0f;
|
||||
query->fov = 45.0f;
|
||||
mCameraMatrix.setColumn(3, mCameraPos);
|
||||
query->cameraMatrix = mCameraMatrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCameraMatrix.mul( zRot, xRot );
|
||||
mCameraMatrix.getColumn( 1, &vecf ); // Forward vector
|
||||
mCameraMatrix.getColumn( 2, &vecu ); // Up vector
|
||||
mCameraMatrix.getColumn( 0, &vecr ); // Right vector
|
||||
|
||||
Point3F flatVecf(vecf.x, vecf.y, 0.0f);
|
||||
|
||||
Point3F modvecf = flatVecf * mOrbitRelPos.y;
|
||||
Point3F modvecu = vecu * mOrbitRelPos.z;
|
||||
Point3F modvecr = vecr * mOrbitRelPos.x;
|
||||
|
||||
// Change the orbit position
|
||||
mOrbitPos += modvecu - modvecr + modvecf;
|
||||
|
||||
F32 vecfmul = mOrbitDist;
|
||||
Point3F virtualVecF = vecf * mOrbitDist;
|
||||
vecf *= vecfmul;
|
||||
|
||||
mCameraPos = mOrbitPos - virtualVecF;
|
||||
|
||||
// Update the camera's position
|
||||
mCameraMatrix.setColumn( 3, (mOrbitPos - vecf) );
|
||||
|
||||
query->farPlane = 2100.0f;
|
||||
query->nearPlane = query->farPlane / 5000.0f;
|
||||
query->fov = 45.0f;
|
||||
query->cameraMatrix = mCameraMatrix;
|
||||
|
||||
// Reset the relative position
|
||||
mOrbitRelPos = Point3F(0,0,0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiMaterialPreview::onMouseEnter(const GuiEvent & event)
|
||||
{
|
||||
Con::executef(this, "onMouseEnter");
|
||||
}
|
||||
|
||||
void GuiMaterialPreview::onMouseLeave(const GuiEvent & event)
|
||||
{
|
||||
Con::executef(this, "onMouseLeave");
|
||||
}
|
||||
|
||||
void GuiMaterialPreview::renderWorld(const RectI &updateRect)
|
||||
{
|
||||
// nothing to render, punt
|
||||
if ( !mModel && !mMountedModel )
|
||||
return;
|
||||
|
||||
S32 time = Platform::getVirtualMilliseconds();
|
||||
//S32 dt = time - lastRenderTime;
|
||||
lastRenderTime = time;
|
||||
|
||||
|
||||
|
||||
F32 left, right, top, bottom, nearPlane, farPlane;
|
||||
bool isOrtho;
|
||||
GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho);
|
||||
Frustum frust( isOrtho, left, right, bottom, top, nearPlane, farPlane, MatrixF::Identity );
|
||||
|
||||
FogData savedFogData = gClientSceneGraph->getFogData();
|
||||
gClientSceneGraph->setFogData( FogData() ); // no fog in preview window
|
||||
|
||||
RenderPassManager* renderPass = gClientSceneGraph->getDefaultRenderPass();
|
||||
SceneRenderState state
|
||||
(
|
||||
gClientSceneGraph,
|
||||
SPT_Diffuse,
|
||||
SceneCameraState( GFX->getViewport(), frust, GFX->getWorldMatrix(), GFX->getProjectionMatrix() ),
|
||||
renderPass,
|
||||
true
|
||||
);
|
||||
|
||||
// Set up our TS render state here.
|
||||
TSRenderState rdata;
|
||||
rdata.setSceneState( &state );
|
||||
|
||||
// We might have some forward lit materials
|
||||
// so pass down a query to gather lights.
|
||||
LightQuery query;
|
||||
query.init( SphereF( Point3F::Zero, 1.0f ) );
|
||||
rdata.setLightQuery( &query );
|
||||
|
||||
// Set up pass transforms
|
||||
renderPass->assignSharedXform(RenderPassManager::View, MatrixF::Identity);
|
||||
renderPass->assignSharedXform(RenderPassManager::Projection, GFX->getProjectionMatrix());
|
||||
|
||||
LIGHTMGR->unregisterAllLights();
|
||||
LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mFakeSun );
|
||||
|
||||
if ( mModel )
|
||||
mModel->render( rdata );
|
||||
|
||||
if ( mMountedModel )
|
||||
{
|
||||
// render a weapon
|
||||
/*
|
||||
MatrixF mat;
|
||||
|
||||
GFX->pushWorldMatrix();
|
||||
GFX->multWorld( mat );
|
||||
|
||||
GFX->popWorldMatrix();
|
||||
*/
|
||||
}
|
||||
|
||||
renderPass->renderPass( &state );
|
||||
|
||||
gClientSceneGraph->setFogData( savedFogData ); // restore fog setting
|
||||
|
||||
// Make sure to remove our fake sun
|
||||
LIGHTMGR->unregisterAllLights();
|
||||
}
|
||||
|
||||
// Make sure the orbit distance is within the acceptable range.
|
||||
void GuiMaterialPreview::setOrbitDistance(F32 distance)
|
||||
{
|
||||
mOrbitDist = mClampF(distance, mMinOrbitDist, mMaxOrbitDist);
|
||||
}
|
||||
|
||||
// This function is meant to be used with a button to put everything back to default settings.
|
||||
void GuiMaterialPreview::resetViewport()
|
||||
{
|
||||
// Reset the camera's orientation.
|
||||
mCameraRot.set( mDegToRad(30.0f), 0, mDegToRad(-30.0f) );
|
||||
mCameraPos.set(0.0f, 1.75f, 1.25f);
|
||||
mOrbitDist = 5.0f;
|
||||
mOrbitPos = mModel->getShape()->center;
|
||||
|
||||
// Reset the viewport's lighting.
|
||||
GuiMaterialPreview::mFakeSun->setColor( ColorF( 1.0f, 1.0f, 1.0f ) );
|
||||
GuiMaterialPreview::mFakeSun->setAmbient( ColorF( 0.5f, 0.5f, 0.5f ) );
|
||||
GuiMaterialPreview::mFakeSun->setDirection( VectorF( 0.0f, 0.707f, -0.707f ) );
|
||||
}
|
||||
|
||||
// Expose the class and functions to the console.
|
||||
IMPLEMENT_CONOBJECT(GuiMaterialPreview);
|
||||
|
||||
ConsoleDocClass( GuiMaterialPreview,
|
||||
"@brief Visual preview of a specified Material\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
// Set the model.
|
||||
DefineEngineMethod(GuiMaterialPreview, setModel, void, ( const char* shapeName ),,
|
||||
"Sets the model to be displayed in this control\n\n"
|
||||
"@param shapeName Name of the model to display.\n")
|
||||
{
|
||||
object->setObjectModel(shapeName);
|
||||
}
|
||||
|
||||
DefineEngineMethod(GuiMaterialPreview, deleteModel, void, (),,
|
||||
"Deletes the preview model.\n")
|
||||
{
|
||||
object->deleteModel();
|
||||
}
|
||||
|
||||
// Set orbit distance around the model.
|
||||
DefineEngineMethod(GuiMaterialPreview, setOrbitDistance, void, ( F32 distance ),,
|
||||
"Sets the distance at which the camera orbits the object. Clamped to the "
|
||||
"acceptable range defined in the class by min and max orbit distances.\n\n"
|
||||
"@param distance The distance to set the orbit to (will be clamped).")
|
||||
{
|
||||
object->setOrbitDistance(distance);
|
||||
}
|
||||
|
||||
// Reset control to default values. Meant to be used with a button.
|
||||
DefineEngineMethod(GuiMaterialPreview, reset, void, (),,
|
||||
"Resets the viewport to default zoom, pan, rotate and lighting.")
|
||||
{
|
||||
object->resetViewport();
|
||||
}
|
||||
|
||||
// This function allows the user to change the light's color.
|
||||
DefineEngineMethod(GuiMaterialPreview, setLightColor, void, ( ColorF color ),,
|
||||
"Sets the color of the light in the scene.\n")
|
||||
{
|
||||
object->setLightColor( color.red, color.green, color.blue );
|
||||
}
|
||||
|
||||
// This function allows the user to change the viewports's ambient color.
|
||||
DefineEngineMethod(GuiMaterialPreview, setAmbientLightColor, void, ( ColorF color ),,
|
||||
"Sets the color of the ambient light in the scene.\n")
|
||||
{
|
||||
object->setAmbientLightColor( color.red, color.green, color.blue );
|
||||
}
|
||||
129
Engine/source/T3D/guiMaterialPreview.h
Normal file
129
Engine/source/T3D/guiMaterialPreview.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Original header:
|
||||
// GuiMaterialPreview Control for Material Editor Written by Travis Vroman of Gaslight Studios
|
||||
// Updated 2-14-09
|
||||
// Portions based off Constructor viewport code.
|
||||
|
||||
#ifndef _GUIMATERIALPREVIEW_H_
|
||||
#define _GUIMATERIALPREVIEW_H_
|
||||
|
||||
#include "gui/3d/guiTSControl.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
|
||||
class LightInfo;
|
||||
|
||||
class GuiMaterialPreview : public GuiTSCtrl
|
||||
{
|
||||
private:
|
||||
typedef GuiTSCtrl Parent;
|
||||
|
||||
protected:
|
||||
enum MouseState
|
||||
{
|
||||
None,
|
||||
Rotating,
|
||||
Zooming,
|
||||
Panning,
|
||||
MovingLight
|
||||
};
|
||||
|
||||
MouseState mMouseState;
|
||||
|
||||
TSShapeInstance* mModel;
|
||||
TSShapeInstance* mMountedModel;
|
||||
U32 mSkinTag;
|
||||
|
||||
// For Camera Panning.
|
||||
F32 mTransStep; //*** Amount of translation with each mouse move
|
||||
F32 mTranMult; //*** With a modifier, how much faster to translate
|
||||
|
||||
// For light translation.
|
||||
F32 mLightTransStep;
|
||||
F32 mLightTranMult;
|
||||
|
||||
Point3F mCameraPos;
|
||||
MatrixF mCameraMatrix;
|
||||
EulerF mCameraRot;
|
||||
Point3F mOrbitPos;
|
||||
Point3F mOrbitRelPos;
|
||||
Point3F mCameraTransform;
|
||||
|
||||
TSThread * runThread;
|
||||
S32 lastRenderTime;
|
||||
|
||||
Point2I mLastMousePoint;
|
||||
|
||||
LightInfo* mFakeSun;
|
||||
|
||||
public:
|
||||
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);
|
||||
bool onMouseWheelUp(const GuiEvent &event);
|
||||
bool onMouseWheelDown(const GuiEvent &event);
|
||||
void onMiddleMouseUp(const GuiEvent &event);
|
||||
void onMiddleMouseDown(const GuiEvent &event);
|
||||
void onMiddleMouseDragged(const GuiEvent &event);
|
||||
|
||||
// For Camera Panning.
|
||||
void setTranslate(S32 modifier, F32 xstep, F32 ystep);
|
||||
|
||||
// For Light Translation.
|
||||
void setLightTranslate(S32 modifier, F32 xstep, F32 ystep);
|
||||
|
||||
// For changing the light color.
|
||||
void setLightColor( F32 r, F32 g, F32 b );
|
||||
|
||||
// For changing the ambient light color.
|
||||
void setAmbientLightColor( F32 r, F32 g, F32 b );
|
||||
|
||||
void setObjectModel(const char * modelName);
|
||||
void deleteModel();
|
||||
void resetViewport();
|
||||
void setOrbitDistance(F32 distance);
|
||||
|
||||
bool processCameraQuery(CameraQuery *query);
|
||||
void renderWorld(const RectI &updateRect);
|
||||
|
||||
DECLARE_CONOBJECT(GuiMaterialPreview);
|
||||
DECLARE_CATEGORY( "Gui Editor" );
|
||||
|
||||
GuiMaterialPreview();
|
||||
~GuiMaterialPreview();
|
||||
|
||||
private:
|
||||
F32 mMaxOrbitDist;
|
||||
F32 mMinOrbitDist;
|
||||
F32 mOrbitDist;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
41
Engine/source/T3D/guiNoMouseCtrl.cpp
Normal file
41
Engine/source/T3D/guiNoMouseCtrl.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "gui/core/guiControl.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class GuiNoMouseCtrl : public GuiControl
|
||||
{
|
||||
typedef GuiControl Parent;
|
||||
public:
|
||||
|
||||
// GuiControl
|
||||
bool pointInControl(const Point2I &) { return(false); }
|
||||
DECLARE_CONOBJECT(GuiNoMouseCtrl);
|
||||
DECLARE_CATEGORY( "Gui Other" );
|
||||
};
|
||||
IMPLEMENT_CONOBJECT(GuiNoMouseCtrl);
|
||||
|
||||
ConsoleDocClass( GuiNoMouseCtrl,
|
||||
"@brief No known usage, possibly Legacy.\n\n"
|
||||
"Not used at all, internal until deprecated\n\n"
|
||||
"@internal");
|
||||
988
Engine/source/T3D/guiObjectView.cpp
Normal file
988
Engine/source/T3D/guiObjectView.cpp
Normal file
|
|
@ -0,0 +1,988 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/guiObjectView.h"
|
||||
#include "renderInstance/renderPassManager.h"
|
||||
#include "lighting/lightManager.h"
|
||||
#include "lighting/lightInfo.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "scene/sceneRenderState.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "math/mathTypes.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT( GuiObjectView );
|
||||
|
||||
ConsoleDocClass( GuiObjectView,
|
||||
"@brief GUI control which displays a 3D model.\n\n"
|
||||
|
||||
"Model displayed in the control can have other objects mounted onto it, and the light settings can be adjusted.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
" new GuiObjectView(ObjectPreview)\n"
|
||||
" {\n"
|
||||
" shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n"
|
||||
" mountedNode = \"mount0\";\n"
|
||||
" lightColor = \"1 1 1 1\";\n"
|
||||
" lightAmbient = \"0.5 0.5 0.5 1\";\n"
|
||||
" lightDirection = \"0 0.707 -0.707\";\n"
|
||||
" orbitDiststance = \"2\";\n"
|
||||
" minOrbitDiststance = \"0.917688\";\n"
|
||||
" maxOrbitDiststance = \"5\";\n"
|
||||
" cameraSpeed = \"0.01\";\n"
|
||||
" cameraZRot = \"0\";\n"
|
||||
" forceFOV = \"0\";\n"
|
||||
" reflectPriority = \"0\";\n"
|
||||
" };\n"
|
||||
"@endtsexample\n\n"
|
||||
|
||||
"@see GuiControl\n\n"
|
||||
|
||||
"@ingroup Gui3D\n"
|
||||
);
|
||||
|
||||
IMPLEMENT_CALLBACK( GuiObjectView, onMouseEnter, void, (),(),
|
||||
"@brief Called whenever the mouse enters the control.\n\n"
|
||||
"@tsexample\n"
|
||||
"// The mouse has entered the control, causing the callback to occur\n"
|
||||
"GuiObjectView::onMouseEnter(%this)\n"
|
||||
" {\n"
|
||||
" // Code to run when the mouse enters this control\n"
|
||||
" }\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl\n\n"
|
||||
);
|
||||
|
||||
IMPLEMENT_CALLBACK( GuiObjectView, onMouseLeave, void, (),(),
|
||||
"@brief Called whenever the mouse leaves the control.\n\n"
|
||||
"@tsexample\n"
|
||||
"// The mouse has left the control, causing the callback to occur\n"
|
||||
"GuiObjectView::onMouseLeave(%this)\n"
|
||||
" {\n"
|
||||
" // Code to run when the mouse leaves this control\n"
|
||||
" }\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl\n\n"
|
||||
);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
GuiObjectView::GuiObjectView()
|
||||
: mMaxOrbitDist( 5.0f ),
|
||||
mMinOrbitDist( 0.0f ),
|
||||
mOrbitDist( 5.0f ),
|
||||
mMouseState( None ),
|
||||
mModel( NULL ),
|
||||
mMountedModel( NULL ),
|
||||
mLastMousePoint( 0, 0 ),
|
||||
mLastRenderTime( 0 ),
|
||||
mRunThread( NULL ),
|
||||
mLight( NULL ),
|
||||
mAnimationSeq( -1 ),
|
||||
mMountNodeName( "mount0" ),
|
||||
mMountNode( -1 ),
|
||||
mCameraSpeed( 0.01f ),
|
||||
mLightColor( 1.0f, 1.0f, 1.0f ),
|
||||
mLightAmbient( 0.5f, 0.5f, 0.5f ),
|
||||
mLightDirection( 0.f, 0.707f, -0.707f )
|
||||
{
|
||||
mCameraMatrix.identity();
|
||||
mCameraRot.set( 0.0f, 0.0f, 3.9f );
|
||||
mCameraPos.set( 0.0f, 1.75f, 1.25f );
|
||||
mCameraMatrix.setColumn( 3, mCameraPos );
|
||||
mOrbitPos.set( 0.0f, 0.0f, 0.0f );
|
||||
|
||||
// By default don't do dynamic reflection
|
||||
// updates for this viewport.
|
||||
mReflectPriority = 0.0f;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
GuiObjectView::~GuiObjectView()
|
||||
{
|
||||
if( mModel )
|
||||
SAFE_DELETE( mModel );
|
||||
if( mMountedModel )
|
||||
SAFE_DELETE( mMountedModel );
|
||||
if( mLight )
|
||||
SAFE_DELETE( mLight );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::initPersistFields()
|
||||
{
|
||||
addGroup( "Model" );
|
||||
|
||||
addField( "shapeFile", TypeStringFilename, Offset( mModelName, GuiObjectView ),
|
||||
"The object model shape file to show in the view." );
|
||||
addField( "skin", TypeRealString, Offset( mSkinName, GuiObjectView ),
|
||||
"The skin to use on the object model." );
|
||||
|
||||
endGroup( "Model" );
|
||||
|
||||
addGroup( "Animation" );
|
||||
|
||||
addField( "animSequence", TypeRealString, Offset( mAnimationSeqName, GuiObjectView ),
|
||||
"The animation sequence to play on the model." );
|
||||
|
||||
endGroup( "Animation" );
|
||||
|
||||
addGroup( "Mounting" );
|
||||
|
||||
addField( "mountedShapeFile", TypeStringFilename, Offset( mMountedModelName, GuiObjectView ),
|
||||
"Optional shape file to mount on the primary model (e.g. weapon)." );
|
||||
addField( "mountedSkin", TypeRealString, Offset( mMountSkinName, GuiObjectView ),
|
||||
"Skin name used on mounted shape file." );
|
||||
addField( "mountedNode", TypeRealString, Offset( mMountNodeName, GuiObjectView ),
|
||||
"Name of node on primary model to which to mount the secondary shape." );
|
||||
|
||||
endGroup( "Mounting" );
|
||||
|
||||
addGroup( "Lighting" );
|
||||
|
||||
addField( "lightColor", TypeColorF, Offset( mLightColor, GuiObjectView ),
|
||||
"Diffuse color of the sunlight used to render the model." );
|
||||
addField( "lightAmbient", TypeColorF, Offset( mLightAmbient, GuiObjectView ),
|
||||
"Ambient color of the sunlight used to render the model." );
|
||||
addField( "lightDirection", TypePoint3F, Offset( mLightDirection, GuiObjectView ),
|
||||
"Direction from which the model is illuminated." );
|
||||
|
||||
endGroup( "Lighting" );
|
||||
|
||||
addGroup( "Camera" );
|
||||
|
||||
addField( "orbitDiststance", TypeF32, Offset( mOrbitDist, GuiObjectView ),
|
||||
"Distance from which to render the model." );
|
||||
addField( "minOrbitDiststance", TypeF32, Offset( mMinOrbitDist, GuiObjectView ),
|
||||
"Maxiumum distance to which the camera can be zoomed out." );
|
||||
addField( "maxOrbitDiststance", TypeF32, Offset( mMaxOrbitDist, GuiObjectView ),
|
||||
"Minimum distance below which the camera will not zoom in further." );
|
||||
addField( "cameraSpeed", TypeF32, Offset( mCameraSpeed, GuiObjectView ),
|
||||
"Multiplier for mouse camera operations." );
|
||||
|
||||
endGroup( "Camera" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onStaticModified( StringTableEntry slotName, const char* newValue )
|
||||
{
|
||||
Parent::onStaticModified( slotName, newValue );
|
||||
|
||||
static StringTableEntry sShapeFile = StringTable->insert( "shapeFile" );
|
||||
static StringTableEntry sSkin = StringTable->insert( "skin" );
|
||||
static StringTableEntry sMountedShapeFile = StringTable->insert( "mountedShapeFile" );
|
||||
static StringTableEntry sMountedSkin = StringTable->insert( "mountedSkin" );
|
||||
static StringTableEntry sMountedNode = StringTable->insert( "mountedNode" );
|
||||
static StringTableEntry sLightColor = StringTable->insert( "lightColor" );
|
||||
static StringTableEntry sLightAmbient = StringTable->insert( "lightAmbient" );
|
||||
static StringTableEntry sLightDirection = StringTable->insert( "lightDirection" );
|
||||
static StringTableEntry sOrbitDistance = StringTable->insert( "orbitDistance" );
|
||||
static StringTableEntry sMinOrbitDistance = StringTable->insert( "minOrbitDistance" );
|
||||
static StringTableEntry sMaxOrbitDistance = StringTable->insert( "maxOrbitDistance" );
|
||||
static StringTableEntry sAnimSequence = StringTable->insert( "animSequence" );
|
||||
|
||||
if( slotName == sShapeFile )
|
||||
setObjectModel( String( mModelName ) );
|
||||
else if( slotName == sSkin )
|
||||
setSkin( String( mSkinName ) );
|
||||
else if( slotName == sMountedShapeFile )
|
||||
setMountedObject( String( mMountedModelName ) );
|
||||
else if( slotName == sMountedSkin )
|
||||
setMountSkin( String( mMountSkinName ) );
|
||||
else if( slotName == sMountedNode )
|
||||
setMountNode( String( mMountNodeName ) );
|
||||
else if( slotName == sLightColor )
|
||||
setLightColor( mLightColor );
|
||||
else if( slotName == sLightAmbient )
|
||||
setLightAmbient( mLightAmbient );
|
||||
else if( slotName == sLightDirection )
|
||||
setLightDirection( mLightDirection );
|
||||
else if( slotName == sOrbitDistance || slotName == sMinOrbitDistance || slotName == sMaxOrbitDistance )
|
||||
setOrbitDistance( mOrbitDist );
|
||||
else if( slotName == sAnimSequence )
|
||||
setObjectAnimation( String( mAnimationSeqName ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool GuiObjectView::onWake()
|
||||
{
|
||||
if( !Parent::onWake() )
|
||||
return false;
|
||||
|
||||
if( !mLight )
|
||||
{
|
||||
mLight = LIGHTMGR->createLightInfo();
|
||||
|
||||
mLight->setColor( mLightColor );
|
||||
mLight->setAmbient( mLightAmbient );
|
||||
mLight->setDirection( mLightDirection );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onMouseDown( const GuiEvent &event )
|
||||
{
|
||||
if( !mActive || !mVisible || !mAwake )
|
||||
return;
|
||||
|
||||
mMouseState = Rotating;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onMouseUp( const GuiEvent &event )
|
||||
{
|
||||
mouseUnlock();
|
||||
mMouseState = None;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onMouseDragged( const GuiEvent &event )
|
||||
{
|
||||
if( mMouseState != Rotating )
|
||||
return;
|
||||
|
||||
Point2I delta = event.mousePoint - mLastMousePoint;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
|
||||
mCameraRot.x += ( delta.y * mCameraSpeed );
|
||||
mCameraRot.z += ( delta.x * mCameraSpeed );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onRightMouseDown( const GuiEvent &event )
|
||||
{
|
||||
mMouseState = Zooming;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onRightMouseUp( const GuiEvent &event )
|
||||
{
|
||||
mouseUnlock();
|
||||
mMouseState = None;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onRightMouseDragged( const GuiEvent &event )
|
||||
{
|
||||
if( mMouseState != Zooming )
|
||||
return;
|
||||
|
||||
S32 delta = event.mousePoint.y - mLastMousePoint.y;
|
||||
mLastMousePoint = event.mousePoint;
|
||||
|
||||
mOrbitDist += ( delta * mCameraSpeed );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setObjectAnimation( S32 index )
|
||||
{
|
||||
mAnimationSeq = index;
|
||||
mAnimationSeqName = String();
|
||||
|
||||
if( mModel )
|
||||
_initAnimation();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setObjectAnimation( const String& sequenceName )
|
||||
{
|
||||
mAnimationSeq = -1;
|
||||
mAnimationSeqName = sequenceName;
|
||||
|
||||
if( mModel )
|
||||
_initAnimation();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setObjectModel( const String& modelName )
|
||||
{
|
||||
SAFE_DELETE( mModel );
|
||||
mRunThread = 0;
|
||||
mModelName = String::EmptyString;
|
||||
|
||||
// Load the shape.
|
||||
|
||||
Resource< TSShape > model = ResourceManager::get().load( modelName );
|
||||
if( !model )
|
||||
{
|
||||
Con::warnf( "GuiObjectView::setObjectModel - Failed to load model '%s'", modelName.c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Instantiate it.
|
||||
|
||||
mModel = new TSShapeInstance( model, true );
|
||||
mModelName = modelName;
|
||||
|
||||
if( !mSkinName.isEmpty() )
|
||||
mModel->reSkin( mSkinName );
|
||||
|
||||
// Initialize camera values.
|
||||
|
||||
mOrbitPos = mModel->getShape()->center;
|
||||
mMinOrbitDist = mModel->getShape()->radius;
|
||||
|
||||
// Initialize animation.
|
||||
|
||||
_initAnimation();
|
||||
_initMount();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setSkin( const String& name )
|
||||
{
|
||||
if( mModel )
|
||||
mModel->reSkin( name, mSkinName );
|
||||
|
||||
mSkinName = name;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setMountSkin( const String& name )
|
||||
{
|
||||
if( mMountedModel )
|
||||
mMountedModel->reSkin( name, mMountSkinName );
|
||||
|
||||
mMountSkinName = name;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setMountNode( S32 index )
|
||||
{
|
||||
setMountNode( String::ToString( "mount%i", index ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setMountNode( const String& name )
|
||||
{
|
||||
mMountNodeName = name;
|
||||
|
||||
if( mModel )
|
||||
_initMount();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setMountedObject( const String& modelName )
|
||||
{
|
||||
SAFE_DELETE( mMountedModel );
|
||||
mMountedModelName = String::EmptyString;
|
||||
|
||||
// Load the model.
|
||||
|
||||
Resource< TSShape > model = ResourceManager::get().load( modelName );
|
||||
if( !model )
|
||||
{
|
||||
Con::warnf( "GuiObjectView::setMountedObject - Failed to load object model '%s'",
|
||||
modelName.c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
mMountedModel = new TSShapeInstance( model, true );
|
||||
mMountedModelName = modelName;
|
||||
|
||||
if( !mMountSkinName.isEmpty() )
|
||||
mMountedModel->reSkin( mMountSkinName );
|
||||
|
||||
if( mModel )
|
||||
_initMount();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool GuiObjectView::processCameraQuery( CameraQuery* query )
|
||||
{
|
||||
// Adjust the camera so that we are still facing the model.
|
||||
|
||||
Point3F vec;
|
||||
MatrixF xRot, zRot;
|
||||
xRot.set( EulerF( mCameraRot.x, 0.0f, 0.0f ) );
|
||||
zRot.set( EulerF( 0.0f, 0.0f, mCameraRot.z ) );
|
||||
|
||||
mCameraMatrix.mul( zRot, xRot );
|
||||
mCameraMatrix.getColumn( 1, &vec );
|
||||
vec *= mOrbitDist;
|
||||
mCameraPos = mOrbitPos - vec;
|
||||
|
||||
query->farPlane = 2100.0f;
|
||||
query->nearPlane = query->farPlane / 5000.0f;
|
||||
query->fov = 45.0f;
|
||||
mCameraMatrix.setColumn( 3, mCameraPos );
|
||||
query->cameraMatrix = mCameraMatrix;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onMouseEnter( const GuiEvent & event )
|
||||
{
|
||||
onMouseEnter_callback();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::onMouseLeave( const GuiEvent & event )
|
||||
{
|
||||
onMouseLeave_callback();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::renderWorld( const RectI& updateRect )
|
||||
{
|
||||
if( !mModel )
|
||||
return;
|
||||
|
||||
GFXTransformSaver _saveTransforms;
|
||||
|
||||
// Determine the camera position, and store off render state.
|
||||
|
||||
MatrixF modelview;
|
||||
MatrixF mv;
|
||||
Point3F cp;
|
||||
|
||||
modelview = GFX->getWorldMatrix();
|
||||
|
||||
mv = modelview;
|
||||
mv.inverse();
|
||||
mv.getColumn( 3, &cp );
|
||||
|
||||
RenderPassManager* renderPass = gClientSceneGraph->getDefaultRenderPass();
|
||||
|
||||
S32 time = Platform::getVirtualMilliseconds();
|
||||
S32 dt = time - mLastRenderTime;
|
||||
mLastRenderTime = time;
|
||||
|
||||
LIGHTMGR->unregisterAllLights();
|
||||
LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mLight );
|
||||
|
||||
GFX->setStateBlock( mDefaultGuiSB );
|
||||
|
||||
F32 left, right, top, bottom, nearPlane, farPlane;
|
||||
bool isOrtho;
|
||||
GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho );
|
||||
|
||||
Frustum frust( false, left, right, top, bottom, nearPlane, farPlane, MatrixF::Identity );
|
||||
|
||||
SceneRenderState state
|
||||
(
|
||||
gClientSceneGraph,
|
||||
SPT_Diffuse,
|
||||
SceneCameraState( GFX->getViewport(), frust, GFX->getWorldMatrix(), GFX->getProjectionMatrix() ),
|
||||
renderPass,
|
||||
false
|
||||
);
|
||||
|
||||
// Set up our TS render state here.
|
||||
TSRenderState rdata;
|
||||
rdata.setSceneState( &state );
|
||||
|
||||
// We might have some forward lit materials
|
||||
// so pass down a query to gather lights.
|
||||
LightQuery query;
|
||||
query.init( SphereF( Point3F::Zero, 1.0f ) );
|
||||
rdata.setLightQuery( &query );
|
||||
|
||||
// Render primary model.
|
||||
|
||||
if( mModel )
|
||||
{
|
||||
if( mRunThread )
|
||||
{
|
||||
mModel->advanceTime( dt / 1000.f, mRunThread );
|
||||
mModel->animate();
|
||||
}
|
||||
|
||||
mModel->render( rdata );
|
||||
}
|
||||
|
||||
// Render mounted model.
|
||||
|
||||
if( mMountedModel && mMountNode != -1 )
|
||||
{
|
||||
GFX->pushWorldMatrix();
|
||||
GFX->multWorld( mModel->mNodeTransforms[ mMountNode ] );
|
||||
GFX->multWorld( mMountTransform );
|
||||
|
||||
mMountedModel->render( rdata );
|
||||
|
||||
GFX->popWorldMatrix();
|
||||
}
|
||||
|
||||
renderPass->renderPass( &state );
|
||||
|
||||
// Make sure to remove our fake sun.
|
||||
LIGHTMGR->unregisterAllLights();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setOrbitDistance( F32 distance )
|
||||
{
|
||||
// Make sure the orbit distance is within the acceptable range
|
||||
mOrbitDist = mClampF( distance, mMinOrbitDist, mMaxOrbitDist );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setCameraSpeed( F32 factor )
|
||||
{
|
||||
mCameraSpeed = factor;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setLightColor( const ColorF& color )
|
||||
{
|
||||
mLightColor = color;
|
||||
if( mLight )
|
||||
mLight->setColor( color );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setLightAmbient( const ColorF& color )
|
||||
{
|
||||
mLightAmbient = color;
|
||||
if( mLight )
|
||||
mLight->setAmbient( color );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::setLightDirection( const Point3F& direction )
|
||||
{
|
||||
mLightDirection = direction;
|
||||
if( mLight )
|
||||
mLight->setDirection( direction );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::_initAnimation()
|
||||
{
|
||||
AssertFatal( mModel, "GuiObjectView::_initAnimation - No model loaded!" );
|
||||
|
||||
if( mAnimationSeqName.isEmpty() && mAnimationSeq == -1 )
|
||||
return;
|
||||
|
||||
// Look up sequence by name.
|
||||
|
||||
if( !mAnimationSeqName.isEmpty() )
|
||||
{
|
||||
mAnimationSeq = mModel->getShape()->findSequence( mAnimationSeqName );
|
||||
|
||||
if( mAnimationSeq == -1 )
|
||||
{
|
||||
Con::errorf( "GuiObjectView::_initAnimation - Cannot find animation sequence '%s' on '%s'",
|
||||
mAnimationSeqName.c_str(),
|
||||
mModelName.c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Start sequence.
|
||||
|
||||
if( mAnimationSeq != -1 )
|
||||
{
|
||||
if( mAnimationSeq >= mModel->getShape()->sequences.size() )
|
||||
{
|
||||
Con::errorf( "GuiObjectView::_initAnimation - Sequence '%i' out of range for model '%s'",
|
||||
mAnimationSeq,
|
||||
mModelName.c_str()
|
||||
);
|
||||
|
||||
mAnimationSeq = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !mRunThread )
|
||||
mRunThread = mModel->addThread();
|
||||
|
||||
mModel->setSequence( mRunThread, mAnimationSeq, 0.f );
|
||||
}
|
||||
|
||||
mLastRenderTime = Platform::getVirtualMilliseconds();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void GuiObjectView::_initMount()
|
||||
{
|
||||
AssertFatal( mModel, "GuiObjectView::_initMount - No model loaded!" );
|
||||
|
||||
if( !mMountedModel )
|
||||
return;
|
||||
|
||||
mMountTransform.identity();
|
||||
|
||||
// Look up the node to which to mount to.
|
||||
|
||||
if( !mMountNodeName.isEmpty() )
|
||||
{
|
||||
mMountNode = mModel->getShape()->findNode( mMountNodeName );
|
||||
if( mMountNode == -1 )
|
||||
{
|
||||
Con::errorf( "GuiObjectView::_initMount - No node '%s' on '%s'",
|
||||
mMountNodeName.c_str(),
|
||||
mModelName.c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure mount node is valid.
|
||||
|
||||
if( mMountNode != -1 && mMountNode >= mModel->getShape()->nodes.size() )
|
||||
{
|
||||
Con::errorf( "GuiObjectView::_initMount - Mount node index '%i' out of range for '%s'",
|
||||
mMountNode,
|
||||
mModelName.c_str()
|
||||
);
|
||||
|
||||
mMountNode = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Look up node on the mounted model from
|
||||
// which to mount to the primary model's node.
|
||||
|
||||
S32 mountPoint = mMountedModel->getShape()->findNode( "mountPoint" );
|
||||
if( mountPoint != -1 )
|
||||
{
|
||||
mMountedModel->getShape()->getNodeWorldTransform( mountPoint, &mMountTransform ),
|
||||
mMountTransform.inverse();
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Console Methods.
|
||||
//=============================================================================
|
||||
// MARK: ---- Console Methods ----
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, getModel, const char*, (),,
|
||||
"@brief Return the model displayed in this view.\n\n"
|
||||
"@tsexample\n"
|
||||
"// Request the displayed model name from the GuiObjectView object.\n"
|
||||
"%modelName = %thisGuiObjectView.getModel();\n"
|
||||
"@endtsexample\n\n"
|
||||
"@return Name of the displayed model.\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
return Con::getReturnBuffer( object->getModelName() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setModel, void, (const char* shapeName),,
|
||||
"@brief Sets the model to be displayed in this control.\n\n"
|
||||
"@param shapeName Name of the model to display.\n"
|
||||
"@tsexample\n"
|
||||
"// Define the model we want to display\n"
|
||||
"%shapeName = \"gideon.dts\";\n\n"
|
||||
"// Tell the GuiObjectView object to display the defined model\n"
|
||||
"%thisGuiObjectView.setModel(%shapeName);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setObjectModel( shapeName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, getMountedModel, const char*, (),,
|
||||
"@brief Return the name of the mounted model.\n\n"
|
||||
"@tsexample\n"
|
||||
"// Request the name of the mounted model from the GuiObjectView object\n"
|
||||
"%mountedModelName = %thisGuiObjectView.getMountedModel();\n"
|
||||
"@endtsexample\n\n"
|
||||
"@return Name of the mounted model.\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
return Con::getReturnBuffer( object->getMountedModelName() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setMountedModel, void, (const char* shapeName),,
|
||||
"@brief Sets the model to be mounted on the primary model.\n\n"
|
||||
"@param shapeName Name of the model to mount.\n"
|
||||
"@tsexample\n"
|
||||
"// Define the model name to mount\n"
|
||||
"%modelToMount = \"GideonGlasses.dts\";\n\n"
|
||||
"// Inform the GuiObjectView object to mount the defined model to the existing model in the control\n"
|
||||
"%thisGuiObjectView.setMountedModel(%modelToMount);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setObjectModel(shapeName);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, getSkin, const char*, (),,
|
||||
"@brief Return the name of skin used on the primary model.\n\n"
|
||||
"@tsexample\n"
|
||||
"// Request the name of the skin used on the primary model in the control\n"
|
||||
"%skinName = %thisGuiObjectView.getSkin();\n"
|
||||
"@endtsexample\n\n"
|
||||
"@return Name of the skin used on the primary model.\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
return Con::getReturnBuffer( object->getSkin() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setSkin, void, (const char* skinName),,
|
||||
"@brief Sets the skin to use on the model being displayed.\n\n"
|
||||
"@param skinName Name of the skin to use.\n"
|
||||
"@tsexample\n"
|
||||
"// Define the skin we want to apply to the main model in the control\n"
|
||||
"%skinName = \"disco_gideon\";\n\n"
|
||||
"// Inform the GuiObjectView control to update the skin the to defined skin\n"
|
||||
"%thisGuiObjectView.setSkin(%skinName);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setSkin( skinName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, getMountSkin, const char*, ( S32 param1, S32 param2),,
|
||||
"@brief Return the name of skin used on the mounted model.\n\n"
|
||||
"@tsexample\n"
|
||||
"// Request the skin name from the model mounted on to the main model in the control\n"
|
||||
"%mountModelSkin = %thisGuiObjectView.getMountSkin();\n"
|
||||
"@endtsexample\n\n"
|
||||
"@return Name of the skin used on the mounted model.\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
return Con::getReturnBuffer( object->getMountSkin() );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setMountSkin, void, (const char* skinName),,
|
||||
"@brief Sets the skin to use on the mounted model.\n\n"
|
||||
"@param skinName Name of the skin to set on the model mounted to the main model in the control\n"
|
||||
"@tsexample\n"
|
||||
"// Define the name of the skin\n"
|
||||
"%skinName = \"BronzeGlasses\";\n\n"
|
||||
"// Inform the GuiObjectView Control of the skin to use on the mounted model\n"
|
||||
"%thisGuiObjectViewCtrl.setMountSkin(%skinName);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setMountSkin(skinName);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setSeq, void, (const char* indexOrName),,
|
||||
"@brief Sets the animation to play for the viewed object.\n\n"
|
||||
"@param indexOrName The index or name of the animation to play.\n"
|
||||
"@tsexample\n"
|
||||
"// Set the animation index value, or animation sequence name.\n"
|
||||
"%indexVal = \"3\";\n"
|
||||
"//OR:\n"
|
||||
"%indexVal = \"idle\";\n\n"
|
||||
"// Inform the GuiObjectView object to set the animation sequence of the object in the control.\n"
|
||||
"%thisGuiObjectVew.setSeq(%indexVal);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
if( dIsdigit( indexOrName[0] ) )
|
||||
object->setObjectAnimation( dAtoi( indexOrName ) );
|
||||
else
|
||||
object->setObjectAnimation( indexOrName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setMount, void, ( const char* shapeName, const char* mountNodeIndexOrName),,
|
||||
"@brief Mounts the given model to the specified mount point of the primary model displayed in this control.\n\n"
|
||||
"Detailed description\n\n"
|
||||
"@param shapeName Name of the model to mount.\n"
|
||||
"@param mountNodeIndexOrName Index or name of the mount point to be mounted to. If index, corresponds to \"mountN\" in your shape where N is the number passed here.\n"
|
||||
"@tsexample\n"
|
||||
"// Set the shapeName to mount\n"
|
||||
"%shapeName = \"GideonGlasses.dts\"\n\n"
|
||||
"// Set the mount node of the primary model in the control to mount the new shape at\n"
|
||||
"%mountNodeIndexOrName = \"3\";\n"
|
||||
"//OR:\n"
|
||||
"%mountNodeIndexOrName = \"Face\";\n\n"
|
||||
"// Inform the GuiObjectView object to mount the shape at the specified node.\n"
|
||||
"%thisGuiObjectView.setMount(%shapeName,%mountNodeIndexOrName);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
if( dIsdigit( mountNodeIndexOrName[0] ) )
|
||||
object->setMountNode( dAtoi( mountNodeIndexOrName ) );
|
||||
else
|
||||
object->setMountNode( mountNodeIndexOrName );
|
||||
|
||||
object->setMountedObject( shapeName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, getOrbitDistance, F32, (),,
|
||||
"@brief Return the current distance at which the camera orbits the object.\n\n"
|
||||
"@tsexample\n"
|
||||
"// Request the current orbit distance\n"
|
||||
"%orbitDistance = %thisGuiObjectView.getOrbitDistance();\n"
|
||||
"@endtsexample\n\n"
|
||||
"@return The distance at which the camera orbits the object.\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
return object->getOrbitDistance();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setOrbitDistance, void, (F32 distance),,
|
||||
"@brief Sets the distance at which the camera orbits the object. Clamped to the acceptable range defined in the class by min and max orbit distances.\n\n"
|
||||
"Detailed description\n\n"
|
||||
"@param distance The distance to set the orbit to (will be clamped).\n"
|
||||
"@tsexample\n"
|
||||
"// Define the orbit distance value\n"
|
||||
"%orbitDistance = \"1.5\";\n\n"
|
||||
"// Inform the GuiObjectView object to set the orbit distance to the defined value\n"
|
||||
"%thisGuiObjectView.setOrbitDistance(%orbitDistance);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setOrbitDistance( distance );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, getCameraSpeed, F32, (),,
|
||||
"@brief Return the current multiplier for camera zooming and rotation.\n\n"
|
||||
"@tsexample\n"
|
||||
"// Request the current camera zooming and rotation multiplier value\n"
|
||||
"%multiplier = %thisGuiObjectView.getCameraSpeed();\n"
|
||||
"@endtsexample\n\n"
|
||||
"@return Camera zooming / rotation multiplier value.\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
return object->getCameraSpeed();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setCameraSpeed, void, (F32 factor),,
|
||||
"@brief Sets the multiplier for the camera rotation and zoom speed.\n\n"
|
||||
"@param factor Multiplier for camera rotation and zoom speed.\n"
|
||||
"@tsexample\n"
|
||||
"// Set the factor value\n"
|
||||
"%factor = \"0.75\";\n\n"
|
||||
"// Inform the GuiObjectView object to set the camera speed.\n"
|
||||
"%thisGuiObjectView.setCameraSpeed(%factor);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setCameraSpeed( factor );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setLightColor, void, ( ColorF color),,
|
||||
"@brief Set the light color on the sun object used to render the model.\n\n"
|
||||
"@param color Color of sunlight.\n"
|
||||
"@tsexample\n"
|
||||
"// Set the color value for the sun\n"
|
||||
"%color = \"1.0 0.4 0.5\";\n\n"
|
||||
"// Inform the GuiObjectView object to change the sun color to the defined value\n"
|
||||
"%thisGuiObjectView.setLightColor(%color);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setLightColor( color );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setLightAmbient, void, (ColorF color),,
|
||||
"@brief Set the light ambient color on the sun object used to render the model.\n\n"
|
||||
"@param color Ambient color of sunlight.\n"
|
||||
"@tsexample\n"
|
||||
"// Define the sun ambient color value\n"
|
||||
"%color = \"1.0 0.4 0.6\";\n\n"
|
||||
"// Inform the GuiObjectView object to set the sun ambient color to the requested value\n"
|
||||
"%thisGuiObjectView.setLightAmbient(%color);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setLightAmbient( color );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod( GuiObjectView, setLightDirection, void, (Point3F direction),,
|
||||
"@brief Set the light direction from which to light the model.\n\n"
|
||||
"@param direction XYZ direction from which the light will shine on the model\n"
|
||||
"@tsexample\n"
|
||||
"// Set the light direction\n"
|
||||
"%direction = \"1.0 0.2 0.4\"\n\n"
|
||||
"// Inform the GuiObjectView object to change the light direction to the defined value\n"
|
||||
"%thisGuiObjectView.setLightDirection(%direction);\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see GuiControl")
|
||||
{
|
||||
object->setLightDirection( direction );
|
||||
}
|
||||
285
Engine/source/T3D/guiObjectView.h
Normal file
285
Engine/source/T3D/guiObjectView.h
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GUIOBJECTVIEW_H_
|
||||
#define _GUIOBJECTVIEW_H_
|
||||
|
||||
#ifndef _GUITSCONTROL_H_
|
||||
#include "gui/3d/guiTSControl.h"
|
||||
#endif
|
||||
#ifndef _TSSHAPEINSTANCE_H_
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#endif
|
||||
|
||||
|
||||
class LightInfo;
|
||||
|
||||
|
||||
/// A control that displays a TSShape in its view.
|
||||
class GuiObjectView : public GuiTSCtrl
|
||||
{
|
||||
public:
|
||||
|
||||
typedef GuiTSCtrl Parent;
|
||||
|
||||
DECLARE_CALLBACK( void, onMouseEnter, ());
|
||||
DECLARE_CALLBACK( void, onMouseLeave, ());
|
||||
|
||||
protected:
|
||||
|
||||
/// @name Mouse Control
|
||||
/// @{
|
||||
|
||||
enum MouseState
|
||||
{
|
||||
None,
|
||||
Rotating,
|
||||
Zooming
|
||||
};
|
||||
|
||||
/// Current mouse operation.
|
||||
MouseState mMouseState;
|
||||
|
||||
/// Last mouse position during tracked mouse operations.
|
||||
Point2I mLastMousePoint;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Model
|
||||
/// @{
|
||||
|
||||
/// Name of the model loaded for display.
|
||||
String mModelName;
|
||||
|
||||
/// Model being displayed in the view.
|
||||
TSShapeInstance* mModel;
|
||||
|
||||
/// Name of skin to use on model.
|
||||
String mSkinName;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Camera State
|
||||
/// @{
|
||||
|
||||
Point3F mCameraPos;
|
||||
MatrixF mCameraMatrix;
|
||||
EulerF mCameraRot;
|
||||
Point3F mOrbitPos;
|
||||
|
||||
F32 mMaxOrbitDist;
|
||||
F32 mMinOrbitDist;
|
||||
|
||||
///
|
||||
F32 mOrbitDist;
|
||||
|
||||
/// Multiplier for camera mouse operations (rotation and zooming).
|
||||
F32 mCameraSpeed;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Mounting
|
||||
/// @{
|
||||
|
||||
/// Name of model to mount to the primary model.
|
||||
String mMountedModelName;
|
||||
|
||||
///
|
||||
String mMountSkinName;
|
||||
|
||||
/// Index of the node to mount the secondary model to. -1 (disabled) by default.
|
||||
S32 mMountNode;
|
||||
|
||||
/// Name of node to mount the secondary model to. Unset by default.
|
||||
String mMountNodeName;
|
||||
|
||||
/// Model mounted as an image to the primary model.
|
||||
TSShapeInstance* mMountedModel;
|
||||
|
||||
///
|
||||
MatrixF mMountTransform;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Animation
|
||||
/// @{
|
||||
|
||||
/// Index of the animation sequence to play on the model. -1 (disabled) by default.
|
||||
S32 mAnimationSeq;
|
||||
|
||||
/// Name of the animation sequence to play on the model. Unset by default.
|
||||
String mAnimationSeqName;
|
||||
|
||||
/// Animation thread on the model.
|
||||
TSThread* mRunThread;
|
||||
|
||||
/// Last time we rendered the model. Used for animation.
|
||||
S32 mLastRenderTime;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Lighting
|
||||
/// @{
|
||||
|
||||
/// Light object used as sun during rendering.
|
||||
LightInfo* mLight;
|
||||
|
||||
///
|
||||
ColorF mLightColor;
|
||||
|
||||
///
|
||||
ColorF mLightAmbient;
|
||||
|
||||
///
|
||||
Point3F mLightDirection;
|
||||
|
||||
/// @}
|
||||
|
||||
///
|
||||
void _initAnimation();
|
||||
|
||||
///
|
||||
void _initMount();
|
||||
|
||||
///
|
||||
void onStaticModified( StringTableEntry slotName, const char* newValue );
|
||||
|
||||
public:
|
||||
|
||||
GuiObjectView();
|
||||
~GuiObjectView();
|
||||
|
||||
/// @name Model
|
||||
/// @{
|
||||
|
||||
///
|
||||
const String& getModelName() const { return mModelName; }
|
||||
|
||||
/// Return the instance of the model being rendered in the view.
|
||||
TSShapeInstance* getModel() const { return mModel; }
|
||||
|
||||
/// Return the name of the skin used on the primary model.
|
||||
const String& getSkin() const { return mSkinName; }
|
||||
|
||||
/// Set the skin to use on the primary model.
|
||||
void setSkin( const String& name );
|
||||
|
||||
/// Set the model to show in this view.
|
||||
void setObjectModel( const String& modelName );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Animation
|
||||
/// @{
|
||||
|
||||
/// Set the animation sequence to play on the model.
|
||||
/// @param seqIndex Index of sequence to play.
|
||||
void setObjectAnimation( S32 seqIndex );
|
||||
|
||||
/// Set the animation sequence to play on the model.
|
||||
/// @param seqIndex Name of sequence to play.
|
||||
void setObjectAnimation( const String& sequenceName );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Mounting
|
||||
/// @{
|
||||
|
||||
/// Return the model mounted to the current primary model; NULL if none.
|
||||
TSShapeInstance* getMountedModel() const { return mMountedModel; }
|
||||
|
||||
///
|
||||
const String& getMountedModelName() const { return mMountedModelName; }
|
||||
|
||||
/// Return the name of the skin used on the mounted model.
|
||||
const String& getMountSkin() const { return mMountSkinName; }
|
||||
|
||||
/// Set the skin to use on the mounted model.
|
||||
void setMountSkin( const String& name );
|
||||
|
||||
///
|
||||
void setMountNode( S32 index );
|
||||
|
||||
///
|
||||
void setMountNode( const String& nodeName );
|
||||
|
||||
///
|
||||
void setMountedObject( const String& modelName );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Camera
|
||||
/// @{
|
||||
|
||||
/// Return the current camera speed multiplier.
|
||||
F32 getCameraSpeed() const { return mCameraSpeed; }
|
||||
|
||||
/// Set the multiplier to apply to camera rotation and zooming.
|
||||
void setCameraSpeed( F32 factor );
|
||||
|
||||
///
|
||||
F32 getOrbitDistance() const { return mOrbitDist; }
|
||||
|
||||
/// Sets the distance at which the camera orbits the object. Clamped to the
|
||||
/// acceptable range defined in the class by min and max orbit distances.
|
||||
///
|
||||
/// @param distance The distance to set the orbit to (will be clamped).
|
||||
void setOrbitDistance( F32 distance );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Lighting
|
||||
/// @{
|
||||
|
||||
///
|
||||
void setLightColor( const ColorF& color );
|
||||
|
||||
///
|
||||
void setLightAmbient( const ColorF& color );
|
||||
|
||||
///
|
||||
void setLightDirection( const Point3F& direction );
|
||||
|
||||
/// @}
|
||||
|
||||
// GuiTsCtrl.
|
||||
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 );
|
||||
|
||||
bool processCameraQuery( CameraQuery* query );
|
||||
void renderWorld( const RectI& updateRect );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
DECLARE_CONOBJECT( GuiObjectView );
|
||||
DECLARE_DESCRIPTION( "A control that shows a TSShape model." );
|
||||
};
|
||||
|
||||
#endif // !_GUIOBJECTVIEW_H_
|
||||
1370
Engine/source/T3D/item.cpp
Normal file
1370
Engine/source/T3D/item.cpp
Normal file
File diff suppressed because it is too large
Load diff
191
Engine/source/T3D/item.h
Normal file
191
Engine/source/T3D/item.h
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ITEM_H_
|
||||
#define _ITEM_H_
|
||||
|
||||
#ifndef _SHAPEBASE_H_
|
||||
#include "T3D/shapeBase.h"
|
||||
#endif
|
||||
#ifndef _BOXCONVEX_H_
|
||||
#include "collision/boxConvex.h"
|
||||
#endif
|
||||
#ifndef _DYNAMIC_CONSOLETYPES_H_
|
||||
#include "console/dynamicTypes.h"
|
||||
#endif
|
||||
|
||||
|
||||
class PhysicsBody;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct ItemData: public ShapeBaseData {
|
||||
typedef ShapeBaseData Parent;
|
||||
|
||||
F32 friction;
|
||||
F32 elasticity;
|
||||
|
||||
bool sticky;
|
||||
F32 gravityMod;
|
||||
F32 maxVelocity;
|
||||
|
||||
bool lightOnlyStatic;
|
||||
S32 lightType;
|
||||
ColorF lightColor;
|
||||
S32 lightTime;
|
||||
F32 lightRadius;
|
||||
|
||||
bool simpleServerCollision;
|
||||
|
||||
ItemData();
|
||||
DECLARE_CONOBJECT(ItemData);
|
||||
static void initPersistFields();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class Item: public ShapeBase
|
||||
{
|
||||
protected:
|
||||
typedef ShapeBase Parent;
|
||||
|
||||
enum MaskBits {
|
||||
HiddenMask = Parent::NextFreeMask,
|
||||
ThrowSrcMask = Parent::NextFreeMask << 1,
|
||||
PositionMask = Parent::NextFreeMask << 2,
|
||||
RotationMask = Parent::NextFreeMask << 3,
|
||||
NextFreeMask = Parent::NextFreeMask << 4
|
||||
};
|
||||
|
||||
// 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 mStatic;
|
||||
bool mRotate;
|
||||
|
||||
//
|
||||
VectorF mVelocity;
|
||||
bool mAtRest;
|
||||
|
||||
S32 mAtRestCounter;
|
||||
static const S32 csmAtRestTimer;
|
||||
|
||||
bool mInLiquid;
|
||||
|
||||
ShapeBase* mCollisionObject;
|
||||
U32 mCollisionTimeout;
|
||||
|
||||
PhysicsBody *mPhysicsRep;
|
||||
|
||||
bool mSubclassItemHandlesScene; ///< A subclass of Item will handle all of the adding to the scene
|
||||
|
||||
protected:
|
||||
DECLARE_CALLBACK( void, onStickyCollision, ( const char* objID ));
|
||||
DECLARE_CALLBACK( void, onEnterLiquid, ( const char* objID, const char* waterCoverage, const char* liquidType ));
|
||||
DECLARE_CALLBACK( void, onLeaveLiquid, ( const char* objID, const char* liquidType ));
|
||||
|
||||
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(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
|
||||
void buildConvex(const Box3F& box, Convex* convex);
|
||||
void onDeleteNotify(SimObject*);
|
||||
|
||||
protected:
|
||||
void _updatePhysics();
|
||||
void prepRenderImage(SceneRenderState *state);
|
||||
void advanceTime(F32 dt);
|
||||
|
||||
public:
|
||||
DECLARE_CONOBJECT(Item);
|
||||
|
||||
|
||||
Item();
|
||||
~Item();
|
||||
static void initPersistFields();
|
||||
static void consoleInit();
|
||||
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onNewDataBlock( GameBaseData *dptr, bool reload );
|
||||
|
||||
bool isStatic() { return mStatic; }
|
||||
bool isAtRest() { return mAtRest; }
|
||||
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);
|
||||
virtual void setTransform(const MatrixF &mat);
|
||||
|
||||
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
|
||||
void unpackUpdate(NetConnection *conn, BitStream *stream);
|
||||
};
|
||||
|
||||
typedef Item::LightType ItemLightType;
|
||||
DefineEnumType( ItemLightType );
|
||||
|
||||
#endif
|
||||
339
Engine/source/T3D/levelInfo.cpp
Normal file
339
Engine/source/T3D/levelInfo.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "T3D/levelInfo.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "scene/sceneManager.h"
|
||||
#include "lighting/advanced/advancedLightManager.h"
|
||||
#include "lighting/advanced/advancedLightBinManager.h"
|
||||
#include "sfx/sfxAmbience.h"
|
||||
#include "sfx/sfxSoundscape.h"
|
||||
#include "sfx/sfxSystem.h"
|
||||
#include "sfx/sfxTypes.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "math/mathIO.h"
|
||||
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(LevelInfo);
|
||||
|
||||
ConsoleDocClass( LevelInfo,
|
||||
"@brief Stores and controls the rendering and status information for a game level.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"new LevelInfo(theLevelInfo)\n"
|
||||
"{\n"
|
||||
" visibleDistance = \"1000\";\n"
|
||||
" fogColor = \"0.6 0.6 0.7 1\";\n"
|
||||
" fogDensity = \"0\";\n"
|
||||
" fogDensityOffset = \"700\";\n"
|
||||
" fogAtmosphereHeight = \"0\";\n"
|
||||
" canvasClearColor = \"0 0 0 255\";\n"
|
||||
" canSaveDynamicFields = \"1\";\n"
|
||||
" levelName = \"Blank Room\";\n"
|
||||
" desc0 = \"A blank room ready to be populated with Torque objects.\";\n"
|
||||
" Enabled = \"1\";\n"
|
||||
"};\n"
|
||||
"@endtsexample\n"
|
||||
"@ingroup enviroMisc\n"
|
||||
);
|
||||
|
||||
|
||||
/// The color used to clear the canvas.
|
||||
/// @see GuiCanvas
|
||||
extern ColorI gCanvasClearColor;
|
||||
|
||||
/// @see DecalManager
|
||||
extern F32 gDecalBias;
|
||||
|
||||
|
||||
/// Default SFXAmbience used to reset the global soundscape.
|
||||
static SFXAmbience sDefaultAmbience;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
LevelInfo::LevelInfo()
|
||||
: mNearClip( 0.1f ),
|
||||
mVisibleDistance( 1000.0f ),
|
||||
mDecalBias( 0.0015f ),
|
||||
mCanvasClearColor( 255, 0, 255, 255 ),
|
||||
mSoundAmbience( NULL ),
|
||||
mSoundscape( NULL ),
|
||||
mSoundDistanceModel( SFXDistanceModelLinear ),
|
||||
mWorldSize( 10000.0f ),
|
||||
mAmbientLightBlendPhase( 1.f )
|
||||
{
|
||||
mFogData.density = 0.0f;
|
||||
mFogData.densityOffset = 0.0f;
|
||||
mFogData.atmosphereHeight = 0.0f;
|
||||
mFogData.color.set( 0.5f, 0.5f, 0.5f, 1.0f ),
|
||||
|
||||
mNetFlags.set( ScopeAlways | Ghostable );
|
||||
|
||||
mAdvancedLightmapSupport = false;
|
||||
|
||||
// Register with the light manager activation signal, and we need to do it first
|
||||
// so the advanced light bin manager can be instructed about MRT lightmaps
|
||||
LightManager::smActivateSignal.notify(this, &LevelInfo::_onLMActivate, 0.01f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
LevelInfo::~LevelInfo()
|
||||
{
|
||||
LightManager::smActivateSignal.remove(this, &LevelInfo::_onLMActivate);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LevelInfo::initPersistFields()
|
||||
{
|
||||
addGroup( "Visibility" );
|
||||
|
||||
addField( "nearClip", TypeF32, Offset( mNearClip, LevelInfo ), "Closest distance from the camera's position to render the world." );
|
||||
addField( "visibleDistance", TypeF32, Offset( mVisibleDistance, LevelInfo ), "Furthest distance fromt he camera's position to render the world." );
|
||||
addField( "decalBias", TypeF32, Offset( mDecalBias, LevelInfo ),
|
||||
"NearPlane bias used when rendering Decal and DecalRoad. This should be tuned to the visibleDistance in your level." );
|
||||
|
||||
endGroup( "Visibility" );
|
||||
|
||||
addGroup( "Fog" );
|
||||
|
||||
addField( "fogColor", TypeColorF, Offset( mFogData.color, LevelInfo ),
|
||||
"The default color for the scene fog." );
|
||||
|
||||
addField( "fogDensity", TypeF32, Offset( mFogData.density, LevelInfo ),
|
||||
"The 0 to 1 density value for the exponential fog falloff." );
|
||||
|
||||
addField( "fogDensityOffset", TypeF32, Offset( mFogData.densityOffset, LevelInfo ),
|
||||
"An offset from the camera in meters for moving the start of the fog effect." );
|
||||
|
||||
addField( "fogAtmosphereHeight", TypeF32, Offset( mFogData.atmosphereHeight, LevelInfo ),
|
||||
"A height in meters for altitude fog falloff." );
|
||||
|
||||
endGroup( "Fog" );
|
||||
|
||||
addGroup( "LevelInfo" );
|
||||
|
||||
addField( "canvasClearColor", TypeColorI, Offset( mCanvasClearColor, LevelInfo ),
|
||||
"The color used to clear the background before the scene or any GUIs are rendered." );
|
||||
|
||||
endGroup( "LevelInfo" );
|
||||
|
||||
addGroup( "Lighting" );
|
||||
|
||||
addField( "ambientLightBlendPhase", TypeF32, Offset( mAmbientLightBlendPhase, LevelInfo ),
|
||||
"Number of seconds it takes to blend from one ambient light color to a different one." );
|
||||
|
||||
addField( "ambientLightBlendCurve", TypeEaseF, Offset( mAmbientLightBlendCurve, LevelInfo ),
|
||||
"Interpolation curve to use for blending from one ambient light color to a different one." );
|
||||
|
||||
addField( "advancedLightmapSupport", TypeBool, Offset( mAdvancedLightmapSupport, LevelInfo ),
|
||||
"Enable expanded support for mixing static and dynamic lighting (more costly)" );
|
||||
|
||||
endGroup( "Lighting" );
|
||||
|
||||
addGroup( "Sound" );
|
||||
|
||||
addField( "soundAmbience", TypeSFXAmbienceName, Offset( mSoundAmbience, LevelInfo ), "The global ambient sound environment." );
|
||||
addField( "soundDistanceModel", TypeSFXDistanceModel, Offset( mSoundDistanceModel, LevelInfo ), "The distance attenuation model to use." );
|
||||
|
||||
endGroup( "Sound" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LevelInfo::inspectPostApply()
|
||||
{
|
||||
_updateSceneGraph();
|
||||
setMaskBits( 0xFFFFFFFF );
|
||||
|
||||
Parent::inspectPostApply();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 LevelInfo::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(conn, mask, stream);
|
||||
|
||||
stream->write( mNearClip );
|
||||
stream->write( mVisibleDistance );
|
||||
stream->write( mDecalBias );
|
||||
|
||||
stream->write( mFogData.density );
|
||||
stream->write( mFogData.densityOffset );
|
||||
stream->write( mFogData.atmosphereHeight );
|
||||
stream->write( mFogData.color );
|
||||
|
||||
stream->write( mCanvasClearColor );
|
||||
stream->write( mWorldSize );
|
||||
|
||||
stream->writeFlag( mAdvancedLightmapSupport );
|
||||
stream->write( mAmbientLightBlendPhase );
|
||||
mathWrite( *stream, mAmbientLightBlendCurve );
|
||||
|
||||
sfxWrite( stream, mSoundAmbience );
|
||||
stream->writeInt( mSoundDistanceModel, 1 );
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LevelInfo::unpackUpdate(NetConnection *conn, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(conn, stream);
|
||||
|
||||
stream->read( &mNearClip );
|
||||
stream->read( &mVisibleDistance );
|
||||
stream->read( &mDecalBias );
|
||||
|
||||
stream->read( &mFogData.density );
|
||||
stream->read( &mFogData.densityOffset );
|
||||
stream->read( &mFogData.atmosphereHeight );
|
||||
stream->read( &mFogData.color );
|
||||
|
||||
stream->read( &mCanvasClearColor );
|
||||
stream->read( &mWorldSize );
|
||||
|
||||
mAdvancedLightmapSupport = stream->readFlag();
|
||||
stream->read( &mAmbientLightBlendPhase );
|
||||
mathRead( *stream, &mAmbientLightBlendCurve );
|
||||
|
||||
String errorStr;
|
||||
if( !sfxReadAndResolve( stream, &mSoundAmbience, errorStr ) )
|
||||
Con::errorf( "%s", errorStr.c_str() );
|
||||
mSoundDistanceModel = ( SFXDistanceModel ) stream->readInt( 1 );
|
||||
|
||||
if( isProperlyAdded() )
|
||||
{
|
||||
_updateSceneGraph();
|
||||
|
||||
if( mSoundscape )
|
||||
{
|
||||
if( mSoundAmbience )
|
||||
mSoundscape->setAmbience( mSoundAmbience );
|
||||
else
|
||||
mSoundscape->setAmbience( &sDefaultAmbience );
|
||||
}
|
||||
|
||||
SFX->setDistanceModel( mSoundDistanceModel );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool LevelInfo::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
// If no sound ambience has been set, default to
|
||||
// 'AudioAmbienceDefault'.
|
||||
|
||||
if( !mSoundAmbience )
|
||||
Sim::findObject( "AudioAmbienceDefault", mSoundAmbience );
|
||||
|
||||
// Set up sound on client.
|
||||
|
||||
if( isClientObject() )
|
||||
{
|
||||
SFX->setDistanceModel( mSoundDistanceModel );
|
||||
|
||||
// Set up the global ambient soundscape.
|
||||
|
||||
mSoundscape = SFX->getSoundscapeManager()->getGlobalSoundscape();
|
||||
if( mSoundAmbience )
|
||||
mSoundscape->setAmbience( mSoundAmbience );
|
||||
}
|
||||
|
||||
_updateSceneGraph();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LevelInfo::onRemove()
|
||||
{
|
||||
if( mSoundscape )
|
||||
mSoundscape->setAmbience( &sDefaultAmbience );
|
||||
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LevelInfo::_updateSceneGraph()
|
||||
{
|
||||
// Clamp above zero before setting on the sceneGraph.
|
||||
// If we don't we get serious crashes.
|
||||
if ( mNearClip <= 0.0f )
|
||||
mNearClip = 0.001f;
|
||||
|
||||
SceneManager* scene = isClientObject() ? gClientSceneGraph : gServerSceneGraph;
|
||||
|
||||
scene->setNearClip( mNearClip );
|
||||
scene->setVisibleDistance( mVisibleDistance );
|
||||
|
||||
gDecalBias = mDecalBias;
|
||||
|
||||
// Set ambient lighting properties.
|
||||
|
||||
scene->setAmbientLightTransitionTime( mAmbientLightBlendPhase * 1000.f );
|
||||
scene->setAmbientLightTransitionCurve( mAmbientLightBlendCurve );
|
||||
|
||||
// Copy our AirFogData into the sceneGraph.
|
||||
scene->setFogData( mFogData );
|
||||
|
||||
// If the level info specifies that MRT pre-pass should be used in this scene
|
||||
// enable it via the appropriate light manager
|
||||
// (Basic lighting doesn't do anything different right now)
|
||||
#ifndef TORQUE_DEDICATED
|
||||
if(isClientObject())
|
||||
_onLMActivate(LIGHTMGR->getId(), true);
|
||||
#endif
|
||||
|
||||
// TODO: This probably needs to be moved.
|
||||
gCanvasClearColor = mCanvasClearColor;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void LevelInfo::_onLMActivate(const char *lm, bool enable)
|
||||
{
|
||||
#ifndef TORQUE_DEDICATED
|
||||
// Advanced light manager
|
||||
if(enable && String(lm) == String("ADVLM"))
|
||||
{
|
||||
AssertFatal(dynamic_cast<AdvancedLightManager *>(LIGHTMGR), "Bad light manager type!");
|
||||
AdvancedLightManager *lightMgr = static_cast<AdvancedLightManager *>(LIGHTMGR);
|
||||
lightMgr->getLightBinManager()->MRTLightmapsDuringPrePass(mAdvancedLightmapSupport);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
138
Engine/source/T3D/levelInfo.h
Normal file
138
Engine/source/T3D/levelInfo.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _LEVELINFO_H_
|
||||
#define _LEVELINFO_H_
|
||||
|
||||
#ifndef _NETOBJECT_H_
|
||||
#include "sim/netObject.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
#ifndef _FOGSTRUCTS_H_
|
||||
#include "scene/fogStructs.h"
|
||||
#endif
|
||||
#ifndef _SFXCOMMON_H_
|
||||
#include "sfx/sfxCommon.h"
|
||||
#endif
|
||||
|
||||
|
||||
class SFXAmbience;
|
||||
class SFXSoundscape;
|
||||
|
||||
|
||||
class LevelInfo : public NetObject
|
||||
{
|
||||
typedef NetObject Parent;
|
||||
|
||||
private:
|
||||
|
||||
F32 mWorldSize;
|
||||
|
||||
FogData mFogData;
|
||||
|
||||
F32 mNearClip;
|
||||
|
||||
F32 mVisibleDistance;
|
||||
|
||||
F32 mDecalBias;
|
||||
|
||||
ColorI mCanvasClearColor;
|
||||
|
||||
/// @name Lighting Properties
|
||||
/// @{
|
||||
|
||||
bool mAdvancedLightmapSupport;
|
||||
|
||||
/// Seconds it takes to go from one global ambient color
|
||||
/// to a different one.
|
||||
F32 mAmbientLightBlendPhase;
|
||||
|
||||
/// Interpolation for going from one global ambient color
|
||||
/// to a different one.
|
||||
EaseF mAmbientLightBlendCurve;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Sound Properties
|
||||
/// @{
|
||||
|
||||
/// Global ambient sound space properties.
|
||||
SFXAmbience* mSoundAmbience;
|
||||
|
||||
/// Distance attenuation model to use.
|
||||
SFXDistanceModel mSoundDistanceModel;
|
||||
|
||||
///
|
||||
SFXSoundscape* mSoundscape;
|
||||
|
||||
/// @}
|
||||
|
||||
/// Responsible for passing on
|
||||
/// the LevelInfo settings to the
|
||||
/// client SceneGraph, from which
|
||||
/// other systems can get at them.
|
||||
void _updateSceneGraph();
|
||||
|
||||
void _onLMActivate(const char *lm, bool enable);
|
||||
|
||||
public:
|
||||
|
||||
LevelInfo();
|
||||
virtual ~LevelInfo();
|
||||
|
||||
DECLARE_CONOBJECT(LevelInfo);
|
||||
|
||||
/// @name SceneObject Inheritance
|
||||
/// @{
|
||||
|
||||
virtual SFXAmbience* getSoundAmbience() const { return mSoundAmbience; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name SimObject Inheritance
|
||||
/// @{
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
virtual void inspectPostApply();
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name NetObject Inheritance
|
||||
/// @{
|
||||
|
||||
enum NetMaskBits
|
||||
{
|
||||
UpdateMask = BIT(0)
|
||||
};
|
||||
|
||||
virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream );
|
||||
virtual void unpackUpdate( NetConnection *conn, BitStream *stream );
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif // _LEVELINFO_H_
|
||||
309
Engine/source/T3D/lightAnimData.cpp
Normal file
309
Engine/source/T3D/lightAnimData.cpp
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "lightAnimData.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "T3D/lightBase.h"
|
||||
#include "math/mRandom.h"
|
||||
#include "math/mathIO.h"
|
||||
#include "T3D/gameBase/processList.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
|
||||
|
||||
LightAnimData::LightAnimData()
|
||||
{
|
||||
}
|
||||
|
||||
LightAnimData::~LightAnimData()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_DATABLOCK_V1( LightAnimData );
|
||||
|
||||
ConsoleDocClass( LightAnimData,
|
||||
"@brief A datablock which defines and performs light animation, such as rotation, brightness fade, and colorization.\n\n"
|
||||
|
||||
"@tsexample\n"
|
||||
"datablock LightAnimData( SubtlePulseLightAnim )\n"
|
||||
"{\n"
|
||||
" brightnessA = 0.5;\n"
|
||||
" brightnessZ = 1;\n"
|
||||
" brightnessPeriod = 1;\n"
|
||||
" brightnessKeys = \"aza\";\n"
|
||||
" brightnessSmooth = true;\n"
|
||||
"};\n"
|
||||
"@endtsexample\n\n"
|
||||
"@see LightBase\n\n"
|
||||
"@see LightDescription\n\n"
|
||||
"@ingroup FX\n"
|
||||
"@ingroup Lighting\n"
|
||||
);
|
||||
|
||||
void LightAnimData::initPersistFields()
|
||||
{
|
||||
addGroup( "Offset",
|
||||
"The XYZ translation animation state relative to the light position." );
|
||||
|
||||
addField( "offsetA", TypeF32, Offset( mOffset.value1, LightAnimData ), 3,
|
||||
"The value of the A key in the keyframe sequence." );
|
||||
|
||||
addField( "offsetZ", TypeF32, Offset( mOffset.value2, LightAnimData ), 3,
|
||||
"The value of the Z key in the keyframe sequence." );
|
||||
|
||||
addField( "offsetPeriod", TypeF32, Offset( mOffset.period, LightAnimData ), 3,
|
||||
"The animation time for keyframe sequence." );
|
||||
|
||||
addField( "offsetKeys", TypeString, Offset( mOffset.keys, LightAnimData ), 3,
|
||||
"The keyframe sequence encoded into a string where characters from A to Z define "
|
||||
"a position between the two animation values." );
|
||||
|
||||
addField( "offsetSmooth", TypeBool, Offset( mOffset.smooth, LightAnimData ), 3,
|
||||
"If true the transition between keyframes will be smooth." );
|
||||
|
||||
endGroup( "Offset" );
|
||||
|
||||
addGroup( "Rotation",
|
||||
"The XYZ rotation animation state relative to the light orientation." );
|
||||
|
||||
addField( "rotA", TypeF32, Offset( mRot.value1, LightAnimData ), 3,
|
||||
"The value of the A key in the keyframe sequence." );
|
||||
|
||||
addField( "rotZ", TypeF32, Offset( mRot.value2, LightAnimData ), 3,
|
||||
"The value of the Z key in the keyframe sequence." );
|
||||
|
||||
addField( "rotPeriod", TypeF32, Offset( mRot.period, LightAnimData ), 3,
|
||||
"The animation time for keyframe sequence." );
|
||||
|
||||
addField( "rotKeys", TypeString, Offset( mRot.keys, LightAnimData ), 3,
|
||||
"The keyframe sequence encoded into a string where characters from A to Z define "
|
||||
"a position between the two animation values." );
|
||||
|
||||
addField( "rotSmooth", TypeBool, Offset( mRot.smooth, LightAnimData ), 3,
|
||||
"If true the transition between keyframes will be smooth." );
|
||||
|
||||
endGroup( "Rotation" );
|
||||
|
||||
addGroup( "Color",
|
||||
"The RGB color animation state." );
|
||||
|
||||
addField( "colorA", TypeF32, Offset( mColor.value1, LightAnimData ), 3,
|
||||
"The value of the A key in the keyframe sequence." );
|
||||
|
||||
addField( "colorZ", TypeF32, Offset( mColor.value2, LightAnimData ), 3,
|
||||
"The value of the Z key in the keyframe sequence." );
|
||||
|
||||
addField( "colorPeriod", TypeF32, Offset( mColor.period, LightAnimData ), 3,
|
||||
"The animation time for keyframe sequence." );
|
||||
|
||||
addField( "colorKeys", TypeString, Offset( mColor.keys, LightAnimData ), 3,
|
||||
"The keyframe sequence encoded into a string where characters from A to Z define "
|
||||
"a position between the two animation values." );
|
||||
|
||||
addField( "colorSmooth", TypeBool, Offset( mColor.smooth, LightAnimData ), 3,
|
||||
"If true the transition between keyframes will be smooth." );
|
||||
|
||||
endGroup( "Color" );
|
||||
|
||||
addGroup( "Brightness",
|
||||
"The brightness animation state." );
|
||||
|
||||
addField( "brightnessA", TypeF32, Offset( mBrightness.value1, LightAnimData ),
|
||||
"The value of the A key in the keyframe sequence." );
|
||||
|
||||
addField( "brightnessZ", TypeF32, Offset( mBrightness.value2, LightAnimData ),
|
||||
"The value of the Z key in the keyframe sequence." );
|
||||
|
||||
addField( "brightnessPeriod", TypeF32, Offset( mBrightness.period, LightAnimData ),
|
||||
"The animation time for keyframe sequence." );
|
||||
|
||||
addField( "brightnessKeys", TypeString, Offset( mBrightness.keys, LightAnimData ),
|
||||
"The keyframe sequence encoded into a string where characters from A to Z define "
|
||||
"a position between the two animation values." );
|
||||
|
||||
addField( "brightnessSmooth", TypeBool, Offset( mBrightness.smooth, LightAnimData ),
|
||||
"If true the transition between keyframes will be smooth." );
|
||||
|
||||
endGroup( "Brightness" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool LightAnimData::preload( bool server, String &errorStr )
|
||||
{
|
||||
if ( !Parent::preload( server, errorStr ) )
|
||||
return false;
|
||||
|
||||
_updateKeys();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LightAnimData::inspectPostApply()
|
||||
{
|
||||
Parent::inspectPostApply();
|
||||
_updateKeys();
|
||||
}
|
||||
|
||||
void LightAnimData::_updateKeys()
|
||||
{
|
||||
mOffset.updateKey();
|
||||
mRot.updateKey();
|
||||
mColor.updateKey();
|
||||
mBrightness.updateKey();
|
||||
}
|
||||
|
||||
template<U32 COUNT>
|
||||
void LightAnimData::AnimValue<COUNT>::updateKey()
|
||||
{
|
||||
for ( U32 i=0; i < COUNT; i++ )
|
||||
{
|
||||
timeScale[i] = 0.0f;
|
||||
keyLen[i] = 0.0f;
|
||||
|
||||
if ( keys[i] && keys[i][0] && period[i] > 0.0f )
|
||||
{
|
||||
keyLen[i] = dStrlen( keys[i] );
|
||||
timeScale[i] = (F32)( keyLen[i] - 1 ) / period[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<U32 COUNT>
|
||||
bool LightAnimData::AnimValue<COUNT>::animate( F32 time, F32 *output )
|
||||
{
|
||||
F32 scaledTime, lerpFactor, valueRange, keyFrameLerp;
|
||||
U32 posFrom, posTo;
|
||||
S32 keyFrameFrom, keyFrameTo;
|
||||
|
||||
bool wasAnimated = false;
|
||||
|
||||
for ( U32 i=0; i < COUNT; i++ )
|
||||
{
|
||||
if ( mIsZero( timeScale[i] ) )
|
||||
continue;
|
||||
|
||||
wasAnimated = true;
|
||||
|
||||
scaledTime = mFmod( time, period[i] ) * timeScale[i];
|
||||
|
||||
posFrom = mFloor( scaledTime );
|
||||
posTo = mCeil( scaledTime );
|
||||
|
||||
keyFrameFrom = dToupper( keys[i][posFrom] ) - 65;
|
||||
keyFrameTo = dToupper( keys[i][posTo] ) - 65;
|
||||
valueRange = ( value2[i] - value1[i] ) / 25.0f;
|
||||
|
||||
if ( !smooth[i] )
|
||||
output[i] = value1[i] + ( keyFrameFrom * valueRange );
|
||||
else
|
||||
{
|
||||
lerpFactor = scaledTime - posFrom;
|
||||
keyFrameLerp = ( keyFrameTo - keyFrameFrom ) * lerpFactor;
|
||||
|
||||
output[i] = value1[i] + ( ( keyFrameFrom + keyFrameLerp ) * valueRange );
|
||||
}
|
||||
}
|
||||
|
||||
return wasAnimated;
|
||||
}
|
||||
|
||||
template<U32 COUNT>
|
||||
void LightAnimData::AnimValue<COUNT>::write( BitStream *stream ) const
|
||||
{
|
||||
for ( U32 i=0; i < COUNT; i++ )
|
||||
{
|
||||
stream->write( value1[i] );
|
||||
stream->write( value2[i] );
|
||||
stream->write( period[i] );
|
||||
stream->writeString( keys[i] );
|
||||
}
|
||||
}
|
||||
|
||||
template<U32 COUNT>
|
||||
void LightAnimData::AnimValue<COUNT>::read( BitStream *stream )
|
||||
{
|
||||
for ( U32 i=0; i < COUNT; i++ )
|
||||
{
|
||||
stream->read( &value1[i] );
|
||||
stream->read( &value2[i] );
|
||||
stream->read( &period[i] );
|
||||
keys[i] = stream->readSTString();
|
||||
}
|
||||
}
|
||||
|
||||
void LightAnimData::packData( BitStream *stream )
|
||||
{
|
||||
Parent::packData( stream );
|
||||
|
||||
mOffset.write( stream );
|
||||
mRot.write( stream );
|
||||
mColor.write( stream );
|
||||
mBrightness.write( stream );
|
||||
}
|
||||
|
||||
void LightAnimData::unpackData( BitStream *stream )
|
||||
{
|
||||
Parent::unpackData( stream );
|
||||
|
||||
mOffset.read( stream );
|
||||
mRot.read( stream );
|
||||
mColor.read( stream );
|
||||
mBrightness.read( stream );
|
||||
}
|
||||
|
||||
void LightAnimData::animate( LightInfo *lightInfo, LightAnimState *state )
|
||||
{
|
||||
PROFILE_SCOPE( LightAnimData_animate );
|
||||
|
||||
// Calculate the input time for animation.
|
||||
F32 time = state->animationPhase +
|
||||
( (F32)Sim::getCurrentTime() * 0.001f ) /
|
||||
state->animationPeriod;
|
||||
|
||||
MatrixF transform( state->transform );
|
||||
|
||||
EulerF euler( Point3F::Zero );
|
||||
if ( mRot.animate( time, euler ) )
|
||||
{
|
||||
euler.x = mDegToRad( euler.x );
|
||||
euler.y = mDegToRad( euler.y );
|
||||
euler.z = mDegToRad( euler.z );
|
||||
MatrixF rot( euler );
|
||||
transform.mul( rot );
|
||||
}
|
||||
|
||||
Point3F offset( Point3F::Zero );
|
||||
if ( mOffset.animate( time, offset ) )
|
||||
transform.displace( offset );
|
||||
|
||||
lightInfo->setTransform( transform );
|
||||
|
||||
ColorF color = state->color;
|
||||
mColor.animate( time, color );
|
||||
lightInfo->setColor( color );
|
||||
|
||||
F32 brightness = state->brightness;
|
||||
mBrightness.animate( time, &brightness );
|
||||
lightInfo->setBrightness( brightness );
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue