tge/engine/game/shapeBase.cc
2017-04-17 06:17:10 -06:00

4073 lines
119 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "platform/platform.h"
#include "core/dnet.h"
#include "audio/audioDataBlock.h"
#include "game/gameConnection.h"
#include "game/moveManager.h"
#include "console/consoleTypes.h"
#include "core/bitStream.h"
#include "ts/tsPartInstance.h"
#include "ts/tsShapeInstance.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "game/shadow.h"
#include "game/fx/explosion.h"
#include "game/shapeBase.h"
#include "terrain/waterBlock.h"
#include "game/debris.h"
#include "terrain/sky.h"
#include "game/physicalZone.h"
#include "sceneGraph/detailManager.h"
#include "math/mathUtils.h"
#include "math/mMatrix.h"
#include "math/mRandom.h"
#include "platform/profiler.h"
IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData);
//----------------------------------------------------------------------------
// Timeout for non-looping sounds on a channel
static SimTime sAudioTimeout = 500;
bool ShapeBase::gRenderEnvMaps = true;
F32 ShapeBase::sWhiteoutDec = 0.007;
F32 ShapeBase::sDamageFlashDec = 0.007;
U32 ShapeBase::sLastRenderFrame = 0;
static const char *sDamageStateName[] =
{
// Index by enum ShapeBase::DamageState
"Enabled",
"Disabled",
"Destroyed"
};
//----------------------------------------------------------------------------
ShapeBaseData::ShapeBaseData()
{
shadowEnable = false;
shadowCanMove = false;
shadowCanAnimate = false;
shapeName = "";
cloakTexName = "";
mass = 1;
drag = 0;
density = 1;
maxEnergy = 0;
maxDamage = 1.0;
disabledLevel = 1.0;
destroyedLevel = 1.0;
repairRate = 0.0033;
eyeNode = -1;
shadowNode = -1;
cameraNode = -1;
damageSequence = -1;
hulkSequence = -1;
cameraMaxDist = 0;
cameraMinDist = 0.2;
cameraDefaultFov = 90.f;
cameraMinFov = 5.f;
cameraMaxFov = 120.f;
emap = false;
aiAvoidThis = false;
isInvincible = false;
renderWhenDestroyed = true;
debris = NULL;
debrisID = 0;
debrisShapeName = NULL;
explosion = NULL;
explosionID = 0;
underwaterExplosion = NULL;
underwaterExplosionID = 0;
firstPersonOnly = false;
useEyePoint = false;
observeThroughObject = false;
computeCRC = false;
// no shadows by default
genericShadowLevel = 2.0f;
noShadowLevel = 2.0f;
inheritEnergyFromMount = false;
for(U32 j = 0; j < NumHudRenderImages; j++)
{
hudImageNameFriendly[j] = 0;
hudImageNameEnemy[j] = 0;
hudRenderCenter[j] = false;
hudRenderModulated[j] = false;
hudRenderAlways[j] = false;
hudRenderDistance[j] = false;
hudRenderName[j] = false;
}
}
static ShapeBaseData gShapeBaseDataProto;
ShapeBaseData::~ShapeBaseData()
{
}
bool ShapeBaseData::preload(bool server, char errorBuffer[256])
{
if (!Parent::preload(server, errorBuffer))
return false;
bool shapeError = false;
// Resolve objects transmitted from server
if (!server) {
if( !explosion && explosionID != 0 )
{
if( Sim::findObject( explosionID, explosion ) == false)
{
Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(explosion): 0x%x", explosionID );
}
AssertFatal(!(explosion && ((explosionID < DataBlockObjectIdFirst) || (explosionID > DataBlockObjectIdLast))),
"ShapeBaseData::preload: invalid explosion data");
}
if( !underwaterExplosion && underwaterExplosionID != 0 )
{
if( Sim::findObject( underwaterExplosionID, underwaterExplosion ) == false)
{
Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(underwaterExplosion): 0x%x", underwaterExplosionID );
}
AssertFatal(!(underwaterExplosion && ((underwaterExplosionID < DataBlockObjectIdFirst) || (underwaterExplosionID > DataBlockObjectIdLast))),
"ShapeBaseData::preload: invalid underwaterExplosion data");
}
if( !debris && debrisID != 0 )
{
Sim::findObject( debrisID, debris );
AssertFatal(!(debris && ((debrisID < DataBlockObjectIdFirst) || (debrisID > DataBlockObjectIdLast))),
"ShapeBaseData::preload: invalid debris data");
}
if( debrisShapeName && debrisShapeName[0] != '\0' && !bool(debrisShape) )
{
debrisShape = ResourceManager->load(debrisShapeName);
if( bool(debrisShape) == false )
{
dSprintf(errorBuffer, 256, "ShapeBaseData::load: Couldn't load shape \"%s\"", debrisShapeName);
return false;
}
else
{
if(!server && !debrisShape->preloadMaterialList() && NetConnection::filesWereDownloaded())
shapeError = true;
TSShapeInstance* pDummy = new TSShapeInstance(debrisShape, !server);
delete pDummy;
}
}
}
//
if (shapeName && shapeName[0]) {
S32 i;
// Resolve shapename
shape = ResourceManager->load(shapeName, computeCRC);
if (!bool(shape)) {
dSprintf(errorBuffer, 256, "ShapeBaseData: Couldn't load shape \"%s\"",shapeName);
return false;
}
if(!server && !shape->preloadMaterialList() && NetConnection::filesWereDownloaded())
shapeError = true;
if(computeCRC)
{
Con::printf("Validation required for shape: %s", shapeName);
if(server)
mCRC = shape.getCRC();
else if(mCRC != shape.getCRC())
{
dSprintf(errorBuffer, 256, "Shape \"%s\" does not match version on server.",shapeName);
return false;
}
}
// Resolve details and camera node indexes.
for (i = 0; i < shape->details.size(); i++)
{
char* name = (char*)shape->names[shape->details[i].nameIndex];
if (dStrstr((const char *)dStrlwr(name), "collision-"))
{
collisionDetails.push_back(i);
collisionBounds.increment();
shape->computeBounds(collisionDetails.last(), collisionBounds.last());
shape->getAccelerator(collisionDetails.last());
if (!shape->bounds.isContained(collisionBounds.last()))
{
Con::warnf("Warning: shape %s collision detail %d (Collision-%d) bounds exceed that of shape.", shapeName, collisionDetails.size() - 1, collisionDetails.last());
collisionBounds.last() = shape->bounds;
}
else if (collisionBounds.last().isValidBox() == false)
{
Con::errorf("Error: shape %s-collision detail %d (Collision-%d) bounds box invalid!", shapeName, collisionDetails.size() - 1, collisionDetails.last());
collisionBounds.last() = shape->bounds;
}
// The way LOS works is that it will check to see if there is a LOS detail that matches
// the the collision detail + 1 + MaxCollisionShapes (this variable name should change in
// the future). If it can't find a matching LOS it will simply use the collision instead.
// We check for any "unmatched" LOS's further down
LOSDetails.increment();
char buff[128];
dSprintf(buff, sizeof(buff), "LOS-%d", i + 1 + MaxCollisionShapes);
U32 los = shape->findDetail(buff);
if (los == -1)
LOSDetails.last() = i;
else
LOSDetails.last() = los;
}
}
// Snag any "unmatched" LOS details
for (i = 0; i < shape->details.size(); i++)
{
char* name = (char*)shape->names[shape->details[i].nameIndex];
if (dStrstr((const char *)dStrlwr(name), "los-"))
{
// See if we already have this LOS
bool found = false;
for (U32 j = 0; j < LOSDetails.size(); j++)
{
if (LOSDetails[j] == i)
{
found = true;
break;
}
}
if (!found)
LOSDetails.push_back(i);
}
}
debrisDetail = shape->findDetail("Debris-17");
eyeNode = shape->findNode("eye");
cameraNode = shape->findNode("cam");
if (cameraNode == -1)
cameraNode = eyeNode;
// Resolve mount point node indexes
for (i = 0; i < NumMountPoints; i++) {
char fullName[256];
dSprintf(fullName,sizeof(fullName),"mount%d",i);
mountPointNode[i] = shape->findNode(fullName);
}
// find the AIRepairNode - hardcoded to be the last node in the array...
mountPointNode[AIRepairNode] = shape->findNode("AIRepairNode");
//
hulkSequence = shape->findSequence("Visibility");
damageSequence = shape->findSequence("Damage");
//
F32 w = shape->bounds.len_y() / 2;
if (cameraMaxDist < w)
cameraMaxDist = w;
}
if(!server)
{
// grab all the hud images
for(U32 i = 0; i < NumHudRenderImages; i++)
{
if(hudImageNameFriendly[i] && hudImageNameFriendly[i][0])
hudImageFriendly[i] = TextureHandle(hudImageNameFriendly[i], BitmapTexture);
if(hudImageNameEnemy[i] && hudImageNameEnemy[i][0])
hudImageEnemy[i] = TextureHandle(hudImageNameEnemy[i], BitmapTexture);
}
}
return !shapeError;
}
void ShapeBaseData::initPersistFields()
{
Parent::initPersistFields();
addGroup("Shadows");
addField("shadowEnable", TypeBool, Offset(shadowEnable, ShapeBaseData));
addField("shadowCanMove", TypeBool, Offset(shadowCanMove, ShapeBaseData));
addField("shadowCanAnimate", TypeBool, Offset(shadowCanAnimate, ShapeBaseData));
endGroup("Shadows");
addGroup("Render");
addField("shapeFile", TypeFilename, Offset(shapeName, ShapeBaseData));
addField("cloakTexture", TypeFilename, Offset(cloakTexName, ShapeBaseData));
addField("emap", TypeBool, Offset(emap, ShapeBaseData));
endGroup("Render");
addGroup("Destruction", "Parameters related to the destruction effects of this object.");
addField("explosion", TypeExplosionDataPtr, Offset(explosion, ShapeBaseData));
addField("underwaterExplosion", TypeExplosionDataPtr, Offset(underwaterExplosion, ShapeBaseData));
addField("debris", TypeDebrisDataPtr, Offset(debris, ShapeBaseData));
addField("renderWhenDestroyed", TypeBool, Offset(renderWhenDestroyed, ShapeBaseData));
addField("debrisShapeName", TypeFilename, Offset(debrisShapeName, ShapeBaseData));
endGroup("Destruction");
addGroup("Physics");
addField("mass", TypeF32, Offset(mass, ShapeBaseData));
addField("drag", TypeF32, Offset(drag, ShapeBaseData));
addField("density", TypeF32, Offset(density, ShapeBaseData));
endGroup("Physics");
addGroup("Damage/Energy");
addField("maxEnergy", TypeF32, Offset(maxEnergy, ShapeBaseData));
addField("maxDamage", TypeF32, Offset(maxDamage, ShapeBaseData));
addField("disabledLevel", TypeF32, Offset(disabledLevel, ShapeBaseData));
addField("destroyedLevel", TypeF32, Offset(destroyedLevel, ShapeBaseData));
addField("repairRate", TypeF32, Offset(repairRate, ShapeBaseData));
addField("inheritEnergyFromMount", TypeBool, Offset(inheritEnergyFromMount, ShapeBaseData));
addField("isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData));
endGroup("Damage/Energy");
addGroup("Camera");
addField("cameraMaxDist", TypeF32, Offset(cameraMaxDist, ShapeBaseData));
addField("cameraMinDist", TypeF32, Offset(cameraMinDist, ShapeBaseData));
addField("cameraDefaultFov", TypeF32, Offset(cameraDefaultFov, ShapeBaseData));
addField("cameraMinFov", TypeF32, Offset(cameraMinFov, ShapeBaseData));
addField("cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData));
addField("firstPersonOnly", TypeBool, Offset(firstPersonOnly, ShapeBaseData));
addField("useEyePoint", TypeBool, Offset(useEyePoint, ShapeBaseData));
addField("observeThroughObject", TypeBool, Offset(observeThroughObject, ShapeBaseData));
endGroup("Camera");
// This hud code is going to get ripped out soon...
addGroup("HUD", "@deprecated Likely to be removed soon.");
addField("hudImageName", TypeFilename, Offset(hudImageNameFriendly, ShapeBaseData), NumHudRenderImages);
addField("hudImageNameFriendly", TypeFilename, Offset(hudImageNameFriendly, ShapeBaseData), NumHudRenderImages);
addField("hudImageNameEnemy", TypeFilename, Offset(hudImageNameEnemy, ShapeBaseData), NumHudRenderImages);
addField("hudRenderCenter", TypeBool, Offset(hudRenderCenter, ShapeBaseData), NumHudRenderImages);
addField("hudRenderModulated", TypeBool, Offset(hudRenderModulated, ShapeBaseData), NumHudRenderImages);
addField("hudRenderAlways", TypeBool, Offset(hudRenderAlways, ShapeBaseData), NumHudRenderImages);
addField("hudRenderDistance", TypeBool, Offset(hudRenderDistance, ShapeBaseData), NumHudRenderImages);
addField("hudRenderName", TypeBool, Offset(hudRenderName, ShapeBaseData), NumHudRenderImages);
endGroup("HUD");
addGroup("Misc");
addField("aiAvoidThis", TypeBool, Offset(aiAvoidThis, ShapeBaseData));
addField("computeCRC", TypeBool, Offset(computeCRC, ShapeBaseData));
endGroup("Misc");
}
ConsoleMethod( ShapeBaseData, checkDeployPos, bool, 3, 3, "(Transform xform)")
{
if (bool(object->shape) == false)
return false;
Point3F pos(0, 0, 0);
AngAxisF aa(Point3F(0, 0, 1), 0);
dSscanf(argv[2],"%g %g %g %g %g %g %g",
&pos.x,&pos.y,&pos.z,&aa.axis.x,&aa.axis.y,&aa.axis.z,&aa.angle);
MatrixF mat;
aa.setMatrix(&mat);
mat.setColumn(3,pos);
Box3F objBox = object->shape->bounds;
Point3F boxCenter = (objBox.min + objBox.max) * 0.5;
objBox.min = boxCenter + (objBox.min - boxCenter) * 0.9;
objBox.max = boxCenter + (objBox.max - boxCenter) * 0.9;
Box3F wBox = objBox;
mat.mul(wBox);
EarlyOutPolyList polyList;
polyList.mNormal.set(0,0,0);
polyList.mPlaneList.clear();
polyList.mPlaneList.setSize(6);
polyList.mPlaneList[0].set(objBox.min,VectorF(-1,0,0));
polyList.mPlaneList[1].set(objBox.max,VectorF(0,1,0));
polyList.mPlaneList[2].set(objBox.max,VectorF(1,0,0));
polyList.mPlaneList[3].set(objBox.min,VectorF(0,-1,0));
polyList.mPlaneList[4].set(objBox.min,VectorF(0,0,-1));
polyList.mPlaneList[5].set(objBox.max,VectorF(0,0,1));
for (U32 i = 0; i < 6; i++)
{
PlaneF temp;
mTransformPlane(mat, Point3F(1, 1, 1), polyList.mPlaneList[i], &temp);
polyList.mPlaneList[i] = temp;
}
if (gServerContainer.buildPolyList(wBox, InteriorObjectType | StaticShapeObjectType, &polyList))
return false;
return true;
}
ConsoleMethod(ShapeBaseData, getDeployTransform, const char *, 4, 4, "(Point3F pos, Point3F normal)")
{
Point3F normal;
Point3F position;
dSscanf(argv[2], "%g %g %g", &position.x, &position.y, &position.z);
dSscanf(argv[3], "%g %g %g", &normal.x, &normal.y, &normal.z);
normal.normalize();
VectorF xAxis;
if( mFabs(normal.z) > mFabs(normal.x) && mFabs(normal.z) > mFabs(normal.y))
mCross( VectorF( 0, 1, 0 ), normal, &xAxis );
else
mCross( VectorF( 0, 0, 1 ), normal, &xAxis );
VectorF yAxis;
mCross( normal, xAxis, &yAxis );
MatrixF testMat(true);
testMat.setColumn( 0, xAxis );
testMat.setColumn( 1, yAxis );
testMat.setColumn( 2, normal );
testMat.setPosition( position );
char *returnBuffer = Con::getReturnBuffer(256);
Point3F pos;
testMat.getColumn(3,&pos);
AngAxisF aa(testMat);
dSprintf(returnBuffer,256,"%g %g %g %g %g %g %g",
pos.x,pos.y,pos.z,aa.axis.x,aa.axis.y,aa.axis.z,aa.angle);
return returnBuffer;
}
void ShapeBaseData::packData(BitStream* stream)
{
Parent::packData(stream);
if(stream->writeFlag(computeCRC))
stream->write(mCRC);
stream->writeFlag(shadowEnable);
stream->writeFlag(shadowCanMove);
stream->writeFlag(shadowCanAnimate);
stream->writeString(shapeName);
stream->writeString(cloakTexName);
if(stream->writeFlag(mass != gShapeBaseDataProto.mass))
stream->write(mass);
if(stream->writeFlag(drag != gShapeBaseDataProto.drag))
stream->write(drag);
if(stream->writeFlag(density != gShapeBaseDataProto.density))
stream->write(density);
if(stream->writeFlag(maxEnergy != gShapeBaseDataProto.maxEnergy))
stream->write(maxEnergy);
if(stream->writeFlag(cameraMaxDist != gShapeBaseDataProto.cameraMaxDist))
stream->write(cameraMaxDist);
if(stream->writeFlag(cameraMinDist != gShapeBaseDataProto.cameraMinDist))
stream->write(cameraMinDist);
cameraDefaultFov = mClampF(cameraDefaultFov, cameraMinFov, cameraMaxFov);
if(stream->writeFlag(cameraDefaultFov != gShapeBaseDataProto.cameraDefaultFov))
stream->write(cameraDefaultFov);
if(stream->writeFlag(cameraMinFov != gShapeBaseDataProto.cameraMinFov))
stream->write(cameraMinFov);
if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov))
stream->write(cameraMaxFov);
stream->writeString( debrisShapeName );
stream->writeFlag(observeThroughObject);
if( stream->writeFlag( debris != NULL ) )
{
stream->writeRangedU32(packed? SimObjectId(debris):
debris->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
}
stream->writeFlag(emap);
stream->writeFlag(isInvincible);
stream->writeFlag(renderWhenDestroyed);
if( stream->writeFlag( explosion != NULL ) )
{
stream->writeRangedU32( explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
if( stream->writeFlag( underwaterExplosion != NULL ) )
{
stream->writeRangedU32( underwaterExplosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
stream->writeFlag(inheritEnergyFromMount);
stream->writeFlag(firstPersonOnly);
stream->writeFlag(useEyePoint);
}
void ShapeBaseData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
computeCRC = stream->readFlag();
if(computeCRC)
stream->read(&mCRC);
shadowEnable = stream->readFlag();
shadowCanMove = stream->readFlag();
shadowCanAnimate = stream->readFlag();
shapeName = stream->readSTString();
cloakTexName = stream->readSTString();
if(stream->readFlag())
stream->read(&mass);
else
mass = gShapeBaseDataProto.mass;
if(stream->readFlag())
stream->read(&drag);
else
drag = gShapeBaseDataProto.drag;
if(stream->readFlag())
stream->read(&density);
else
density = gShapeBaseDataProto.density;
if(stream->readFlag())
stream->read(&maxEnergy);
else
maxEnergy = gShapeBaseDataProto.maxEnergy;
if(stream->readFlag())
stream->read(&cameraMaxDist);
else
cameraMaxDist = gShapeBaseDataProto.cameraMaxDist;
if(stream->readFlag())
stream->read(&cameraMinDist);
else
cameraMinDist = gShapeBaseDataProto.cameraMinDist;
if(stream->readFlag())
stream->read(&cameraDefaultFov);
else
cameraDefaultFov = gShapeBaseDataProto.cameraDefaultFov;
if(stream->readFlag())
stream->read(&cameraMinFov);
else
cameraMinFov = gShapeBaseDataProto.cameraMinFov;
if(stream->readFlag())
stream->read(&cameraMaxFov);
else
cameraMaxFov = gShapeBaseDataProto.cameraMaxFov;
debrisShapeName = stream->readSTString();
observeThroughObject = stream->readFlag();
if( stream->readFlag() )
{
debrisID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
emap = stream->readFlag();
isInvincible = stream->readFlag();
renderWhenDestroyed = stream->readFlag();
if( stream->readFlag() )
{
explosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
if( stream->readFlag() )
{
underwaterExplosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
inheritEnergyFromMount = stream->readFlag();
firstPersonOnly = stream->readFlag();
useEyePoint = stream->readFlag();
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
Chunker<ShapeBase::CollisionTimeout> sTimeoutChunker;
ShapeBase::CollisionTimeout* ShapeBase::sFreeTimeoutList = 0;
//----------------------------------------------------------------------------
IMPLEMENT_CO_NETOBJECT_V1(ShapeBase);
ShapeBase::ShapeBase()
{
mTypeMask |= ShapeBaseObjectType;
mDrag = 0;
mBuoyancy = 0;
mWaterCoverage = 0;
mLiquidType = 0;
mLiquidHeight = 0.0f;
//mControllingClient = 0;
mControllingObject = 0;
mGravityMod = 1.0;
mAppliedForce.set(0, 0, 0);
mTimeoutList = 0;
mDataBlock = NULL;
mShapeInstance = 0;
mEnergy = 0;
mRechargeRate = 0;
mDamage = 0;
mRepairRate = 0;
mRepairReserve = 0;
mDamageState = Enabled;
mDamageThread = 0;
mHulkThread = 0;
mLastRenderFrame = 0;
mLastRenderDistance = 0;
mCloaked = false;
mCloakLevel = 0.0;
mMount.object = 0;
mMount.link = 0;
mMount.list = 0;
mHidden = false;
for (int a = 0; a < MaxSoundThreads; a++) {
mSoundThread[a].play = false;
mSoundThread[a].profile = 0;
mSoundThread[a].sound = 0;
}
S32 i;
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].forward = true;
}
for (i = 0; i < MaxTriggerKeys; i++)
mTrigger[i] = false;
mDamageFlash = 0.0;
mWhiteOut = 0.0;
mInvincibleEffect = 0.0f;
mInvincibleDelta = 0.0f;
mInvincibleCount = 0.0f;
mInvincibleSpeed = 0.0f;
mInvincibleTime = 0.0f;
mInvincibleFade = 0.1;
mInvincibleOn = false;
mIsControlled = false;
mConvexList = new Convex;
mCameraFov = 90.f;
mShieldNormal.set(0, 0, 1);
mFadeOut = true;
mFading = false;
mFadeVal = 1.0;
mFadeTime = 1.0;
mFadeElapsedTime = 0.0;
mFadeDelay = 0.0;
mFlipFadeVal = false;
mLightTime = 0;
damageDir.set(0, 0, 1);
}
ShapeBase::~ShapeBase()
{
delete mConvexList;
mConvexList = NULL;
AssertFatal(mMount.link == 0,"ShapeBase::~ShapeBase: An object is still mounted");
if( mShapeInstance && (mShapeInstance->getDebrisRefCount() == 0) )
{
delete mShapeInstance;
}
CollisionTimeout* ptr = mTimeoutList;
while (ptr) {
CollisionTimeout* cur = ptr;
ptr = ptr->next;
cur->next = sFreeTimeoutList;
sFreeTimeoutList = cur;
}
}
//----------------------------------------------------------------------------
bool ShapeBase::onAdd()
{
if(!Parent::onAdd())
return false;
// Resolve sounds that arrived in the initial update
S32 i;
for (i = 0; i < MaxSoundThreads; i++)
updateAudioState(mSoundThread[i]);
for (i = 0; i < MaxScriptThreads; i++)
{
Thread& st = mScriptThread[i];
if(st.thread)
updateThread(st);
}
if (isClientObject())
{
if(mDataBlock->cloakTexName != StringTable->insert(""))
mCloakTexture = TextureHandle(mDataBlock->cloakTexName, MeshTexture, false);
//one of the mounted images must have a light source...
for (S32 i = 0; i < MaxMountedImages; i++)
{
ShapeBaseImageData* imageData = getMountedImage(i);
if (imageData != NULL && imageData->lightType != ShapeBaseImageData::NoLight)
{
Sim::getLightSet()->addObject(this);
break;
}
}
}
return true;
}
void ShapeBase::onRemove()
{
mConvexList->nukeList();
unmount();
Parent::onRemove();
// Stop any running sounds on the client
if (isGhost())
for (S32 i = 0; i < MaxSoundThreads; i++)
stopAudio(i);
}
void ShapeBase::onSceneRemove()
{
mConvexList->nukeList();
Parent::onSceneRemove();
}
bool ShapeBase::onNewDataBlock(GameBaseData* dptr)
{
if (Parent::onNewDataBlock(dptr) == false)
return false;
mDataBlock = dynamic_cast<ShapeBaseData*>(dptr);
if (!mDataBlock)
return false;
setMaskBits(DamageMask);
mDamageThread = 0;
mHulkThread = 0;
// Even if loadShape succeeds, there may not actually be
// a shape assigned to this object.
if (bool(mDataBlock->shape)) {
delete mShapeInstance;
mShapeInstance = new TSShapeInstance(mDataBlock->shape, isClientObject());
if (isClientObject())
mShapeInstance->cloneMaterialList();
mObjBox = mDataBlock->shape->bounds;
resetWorldBox();
// Initialize the threads
for (U32 i = 0; i < MaxScriptThreads; i++) {
Thread& st = mScriptThread[i];
if (st.sequence != -1) {
// TG: Need to see about supressing non-cyclic sounds
// if the sequences were actived before the object was
// ghosted.
// TG: Cyclic animations need to have a random pos if
// they were started before the object was ghosted.
// If there was something running on the old shape, the thread
// needs to be reset. Otherwise we assume that it's been
// initialized either by the constructor or from the server.
bool reset = st.thread != 0;
st.thread = 0;
setThreadSequence(i,st.sequence,reset);
}
}
if (mDataBlock->damageSequence != -1) {
mDamageThread = mShapeInstance->addThread();
mShapeInstance->setSequence(mDamageThread,
mDataBlock->damageSequence,0);
}
if (mDataBlock->hulkSequence != -1) {
mHulkThread = mShapeInstance->addThread();
mShapeInstance->setSequence(mHulkThread,
mDataBlock->hulkSequence,0);
}
}
if (isGhost() && mSkinNameHandle.isValidString() && mShapeInstance) {
mShapeInstance->reSkin(mSkinNameHandle);
mSkinHash = _StringTable::hashString(mSkinNameHandle.getString());
}
//
mEnergy = 0;
mDamage = 0;
mDamageState = Enabled;
mRepairReserve = 0;
updateMass();
updateDamageLevel();
updateDamageState();
mDrag = mDataBlock->drag;
mCameraFov = mDataBlock->cameraDefaultFov;
return true;
}
void ShapeBase::onDeleteNotify(SimObject* obj)
{
if (obj == getProcessAfter())
clearProcessAfter();
Parent::onDeleteNotify(obj);
if (obj == mMount.object)
unmount();
}
void ShapeBase::onImpact(SceneObject* obj, VectorF vec)
{
if (!isGhost()) {
char buff1[256];
char buff2[32];
dSprintf(buff1,sizeof(buff1),"%g %g %g",vec.x, vec.y, vec.z);
dSprintf(buff2,sizeof(buff2),"%g",vec.len());
Con::executef(mDataBlock,5,"onImpact",scriptThis(), obj->getIdString(), buff1, buff2);
}
}
void ShapeBase::onImpact(VectorF vec)
{
if (!isGhost()) {
char buff1[256];
char buff2[32];
dSprintf(buff1,sizeof(buff1),"%g %g %g",vec.x, vec.y, vec.z);
dSprintf(buff2,sizeof(buff2),"%g",vec.len());
Con::executef(mDataBlock,5,"onImpact",scriptThis(), "0", buff1, buff2);
}
}
//----------------------------------------------------------------------------
void ShapeBase::processTick(const Move* move)
{
// Energy management
if (mDamageState == Enabled && mDataBlock->inheritEnergyFromMount == false) {
F32 store = mEnergy;
mEnergy += mRechargeRate;
if (mEnergy > mDataBlock->maxEnergy)
mEnergy = mDataBlock->maxEnergy;
else
if (mEnergy < 0)
mEnergy = 0;
// Virtual setEnergyLevel is used here by some derived classes to
// decide whether they really want to set the energy mask bit.
if (mEnergy != store)
setEnergyLevel(mEnergy);
}
// Repair management
if (mDataBlock->isInvincible == false)
{
F32 store = mDamage;
mDamage -= mRepairRate;
mDamage = mClampF(mDamage, 0.f, mDataBlock->maxDamage);
if (mRepairReserve > mDamage)
mRepairReserve = mDamage;
if (mRepairReserve > 0.0)
{
F32 rate = getMin(mDataBlock->repairRate, mRepairReserve);
mDamage -= rate;
mRepairReserve -= rate;
}
if (store != mDamage)
{
updateDamageLevel();
if (isServerObject()) {
char delta[100];
dSprintf(delta,sizeof(delta),"%g",mDamage - store);
setMaskBits(DamageMask);
Con::executef(mDataBlock,3,"onDamage",scriptThis(),delta);
}
}
}
if (isServerObject()) {
// Server only...
advanceThreads(TickSec);
updateServerAudio();
// update wet state
setImageWetState(0, mWaterCoverage > 0.4); // more than 40 percent covered
if(mFading)
{
F32 dt = TickMs / 1000.0;
F32 newFadeET = mFadeElapsedTime + dt;
if(mFadeElapsedTime < mFadeDelay && newFadeET >= mFadeDelay)
setMaskBits(CloakMask);
mFadeElapsedTime = newFadeET;
if(mFadeElapsedTime > mFadeTime + mFadeDelay)
{
mFadeVal = F32(!mFadeOut);
mFading = false;
}
}
}
// Advance images
for (int i = 0; i < MaxMountedImages; i++)
{
if (mMountedImageList[i].dataBlock != NULL)
updateImageState(i, TickSec);
}
// Call script on trigger state changes
if (move && mDataBlock && isServerObject()) {
for (S32 i = 0; i < MaxTriggerKeys; i++) {
if (move->trigger[i] != mTrigger[i]) {
mTrigger[i] = move->trigger[i];
char buf1[20],buf2[20];
dSprintf(buf1,sizeof(buf1),"%d",i);
dSprintf(buf2,sizeof(buf2),"%d",(move->trigger[i]?1:0));
Con::executef(mDataBlock,4,"onTrigger",scriptThis(),buf1,buf2);
}
}
}
// Update the damage flash and the whiteout
//
if (mDamageFlash > 0.0)
{
mDamageFlash -= sDamageFlashDec;
if (mDamageFlash <= 0.0)
mDamageFlash = 0.0;
}
if (mWhiteOut > 0.0)
{
mWhiteOut -= sWhiteoutDec;
if (mWhiteOut <= 0.0)
mWhiteOut = 0.0;
}
}
void ShapeBase::advanceTime(F32 dt)
{
// On the client, the shape threads and images are
// advanced at framerate.
advanceThreads(dt);
updateAudioPos();
for (int i = 0; i < MaxMountedImages; i++)
if (mMountedImageList[i].dataBlock)
updateImageAnimation(i,dt);
// Cloaking takes 0.5 seconds
if (mCloaked && mCloakLevel != 1.0) {
mCloakLevel += dt * 2;
if (mCloakLevel >= 1.0)
mCloakLevel = 1.0;
} else if (!mCloaked && mCloakLevel != 0.0) {
mCloakLevel -= dt * 2;
if (mCloakLevel <= 0.0)
mCloakLevel = 0.0;
}
if(mInvincibleOn)
updateInvincibleEffect(dt);
if(mFading)
{
mFadeElapsedTime += dt;
if(mFadeElapsedTime > mFadeTime)
{
mFadeVal = F32(!mFadeOut);
mFading = false;
}
else
{
mFadeVal = mFadeElapsedTime / mFadeTime;
if(mFadeOut)
mFadeVal = 1 - mFadeVal;
}
}
}
//----------------------------------------------------------------------------
//void ShapeBase::setControllingClient(GameConnection* client)
//{
// mControllingClient = client;
//
// // piggybacks on the cloak update
// setMaskBits(CloakMask);
//}
void ShapeBase::setControllingObject(ShapeBase* obj)
{
if (obj) {
setProcessTick(false);
// Even though we don't processTick, we still need to
// process after the controller in case anyone is mounted
// on this object.
processAfter(obj);
}
else {
setProcessTick(true);
clearProcessAfter();
// Catch the case of the controlling object actually
// mounted on this object.
if (mControllingObject->mMount.object == this)
mControllingObject->processAfter(this);
}
mControllingObject = obj;
}
ShapeBase* ShapeBase::getControlObject()
{
return 0;
}
void ShapeBase::setControlObject(ShapeBase*)
{
}
bool ShapeBase::isFirstPerson()
{
// Always first person as far as the server is concerned.
if (!isGhost())
return true;
if (GameConnection* con = getControllingClient())
return con->getControlObject() == this && con->isFirstPerson();
return false;
}
// Camera: (in degrees) ------------------------------------------------------
F32 ShapeBase::getCameraFov()
{
return(mCameraFov);
}
F32 ShapeBase::getDefaultCameraFov()
{
return(mDataBlock->cameraDefaultFov);
}
bool ShapeBase::isValidCameraFov(F32 fov)
{
return((fov >= mDataBlock->cameraMinFov) && (fov <= mDataBlock->cameraMaxFov));
}
void ShapeBase::setCameraFov(F32 fov)
{
mCameraFov = mClampF(fov, mDataBlock->cameraMinFov, mDataBlock->cameraMaxFov);
}
//----------------------------------------------------------------------------
static void scopeCallback(SceneObject* obj, void *conPtr)
{
NetConnection * ptr = reinterpret_cast<NetConnection*>(conPtr);
if (obj->isScopeable())
ptr->objectInScope(obj);
}
void ShapeBase::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query)
{
// update the camera query
query->camera = this;
// bool grabEye = true;
if(GameConnection * con = dynamic_cast<GameConnection*>(cr))
{
// get the fov from the connection (in deg)
F32 fov;
if (con->getControlCameraFov(&fov))
{
query->fov = mDegToRad(fov/2);
query->sinFov = mSin(query->fov);
query->cosFov = mCos(query->fov);
}
}
// failed to query the camera info?
// if(grabEye) LH - always use eye as good enough, avoid camera animate
{
MatrixF eyeTransform;
getEyeTransform(&eyeTransform);
eyeTransform.getColumn(3, &query->pos);
eyeTransform.getColumn(1, &query->orientation);
}
// grab the visible distance from the sky
Sky * sky = gServerSceneGraph->getCurrentSky();
if(sky)
query->visibleDistance = sky->getVisibleDistance();
else
query->visibleDistance = 1000.f;
// First, we are certainly in scope, and whatever we're riding is too...
cr->objectInScope(this);
if (isMounted())
cr->objectInScope(mMount.object);
if (mSceneManager == NULL)
{
// Scope everything...
gServerContainer.findObjects(0xFFFFFFFF,scopeCallback,cr);
return;
}
// update the scenemanager
mSceneManager->scopeScene(query->pos, query->visibleDistance, cr);
// let the (game)connection do some scoping of its own (commandermap...)
cr->doneScopingScene();
}
//----------------------------------------------------------------------------
F32 ShapeBase::getEnergyLevel()
{
if (mDataBlock->inheritEnergyFromMount == false)
return mEnergy;
else if (isMounted()) {
return getObjectMount()->getEnergyLevel();
} else {
return 0.0f;
}
}
F32 ShapeBase::getEnergyValue()
{
if (mDataBlock->inheritEnergyFromMount == false) {
F32 maxEnergy = mDataBlock->maxEnergy;
if ( maxEnergy > 0.f )
return (mEnergy / mDataBlock->maxEnergy);
} else if (isMounted()) {
F32 maxEnergy = getObjectMount()->mDataBlock->maxEnergy;
if ( maxEnergy > 0.f )
return (getObjectMount()->getEnergyLevel() / maxEnergy);
}
return 0.0f;
}
void ShapeBase::setEnergyLevel(F32 energy)
{
if (mDataBlock->inheritEnergyFromMount == false) {
if (mDamageState == Enabled) {
mEnergy = (energy > mDataBlock->maxEnergy)?
mDataBlock->maxEnergy: (energy < 0)? 0: energy;
}
} else {
// Pass the set onto whatever we're mounted to...
if (isMounted())
getObjectMount()->setEnergyLevel(energy);
}
}
void ShapeBase::setDamageLevel(F32 damage)
{
if (!mDataBlock->isInvincible) {
F32 store = mDamage;
mDamage = mClampF(damage, 0.f, mDataBlock->maxDamage);
if (store != mDamage) {
updateDamageLevel();
if (isServerObject()) {
setMaskBits(DamageMask);
char delta[100];
dSprintf(delta,sizeof(delta),"%g",mDamage - store);
Con::executef(mDataBlock,3,"onDamage",scriptThis(),delta);
}
}
}
}
//----------------------------------------------------------------------------
static F32 sWaterDensity = 1;
static F32 sWaterViscosity = 15;
static F32 sWaterCoverage = 0;
static U32 sWaterType = 0;
static F32 sWaterHeight = 0.0f;
static void waterFind(SceneObject* obj, void* key)
{
ShapeBase* shape = reinterpret_cast<ShapeBase*>(key);
WaterBlock* wb = dynamic_cast<WaterBlock*>(obj);
AssertFatal(wb != NULL, "Error, not a water block!");
if (wb == NULL)
{
sWaterCoverage = 0;
return;
}
if (wb->isPointSubmergedSimple(shape->getPosition()))
{
const Box3F& wbox = obj->getWorldBox();
const Box3F& sbox = shape->getWorldBox();
sWaterType = wb->getLiquidType();
if (wbox.max.z < sbox.max.z)
sWaterCoverage = (wbox.max.z - sbox.min.z) / (sbox.max.z - sbox.min.z);
else
sWaterCoverage = 1;
sWaterViscosity = wb->getViscosity();
sWaterDensity = wb->getDensity();
sWaterHeight = wb->getSurfaceHeight();
}
}
void physicalZoneFind(SceneObject* obj, void *key)
{
ShapeBase* shape = reinterpret_cast<ShapeBase*>(key);
PhysicalZone* pz = dynamic_cast<PhysicalZone*>(obj);
AssertFatal(pz != NULL, "Error, not a physical zone!");
if (pz == NULL || pz->testObject(shape) == false) {
return;
}
if (pz->isActive()) {
shape->mGravityMod *= pz->getGravityMod();
shape->mAppliedForce += pz->getForce();
}
}
void findRouter(SceneObject* obj, void *key)
{
if (obj->getTypeMask() & WaterObjectType)
waterFind(obj, key);
else if (obj->getTypeMask() & PhysicalZoneObjectType)
physicalZoneFind(obj, key);
else {
AssertFatal(false, "Error, must be either water or physical zone here!");
}
}
void ShapeBase::updateContainer()
{
// Update container drag and buoyancy properties
mDrag = mDataBlock->drag;
mBuoyancy = 0;
sWaterCoverage = 0;
mGravityMod = 1.0;
mAppliedForce.set(0, 0, 0);
mContainer->findObjects(getWorldBox(), WaterObjectType|PhysicalZoneObjectType,findRouter,this);
sWaterCoverage = mClampF(sWaterCoverage,0,1);
mWaterCoverage = sWaterCoverage;
mLiquidType = sWaterType;
mLiquidHeight = sWaterHeight;
if (mWaterCoverage >= 0.1f) {
mDrag = mDataBlock->drag * sWaterViscosity * sWaterCoverage;
mBuoyancy = (sWaterDensity / mDataBlock->density) * sWaterCoverage;
}
}
//----------------------------------------------------------------------------
void ShapeBase::applyRepair(F32 amount)
{
// Repair increases the repair reserve
if (amount > 0 && ((mRepairReserve += amount) > mDamage))
mRepairReserve = mDamage;
}
void ShapeBase::applyDamage(F32 amount)
{
if (amount > 0)
setDamageLevel(mDamage + amount);
}
F32 ShapeBase::getDamageValue()
{
// Return a 0-1 damage value.
return mDamage / mDataBlock->maxDamage;
}
void ShapeBase::updateDamageLevel()
{
if (mDamageThread) {
// mDamage is already 0-1 on the client
if (mDamage >= mDataBlock->destroyedLevel) {
if (getDamageState() == Destroyed)
mShapeInstance->setPos(mDamageThread, 0);
else
mShapeInstance->setPos(mDamageThread, 1);
} else {
mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel);
}
}
}
//----------------------------------------------------------------------------
void ShapeBase::setDamageState(DamageState state)
{
if (mDamageState == state)
return;
const char* script = 0;
const char* lastState = 0;
if (!isGhost()) {
if (state != getDamageState())
setMaskBits(DamageMask);
lastState = getDamageStateName();
switch (state) {
case Destroyed: {
if (mDamageState == Enabled)
setDamageState(Disabled);
script = "onDestroyed";
break;
}
case Disabled:
if (mDamageState == Enabled)
script = "onDisabled";
break;
case Enabled:
script = "onEnabled";
break;
}
}
mDamageState = state;
if (mDamageState != Enabled) {
mRepairReserve = 0;
mEnergy = 0;
}
if (script) {
// Like to call the scripts after the state has been intialize.
// This should only end up being called on the server.
Con::executef(mDataBlock,3,script,scriptThis(),lastState);
}
updateDamageState();
updateDamageLevel();
}
bool ShapeBase::setDamageState(const char* state)
{
for (S32 i = 0; i < NumDamageStates; i++)
if (!dStricmp(state,sDamageStateName[i])) {
setDamageState(DamageState(i));
return true;
}
return false;
}
const char* ShapeBase::getDamageStateName()
{
return sDamageStateName[mDamageState];
}
void ShapeBase::updateDamageState()
{
if (mHulkThread) {
F32 pos = (mDamageState == Destroyed)? 1: 0;
if (mShapeInstance->getPos(mHulkThread) != pos) {
mShapeInstance->setPos(mHulkThread,pos);
if (isClientObject())
mShapeInstance->animate();
}
}
}
//----------------------------------------------------------------------------
void ShapeBase::blowUp()
{
Point3F center;
mObjBox.getCenter(&center);
center += getPosition();
MatrixF trans = getTransform();
trans.setPosition( center );
// explode
Explosion* pExplosion = NULL;
if( pointInWater( (Point3F &)center ) && mDataBlock->underwaterExplosion )
{
pExplosion = new Explosion;
pExplosion->onNewDataBlock(mDataBlock->underwaterExplosion);
}
else
{
if (mDataBlock->explosion)
{
pExplosion = new Explosion;
pExplosion->onNewDataBlock(mDataBlock->explosion);
}
}
if( pExplosion )
{
pExplosion->setTransform(trans);
pExplosion->setInitialState(center, damageDir);
if (pExplosion->registerObject() == false)
{
Con::errorf(ConsoleLogEntry::General, "ShapeBase(%s)::explode: couldn't register explosion",
mDataBlock->getName() );
delete pExplosion;
pExplosion = NULL;
}
}
TSShapeInstance *debShape = NULL;
if( mDataBlock->debrisShape.isNull() )
{
return;
}
else
{
debShape = new TSShapeInstance( mDataBlock->debrisShape, true);
}
Vector< TSPartInstance * > partList;
TSPartInstance::breakShape( debShape, 0, partList, NULL, NULL, 0 );
if( !mDataBlock->debris )
{
mDataBlock->debris = new DebrisData;
}
// cycle through partlist and create debris pieces
for( U32 i=0; i<partList.size(); i++ )
{
//Point3F axis( 0.0, 0.0, 1.0 );
Point3F randomDir = MathUtils::randomDir( damageDir, 0, 50 );
Debris *debris = new Debris;
debris->setPartInstance( partList[i] );
debris->init( center, randomDir );
debris->onNewDataBlock( mDataBlock->debris );
debris->setTransform( trans );
if( !debris->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() );
delete debris;
debris = NULL;
}
else
{
debShape->incDebrisRefCount();
}
}
damageDir.set(0, 0, 1);
}
//----------------------------------------------------------------------------
void ShapeBase::mountObject(ShapeBase* obj,U32 node)
{
// if (obj->mMount.object == this)
// return;
if (obj->mMount.object)
obj->unmount();
// Since the object is mounting to us, nothing should be colliding with it for a while
obj->mConvexList->nukeList();
obj->mMount.object = this;
obj->mMount.node = (node >= 0 && node < ShapeBaseData::NumMountPoints)? node: 0;
obj->mMount.link = mMount.list;
mMount.list = obj;
if (obj != getControllingObject())
obj->processAfter(this);
obj->deleteNotify(this);
obj->setMaskBits(MountedMask);
obj->onMount(this,node);
}
void ShapeBase::unmountObject(ShapeBase* obj)
{
if (obj->mMount.object == this) {
// Find and unlink the object
for(ShapeBase **ptr = & mMount.list; (*ptr); ptr = &((*ptr)->mMount.link) )
{
if(*ptr == obj)
{
*ptr = obj->mMount.link;
break;
}
}
if (obj != getControllingObject())
obj->clearProcessAfter();
obj->clearNotify(this);
obj->mMount.object = 0;
obj->mMount.link = 0;
obj->setMaskBits(MountedMask);
obj->onUnmount(this,obj->mMount.node);
}
}
void ShapeBase::unmount()
{
if (mMount.object)
mMount.object->unmountObject(this);
}
void ShapeBase::onMount(ShapeBase* obj,S32 node)
{
if (!isGhost()) {
char buff1[32];
dSprintf(buff1,sizeof(buff1),"%d",node);
Con::executef(mDataBlock,4,"onMount",scriptThis(),obj->scriptThis(),buff1);
}
}
void ShapeBase::onUnmount(ShapeBase* obj,S32 node)
{
if (!isGhost()) {
char buff1[32];
dSprintf(buff1,sizeof(buff1),"%d",node);
Con::executef(mDataBlock,4,"onUnmount",scriptThis(),obj->scriptThis(),buff1);
}
}
S32 ShapeBase::getMountedObjectCount()
{
S32 count = 0;
for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link)
count++;
return count;
}
ShapeBase* ShapeBase::getMountedObject(S32 idx)
{
if (idx >= 0) {
S32 count = 0;
for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link)
if (count++ == idx)
return itr;
}
return 0;
}
S32 ShapeBase::getMountedObjectNode(S32 idx)
{
if (idx >= 0) {
S32 count = 0;
for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link)
if (count++ == idx)
return itr->mMount.node;
}
return -1;
}
ShapeBase* ShapeBase::getMountNodeObject(S32 node)
{
for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link)
if (itr->mMount.node == node)
return itr;
return 0;
}
Point3F ShapeBase::getAIRepairPoint()
{
if (mDataBlock->mountPointNode[ShapeBaseData::AIRepairNode] < 0)
return Point3F(0, 0, 0);
MatrixF xf(true);
getMountTransform(ShapeBaseData::AIRepairNode,&xf);
Point3F pos(0, 0, 0);
xf.getColumn(3,&pos);
return pos;
}
//----------------------------------------------------------------------------
void ShapeBase::getEyeTransform(MatrixF* mat)
{
// Returns eye to world space transform
S32 eyeNode = mDataBlock->eyeNode;
if (eyeNode != -1)
mat->mul(getTransform(), mShapeInstance->mNodeTransforms[eyeNode]);
else
*mat = getTransform();
}
void ShapeBase::getRenderEyeTransform(MatrixF* mat)
{
// Returns eye to world space transform
S32 eyeNode = mDataBlock->eyeNode;
if (eyeNode != -1)
mat->mul(getRenderTransform(), mShapeInstance->mNodeTransforms[eyeNode]);
else
*mat = getRenderTransform();
}
void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat)
{
// Returns camera to world space transform
// Handles first person / third person camera position
if (isServerObject() && mShapeInstance)
mShapeInstance->animateNodeSubtrees(true);
if (*pos != 0)
{
F32 min,max;
Point3F offset;
MatrixF eye,rot;
getCameraParameters(&min,&max,&offset,&rot);
getRenderEyeTransform(&eye);
mat->mul(eye,rot);
// Use the eye transform to orient the camera
VectorF vp,vec;
vp.x = vp.z = 0;
vp.y = -(max - min) * *pos;
eye.mulV(vp,&vec);
// Use the camera node's pos.
Point3F osp,sp;
if (mDataBlock->cameraNode != -1) {
mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
// Scale the camera position before applying the transform
const Point3F& scale = getScale();
osp.convolve( scale );
getRenderTransform().mulP(osp,&sp);
}
else
getRenderTransform().getColumn(3,&sp);
// Make sure we don't extend the camera into anything solid
Point3F ep = sp + vec + offset;
disableCollision();
if (isMounted())
getObjectMount()->disableCollision();
RayInfo collision;
if (mContainer->castRay(sp, ep,
(0xFFFFFFFF & ~(WaterObjectType |
GameBaseObjectType |
DefaultObjectType)),
&collision) == true) {
F32 veclen = vec.len();
F32 adj = (-mDot(vec, collision.normal) / veclen) * 0.1;
F32 newPos = getMax(0.0f, collision.t - adj);
if (newPos == 0.0f)
eye.getColumn(3,&ep);
else
ep = sp + offset + (vec * newPos);
}
mat->setColumn(3,ep);
if (isMounted())
getObjectMount()->enableCollision();
enableCollision();
}
else
{
getRenderEyeTransform(mat);
}
}
// void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat)
// {
// // Returns camera to world space transform
// // Handles first person / third person camera position
// if (isServerObject() && mShapeInstance)
// mShapeInstance->animateNodeSubtrees(true);
// if (*pos != 0) {
// F32 min,max;
// Point3F offset;
// MatrixF eye,rot;
// getCameraParameters(&min,&max,&offset,&rot);
// getRenderEyeTransform(&eye);
// mat->mul(eye,rot);
// // Use the eye transform to orient the camera
// VectorF vp,vec;
// vp.x = vp.z = 0;
// vp.y = -(max - min) * *pos;
// eye.mulV(vp,&vec);
// // Use the camera node's pos.
// Point3F osp,sp;
// if (mDataBlock->cameraNode != -1) {
// mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
// getRenderTransform().mulP(osp,&sp);
// }
// else
// getRenderTransform().getColumn(3,&sp);
// // Make sure we don't extend the camera into anything solid
// Point3F ep = sp + vec;
// ep += offset;
// disableCollision();
// if (isMounted())
// getObjectMount()->disableCollision();
// RayInfo collision;
// if (mContainer->castRay(sp,ep,(0xFFFFFFFF & ~(WaterObjectType|ForceFieldObjectType|GameBaseObjectType|DefaultObjectType)),&collision)) {
// *pos = collision.t *= 0.9;
// if (*pos == 0)
// eye.getColumn(3,&ep);
// else
// ep = sp + vec * *pos;
// }
// mat->setColumn(3,ep);
// if (isMounted())
// getObjectMount()->enableCollision();
// enableCollision();
// }
// else
// {
// getRenderEyeTransform(mat);
// }
// }
// void ShapeBase::getRenderCameraTransform(F32* pos,MatrixF* mat)
// {
// // Returns camera to world space transform
// // Handles first person / third person camera position
// if (isServerObject() && mShapeInstance)
// mShapeInstance->animateNodeSubtrees(true);
// if (*pos != 0) {
// F32 min,max;
// Point3F offset;
// MatrixF eye,rot;
// getCameraParameters(&min,&max,&offset,&rot);
// getRenderEyeTransform(&eye);
// mat->mul(eye,rot);
// // Use the eye transform to orient the camera
// VectorF vp,vec;
// vp.x = vp.z = 0;
// vp.y = -(max - min) * *pos;
// eye.mulV(vp,&vec);
// // Use the camera node's pos.
// Point3F osp,sp;
// if (mDataBlock->cameraNode != -1) {
// mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
// getRenderTransform().mulP(osp,&sp);
// }
// else
// getRenderTransform().getColumn(3,&sp);
// // Make sure we don't extend the camera into anything solid
// Point3F ep = sp + vec;
// ep += offset;
// disableCollision();
// if (isMounted())
// getObjectMount()->disableCollision();
// RayInfo collision;
// if (mContainer->castRay(sp,ep,(0xFFFFFFFF & ~(WaterObjectType|ForceFieldObjectType|GameBaseObjectType|DefaultObjectType)),&collision)) {
// *pos = collision.t *= 0.9;
// if (*pos == 0)
// eye.getColumn(3,&ep);
// else
// ep = sp + vec * *pos;
// }
// mat->setColumn(3,ep);
// if (isMounted())
// getObjectMount()->enableCollision();
// enableCollision();
// }
// else
// {
// getRenderEyeTransform(mat);
// }
// }
void ShapeBase::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
{
*min = mDataBlock->cameraMinDist;
*max = mDataBlock->cameraMaxDist;
off->set(0,0,0);
rot->identity();
}
//----------------------------------------------------------------------------
F32 ShapeBase::getDamageFlash() const
{
return mDamageFlash;
}
void ShapeBase::setDamageFlash(const F32 flash)
{
mDamageFlash = flash;
if (mDamageFlash < 0.0)
mDamageFlash = 0;
else if (mDamageFlash > 1.0)
mDamageFlash = 1.0;
}
//----------------------------------------------------------------------------
F32 ShapeBase::getWhiteOut() const
{
return mWhiteOut;
}
void ShapeBase::setWhiteOut(const F32 flash)
{
mWhiteOut = flash;
if (mWhiteOut < 0.0)
mWhiteOut = 0;
else if (mWhiteOut > 1.5)
mWhiteOut = 1.5;
}
//----------------------------------------------------------------------------
bool ShapeBase::onlyFirstPerson() const
{
return mDataBlock->firstPersonOnly;
}
bool ShapeBase::useObjsEyePoint() const
{
return mDataBlock->useEyePoint;
}
//----------------------------------------------------------------------------
F32 ShapeBase::getInvincibleEffect() const
{
return mInvincibleEffect;
}
void ShapeBase::setupInvincibleEffect(F32 time, F32 speed)
{
if(isClientObject())
{
mInvincibleCount = mInvincibleTime = time;
mInvincibleSpeed = mInvincibleDelta = speed;
mInvincibleEffect = 0.0f;
mInvincibleOn = true;
mInvincibleFade = 1.0f;
}
else
{
mInvincibleTime = time;
mInvincibleSpeed = speed;
setMaskBits(InvincibleMask);
}
}
void ShapeBase::updateInvincibleEffect(F32 dt)
{
if(mInvincibleCount > 0.0f )
{
if(mInvincibleEffect >= ((0.3 * mInvincibleFade) + 0.05f) && mInvincibleDelta > 0.0f)
mInvincibleDelta = -mInvincibleSpeed;
else if(mInvincibleEffect <= 0.05f && mInvincibleDelta < 0.0f)
{
mInvincibleDelta = mInvincibleSpeed;
mInvincibleFade = mInvincibleCount / mInvincibleTime;
}
mInvincibleEffect += mInvincibleDelta;
mInvincibleCount -= dt;
}
else
{
mInvincibleEffect = 0.0f;
mInvincibleOn = false;
}
}
//----------------------------------------------------------------------------
void ShapeBase::setVelocity(const VectorF&)
{
}
void ShapeBase::applyImpulse(const Point3F&,const VectorF&)
{
}
//----------------------------------------------------------------------------
void ShapeBase::playAudio(U32 slot,AudioProfile* profile)
{
AssertFatal(slot < MaxSoundThreads,"ShapeBase::playSound: Incorrect argument");
Sound& st = mSoundThread[slot];
if (profile && (!st.play || st.profile != profile)) {
setMaskBits(SoundMaskN << slot);
st.play = true;
st.profile = profile;
updateAudioState(st);
}
}
void ShapeBase::stopAudio(U32 slot)
{
AssertFatal(slot < MaxSoundThreads,"ShapeBase::stopSound: Incorrect argument");
Sound& st = mSoundThread[slot];
if (st.play) {
st.play = false;
setMaskBits(SoundMaskN << slot);
updateAudioState(st);
}
}
void ShapeBase::updateServerAudio()
{
// Timeout non-looping sounds
for (int i = 0; i < MaxSoundThreads; i++) {
Sound& st = mSoundThread[i];
if (st.play && st.timeout && st.timeout < Sim::getCurrentTime()) {
clearMaskBits(SoundMaskN << i);
st.play = false;
}
}
}
void ShapeBase::updateAudioState(Sound& st)
{
if (st.sound) {
alxStop(st.sound);
st.sound = 0;
}
if (st.play && st.profile) {
if (isGhost()) {
if (Sim::findObject(SimObjectId(st.profile), st.profile))
st.sound = alxPlay(st.profile, &getTransform());
else
st.play = false;
}
else {
// Non-looping sounds timeout on the server
st.timeout = st.profile->mDescriptionObject->mDescription.mIsLooping? 0:
Sim::getCurrentTime() + sAudioTimeout;
}
}
else
st.play = false;
}
void ShapeBase::updateAudioPos()
{
for (int i = 0; i < MaxSoundThreads; i++)
if (AUDIOHANDLE sh = mSoundThread[i].sound)
alxSourceMatrixF(sh, &getTransform());
}
//----------------------------------------------------------------------------
bool ShapeBase::setThreadSequence(U32 slot,S32 seq,bool reset)
{
Thread& st = mScriptThread[slot];
if (st.thread && st.sequence == seq && st.state == Thread::Play)
return true;
if (seq < MaxSequenceIndex) {
setMaskBits(ThreadMaskN << slot);
st.sequence = seq;
if (reset) {
st.state = Thread::Play;
st.atEnd = false;
st.forward = true;
}
if (mShapeInstance) {
if (!st.thread)
st.thread = mShapeInstance->addThread();
mShapeInstance->setSequence(st.thread,seq,0);
stopThreadSound(st);
updateThread(st);
}
return true;
}
return false;
}
void ShapeBase::updateThread(Thread& st)
{
switch (st.state) {
case Thread::Stop:
mShapeInstance->setTimeScale(st.thread,1);
mShapeInstance->setPos(st.thread,0);
// Drop through to pause state
case Thread::Pause:
mShapeInstance->setTimeScale(st.thread,0);
stopThreadSound(st);
break;
case Thread::Play:
if (st.atEnd) {
mShapeInstance->setTimeScale(st.thread,1);
mShapeInstance->setPos(st.thread,st.forward? 1: 0);
mShapeInstance->setTimeScale(st.thread,0);
stopThreadSound(st);
}
else {
mShapeInstance->setTimeScale(st.thread,st.forward? 1: -1);
if (!st.sound)
startSequenceSound(st);
}
break;
}
}
bool ShapeBase::stopThread(U32 slot)
{
Thread& st = mScriptThread[slot];
if (st.sequence != -1 && st.state != Thread::Stop) {
setMaskBits(ThreadMaskN << slot);
st.state = Thread::Stop;
updateThread(st);
return true;
}
return false;
}
bool ShapeBase::pauseThread(U32 slot)
{
Thread& st = mScriptThread[slot];
if (st.sequence != -1 && st.state != Thread::Pause) {
setMaskBits(ThreadMaskN << slot);
st.state = Thread::Pause;
updateThread(st);
return true;
}
return false;
}
bool ShapeBase::playThread(U32 slot)
{
Thread& st = mScriptThread[slot];
if (st.sequence != -1 && st.state != Thread::Play) {
setMaskBits(ThreadMaskN << slot);
st.state = Thread::Play;
updateThread(st);
return true;
}
return false;
}
bool ShapeBase::setThreadDir(U32 slot,bool forward)
{
Thread& st = mScriptThread[slot];
if (st.sequence != -1) {
if (st.forward != forward) {
setMaskBits(ThreadMaskN << slot);
st.forward = forward;
st.atEnd = false;
updateThread(st);
}
return true;
}
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++) {
Thread& st = mScriptThread[i];
if (st.thread) {
if (!mShapeInstance->getShape()->sequences[st.sequence].isCyclic() && !st.atEnd &&
(st.forward? mShapeInstance->getPos(st.thread) >= 1.0:
mShapeInstance->getPos(st.thread) <= 0)) {
st.atEnd = true;
updateThread(st);
if (!isGhost()) {
char slot[16];
dSprintf(slot,sizeof(slot),"%d",i);
Con::executef(mDataBlock,3,"onEndSequence",scriptThis(),slot);
}
}
mShapeInstance->advanceTime(dt,st.thread);
}
}
}
//----------------------------------------------------------------------------
TSShape const* ShapeBase::getShape()
{
return mShapeInstance? mShapeInstance->getShape(): 0;
}
void ShapeBase::calcClassRenderData()
{
// This is truly lame, but I didn't want to duplicate the whole preprender logic
// in the player as well as the renderImage logic. DMM
}
bool ShapeBase::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 startZone, const bool modifyBaseState)
{
AssertFatal(modifyBaseState == false, "Error, should never be called with this parameter set");
AssertFatal(startZone == 0xFFFFFFFF, "Error, startZone should indicate -1");
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
if( ( getDamageState() == Destroyed ) && ( !mDataBlock->renderWhenDestroyed ) )
return false;
// Select detail levels on mounted items
// but... always draw the control object's mounted images
// in high detail (I can't believe I'm commenting this hack :)
F32 saveError = TSShapeInstance::smScreenError;
GameConnection *con = GameConnection::getConnectionToServer();
bool fogExemption = false;
ShapeBase *co = NULL;
if(con && ( (co = con->getControlObject()) != NULL) )
{
if(co == this || co->getObjectMount() == this)
{
TSShapeInstance::smScreenError = 0.001;
fogExemption = true;
}
}
if (state->isObjectRendered(this))
{
mLastRenderFrame = sLastRenderFrame;
// get shape detail and fog information...we might not even need to be drawn
Point3F cameraOffset;
getRenderTransform().getColumn(3,&cameraOffset);
cameraOffset -= state->getCameraPosition();
F32 dist = cameraOffset.len();
if (dist < 0.01)
dist = 0.01;
F32 fogAmount = state->getHazeAndFog(dist,cameraOffset.z);
F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
if (mShapeInstance)
DetailManager::selectPotentialDetails(mShapeInstance,dist,invScale);
if (mShapeInstance)
mShapeInstance->animate();
if ((fogAmount>0.99f && fogExemption == false) ||
(mShapeInstance && mShapeInstance->getCurrentDetail()<0) ||
(!mShapeInstance && !gShowBoundingBox)) {
// no, don't draw anything
return false;
}
for (U32 i = 0; i < MaxMountedImages; i++)
{
MountedImage& image = mMountedImageList[i];
if (image.dataBlock && image.shapeInstance)
{
DetailManager::selectPotentialDetails(image.shapeInstance,dist,invScale);
if (mCloakLevel == 0.0f && image.shapeInstance->hasSolid() && mFadeVal == 1.0f)
{
ShapeImageRenderImage* rimage = new ShapeImageRenderImage;
rimage->obj = this;
rimage->mSBase = this;
rimage->mIndex = i;
rimage->isTranslucent = false;
rimage->textureSortKey = (U32)(dsize_t)(image.dataBlock);
state->insertRenderImage(rimage);
}
if ((mCloakLevel != 0.0f || mFadeVal != 1.0f || mShapeInstance->hasTranslucency()) ||
(mMount.object == NULL))
{
ShapeImageRenderImage* rimage = new ShapeImageRenderImage;
rimage->obj = this;
rimage->mSBase = this;
rimage->mIndex = i;
rimage->isTranslucent = true;
rimage->sortType = SceneRenderImage::Point;
rimage->textureSortKey = (U32)(dsize_t)(image.dataBlock);
state->setImageRefPoint(this, rimage);
state->insertRenderImage(rimage);
}
}
}
TSShapeInstance::smScreenError = saveError;
if (mCloakLevel == 0.0f && mShapeInstance->hasSolid() && mFadeVal == 1.0f)
{
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = false;
image->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock);
state->insertRenderImage(image);
}
if ((mCloakLevel != 0.0f || mFadeVal != 1.0f || mShapeInstance->hasTranslucency()) ||
(mMount.object == NULL))
{
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::Point;
image->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock);
state->setImageRefPoint(this, image);
state->insertRenderImage(image);
}
calcClassRenderData();
}
return false;
}
void ShapeBase::renderObject(SceneState* state, SceneRenderImage* image)
{
PROFILE_START(ShapeBaseRenderObject);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
dglGetViewport(&viewport);
gClientSceneGraph->getLightManager()->sgSetupLights(this);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
state->setupObjectProjection(this);
// This is something of a hack, but since the 3space objects don't have a
// clear conception of texels/meter like the interiors do, we're sorta
// stuck. I can't even claim this is anything more scientific than eyeball
// work. DMM
F32 axis = (getObjBox().len_x() + getObjBox().len_y() + getObjBox().len_z()) / 3.0;
F32 dist = (getRenderWorldBox().getClosestPoint(state->getCameraPosition()) - state->getCameraPosition()).len();
if (dist != 0)
{
F32 projected = dglProjectRadius(dist, axis) / 25;
if (projected < (1.0 / 16.0))
{
TextureManager::setSmallTexturesActive(true);
}
}
// render shield effect
if (mCloakLevel == 0.0f && mFadeVal == 1.0f)
{
if (image->isTranslucent == true)
{
TSShapeInstance::smNoRenderNonTranslucent = true;
TSShapeInstance::smNoRenderTranslucent = false;
}
else
{
TSShapeInstance::smNoRenderNonTranslucent = false;
TSShapeInstance::smNoRenderTranslucent = true;
}
}
else
{
TSShapeInstance::smNoRenderNonTranslucent = false;
TSShapeInstance::smNoRenderTranslucent = false;
}
TSMesh::setOverrideFade( mFadeVal );
ShapeImageRenderImage* shiri = dynamic_cast<ShapeImageRenderImage*>(image);
if (shiri != NULL)
{
renderMountedImage(state, shiri);
}
else
{
renderImage(state, image);
}
TSMesh::setOverrideFade( 1.0 );
TSShapeInstance::smNoRenderNonTranslucent = false;
TSShapeInstance::smNoRenderTranslucent = false;
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
gClientSceneGraph->getLightManager()->sgResetLights();
// Debugging Bounding Box
if (!mShapeInstance || gShowBoundingBox) {
glDisable(GL_DEPTH_TEST);
Point3F box;
glPushMatrix();
dglMultMatrix(&getRenderTransform());
box = (mObjBox.min + mObjBox.max) * 0.5;
glTranslatef(box.x,box.y,box.z);
box = (mObjBox.max - mObjBox.min) * 0.5;
glScalef(box.x,box.y,box.z);
glColor3f(1, 0, 1);
wireCube(Point3F(1,1,1),Point3F(0,0,0));
glPopMatrix();
glPushMatrix();
box = (mWorldBox.min + mWorldBox.max) * 0.5;
glTranslatef(box.x,box.y,box.z);
box = (mWorldBox.max - mWorldBox.min) * 0.5;
glScalef(box.x,box.y,box.z);
glColor3f(0, 1, 1);
wireCube(Point3F(1,1,1),Point3F(0,0,0));
glPopMatrix();
for (U32 i = 0; i < MaxMountedImages; i++) {
MountedImage& image = mMountedImageList[i];
if (image.dataBlock && image.shapeInstance) {
MatrixF mat;
glPushMatrix();
getRenderImageTransform(i,&mat);
dglMultMatrix(&mat);
glColor3f(1, 0, 1);
wireCube(Point3F(0.05,0.05,0.05),Point3F(0,0,0));
glPopMatrix();
glPushMatrix();
getRenderMountTransform(i,&mat);
dglMultMatrix(&mat);
glColor3f(1, 0, 1);
wireCube(Point3F(0.05,0.05,0.05),Point3F(0,0,0));
glPopMatrix();
glPushMatrix();
getRenderMuzzleTransform(i,&mat);
dglMultMatrix(&mat);
glColor3f(1, 0, 1);
wireCube(Point3F(0.05,0.05,0.05),Point3F(0,0,0));
glPopMatrix();
}
}
glEnable(GL_DEPTH_TEST);
}
dglSetCanonicalState();
TextureManager::setSmallTexturesActive(false);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
PROFILE_END();
}
void ShapeBase::renderShadow(F32 dist, F32 fogAmount)
{
if(!mDataBlock->shadowEnable)
return;
shadows.sgRender(this, mShapeInstance, dist, fogAmount,
mDataBlock->genericShadowLevel, mDataBlock->noShadowLevel,
mDataBlock->shadowNode, mDataBlock->shadowCanMove,
mDataBlock->shadowCanAnimate);
}
void ShapeBase::renderMountedImage(SceneState* state, ShapeImageRenderImage* rimage)
{
AssertFatal(rimage->mSBase == this, "Error, wrong image");
Point3F cameraOffset;
getRenderTransform().getColumn(3,&cameraOffset);
cameraOffset -= state->getCameraPosition();
F32 dist = cameraOffset.len();
F32 fogAmount = state->getHazeAndFog(dist,cameraOffset.z);
// Mounted items
PROFILE_START(ShapeBaseRenderMounted);
MountedImage& image = mMountedImageList[rimage->mIndex];
if (image.dataBlock && image.shapeInstance && DetailManager::selectCurrentDetail(image.shapeInstance)) {
MatrixF mat;
getRenderImageTransform(rimage->mIndex, &mat);
glPushMatrix();
dglMultMatrix(&mat);
if (image.dataBlock->cloakable && mCloakLevel != 0.0)
image.shapeInstance->setAlphaAlways(0.15 + (1 - mCloakLevel) * 0.85);
else
image.shapeInstance->setAlphaAlways(1.0);
if (mCloakLevel == 0.0 && (image.dataBlock->emap && gRenderEnvMaps) && state->getEnvironmentMap().getGLName() != 0) {
image.shapeInstance->setEnvironmentMap(state->getEnvironmentMap());
image.shapeInstance->setEnvironmentMapOn(true, 1.0);
} else {
image.shapeInstance->setEnvironmentMapOn(false, 1.0);
}
image.shapeInstance->setupFog(fogAmount,state->getFogColor());
image.shapeInstance->animate();
image.shapeInstance->render();
// easiest just to shut it off here. If we're cloaked on the next frame,
// we don't want envmaps...
image.shapeInstance->setEnvironmentMapOn(false, 1.0);
glPopMatrix();
}
PROFILE_END();
}
void ShapeBase::renderImage(SceneState* state, SceneRenderImage* image)
{
glMatrixMode(GL_MODELVIEW);
// Base shape
F32 fogAmount = 0.0f;
F32 dist = 0.0f;
PROFILE_START(ShapeBaseRenderPrimary);
if (mShapeInstance && DetailManager::selectCurrentDetail(mShapeInstance)) {
glPushMatrix();
dglMultMatrix(&getRenderTransform());
glScalef(mObjScale.x,mObjScale.y,mObjScale.z);
if (mCloakLevel != 0.0) {
glMatrixMode(GL_TEXTURE);
glPushMatrix();
static U32 shiftX = 0;
static U32 shiftY = 0;
shiftX = (shiftX + 1) % 128;
shiftY = (shiftY + 1) % 127;
glTranslatef(F32(shiftX) / 127.0, F32(shiftY)/126.0, 0);
glMatrixMode(GL_MODELVIEW);
mShapeInstance->setAlphaAlways(0.125 + (1 - mCloakLevel) * 0.875);
mShapeInstance->setOverrideTexture(mCloakTexture);
}
else {
mShapeInstance->setAlphaAlways(1.0);
}
if (mCloakLevel == 0.0 && (mDataBlock->emap && gRenderEnvMaps) && state->getEnvironmentMap().getGLName() != 0) {
mShapeInstance->setEnvironmentMap(state->getEnvironmentMap());
mShapeInstance->setEnvironmentMapOn(true, 1.0);
} else {
mShapeInstance->setEnvironmentMapOn(false, 1.0);
}
Point3F cameraOffset;
getRenderTransform().getColumn(3,&cameraOffset);
cameraOffset -= state->getCameraPosition();
dist = cameraOffset.len();
fogAmount = state->getHazeAndFog(dist,cameraOffset.z);
mShapeInstance->setupFog(fogAmount,state->getFogColor());
mShapeInstance->animate();
mShapeInstance->render();
mShapeInstance->setEnvironmentMapOn(false, 1.0);
if (mCloakLevel != 0.0) {
glMatrixMode(GL_TEXTURE);
glPopMatrix();
mShapeInstance->clearOverrideTexture();
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
PROFILE_END();
PROFILE_START(ShapeBaseRenderShadow);
// Shadow...
if (mShapeInstance && mCloakLevel == 0.0 &&
mMount.object == NULL &&
image->isTranslucent == true)
{
// we are shadow enabled...
renderShadow(dist,fogAmount);
}
PROFILE_END();
}
//----------------------------------------------------------------------------
static ColorF cubeColors[8] = {
ColorF(0, 0, 0), ColorF(1, 0, 0), ColorF(0, 1, 0), ColorF(0, 0, 1),
ColorF(1, 1, 0), ColorF(1, 0, 1), ColorF(0, 1, 1), ColorF(1, 1, 1)
};
static Point3F cubePoints[8] = {
Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1),
Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1)
};
static U32 cubeFaces[6][4] = {
{ 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
{ 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
};
void ShapeBase::wireCube(const Point3F& size, const Point3F& pos)
{
glDisable(GL_CULL_FACE);
for(int i = 0; i < 6; i++) {
glBegin(GL_LINE_LOOP);
for(int vert = 0; vert < 4; vert++) {
int idx = cubeFaces[i][vert];
glVertex3f(cubePoints[idx].x * size.x + pos.x, cubePoints[idx].y * size.y + pos.y, cubePoints[idx].z * size.z + pos.z);
}
glEnd();
}
}
//----------------------------------------------------------------------------
bool ShapeBase::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
{
if (mShapeInstance)
{
RayInfo shortest;
shortest.t = 1e8;
info->object = NULL;
for (U32 i = 0; i < mDataBlock->LOSDetails.size(); i++)
{
mShapeInstance->animate(mDataBlock->LOSDetails[i]);
if (mShapeInstance->castRay(start, end, info, mDataBlock->LOSDetails[i]))
{
info->object = this;
if (info->t < shortest.t)
shortest = *info;
}
}
if (info->object == this)
{
// Copy out the shortest time...
*info = shortest;
return true;
}
}
return false;
}
//----------------------------------------------------------------------------
bool ShapeBase::buildPolyList(AbstractPolyList* polyList, const Box3F &, const SphereF &)
{
if (mShapeInstance) {
bool ret = false;
polyList->setTransform(&mObjToWorld, mObjScale);
polyList->setObject(this);
for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++)
{
mShapeInstance->buildPolyList(polyList,mDataBlock->collisionDetails[i]);
ret = true;
}
return ret;
}
return false;
}
void ShapeBase::buildConvex(const Box3F& box, Convex* convex)
{
if (mShapeInstance == NULL)
return;
// These should really come out of a pool
mConvexList->collectGarbage();
Box3F realBox = box;
mWorldToObj.mul(realBox);
realBox.min.convolveInverse(mObjScale);
realBox.max.convolveInverse(mObjScale);
if (realBox.isOverlapped(getObjBox()) == false)
return;
for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++)
{
Box3F newbox = mDataBlock->collisionBounds[i];
newbox.min.convolve(mObjScale);
newbox.max.convolve(mObjScale);
mObjToWorld.mul(newbox);
if (box.isOverlapped(newbox) == false)
continue;
// See if this hull exists in the working set already...
Convex* cc = 0;
CollisionWorkingList& wl = convex->getWorkingList();
for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
if (itr->mConvex->getType() == ShapeBaseConvexType &&
(static_cast<ShapeBaseConvex*>(itr->mConvex)->pShapeBase == this &&
static_cast<ShapeBaseConvex*>(itr->mConvex)->hullId == i)) {
cc = itr->mConvex;
break;
}
}
if (cc)
continue;
// Create a new convex.
ShapeBaseConvex* cp = new ShapeBaseConvex;
mConvexList->registerObject(cp);
convex->addToWorkingList(cp);
cp->mObject = this;
cp->pShapeBase = this;
cp->hullId = i;
cp->box = mDataBlock->collisionBounds[i];
cp->transform = 0;
cp->findNodeTransform();
}
}
//----------------------------------------------------------------------------
void ShapeBase::queueCollision(ShapeBase* obj, const VectorF& vec)
{
// Add object to list of collisions.
SimTime time = Sim::getCurrentTime();
S32 num = obj->getId();
CollisionTimeout** adr = &mTimeoutList;
CollisionTimeout* ptr = mTimeoutList;
while (ptr) {
if (ptr->objectNumber == num) {
if (ptr->expireTime < time) {
ptr->expireTime = time + CollisionTimeoutValue;
ptr->object = obj;
ptr->vector = vec;
}
return;
}
// Recover expired entries
if (ptr->expireTime < time) {
CollisionTimeout* cur = ptr;
*adr = ptr->next;
ptr = ptr->next;
cur->next = sFreeTimeoutList;
sFreeTimeoutList = cur;
}
else {
adr = &ptr->next;
ptr = ptr->next;
}
}
// New entry for the object
if (sFreeTimeoutList != NULL)
{
ptr = sFreeTimeoutList;
sFreeTimeoutList = ptr->next;
ptr->next = NULL;
}
else
{
ptr = sTimeoutChunker.alloc();
}
ptr->object = obj;
ptr->objectNumber = obj->getId();
ptr->vector = vec;
ptr->expireTime = time + CollisionTimeoutValue;
ptr->next = mTimeoutList;
mTimeoutList = ptr;
}
void ShapeBase::notifyCollision()
{
// Notify all the objects that were just stamped during the queueing
// process.
SimTime expireTime = Sim::getCurrentTime() + CollisionTimeoutValue;
for (CollisionTimeout* ptr = mTimeoutList; ptr; ptr = ptr->next)
{
if (ptr->expireTime == expireTime && ptr->object)
{
SimObjectPtr<ShapeBase> safePtr(ptr->object);
SimObjectPtr<ShapeBase> safeThis(this);
onCollision(ptr->object,ptr->vector);
ptr->object = 0;
if(!bool(safeThis))
return;
if(bool(safePtr))
safePtr->onCollision(this,ptr->vector);
if(!bool(safeThis))
return;
}
}
}
void ShapeBase::onCollision(ShapeBase* object,VectorF vec)
{
if (!isGhost()) {
char buff1[256];
char buff2[32];
dSprintf(buff1,sizeof(buff1),"%g %g %g",vec.x, vec.y, vec.z);
dSprintf(buff2,sizeof(buff2),"%g",vec.len());
Con::executef(mDataBlock,5,"onCollision",scriptThis(),object->scriptThis(), buff1, buff2);
}
}
//--------------------------------------------------------------------------
bool ShapeBase::pointInWater( Point3F &point )
{
SimpleQueryList sql;
if (isServerObject())
gServerSceneGraph->getWaterObjectList(sql);
else
gClientSceneGraph->getWaterObjectList(sql);
for (U32 i = 0; i < sql.mList.size(); i++)
{
WaterBlock* pBlock = dynamic_cast<WaterBlock*>(sql.mList[i]);
if (pBlock && pBlock->isPointSubmergedSimple( point ))
return true;
}
return false;
}
//----------------------------------------------------------------------------
void ShapeBase::writePacketData(GameConnection *connection, BitStream *stream)
{
Parent::writePacketData(connection, stream);
stream->write(getEnergyLevel());
stream->write(mRechargeRate);
}
void ShapeBase::readPacketData(GameConnection *connection, BitStream *stream)
{
Parent::readPacketData(connection, stream);
F32 energy;
stream->read(&energy);
setEnergyLevel(energy);
stream->read(&mRechargeRate);
}
U32 ShapeBase::getPacketDataChecksum(GameConnection *connection)
{
// just write the packet data into a buffer
// then we can CRC the buffer. This should always let us
// know when there is a checksum problem.
static U8 buffer[1500] = { 0, };
BitStream stream(buffer, sizeof(buffer));
writePacketData(connection, &stream);
U32 byteCount = stream.getPosition();
U32 ret = calculateCRC(buffer, byteCount, 0xFFFFFFFF);
dMemset(buffer, 0, byteCount);
return ret;
}
F32 ShapeBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips)
{
// If it's the scope object, must be high priority
if (camInfo->camera == this) {
// Most priorities are between 0 and 1, so this
// should be something larger.
return 10.0f;
}
if (camInfo->camera && (camInfo->camera->getType() & ShapeBaseObjectType))
{
// see if the camera is mounted to this...
// if it is, this should have a high priority
if(((ShapeBase *) camInfo->camera)->getObjectMount() == this)
return 10.0f;
}
return Parent::getUpdatePriority(camInfo, updateMask, updateSkips);
}
U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
if (mask & InitialUpdateMask) {
// mask off sounds that aren't playing
S32 i;
for (i = 0; i < MaxSoundThreads; i++)
if (!mSoundThread[i].play)
mask &= ~(SoundMaskN << i);
// mask off threads that aren't running
for (i = 0; i < MaxScriptThreads; i++)
if (mScriptThread[i].sequence == -1)
mask &= ~(ThreadMaskN << i);
// mask off images that aren't updated
for(i = 0; i < MaxMountedImages; i++)
if(!mMountedImageList[i].dataBlock)
mask &= ~(ImageMaskN << i);
}
if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask |
ThreadMask | ImageMask | CloakMask | MountedMask | InvincibleMask |
ShieldMask | SkinMask)))
return retMask;
if (stream->writeFlag(mask & DamageMask)) {
stream->writeFloat(mClampF(mDamage / mDataBlock->maxDamage, 0.f, 1.f), DamageLevelBits);
stream->writeInt(mDamageState,NumDamageStateBits);
stream->writeNormalVector( damageDir, 8 );
}
if (stream->writeFlag(mask & ThreadMask)) {
for (int i = 0; i < MaxScriptThreads; i++) {
Thread& st = mScriptThread[i];
if (stream->writeFlag(st.sequence != -1 && (mask & (ThreadMaskN << i)))) {
stream->writeInt(st.sequence,ThreadSequenceBits);
stream->writeInt(st.state,2);
stream->writeFlag(st.forward);
stream->writeFlag(st.atEnd);
}
}
}
if (stream->writeFlag(mask & SoundMask)) {
for (int i = 0; i < MaxSoundThreads; i++) {
Sound& st = mSoundThread[i];
if (stream->writeFlag(mask & (SoundMaskN << i)))
if (stream->writeFlag(st.play))
stream->writeRangedU32(st.profile->getId(),DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
}
if (stream->writeFlag(mask & ImageMask)) {
for (int i = 0; i < MaxMountedImages; i++)
if (stream->writeFlag(mask & (ImageMaskN << i))) {
MountedImage& image = mMountedImageList[i];
if (stream->writeFlag(image.dataBlock))
stream->writeInt(image.dataBlock->getId() - DataBlockObjectIdFirst,
DataBlockObjectIdBitSize);
con->packStringHandleU(stream, image.skinNameHandle);
stream->writeFlag(image.wet);
stream->writeFlag(image.ammo);
stream->writeFlag(image.loaded);
stream->writeFlag(image.target);
stream->writeFlag(image.triggerDown);
stream->writeInt(image.fireCount,3);
if (mask & InitialUpdateMask)
stream->writeFlag(isImageFiring(i));
}
}
// Group some of the uncommon stuff together.
if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask))) {
if (stream->writeFlag(mask & CloakMask)) {
// cloaking
stream->writeFlag( mCloaked );
// piggyback control update
stream->writeFlag(bool(getControllingClient()));
// fading
if(stream->writeFlag(mFading && mFadeElapsedTime >= mFadeDelay)) {
stream->writeFlag(mFadeOut);
stream->write(mFadeTime);
}
else
stream->writeFlag(mFadeVal == 1.0f);
}
if (stream->writeFlag(mask & NameMask)) {
con->packStringHandleU(stream, mShapeNameHandle);
}
if (stream->writeFlag(mask & ShieldMask)) {
stream->writeNormalVector(mShieldNormal, ShieldNormalBits);
stream->writeFloat( getEnergyValue(), EnergyLevelBits );
}
if (stream->writeFlag(mask & InvincibleMask)) {
stream->write(mInvincibleTime);
stream->write(mInvincibleSpeed);
}
if (stream->writeFlag(mask & SkinMask)) {
con->packStringHandleU(stream, mSkinNameHandle);
}
}
if (mask & MountedMask) {
if (mMount.object) {
S32 gIndex = con->getGhostIndex(mMount.object);
if (stream->writeFlag(gIndex != -1)) {
stream->writeFlag(true);
stream->writeInt(gIndex,NetConnection::GhostIdBitSize);
stream->writeInt(mMount.node,ShapeBaseData::NumMountPointBits);
}
else
// Will have to try again later
retMask |= MountedMask;
}
else
// Unmount if this isn't the initial packet
if (stream->writeFlag(!(mask & InitialUpdateMask)))
stream->writeFlag(false);
}
else
stream->writeFlag(false);
return retMask;
}
void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con, stream);
mLastRenderFrame = sLastRenderFrame; // make sure we get a process after the event...
if(!stream->readFlag())
return;
if (stream->readFlag()) {
mDamage = mClampF(stream->readFloat(DamageLevelBits) * mDataBlock->maxDamage, 0.f, mDataBlock->maxDamage);
DamageState prevState = mDamageState;
mDamageState = DamageState(stream->readInt(NumDamageStateBits));
stream->readNormalVector( &damageDir, 8 );
if (prevState != Destroyed && mDamageState == Destroyed && isProperlyAdded())
blowUp();
updateDamageLevel();
updateDamageState();
}
if (stream->readFlag()) {
for (S32 i = 0; i < MaxScriptThreads; i++) {
if (stream->readFlag()) {
Thread& st = mScriptThread[i];
U32 seq = stream->readInt(ThreadSequenceBits);
st.state = stream->readInt(2);
st.forward = stream->readFlag();
st.atEnd = stream->readFlag();
if (st.sequence != seq)
setThreadSequence(i,seq,false);
else
updateThread(st);
}
}
}
if (stream->readFlag()) {
for (S32 i = 0; i < MaxSoundThreads; i++) {
if (stream->readFlag()) {
Sound& st = mSoundThread[i];
if ((st.play = stream->readFlag()) == true) {
st.profile = (AudioProfile*) stream->readRangedU32(DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
if (isProperlyAdded())
updateAudioState(st);
}
}
}
if (stream->readFlag()) {
for (int i = 0; i < MaxMountedImages; i++) {
if (stream->readFlag()) {
MountedImage& image = mMountedImageList[i];
ShapeBaseImageData* imageData = 0;
if (stream->readFlag()) {
SimObjectId id = stream->readInt(DataBlockObjectIdBitSize) +
DataBlockObjectIdFirst;
if (!Sim::findObject(id,imageData)) {
con->setLastError("Invalid packet (mounted images).");
return;
}
}
StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream);
image.wet = stream->readFlag();
image.ammo = stream->readFlag();
image.loaded = stream->readFlag();
image.target = stream->readFlag();
image.triggerDown = stream->readFlag();
int count = stream->readInt(3);
if ((image.dataBlock != imageData) || (image.skinNameHandle != skinDesiredNameHandle)) {
setImage(i, imageData, skinDesiredNameHandle, image.loaded, image.ammo, image.triggerDown);
}
if (isProperlyAdded()) {
// Normal processing
if (count != image.fireCount)
{
image.fireCount = count;
setImageState(i,getImageFireState(i),true);
if( imageData && imageData->lightType == ShapeBaseImageData::WeaponFireLight )
{
mLightTime = Sim::getCurrentTime();
}
}
updateImageState(i,0);
}
else
{
bool firing = stream->readFlag();
if(imageData)
{
// Initial state
image.fireCount = count;
if (firing)
setImageState(i,getImageFireState(i),true);
}
}
}
}
}
if (stream->readFlag())
{
if(stream->readFlag()) // Cloaked and control
{
setCloakedState(stream->readFlag());
mIsControlled = stream->readFlag();
if (( mFading = stream->readFlag()) == true) {
mFadeOut = stream->readFlag();
if(mFadeOut)
mFadeVal = 1.0f;
else
mFadeVal = 0;
stream->read(&mFadeTime);
mFadeDelay = 0;
mFadeElapsedTime = 0;
}
else
mFadeVal = F32(stream->readFlag());
}
if (stream->readFlag()) { // NameMask
mShapeNameHandle = con->unpackStringHandleU(stream);
}
if(stream->readFlag()) // ShieldMask
{
// Cloaking, Shield, and invul masking
Point3F shieldNormal;
stream->readNormalVector(&shieldNormal, ShieldNormalBits);
F32 energyPercent = stream->readFloat(EnergyLevelBits);
}
if (stream->readFlag()) { // InvincibleMask
F32 time, speed;
stream->read(&time);
stream->read(&speed);
setupInvincibleEffect(time, speed);
}
if (stream->readFlag()) { // SkinMask
StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream);;
if (mSkinNameHandle != skinDesiredNameHandle) {
mSkinNameHandle = skinDesiredNameHandle;
if (mShapeInstance) {
mShapeInstance->reSkin(mSkinNameHandle);
if (mSkinNameHandle.isValidString()) {
mSkinHash = _StringTable::hashString(mSkinNameHandle.getString());
}
}
}
}
}
if (stream->readFlag()) {
if (stream->readFlag()) {
S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
ShapeBase* obj = dynamic_cast<ShapeBase*>(con->resolveGhost(gIndex));
S32 node = stream->readInt(ShapeBaseData::NumMountPointBits);
if(!obj)
{
con->setLastError("Invalid packet from server.");
return;
}
obj->mountObject(this,node);
}
else
unmount();
}
}
//--------------------------------------------------------------------------
void ShapeBase::forceUncloak(const char * reason)
{
AssertFatal(isServerObject(), "ShapeBase::forceUncloak: server only call");
if(!mCloaked)
return;
Con::executef(mDataBlock, 3, "onForceUncloak", scriptThis(), reason ? reason : "");
}
void ShapeBase::setCloakedState(bool cloaked)
{
if (cloaked == mCloaked)
return;
if (isServerObject())
setMaskBits(CloakMask);
// Have to do this for the client, if we are ghosted over in the initial
// packet as cloaked, we set the state immediately to the extreme
if (isProperlyAdded() == false) {
mCloaked = cloaked;
if (mCloaked)
mCloakLevel = 1.0;
else
mCloakLevel = 0.0;
} else {
mCloaked = cloaked;
}
}
//--------------------------------------------------------------------------
void ShapeBase::setHidden(bool hidden)
{
if (hidden != mHidden) {
// need to set a mask bit to make the ghost manager delete copies of this object
// hacky, but oh well.
setMaskBits(CloakMask);
if (mHidden)
addToScene();
else
removeFromScene();
mHidden = hidden;
}
}
//--------------------------------------------------------------------------
void ShapeBaseConvex::findNodeTransform()
{
S32 dl = pShapeBase->mDataBlock->collisionDetails[hullId];
TSShapeInstance* si = pShapeBase->getShapeInstance();
TSShape* shape = si->getShape();
const TSShape::Detail* detail = &shape->details[dl];
const S32 subs = detail->subShapeNum;
const S32 start = shape->subShapeFirstObject[subs];
const S32 end = start + shape->subShapeNumObjects[subs];
// Find the first object that contains a mesh for this
// detail level. There should only be one mesh per
// collision detail level.
for (S32 i = start; i < end; i++)
{
const TSShape::Object* obj = &shape->objects[i];
if (obj->numMeshes && detail->objectDetailNum < obj->numMeshes)
{
nodeTransform = &si->mNodeTransforms[obj->nodeIndex];
return;
}
}
return;
}
const MatrixF& ShapeBaseConvex::getTransform() const
{
// If the transform isn't specified, it's assumed to be the
// origin of the shape.
const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform();
// Multiply on the mesh shape offset
// tg: Returning this static here is not really a good idea, but
// all this Convex code needs to be re-organized.
if (nodeTransform) {
static MatrixF mat;
mat.mul(omat,*nodeTransform);
return mat;
}
return omat;
}
Box3F ShapeBaseConvex::getBoundingBox() const
{
const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform();
return getBoundingBox(omat, mObject->getScale());
}
Box3F ShapeBaseConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
{
Box3F newBox = box;
newBox.min.convolve(scale);
newBox.max.convolve(scale);
mat.mul(newBox);
return newBox;
}
Point3F ShapeBaseConvex::support(const VectorF& v) const
{
TSShape::ConvexHullAccelerator* pAccel =
pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]);
AssertFatal(pAccel != NULL, "Error, no accel!");
F32 currMaxDP = mDot(pAccel->vertexList[0], v);
U32 index = 0;
for (U32 i = 1; i < pAccel->numVerts; i++) {
F32 dp = mDot(pAccel->vertexList[i], v);
if (dp > currMaxDP) {
currMaxDP = dp;
index = i;
}
}
return pAccel->vertexList[index];
}
void ShapeBaseConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf)
{
cf->material = 0;
cf->object = mObject;
TSShape::ConvexHullAccelerator* pAccel =
pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]);
AssertFatal(pAccel != NULL, "Error, no accel!");
F32 currMaxDP = mDot(pAccel->vertexList[0], n);
U32 index = 0;
U32 i;
for (i = 1; i < pAccel->numVerts; i++) {
F32 dp = mDot(pAccel->vertexList[i], n);
if (dp > currMaxDP) {
currMaxDP = dp;
index = i;
}
}
const U8* emitString = pAccel->emitStrings[index];
U32 currPos = 0;
U32 numVerts = emitString[currPos++];
for (i = 0; i < numVerts; i++) {
cf->mVertexList.increment();
U32 index = emitString[currPos++];
mat.mulP(pAccel->vertexList[index], &cf->mVertexList.last());
}
U32 numEdges = emitString[currPos++];
for (i = 0; i < numEdges; i++) {
U32 ev0 = emitString[currPos++];
U32 ev1 = emitString[currPos++];
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = ev0;
cf->mEdgeList.last().vertex[1] = ev1;
}
U32 numFaces = emitString[currPos++];
for (i = 0; i < numFaces; i++) {
cf->mFaceList.increment();
U32 plane = emitString[currPos++];
mat.mulV(pAccel->normalList[plane], &cf->mFaceList.last().normal);
for (U32 j = 0; j < 3; j++)
cf->mFaceList.last().vertex[j] = emitString[currPos++];
}
}
void ShapeBaseConvex::getPolyList(AbstractPolyList* list)
{
list->setTransform(&pShapeBase->getTransform(), pShapeBase->getScale());
list->setObject(pShapeBase);
pShapeBase->mShapeInstance->animate(pShapeBase->mDataBlock->collisionDetails[hullId]);
pShapeBase->mShapeInstance->buildPolyList(list,pShapeBase->mDataBlock->collisionDetails[hullId]);
}
//--------------------------------------------------------------------------
bool ShapeBase::isInvincible()
{
if( mDataBlock )
{
return mDataBlock->isInvincible;
}
return false;
}
void ShapeBase::startFade( F32 fadeTime, F32 fadeDelay, bool fadeOut )
{
setMaskBits(CloakMask);
mFadeElapsedTime = 0;
mFading = true;
if(fadeDelay < 0)
fadeDelay = 0;
if(fadeTime < 0)
fadeTime = 0;
mFadeTime = fadeTime;
mFadeDelay = fadeDelay;
mFadeOut = fadeOut;
mFadeVal = F32(mFadeOut);
}
//--------------------------------------------------------------------------
void ShapeBase::setShapeName(const char* name)
{
if (!isGhost()) {
if (name[0] != '\0') {
// Use tags for better network performance
// Should be a tag, but we'll convert to one if it isn't.
if (name[0] == StringTagPrefixByte)
mShapeNameHandle = StringHandle(U32(dAtoi(name + 1)));
else
mShapeNameHandle = StringHandle(name);
}
else {
mShapeNameHandle = StringHandle();
}
setMaskBits(NameMask);
}
}
void ShapeBase::setSkinName(const char* name)
{
if (!isGhost()) {
if (name[0] != '\0') {
// Use tags for better network performance
// Should be a tag, but we'll convert to one if it isn't.
if (name[0] == StringTagPrefixByte) {
mSkinNameHandle = StringHandle(U32(dAtoi(name + 1)));
}
else {
mSkinNameHandle = StringHandle(name);
}
}
else {
mSkinNameHandle = StringHandle();
}
setMaskBits(SkinMask);
}
}
//--------------------------------------------------------------------------
//----------------------------------------------------------------------------
ConsoleMethod( ShapeBase, setHidden, void, 3, 3, "(bool show)")
{
object->setHidden(dAtob(argv[2]));
}
ConsoleMethod( ShapeBase, isHidden, bool, 2, 2, "")
{
return object->isHidden();
}
//----------------------------------------------------------------------------
ConsoleMethod( ShapeBase, playAudio, bool, 4, 4, "(int slot, AudioProfile ap)")
{
U32 slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxSoundThreads) {
AudioProfile* profile;
if (Sim::findObject(argv[3],profile)) {
object->playAudio(slot,profile);
return true;
}
}
return false;
}
ConsoleMethod( ShapeBase, stopAudio, bool, 3, 3, "(int slot)")
{
U32 slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxSoundThreads) {
object->stopAudio(slot);
return true;
}
return false;
}
//----------------------------------------------------------------------------
ConsoleMethod( ShapeBase, playThread, bool, 3, 4, "(int slot, string sequenceName)")
{
U32 slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
if (argc == 4) {
if (object->getShape()) {
S32 seq = object->getShape()->findSequence(argv[3]);
if (seq != -1 && object->setThreadSequence(slot,seq))
return true;
}
}
else
if (object->playThread(slot))
return true;
}
return false;
}
ConsoleMethod( ShapeBase, setThreadDir, bool, 4, 4, "(int slot, bool isForward)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
if (object->setThreadDir(slot,dAtob(argv[3])))
return true;
}
return false;
}
ConsoleMethod( ShapeBase, stopThread, bool, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
if (object->stopThread(slot))
return true;
}
return false;
}
ConsoleMethod( ShapeBase, pauseThread, bool, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) {
if (object->pauseThread(slot))
return true;
}
return false;
}
//----------------------------------------------------------------------------
ConsoleMethod( ShapeBase, mountObject, bool, 4, 4, "( ShapeBase object, int slot )"
"Mount ourselves on an object in the specified slot.")
{
ShapeBase *target;
if (Sim::findObject(argv[2],target)) {
S32 node = -1;
dSscanf(argv[3],"%d",&node);
if (node >= 0 && node < ShapeBaseData::NumMountPoints)
object->mountObject(target,node);
return true;
}
return false;
}
ConsoleMethod( ShapeBase, unmountObject, bool, 3, 3, "(ShapeBase obj)"
"Unmount an object from ourselves.")
{
ShapeBase *target;
if (Sim::findObject(argv[2],target)) {
object->unmountObject(target);
return true;
}
return false;
}
ConsoleMethod( ShapeBase, unmount, void, 2, 2, "Unmount from the currently mounted object if any.")
{
object->unmount();
}
ConsoleMethod( ShapeBase, isMounted, bool, 2, 2, "Are we mounted?")
{
return object->isMounted();
}
ConsoleMethod( ShapeBase, getObjectMount, S32, 2, 2, "Returns the ShapeBase we're mounted on.")
{
return object->isMounted()? object->getObjectMount()->getId(): 0;
}
ConsoleMethod( ShapeBase, getMountedObjectCount, S32, 2, 2, "")
{
return object->getMountedObjectCount();
}
ConsoleMethod( ShapeBase, getMountedObject, S32, 3, 3, "(int slot)")
{
ShapeBase* mobj = object->getMountedObject(dAtoi(argv[2]));
return mobj? mobj->getId(): 0;
}
ConsoleMethod( ShapeBase, getMountedObjectNode, S32, 3, 3, "(int node)")
{
return object->getMountedObjectNode(dAtoi(argv[2]));
}
ConsoleMethod( ShapeBase, getMountNodeObject, S32, 3, 3, "(int node)")
{
ShapeBase* mobj = object->getMountNodeObject(dAtoi(argv[2]));
return mobj? mobj->getId(): 0;
}
//----------------------------------------------------------------------------
ConsoleMethod( ShapeBase, mountImage, bool, 4, 6, "(ShapeBaseImageData image, int slot, bool loaded=true, string skinTag=NULL)")
{
ShapeBaseImageData* imageData;
if (Sim::findObject(argv[2],imageData)) {
U32 slot = dAtoi(argv[3]);
bool loaded = (argc == 5)? dAtob(argv[4]): true;
StringHandle team;
if(argc == 6)
{
if(argv[5][0] == StringTagPrefixByte)
team = StringHandle(U32(dAtoi(argv[5]+1)));
}
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
object->mountImage(imageData,slot,loaded,team);
}
return false;
}
ConsoleMethod( ShapeBase, unmountImage, bool, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
return object->unmountImage(slot);
return false;
}
ConsoleMethod( ShapeBase, getMountedImage, S32, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
if (ShapeBaseImageData* data = object->getMountedImage(slot))
return data->getId();
return 0;
}
ConsoleMethod( ShapeBase, getPendingImage, S32, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
if (ShapeBaseImageData* data = object->getPendingImage(slot))
return data->getId();
return 0;
}
ConsoleMethod( ShapeBase, isImageFiring, bool, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
return object->isImageFiring(slot);
return false;
}
ConsoleMethod( ShapeBase, isImageMounted, bool, 3, 3, "(ShapeBaseImageData db)")
{
ShapeBaseImageData* imageData;
if (Sim::findObject(argv[2],imageData))
return object->isImageMounted(imageData);
return false;
}
ConsoleMethod( ShapeBase, getMountSlot, S32, 3, 3, "(ShapeBaseImageData db)")
{
ShapeBaseImageData* imageData;
if (Sim::findObject(argv[2],imageData))
return object->getMountSlot(imageData);
return -1;
}
ConsoleMethod( ShapeBase, getImageSkinTag, S32, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
return object->getImageSkinTag(slot).getIndex();
return -1;
}
ConsoleMethod( ShapeBase, getImageState, const char*, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
return object->getImageState(slot);
return "Error";
}
ConsoleMethod( ShapeBase, getImageTrigger, bool, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
return object->getImageTriggerState(slot);
return false;
}
ConsoleMethod( ShapeBase, setImageTrigger, bool, 4, 4, "(int slot, bool isTriggered)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
object->setImageTriggerState(slot,dAtob(argv[3]));
return object->getImageTriggerState(slot);
}
return false;
}
ConsoleMethod( ShapeBase, getImageAmmo, bool, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
return object->getImageAmmoState(slot);
return false;
}
ConsoleMethod( ShapeBase, setImageAmmo, bool, 4, 4, "(int slot, bool hasAmmo)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
bool ammo = dAtob(argv[3]);
object->setImageAmmoState(slot,dAtob(argv[3]));
return ammo;
}
return false;
}
ConsoleMethod( ShapeBase, getImageLoaded, bool, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
return object->getImageLoadedState(slot);
return false;
}
ConsoleMethod( ShapeBase, setImageLoaded, bool, 4, 4, "(int slot, bool loaded)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
bool loaded = dAtob(argv[3]);
object->setImageLoadedState(slot, dAtob(argv[3]));
return loaded;
}
return false;
}
ConsoleMethod( ShapeBase, getMuzzleVector, const char*, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
VectorF v;
object->getMuzzleVector(slot,&v);
char* buff = Con::getReturnBuffer(100);
dSprintf(buff,100,"%g %g %g",v.x,v.y,v.z);
return buff;
}
return "0 1 0";
}
ConsoleMethod( ShapeBase, getMuzzlePoint, const char*, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages) {
Point3F p;
object->getMuzzlePoint(slot,&p);
char* buff = Con::getReturnBuffer(100);
dSprintf(buff,100,"%g %g %g",p.x,p.y,p.z);
return buff;
}
return "0 0 0";
}
ConsoleMethod( ShapeBase, getSlotTransform, const char*, 3, 3, "(int slot)")
{
int slot = dAtoi(argv[2]);
MatrixF xf(true);
if (slot >= 0 && slot < ShapeBase::MaxMountedImages)
object->getMountTransform(slot,&xf);
Point3F pos;
xf.getColumn(3,&pos);
AngAxisF aa(xf);
char* buff = Con::getReturnBuffer(200);
dSprintf(buff,200,"%g %g %g %g %g %g %g",
pos.x,pos.y,pos.z,aa.axis.x,aa.axis.y,aa.axis.z,aa.angle);
return buff;
}
ConsoleMethod( ShapeBase, getAIRepairPoint, const char*, 2, 2, "Get the position at which the AI should stand to repair things.")
{
Point3F pos = object->getAIRepairPoint();
char* buff = Con::getReturnBuffer(200);
dSprintf(buff,200,"%g %g %g", pos.x,pos.y,pos.z);
return buff;
}
ConsoleMethod( ShapeBase, getVelocity, const char *, 2, 2, "")
{
const VectorF& vel = object->getVelocity();
char* buff = Con::getReturnBuffer(100);
dSprintf(buff,100,"%g %g %g",vel.x,vel.y,vel.z);
return buff;
}
ConsoleMethod( ShapeBase, setVelocity, bool, 3, 3, "(Vector3F vel)")
{
VectorF vel(0,0,0);
dSscanf(argv[2],"%g %g %g",&vel.x,&vel.y,&vel.z);
object->setVelocity(vel);
return true;
}
ConsoleMethod( ShapeBase, applyImpulse, bool, 4, 4, "(Point3F Pos, VectorF vel)")
{
Point3F pos(0,0,0);
VectorF vel(0,0,0);
dSscanf(argv[2],"%g %g %g",&pos.x,&pos.y,&pos.z);
dSscanf(argv[3],"%g %g %g",&vel.x,&vel.y,&vel.z);
object->applyImpulse(pos,vel);
return true;
}
ConsoleMethod( ShapeBase, getEyeVector, const char*, 2, 2, "")
{
MatrixF mat;
object->getEyeTransform(&mat);
VectorF v2;
mat.getColumn(1,&v2);
char* buff = Con::getReturnBuffer(100);
dSprintf(buff, 100,"%g %g %g",v2.x,v2.y,v2.z);
return buff;
}
ConsoleMethod( ShapeBase, getEyePoint, const char*, 2, 2, "")
{
MatrixF mat;
object->getEyeTransform(&mat);
Point3F ep;
mat.getColumn(3,&ep);
char* buff = Con::getReturnBuffer(100);
dSprintf(buff, 100,"%g %g %g",ep.x,ep.y,ep.z);
return buff;
}
ConsoleMethod( ShapeBase, getEyeTransform, const char*, 2, 2, "")
{
MatrixF mat;
object->getEyeTransform(&mat);
Point3F pos;
mat.getColumn(3,&pos);
AngAxisF aa(mat);
char* buff = Con::getReturnBuffer(100);
dSprintf(buff,100,"%g %g %g %g %g %g %g",
pos.x,pos.y,pos.z,aa.axis.x,aa.axis.y,aa.axis.z,aa.angle);
return buff;
}
ConsoleMethod( ShapeBase, setEnergyLevel, void, 3, 3, "(float level)")
{
object->setEnergyLevel(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, getEnergyLevel, F32, 2, 2, "")
{
return object->getEnergyLevel();
}
ConsoleMethod( ShapeBase, getEnergyPercent, F32, 2, 2, "")
{
return object->getEnergyValue();
}
ConsoleMethod( ShapeBase, setDamageLevel, void, 3, 3, "(float level)")
{
object->setDamageLevel(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, getDamageLevel, F32, 2, 2, "")
{
return object->getDamageLevel();
}
ConsoleMethod( ShapeBase, getDamagePercent, F32, 2, 2, "")
{
return object->getDamageValue();
}
ConsoleMethod( ShapeBase, setDamageState, bool, 3, 3, "(string state)")
{
return object->setDamageState(argv[2]);
}
ConsoleMethod( ShapeBase, getDamageState, const char*, 2, 2, "")
{
return object->getDamageStateName();
}
ConsoleMethod( ShapeBase, isDestroyed, bool, 2, 2, "")
{
return object->isDestroyed();
}
ConsoleMethod( ShapeBase, isDisabled, bool, 2, 2, "True if the state is not Enabled.")
{
return object->getDamageState() != ShapeBase::Enabled;
}
ConsoleMethod( ShapeBase, isEnabled, bool, 2, 2, "")
{
return object->getDamageState() == ShapeBase::Enabled;
}
ConsoleMethod( ShapeBase, applyDamage, void, 3, 3, "(float amt)")
{
object->applyDamage(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, applyRepair, void, 3, 3, "(float amt)")
{
object->applyRepair(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, setRepairRate, void, 3, 3, "(float amt)")
{
F32 rate = dAtof(argv[2]);
if(rate < 0)
rate = 0;
object->setRepairRate(rate);
}
ConsoleMethod( ShapeBase, getRepairRate, F32, 2, 2, "")
{
return object->getRepairRate();
}
ConsoleMethod( ShapeBase, setRechargeRate, void, 3, 3, "(float rate)")
{
object->setRechargeRate(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, getRechargeRate, F32, 2, 2, "")
{
return object->getRechargeRate();
}
ConsoleMethod( ShapeBase, getControllingClient, S32, 2, 2, "Returns a GameConnection.")
{
if (GameConnection* con = object->getControllingClient())
return con->getId();
return 0;
}
ConsoleMethod( ShapeBase, getControllingObject, S32, 2, 2, "")
{
if (ShapeBase* con = object->getControllingObject())
return con->getId();
return 0;
}
// return true if can cloak, otherwise the reason why object cannot cloak
ConsoleMethod( ShapeBase, canCloak, bool, 2, 2, "")
{
return true;
}
ConsoleMethod( ShapeBase, setCloaked, void, 3, 3, "(bool isCloaked)")
{
bool cloaked = dAtob(argv[2]);
if (object->isServerObject())
object->setCloakedState(cloaked);
}
ConsoleMethod( ShapeBase, isCloaked, bool, 2, 2, "")
{
return object->getCloakedState();
}
ConsoleMethod( ShapeBase, setDamageFlash, void, 3, 3, "(float lvl)")
{
F32 flash = dAtof(argv[2]);
if (object->isServerObject())
object->setDamageFlash(flash);
}
ConsoleMethod( ShapeBase, getDamageFlash, F32, 2, 2, "")
{
return object->getDamageFlash();
}
ConsoleMethod( ShapeBase, setWhiteOut, void, 3, 3, "(float flashLevel)")
{
F32 flash = dAtof(argv[2]);
if (object->isServerObject())
object->setWhiteOut(flash);
}
ConsoleMethod( ShapeBase, getWhiteOut, F32, 2, 2, "")
{
return object->getWhiteOut();
}
ConsoleMethod( ShapeBase, getCameraFov, F32, 2, 2, "")
{
if (object->isServerObject())
return object->getCameraFov();
return 0.0;
}
ConsoleMethod( ShapeBase, setCameraFov, void, 3, 3, "(float fov)")
{
if (object->isServerObject())
object->setCameraFov(dAtof(argv[2]));
}
ConsoleMethod( ShapeBase, setInvincibleMode, void, 4, 4, "(float time, float speed)")
{
object->setupInvincibleEffect(dAtof(argv[2]), dAtof(argv[3]));
}
ConsoleFunction(setShadowDetailLevel, void , 2, 2, "setShadowDetailLevel(val 0...1);")
{
argc;
F32 val = dAtof(argv[1]);
if (val < 0.0f)
val = 0.0f;
else if (val > 1.0f)
val = 1.0f;
if (mFabs(Shadow::getGlobalShadowDetailLevel()-val)<0.001f)
return;
// shadow details determined in two places:
// 1. setGlobalShadowDetailLevel
// 2. static shape header has some #defines that determine
// at what level of shadow detail each type of
// object uses a generic shadow or no shadow at all
Shadow::setGlobalShadowDetailLevel(val);
Con::setFloatVariable("$pref::Shadows", val);
}
ConsoleMethod( ShapeBase, startFade, void, 5, 5, "( int fadeTimeMS, int fadeDelayMS, bool fadeOut )")
{
U32 fadeTime;
U32 fadeDelay;
bool fadeOut;
dSscanf(argv[2], "%d", &fadeTime );
dSscanf(argv[3], "%d", &fadeDelay );
fadeOut = dAtob(argv[4]);
object->startFade( fadeTime / 1000.0, fadeDelay / 1000.0, fadeOut );
}
ConsoleMethod( ShapeBase, setDamageVector, void, 3, 3, "(Vector3F origin)")
{
VectorF normal;
dSscanf(argv[2], "%g %g %g", &normal.x, &normal.y, &normal.z);
normal.normalize();
object->setDamageDir(VectorF(normal.x, normal.y, normal.z));
}
ConsoleMethod( ShapeBase, setShapeName, void, 3, 3, "(string tag)")
{
object->setShapeName(argv[2]);
}
ConsoleMethod( ShapeBase, setSkinName, void, 3, 3, "(string tag)")
{
object->setSkinName(argv[2]);
}
ConsoleMethod( ShapeBase, getShapeName, const char*, 2, 2, "")
{
return object->getShapeName();
}
ConsoleMethod( ShapeBase, getSkinName, const char*, 2, 2, "")
{
return object->getSkinName();
}
//----------------------------------------------------------------------------
void ShapeBase::consoleInit()
{
Con::addVariable("SB::DFDec", TypeF32, &sDamageFlashDec);
Con::addVariable("SB::WODec", TypeF32, &sWhiteoutDec);
Con::addVariable("pref::environmentMaps", TypeBool, &gRenderEnvMaps);
}