Merge branch 'development' into style-cleanup

Conflicts:
	Engine/source/console/astNodes.cpp
	Engine/source/console/codeBlock.cpp
	Engine/source/console/compiledEval.cpp
	Engine/source/ts/collada/colladaAppMesh.cpp
	Engine/source/ts/tsShape.cpp
	Engine/source/ts/tsShapeConstruct.cpp
This commit is contained in:
Daniel Buckmaster 2014-12-15 12:15:55 +11:00
commit 33ff180593
2053 changed files with 172002 additions and 69530 deletions

View file

@ -457,8 +457,9 @@ 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 );
static const U32 bufSize = 256;
char *returnBuffer = Con::getReturnBuffer( bufSize );
dSprintf( returnBuffer, bufSize, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z );
return returnBuffer;
}
@ -470,8 +471,9 @@ ConsoleMethod( AIClient, getMoveDestination, const char *, 2, 2, "ai.getMoveDest
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 );
static const U32 bufSize = 256;
char *returnBuffer = Con::getReturnBuffer( bufSize );
dSprintf( returnBuffer, bufSize, "%f %f %f", movePoint.x, movePoint.y, movePoint.z );
return returnBuffer;
}
@ -522,8 +524,9 @@ 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 );
static const U32 bufSize = 256;
char *returnBuffer = Con::getReturnBuffer( bufSize );
dSprintf( returnBuffer, bufSize, "%f %f %f", locPoint.x, locPoint.y, locPoint.z );
return returnBuffer;
}

View file

@ -147,6 +147,7 @@ ConsoleFunction(aiConnect, S32 , 2, 20, "(...)"
// Make sure and leav args[1] empty.
const char* args[21];
args[0] = "onConnect";
args[1] = NULL; // Filled in later
for (S32 i = 1; i < argc; i++)
args[i + 1] = argv[i];

View file

@ -28,6 +28,8 @@
#include "T3D/gameBase/moveManager.h"
#include "console/engineAPI.h"
static U32 sAIPlayerLoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
IMPLEMENT_CO_NETOBJECT_V1(AIPlayer);
ConsoleDocClass( AIPlayer,
@ -417,28 +419,21 @@ bool AIPlayer::getAIMove(Move *movePtr)
// 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,
StaticShapeObjectType | StaticObjectType |
TerrainObjectType, &dummy)) {
if (mTargetInLOS) {
throwCallback( "onTargetExitLOS" );
mTargetInLOS = false;
}
}
else
if (!mTargetInLOS) {
throwCallback( "onTargetEnterLOS" );
if (mAimObject)
{
if (checkInLos(mAimObject.getPointer()))
{
if (!mTargetInLOS)
{
throwCallback("onTargetEnterLOS");
mTargetInLOS = true;
}
}
else if (mTargetInLOS)
{
throwCallback("onTargetExitLOS");
mTargetInLOS = false;
}
}
// Replicate the trigger state into the move so that
@ -610,3 +605,112 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),,
GameBase* obj = object->getAimObject();
return obj? obj->getId(): -1;
}
bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled)
{
if (!isServerObject()) return false;
if (!target)
{
target = mAimObject.getPointer();
if (!target)
return false;
}
if (_checkEnabled)
{
if (target->getTypeMask() & ShapeBaseObjectType)
{
ShapeBase *shapeBaseCheck = static_cast<ShapeBase *>(target);
if (shapeBaseCheck)
if (shapeBaseCheck->getDamageState() != Enabled) return false;
}
else
return false;
}
RayInfo ri;
disableCollision();
S32 mountCount = target->getMountedObjectCount();
for (S32 i = 0; i < mountCount; i++)
{
target->getMountedObject(i)->disableCollision();
}
Point3F checkPoint ;
if (_useMuzzle)
getMuzzlePointAI(0, &checkPoint );
else
{
MatrixF eyeMat;
getEyeTransform(&eyeMat);
eyeMat.getColumn(3, &checkPoint );
}
bool hit = !gServerContainer.castRay(checkPoint, target->getBoxCenter(), sAIPlayerLoSMask, &ri);
enableCollision();
for (S32 i = 0; i < mountCount; i++)
{
target->getMountedObject(i)->enableCollision();
}
return hit;
}
DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool useMuzzle, bool checkEnabled),(NULL, false, false),
"@brief Check whether an object is in line of sight.\n"
"@obj Object to check. (If blank, it will check the current target).\n"
"@useMuzzle Use muzzle position. Otherwise use eye position. (defaults to false).\n"
"@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n")
{
return object->checkInLos(obj, useMuzzle, checkEnabled);
}
bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled)
{
if (!isServerObject()) return false;
if (!target)
{
target = mAimObject.getPointer();
if (!target)
return false;
}
if (_checkEnabled)
{
if (target->getTypeMask() & ShapeBaseObjectType)
{
ShapeBase *shapeBaseCheck = static_cast<ShapeBase *>(target);
if (shapeBaseCheck)
if (shapeBaseCheck->getDamageState() != Enabled) return false;
}
else
return false;
}
MatrixF cam = getTransform();
Point3F camPos;
VectorF camDir;
cam.getColumn(3, &camPos);
cam.getColumn(1, &camDir);
camFov = mDegToRad(camFov) / 2;
Point3F shapePos = target->getBoxCenter();
VectorF shapeDir = shapePos - camPos;
// 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);
return (dot > camFov);
}
DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (NULL, 45.0f, false),
"@brief Check whether an object is within a specified veiw cone.\n"
"@obj Object to check. (If blank, it will check the current target).\n"
"@fov view angle in degrees.(Defaults to 45)\n"
"@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n")
{
return object->checkInFoV(obj, fov, checkEnabled);
}

View file

@ -80,6 +80,8 @@ public:
void setAimLocation( const Point3F &location );
Point3F getAimLocation() const { return mAimLocation; }
void clearAim();
bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false);
bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false);
// Movement sets/gets
void setMoveSpeed( const F32 speed );

View file

@ -214,7 +214,7 @@ void CameraSpline::renderTimeMap()
// Render the buffer
GFX->pushWorldMatrix();
GFX->disableShaders();
GFX->setupGenericShaders();
GFX->setVertexBuffer(vb);
GFX->drawPrimitive(GFXLineStrip,0,index);
GFX->popWorldMatrix();

View file

@ -41,7 +41,7 @@
#include "lighting/lightQuery.h"
const U32 csmStaticCollisionMask = TerrainObjectType;
const U32 csmStaticCollisionMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
const U32 csmDynamicCollisionMask = StaticShapeObjectType;
@ -99,7 +99,6 @@ DebrisData::DebrisData()
friction = 0.2f;
numBounces = 0;
bounceVariance = 0;
minSpinSpeed = maxSpinSpeed = 0.0;
staticOnMaxBounce = false;
explodeOnMaxBounce = false;
snapOnMaxBounce = false;
@ -511,6 +510,12 @@ bool Debris::onAdd()
return false;
}
if( !mDataBlock )
{
Con::errorf("Debris::onAdd - Fail - No datablock");
return false;
}
// create emitters
for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
{
@ -653,8 +658,7 @@ void Debris::onRemove()
}
}
getSceneManager()->removeObjectFromScene(this);
getContainer()->removeObject(this);
removeFromScene();
Parent::onRemove();
}

View file

@ -45,6 +45,7 @@ protected:
public:
CameraFX();
virtual ~CameraFX() { }
MatrixF & getTrans(){ return mCamFXTrans; }
virtual bool isExpired(){ return mElapsedTime >= mDuration; }

View file

@ -749,9 +749,9 @@ bool ExplosionData::preload(bool server, String &errorStr)
if( !server )
{
String errorStr;
if( !sfxResolve( &soundProfile, errorStr ) )
Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock: %s", errorStr.c_str());
String sfxErrorStr;
if( !sfxResolve( &soundProfile, sfxErrorStr ) )
Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock: %s", sfxErrorStr.c_str());
if (!particleEmitter && particleEmitterId != 0)
if (Sim::findObject(particleEmitterId, particleEmitter) == false)
Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock");
@ -784,6 +784,7 @@ bool ExplosionData::preload(bool server, String &errorStr)
//--------------------------------------
//
Explosion::Explosion()
: mDataBlock( NULL )
{
mTypeMask |= ExplosionObjectType | LightObjectType;
@ -844,6 +845,12 @@ bool Explosion::onAdd()
if ( !conn || !Parent::onAdd() )
return false;
if( !mDataBlock )
{
Con::errorf("Explosion::onAdd - Fail - No datablok");
return false;
}
mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
@ -957,10 +964,7 @@ void Explosion::onRemove()
mMainEmitter = NULL;
}
if (getSceneManager() != NULL)
getSceneManager()->removeObjectFromScene(this);
if (getContainer() != NULL)
getContainer()->removeObject(this);
removeFromScene();
Parent::onRemove();
}

View file

@ -287,14 +287,14 @@ bool LightningData::preload(bool server, String &errorStr)
if (server == false)
{
String errorStr;
String sfxErrorStr;
for (U32 i = 0; i < MaxThunders; i++) {
if( !sfxResolve( &thunderSounds[ i ], errorStr ) )
Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", errorStr.c_str());
if( !sfxResolve( &thunderSounds[ i ], sfxErrorStr ) )
Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str());
}
if( !sfxResolve( &strikeSound, errorStr ) )
Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", errorStr.c_str());
if( !sfxResolve( &strikeSound, sfxErrorStr ) )
Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str());
for (U32 i = 0; i < MaxTextures; i++)
{

View file

@ -1168,9 +1168,7 @@ void Precipitation::destroySplash(Raindrop *drop)
PROFILE_START(PrecipDestroySplash);
if (drop == mSplashHead)
{
mSplashHead = NULL;
PROFILE_END();
return;
mSplashHead = mSplashHead->nextSplashDrop;
}
if (drop->nextSplashDrop)
@ -1668,7 +1666,7 @@ void Precipitation::renderObject(ObjectRenderInst *ri, SceneRenderState *state,
}
else
{
GFX->disableShaders();
GFX->setupGenericShaders(GFXDevice::GSTexture);
// We don't support distance fade or lighting without shaders.
GFX->setStateBlock(mDistantSB);
@ -1801,7 +1799,7 @@ void Precipitation::renderObject(ObjectRenderInst *ri, SceneRenderState *state,
GFX->setShaderConstBuffer(mSplashShaderConsts);
}
else
GFX->disableShaders();
GFX->setupGenericShaders(GFXDevice::GSTexture);
while (curr)
{

View file

@ -0,0 +1,707 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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 "console/consoleTypes.h"
#include "console/typeValidators.h"
#include "core/stream/bitStream.h"
#include "T3D/shapeBase.h"
#include "ts/tsShapeInstance.h"
#include "T3D/fx/ribbon.h"
#include "math/mathUtils.h"
#include "math/mathIO.h"
#include "sim/netConnection.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
#include "materials/sceneData.h"
#include "materials/matInstance.h"
#include "gui/3d/guiTSControl.h"
#include "materials/materialManager.h"
#include "materials/processedShaderMaterial.h"
#include "gfx/gfxTransformSaver.h"
IMPLEMENT_CO_DATABLOCK_V1(RibbonData);
IMPLEMENT_CO_NETOBJECT_V1(Ribbon);
//--------------------------------------------------------------------------
//
RibbonData::RibbonData()
{
for (U8 i = 0; i < NumFields; i++) {
mSizes[i] = 0.0f;
mColours[i].set(0.0f, 0.0f, 0.0f, 1.0f);
mTimes[i] = -1.0f;
}
mRibbonLength = 0;
mUseFadeOut = false;
mFadeAwayStep = 0.032f;
segmentsPerUpdate = 1;
mMatName = StringTable->insert("");
mTileScale = 1.0f;
mFixedTexcoords = false;
mSegmentSkipAmount = 0;
mTexcoordsRelativeToDistance = false;
}
//--------------------------------------------------------------------------
void RibbonData::initPersistFields()
{
Parent::initPersistFields();
addGroup("Ribbon");
addField("size", TypeF32, Offset(mSizes, RibbonData), NumFields,
"The size of the ribbon at the specified keyframe.");
addField("color", TypeColorF, Offset(mColours, RibbonData), NumFields,
"The colour of the ribbon at the specified keyframe.");
addField("position", TypeF32, Offset(mTimes, RibbonData), NumFields,
"The position of the keyframe along the lifetime of the ribbon.");
addField("ribbonLength", TypeS32, Offset(mRibbonLength, RibbonData),
"The amount of segments the Ribbon can maximally have in length.");
addField("segmentsPerUpdate", TypeS32, Offset(segmentsPerUpdate, RibbonData),
"How many segments to add each update.");
addField("skipAmount", TypeS32, Offset(mSegmentSkipAmount, RibbonData),
"The amount of segments to skip each update.");
addField("useFadeOut", TypeBool, Offset(mUseFadeOut, RibbonData),
"If true, the ribbon will fade away after deletion.");
addField("fadeAwayStep", TypeF32, Offset(mFadeAwayStep, RibbonData),
"How much to fade the ribbon with each update, after deletion.");
addField("ribbonMaterial", TypeString, Offset(mMatName, RibbonData),
"The material the ribbon uses for rendering.");
addField("tileScale", TypeF32, Offset(mTileScale, RibbonData),
"How much to scale each 'tile' with, where 1 means the material is stretched"
"across the whole ribbon. (If TexcoordsRelativeToDistance is true, this is in meters.)");
addField("fixedTexcoords", TypeBool, Offset(mFixedTexcoords, RibbonData),
"If true, this prevents 'floating' texture coordinates.");
addField("texcoordsRelativeToDistance", TypeBool, Offset(mTexcoordsRelativeToDistance, RibbonData),
"If true, texture coordinates are scaled relative to distance, this prevents"
"'stretched' textures.");
endGroup("Ribbon");
}
//--------------------------------------------------------------------------
bool RibbonData::onAdd()
{
if(!Parent::onAdd())
return false;
return true;
}
bool RibbonData::preload(bool server, String &errorBuffer)
{
if (Parent::preload(server, errorBuffer) == false)
return false;
return true;
}
//--------------------------------------------------------------------------
void RibbonData::packData(BitStream* stream)
{
Parent::packData(stream);
for (U8 i = 0; i < NumFields; i++) {
stream->write(mSizes[i]);
stream->write(mColours[i]);
stream->write(mTimes[i]);
}
stream->write(mRibbonLength);
stream->writeString(mMatName);
stream->writeFlag(mUseFadeOut);
stream->write(mFadeAwayStep);
stream->write(segmentsPerUpdate);
stream->write(mTileScale);
stream->writeFlag(mFixedTexcoords);
stream->writeFlag(mTexcoordsRelativeToDistance);
}
void RibbonData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
for (U8 i = 0; i < NumFields; i++) {
stream->read(&mSizes[i]);
stream->read(&mColours[i]);
stream->read(&mTimes[i]);
}
stream->read(&mRibbonLength);
mMatName = StringTable->insert(stream->readSTString());
mUseFadeOut = stream->readFlag();
stream->read(&mFadeAwayStep);
stream->read(&segmentsPerUpdate);
stream->read(&mTileScale);
mFixedTexcoords = stream->readFlag();
mTexcoordsRelativeToDistance = stream->readFlag();
}
//--------------------------------------------------------------------------
//--------------------------------------
//
Ribbon::Ribbon()
{
mTypeMask |= StaticObjectType;
VECTOR_SET_ASSOCIATION(mSegmentPoints);
mSegmentPoints.clear();
mRibbonMat = NULL;
mUpdateBuffers = true;
mDeleteOnEnd = false;
mUseFadeOut = false;
mFadeAwayStep = 1.0f;
mFadeOut = 1.0f;
mNetFlags.clear(Ghostable);
mNetFlags.set(IsGhost);
mRadiusSC = NULL;
mRibbonProjSC = NULL;
mSegmentOffset = 0;
mSegmentIdx = 0;
mTravelledDistance = 0;
}
Ribbon::~Ribbon()
{
//Make sure we cleanup
SAFE_DELETE(mRibbonMat);
}
//--------------------------------------------------------------------------
void Ribbon::initPersistFields()
{
Parent::initPersistFields();
}
bool Ribbon::onAdd()
{
if(!Parent::onAdd())
return false;
// add to client side mission cleanup
SimGroup *cleanup = dynamic_cast<SimGroup *>( Sim::findObject( "ClientMissionCleanup") );
if( cleanup != NULL )
{
cleanup->addObject( this );
}
else
{
AssertFatal( false, "Error, could not find ClientMissionCleanup group" );
return false;
}
if (!isServerObject()) {
if(GFX->getPixelShaderVersion() >= 1.1 && dStrlen(mDataBlock->mMatName) > 0 )
{
mRibbonMat = MATMGR->createMatInstance( mDataBlock->mMatName );
GFXStateBlockDesc desc;
desc.setZReadWrite( true, false );
desc.cullDefined = true;
desc.cullMode = GFXCullNone;
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
desc.samplersDefined = true;
GFXSamplerStateDesc sDesc(GFXSamplerStateDesc::getClampLinear());
sDesc.addressModeV = GFXAddressWrap;
desc.samplers[0] = sDesc;
mRibbonMat->addStateBlockDesc( desc );
mRibbonMat->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPCNTT>());
mRadiusSC = mRibbonMat->getMaterialParameterHandle( "$radius" );
mRibbonProjSC = mRibbonMat->getMaterialParameterHandle( "$ribbonProj" );
} else {
Con::warnf( "Invalid Material name: %s: for Ribbon", mDataBlock->mMatName );
#ifdef TORQUE_DEBUG
Con::warnf( "- This could be caused by having the shader data datablocks in server-only code." );
#endif
mRibbonMat = NULL;
}
}
mObjBox.minExtents.set( 1.0f, 1.0f, 1.0f );
mObjBox.maxExtents.set( 2.0f, 2.0f, 2.0f );
// Reset the World Box.
resetWorldBox();
// Set the Render Transform.
setRenderTransform(mObjToWorld);
addToScene();
return true;
}
void Ribbon::onRemove()
{
removeFromScene();
SAFE_DELETE(mRibbonMat);
Parent::onRemove();
}
bool Ribbon::onNewDataBlock(GameBaseData* dptr, bool reload)
{
mDataBlock = dynamic_cast<RibbonData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
return false;
return true;
}
void Ribbon::processTick(const Move* move)
{
Parent::processTick(move);
if (mDeleteOnEnd) {
if (mUseFadeOut) {
if (mFadeOut <= 0.0f) {
mFadeOut = 0.0f;
//delete this class
mDeleteOnEnd = false;
safeDeleteObject();
return;
}
mFadeOut -= mFadeAwayStep;
if (mFadeOut < 0.0f) {
mFadeOut = 0.0f;
}
mUpdateBuffers = true;
} else {
//if (mSegmentPoints.size() == 0) {
//delete this class
mDeleteOnEnd = false;
safeDeleteObject();
return;
//}
//mSegmentPoints.pop_back();
}
}
}
void Ribbon::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
}
void Ribbon::interpolateTick(F32 delta)
{
Parent::interpolateTick(delta);
}
void Ribbon::addSegmentPoint(Point3F &point, MatrixF &mat) {
//update our position
setRenderTransform(mat);
MatrixF xform(true);
xform.setColumn(3, point);
setTransform(xform);
if(mSegmentIdx < mDataBlock->mSegmentSkipAmount)
{
mSegmentIdx++;
return;
}
mSegmentIdx = 0;
U32 segmentsToDelete = checkRibbonDistance(mDataBlock->segmentsPerUpdate);
for (U32 i = 0; i < segmentsToDelete; i++) {
U32 last = mSegmentPoints.size() - 1;
if (last < 0)
break;
mTravelledDistance += last ? (mSegmentPoints[last] - mSegmentPoints[last-1]).len() : 0;
mSegmentPoints.pop_back();
mUpdateBuffers = true;
mSegmentOffset++;
}
//If there is no other points, just add a new one.
if (mSegmentPoints.size() == 0) {
mSegmentPoints.push_front(point);
mUpdateBuffers = true;
return;
}
Point3F startPoint = mSegmentPoints[0];
//add X points based on how many segments Per Update from last point to current point
for (U32 i = 0; i < mDataBlock->segmentsPerUpdate; i++) {
F32 interp = (F32(i+1) / (F32)mDataBlock->segmentsPerUpdate);
//(end - start) * percentage) + start
Point3F derivedPoint = ((point - startPoint) * interp) + startPoint;
mSegmentPoints.push_front(derivedPoint);
mUpdateBuffers = true;
}
if (mSegmentPoints.size() > 1) {
Point3F pointA = mSegmentPoints[mSegmentPoints.size()-1];
Point3F pointB = mSegmentPoints[0];
Point3F diffSize = pointA - pointB;
if (diffSize.x == 0.0f)
diffSize.x = 1.0f;
if (diffSize.y == 0.0f)
diffSize.y = 1.0f;
if (diffSize.z == 0.0f)
diffSize.z = 1.0f;
Box3F objBox;
objBox.minExtents.set( diffSize * -1 );
objBox.maxExtents.set( diffSize );
if (objBox.minExtents.x > objBox.maxExtents.x) {
F32 tmp = objBox.minExtents.x;
objBox.minExtents.x = objBox.maxExtents.x;
objBox.maxExtents.x = tmp;
}
if (objBox.minExtents.y > objBox.maxExtents.y) {
F32 tmp = objBox.minExtents.y;
objBox.minExtents.y = objBox.maxExtents.y;
objBox.maxExtents.y = tmp;
}
if (objBox.minExtents.z > objBox.maxExtents.z) {
F32 tmp = objBox.minExtents.z;
objBox.minExtents.z = objBox.maxExtents.z;
objBox.maxExtents.z = tmp;
}
if (objBox.isValidBox()) {
mObjBox = objBox;
// Reset the World Box.
resetWorldBox();
}
}
}
void Ribbon::deleteOnEnd() {
mDeleteOnEnd = true;
mUseFadeOut = mDataBlock->mUseFadeOut;
mFadeAwayStep = mDataBlock->mFadeAwayStep;
}
U32 Ribbon::checkRibbonDistance(S32 segments) {
S32 len = mSegmentPoints.size();
S32 difference = (mDataBlock->mRibbonLength/(mDataBlock->mSegmentSkipAmount+1)) - len;
if (difference < 0)
return mAbs(difference);
return 0; //do not delete any points
}
void Ribbon::setShaderParams() {
F32 numSegments = (F32)mSegmentPoints.size();
F32 length = (F32)mDataBlock->mRibbonLength;
Point3F radius(numSegments / length, numSegments, length);
MaterialParameters* matParams = mRibbonMat->getMaterialParameters();
matParams->setSafe( mRadiusSC, radius );
}
//--------------------------------------------------------------------------
//U32 Ribbon::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
//{
// U32 retMask = Parent::packUpdate(con, mask, stream);
// return retMask;
//}
//
//void Ribbon::unpackUpdate(NetConnection* con, BitStream* stream)
//{
// Parent::unpackUpdate(con, stream);
//}
//--------------------------------------------------------------------------
void Ribbon::prepRenderImage(SceneRenderState *state)
{
if (mFadeOut == 0.0f)
return;
if(!mRibbonMat)
return;
if (mDeleteOnEnd == true && mUseFadeOut == false) {
return;
}
// We only render during the normal diffuse render pass.
if( !state->isDiffusePass() )
return;
U32 segments = mSegmentPoints.size();
if (segments < 2)
return;
MeshRenderInst *ri = state->getRenderPass()->allocInst<MeshRenderInst>();
ri->type = RenderPassManager::RIT_Translucent;
ri->translucentSort = true;
ri->sortDistSq = ( mSegmentPoints[0] - state->getCameraPosition() ).lenSquared();
RenderPassManager *renderPass = state->getRenderPass();
MatrixF *proj = renderPass->allocUniqueXform(MatrixF( true ));
proj->mul(GFX->getProjectionMatrix());
proj->mul(GFX->getWorldMatrix());
ri->objectToWorld = &MatrixF::Identity;
ri->worldToCamera = &MatrixF::Identity;
ri->projection = proj;
ri->matInst = mRibbonMat;
// Set up our vertex buffer and primitive buffer
if(mUpdateBuffers)
createBuffers(state, verts, primBuffer, segments);
ri->vertBuff = &verts;
ri->primBuff = &primBuffer;
ri->visibility = 1.0f;
ri->prim = renderPass->allocPrim();
ri->prim->type = GFXTriangleList;
ri->prim->minIndex = 0;
ri->prim->startIndex = 0;
ri->prim->numPrimitives = (segments-1) * 2;
ri->prim->startVertex = 0;
ri->prim->numVertices = segments * 2;
if (mRibbonMat) {
ri->defaultKey = mRibbonMat->getStateHint();
} else {
ri->defaultKey = 1;
}
ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe!
state->getRenderPass()->addInst(ri);
}
void Ribbon::createBuffers(SceneRenderState *state, GFXVertexBufferHandle<GFXVertexPCNTT> &verts, GFXPrimitiveBufferHandle &pb, U32 segments) {
PROFILE_SCOPE( Ribbon_createBuffers );
Point3F cameraPos = state->getCameraPosition();
U32 count = 0;
U32 indexCount = 0;
verts.set(GFX, (segments*2), GFXBufferTypeDynamic);
// create index buffer based on that size
U32 indexListSize = (segments-1) * 6;
pb.set( GFX, indexListSize, 0, GFXBufferTypeDynamic );
U16 *indices = NULL;
verts.lock();
pb.lock( &indices );
F32 totalLength = 0;
if(mDataBlock->mTexcoordsRelativeToDistance)
{
for (U32 i = 0; i < segments; i++)
if (i != 0)
totalLength += (mSegmentPoints[i] - mSegmentPoints[i-1]).len();
}
U8 fixedAppend = 0;
F32 curLength = 0;
for (U32 i = 0; i < segments; i++) {
F32 interpol = ((F32)i / (F32)(segments-1));
Point3F leftvert = mSegmentPoints[i];
Point3F rightvert = mSegmentPoints[i];
F32 tRadius = mDataBlock->mSizes[0];
ColorF tColor = mDataBlock->mColours[0];
for (U8 j = 0; j < RibbonData::NumFields-1; j++) {
F32 curPosition = mDataBlock->mTimes[j];
F32 curRadius = mDataBlock->mSizes[j];
ColorF curColor = mDataBlock->mColours[j];
F32 nextPosition = mDataBlock->mTimes[j+1];
F32 nextRadius = mDataBlock->mSizes[j+1];
ColorF nextColor = mDataBlock->mColours[j+1];
if ( curPosition < 0
|| curPosition > interpol )
break;
F32 positionDiff = (interpol - curPosition) / (nextPosition - curPosition);
tRadius = curRadius + (nextRadius - curRadius) * positionDiff;
tColor.interpolate(curColor, nextColor, positionDiff);
}
Point3F diff;
F32 length;
if (i == 0) {
diff = mSegmentPoints[i+1] - mSegmentPoints[i];
length = 0;
} else if (i == segments-1) {
diff = mSegmentPoints[i] - mSegmentPoints[i-1];
length = diff.len();
} else {
diff = mSegmentPoints[i+1] - mSegmentPoints[i-1];
length = (mSegmentPoints[i] - mSegmentPoints[i-1]).len();
}
//left point
Point3F eyeMinPos = cameraPos - leftvert;
Point3F perpendicular = mCross(diff, eyeMinPos);
perpendicular.normalize();
perpendicular = perpendicular * tRadius * -1.0f;
perpendicular += mSegmentPoints[i];
verts[count].point.set(perpendicular);
ColorF color = tColor;
if (mDataBlock->mUseFadeOut)
color.alpha *= mFadeOut;
F32 texCoords;
if(mDataBlock->mFixedTexcoords && !mDataBlock->mTexcoordsRelativeToDistance)
{
U32 fixedIdx = (i+mDataBlock->mRibbonLength-mSegmentOffset)%mDataBlock->mRibbonLength;
if(fixedIdx == 0 && i > 0)
fixedAppend++;
F32 fixedInterpol = (F32)fixedIdx / (F32)(mDataBlock->mRibbonLength);
fixedInterpol += fixedAppend;
texCoords = (1.0f - fixedInterpol)*mDataBlock->mTileScale;
}
else if(mDataBlock->mTexcoordsRelativeToDistance)
texCoords = (mTravelledDistance + (totalLength - (curLength + length)))*mDataBlock->mTileScale;
else
texCoords = (1.0f - interpol)*mDataBlock->mTileScale;
verts[count].color = color;
verts[count].texCoord[1] = Point2F(interpol, 0);
verts[count].texCoord[0] = Point2F(0.0f, texCoords);
verts[count].normal.set(diff);
//Triangle strip style indexing, so grab last 2
if (count > 1) {
indices[indexCount] = count-2;
indexCount++;
indices[indexCount] = count;
indexCount++;
indices[indexCount] = count-1;
indexCount++;
}
count++;
eyeMinPos = cameraPos - rightvert;
perpendicular = mCross(diff, eyeMinPos);
perpendicular.normalize();
perpendicular = perpendicular * tRadius;
perpendicular += mSegmentPoints[i];
verts[count].point.set(perpendicular);
color = tColor;
if (mDataBlock->mUseFadeOut)
color.alpha *= mFadeOut;
verts[count].color = color;
verts[count].texCoord[1] = Point2F(interpol, 1);
verts[count].texCoord[0] = Point2F(1.0f, texCoords);
verts[count].normal.set(diff);
//Triangle strip style indexing, so grab last 2
if (count > 1) {
indices[indexCount] = count-2;
indexCount++;
indices[indexCount] = count-1;
indexCount++;
indices[indexCount] = count;
indexCount++;
}
count++;
curLength += length;
}
Point3F pointA = verts[count-1].point;
Point3F pointB = verts[0].point;
pb.unlock();
verts.unlock();
Point3F diffSize = pointA - pointB;
Box3F objBox;
objBox.minExtents.set( diffSize * -1 );
objBox.maxExtents.set( diffSize );
if (objBox.minExtents.x > objBox.maxExtents.x) {
F32 tmp = objBox.minExtents.x;
objBox.minExtents.x = objBox.maxExtents.x;
objBox.maxExtents.x = tmp;
}
if (objBox.minExtents.y > objBox.maxExtents.y) {
F32 tmp = objBox.minExtents.y;
objBox.minExtents.y = objBox.maxExtents.y;
objBox.maxExtents.y = tmp;
}
if (objBox.minExtents.z > objBox.maxExtents.z) {
F32 tmp = objBox.minExtents.z;
objBox.minExtents.z = objBox.maxExtents.z;
objBox.maxExtents.z = tmp;
}
if (objBox.isValidBox()) {
mObjBox = objBox;
// Reset the World Box.
resetWorldBox();
}
mUpdateBuffers = false;
}

View file

@ -0,0 +1,142 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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 _RIBBON_H_
#define _RIBBON_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
#ifndef _GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif
#ifndef _GFXVERTEXBUFFER_H_
#include "gfx/gfxVertexBuffer.h"
#endif
#include "materials/materialParameters.h"
#include "math/util/matrixSet.h"
//--------------------------------------------------------------------------
class RibbonData : public GameBaseData
{
typedef GameBaseData Parent;
protected:
bool onAdd();
public:
enum Constants
{
NumFields = 4
};
F32 mSizes[NumFields]; ///< The radius for each keyframe.
ColorF mColours[NumFields]; ///< The colour of the ribbon for each keyframe.
F32 mTimes[NumFields]; ///< The relative time for each keyframe.
U32 mRibbonLength; ///< The amount of segments that will make up the ribbon.
S32 segmentsPerUpdate; ///< Amount of segments to add each update.
S32 mSegmentSkipAmount; ///< The amount of segments to skip each time segments are added.
bool mUseFadeOut; ///< If true, the ribbon will fade away after deletion.
F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion.
StringTableEntry mMatName; ///< The material for the ribbon.
F32 mTileScale; ///< A scalar to scale the texcoord.
bool mFixedTexcoords; ///< If true, texcoords will stay the same over the lifetime for each segment.
bool mTexcoordsRelativeToDistance; ///< If true, texcoords will not be stretched if the distance between 2 segments are long.
RibbonData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, String &errorBuffer);
static void initPersistFields();
DECLARE_CONOBJECT(RibbonData);
};
//--------------------------------------------------------------------------
class Ribbon : public GameBase
{
typedef GameBase Parent;
RibbonData* mDataBlock;
bool mDeleteOnEnd; ///< If true, the ribbon should delete itself as soon as the last segment is deleted
bool mUseFadeOut; ///< If true, the ribbon will fade away upon deletion
F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion.
F32 mFadeOut;
F32 mTravelledDistance; ///< How far the ribbon has travelled in it's lifetime.
Vector<Point3F> mSegmentPoints; ///< The points in space where the ribbon has spawned segments.
U32 mSegmentOffset;
U32 mSegmentIdx;
bool mUpdateBuffers; ///< If true, the vertex buffers need to be updated.
BaseMatInstance *mRibbonMat;
MaterialParameterHandle* mRadiusSC;
MaterialParameterHandle* mRibbonProjSC;
GFXPrimitiveBufferHandle primBuffer;
GFXVertexBufferHandle<GFXVertexPCNTT> verts;
protected:
bool onAdd();
void processTick(const Move*);
void advanceTime(F32);
void interpolateTick(F32 delta);
// Rendering
void prepRenderImage(SceneRenderState *state);
void setShaderParams();
///Checks to see if ribbon is too long
U32 checkRibbonDistance(S32 segments);
/// Construct the vertex and primitive buffers
void createBuffers(SceneRenderState *state, GFXVertexBufferHandle<GFXVertexPCNTT> &verts, GFXPrimitiveBufferHandle &pb, U32 segments);
public:
Ribbon();
~Ribbon();
DECLARE_CONOBJECT(Ribbon);
static void initPersistFields();
bool onNewDataBlock(GameBaseData*,bool);
void onRemove();
/// Used to add another segment to the ribbon.
void addSegmentPoint(Point3F &point, MatrixF &mat);
/// Delete all segments.
void clearSegments() { mSegmentPoints.clear(); }
/// Delete the ribbon when all segments have been deleted.
void deleteOnEnd();
};
#endif // _H_RIBBON

View file

@ -0,0 +1,324 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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 "ribbonNode.h"
#include "console/consoleTypes.h"
#include "core/stream/bitStream.h"
#include "T3D/fx/ribbon.h"
#include "math/mathIO.h"
#include "sim/netConnection.h"
#include "console/engineAPI.h"
IMPLEMENT_CO_DATABLOCK_V1(RibbonNodeData);
IMPLEMENT_CO_NETOBJECT_V1(RibbonNode);
ConsoleDocClass( RibbonNodeData,
"@brief Contains additional data to be associated with a RibbonNode."
"@ingroup FX\n"
);
ConsoleDocClass( RibbonNode, ""
);
//-----------------------------------------------------------------------------
// RibbonNodeData
//-----------------------------------------------------------------------------
RibbonNodeData::RibbonNodeData()
{
}
RibbonNodeData::~RibbonNodeData()
{
}
//-----------------------------------------------------------------------------
// initPersistFields
//-----------------------------------------------------------------------------
void RibbonNodeData::initPersistFields()
{
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// RibbonNode
//-----------------------------------------------------------------------------
RibbonNode::RibbonNode()
{
// Todo: ScopeAlways?
mNetFlags.set(Ghostable);
mTypeMask |= EnvironmentObjectType;
mActive = true;
mDataBlock = NULL;
mRibbonDatablock = NULL;
mRibbonDatablockId = 0;
mRibbon = NULL;
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
RibbonNode::~RibbonNode()
{
//
}
//-----------------------------------------------------------------------------
// initPersistFields
//-----------------------------------------------------------------------------
void RibbonNode::initPersistFields()
{
addField( "active", TYPEID< bool >(), Offset(mActive,RibbonNode),
"Controls whether ribbon is emitted from this node." );
addField( "ribbon", TYPEID< RibbonData >(), Offset(mRibbonDatablock, RibbonNode),
"Datablock to use for the ribbon." );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// onAdd
//-----------------------------------------------------------------------------
bool RibbonNode::onAdd()
{
if( !Parent::onAdd() )
return false;
if( !mRibbonDatablock && mRibbonDatablockId != 0 )
{
if( Sim::findObject(mRibbonDatablockId, mRibbonDatablock) == false )
Con::errorf(ConsoleLogEntry::General, "RibbonNode::onAdd: Invalid packet, bad datablockId(mRibbonDatablock): %d", mRibbonDatablockId);
}
if( isClientObject() )
{
setRibbonDatablock( mRibbonDatablock );
}
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 RibbonNode::onRemove()
{
removeFromScene();
if( isClientObject() )
{
if( mRibbon )
{
mRibbon->deleteOnEnd();
mRibbon = NULL;
}
}
Parent::onRemove();
}
//-----------------------------------------------------------------------------
// onNewDataBlock
//-----------------------------------------------------------------------------
bool RibbonNode::onNewDataBlock( GameBaseData *dptr, bool reload )
{
mDataBlock = dynamic_cast<RibbonNodeData*>( dptr );
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
return false;
// Todo: Uncomment if this is a "leaf" class
scriptOnNewDataBlock();
return true;
}
//-----------------------------------------------------------------------------
void RibbonNode::inspectPostApply()
{
Parent::inspectPostApply();
setMaskBits(StateMask | EmitterDBMask);
}
//-----------------------------------------------------------------------------
// processTick
//-----------------------------------------------------------------------------
void RibbonNode::processTick(const Move* move)
{
Parent::processTick(move);
if ( isMounted() )
{
MatrixF mat;
mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
setTransform( mat );
}
}
//-----------------------------------------------------------------------------
// advanceTime
//-----------------------------------------------------------------------------
void RibbonNode::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
if(!mActive || mRibbon.isNull() || !mDataBlock)
return;
MatrixF trans(getTransform());
Point3F pos = getPosition();
mRibbon->addSegmentPoint(pos, trans);
}
//-----------------------------------------------------------------------------
// packUpdate
//-----------------------------------------------------------------------------
U32 RibbonNode::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(mRibbonDatablock != NULL) )
{
stream->writeRangedU32(mRibbonDatablock->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
}
if ( stream->writeFlag( mask & StateMask ) )
{
stream->writeFlag( mActive );
}
return retMask;
}
//-----------------------------------------------------------------------------
// unpackUpdate
//-----------------------------------------------------------------------------
void RibbonNode::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() )
{
mRibbonDatablockId = stream->readFlag() ?
stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast) : 0;
RibbonData *emitterDB = NULL;
Sim::findObject( mRibbonDatablockId, emitterDB );
if ( isProperlyAdded() )
setRibbonDatablock( emitterDB );
}
if ( stream->readFlag() )
{
mActive = stream->readFlag();
}
}
//-----------------------------------------------------------------------------
// setRibbonDatablock
//-----------------------------------------------------------------------------
void RibbonNode::setRibbonDatablock(RibbonData* data)
{
if ( isServerObject() )
{
setMaskBits( EmitterDBMask );
}
else
{
Ribbon* pRibbon = NULL;
if ( data )
{
// Create emitter with new datablock
pRibbon = new Ribbon;
pRibbon->onNewDataBlock( data, false );
if( pRibbon->registerObject() == false )
{
Con::warnf(ConsoleLogEntry::General, "Could not register base ribbon of class: %s", data->getName() ? data->getName() : data->getIdString() );
delete pRibbon;
return;
}
}
// Replace emitter
if ( mRibbon )
mRibbon->deleteOnEnd();
mRibbon = pRibbon;
}
mRibbonDatablock = data;
}
DefineEngineMethod(RibbonNode, setRibbonDatablock, void, (RibbonData* ribbonDatablock), (0),
"Assigns the datablock for this ribbon node.\n"
"@param ribbonDatablock RibbonData datablock to assign\n"
"@tsexample\n"
"// Assign a new emitter datablock\n"
"%emitter.setRibbonDatablock( %ribbonDatablock );\n"
"@endtsexample\n" )
{
if ( !ribbonDatablock )
{
Con::errorf("RibbonData datablock could not be found when calling setRibbonDataBlock in ribbonNode.");
return;
}
object->setRibbonDatablock(ribbonDatablock);
}
DefineEngineMethod(RibbonNode, setActive, void, (bool active),,
"Turns the ribbon on or off.\n"
"@param active New ribbon state\n" )
{
object->setActive( active );
}

View file

@ -0,0 +1,105 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2014 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 _RIBBON_NODE_H_
#define _RIBBON_NODE_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
class RibbonData;
class Ribbon;
//*****************************************************************************
// ParticleEmitterNodeData
//*****************************************************************************
class RibbonNodeData : public GameBaseData
{
typedef GameBaseData Parent;
public:
F32 timeMultiple;
public:
RibbonNodeData();
~RibbonNodeData();
DECLARE_CONOBJECT(RibbonNodeData);
static void initPersistFields();
};
//*****************************************************************************
// ParticleEmitterNode
//*****************************************************************************
class RibbonNode : public GameBase
{
typedef GameBase Parent;
enum MaskBits
{
StateMask = Parent::NextFreeMask << 0,
EmitterDBMask = Parent::NextFreeMask << 1,
NextFreeMask = Parent::NextFreeMask << 2,
};
RibbonNodeData* mDataBlock;
protected:
bool onAdd();
void onRemove();
bool onNewDataBlock( GameBaseData *dptr, bool reload );
void inspectPostApply();
RibbonData* mRibbonDatablock;
S32 mRibbonDatablockId;
SimObjectPtr<Ribbon> mRibbon;
bool mActive;
public:
RibbonNode();
~RibbonNode();
Ribbon *getRibbonEmitter() {return mRibbon;}
// Time/Move Management
void processTick(const Move* move);
void advanceTime(F32 dt);
DECLARE_CONOBJECT(RibbonNode);
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 setRibbonDatablock(RibbonData* data);
};
#endif // _RIBBON_NODE_H_

View file

@ -303,6 +303,7 @@ bool SplashData::preload(bool server, String &errorStr)
// Splash
//--------------------------------------------------------------------------
Splash::Splash()
: mDataBlock( NULL )
{
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
@ -353,6 +354,12 @@ bool Splash::onAdd()
if(!conn || !Parent::onAdd())
return false;
if( !mDataBlock )
{
Con::errorf("Splash::onAdd - Fail - No datablock");
return false;
}
mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
@ -408,8 +415,7 @@ void Splash::onRemove()
ringList.clear();
getSceneManager()->removeObjectFromScene(this);
getContainer()->removeObject(this);
removeFromScene();
Parent::onRemove();
}

View file

@ -281,7 +281,8 @@ ConsoleMethod(GameConnection, setConnectArgs, void, 3, 17,
"@see GameConnection::onConnect()\n\n")
{
object->setConnectArgs(argc - 2, argv + 2);
StringStackWrapper args(argc - 2, argv + 2);
object->setConnectArgs(args.count(), args);
}
void GameConnection::onTimedOut()
@ -323,6 +324,7 @@ void GameConnection::onConnectionEstablished(bool isInitiator)
const char *argv[MaxConnectArgs + 2];
argv[0] = "onConnect";
argv[1] = NULL; // Filled in later
for(U32 i = 0; i < mConnectArgc; i++)
argv[i + 2] = mConnectArgv[i];
// NOTE: Need to fallback to Con::execute() as IMPLEMENT_CALLBACK does not
@ -442,7 +444,7 @@ bool GameConnection::readConnectRequest(BitStream *stream, const char **errorStr
*errorString = "CR_INVALID_ARGS";
return false;
}
const char *connectArgv[MaxConnectArgs + 3];
ConsoleValueRef connectArgv[MaxConnectArgs + 3];
for(U32 i = 0; i < mConnectArgc; i++)
{
char argString[256];
@ -451,6 +453,7 @@ bool GameConnection::readConnectRequest(BitStream *stream, const char **errorStr
connectArgv[i + 3] = mConnectArgv[i];
}
connectArgv[0] = "onConnectRequest";
connectArgv[1] = NULL;
char buffer[256];
Net::addressToString(getNetAddress(), buffer);
connectArgv[2] = buffer;
@ -974,7 +977,7 @@ bool GameConnection::readDemoStartBlock(BitStream *stream)
void GameConnection::demoPlaybackComplete()
{
static const char *demoPlaybackArgv[1] = { "demoPlaybackComplete" };
static ConsoleValueRef demoPlaybackArgv[1] = { "demoPlaybackComplete" };
Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(1, demoPlaybackArgv, false));
Parent::demoPlaybackComplete();
}

View file

@ -145,9 +145,10 @@ ConsoleFunction(containerFindFirst, const char*, 6, 6, "(int mask, Point3F point
//return the first element
sgServerQueryIndex = 0;
char *buff = Con::getReturnBuffer(100);
static const U32 bufSize = 100;
char *buff = Con::getReturnBuffer(bufSize);
if (sgServerQueryList.mList.size())
dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
else
buff[0] = '\0';
@ -162,9 +163,10 @@ ConsoleFunction( containerFindNext, const char*, 1, 1, "()"
"@ingroup Game")
{
//return the next element
char *buff = Con::getReturnBuffer(100);
static const U32 bufSize = 100;
char *buff = Con::getReturnBuffer(bufSize);
if (sgServerQueryIndex < sgServerQueryList.mList.size())
dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId());
else
buff[0] = '\0';

View file

@ -110,7 +110,7 @@ GuiObjectView::GuiObjectView()
{
mCameraMatrix.identity();
mCameraRot.set( 0.0f, 0.0f, 3.9f );
mCameraPos.set( 0.0f, 1.75f, 1.25f );
mCameraPos.set( 0.0f, 0.0f, 0.0f );
mCameraMatrix.setColumn( 3, mCameraPos );
mOrbitPos.set( 0.0f, 0.0f, 0.0f );
@ -520,9 +520,9 @@ void GuiObjectView::renderWorld( const RectI& updateRect )
(
gClientSceneGraph,
SPT_Diffuse,
SceneCameraState( GFX->getViewport(), frust, GFX->getWorldMatrix(), GFX->getProjectionMatrix() ),
SceneCameraState( GFX->getViewport(), frust, MatrixF::Identity, GFX->getProjectionMatrix() ),
renderPass,
false
true
);
// Set up our TS render state here.

View file

@ -1241,9 +1241,10 @@ DefineEngineMethod( Item, getLastStickyPos, const char*, (),,
"@note Server side only.\n"
)
{
char* ret = Con::getReturnBuffer(256);
static const U32 bufSize = 256;
char* ret = Con::getReturnBuffer(bufSize);
if (object->isServerObject())
dSprintf(ret, 255, "%g %g %g",
dSprintf(ret, bufSize, "%g %g %g",
object->mStickyCollisionPos.x,
object->mStickyCollisionPos.y,
object->mStickyCollisionPos.z);
@ -1263,9 +1264,10 @@ DefineEngineMethod( Item, getLastStickyNormal, const char *, (),,
"@note Server side only.\n"
)
{
char* ret = Con::getReturnBuffer(256);
static const U32 bufSize = 256;
char* ret = Con::getReturnBuffer(bufSize);
if (object->isServerObject())
dSprintf(ret, 255, "%g %g %g",
dSprintf(ret, bufSize, "%g %g %g",
object->mStickyCollisionNormal.x,
object->mStickyCollisionNormal.y,
object->mStickyCollisionNormal.z);

View file

@ -439,7 +439,7 @@ ConsoleMethod( LightBase, playAnimation, void, 2, 3, "( [LightAnimData anim] )\t
LightAnimData *animData;
if ( !Sim::findObject( argv[2], animData ) )
{
Con::errorf( "LightBase::playAnimation() - Invalid LightAnimData '%s'.", argv[2] );
Con::errorf( "LightBase::playAnimation() - Invalid LightAnimData '%s'.", (const char*)argv[2] );
return;
}
@ -481,4 +481,4 @@ void LightBase::pauseAnimation( void )
mAnimState.active = false;
setMaskBits( UpdateMask );
}
}
}

View file

@ -176,10 +176,11 @@ DefineEngineFunction(getMissionAreaServerObject, MissionArea*, (),,
DefineEngineMethod( MissionArea, getArea, const char *, (),,
"Returns 4 fields: starting x, starting y, extents x, extents y.\n")
{
char* returnBuffer = Con::getReturnBuffer(48);
static const U32 bufSize = 48;
char* returnBuffer = Con::getReturnBuffer(bufSize);
RectI area = object->getArea();
dSprintf(returnBuffer, sizeof(returnBuffer), "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y);
dSprintf(returnBuffer, bufSize, "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y);
return(returnBuffer);
}

View file

@ -223,12 +223,6 @@ ConsoleDocClass( WayPoint,
"@ingroup enviroMisc\n"
);
WayPointTeam::WayPointTeam()
{
mTeamId = 0;
mWayPoint = 0;
}
WayPoint::WayPoint()
{
mName = StringTable->insert("");
@ -252,7 +246,6 @@ bool WayPoint::onAdd()
Sim::getWayPointSet()->addObject(this);
else
{
mTeam.mWayPoint = this;
setMaskBits(UpdateNameMask|UpdateTeamMask);
}
@ -272,8 +265,6 @@ U32 WayPoint::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
U32 retMask = Parent::packUpdate(con, mask, stream);
if(stream->writeFlag(mask & UpdateNameMask))
stream->writeString(mName);
if(stream->writeFlag(mask & UpdateTeamMask))
stream->write(mTeam.mTeamId);
if(stream->writeFlag(mask & UpdateHiddenMask))
stream->writeFlag(isHidden());
return(retMask);
@ -284,47 +275,17 @@ void WayPoint::unpackUpdate(NetConnection * con, BitStream * stream)
Parent::unpackUpdate(con, stream);
if(stream->readFlag())
mName = stream->readSTString(true);
if(stream->readFlag())
stream->read(&mTeam.mTeamId);
if(stream->readFlag())
setHidden(stream->readFlag());
}
//-----------------------------------------------------------------------------
// TypeWayPointTeam
//-----------------------------------------------------------------------------
IMPLEMENT_STRUCT( WayPointTeam, WayPointTeam,,
"" )
END_IMPLEMENT_STRUCT;
//FIXME: this should work but does not; need to check the stripping down to base types within TYPE
//ConsoleType( WayPointTeam, TypeWayPointTeam, WayPointTeam* )
ConsoleType( WayPointTeam, TypeWayPointTeam, WayPointTeam )
ConsoleGetType( TypeWayPointTeam )
{
char * buf = Con::getReturnBuffer(32);
dSprintf(buf, 32, "%d", ((WayPointTeam*)dptr)->mTeamId);
return(buf);
}
ConsoleSetType( TypeWayPointTeam )
{
WayPointTeam * pTeam = (WayPointTeam*)dptr;
pTeam->mTeamId = dAtoi(argv[0]);
if(pTeam->mWayPoint && pTeam->mWayPoint->isServerObject())
pTeam->mWayPoint->setMaskBits(WayPoint::UpdateTeamMask);
}
void WayPoint::initPersistFields()
{
addGroup("Misc");
addField("markerName", TypeCaseString, Offset(mName, WayPoint), "Unique name representing this waypoint");
addField("team", TypeWayPointTeam, Offset(mTeam, WayPoint), "Unique numerical ID assigned to this waypoint, or set of waypoints");
endGroup("Misc");
Parent::initPersistFields();
}
@ -554,7 +515,7 @@ ConsoleMethod(SpawnSphere, spawnObject, S32, 2, 3,
String additionalProps;
if (argc == 3)
additionalProps = String(argv[2]);
additionalProps = (const char*)argv[2];
SimObject* obj = object->spawnObject(additionalProps);

View file

@ -92,17 +92,6 @@ class MissionMarker : public ShapeBase
// Class: WayPoint
//------------------------------------------------------------------------------
class WayPoint;
class WayPointTeam
{
public:
WayPointTeam();
S32 mTeamId;
WayPoint * mWayPoint;
};
DECLARE_STRUCT( WayPointTeam );
DefineConsoleType( TypeWayPointTeam, WayPointTeam * );
class WayPoint : public MissionMarker
{
@ -132,7 +121,6 @@ class WayPoint : public MissionMarker
// field data
StringTableEntry mName;
WayPointTeam mTeam;
static void initPersistFields();

View file

@ -311,7 +311,8 @@ PhysicsDebris* PhysicsDebris::create( PhysicsDebrisData *datablock,
}
PhysicsDebris::PhysicsDebris()
: mLifetime( 0.0f ),
: mDataBlock( NULL ),
mLifetime( 0.0f ),
mShapeInstance( NULL ),
mWorld( NULL ),
mInitialLinVel( Point3F::Zero )
@ -342,6 +343,12 @@ bool PhysicsDebris::onAdd()
if ( !Parent::onAdd() )
return false;
if( !mDataBlock )
{
Con::errorf("PhysicsDebris::onAdd - Fail - No datablock");
return false;
}
// If it has a fixed lifetime then calculate it.
if ( mDataBlock->lifetime > 0.0f )
{

View file

@ -147,13 +147,13 @@ ConsoleFunction( physicsDestroy, void, 1, 1, "physicsDestroy()" )
ConsoleFunction( physicsInitWorld, bool, 2, 2, "physicsInitWorld( String worldName )" )
{
return PHYSICSMGR && PHYSICSMGR->createWorld( String( argv[1] ) );
return PHYSICSMGR && PHYSICSMGR->createWorld( (const char*)argv[1] );
}
ConsoleFunction( physicsDestroyWorld, void, 2, 2, "physicsDestroyWorld( String worldName )" )
{
if ( PHYSICSMGR )
PHYSICSMGR->destroyWorld( String( argv[1] ) );
PHYSICSMGR->destroyWorld( (const char*)argv[1] );
}
@ -162,13 +162,13 @@ ConsoleFunction( physicsDestroyWorld, void, 2, 2, "physicsDestroyWorld( String w
ConsoleFunction( physicsStartSimulation, void, 2, 2, "physicsStartSimulation( String worldName )" )
{
if ( PHYSICSMGR )
PHYSICSMGR->enableSimulation( String( argv[1] ), true );
PHYSICSMGR->enableSimulation( (const char*)argv[1], true );
}
ConsoleFunction( physicsStopSimulation, void, 2, 2, "physicsStopSimulation( String worldName )" )
{
if ( PHYSICSMGR )
PHYSICSMGR->enableSimulation( String( argv[1] ), false );
PHYSICSMGR->enableSimulation( (const char*)argv[1], false );
}
ConsoleFunction( physicsSimulationEnabled, bool, 1, 1, "physicsSimulationEnabled()" )
@ -182,7 +182,7 @@ ConsoleFunction( physicsSimulationEnabled, bool, 1, 1, "physicsSimulationEnabled
ConsoleFunction( physicsSetTimeScale, void, 2, 2, "physicsSetTimeScale( F32 scale )" )
{
if ( PHYSICSMGR )
PHYSICSMGR->setTimeScale( dAtof( argv[1] ) );
PHYSICSMGR->setTimeScale( argv[1] );
}
// Get the currently set time scale.
@ -212,5 +212,5 @@ ConsoleFunction( physicsRestoreState, void, 1, 1, "physicsRestoreState()" )
ConsoleFunction( physicsDebugDraw, void, 2, 2, "physicsDebugDraw( bool enable )" )
{
if ( PHYSICSMGR )
PHYSICSMGR->enableDebugDraw( dAtoi( argv[1] ) );
}
PHYSICSMGR->enableDebugDraw( (S32)argv[1] );
}

View file

@ -448,9 +448,8 @@ void PxWorld::releaseActor( NxActor &actor )
// Clear the userdata.
actor.userData = NULL;
// If the scene is not simulating then we have the
// write lock and can safely delete it now.
if ( !mIsSimulating )
// actors are one of the few objects that are stable removing this way in physx 2.8
if (mScene->isWritable() )
{
mScene->releaseActor( actor );
}

View file

@ -0,0 +1,53 @@
//-----------------------------------------------------------------------------
// 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 _PHYSX3_H_
#define _PHYSX3_H_
//-------------------------------------------------------------------------
//defines to keep PhysX happy and compiling
#if defined(TORQUE_OS_MAC) && !defined(__APPLE__)
#define __APPLE__
#elif defined(TORQUE_OS_LINUX) && !defined(LINUX)
#define LINUX
#elif defined(TORQUE_OS_WIN) && !defined(WIN32)
#define WIN32
#endif
//-------------------------------------------------------------------------
#include <PxPhysicsAPI.h>
#include <PxExtensionsAPI.h>
#include <PxDefaultErrorCallback.h>
#include <PxDefaultAllocator.h>
#include <PxDefaultSimulationFilterShader.h>
#include <PxDefaultCpuDispatcher.h>
#include <PxShapeExt.h>
#include <PxSimpleFactory.h>
#include <PxFoundation.h>
#include <PxController.h>
#include <PxIO.h>
extern physx::PxPhysics* gPhysics3SDK;
#endif // _PHYSX3_

View 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 "platform/platform.h"
#include "T3D/physics/physx3/px3Body.h"
#include "T3D/physics/physx3/px3.h"
#include "T3D/physics/physx3/px3Casts.h"
#include "T3D/physics/physx3/px3World.h"
#include "T3D/physics/physx3/px3Collision.h"
#include "console/console.h"
#include "console/consoleTypes.h"
Px3Body::Px3Body() :
mActor( NULL ),
mMaterial( NULL ),
mWorld( NULL ),
mBodyFlags( 0 ),
mIsEnabled( true ),
mIsStatic(false)
{
}
Px3Body::~Px3Body()
{
_releaseActor();
}
void Px3Body::_releaseActor()
{
if ( !mActor )
return;
mWorld->releaseWriteLock();
mActor->userData = NULL;
mActor->release();
mActor = NULL;
mBodyFlags = 0;
if ( mMaterial )
{
mMaterial->release();
}
mColShape = NULL;
}
bool Px3Body::init( PhysicsCollision *shape,
F32 mass,
U32 bodyFlags,
SceneObject *obj,
PhysicsWorld *world )
{
AssertFatal( obj, "Px3Body::init - Got a null scene object!" );
AssertFatal( world, "Px3Body::init - Got a null world!" );
AssertFatal( dynamic_cast<Px3World*>( world ), "Px3Body::init - The world is the wrong type!" );
AssertFatal( shape, "Px3Body::init - Got a null collision shape!" );
AssertFatal( dynamic_cast<Px3Collision*>( shape ), "Px3Body::init - The collision shape is the wrong type!" );
AssertFatal( !((Px3Collision*)shape)->getShapes().empty(), "Px3Body::init - Got empty collision shape!" );
// Cleanup any previous actor.
_releaseActor();
mWorld = (Px3World*)world;
mColShape = (Px3Collision*)shape;
mBodyFlags = bodyFlags;
const bool isKinematic = mBodyFlags & BF_KINEMATIC;
const bool isTrigger = mBodyFlags & BF_TRIGGER;
const bool isDebris = mBodyFlags & BF_DEBRIS;
if ( isKinematic )
{
mActor = gPhysics3SDK->createRigidDynamic(physx::PxTransform(physx::PxIDENTITY()));
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
actor->setRigidDynamicFlag(physx::PxRigidDynamicFlag::eKINEMATIC, true);
actor->setMass(getMax( mass, 1.0f ));
}
else if ( mass > 0.0f )
{
mActor = gPhysics3SDK->createRigidDynamic(physx::PxTransform(physx::PxIDENTITY()));
}
else
{
mActor = gPhysics3SDK->createRigidStatic(physx::PxTransform(physx::PxIDENTITY()));
mIsStatic = true;
}
mMaterial = gPhysics3SDK->createMaterial(0.6f,0.4f,0.1f);
// Add all the shapes.
const Vector<Px3CollisionDesc*> &shapes = mColShape->getShapes();
for ( U32 i=0; i < shapes.size(); i++ )
{
Px3CollisionDesc* desc = shapes[i];
if( mass > 0.0f )
{
if(desc->pGeometry->getType() == physx::PxGeometryType::eTRIANGLEMESH)
{
Con::errorf("PhysX3 Dynamic Triangle Mesh is not supported.");
}
}
physx::PxShape * pShape = mActor->createShape(*desc->pGeometry,*mMaterial);
physx::PxFilterData colData;
if(isDebris)
colData.word0 = PX3_DEBRIS;
else if(isTrigger)
colData.word0 = PX3_TRIGGER;
else
colData.word0 = PX3_DEFAULT;
//set local pose - actor->createShape with a local pose is deprecated in physx 3.3
pShape->setLocalPose(desc->pose);
//set the skin width
pShape->setContactOffset(0.01f);
pShape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, !isTrigger);
pShape->setFlag(physx::PxShapeFlag::eSCENE_QUERY_SHAPE,true);
pShape->setSimulationFilterData(colData);
pShape->setQueryFilterData(colData);
}
//mass & intertia has to be set after creating the shape
if ( mass > 0.0f )
{
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
physx::PxRigidBodyExt::setMassAndUpdateInertia(*actor,mass);
}
// This sucks, but it has to happen if we want
// to avoid write lock errors from PhysX right now.
mWorld->releaseWriteLock();
mWorld->getScene()->addActor(*mActor);
mIsEnabled = true;
if ( isDebris )
mActor->setDominanceGroup( 31 );
mUserData.setObject( obj );
mUserData.setBody( this );
mActor->userData = &mUserData;
return true;
}
void Px3Body::setMaterial( F32 restitution,
F32 friction,
F32 staticFriction )
{
AssertFatal( mActor, "Px3Body::setMaterial - The actor is null!" );
if ( isDynamic() )
{
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
actor->wakeUp();
}
mMaterial->setRestitution(restitution);
mMaterial->setStaticFriction(staticFriction);
mMaterial->setDynamicFriction(friction);
}
void Px3Body::setSleepThreshold( F32 linear, F32 angular )
{
AssertFatal( mActor, "Px3Body::setSleepThreshold - The actor is null!" );
if(mIsStatic)
return;
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
physx::PxF32 massNormalized= (linear*linear+angular*angular)/2.0f;
actor->setSleepThreshold(massNormalized);
}
void Px3Body::setDamping( F32 linear, F32 angular )
{
AssertFatal( mActor, "Px3Body::setDamping - The actor is null!" );
if(mIsStatic)
return;
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
actor->setLinearDamping( linear );
actor->setAngularDamping( angular );
}
void Px3Body::getState( PhysicsState *outState )
{
AssertFatal( mActor, "Px3Body::getState - The actor is null!" );
AssertFatal( isDynamic(), "Px3Body::getState - This call is only for dynamics!" );
outState->position = px3Cast<Point3F>( mActor->getGlobalPose().p );
outState->orientation = px3Cast<QuatF>( mActor->getGlobalPose().q );
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
outState->linVelocity = px3Cast<Point3F>( actor->getLinearVelocity() );
outState->angVelocity = px3Cast<Point3F>( actor->getAngularVelocity() );
outState->sleeping = actor->isSleeping();
outState->momentum = px3Cast<Point3F>( (1.0f/actor->getMass()) * actor->getLinearVelocity() );
}
F32 Px3Body::getMass() const
{
AssertFatal( mActor, "PxBody::getCMassPosition - The actor is null!" );
if(mIsStatic)
return 0;
const physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
return actor->getMass();
}
Point3F Px3Body::getCMassPosition() const
{
AssertFatal( mActor, "Px3Body::getCMassPosition - The actor is null!" );
if(mIsStatic)
return px3Cast<Point3F>(mActor->getGlobalPose().p);
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
physx::PxTransform pose = actor->getGlobalPose() * actor->getCMassLocalPose();
return px3Cast<Point3F>(pose.p);
}
void Px3Body::setLinVelocity( const Point3F &vel )
{
AssertFatal( mActor, "Px3Body::setLinVelocity - The actor is null!" );
AssertFatal( isDynamic(), "Px3Body::setLinVelocity - This call is only for dynamics!" );
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
actor->setLinearVelocity( px3Cast<physx::PxVec3>( vel ) );
}
void Px3Body::setAngVelocity( const Point3F &vel )
{
AssertFatal( mActor, "Px3Body::setAngVelocity - The actor is null!" );
AssertFatal( isDynamic(), "Px3Body::setAngVelocity - This call is only for dynamics!" );
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
actor->setAngularVelocity(px3Cast<physx::PxVec3>( vel ) );
}
Point3F Px3Body::getLinVelocity() const
{
AssertFatal( mActor, "Px3Body::getLinVelocity - The actor is null!" );
AssertFatal( isDynamic(), "Px3Body::getLinVelocity - This call is only for dynamics!" );
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
return px3Cast<Point3F>( actor->getLinearVelocity() );
}
Point3F Px3Body::getAngVelocity() const
{
AssertFatal( mActor, "Px3Body::getAngVelocity - The actor is null!" );
AssertFatal( isDynamic(), "Px3Body::getAngVelocity - This call is only for dynamics!" );
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
return px3Cast<Point3F>( actor->getAngularVelocity() );
}
void Px3Body::setSleeping( bool sleeping )
{
AssertFatal( mActor, "Px3Body::setSleeping - The actor is null!" );
AssertFatal( isDynamic(), "Px3Body::setSleeping - This call is only for dynamics!" );
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
if ( sleeping )
actor->putToSleep();
else
actor->wakeUp();
}
bool Px3Body::isDynamic() const
{
AssertFatal( mActor, "PxBody::isDynamic - The actor is null!" );
return !mIsStatic && ( mBodyFlags & BF_KINEMATIC ) == 0;
}
PhysicsWorld* Px3Body::getWorld()
{
return mWorld;
}
PhysicsCollision* Px3Body::getColShape()
{
return mColShape;
}
MatrixF& Px3Body::getTransform( MatrixF *outMatrix )
{
AssertFatal( mActor, "Px3Body::getTransform - The actor is null!" );
*outMatrix = px3Cast<MatrixF>(mActor->getGlobalPose());
return *outMatrix;
}
Box3F Px3Body::getWorldBounds()
{
AssertFatal( mActor, "Px3Body::getTransform - The actor is null!" );
physx::PxBounds3 bounds;
bounds.setEmpty();
physx::PxBounds3 shapeBounds;
U32 shapeCount = mActor->getNbShapes();
physx::PxShape **shapes = new physx::PxShape*[shapeCount];
mActor->getShapes(shapes, shapeCount);
for ( U32 i = 0; i < shapeCount; i++ )
{
// Get the shape's bounds.
shapeBounds = physx::PxShapeExt::getWorldBounds(*shapes[i],*mActor);
// Combine them into the total bounds.
bounds.include( shapeBounds );
}
delete [] shapes;
return px3Cast<Box3F>( bounds );
}
void Px3Body::setSimulationEnabled( bool enabled )
{
if ( mIsEnabled == enabled )
return;
//Don't need to enable/disable eSIMULATION_SHAPE for trigger,it's disabled permanently
if(mBodyFlags & BF_TRIGGER)
return;
// This sucks, but it has to happen if we want
// to avoid write lock errors from PhysX right now.
mWorld->releaseWriteLock();
U32 shapeCount = mActor->getNbShapes();
physx::PxShape **shapes = new physx::PxShape*[shapeCount];
mActor->getShapes(shapes, shapeCount);
for ( S32 i = 0; i < mActor->getNbShapes(); i++ )
{
shapes[i]->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,!mIsEnabled);//?????
}
delete [] shapes;
}
void Px3Body::setTransform( const MatrixF &transform )
{
AssertFatal( mActor, "Px3Body::setTransform - The actor is null!" );
// This sucks, but it has to happen if we want
// to avoid write lock errors from PhysX right now.
mWorld->releaseWriteLock();
mActor->setGlobalPose(px3Cast<physx::PxTransform>(transform),false);
if(mIsStatic)
return;
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
bool kinematic = actor->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC;
// If its dynamic we have more to do.
if ( isDynamic() && !kinematic )
{
actor->setLinearVelocity( physx::PxVec3(0) );
actor->setAngularVelocity( physx::PxVec3(0) );
actor->wakeUp();
}
}
void Px3Body::applyCorrection( const MatrixF &transform )
{
AssertFatal( mActor, "Px3Body::applyCorrection - The actor is null!" );
AssertFatal( isDynamic(), "Px3Body::applyCorrection - This call is only for dynamics!" );
// This sucks, but it has to happen if we want
// to avoid write lock errors from PhysX right now.
mWorld->releaseWriteLock();
mActor->setGlobalPose( px3Cast<physx::PxTransform>(transform) );
}
void Px3Body::applyImpulse( const Point3F &origin, const Point3F &force )
{
AssertFatal( mActor, "Px3Body::applyImpulse - The actor is null!" );
// This sucks, but it has to happen if we want
// to avoid write lock errors from PhysX right now.
mWorld->releaseWriteLock();
physx::PxRigidDynamic *actor = mActor->is<physx::PxRigidDynamic>();
if ( mIsEnabled && isDynamic() )
physx::PxRigidBodyExt::addForceAtPos(*actor,px3Cast<physx::PxVec3>(force),
px3Cast<physx::PxVec3>(origin),
physx::PxForceMode::eIMPULSE);
}

View 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 _PX3BODY_H_
#define _PX3BODY_H_
#ifndef _T3D_PHYSICS_PHYSICSBODY_H_
#include "T3D/physics/physicsBody.h"
#endif
#ifndef _PHYSICS_PHYSICSUSERDATA_H_
#include "T3D/physics/physicsUserData.h"
#endif
#ifndef _REFBASE_H_
#include "core/util/refBase.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
class Px3World;
class Px3Collision;
struct Px3CollisionDesc;
namespace physx{
class PxRigidActor;
class PxMaterial;
class PxShape;
}
class Px3Body : public PhysicsBody
{
protected:
/// The physics world we are in.
Px3World *mWorld;
/// The physics actor.
physx::PxRigidActor *mActor;
/// The unshared local material used on all the
/// shapes on this actor.
physx::PxMaterial *mMaterial;
/// We hold the collision reference as it contains
/// allocated objects that we own and must free.
StrongRefPtr<Px3Collision> mColShape;
///
MatrixF mInternalTransform;
/// The body flags set at creation time.
U32 mBodyFlags;
/// Is true if this body is enabled and active
/// in the simulation of the scene.
bool mIsEnabled;
bool mIsStatic;
///
void _releaseActor();
public:
Px3Body();
virtual ~Px3Body();
// PhysicsObject
virtual PhysicsWorld* getWorld();
virtual void setTransform( const MatrixF &xfm );
virtual MatrixF& getTransform( MatrixF *outMatrix );
virtual Box3F getWorldBounds();
virtual void setSimulationEnabled( bool enabled );
virtual bool isSimulationEnabled() { return mIsEnabled; }
// PhysicsBody
virtual bool init( PhysicsCollision *shape,
F32 mass,
U32 bodyFlags,
SceneObject *obj,
PhysicsWorld *world );
virtual bool isDynamic() const;
virtual PhysicsCollision* getColShape();
virtual void setSleepThreshold( F32 linear, F32 angular );
virtual void setDamping( F32 linear, F32 angular );
virtual void getState( PhysicsState *outState );
virtual F32 getMass() const;
virtual Point3F getCMassPosition() const;
virtual void setLinVelocity( const Point3F &vel );
virtual void setAngVelocity( const Point3F &vel );
virtual Point3F getLinVelocity() const;
virtual Point3F getAngVelocity() const;
virtual void setSleeping( bool sleeping );
virtual void setMaterial( F32 restitution,
F32 friction,
F32 staticFriction );
virtual void applyCorrection( const MatrixF &xfm );
virtual void applyImpulse( const Point3F &origin, const Point3F &force );
};
#endif // _PX3BODY_H_

View file

@ -0,0 +1,137 @@
//-----------------------------------------------------------------------------
// 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 _PX3CASTS_H_
#define _PX3CASTS_H_
#ifndef _MPOINT3_H_
#include "math/mPoint3.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#ifndef _MBOX_H_
#include "math/mBox.h"
#endif
#ifndef _MQUAT_H_
#include "math/mQuat.h"
#endif
#ifndef _MTRANSFORM_H_
#include "math/mTransform.h"
#endif
template <class T, class F> inline T px3Cast( const F &from );
//-------------------------------------------------------------------------
template<>
inline Point3F px3Cast( const physx::PxVec3 &vec )
{
return Point3F( vec.x, vec.y, vec.z );
}
template<>
inline physx::PxVec3 px3Cast( const Point3F &point )
{
return physx::PxVec3( point.x, point.y, point.z );
}
//-------------------------------------------------------------------------
template<>
inline QuatF px3Cast( const physx::PxQuat &quat )
{
/// The Torque quat has the opposite winding order.
return QuatF( -quat.x, -quat.y, -quat.z, quat.w );
}
template<>
inline physx::PxQuat px3Cast( const QuatF &quat )
{
/// The Torque quat has the opposite winding order.
physx::PxQuat result( -quat.x, -quat.y, -quat.z, quat.w );
return result;
}
//-------------------------------------------------------------------------
template<>
inline physx::PxExtendedVec3 px3Cast( const Point3F &point )
{
return physx::PxExtendedVec3( point.x, point.y, point.z );
}
template<>
inline Point3F px3Cast( const physx::PxExtendedVec3 &xvec )
{
return Point3F( xvec.x, xvec.y, xvec.z );
}
//-------------------------------------------------------------------------
template<>
inline physx::PxBounds3 px3Cast( const Box3F &box )
{
physx::PxBounds3 bounds(px3Cast<physx::PxVec3>(box.minExtents),
px3Cast<physx::PxVec3>(box.maxExtents));
return bounds;
}
template<>
inline Box3F px3Cast( const physx::PxBounds3 &bounds )
{
return Box3F( bounds.minimum.x,
bounds.minimum.y,
bounds.minimum.z,
bounds.maximum.x,
bounds.maximum.y,
bounds.maximum.z );
}
//-------------------------------------------------------------------------
template<>
inline physx::PxTransform px3Cast( const MatrixF &xfm )
{
physx::PxTransform out;
QuatF q;
q.set(xfm);
out.q = px3Cast<physx::PxQuat>(q);
out.p = px3Cast<physx::PxVec3>(xfm.getPosition());
return out;
}
template<>
inline TransformF px3Cast(const physx::PxTransform &xfm)
{
TransformF out(px3Cast<Point3F>(xfm.p),AngAxisF(px3Cast<QuatF>(xfm.q)));
return out;
}
template<>
inline MatrixF px3Cast( const physx::PxTransform &xfm )
{
MatrixF out;
TransformF t = px3Cast<TransformF>(xfm);
out = t.getMatrix();
return out;
}
#endif //_PX3CASTS_H_

View file

@ -0,0 +1,217 @@
//-----------------------------------------------------------------------------
// 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/physics/physx3/px3Collision.h"
#include "math/mPoint3.h"
#include "math/mMatrix.h"
#include "T3D/physics/physx3/px3.h"
#include "T3D/physics/physx3/px3Casts.h"
#include "T3D/physics/physx3/px3World.h"
#include "T3D/physics/physx3/px3Stream.h"
Px3Collision::Px3Collision()
{
}
Px3Collision::~Px3Collision()
{
for ( U32 i=0; i < mColShapes.size(); i++ )
{
Px3CollisionDesc *desc = mColShapes[i];
delete desc->pGeometry;
// Delete the descriptor.
delete desc;
}
mColShapes.clear();
}
void Px3Collision::addPlane( const PlaneF &plane )
{
physx::PxVec3 pos = px3Cast<physx::PxVec3>(plane.getPosition());
Px3CollisionDesc *desc = new Px3CollisionDesc;
desc->pGeometry = new physx::PxPlaneGeometry();
desc->pose = physx::PxTransform(pos, physx::PxQuat(physx::PxHalfPi, physx::PxVec3(0.0f, -1.0f, 0.0f)));
mColShapes.push_back(desc);
}
void Px3Collision::addBox( const Point3F &halfWidth,const MatrixF &localXfm )
{
Px3CollisionDesc *desc = new Px3CollisionDesc;
desc->pGeometry = new physx::PxBoxGeometry(px3Cast<physx::PxVec3>(halfWidth));
desc->pose = px3Cast<physx::PxTransform>(localXfm);
mColShapes.push_back(desc);
}
void Px3Collision::addSphere( F32 radius,
const MatrixF &localXfm )
{
Px3CollisionDesc *desc = new Px3CollisionDesc;
desc->pGeometry = new physx::PxSphereGeometry(radius);
desc->pose = px3Cast<physx::PxTransform>(localXfm);
mColShapes.push_back(desc);
}
void Px3Collision::addCapsule( F32 radius,
F32 height,
const MatrixF &localXfm )
{
Px3CollisionDesc *desc = new Px3CollisionDesc;
desc->pGeometry = new physx::PxCapsuleGeometry(radius,height*0.5);//uses half height
desc->pose = px3Cast<physx::PxTransform>(localXfm);
mColShapes.push_back(desc);
}
bool Px3Collision::addConvex( const Point3F *points,
U32 count,
const MatrixF &localXfm )
{
physx::PxCooking *cooking = Px3World::getCooking();
physx::PxConvexMeshDesc convexDesc;
convexDesc.points.data = points;
convexDesc.points.stride = sizeof(Point3F);
convexDesc.points.count = count;
convexDesc.flags = physx::PxConvexFlag::eFLIPNORMALS|physx::PxConvexFlag::eCOMPUTE_CONVEX | physx::PxConvexFlag::eINFLATE_CONVEX;
Px3MemOutStream stream;
if(!cooking->cookConvexMesh(convexDesc,stream))
return false;
physx::PxConvexMesh* convexMesh;
Px3MemInStream in(stream.getData(), stream.getSize());
convexMesh = gPhysics3SDK->createConvexMesh(in);
Px3CollisionDesc *desc = new Px3CollisionDesc;
physx::PxVec3 scale = px3Cast<physx::PxVec3>(localXfm.getScale());
physx::PxQuat rotation = px3Cast<physx::PxQuat>(QuatF(localXfm));
physx::PxMeshScale meshScale(scale,rotation);
desc->pGeometry = new physx::PxConvexMeshGeometry(convexMesh,meshScale);
desc->pose = px3Cast<physx::PxTransform>(localXfm);
mColShapes.push_back(desc);
return true;
}
bool Px3Collision::addTriangleMesh( const Point3F *vert,
U32 vertCount,
const U32 *index,
U32 triCount,
const MatrixF &localXfm )
{
physx::PxCooking *cooking = Px3World::getCooking();
physx::PxTriangleMeshDesc meshDesc;
meshDesc.points.count = vertCount;
meshDesc.points.data = vert;
meshDesc.points.stride = sizeof(Point3F);
meshDesc.triangles.count = triCount;
meshDesc.triangles.data = index;
meshDesc.triangles.stride = 3*sizeof(U32);
meshDesc.flags = physx::PxMeshFlag::eFLIPNORMALS;
Px3MemOutStream stream;
if(!cooking->cookTriangleMesh(meshDesc,stream))
return false;
physx::PxTriangleMesh *mesh;
Px3MemInStream in(stream.getData(), stream.getSize());
mesh = gPhysics3SDK->createTriangleMesh(in);
Px3CollisionDesc *desc = new Px3CollisionDesc;
desc->pGeometry = new physx::PxTriangleMeshGeometry(mesh);
desc->pose = px3Cast<physx::PxTransform>(localXfm);
mColShapes.push_back(desc);
return true;
}
bool Px3Collision::addHeightfield( const U16 *heights,
const bool *holes,
U32 blockSize,
F32 metersPerSample,
const MatrixF &localXfm )
{
const F32 heightScale = 0.03125f;
physx::PxHeightFieldSample* samples = (physx::PxHeightFieldSample*) new physx::PxHeightFieldSample[blockSize*blockSize];
memset(samples,0,blockSize*blockSize*sizeof(physx::PxHeightFieldSample));
physx::PxHeightFieldDesc heightFieldDesc;
heightFieldDesc.nbColumns = blockSize;
heightFieldDesc.nbRows = blockSize;
heightFieldDesc.thickness = -10.f;
heightFieldDesc.convexEdgeThreshold = 0;
heightFieldDesc.format = physx::PxHeightFieldFormat::eS16_TM;
heightFieldDesc.samples.data = samples;
heightFieldDesc.samples.stride = sizeof(physx::PxHeightFieldSample);
physx::PxU8 *currentByte = (physx::PxU8*)heightFieldDesc.samples.data;
for ( U32 row = 0; row < blockSize; row++ )
{
const U32 tess = ( row + 1 ) % 2;
for ( U32 column = 0; column < blockSize; column++ )
{
physx::PxHeightFieldSample *currentSample = (physx::PxHeightFieldSample*)currentByte;
U32 index = ( blockSize - row - 1 ) + ( column * blockSize );
currentSample->height = (physx::PxI16)heights[ index ];
if ( holes && holes[ getMax( (S32)index - 1, 0 ) ] ) // row index for holes adjusted so PhysX collision shape better matches rendered terrain
{
currentSample->materialIndex0 = physx::PxHeightFieldMaterial::eHOLE;
currentSample->materialIndex1 = physx::PxHeightFieldMaterial::eHOLE;
}
else
{
currentSample->materialIndex0 = 0;
currentSample->materialIndex1 = 0;
}
int flag = ( column + tess ) % 2;
if(flag)
currentSample->clearTessFlag();
else
currentSample->setTessFlag();
currentByte += heightFieldDesc.samples.stride;
}
}
physx::PxHeightField * hf = gPhysics3SDK->createHeightField(heightFieldDesc);
physx::PxHeightFieldGeometry *geom = new physx::PxHeightFieldGeometry(hf,physx::PxMeshGeometryFlags(),heightScale,metersPerSample,metersPerSample);
physx::PxTransform pose= physx::PxTransform(physx::PxQuat(Float_HalfPi, physx::PxVec3(1, 0, 0 )));
physx::PxTransform pose1= physx::PxTransform(physx::PxQuat(Float_Pi, physx::PxVec3(0, 0, 1 )));
physx::PxTransform pose2 = pose1 * pose;
pose2.p = physx::PxVec3(( blockSize - 1 ) * metersPerSample, 0, 0 );
Px3CollisionDesc *desc = new Px3CollisionDesc;
desc->pGeometry = geom;
desc->pose = pose2;
mColShapes.push_back(desc);
SAFE_DELETE(samples);
return true;
}

View file

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// 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 _PX3COLLISION_H_
#define _PX3COLLISION_H_
#ifndef _T3D_PHYSICS_PHYSICSCOLLISION_H_
#include "T3D/physics/physicsCollision.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#include <foundation/PxTransform.h>
//forward declare
namespace physx{class PxGeometry;}
struct Px3CollisionDesc
{
physx::PxGeometry *pGeometry;
physx::PxTransform pose;
};
class Px3Collision : public PhysicsCollision
{
typedef Vector<Px3CollisionDesc*> Px3CollisionList;
protected:
/// The collision representation.
Px3CollisionList mColShapes;
public:
Px3Collision();
virtual ~Px3Collision();
/// Return the PhysX shape descriptions.
const Px3CollisionList& getShapes() const { return mColShapes; }
// PhysicsCollision
virtual void addPlane( const PlaneF &plane );
virtual void addBox( const Point3F &halfWidth,
const MatrixF &localXfm );
virtual void addSphere( F32 radius,
const MatrixF &localXfm );
virtual void addCapsule( F32 radius,
F32 height,
const MatrixF &localXfm );
virtual bool addConvex( const Point3F *points,
U32 count,
const MatrixF &localXfm );
virtual bool addTriangleMesh( const Point3F *vert,
U32 vertCount,
const U32 *index,
U32 triCount,
const MatrixF &localXfm );
virtual bool addHeightfield( const U16 *heights,
const bool *holes,
U32 blockSize,
F32 metersPerSample,
const MatrixF &localXfm );
};
#endif // _PX3COLLISION_H_

View file

@ -0,0 +1,331 @@
//-----------------------------------------------------------------------------
// 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/physics/physx3/px3Player.h"
#include "T3D/physics/physicsPlugin.h"
#include "T3D/physics/physx3/px3World.h"
#include "T3D/physics/physx3/px3Casts.h"
#include "T3D/physics/physx3/px3Utils.h"
#include "collision/collision.h"
Px3Player::Px3Player()
: PhysicsPlayer(),
mController( NULL ),
mWorld( NULL ),
mObject( NULL ),
mSkinWidth( 0.05f ),
mOriginOffset( 0.0f ),
mElapsed(0)
{
PHYSICSMGR->getPhysicsResetSignal().notify( this, &Px3Player::_onPhysicsReset );
}
Px3Player::~Px3Player()
{
_releaseController();
PHYSICSMGR->getPhysicsResetSignal().remove( this, &Px3Player::_onPhysicsReset );
}
void Px3Player::_releaseController()
{
if ( mController )
{
mController->getActor()->userData = NULL;
mWorld->getStaticChangedSignal().remove( this, &Px3Player::_onStaticChanged );
mController->release();
}
}
void Px3Player::init( const char *type,
const Point3F &size,
F32 runSurfaceCos,
F32 stepHeight,
SceneObject *obj,
PhysicsWorld *world )
{
AssertFatal( obj, "Px3Player::init - Got a null scene object!" );
AssertFatal( world, "Px3Player::init - Got a null world!" );
AssertFatal( dynamic_cast<Px3World*>( world ), "Px3Player::init - The world is the wrong type!" );
// Cleanup any previous controller.
_releaseController();
mObject = obj;
mWorld = (Px3World*)world;
mOriginOffset = size.z * 0.5f;
physx::PxCapsuleControllerDesc desc;
desc.contactOffset = mSkinWidth;
desc.radius = getMax( size.x, size.y ) * 0.5f;
desc.radius -= mSkinWidth;
desc.height = size.z - ( desc.radius * 2.0f );
desc.height -= mSkinWidth * 2.0f;
desc.climbingMode = physx::PxCapsuleClimbingMode::eCONSTRAINED;
desc.position.set( 0, 0, 0 );
desc.upDirection = physx::PxVec3(0,0,1);
desc.reportCallback = this;
desc.slopeLimit = runSurfaceCos;
desc.stepOffset = stepHeight;
desc.behaviorCallback = NULL;
desc.material = gPhysics3SDK->createMaterial(0.1f, 0.1f, 0.2f);
mController = mWorld->createController( desc );
mWorld->getStaticChangedSignal().notify( this, &Px3Player::_onStaticChanged );
physx::PxRigidDynamic *kineActor = mController->getActor();
//player only has one shape
physx::PxShape *shape = px3GetFirstShape(kineActor);
physx::PxFilterData colData;
colData.word0 = PX3_PLAYER;
shape->setSimulationFilterData(colData);
shape->setQueryFilterData(colData);
//store geometry for later use in findContact calls
shape->getCapsuleGeometry(mGeometry);
mUserData.setObject( obj );
kineActor->userData = &mUserData;
}
void Px3Player::_onStaticChanged()
{
if(mController)
mController->invalidateCache();
}
void Px3Player::_onPhysicsReset( PhysicsResetEvent reset )
{
if(mController)
mController->invalidateCache();
}
Point3F Px3Player::move( const VectorF &disp, CollisionList &outCol )
{
AssertFatal( mController, "Px3Player::move - The controller is null!" );
// Return the last position if the simulation is stopped.
//
// See PxPlayer::_onPhysicsReset
if ( !mWorld->isEnabled() )
{
Point3F newPos = px3Cast<Point3F>( mController->getPosition() );
newPos.z -= mOriginOffset;
return newPos;
}
mWorld->releaseWriteLock();
mCollisionList = &outCol;
physx::PxVec3 dispNx( disp.x, disp.y, disp.z );
if (mIsZero(disp.z))
dispNx.z = 0.0f;
U32 groups = 0xffffffff;
groups &= ~( PX3_TRIGGER ); // No trigger shapes!
groups &= ~( PX3_DEBRIS);
physx::PxControllerFilters filter;
physx::PxFilterData data;
data.word0=groups;
filter.mFilterData = &data;
filter.mFilterFlags = physx::PxSceneQueryFilterFlags(physx::PxControllerFlag::eCOLLISION_DOWN|physx::PxControllerFlag::eCOLLISION_SIDES|physx::PxControllerFlag::eCOLLISION_UP);
mController->move( dispNx,0.0001f,0, filter );
Point3F newPos = px3Cast<Point3F>( mController->getPosition() );
newPos.z -= mOriginOffset;
mCollisionList = NULL;
return newPos;
}
void Px3Player::onShapeHit( const physx::PxControllerShapeHit& hit )
{
if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions)
return;
physx::PxRigidActor *actor = hit.actor;
PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
// Fill out the Collision
// structure for use later.
Collision &col = mCollisionList->increment();
dMemset( &col, 0, sizeof( col ) );
col.normal = px3Cast<Point3F>( hit.worldNormal );
col.point = px3Cast<Point3F>( hit.worldPos );
col.distance = hit.length;
if ( userData )
col.object = userData->getObject();
if (mIsZero(hit.dir.z))
{
if (col.normal.z > 0.0f)
{
col.normal.z = 0.0f;
col.normal.normalizeSafe();
}
}
else
{
col.normal.set(0.0f, 0.0f, 1.0f);
}
}
void Px3Player::onControllerHit( const physx::PxControllersHit& hit )
{
if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions)
return;
physx::PxRigidActor *actor = hit.other->getActor();
PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
// For controller-to-controller hit we don't have an actual hit point, so all
// we can do is set the hit object.
Collision &col = mCollisionList->increment();
dMemset( &col, 0, sizeof( col ) );
if ( userData )
col.object = userData->getObject();
}
void Px3Player::findContact( SceneObject **contactObject,
VectorF *contactNormal,
Vector<SceneObject*> *outOverlapObjects ) const
{
// Calculate the sweep motion...
F32 halfCapSize = mOriginOffset;
F32 halfSmallCapSize = halfCapSize * 0.8f;
F32 diff = halfCapSize - halfSmallCapSize;
F32 distance = diff + mSkinWidth + 0.01f;
physx::PxVec3 dir(0,0,-1);
physx::PxScene *scene = mWorld->getScene();
physx::PxHitFlags hitFlags(physx::PxHitFlag::eDEFAULT);
physx::PxQueryFilterData filterData(physx::PxQueryFlag::eDYNAMIC|physx::PxQueryFlag::eSTATIC);
filterData.data.word0 = PX3_DEFAULT;
physx::PxSweepHit sweepHit;
physx::PxRigidDynamic *actor= mController->getActor();
physx::PxU32 shapeIndex;
bool hit = physx::PxRigidBodyExt::linearSweepSingle(*actor,*scene,dir,distance,hitFlags,sweepHit,shapeIndex,filterData);
if ( hit )
{
PhysicsUserData *data = PhysicsUserData::cast( sweepHit.actor->userData);
if ( data )
{
*contactObject = data->getObject();
*contactNormal = px3Cast<Point3F>( sweepHit.normal );
}
}
// Check for overlapped objects ( triggers )
if ( !outOverlapObjects )
return;
filterData.data.word0 = PX3_TRIGGER;
const physx::PxU32 bufferSize = 10;
physx::PxOverlapBufferN<bufferSize> hitBuffer;
hit = scene->overlap(mGeometry,actor->getGlobalPose(),hitBuffer,filterData);
if(hit)
{
for ( U32 i = 0; i < hitBuffer.nbTouches; i++ )
{
PhysicsUserData *data = PhysicsUserData::cast( hitBuffer.touches[i].actor->userData );
if ( data )
outOverlapObjects->push_back( data->getObject() );
}
}
}
void Px3Player::enableCollision()
{
AssertFatal( mController, "Px3Player::enableCollision - The controller is null!" );
mWorld->releaseWriteLock();
px3GetFirstShape(mController->getActor())->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,true);
}
void Px3Player::disableCollision()
{
AssertFatal( mController, "Px3Player::disableCollision - The controller is null!" );
mWorld->releaseWriteLock();
px3GetFirstShape(mController->getActor())->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,false);
}
PhysicsWorld* Px3Player::getWorld()
{
return mWorld;
}
void Px3Player::setTransform( const MatrixF &transform )
{
AssertFatal( mController, "Px3Player::setTransform - The controller is null!" );
mWorld->releaseWriteLock();
Point3F newPos = transform.getPosition();
newPos.z += mOriginOffset;
const Point3F &curPos = px3Cast<Point3F>(mController->getPosition());
if ( !(newPos - curPos ).isZero() )
mController->setPosition( px3Cast<physx::PxExtendedVec3>(newPos) );
}
MatrixF& Px3Player::getTransform( MatrixF *outMatrix )
{
AssertFatal( mController, "Px3Player::getTransform - The controller is null!" );
Point3F newPos = px3Cast<Point3F>( mController->getPosition() );
newPos.z -= mOriginOffset;
outMatrix->setPosition( newPos );
return *outMatrix;
}
void Px3Player::setScale( const Point3F &scale )
{
//Ignored
}
Box3F Px3Player::getWorldBounds()
{
physx::PxBounds3 bounds;
physx::PxRigidDynamic *actor = mController->getActor();
physx::PxShape *shape = px3GetFirstShape(actor);
bounds = physx::PxShapeExt::getWorldBounds(*shape,*actor);
return px3Cast<Box3F>( bounds );
}

View file

@ -0,0 +1,104 @@
//-----------------------------------------------------------------------------
// 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 _PX3PLAYER_H
#define _PX3PLAYER_H
#ifndef _PHYSX3_H_
#include "T3D/physics/physx3/px3.h"
#endif
#ifndef _T3D_PHYSICS_PHYSICSPLAYER_H_
#include "T3D/physics/physicsPlayer.h"
#endif
#ifndef _T3D_PHYSICSCOMMON_H_
#include "T3D/physics/physicsCommon.h"
#endif
class Px3World;
class Px3Player : public PhysicsPlayer, public physx::PxUserControllerHitReport
{
protected:
physx::PxController *mController;
physx::PxCapsuleGeometry mGeometry;
F32 mSkinWidth;
Px3World *mWorld;
SceneObject *mObject;
/// Used to get collision info out of the
/// PxUserControllerHitReport callbacks.
CollisionList *mCollisionList;
///
F32 mOriginOffset;
///
F32 mStepHeight;
U32 mElapsed;
///
void _releaseController();
virtual void onShapeHit( const physx::PxControllerShapeHit &hit );
virtual void onControllerHit( const physx::PxControllersHit &hit );
virtual void onObstacleHit(const physx::PxControllerObstacleHit &){}
void _findContact( SceneObject **contactObject, VectorF *contactNormal ) const;
void _onPhysicsReset( PhysicsResetEvent reset );
void _onStaticChanged();
public:
Px3Player();
virtual ~Px3Player();
// PhysicsObject
virtual PhysicsWorld* getWorld();
virtual void setTransform( const MatrixF &transform );
virtual MatrixF& getTransform( MatrixF *outMatrix );
virtual void setScale( const Point3F &scale );
virtual Box3F getWorldBounds();
virtual void setSimulationEnabled( bool enabled ) {}
virtual bool isSimulationEnabled() { return true; }
// PhysicsPlayer
virtual void init( const char *type,
const Point3F &size,
F32 runSurfaceCos,
F32 stepHeight,
SceneObject *obj,
PhysicsWorld *world );
virtual Point3F move( const VectorF &displacement, CollisionList &outCol );
virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector<SceneObject*> *outOverlapObjects ) const;
virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const { return true; }
virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) {}
virtual void enableCollision();
virtual void disableCollision();
};
#endif // _PX3PLAYER_H_

View file

@ -0,0 +1,226 @@
//-----------------------------------------------------------------------------
// 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 "console/consoleTypes.h"
#include "T3D/physics/physx3/px3World.h"
#include "T3D/physics/physx3/px3Plugin.h"
#include "T3D/physics/physx3/px3Collision.h"
#include "T3D/physics/physx3/px3Body.h"
#include "T3D/physics/physx3/px3Player.h"
#include "T3D/physics/physicsShape.h"
#include "T3D/gameBase/gameProcess.h"
#include "core/util/tNamedFactory.h"
AFTER_MODULE_INIT( Sim )
{
NamedFactory<PhysicsPlugin>::add( "PhysX3", &Px3Plugin::create );
#if defined(TORQUE_OS_WIN) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON)
NamedFactory<PhysicsPlugin>::add( "default", &Px3Plugin::create );
#endif
}
PhysicsPlugin* Px3Plugin::create()
{
// Only create the plugin if it hasn't been set up AND
// the PhysX world is successfully initialized.
bool success = Px3World::restartSDK( false );
if ( success )
return new Px3Plugin();
return NULL;
}
Px3Plugin::Px3Plugin()
{
}
Px3Plugin::~Px3Plugin()
{
}
void Px3Plugin::destroyPlugin()
{
// Cleanup any worlds that are still kicking.
Map<StringNoCase, PhysicsWorld*>::Iterator iter = mPhysicsWorldLookup.begin();
for ( ; iter != mPhysicsWorldLookup.end(); iter++ )
{
iter->value->destroyWorld();
delete iter->value;
}
mPhysicsWorldLookup.clear();
Px3World::restartSDK( true );
delete this;
}
void Px3Plugin::reset()
{
// First delete all the cleanup objects.
if ( getPhysicsCleanup() )
getPhysicsCleanup()->deleteAllObjects();
getPhysicsResetSignal().trigger( PhysicsResetEvent_Restore );
// Now let each world reset itself.
Map<StringNoCase, PhysicsWorld*>::Iterator iter = mPhysicsWorldLookup.begin();
for ( ; iter != mPhysicsWorldLookup.end(); iter++ )
iter->value->reset();
}
PhysicsCollision* Px3Plugin::createCollision()
{
return new Px3Collision();
}
PhysicsBody* Px3Plugin::createBody()
{
return new Px3Body();
}
PhysicsPlayer* Px3Plugin::createPlayer()
{
return new Px3Player();
}
bool Px3Plugin::isSimulationEnabled() const
{
bool ret = false;
Px3World *world = static_cast<Px3World*>( getWorld( smClientWorldName ) );
if ( world )
{
ret = world->isEnabled();
return ret;
}
world = static_cast<Px3World*>( getWorld( smServerWorldName ) );
if ( world )
{
ret = world->isEnabled();
return ret;
}
return ret;
}
void Px3Plugin::enableSimulation( const String &worldName, bool enable )
{
Px3World *world = static_cast<Px3World*>( getWorld( worldName ) );
if ( world )
world->setEnabled( enable );
}
void Px3Plugin::setTimeScale( const F32 timeScale )
{
// Grab both the client and
// server worlds and set their time
// scales to the passed value.
Px3World *world = static_cast<Px3World*>( getWorld( smClientWorldName ) );
if ( world )
world->setEditorTimeScale( timeScale );
world = static_cast<Px3World*>( getWorld( smServerWorldName ) );
if ( world )
world->setEditorTimeScale( timeScale );
}
const F32 Px3Plugin::getTimeScale() const
{
// Grab both the client and
// server worlds and call
// setEnabled( true ) on them.
Px3World *world = static_cast<Px3World*>( getWorld( smClientWorldName ) );
if ( !world )
{
world = static_cast<Px3World*>( getWorld( smServerWorldName ) );
if ( !world )
return 0.0f;
}
return world->getEditorTimeScale();
}
bool Px3Plugin::createWorld( const String &worldName )
{
Map<StringNoCase, PhysicsWorld*>::Iterator iter = mPhysicsWorldLookup.find( worldName );
PhysicsWorld *world = NULL;
iter != mPhysicsWorldLookup.end() ? world = (*iter).value : world = NULL;
if ( world )
{
Con::errorf( "Px3Plugin::createWorld - %s world already exists!", worldName.c_str() );
return false;
}
world = new Px3World();
if ( worldName.equal( smClientWorldName, String::NoCase ) )
world->initWorld( false, ClientProcessList::get() );
else
world->initWorld( true, ServerProcessList::get() );
mPhysicsWorldLookup.insert( worldName, world );
return world != NULL;
}
void Px3Plugin::destroyWorld( const String &worldName )
{
Map<StringNoCase, PhysicsWorld*>::Iterator iter = mPhysicsWorldLookup.find( worldName );
if ( iter == mPhysicsWorldLookup.end() )
return;
PhysicsWorld *world = (*iter).value;
world->destroyWorld();
delete world;
mPhysicsWorldLookup.erase( iter );
}
PhysicsWorld* Px3Plugin::getWorld( const String &worldName ) const
{
if ( mPhysicsWorldLookup.isEmpty() )
return NULL;
Map<StringNoCase, PhysicsWorld*>::ConstIterator iter = mPhysicsWorldLookup.find( worldName );
return iter != mPhysicsWorldLookup.end() ? (*iter).value : NULL;
}
PhysicsWorld* Px3Plugin::getWorld() const
{
if ( mPhysicsWorldLookup.size() == 0 )
return NULL;
Map<StringNoCase, PhysicsWorld*>::ConstIterator iter = mPhysicsWorldLookup.begin();
return iter->value;
}
U32 Px3Plugin::getWorldCount() const
{
return mPhysicsWorldLookup.size();
}

View file

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// 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 _PX3PLUGIN_H_
#define _PX3PLUGIN_H_
#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_
#include "T3D/physics/physicsPlugin.h"
#endif
class Px3ClothShape;
class Px3Plugin : public PhysicsPlugin
{
public:
Px3Plugin();
~Px3Plugin();
/// Create function for factory.
static PhysicsPlugin* create();
// PhysicsPlugin
virtual void destroyPlugin();
virtual void reset();
virtual PhysicsCollision* createCollision();
virtual PhysicsBody* createBody();
virtual PhysicsPlayer* createPlayer();
virtual bool isSimulationEnabled() const;
virtual void enableSimulation( const String &worldName, bool enable );
virtual void setTimeScale( const F32 timeScale );
virtual const F32 getTimeScale() const;
virtual bool createWorld( const String &worldName );
virtual void destroyWorld( const String &worldName );
virtual PhysicsWorld* getWorld( const String &worldName ) const;
virtual PhysicsWorld* getWorld() const;
virtual U32 getWorldCount() const;
};
#endif // _PX3PLUGIN_H_

View file

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// 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/physics/physx3/px3Stream.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "core/strings/stringFunctions.h"
Px3MemOutStream::Px3MemOutStream() : mMemStream(1024)
{
}
Px3MemOutStream::~Px3MemOutStream()
{
}
physx::PxU32 Px3MemOutStream::write(const void *src, physx::PxU32 count)
{
physx::PxU32 out=0;
if(!mMemStream.write(count,src))
return out;
out = count;
return out;
}
Px3MemInStream::Px3MemInStream(physx::PxU8* data, physx::PxU32 length):mMemStream(length,data)
{
}
physx::PxU32 Px3MemInStream::read(void* dest, physx::PxU32 count)
{
physx::PxU32 read =0;
if(!mMemStream.read(count,dest))
return read;
read = count;
return read;
}
void Px3MemInStream::seek(physx::PxU32 pos)
{
mMemStream.setPosition(pos);
}
physx::PxU32 Px3MemInStream::getLength() const
{
return mMemStream.getStreamSize();
}
physx::PxU32 Px3MemInStream::tell() const
{
return mMemStream.getPosition();
}
Px3ConsoleStream::Px3ConsoleStream()
{
}
Px3ConsoleStream::~Px3ConsoleStream()
{
}
void Px3ConsoleStream::reportError( physx::PxErrorCode code, const char *message, const char* file, int line )
{
UTF8 info[1024];
dSprintf( info, 1024, "File: %s\nLine: %d\n%s", file, line, message );
Platform::AlertOK( "PhysX Error", info );
// Con::printf( "PhysX Error:\n %s(%d) : %s\n", file, line, message );
}

View file

@ -0,0 +1,77 @@
//-----------------------------------------------------------------------------
// 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 _PX3STREAM_H_
#define _PX3STREAM_H_
#ifndef _PHYSX3_H_
#include "T3D/physics/physx3/px3.h"
#endif
#ifndef _MEMSTREAM_H_
#include "core/stream/memStream.h"
#endif
class Px3MemOutStream : public physx::PxOutputStream
{
public:
Px3MemOutStream();
virtual ~Px3MemOutStream();
void resetPosition();
virtual physx::PxU32 write(const void *src, physx::PxU32 count);
physx::PxU32 getSize() const {return mMemStream.getStreamSize();}
physx::PxU8* getData() const {return (physx::PxU8*)mMemStream.getBuffer();}
protected:
mutable MemStream mMemStream;
};
class Px3MemInStream: public physx::PxInputData
{
public:
Px3MemInStream(physx::PxU8* data, physx::PxU32 length);
virtual physx::PxU32 read(void* dest, physx::PxU32 count);
physx::PxU32 getLength() const;
virtual void seek(physx::PxU32 pos);
virtual physx::PxU32 tell() const;
protected:
mutable MemStream mMemStream;
};
class Px3ConsoleStream : public physx::PxDefaultErrorCallback
{
protected:
virtual void reportError( physx::PxErrorCode code, const char *message, const char* file, int line );
public:
Px3ConsoleStream();
virtual ~Px3ConsoleStream();
};
#endif // _PX3STREAM_H_

View file

@ -0,0 +1,32 @@
//-----------------------------------------------------------------------------
// 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/physics/physx3/px3Utils.h"
#include "T3D/physics/physx3/px3.h"
physx::PxShape* px3GetFirstShape(physx::PxRigidActor *actor)
{
physx::PxShape *shapes[1];
actor->getShapes(shapes, 1);
return shapes[0];
}

View file

@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// 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 _PX3UTILS_H_
#define _PX3UTILS_H_
namespace physx
{
class PxRigidActor;
class PxShape;
}
extern physx::PxShape* px3GetFirstShape(physx::PxRigidActor *actor);
#endif // _PX3UTILS_H_

View file

@ -0,0 +1,565 @@
//-----------------------------------------------------------------------------
// 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/physics/physx3/px3World.h"
#include "T3D/physics/physx3/px3.h"
#include "T3D/physics/physx3/px3Plugin.h"
#include "T3D/physics/physx3/px3Casts.h"
#include "T3D/physics/physx3/px3Stream.h"
#include "T3D/physics/physicsUserData.h"
#include "console/engineAPI.h"
#include "core/stream/bitStream.h"
#include "platform/profiler.h"
#include "sim/netConnection.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "core/util/safeDelete.h"
#include "collision/collision.h"
#include "T3D/gameBase/gameProcess.h"
#include "gfx/sim/debugDraw.h"
#include "gfx/primBuilder.h"
physx::PxPhysics* gPhysics3SDK = NULL;
physx::PxCooking* Px3World::smCooking = NULL;
physx::PxFoundation* Px3World::smFoundation = NULL;
physx::PxProfileZoneManager* Px3World::smProfileZoneManager = NULL;
physx::PxDefaultCpuDispatcher* Px3World::smCpuDispatcher=NULL;
Px3ConsoleStream* Px3World::smErrorCallback = NULL;
physx::PxVisualDebuggerConnection* Px3World::smPvdConnection=NULL;
physx::PxDefaultAllocator Px3World::smMemoryAlloc;
//Physics timing
F32 Px3World::smPhysicsStepTime = 1.0f/(F32)TickMs;
U32 Px3World::smPhysicsMaxIterations = 4;
Px3World::Px3World(): mScene( NULL ),
mProcessList( NULL ),
mIsSimulating( false ),
mErrorReport( false ),
mTickCount( 0 ),
mIsEnabled( false ),
mEditorTimeScale( 1.0f ),
mAccumulator( 0 ),
mControllerManager( NULL )
{
}
Px3World::~Px3World()
{
}
physx::PxCooking *Px3World::getCooking()
{
return smCooking;
}
void Px3World::setTiming(F32 stepTime,U32 maxIterations)
{
smPhysicsStepTime = stepTime;
smPhysicsMaxIterations = maxIterations;
}
bool Px3World::restartSDK( bool destroyOnly, Px3World *clientWorld, Px3World *serverWorld)
{
// If either the client or the server still exist
// then we cannot reset the SDK.
if ( clientWorld || serverWorld )
return false;
if(smPvdConnection)
smPvdConnection->release();
if(smCooking)
smCooking->release();
if(smCpuDispatcher)
smCpuDispatcher->release();
// Destroy the existing SDK.
if ( gPhysics3SDK )
{
PxCloseExtensions();
gPhysics3SDK->release();
}
if(smErrorCallback)
{
SAFE_DELETE(smErrorCallback);
}
if(smFoundation)
{
smFoundation->release();
SAFE_DELETE(smErrorCallback);
}
// If we're not supposed to restart... return.
if ( destroyOnly )
return true;
bool memTrack = false;
#ifdef TORQUE_DEBUG
memTrack = true;
#endif
smErrorCallback = new Px3ConsoleStream;
smFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, smMemoryAlloc, *smErrorCallback);
smProfileZoneManager = &physx::PxProfileZoneManager::createProfileZoneManager(smFoundation);
gPhysics3SDK = PxCreatePhysics(PX_PHYSICS_VERSION, *smFoundation, physx::PxTolerancesScale(),memTrack,smProfileZoneManager);
if ( !gPhysics3SDK )
{
Con::errorf( "PhysX3 failed to initialize!" );
Platform::messageBox( Con::getVariable( "$appName" ),
avar("PhysX3 could not be started!\r\n"),
MBOk, MIStop );
Platform::forceShutdown( -1 );
// We shouldn't get here, but this shuts up
// source diagnostic tools.
return false;
}
if(!PxInitExtensions(*gPhysics3SDK))
{
Con::errorf( "PhysX3 failed to initialize extensions!" );
Platform::messageBox( Con::getVariable( "$appName" ),
avar("PhysX3 could not be started!\r\n"),
MBOk, MIStop );
Platform::forceShutdown( -1 );
return false;
}
smCooking = PxCreateCooking(PX_PHYSICS_VERSION, *smFoundation, physx::PxCookingParams(physx::PxTolerancesScale()));
if(!smCooking)
{
Con::errorf( "PhysX3 failed to initialize cooking!" );
Platform::messageBox( Con::getVariable( "$appName" ),
avar("PhysX3 could not be started!\r\n"),
MBOk, MIStop );
Platform::forceShutdown( -1 );
return false;
}
#ifdef TORQUE_DEBUG
physx::PxVisualDebuggerConnectionFlags connectionFlags(physx::PxVisualDebuggerExt::getAllConnectionFlags());
smPvdConnection = physx::PxVisualDebuggerExt::createConnection(gPhysics3SDK->getPvdConnectionManager(),
"localhost", 5425, 100, connectionFlags);
#endif
return true;
}
void Px3World::destroyWorld()
{
getPhysicsResults();
// Release the tick processing signals.
if ( mProcessList )
{
mProcessList->preTickSignal().remove( this, &Px3World::getPhysicsResults );
mProcessList->postTickSignal().remove( this, &Px3World::tickPhysics );
mProcessList = NULL;
}
if(mControllerManager)
{
mControllerManager->release();
mControllerManager = NULL;
}
// Destroy the scene.
if ( mScene )
{
// Release the scene.
mScene->release();
mScene = NULL;
}
}
bool Px3World::initWorld( bool isServer, ProcessList *processList )
{
if ( !gPhysics3SDK )
{
Con::errorf( "Physx3World::init - PhysXSDK not initialized!" );
return false;
}
mIsServer = isServer;
physx::PxSceneDesc sceneDesc(gPhysics3SDK->getTolerancesScale());
sceneDesc.gravity = px3Cast<physx::PxVec3>(mGravity);
sceneDesc.userData = this;
if(!sceneDesc.cpuDispatcher)
{
smCpuDispatcher = physx::PxDefaultCpuDispatcherCreate(PHYSICSMGR->getThreadCount());
sceneDesc.cpuDispatcher = smCpuDispatcher;
Con::printf("PhysX3 using Cpu: %d workers", smCpuDispatcher->getWorkerCount());
}
sceneDesc.flags |= physx::PxSceneFlag::eENABLE_CCD;
sceneDesc.flags |= physx::PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader;
mScene = gPhysics3SDK->createScene(sceneDesc);
physx::PxDominanceGroupPair debrisDominance( 0.0f, 1.0f );
mScene->setDominanceGroupPair(0,31,debrisDominance);
mControllerManager = PxCreateControllerManager(*mScene);
AssertFatal( processList, "Px3World::init() - We need a process list to create the world!" );
mProcessList = processList;
mProcessList->preTickSignal().notify( this, &Px3World::getPhysicsResults );
mProcessList->postTickSignal().notify( this, &Px3World::tickPhysics, 1000.0f );
return true;
}
// Most of this borrowed from bullet physics library, see btDiscreteDynamicsWorld.cpp
bool Px3World::_simulate(const F32 dt)
{
int numSimulationSubSteps = 0;
//fixed timestep with interpolation
mAccumulator += dt;
if (mAccumulator >= smPhysicsStepTime)
{
numSimulationSubSteps = int(mAccumulator / smPhysicsStepTime);
mAccumulator -= numSimulationSubSteps * smPhysicsStepTime;
}
if (numSimulationSubSteps)
{
//clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
int clampedSimulationSteps = (numSimulationSubSteps > smPhysicsMaxIterations)? smPhysicsMaxIterations : numSimulationSubSteps;
for (int i=0;i<clampedSimulationSteps;i++)
{
mScene->fetchResults(true);
mScene->simulate(smPhysicsStepTime);
}
}
mIsSimulating = true;
return true;
}
void Px3World::tickPhysics( U32 elapsedMs )
{
if ( !mScene || !mIsEnabled )
return;
// Did we forget to call getPhysicsResults somewhere?
AssertFatal( !mIsSimulating, "PhysX3World::tickPhysics() - Already simulating!" );
// The elapsed time should be non-zero and
// a multiple of TickMs!
AssertFatal( elapsedMs != 0 &&
( elapsedMs % TickMs ) == 0 , "PhysX3World::tickPhysics() - Got bad elapsed time!" );
PROFILE_SCOPE(Px3World_TickPhysics);
// Convert it to seconds.
const F32 elapsedSec = (F32)elapsedMs * 0.001f;
mIsSimulating = _simulate(elapsedSec * mEditorTimeScale);
//Con::printf( "%s PhysX3World::tickPhysics!", mIsServer ? "Client" : "Server" );
}
void Px3World::getPhysicsResults()
{
if ( !mScene || !mIsSimulating )
return;
PROFILE_SCOPE(Px3World_GetPhysicsResults);
// Get results from scene.
mScene->fetchResults(true);
mIsSimulating = false;
mTickCount++;
// Con::printf( "%s PhysXWorld::getPhysicsResults!", this == smClientWorld ? "Client" : "Server" );
}
void Px3World::releaseWriteLocks()
{
Px3World *world = dynamic_cast<Px3World*>( PHYSICSMGR->getWorld( "server" ) );
if ( world )
world->releaseWriteLock();
world = dynamic_cast<Px3World*>( PHYSICSMGR->getWorld( "client" ) );
if ( world )
world->releaseWriteLock();
}
void Px3World::releaseWriteLock()
{
if ( !mScene || !mIsSimulating )
return;
PROFILE_SCOPE(PxWorld_ReleaseWriteLock);
// We use checkResults here to release the write lock
// but we do not change the simulation flag or increment
// the tick count... we may have gotten results, but the
// simulation hasn't really ticked!
mScene->checkResults( true );
//AssertFatal( mScene->isWritable(), "PhysX3World::releaseWriteLock() - We should have been writable now!" );
}
bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse )
{
physx::PxVec3 orig = px3Cast<physx::PxVec3>( startPnt );
physx::PxVec3 dir = px3Cast<physx::PxVec3>( endPnt - startPnt );
physx::PxF32 maxDist = dir.magnitude();
dir.normalize();
U32 groups = 0xffffffff;
groups &= ~( PX3_TRIGGER ); // No trigger shapes!
physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::eIMPACT | physx::PxHitFlag::eNORMAL);
physx::PxQueryFilterData filterData(physx::PxQueryFlag::eSTATIC|physx::PxQueryFlag::eDYNAMIC);
filterData.data.word0 = groups;
physx::PxRaycastBuffer buf;
if(!mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData))
return false;
if(!buf.hasBlock)
return false;
const physx::PxRaycastHit hit = buf.block;
physx::PxRigidActor *actor = hit.actor;
PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
if ( ri )
{
ri->object = ( userData != NULL ) ? userData->getObject() : NULL;
if ( ri->object == NULL )
ri->distance = hit.distance;
ri->normal = px3Cast<Point3F>( hit.normal );
ri->point = px3Cast<Point3F>( hit.position );
ri->t = maxDist / hit.distance;
}
if ( impulse.isZero() ||
!actor->isRigidDynamic() ||
actor->is<physx::PxRigidDynamic>()->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC )
return true;
physx::PxRigidBody *body = actor->is<physx::PxRigidBody>();
physx::PxVec3 force = px3Cast<physx::PxVec3>( impulse );
physx::PxRigidBodyExt::addForceAtPos(*body,force,hit.position,physx::PxForceMode::eIMPULSE);
return true;
}
PhysicsBody* Px3World::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes )
{
physx::PxVec3 orig = px3Cast<physx::PxVec3>( start );
physx::PxVec3 dir = px3Cast<physx::PxVec3>( end - start );
physx::PxF32 maxDist = dir.magnitude();
dir.normalize();
U32 groups = 0xFFFFFFFF;
if ( !( bodyTypes & BT_Player ) )
groups &= ~( PX3_PLAYER );
// TODO: For now always skip triggers and debris,
// but we should consider how game specifc this API
// should be in the future.
groups &= ~( PX3_TRIGGER ); // triggers
groups &= ~( PX3_DEBRIS ); // debris
physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::eIMPACT | physx::PxHitFlag::eNORMAL);
physx::PxQueryFilterData filterData;
if(bodyTypes & BT_Static)
filterData.flags |= physx::PxQueryFlag::eSTATIC;
if(bodyTypes & BT_Dynamic)
filterData.flags |= physx::PxQueryFlag::eDYNAMIC;
filterData.data.word0 = groups;
physx::PxRaycastBuffer buf;
if( !mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData) )
return NULL;
if(!buf.hasBlock)
return NULL;
physx::PxRigidActor *actor = buf.block.actor;
PhysicsUserData *userData = PhysicsUserData::cast( actor->userData );
if( !userData )
return NULL;
return userData->getBody();
}
void Px3World::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude )
{
physx::PxVec3 nxPos = px3Cast<physx::PxVec3>( pos );
const physx::PxU32 bufferSize = 10;
physx::PxSphereGeometry worldSphere(radius);
physx::PxTransform pose(nxPos);
physx::PxOverlapBufferN<bufferSize> buffer;
if(!mScene->overlap(worldSphere,pose,buffer))
return;
for ( physx::PxU32 i = 0; i < buffer.nbTouches; i++ )
{
physx::PxRigidActor *actor = buffer.touches[i].actor;
bool dynamic = actor->isRigidDynamic();
if ( !dynamic )
continue;
bool kinematic = actor->is<physx::PxRigidDynamic>()->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC;
if ( kinematic )
continue;
physx::PxVec3 force = actor->getGlobalPose().p - nxPos;
force.normalize();
force *= forceMagnitude;
physx::PxRigidBody *body = actor->is<physx::PxRigidBody>();
physx::PxRigidBodyExt::addForceAtPos(*body,force,nxPos,physx::PxForceMode::eIMPULSE);
}
}
void Px3World::setEnabled( bool enabled )
{
mIsEnabled = enabled;
if ( !mIsEnabled )
getPhysicsResults();
}
physx::PxController* Px3World::createController( physx::PxControllerDesc &desc )
{
if ( !mScene )
return NULL;
// We need the writelock!
releaseWriteLock();
physx::PxController* pController = mControllerManager->createController(desc);
AssertFatal( pController, "Px3World::createController - Got a null!" );
return pController;
}
static ColorI getDebugColor( physx::PxU32 packed )
{
ColorI col;
col.blue = (packed)&0xff;
col.green = (packed>>8)&0xff;
col.red = (packed>>16)&0xff;
col.alpha = 255;
return col;
}
void Px3World::onDebugDraw( const SceneRenderState *state )
{
if ( !mScene )
return;
mScene->setVisualizationParameter(physx::PxVisualizationParameter::eSCALE,1.0f);
mScene->setVisualizationParameter(physx::PxVisualizationParameter::eBODY_AXES,1.0f);
mScene->setVisualizationParameter(physx::PxVisualizationParameter::eCOLLISION_SHAPES,1.0f);
const physx::PxRenderBuffer *renderBuffer = &mScene->getRenderBuffer();
if(!renderBuffer)
return;
// Render points
{
physx::PxU32 numPoints = renderBuffer->getNbPoints();
const physx::PxDebugPoint *points = renderBuffer->getPoints();
PrimBuild::begin( GFXPointList, numPoints );
while ( numPoints-- )
{
PrimBuild::color( getDebugColor(points->color) );
PrimBuild::vertex3fv(px3Cast<Point3F>(points->pos));
points++;
}
PrimBuild::end();
}
// Render lines
{
physx::PxU32 numLines = renderBuffer->getNbLines();
const physx::PxDebugLine *lines = renderBuffer->getLines();
PrimBuild::begin( GFXLineList, numLines * 2 );
while ( numLines-- )
{
PrimBuild::color( getDebugColor( lines->color0 ) );
PrimBuild::vertex3fv( px3Cast<Point3F>(lines->pos0));
PrimBuild::color( getDebugColor( lines->color1 ) );
PrimBuild::vertex3fv( px3Cast<Point3F>(lines->pos1));
lines++;
}
PrimBuild::end();
}
// Render triangles
{
physx::PxU32 numTris = renderBuffer->getNbTriangles();
const physx::PxDebugTriangle *triangles = renderBuffer->getTriangles();
PrimBuild::begin( GFXTriangleList, numTris * 3 );
while ( numTris-- )
{
PrimBuild::color( getDebugColor( triangles->color0 ) );
PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos0) );
PrimBuild::color( getDebugColor( triangles->color1 ) );
PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos1));
PrimBuild::color( getDebugColor( triangles->color2 ) );
PrimBuild::vertex3fv( px3Cast<Point3F>(triangles->pos2) );
triangles++;
}
PrimBuild::end();
}
}
//set simulation timing via script
DefineEngineFunction( physx3SetSimulationTiming, void, ( F32 stepTime, U32 maxSteps ),, "Set simulation timing of the PhysX 3 plugin" )
{
Px3World::setTiming(stepTime,maxSteps);
}

View file

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// 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 _PX3WORLD_H_
#define _PX3WORLD_H_
#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_
#include "T3D/physics/physicsWorld.h"
#endif
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _PHYSX3_H_
#include "T3D/physics/physx3/px3.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class Px3ConsoleStream;
class Px3ContactReporter;
class FixedStepper;
enum Px3CollisionGroup
{
PX3_DEFAULT = BIT(0),
PX3_PLAYER = BIT(1),
PX3_DEBRIS = BIT(2),
PX3_TRIGGER = BIT(3),
};
class Px3World : public PhysicsWorld
{
protected:
physx::PxScene* mScene;
bool mIsEnabled;
bool mIsSimulating;
bool mIsServer;
U32 mTickCount;
ProcessList *mProcessList;
F32 mEditorTimeScale;
bool mErrorReport;
physx::PxControllerManager* mControllerManager;
static Px3ConsoleStream *smErrorCallback;
static physx::PxDefaultAllocator smMemoryAlloc;
static physx::PxFoundation* smFoundation;
static physx::PxCooking *smCooking;
static physx::PxProfileZoneManager* smProfileZoneManager;
static physx::PxDefaultCpuDispatcher* smCpuDispatcher;
static physx::PxVisualDebuggerConnection* smPvdConnection;
static F32 smPhysicsStepTime;
static U32 smPhysicsMaxIterations;
F32 mAccumulator;
bool _simulate(const F32 dt);
public:
Px3World();
virtual ~Px3World();
virtual bool initWorld( bool isServer, ProcessList *processList );
virtual void destroyWorld();
virtual void onDebugDraw( const SceneRenderState *state );
virtual void reset() {}
virtual bool castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse );
virtual PhysicsBody* castRay( const Point3F &start, const Point3F &end, U32 bodyTypes = BT_All );
virtual void explosion( const Point3F &pos, F32 radius, F32 forceMagnitude );
virtual bool isEnabled() const { return mIsEnabled; }
physx::PxScene* getScene(){ return mScene;}
void setEnabled( bool enabled );
U32 getTick() { return mTickCount; }
void tickPhysics( U32 elapsedMs );
void getPhysicsResults();
void setEditorTimeScale( F32 timeScale ) { mEditorTimeScale = timeScale; }
const F32 getEditorTimeScale() const { return mEditorTimeScale; }
void releaseWriteLock();
bool isServer(){return mIsServer;}
physx::PxController* createController( physx::PxControllerDesc &desc );
//static
static bool restartSDK( bool destroyOnly = false, Px3World *clientWorld = NULL, Px3World *serverWorld = NULL );
static void releaseWriteLocks();
static physx::PxCooking *getCooking();
static void setTiming(F32 stepTime,U32 maxIterations);
};
#endif // _PX3WORLD_H_

View file

@ -356,6 +356,7 @@ PlayerData::PlayerData()
decalID = 0;
decalOffset = 0.0f;
actionCount = 0;
lookAction = 0;
// size of bounding box
@ -427,9 +428,9 @@ bool PlayerData::preload(bool server, String &errorStr)
{
for( U32 i = 0; i < MaxSounds; ++ i )
{
String errorStr;
if( !sfxResolve( &sound[ i ], errorStr ) )
Con::errorf( "PlayerData::preload: %s", errorStr.c_str() );
String sfxErrorStr;
if( !sfxResolve( &sound[ i ], sfxErrorStr ) )
Con::errorf( "PlayerData::preload: %s", sfxErrorStr.c_str() );
}
}
@ -487,10 +488,6 @@ bool PlayerData::preload(bool server, String &errorStr)
dp->death = false;
if (dp->sequence != -1)
getGroundInfo(si,thread,dp);
// No real reason to spam the console about a missing jet animation
if (dStricmp(sp->name, "jet") != 0)
AssertWarn(dp->sequence != -1, avar("PlayerData::preload - Unable to find named animation sequence '%s'!", sp->name));
}
for (S32 b = 0; b < mShape->mSequences.size(); b++)
{
@ -586,7 +583,10 @@ bool PlayerData::preload(bool server, String &errorStr)
Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShapeFP[i].getPath());
if (!fileRef)
{
errorStr = String::ToString("PlayerData: Mounted image %d loading failed, shape \"%s\" is not found.",i,mShapeFP[i].getPath().getFullPath().c_str());
return false;
}
if(server)
mCRCFP[i] = fileRef->getChecksum();
@ -2952,7 +2952,7 @@ void Player::updateMove(const Move* move)
// Clamp acceleration.
F32 maxAcc = (mDataBlock->swimForce / getMass()) * TickSec;
if ( false && swimSpeed > maxAcc )
if ( swimSpeed > maxAcc )
swimAcc *= maxAcc / swimSpeed;
acc += swimAcc;
@ -3692,7 +3692,7 @@ bool Player::setActionThread(const char* sequence,bool hold,bool wait,bool fsp)
void Player::setActionThread(U32 action,bool forward,bool hold,bool wait,bool fsp, bool forceSet)
{
if (!mDataBlock || (mActionAnimation.action == action && mActionAnimation.forward == forward && !forceSet))
if (!mDataBlock || !mDataBlock->actionCount || (mActionAnimation.action == action && mActionAnimation.forward == forward && !forceSet))
return;
if (action >= PlayerData::NumActionAnims)
@ -6507,8 +6507,9 @@ DefineEngineMethod( Player, getDamageLocation, const char*, ( Point3F pos ),,
object->getDamageLocation(pos, buffer1, buffer2);
char *buff = Con::getReturnBuffer(128);
dSprintf(buff, 128, "%s %s", buffer1, buffer2);
static const U32 bufSize = 128;
char *buff = Con::getReturnBuffer(bufSize);
dSprintf(buff, bufSize, "%s %s", buffer1, buffer2);
return buff;
}

View file

@ -320,9 +320,9 @@ bool ProjectileData::preload(bool server, String &errorStr)
if (Sim::findObject(decalId, decal) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(decal): %d", decalId);
String errorStr;
if( !sfxResolve( &sound, errorStr ) )
Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet: %s", errorStr.c_str());
String sfxErrorStr;
if( !sfxResolve( &sound, sfxErrorStr ) )
Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet: %s", sfxErrorStr.c_str());
if (!lightDesc && lightDescId != 0)
if (Sim::findObject(lightDescId, lightDesc) == false)
@ -550,6 +550,7 @@ S32 ProjectileData::scaleValue( S32 value, bool down )
//
Projectile::Projectile()
: mPhysicsWorld( NULL ),
mDataBlock( NULL ),
mCurrPosition( 0, 0, 0 ),
mCurrVelocity( 0, 0, 1 ),
mSourceObjectId( -1 ),
@ -697,6 +698,12 @@ bool Projectile::onAdd()
if(!Parent::onAdd())
return false;
if( !mDataBlock )
{
Con::errorf("Projectile::onAdd - Fail - Not datablock");
return false;
}
if (isServerObject())
{
ShapeBase* ptr;
@ -1011,7 +1018,7 @@ void Projectile::explode( const Point3F &p, const Point3F &n, const U32 collideT
// Client (impact) decal.
if ( mDataBlock->decal )
gDecalManager->addDecal( p, n, 0.0f, mDataBlock->decal );
gDecalManager->addDecal(p, n, mRandF(0.0f, M_2PI_F), mDataBlock->decal);
// Client object
updateSound();

View file

@ -136,11 +136,11 @@ bool ProximityMineData::preload( bool server, String& errorStr )
if ( !server )
{
// Resolve sounds
String errorStr;
if( !sfxResolve( &armingSound, errorStr ) )
Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", errorStr.c_str() );
if( !sfxResolve( &triggerSound, errorStr ) )
Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", errorStr.c_str() );
String sfxErrorStr;
if( !sfxResolve( &armingSound, sfxErrorStr ) )
Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", sfxErrorStr.c_str() );
if( !sfxResolve( &triggerSound, sfxErrorStr ) )
Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", sfxErrorStr.c_str() );
}
if ( mShape )

View file

@ -92,7 +92,7 @@ ConsoleDocClass( RigidShapeData,
"@see RigidShape\n"
"@see ShapeBase\n\n"
"@ingroup Platform\n"
"@ingroup Physics\n"
);
@ -149,7 +149,7 @@ ConsoleDocClass( RigidShape,
"@see RigidShapeData\n"
"@see ShapeBase\n\n"
"@ingroup Platform\n"
"@ingroup Physics\n"
);
@ -302,6 +302,7 @@ bool RigidShapeData::preload(bool server, String &errorStr)
if (!collisionDetails.size() || collisionDetails[0] == -1)
{
Con::errorf("RigidShapeData::preload failed: Rigid shapes must define a collision-1 detail");
errorStr = String::ToString("RigidShapeData: Couldn't load shape \"%s\"",shapeName);
return false;
}
@ -1140,11 +1141,11 @@ void RigidShape::updatePos(F32 dt)
void RigidShape::updateForces(F32 /*dt*/)
{
if (mDisableMove) return;
Point3F gravForce(0, 0, sRigidShapeGravity * mRigid.mass * mGravityMod);
MatrixF currTransform;
mRigid.getTransform(&currTransform);
mRigid.atRest = false;
Point3F torque(0, 0, 0);
Point3F force(0, 0, 0);

View file

@ -91,7 +91,9 @@ void SFX3DObject::getEarTransform( MatrixF& transform ) const
if ( !shapeInstance )
{
// Just in case.
transform = mObject->getTransform();
GameConnection* connection = dynamic_cast<GameConnection *>(NetConnection::getConnectionToServer());
if ( !connection || !connection->getControlCameraTransform( 0.0f, &transform ) )
transform = mObject->getTransform();
return;
}

View file

@ -114,11 +114,12 @@ IMPLEMENT_CALLBACK( ShapeBaseData, onTrigger, void, ( ShapeBase* obj, S32 index,
"@param index Index of the trigger that changed\n"
"@param state New state of the trigger\n" );
IMPLEMENT_CALLBACK( ShapeBaseData, onEndSequence, void, ( ShapeBase* obj, S32 slot ), ( obj, slot ),
IMPLEMENT_CALLBACK(ShapeBaseData, onEndSequence, void, (ShapeBase* obj, S32 slot, const char* name), (obj, slot, name),
"@brief Called when a thread playing a non-cyclic sequence reaches the end of the "
"sequence.\n\n"
"@param obj The ShapeBase object\n"
"@param slot Thread slot that finished playing\n" );
"@param slot Thread slot that finished playing\n"
"@param name Thread name that finished playing\n");
IMPLEMENT_CALLBACK( ShapeBaseData, onForceUncloak, void, ( ShapeBase* obj, const char* reason ), ( obj, reason ),
"@brief Called when the object is forced to uncloak.\n\n"
@ -306,7 +307,10 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShape.getPath());
if (!fileRef)
{
errorStr = String::ToString("ShapeBaseData: Couldn't load shape \"%s\"",shapeName);
return false;
}
if(server)
mCRC = fileRef->getChecksum();
@ -928,7 +932,6 @@ ShapeBase::ShapeBase()
for (i = 0; i < MaxScriptThreads; i++) {
mScriptThread[i].sequence = -1;
mScriptThread[i].thread = 0;
mScriptThread[i].sound = 0;
mScriptThread[i].state = Thread::Stop;
mScriptThread[i].atEnd = false;
mScriptThread[i].timescale = 1.f;
@ -2152,14 +2155,13 @@ bool ShapeBase::setThreadSequence(U32 slot, S32 seq, bool reset)
if (reset) {
st.state = Thread::Play;
st.atEnd = false;
st.timescale = 1.f;
st.position = 0.f;
st.timescale = 1.f;
st.position = 0.f;
}
if (mShapeInstance) {
if (!st.thread)
st.thread = mShapeInstance->addThread();
mShapeInstance->setSequence(st.thread,seq,0);
stopThreadSound(st);
mShapeInstance->setSequence(st.thread,seq,st.position);
updateThread(st);
}
return true;
@ -2174,19 +2176,12 @@ void ShapeBase::updateThread(Thread& st)
case Thread::Stop:
{
mShapeInstance->setTimeScale( st.thread, 1.f );
mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 0.0f : 1.0f );
mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f );
} // Drop through to pause state
case Thread::Pause:
{
if ( st.position != -1.f )
{
mShapeInstance->setTimeScale( st.thread, 1.f );
mShapeInstance->setPos( st.thread, st.position );
}
mShapeInstance->setTimeScale( st.thread, 0.f );
stopThreadSound( st );
} break;
case Thread::Play:
@ -2196,7 +2191,6 @@ void ShapeBase::updateThread(Thread& st)
mShapeInstance->setTimeScale(st.thread,1);
mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f );
mShapeInstance->setTimeScale(st.thread,0);
stopThreadSound(st);
st.state = Thread::Stop;
}
else
@ -2208,16 +2202,11 @@ void ShapeBase::updateThread(Thread& st)
}
mShapeInstance->setTimeScale(st.thread, st.timescale );
if (!st.sound)
{
startSequenceSound(st);
}
}
} break;
case Thread::Destroy:
{
stopThreadSound(st);
st.atEnd = true;
st.sequence = -1;
if(st.thread)
@ -2325,19 +2314,6 @@ bool ShapeBase::setThreadTimeScale( U32 slot, F32 timeScale )
return false;
}
void ShapeBase::stopThreadSound(Thread& thread)
{
if (thread.sound) {
}
}
void ShapeBase::startSequenceSound(Thread& thread)
{
if (!isGhost() || !thread.thread)
return;
stopThreadSound(thread);
}
void ShapeBase::advanceThreads(F32 dt)
{
for (U32 i = 0; i < MaxScriptThreads; i++) {
@ -2349,7 +2325,7 @@ void ShapeBase::advanceThreads(F32 dt)
st.atEnd = true;
updateThread(st);
if (!isGhost()) {
mDataBlock->onEndSequence_callback( this, i );
mDataBlock->onEndSequence_callback(this, i, this->getThreadSequenceName(i));
}
}
@ -2358,6 +2334,7 @@ void ShapeBase::advanceThreads(F32 dt)
if(st.thread)
{
mShapeInstance->advanceTime(dt,st.thread);
st.position = mShapeInstance->getPos(st.thread);
}
}
}
@ -3058,9 +3035,9 @@ void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)
if (stream->readFlag()) {
Thread& st = mScriptThread[i];
U32 seq = stream->readInt(ThreadSequenceBits);
st.state = stream->readInt(2);
stream->read( &st.timescale );
stream->read( &st.position );
st.state = Thread::State(stream->readInt(2));
stream->read( &st.timescale );
stream->read( &st.position );
st.atEnd = stream->readFlag();
if (st.sequence != seq && st.state != Thread::Destroy)
setThreadSequence(i,seq,false);

View file

@ -648,7 +648,7 @@ public:
DECLARE_CALLBACK( void, onCollision, ( ShapeBase* obj, SceneObject* collObj, VectorF vec, F32 len ) );
DECLARE_CALLBACK( void, onDamage, ( ShapeBase* obj, F32 delta ) );
DECLARE_CALLBACK( void, onTrigger, ( ShapeBase* obj, S32 index, bool state ) );
DECLARE_CALLBACK( void, onEndSequence, ( ShapeBase* obj, S32 slot ) );
DECLARE_CALLBACK(void, onEndSequence, (ShapeBase* obj, S32 slot, const char* name));
DECLARE_CALLBACK( void, onForceUncloak, ( ShapeBase* obj, const char* reason ) );
/// @}
};
@ -729,12 +729,9 @@ protected:
Play, Stop, Pause, Destroy
};
TSThread* thread; ///< Pointer to 3space data.
U32 state; ///< State of the thread
///
/// @see Thread::State
State state; ///< State of the thread
S32 sequence; ///< The animation sequence which is running in this thread.
F32 timescale; ///< Timescale
U32 sound; ///< Handle to sound.
F32 timescale; ///< Timescale
bool atEnd; ///< Are we at the end of this thread?
F32 position;
};
@ -1354,14 +1351,6 @@ public:
/// @param timescale Timescale
bool setThreadTimeScale( U32 slot, F32 timeScale );
/// Start the sound associated with an animation thread
/// @param thread Thread
void startSequenceSound(Thread& thread);
/// Stop the sound associated with an animation thread
/// @param thread Thread
void stopThreadSound(Thread& thread);
/// Advance all animation threads attached to this shapebase
/// @param dt Change in time from last call to this function
void advanceThreads(F32 dt);

View file

@ -462,7 +462,10 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr)
Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(shape[i].getPath());
if (!fileRef)
{
errorStr = String::ToString("ShapeBaseImageData: Couldn't load shape \"%s\"",name);
return false;
}
if(server)
{

View file

@ -259,8 +259,9 @@ ConsoleGetType( TypeTriggerPolyhedron )
AssertFatal(currVec == 3, "Internal error: Bad trigger polyhedron");
// Build output string.
char* retBuf = Con::getReturnBuffer(1024);
dSprintf(retBuf, 1023, "%7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f",
static const U32 bufSize = 1024;
char* retBuf = Con::getReturnBuffer(bufSize);
dSprintf(retBuf, bufSize, "%7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f",
origin.x, origin.y, origin.z,
vecs[0].x, vecs[0].y, vecs[0].z,
vecs[2].x, vecs[2].y, vecs[2].z,

View file

@ -111,6 +111,11 @@ TSStatic::TSStatic()
mMeshCulling = false;
mUseOriginSort = false;
mUseAlphaFade = false;
mAlphaFadeStart = 100.0f;
mAlphaFadeEnd = 150.0f;
mInvertAlphaFade = false;
mAlphaFade = 1.0f;
mPhysicsRep = NULL;
mCollisionType = CollisionMesh;
@ -192,6 +197,13 @@ void TSStatic::initPersistFields()
endGroup("Collision");
addGroup( "AlphaFade" );
addField( "Alpha Fade Enable", TypeBool, Offset(mUseAlphaFade, TSStatic), "Turn on/off Alpha Fade" );
addField( "Alpha Fade Start", TypeF32, Offset(mAlphaFadeStart, TSStatic), "Distance of start Alpha Fade" );
addField( "Alpha Fade End", TypeF32, Offset(mAlphaFadeEnd, TSStatic), "Distance of end Alpha Fade" );
addField( "Alpha Fade Inverse", TypeBool, Offset(mInvertAlphaFade, TSStatic), "Invert Alpha Fade's Start & End Distance" );
endGroup( "AlphaFade" );
addGroup("Debug");
addField( "renderNormals", TypeF32, Offset( mRenderNormalScalar, TSStatic ),
@ -502,6 +514,36 @@ void TSStatic::prepRenderImage( SceneRenderState* state )
if (dist < 0.01f)
dist = 0.01f;
if (mUseAlphaFade)
{
mAlphaFade = 1.0f;
if ((mAlphaFadeStart < mAlphaFadeEnd) && mAlphaFadeStart > 0.1f)
{
if (mInvertAlphaFade)
{
if (dist <= mAlphaFadeStart)
{
return;
}
if (dist < mAlphaFadeEnd)
{
mAlphaFade = ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart));
}
}
else
{
if (dist >= mAlphaFadeEnd)
{
return;
}
if (dist > mAlphaFadeStart)
{
mAlphaFade -= ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart));
}
}
}
}
F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
if ( mForceDetail == -1 )
@ -545,6 +587,19 @@ void TSStatic::prepRenderImage( SceneRenderState* state )
GFX->setWorldMatrix( mat );
mShapeInstance->animate();
if(mShapeInstance)
{
if (mUseAlphaFade)
{
mShapeInstance->setAlphaAlways(mAlphaFade);
S32 s = mShapeInstance->mMeshObjects.size();
for(S32 x = 0; x < s; x++)
{
mShapeInstance->mMeshObjects[x].visible = mAlphaFade;
}
}
}
mShapeInstance->render( rdata );
if ( mRenderNormalScalar > 0 )
@ -625,6 +680,13 @@ U32 TSStatic::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
stream->writeFlag( mPlayAmbient );
if ( stream->writeFlag(mUseAlphaFade) )
{
stream->write(mAlphaFadeStart);
stream->write(mAlphaFadeEnd);
stream->write(mInvertAlphaFade);
}
if ( mLightPlugin )
retMask |= mLightPlugin->packUpdate(this, AdvancedStaticOptionsMask, con, mask, stream);
@ -682,6 +744,14 @@ void TSStatic::unpackUpdate(NetConnection *con, BitStream *stream)
mPlayAmbient = stream->readFlag();
mUseAlphaFade = stream->readFlag();
if (mUseAlphaFade)
{
stream->read(&mAlphaFadeStart);
stream->read(&mAlphaFadeEnd);
stream->read(&mInvertAlphaFade);
}
if ( mLightPlugin )
{
mLightPlugin->unpackUpdate(this, con, stream);
@ -702,41 +772,9 @@ bool TSStatic::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
if ( mCollisionType == Bounds )
{
F32 st, et, fst = 0.0f, fet = 1.0f;
F32 *bmin = &mObjBox.minExtents.x;
F32 *bmax = &mObjBox.maxExtents.x;
F32 const *si = &start.x;
F32 const *ei = &end.x;
for ( U32 i = 0; i < 3; i++ )
{
if (*si < *ei)
{
if ( *si > *bmax || *ei < *bmin )
return false;
F32 di = *ei - *si;
st = ( *si < *bmin ) ? ( *bmin - *si ) / di : 0.0f;
et = ( *ei > *bmax ) ? ( *bmax - *si ) / di : 1.0f;
}
else
{
if ( *ei > *bmax || *si < *bmin )
return false;
F32 di = *ei - *si;
st = ( *si > *bmax ) ? ( *bmax - *si ) / di : 0.0f;
et = ( *ei < *bmin ) ? ( *bmin - *si ) / di : 1.0f;
}
if ( st > fst ) fst = st;
if ( et < fet ) fet = et;
if ( fet < fst )
return false;
bmin++; bmax++;
si++; ei++;
}
info->normal = start - end;
info->normal.normalizeSafe();
getTransform().mulV( info->normal );
F32 fst;
if (!mObjBox.collideLine(start, end, &fst, &info->normal))
return false;
info->t = fst;
info->object = this;

View file

@ -97,6 +97,13 @@ class TSStatic : public SceneObject
};
public:
void setAlphaFade(bool enable, F32 start, F32 end, bool inverse)
{
mUseAlphaFade = enable;
mAlphaFadeStart = start;
mAlphaFadeEnd = end;
mInvertAlphaFade = inverse;
}
/// The different types of mesh data types
enum MeshType
@ -108,6 +115,11 @@ public:
};
protected:
bool mUseAlphaFade;
F32 mAlphaFadeStart;
F32 mAlphaFadeEnd;
F32 mAlphaFade;
bool mInvertAlphaFade;
bool onAdd();
void onRemove();

View file

@ -892,7 +892,7 @@ void AITurretShape::_trackTarget(F32 dt)
//if (pitch > M_PI_F)
// pitch = -(pitch - M_2PI_F);
Point3F rot(pitch, 0.0f, -yaw);
Point3F rot(-pitch, 0.0f, yaw);
// If we have a rotation rate make sure we follow it
if (mHeadingRate > 0)

View file

@ -1155,7 +1155,7 @@ void TurretShape::unpackUpdate(NetConnection *connection, BitStream *stream)
void TurretShape::getWeaponMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat )
{
// Returns mount point to world space transform
if ( index >= 0 && index < SceneObject::NumMountPoints) {
if ( index >= 0 && index < ShapeBase::MaxMountedImages) {
S32 ni = mDataBlock->weaponMountNode[index];
if (ni != -1) {
MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni];
@ -1180,7 +1180,7 @@ void TurretShape::getWeaponMountTransform( S32 index, const MatrixF &xfm, Matrix
void TurretShape::getRenderWeaponMountTransform( F32 delta, S32 mountPoint, const MatrixF &xfm, MatrixF *outMat )
{
// Returns mount point to world space transform
if ( mountPoint >= 0 && mountPoint < SceneObject::NumMountPoints) {
if ( mountPoint >= 0 && mountPoint < ShapeBase::MaxMountedImages) {
S32 ni = mDataBlock->weaponMountNode[mountPoint];
if (ni != -1) {
MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni];

View file

@ -218,6 +218,7 @@ bool VehicleData::preload(bool server, String &errorStr)
if (!collisionDetails.size() || collisionDetails[0] == -1)
{
Con::errorf("VehicleData::preload failed: Vehicle models must define a collision-1 detail");
errorStr = String::ToString("VehicleData: Couldn't load shape \"%s\"",shapeName);
return false;
}

View file

@ -71,6 +71,12 @@ bool VehicleBlocker::onAdd()
mObjBox.minExtents.set(-mDimensions.x, -mDimensions.y, 0);
mObjBox.maxExtents.set( mDimensions.x, mDimensions.y, mDimensions.z);
if( !mObjBox.isValidBox() )
{
Con::errorf("VehicleBlocker::onAdd - Fail - No valid object box");
return false;
}
resetWorldBox();
setRenderTransform(mObjToWorld);