123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "T3D/item.h"
- #include "core/stream/bitStream.h"
- #include "math/mMath.h"
- #include "console/console.h"
- #include "console/consoleTypes.h"
- #include "sim/netConnection.h"
- #include "collision/boxConvex.h"
- #include "collision/earlyOutPolyList.h"
- #include "collision/extrudedPolyList.h"
- #include "math/mPolyhedron.h"
- #include "math/mathIO.h"
- #include "lighting/lightInfo.h"
- #include "lighting/lightManager.h"
- #include "T3D/physics/physicsPlugin.h"
- #include "T3D/physics/physicsBody.h"
- #include "T3D/physics/physicsCollision.h"
- #include "ts/tsShapeInstance.h"
- #include "console/engineAPI.h"
- const F32 sRotationSpeed = 6.0f; // Secs/Rotation
- const F32 sAtRestVelocity = 0.15f; // Min speed after collision
- const S32 sCollisionTimeout = 15; // Timout value in ticks
- // Client prediction
- static F32 sMinWarpTicks = 0.5 ; // Fraction of tick at which instant warp occures
- static S32 sMaxWarpTicks = 3; // Max warp duration in ticks
- const U32 sClientCollisionMask = (TerrainObjectType |
- StaticShapeObjectType |
- VehicleObjectType |
- PlayerObjectType);
- const U32 sServerCollisionMask = (sClientCollisionMask);
- const S32 Item::csmAtRestTimer = 64;
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_DATABLOCK_V1(ItemData);
- ConsoleDocClass( ItemData,
- "@brief Stores properties for an individual Item type.\n\n"
- "Items represent an object in the world, usually one that the player will interact with. "
- "One example is a health kit on the group that is automatically picked up when the player "
- "comes into contact with it.\n\n"
- "ItemData provides the common properties for a set of Items. These properties include a "
- "DTS or DAE model used to render the Item in the world, its physical properties for when the "
- "Item interacts with the world (such as being tossed by the player), and any lights that emit "
- "from the Item.\n\n"
- "@tsexample\n"
- "datablock ItemData(HealthKitSmall)\n"
- "{\n"
- " category =\"Health\";\n"
- " className = \"HealthPatch\";\n"
- " shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n"
- " gravityMod = \"1.0\";\n"
- " mass = 2;\n"
- " friction = 1;\n"
- " elasticity = 0.3;\n"
- " density = 2;\n"
- " drag = 0.5;\n"
- " maxVelocity = \"10.0\";\n"
- " emap = true;\n"
- " sticky = false;\n"
- " dynamicType = \"0\"\n;"
- " lightOnlyStatic = false;\n"
- " lightType = \"NoLight\";\n"
- " lightColor = \"1.0 1.0 1.0 1.0\";\n"
- " lightTime = 1000;\n"
- " lightRadius = 10.0;\n"
- " simpleServerCollision = true;"
- " // Dynamic properties used by the scripts\n\n"
- " pickupName = \"a small health kit\";\n"
- " repairAmount = 50;\n"
- "};\n"
- "@endtsexample\n"
- "@ingroup gameObjects\n"
- );
- ItemData::ItemData()
- {
- shadowEnable = true;
- friction = 0;
- elasticity = 0;
- sticky = false;
- gravityMod = 1.0;
- maxVelocity = 25.0f;
- density = 2;
- drag = 0.5;
- lightOnlyStatic = false;
- lightType = Item::NoLight;
- lightColor.set(1.f,1.f,1.f,1.f);
- lightTime = 1000;
- lightRadius = 10.f;
- simpleServerCollision = true;
- }
- ImplementEnumType( ItemLightType,
- "@brief The type of light the Item has\n\n"
- "@ingroup gameObjects\n\n")
- { Item::NoLight, "NoLight", "The item has no light attached.\n" },
- { Item::ConstantLight, "ConstantLight", "The item has a constantly emitting light attached.\n" },
- { Item::PulsingLight, "PulsingLight", "The item has a pulsing light attached.\n" }
- EndImplementEnumType;
- void ItemData::initPersistFields()
- {
- addField("friction", TypeF32, Offset(friction, ItemData), "A floating-point value specifying how much velocity is lost to impact and sliding friction.");
- addField("elasticity", TypeF32, Offset(elasticity, ItemData), "A floating-point value specifying how 'bouncy' this ItemData is.");
- addField("sticky", TypeBool, Offset(sticky, ItemData),
- "@brief If true, ItemData will 'stick' to any surface it collides with.\n\n"
- "When an item does stick to a surface, the Item::onStickyCollision() callback is called. The Item has methods to retrieve "
- "the world position and normal the Item is stuck to.\n"
- "@note Valid objects to stick to must be of StaticShapeObjectType.\n");
- addField("gravityMod", TypeF32, Offset(gravityMod, ItemData), "Floating point value to multiply the existing gravity with, just for this ItemData.");
- addField("maxVelocity", TypeF32, Offset(maxVelocity, ItemData), "Maximum velocity that this ItemData is able to move.");
- addField("lightType", TYPEID< Item::LightType >(), Offset(lightType, ItemData), "Type of light to apply to this ItemData. Options are NoLight, ConstantLight, PulsingLight. Default is NoLight." );
- addField("lightColor", TypeColorF, Offset(lightColor, ItemData),
- "@brief Color value to make this light. Example: \"1.0,1.0,1.0\"\n\n"
- "@see lightType\n");
- addField("lightTime", TypeS32, Offset(lightTime, ItemData),
- "@brief Time value for the light of this ItemData, used to control the pulse speed of the PulsingLight LightType.\n\n"
- "@see lightType\n");
- addField("lightRadius", TypeF32, Offset(lightRadius, ItemData),
- "@brief Distance from the center point of this ItemData for the light to affect\n\n"
- "@see lightType\n");
- addField("lightOnlyStatic", TypeBool, Offset(lightOnlyStatic, ItemData),
- "@brief If true, this ItemData will only cast a light if the Item for this ItemData has a static value of true.\n\n"
- "@see lightType\n");
- addField("simpleServerCollision", TypeBool, Offset(simpleServerCollision, ItemData),
- "@brief Determines if only simple server-side collision will be used (for pick ups).\n\n"
- "If set to true then only simple, server-side collision detection will be used. This is often the case "
- "if the item is used for a pick up object, such as ammo. If set to false then a full collision volume "
- "will be used as defined by the shape. The default is true.\n"
- "@note Only applies when using a physics library.\n"
- "@see TurretShape and ProximityMine for examples that should set this to false to allow them to be "
- "shot by projectiles.\n");
- Parent::initPersistFields();
- }
- void ItemData::packData(BitStream* stream)
- {
- Parent::packData(stream);
- stream->writeFloat(friction, 10);
- stream->writeFloat(elasticity, 10);
- stream->writeFlag(sticky);
- if(stream->writeFlag(gravityMod != 1.0))
- stream->writeFloat(gravityMod, 10);
- if(stream->writeFlag(maxVelocity != -1))
- stream->write(maxVelocity);
- if(stream->writeFlag(lightType != Item::NoLight))
- {
- AssertFatal(Item::NumLightTypes < (1 << 2), "ItemData: light type needs more bits");
- stream->writeInt(lightType, 2);
- stream->writeFloat(lightColor.red, 7);
- stream->writeFloat(lightColor.green, 7);
- stream->writeFloat(lightColor.blue, 7);
- stream->writeFloat(lightColor.alpha, 7);
- stream->write(lightTime);
- stream->write(lightRadius);
- stream->writeFlag(lightOnlyStatic);
- }
- stream->writeFlag(simpleServerCollision);
- }
- void ItemData::unpackData(BitStream* stream)
- {
- Parent::unpackData(stream);
- friction = stream->readFloat(10);
- elasticity = stream->readFloat(10);
- sticky = stream->readFlag();
- if(stream->readFlag())
- gravityMod = stream->readFloat(10);
- else
- gravityMod = 1.0;
- if(stream->readFlag())
- stream->read(&maxVelocity);
- else
- maxVelocity = -1;
- if(stream->readFlag())
- {
- lightType = stream->readInt(2);
- lightColor.red = stream->readFloat(7);
- lightColor.green = stream->readFloat(7);
- lightColor.blue = stream->readFloat(7);
- lightColor.alpha = stream->readFloat(7);
- stream->read(&lightTime);
- stream->read(&lightRadius);
- lightOnlyStatic = stream->readFlag();
- }
- else
- lightType = Item::NoLight;
- simpleServerCollision = stream->readFlag();
- }
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_NETOBJECT_V1(Item);
- ConsoleDocClass( Item,
- "@brief Base Item class. Uses the ItemData datablock for common properties.\n\n"
- "Items represent an object in the world, usually one that the player will interact with. "
- "One example is a health kit on the group that is automatically picked up when the player "
- "comes into contact with it.\n\n"
- "@tsexample\n"
- "// This is the \"health patch\" dropped by a dying player.\n"
- "datablock ItemData(HealthKitPatch)\n"
- "{\n"
- " // Mission editor category, this datablock will show up in the\n"
- " // specified category under the \"shapes\" root category.\n"
- " category = \"Health\";\n\n"
- " className = \"HealthPatch\";\n\n"
- " // Basic Item properties\n"
- " shapeFile = \"art/shapes/items/patch/healthpatch.dts\";\n"
- " mass = 2;\n"
- " friction = 1;\n"
- " elasticity = 0.3;\n"
- " emap = true;\n\n"
- " // Dynamic properties used by the scripts\n"
- " pickupName = \"a health patch\";\n"
- " repairAmount = 50;\n"
- "};\n\n"
- "%obj = new Item()\n"
- "{\n"
- " dataBlock = HealthKitSmall;\n"
- " parentGroup = EWCreatorWindow.objectGroup;\n"
- " static = true;\n"
- " rotate = true;\n"
- "};\n"
- "@endtsexample\n\n"
- "@see ItemData\n"
- "@ingroup gameObjects\n"
- );
- IMPLEMENT_CALLBACK( Item, onStickyCollision, void, ( const char* objID ),( objID ),
- "@brief Informs the Item object that it is now sticking to another object.\n\n"
- "This callback is only called if the ItemData::sticky property for this Item is true.\n"
- "@param objID Object ID this Item object.\n"
- "@note Server side only.\n"
- "@see Item, ItemData\n"
- );
- IMPLEMENT_CALLBACK( Item, onEnterLiquid, void, ( const char* objID, F32 waterCoverage, const char* liquidType ),( objID, waterCoverage, liquidType ),
- "Informs an Item object that it has entered liquid, along with information about the liquid type.\n"
- "@param objID Object ID for this Item object.\n"
- "@param waterCoverage How much coverage of water this Item object has.\n"
- "@param liquidType The type of liquid that this Item object has entered.\n"
- "@note Server side only.\n"
- "@see Item, ItemData, WaterObject\n"
- );
- IMPLEMENT_CALLBACK( Item, onLeaveLiquid, void, ( const char* objID, const char* liquidType ),( objID, liquidType ),
- "Informs an Item object that it has left a liquid, along with information about the liquid type.\n"
- "@param objID Object ID for this Item object.\n"
- "@param liquidType The type of liquid that this Item object has left.\n"
- "@note Server side only.\n"
- "@see Item, ItemData, WaterObject\n"
- );
- Item::Item()
- {
- mTypeMask |= ItemObjectType | DynamicShapeObjectType;
- mDataBlock = 0;
- mStatic = false;
- mRotate = false;
- mVelocity = VectorF(0,0,0);
- mAtRest = true;
- mAtRestCounter = 0;
- mInLiquid = false;
- mDelta.warpTicks = 0;
- mDelta.dt = 1;
- mCollisionObject = 0;
- mCollisionTimeout = 0;
- mPhysicsRep = NULL;
- mConvex.init(this);
- mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
- mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
- mLight = NULL;
- mSubclassItemHandlesScene = false;
- }
- Item::~Item()
- {
- SAFE_DELETE(mLight);
- }
- //----------------------------------------------------------------------------
- bool Item::onAdd()
- {
- if (!Parent::onAdd() || !mDataBlock)
- return false;
- if (mStatic)
- mAtRest = true;
- mObjToWorld.getColumn(3,&mDelta.pos);
- // Setup the box for our convex object...
- mObjBox.getCenter(&mConvex.mCenter);
- mConvex.mSize.x = mObjBox.len_x() / 2.0;
- mConvex.mSize.y = mObjBox.len_y() / 2.0;
- mConvex.mSize.z = mObjBox.len_z() / 2.0;
- mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
- mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
- if( !isHidden() && !mSubclassItemHandlesScene )
- addToScene();
- if (isServerObject())
- {
- if (!mSubclassItemHandlesScene)
- scriptOnAdd();
- }
- else if (mDataBlock->lightType != NoLight)
- {
- mDropTime = Sim::getCurrentTime();
- }
- _updatePhysics();
- return true;
- }
- void Item::_updatePhysics()
- {
- SAFE_DELETE( mPhysicsRep );
- if ( !PHYSICSMGR )
- return;
- if (mDataBlock->simpleServerCollision)
- {
- // We only need the trigger on the server.
- if ( isServerObject() )
- {
- PhysicsCollision *colShape = PHYSICSMGR->createCollision();
- colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity );
- PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
- mPhysicsRep = PHYSICSMGR->createBody();
- mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world );
- mPhysicsRep->setTransform( getTransform() );
- }
- }
- else
- {
- if ( !mShapeInstance )
- return;
- PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() );
- if ( colShape )
- {
- PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
- mPhysicsRep = PHYSICSMGR->createBody();
- mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world );
- mPhysicsRep->setTransform( getTransform() );
- }
- }
- }
- bool Item::onNewDataBlock( GameBaseData *dptr, bool reload )
- {
- mDataBlock = dynamic_cast<ItemData*>(dptr);
- if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
- return false;
- if (!mSubclassItemHandlesScene)
- scriptOnNewDataBlock();
- if ( isProperlyAdded() )
- _updatePhysics();
- return true;
- }
- void Item::onRemove()
- {
- mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
- mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);
- SAFE_DELETE( mPhysicsRep );
- if (!mSubclassItemHandlesScene)
- {
- scriptOnRemove();
- removeFromScene();
- }
- Parent::onRemove();
- }
- void Item::onDeleteNotify( SimObject *obj )
- {
- if ( obj == mCollisionObject )
- {
- mCollisionObject = NULL;
- mCollisionTimeout = 0;
- }
- Parent::onDeleteNotify( obj );
- }
- // Lighting: -----------------------------------------------------------------
- void Item::registerLights(LightManager * lightManager, bool lightingScene)
- {
- if(lightingScene)
- return;
- if(mDataBlock->lightOnlyStatic && !mStatic)
- return;
- F32 intensity;
- switch(mDataBlock->lightType)
- {
- case ConstantLight:
- intensity = mFadeVal;
- break;
- case PulsingLight:
- {
- S32 delta = Sim::getCurrentTime() - mDropTime;
- intensity = 0.5f + 0.5f * mSin(M_PI_F * F32(delta) / F32(mDataBlock->lightTime));
- intensity = 0.15f + intensity * 0.85f;
- intensity *= mFadeVal; // fade out light on flags
- break;
- }
- default:
- return;
- }
- // Create a light if needed
- if (!mLight)
- {
- mLight = lightManager->createLightInfo();
- }
- mLight->setColor( mDataBlock->lightColor * intensity );
- mLight->setType( LightInfo::Point );
- mLight->setRange( mDataBlock->lightRadius );
- mLight->setPosition( getBoxCenter() );
- lightManager->registerGlobalLight( mLight, this );
- }
- //----------------------------------------------------------------------------
- Point3F Item::getVelocity() const
- {
- return mVelocity;
- }
- void Item::setVelocity(const VectorF& vel)
- {
- mVelocity = vel;
- // Clamp against the maximum velocity.
- if ( mDataBlock->maxVelocity > 0 )
- {
- F32 len = mVelocity.magnitudeSafe();
- if ( len > mDataBlock->maxVelocity )
- {
- Point3F excess = mVelocity * ( 1.0f - (mDataBlock->maxVelocity / len ) );
- mVelocity -= excess;
- }
- }
- setMaskBits(PositionMask);
- mAtRest = false;
- mAtRestCounter = 0;
- }
- void Item::applyImpulse(const Point3F&,const VectorF& vec)
- {
- // Items ignore angular velocity
- VectorF vel;
- vel.x = vec.x / mDataBlock->mass;
- vel.y = vec.y / mDataBlock->mass;
- vel.z = vec.z / mDataBlock->mass;
- setVelocity(vel);
- }
- void Item::setCollisionTimeout(ShapeBase* obj)
- {
- if (mCollisionObject)
- clearNotify(mCollisionObject);
- deleteNotify(obj);
- mCollisionObject = obj;
- mCollisionTimeout = sCollisionTimeout;
- setMaskBits(ThrowSrcMask);
- }
- //----------------------------------------------------------------------------
- void Item::processTick(const Move* move)
- {
- Parent::processTick(move);
- if ( isMounted() )
- return;
- //
- if (mCollisionObject && !--mCollisionTimeout)
- mCollisionObject = 0;
- // Warp to catch up to server
- if (mDelta.warpTicks > 0)
- {
- mDelta.warpTicks--;
- // Set new pos.
- MatrixF mat = mObjToWorld;
- mat.getColumn(3,&mDelta.pos);
- mDelta.pos += mDelta.warpOffset;
- mat.setColumn(3, mDelta.pos);
- Parent::setTransform(mat);
- // Backstepping
- mDelta.posVec.x = -mDelta.warpOffset.x;
- mDelta.posVec.y = -mDelta.warpOffset.y;
- mDelta.posVec.z = -mDelta.warpOffset.z;
- }
- else
- {
- if (isServerObject() && mAtRest && (mStatic == false && mDataBlock->sticky == false))
- {
- if (++mAtRestCounter > csmAtRestTimer)
- {
- mAtRest = false;
- mAtRestCounter = 0;
- setMaskBits(PositionMask);
- }
- }
- if (!mStatic && !mAtRest && isHidden() == false)
- {
- updateVelocity(TickSec);
- updateWorkingCollisionSet(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
- updatePos(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec);
- }
- else
- {
- // Need to clear out last updatePos or warp interpolation
- mDelta.posVec.set(0,0,0);
- }
- }
- }
- void Item::interpolateTick(F32 dt)
- {
- Parent::interpolateTick(dt);
- if ( isMounted() )
- return;
- // Client side interpolation
- Point3F pos = mDelta.pos + mDelta.posVec * dt;
- MatrixF mat = mRenderObjToWorld;
- mat.setColumn(3,pos);
- setRenderTransform(mat);
- mDelta.dt = dt;
- // PATHSHAPE
- updateRenderChangesByParent();
- // PATHSHAPE END
- }
- //----------------------------------------------------------------------------
- void Item::setTransform(const MatrixF& mat)
- {
- Point3F pos;
- mat.getColumn(3,&pos);
- MatrixF tmat;
- if (!mRotate) {
- // Forces all rotation to be around the z axis
- VectorF vec;
- mat.getColumn(1,&vec);
- tmat.set(EulerF(0,0,-mAtan2(-vec.x,vec.y)));
- }
- else
- tmat.identity();
- tmat.setColumn(3,pos);
- Parent::setTransform(tmat);
- if (!mStatic)
- {
- mAtRest = false;
- mAtRestCounter = 0;
- }
- if ( mPhysicsRep )
- mPhysicsRep->setTransform( getTransform() );
- setMaskBits(RotationMask | PositionMask | NoWarpMask);
- }
- //----------------------------------------------------------------------------
- void Item::updateWorkingCollisionSet(const U32 mask, const F32 dt)
- {
- // It is assumed that we will never accelerate more than 10 m/s for gravity...
- //
- Point3F scaledVelocity = mVelocity * dt;
- F32 len = scaledVelocity.len();
- F32 newLen = len + (10 * dt);
- // Check to see if it is actually necessary to construct the new working list,
- // or if we can use the cached version from the last query. We use the x
- // component of the min member of the mWorkingQueryBox, which is lame, but
- // it works ok.
- bool updateSet = false;
- Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
- F32 l = (newLen * 1.1) + 0.1; // from Convex::updateWorkingList
- convexBox.minExtents -= Point3F(l, l, l);
- convexBox.maxExtents += Point3F(l, l, l);
- // Check containment
- {
- if (mWorkingQueryBox.minExtents.x != -1e9)
- {
- if (mWorkingQueryBox.isContained(convexBox) == false)
- {
- // Needed region is outside the cached region. Update it.
- updateSet = true;
- }
- else
- {
- // We can leave it alone, we're still inside the cached region
- }
- }
- else
- {
- // Must update
- updateSet = true;
- }
- }
- // Actually perform the query, if necessary
- if (updateSet == true)
- {
- mWorkingQueryBox = convexBox;
- mWorkingQueryBox.minExtents -= Point3F(2 * l, 2 * l, 2 * l);
- mWorkingQueryBox.maxExtents += Point3F(2 * l, 2 * l, 2 * l);
- disableCollision();
- if (mCollisionObject)
- mCollisionObject->disableCollision();
- mConvex.updateWorkingList(mWorkingQueryBox, mask);
- if (mCollisionObject)
- mCollisionObject->enableCollision();
- enableCollision();
- }
- }
- void Item::updateVelocity(const F32 dt)
- {
- // Container buoyancy & drag
- // Acceleration due to gravity
- mVelocity.z += (mNetGravity * mDataBlock->gravityMod) * dt;
- mVelocity -= mVelocity * mDrag * dt;
- // Add in physical zone force
- mVelocity += mAppliedForce;
- F32 len;
- if (mDataBlock->maxVelocity > 0 && (len = mVelocity.len()) > (mDataBlock->maxVelocity * 1.05)) {
- Point3F excess = mVelocity * (1.0 - (mDataBlock->maxVelocity / len ));
- excess *= 0.1f;
- mVelocity -= excess;
- }
- }
- void Item::updatePos(const U32 /*mask*/, const F32 dt)
- {
- // Try and move
- Point3F pos;
- mObjToWorld.getColumn(3,&pos);
- mDelta.posVec = pos;
- bool contact = false;
- bool nonStatic = false;
- bool stickyNotify = false;
- CollisionList collisionList;
- F32 time = dt;
- static Polyhedron sBoxPolyhedron;
- static ExtrudedPolyList sExtrudedPolyList;
- static EarlyOutPolyList sEarlyOutPolyList;
- MatrixF collisionMatrix(true);
- Point3F end = pos + mVelocity * time;
- U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask;
- // Part of our speed problem here is that we don't track contact surfaces, like we do
- // with the player. In order to handle the most common and performance impacting
- // instance of this problem, we'll use a ray cast to detect any contact surfaces below
- // us. This won't be perfect, but it only needs to catch a few of these to make a
- // big difference. We'll cast from the top center of the bounding box at the tick's
- // beginning to the bottom center of the box at the end.
- Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
- (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
- mObjBox.maxExtents.z);
- Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
- (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
- mObjBox.minExtents.z);
- collisionMatrix.setColumn(3, pos);
- collisionMatrix.mulP(startCast);
- collisionMatrix.setColumn(3, end);
- collisionMatrix.mulP(endCast);
- RayInfo rinfo;
- bool doToughCollision = true;
- disableCollision();
- if (mCollisionObject)
- mCollisionObject->disableCollision();
- if (getContainer()->castRay(startCast, endCast, mask, &rinfo))
- {
- F32 bd = -mDot(mVelocity, rinfo.normal);
- if (bd >= 0.0)
- {
- // Contact!
- if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
- mVelocity.set(0, 0, 0);
- mAtRest = true;
- mAtRestCounter = 0;
- stickyNotify = true;
- mStickyCollisionPos = rinfo.point;
- mStickyCollisionNormal = rinfo.normal;
- doToughCollision = false;;
- } else {
- // Subtract out velocity into surface and friction
- VectorF fv = mVelocity + rinfo.normal * bd;
- F32 fvl = fv.len();
- if (fvl) {
- F32 ff = bd * mDataBlock->friction;
- if (ff < fvl) {
- fv *= ff / fvl;
- fvl = ff;
- }
- }
- bd *= 1 + mDataBlock->elasticity;
- VectorF dv = rinfo.normal * (bd + 0.002);
- mVelocity += dv;
- mVelocity -= fv;
- // Keep track of what we hit
- contact = true;
- U32 typeMask = rinfo.object->getTypeMask();
- if (!(typeMask & StaticObjectType))
- nonStatic = true;
- if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
- ShapeBase* col = static_cast<ShapeBase*>(rinfo.object);
- queueCollision(col,mVelocity - col->getVelocity());
- }
- }
- }
- }
- enableCollision();
- if (mCollisionObject)
- mCollisionObject->enableCollision();
- if (doToughCollision)
- {
- U32 count;
- for (count = 0; count < 3; count++)
- {
- // Build list from convex states here...
- end = pos + mVelocity * time;
- collisionMatrix.setColumn(3, end);
- Box3F wBox = getObjBox();
- collisionMatrix.mul(wBox);
- Box3F testBox = wBox;
- Point3F oldMin = testBox.minExtents;
- Point3F oldMax = testBox.maxExtents;
- testBox.minExtents.setMin(oldMin + (mVelocity * time));
- testBox.maxExtents.setMin(oldMax + (mVelocity * time));
- sEarlyOutPolyList.clear();
- sEarlyOutPolyList.mNormal.set(0,0,0);
- sEarlyOutPolyList.mPlaneList.setSize(6);
- sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0));
- sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0));
- sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0));
- sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0));
- sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1));
- sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1));
- CollisionWorkingList& eorList = mConvex.getWorkingList();
- CollisionWorkingList* eopList = eorList.wLink.mNext;
- while (eopList != &eorList) {
- if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0)
- {
- Box3F convexBox = eopList->mConvex->getBoundingBox();
- if (testBox.isOverlapped(convexBox))
- {
- eopList->mConvex->getPolyList(&sEarlyOutPolyList);
- if (sEarlyOutPolyList.isEmpty() == false)
- break;
- }
- }
- eopList = eopList->wLink.mNext;
- }
- if (sEarlyOutPolyList.isEmpty())
- {
- pos = end;
- break;
- }
- collisionMatrix.setColumn(3, pos);
- sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true);
- // Build extruded polyList...
- VectorF vector = end - pos;
- sExtrudedPolyList.extrude(sBoxPolyhedron, vector);
- sExtrudedPolyList.setVelocity(mVelocity);
- sExtrudedPolyList.setCollisionList(&collisionList);
- CollisionWorkingList& rList = mConvex.getWorkingList();
- CollisionWorkingList* pList = rList.wLink.mNext;
- while (pList != &rList) {
- if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0)
- {
- Box3F convexBox = pList->mConvex->getBoundingBox();
- if (testBox.isOverlapped(convexBox))
- {
- pList->mConvex->getPolyList(&sExtrudedPolyList);
- }
- }
- pList = pList->wLink.mNext;
- }
- if (collisionList.getTime() < 1.0)
- {
- // Set to collision point
- F32 cdt = time * collisionList.getTime();
- pos += mVelocity * cdt;
- time -= cdt;
- // Pick the most resistant surface
- F32 bd = 0;
- const Collision* collision = 0;
- for (S32 c = 0; c < collisionList.getCount(); c++) {
- const Collision &cp = collisionList[c];
- F32 dot = -mDot(mVelocity,cp.normal);
- if (dot > bd) {
- bd = dot;
- collision = &cp;
- }
- }
- if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
- mVelocity.set(0, 0, 0);
- mAtRest = true;
- mAtRestCounter = 0;
- stickyNotify = true;
- mStickyCollisionPos = collision->point;
- mStickyCollisionNormal = collision->normal;
- break;
- } else {
- // Subtract out velocity into surface and friction
- if (collision) {
- VectorF fv = mVelocity + collision->normal * bd;
- F32 fvl = fv.len();
- if (fvl) {
- F32 ff = bd * mDataBlock->friction;
- if (ff < fvl) {
- fv *= ff / fvl;
- fvl = ff;
- }
- }
- bd *= 1 + mDataBlock->elasticity;
- VectorF dv = collision->normal * (bd + 0.002);
- mVelocity += dv;
- mVelocity -= fv;
- // Keep track of what we hit
- contact = true;
- U32 typeMask = collision->object->getTypeMask();
- if (!(typeMask & StaticObjectType))
- nonStatic = true;
- if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
- ShapeBase* col = static_cast<ShapeBase*>(collision->object);
- queueCollision(col,mVelocity - col->getVelocity());
- }
- }
- }
- }
- else
- {
- pos = end;
- break;
- }
- }
- if (count == 3)
- {
- // Couldn't move...
- mVelocity.set(0, 0, 0);
- }
- }
- // If on the client, calculate delta for backstepping
- if (isGhost()) {
- mDelta.pos = pos;
- mDelta.posVec -= pos;
- mDelta.dt = 1;
- }
- // Update transform
- MatrixF mat = mObjToWorld;
- mat.setColumn(3,pos);
- Parent::setTransform(mat);
- enableCollision();
- if (mCollisionObject)
- mCollisionObject->enableCollision();
- updateContainer();
- if ( mPhysicsRep )
- mPhysicsRep->setTransform( mat );
- //
- if (contact) {
- // Check for rest condition
- if (!nonStatic && mVelocity.len() < sAtRestVelocity) {
- mVelocity.x = mVelocity.y = mVelocity.z = 0;
- mAtRest = true;
- mAtRestCounter = 0;
- }
- // Only update the client if we hit a non-static shape or
- // if this is our final rest pos.
- if (nonStatic || mAtRest)
- setMaskBits(PositionMask);
- }
- // Collision callbacks. These need to be processed whether we hit
- // anything or not.
- if (!isGhost())
- {
- SimObjectPtr<Item> safePtr(this);
- if (stickyNotify)
- {
- notifyCollision();
- if(bool(safePtr))
- onStickyCollision_callback( getIdString() );
- }
- else
- notifyCollision();
- // water
- if(bool(safePtr))
- {
- if(!mInLiquid && mWaterCoverage != 0.0f)
- {
- onEnterLiquid_callback( getIdString(), mWaterCoverage, mLiquidType.c_str() );
- mInLiquid = true;
- }
- else if(mInLiquid && mWaterCoverage == 0.0f)
- {
- onLeaveLiquid_callback(getIdString(), mLiquidType.c_str());
- mInLiquid = false;
- }
- }
- }
- }
- //----------------------------------------------------------------------------
- static MatrixF IMat(1);
- bool Item::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F&, const SphereF&)
- {
- if ( context == PLC_Decal )
- return false;
- // Collision with the item is always against the item's object
- // space bounding box axis aligned in world space.
- Point3F pos;
- mObjToWorld.getColumn(3,&pos);
- IMat.setColumn(3,pos);
- polyList->setTransform(&IMat, mObjScale);
- polyList->setObject(this);
- polyList->addBox(mObjBox);
- return true;
- }
- //----------------------------------------------------------------------------
- U32 Item::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
- {
- U32 retMask = Parent::packUpdate(connection,mask,stream);
- if (stream->writeFlag(mask & InitialUpdateMask)) {
- stream->writeFlag(mRotate);
- stream->writeFlag(mStatic);
- if (stream->writeFlag(getScale() != Point3F(1, 1, 1)))
- mathWrite(*stream, getScale());
- }
- if (mask & ThrowSrcMask && mCollisionObject) {
- S32 gIndex = connection->getGhostIndex(mCollisionObject);
- if (stream->writeFlag(gIndex != -1))
- stream->writeInt(gIndex,NetConnection::GhostIdBitSize);
- }
- else
- stream->writeFlag(false);
- if (stream->writeFlag(mask & RotationMask && !mRotate)) {
- // Assumes rotation is about the Z axis
- AngAxisF aa(mObjToWorld);
- stream->writeFlag(aa.axis.z < 0);
- stream->write(aa.angle);
- }
- if (stream->writeFlag(mask & PositionMask)) {
- Point3F pos;
- mObjToWorld.getColumn(3,&pos);
- mathWrite(*stream, pos);
- if (!stream->writeFlag(mAtRest)) {
- mathWrite(*stream, mVelocity);
- }
- stream->writeFlag(!(mask & NoWarpMask));
- }
- return retMask;
- }
- void Item::unpackUpdate(NetConnection *connection, BitStream *stream)
- {
- Parent::unpackUpdate(connection,stream);
- // InitialUpdateMask
- if (stream->readFlag()) {
- mRotate = stream->readFlag();
- mStatic = stream->readFlag();
- if (stream->readFlag())
- mathRead(*stream, &mObjScale);
- else
- mObjScale.set(1, 1, 1);
- }
- // ThrowSrcMask && mCollisionObject
- if (stream->readFlag()) {
- S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize);
- setCollisionTimeout(static_cast<ShapeBase*>(connection->resolveGhost(gIndex)));
- }
- MatrixF mat = mObjToWorld;
- // RotationMask && !mRotate
- if (stream->readFlag()) {
- // Assumes rotation is about the Z axis
- AngAxisF aa;
- aa.axis.set(0.0f, 0.0f, stream->readFlag() ? -1.0f : 1.0f);
- stream->read(&aa.angle);
- aa.setMatrix(&mat);
- Point3F pos;
- mObjToWorld.getColumn(3,&pos);
- mat.setColumn(3,pos);
- }
- // PositionMask
- if (stream->readFlag()) {
- Point3F pos;
- mathRead(*stream, &pos);
- F32 speed = mVelocity.len();
- if ((mAtRest = stream->readFlag()) == true)
- mVelocity.set(0.0f, 0.0f, 0.0f);
- else
- mathRead(*stream, &mVelocity);
- if (stream->readFlag() && isProperlyAdded()) {
- // Determin number of ticks to warp based on the average
- // of the client and server velocities.
- mDelta.warpOffset = pos - mDelta.pos;
- F32 as = (speed + mVelocity.len()) * 0.5f * TickSec;
- F32 dt = (as > 0.00001f) ? mDelta.warpOffset.len() / as: sMaxWarpTicks;
- mDelta.warpTicks = (S32)((dt > sMinWarpTicks)? getMax(mFloor(dt + 0.5f), 1.0f): 0.0f);
- if (mDelta.warpTicks)
- {
- // Setup the warp to start on the next tick, only the
- // object's position is warped.
- if (mDelta.warpTicks > sMaxWarpTicks)
- mDelta.warpTicks = sMaxWarpTicks;
- mDelta.warpOffset /= (F32)mDelta.warpTicks;
- }
- else {
- // Going to skip the warp, server and client are real close.
- // Adjust the frame interpolation to move smoothly to the
- // new position within the current tick.
- Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
- VectorF vec = mDelta.pos - cp;
- F32 vl = vec.len();
- if (vl) {
- F32 s = mDelta.posVec.len() / vl;
- mDelta.posVec = (cp - pos) * s;
- }
- mDelta.pos = pos;
- mat.setColumn(3,pos);
- }
- }
- else {
- // Set the item to the server position
- mDelta.warpTicks = 0;
- mDelta.posVec.set(0,0,0);
- mDelta.pos = pos;
- mDelta.dt = 0;
- mat.setColumn(3,pos);
- }
- }
- Parent::setTransform(mat);
- }
- DefineEngineMethod( Item, isStatic, bool, (),,
- "@brief Is the object static (ie, non-movable)?\n\n"
- "@return True if the object is static, false if it is not.\n"
- "@tsexample\n"
- "// Query the item on if it is or is not static.\n"
- "%isStatic = %itemData.isStatic();\n\n"
- "@endtsexample\n\n"
- "@see static\n"
- )
- {
- return object->isStatic();
- }
- DefineEngineMethod( Item, isAtRest, bool, (),,
- "@brief Is the object at rest (ie, no longer moving)?\n\n"
- "@return True if the object is at rest, false if it is not.\n"
- "@tsexample\n"
- "// Query the item on if it is or is not at rest.\n"
- "%isAtRest = %item.isAtRest();\n\n"
- "@endtsexample\n\n"
- )
- {
- return object->isAtRest();
- }
- DefineEngineMethod( Item, isRotating, bool, (),,
- "@brief Is the object still rotating?\n\n"
- "@return True if the object is still rotating, false if it is not.\n"
- "@tsexample\n"
- "// Query the item on if it is or is not rotating.\n"
- "%isRotating = %itemData.isRotating();\n\n"
- "@endtsexample\n\n"
- "@see rotate\n"
- )
- {
- return object->isRotating();
- }
- DefineEngineMethod( Item, setCollisionTimeout, bool, (S32 ignoreColObj),,
- "@brief Temporarily disable collisions against a specific ShapeBase object.\n\n"
- "This is useful to prevent a player from immediately picking up an Item they have "
- "just thrown. Only one object may be on the timeout list at a time. The timeout is "
- "defined as 15 ticks.\n\n"
- "@param objectID ShapeBase object ID to disable collisions against.\n"
- "@return Returns true if the ShapeBase object requested could be found, false if it could not.\n"
- "@tsexample\n"
- "// Set the ShapeBase Object ID to disable collisions against\n"
- "%ignoreColObj = %player.getID();\n\n"
- "// Inform this Item object to ignore collisions temproarily against the %ignoreColObj.\n"
- "%item.setCollisionTimeout(%ignoreColObj);\n\n"
- "@endtsexample\n\n"
- )
- {
- ShapeBase* source = NULL;
- if (Sim::findObject(ignoreColObj,source)) {
- object->setCollisionTimeout(source);
- return true;
- }
- return false;
- }
- DefineEngineMethod( Item, getLastStickyPos, const char*, (),,
- "@brief Get the position on the surface on which this Item is stuck.\n\n"
- "@return Returns The XYZ position of where this Item is stuck.\n"
- "@tsexample\n"
- "// Acquire the position where this Item is currently stuck\n"
- "%stuckPosition = %item.getLastStickPos();\n\n"
- "@endtsexample\n\n"
- "@note Server side only.\n"
- )
- {
- static const U32 bufSize = 256;
- char* ret = Con::getReturnBuffer(bufSize);
- if (object->isServerObject())
- dSprintf(ret, bufSize, "%g %g %g",
- object->mStickyCollisionPos.x,
- object->mStickyCollisionPos.y,
- object->mStickyCollisionPos.z);
- else
- dStrcpy(ret, "0 0 0", bufSize);
- return ret;
- }
- DefineEngineMethod( Item, getLastStickyNormal, const char *, (),,
- "@brief Get the normal of the surface on which the object is stuck.\n\n"
- "@return Returns The XYZ normal from where this Item is stuck.\n"
- "@tsexample\n"
- "// Acquire the position where this Item is currently stuck\n"
- "%stuckPosition = %item.getLastStickPos();\n\n"
- "@endtsexample\n\n"
- "@note Server side only.\n"
- )
- {
- static const U32 bufSize = 256;
- char* ret = Con::getReturnBuffer(bufSize);
- if (object->isServerObject())
- dSprintf(ret, bufSize, "%g %g %g",
- object->mStickyCollisionNormal.x,
- object->mStickyCollisionNormal.y,
- object->mStickyCollisionNormal.z);
- else
- dStrcpy(ret, "0 0 0", bufSize);
- return ret;
- }
- //----------------------------------------------------------------------------
- bool Item::_setStatic(void *object, const char *index, const char *data)
- {
- Item *i = static_cast<Item*>(object);
- i->mAtRest = dAtob(data);
- i->setMaskBits(InitialUpdateMask | PositionMask);
- return true;
- }
- bool Item::_setRotate(void *object, const char *index, const char *data)
- {
- Item *i = static_cast<Item*>(object);
- i->setMaskBits(InitialUpdateMask | RotationMask);
- return true;
- }
- void Item::initPersistFields()
- {
- addGroup("Misc");
- addProtectedField("static", TypeBool, Offset(mStatic, Item), &_setStatic, &defaultProtectedGetFn, "If true, the object is not moving in the world.\n");
- addProtectedField("rotate", TypeBool, Offset(mRotate, Item), &_setRotate, &defaultProtectedGetFn, "If true, the object will automatically rotate around its Z axis.\n");
- endGroup("Misc");
- Parent::initPersistFields();
- }
- void Item::consoleInit()
- {
- Con::addVariable("Item::minWarpTicks",TypeF32,&sMinWarpTicks,
- "@brief Fraction of tick at which instant warp occures on the client.\n\n"
- "@ingroup GameObjects");
- Con::addVariable("Item::maxWarpTicks",TypeS32,&sMaxWarpTicks,
- "@brief When a warp needs to occur due to the client being too far off from the server, this is the "
- "maximum number of ticks we'll allow the client to warp to catch up.\n\n"
- "@ingroup GameObjects");
- }
- //----------------------------------------------------------------------------
- void Item::prepRenderImage( SceneRenderState* state )
- {
- // Items do NOT render if destroyed
- if (getDamageState() == Destroyed)
- return;
- Parent::prepRenderImage( state );
- }
- void Item::buildConvex(const Box3F& box, Convex* convex)
- {
- if (mShapeInstance == NULL)
- return;
- // These should really come out of a pool
- mConvexList->collectGarbage();
- if (box.isOverlapped(getWorldBox()) == false)
- return;
- // Just return a box convex for the entire shape...
- Convex* cc = 0;
- CollisionWorkingList& wl = convex->getWorkingList();
- for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
- if (itr->mConvex->getType() == BoxConvexType &&
- itr->mConvex->getObject() == this) {
- cc = itr->mConvex;
- break;
- }
- }
- if (cc)
- return;
- // Create a new convex.
- BoxConvex* cp = new BoxConvex;
- mConvexList->registerObject(cp);
- convex->addToWorkingList(cp);
- cp->init(this);
- mObjBox.getCenter(&cp->mCenter);
- cp->mSize.x = mObjBox.len_x() / 2.0f;
- cp->mSize.y = mObjBox.len_y() / 2.0f;
- cp->mSize.z = mObjBox.len_z() / 2.0f;
- }
- void Item::advanceTime(F32 dt)
- {
- Parent::advanceTime(dt);
- if ( isMounted() )
- return;
- if( mRotate )
- {
- F32 r = (dt / sRotationSpeed) * M_2PI;
- Point3F pos = mRenderObjToWorld.getPosition();
- MatrixF rotMatrix;
- if( mRotate )
- {
- rotMatrix.set( EulerF( 0.0, 0.0, r ) );
- }
- else
- {
- rotMatrix.set( EulerF( r * 0.5, 0.0, r ) );
- }
- MatrixF mat = mRenderObjToWorld;
- mat.setPosition( pos );
- mat.mul( rotMatrix );
- setRenderTransform(mat);
- }
- }
|