| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661 |
- //-----------------------------------------------------------------------------
- // 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/vehicles/wheeledVehicle.h"
- #include "math/mMath.h"
- #include "math/mathIO.h"
- #include "console/simBase.h"
- #include "console/console.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "collision/clippedPolyList.h"
- #include "collision/planeExtractor.h"
- #include "core/stream/bitStream.h"
- #include "core/dnet.h"
- #include "T3D/gameBase/gameConnection.h"
- #include "ts/tsShapeInstance.h"
- #include "T3D/fx/particleEmitter.h"
- #include "sfx/sfxSystem.h"
- #include "sfx/sfxTrack.h"
- #include "sfx/sfxSource.h"
- #include "sfx/sfxTypes.h"
- #include "scene/sceneManager.h"
- #include "core/resourceManager.h"
- #include "materials/materialDefinition.h"
- #include "materials/baseMatInstance.h"
- #include "lighting/lightQuery.h"
- // Collision masks are used to determine what type of objects the
- // wheeled vehicle will collide with.
- static U32 sClientCollisionMask =
- TerrainObjectType | PlayerObjectType |
- StaticShapeObjectType | VehicleObjectType |
- VehicleBlockerObjectType;
- // Misc. sound constants
- static F32 sMinSquealVolume = 0.05f;
- static F32 sIdleEngineVolume = 0.2f;
- //----------------------------------------------------------------------------
- // Vehicle Tire Data Block
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleTire);
- ConsoleDocClass( WheeledVehicleTire,
- "@brief Defines the properties of a WheeledVehicle tire.\n\n"
- "Tires act as springs and generate lateral and longitudinal forces to move "
- "the vehicle. These distortion/spring forces are what convert wheel angular "
- "velocity into forces that act on the rigid body.\n"
- "@ingroup Vehicles\n"
- );
- WheeledVehicleTire::WheeledVehicleTire()
- {
- INIT_ASSET(Shape);
- staticFriction = 1;
- kineticFriction = 0.5f;
- restitution = 1;
- radius = 0.6f;
- lateralForce = 10;
- lateralDamping = 1;
- lateralRelaxation = 1;
- longitudinalForce = 10;
- longitudinalDamping = 1;
- longitudinalRelaxation = 1;
- mass = 1.f;
- }
- bool WheeledVehicleTire::preload(bool server, String &errorStr)
- {
- // Load up the tire shape. ShapeBase has an option to force a
- // CRC check, this is left out here, but could be easily added.
- if (!mShape)
- {
- errorStr = String::ToString("WheeledVehicleTire: Couldn't load shape \"%s\"", mShapeAssetId);
- return false;
- }
- else
- {
- // Determinw wheel radius from the shape's bounding box.
- // The tire should be built with it's hub axis along the
- // object's Y axis.
- radius = mShape->mBounds.len_z() / 2;
- }
- return true;
- }
- void WheeledVehicleTire::initPersistFields()
- {
- INITPERSISTFIELD_SHAPEASSET(Shape, WheeledVehicleTire, "The shape to use for the wheel.");
- addField( "mass", TypeF32, Offset(mass, WheeledVehicleTire),
- "The mass of the wheel.\nCurrently unused." );
- addField( "radius", TypeF32, Offset(radius, WheeledVehicleTire),
- "@brief The radius of the wheel.\n\n"
- "The radius is determined from the bounding box of the shape provided "
- "in the shapefile field, and does not need to be specified in script. "
- "The tire should be built with its hub axis along the object's Y-axis." );
- addField( "staticFriction", TypeF32, Offset(staticFriction, WheeledVehicleTire),
- "Tire friction when the wheel is not slipping (has traction)." );
- addField( "kineticFriction", TypeF32, Offset(kineticFriction, WheeledVehicleTire),
- "Tire friction when the wheel is slipping (no traction)." );
- addField( "restitution", TypeF32, Offset(restitution, WheeledVehicleTire),
- "Tire restitution.\nCurrently unused." );
- addField( "lateralForce", TypeF32, Offset(lateralForce, WheeledVehicleTire),
- "@brief Tire force perpendicular to the direction of movement.\n\n"
- "Lateral force can in simple terms be considered left/right steering "
- "force. WheeledVehicles are acted upon by forces generated by their tires "
- "and the lateralForce measures the magnitude of the force exerted on the "
- "vehicle when the tires are deformed along the x-axis. With real wheeled "
- "vehicles, tires are constantly being deformed and it is the interplay of "
- "deformation forces which determines how a vehicle moves. In Torque's "
- "simulation of vehicle physics, tire deformation obviously can't be handled "
- "with absolute realism, but the interplay of a vehicle's velocity, its "
- "engine's torque and braking forces, and its wheels' friction, lateral "
- "deformation, lateralDamping, lateralRelaxation, longitudinal deformation, "
- "longitudinalDamping, and longitudinalRelaxation forces, along with its "
- "wheels' angular velocity are combined to create a robust real-time "
- "physical simulation.\n\n"
- "For this field, the larger the value supplied for the lateralForce, the "
- "larger the effect steering maneuvers can have. In Torque tire forces are "
- "applied at a vehicle's wheel hubs." );
- addField( "lateralDamping", TypeF32, Offset(lateralDamping, WheeledVehicleTire),
- "Damping force applied against lateral forces generated by the tire.\n\n"
- "@see lateralForce" );
- addField( "lateralRelaxation", TypeF32, Offset(lateralRelaxation, WheeledVehicleTire),
- "@brief Relaxing force applied against lateral forces generated by the tire.\n\n"
- "The lateralRelaxation force measures how strongly the tire effectively "
- "un-deforms.\n\n@see lateralForce" );
- addField( "longitudinalForce", TypeF32, Offset(longitudinalForce, WheeledVehicleTire),
- "@brief Tire force in the direction of movement.\n\n"
- "Longitudinal force can in simple terms be considered forward/backward "
- "movement force. WheeledVehicles are acted upon by forces generated by "
- "their tires and the longitudinalForce measures the magnitude of the "
- "force exerted on the vehicle when the tires are deformed along the y-axis.\n\n"
- "For this field, the larger the value, the larger the effect "
- "acceleration/deceleration inputs have.\n\n"
- "@see lateralForce" );
- addField( "longitudinalDamping", TypeF32, Offset(longitudinalDamping, WheeledVehicleTire),
- "Damping force applied against longitudinal forces generated by the tire.\n\n"
- "@see longitudinalForce" );
- addField( "longitudinalRelaxation", TypeF32, Offset(longitudinalRelaxation, WheeledVehicleTire),
- "@brief Relaxing force applied against longitudinal forces generated by the tire.\n\n"
- "The longitudinalRelaxation force measures how strongly the tire effectively "
- "un-deforms.\n\n"
- "@see longitudinalForce" );
- Parent::initPersistFields();
- }
- void WheeledVehicleTire::packData(BitStream* stream)
- {
- Parent::packData(stream);
- PACKDATA_ASSET(Shape);
- stream->write(mass);
- stream->write(staticFriction);
- stream->write(kineticFriction);
- stream->write(restitution);
- stream->write(radius);
- stream->write(lateralForce);
- stream->write(lateralDamping);
- stream->write(lateralRelaxation);
- stream->write(longitudinalForce);
- stream->write(longitudinalDamping);
- stream->write(longitudinalRelaxation);
- }
- void WheeledVehicleTire::unpackData(BitStream* stream)
- {
- Parent::unpackData(stream);
- UNPACKDATA_ASSET(Shape);
- stream->read(&mass);
- stream->read(&staticFriction);
- stream->read(&kineticFriction);
- stream->read(&restitution);
- stream->read(&radius);
- stream->read(&lateralForce);
- stream->read(&lateralDamping);
- stream->read(&lateralRelaxation);
- stream->read(&longitudinalForce);
- stream->read(&longitudinalDamping);
- stream->read(&longitudinalRelaxation);
- }
- //----------------------------------------------------------------------------
- // Vehicle Spring Data Block
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleSpring);
- ConsoleDocClass( WheeledVehicleSpring,
- "@brief Defines the properties of a WheeledVehicle spring.\n\n"
- "@ingroup Vehicles\n"
- );
- WheeledVehicleSpring::WheeledVehicleSpring()
- {
- length = 1;
- force = 10;
- damping = 1;
- antiSway = 1;
- }
- void WheeledVehicleSpring::initPersistFields()
- {
- addField( "length", TypeF32, Offset(length, WheeledVehicleSpring),
- "@brief Maximum spring length. ie. how far the wheel can extend from the "
- "root hub position.\n\n"
- "This should be set to the vertical (Z) distance the hub travels in the "
- "associated spring animation." );
- addField( "force", TypeF32, Offset(force, WheeledVehicleSpring),
- "@brief Maximum spring force (when compressed to minimum length, 0).\n\n"
- "Increasing this will make the vehicle suspension ride higher (for a given "
- "vehicle mass), and also make the vehicle more bouncy when landing jumps." );
- addField( "damping", TypeF32, Offset(damping, WheeledVehicleSpring),
- "@brief Force applied to slow changes to the extension of this spring.\n\n"
- "Increasing this makes the suspension stiffer which can help stabilise "
- "bouncy vehicles." );
- addField( "antiSwayForce", TypeF32, Offset(antiSway, WheeledVehicleSpring),
- "@brief Force applied to equalize extension of the spring on the opposite "
- "wheel.\n\n"
- "This force helps to keep the suspension balanced when opposite wheels "
- "are at different heights." );
- Parent::initPersistFields();
- }
- void WheeledVehicleSpring::packData(BitStream* stream)
- {
- Parent::packData(stream);
- stream->write(length);
- stream->write(force);
- stream->write(damping);
- stream->write(antiSway);
- }
- void WheeledVehicleSpring::unpackData(BitStream* stream)
- {
- Parent::unpackData(stream);
- stream->read(&length);
- stream->read(&force);
- stream->read(&damping);
- stream->read(&antiSway);
- }
- //----------------------------------------------------------------------------
- // Wheeled Vehicle Data Block
- //----------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleData);
- ConsoleDocClass( WheeledVehicleData,
- "@brief Defines the properties of a WheeledVehicle.\n\n"
- "@ingroup Vehicles\n"
- );
- typedef WheeledVehicleData::Sounds wheelSoundsEnum;
- DefineEnumType(wheelSoundsEnum);
- ImplementEnumType(wheelSoundsEnum, "enum types.\n"
- "@ingroup WheeledVehicleData\n\n")
- {WheeledVehicleData::JetSound, "JetSound", "..." },
- {WheeledVehicleData::EngineSound, "EngineSound", "..." },
- {WheeledVehicleData::SquealSound, "SquealSound", "..." },
- {WheeledVehicleData::WheelImpactSound, "WheelImpactSound", "..." },
- EndImplementEnumType;
- WheeledVehicleData::WheeledVehicleData()
- {
- tireEmitter = 0;
- maxWheelSpeed = 40;
- engineTorque = 1;
- engineBrake = 1;
- brakeTorque = 1;
- brakeLightSequence = -1;
- steeringSequence = -1;
- wheelCount = 0;
- dMemset(&wheel, 0, sizeof(wheel));
- for (S32 i = 0; i < MaxSounds; i++)
- INIT_ASSET_ARRAY(WheeledVehicleSounds, i);
- }
- //----------------------------------------------------------------------------
- /** Load the vehicle shape
- Loads and extracts information from the vehicle shape.
- Wheel Sequences
- spring# Wheel spring motion: time 0 = wheel fully extended,
- the hub must be displaced, but not directly animated
- as it will be rotated in code.
- Other Sequences
- steering Wheel steering: time 0 = full right, 0.5 = center
- brakeLight Brake light, time 0 = off, 1 = braking
- Wheel Nodes
- hub# Wheel hub
- The steering and animation sequences are optional.
- */
- bool WheeledVehicleData::preload(bool server, String &errorStr)
- {
- if (!Parent::preload(server, errorStr))
- return false;
- // A temporary shape instance is created so that we can
- // animate the shape and extract wheel information.
- TSShapeInstance* si = new TSShapeInstance(mShape, false);
- // Resolve objects transmitted from server
- if (!server) {
- for (S32 i = 0; i < MaxSounds; i++)
- {
- if (getWheeledVehicleSounds(i) != StringTable->EmptyString())
- {
- _setWheeledVehicleSounds(getWheeledVehicleSounds(i), i);
- }
- }
- if (tireEmitter)
- Sim::findObject(SimObjectId((uintptr_t)tireEmitter),tireEmitter);
- }
- // Extract wheel information from the shape
- TSThread* thread = si->addThread();
- Wheel* wp = wheel;
- char buff[10];
- for (S32 i = 0; i < MaxWheels; i++) {
- // The wheel must have a hub node to operate at all.
- dSprintf(buff,sizeof(buff),"hub%d",i);
- wp->springNode = mShape->findNode(buff);
- if (wp->springNode != -1) {
- // Check for spring animation.. If there is none we just grab
- // the current position of the hub. Otherwise we'll animate
- // and get the position at time 0.
- dSprintf(buff,sizeof(buff),"spring%d",i);
- wp->springSequence = mShape->findSequence(buff);
- if (wp->springSequence == -1)
- si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos);
- else {
- si->setSequence(thread,wp->springSequence,0);
- si->animate();
- si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos);
- // Determin the length of the animation so we can scale it
- // according the actual wheel position.
- Point3F downPos;
- si->setSequence(thread,wp->springSequence,1);
- si->animate();
- si->mNodeTransforms[wp->springNode].getColumn(3, &downPos);
- wp->springLength = wp->pos.z - downPos.z;
- if (!wp->springLength)
- wp->springSequence = -1;
- }
- // Match wheels that are mirrored along the Y axis.
- mirrorWheel(wp);
- wp++;
- }
- }
- wheelCount = wp - wheel;
- // Check for steering. Should think about normalizing the
- // steering animation the way the suspension is, but I don't
- // think it's as critical.
- steeringSequence = mShape->findSequence("steering");
- // Brakes
- brakeLightSequence = mShape->findSequence("brakelight");
- // Extract collision planes from shape collision detail level
- if (collisionDetails[0] != -1) {
- MatrixF imat(1);
- SphereF sphere;
- sphere.center = mShape->center;
- sphere.radius = mShape->mRadius;
- PlaneExtractorPolyList polyList;
- polyList.mPlaneList = &rigidBody.mPlaneList;
- polyList.setTransform(&imat, Point3F(1,1,1));
- si->buildPolyList(&polyList,collisionDetails[0]);
- }
- delete si;
- return true;
- }
- //----------------------------------------------------------------------------
- /** Find a matching lateral wheel
- Looks for a matching wheeling mirrored along the Y axis, within some
- tolerance (current 0.5m), if one is found, the two wheels are lined up.
- */
- bool WheeledVehicleData::mirrorWheel(Wheel* we)
- {
- we->opposite = -1;
- for (Wheel* wp = wheel; wp != we; wp++)
- if (mFabs(wp->pos.y - we->pos.y) < 0.5)
- {
- we->pos.x = -wp->pos.x;
- we->pos.y = wp->pos.y;
- we->pos.z = wp->pos.z;
- we->opposite = wp - wheel;
- wp->opposite = we - wheel;
- return true;
- }
- return false;
- }
- //----------------------------------------------------------------------------
- void WheeledVehicleData::initPersistFields()
- {
- INITPERSISTFIELD_SOUNDASSET_ENUMED(WheeledVehicleSounds, wheelSoundsEnum, MaxSounds, WheeledVehicleData, "Sounds related to wheeled vehicle.");
- addField("tireEmitter",TYPEID< ParticleEmitterData >(), Offset(tireEmitter, WheeledVehicleData),
- "ParticleEmitterData datablock used to generate particles from each wheel "
- "when the vehicle is moving and the wheel is in contact with the ground.");
- addField("maxWheelSpeed", TypeF32, Offset(maxWheelSpeed, WheeledVehicleData),
- "@brief Maximum linear velocity of each wheel.\n\n"
- "This caps the maximum speed of the vehicle." );
- addField("engineTorque", TypeF32, Offset(engineTorque, WheeledVehicleData),
- "@brief Torque available from the engine at 100% throttle.\n\n"
- "This controls vehicle acceleration. ie. how fast it will reach maximum speed." );
- addField("engineBrake", TypeF32, Offset(engineBrake, WheeledVehicleData),
- "@brief Braking torque applied by the engine when the throttle and brake "
- "are both 0.\n\n"
- "This controls how quickly the vehicle will coast to a stop." );
- addField("brakeTorque", TypeF32, Offset(brakeTorque, WheeledVehicleData),
- "@brief Torque applied when braking.\n\n"
- "This controls how fast the vehicle will stop when the brakes are applied." );
-
- Parent::initPersistFields();
- }
- //----------------------------------------------------------------------------
- void WheeledVehicleData::packData(BitStream* stream)
- {
- Parent::packData(stream);
- if (stream->writeFlag(tireEmitter))
- stream->writeRangedU32(mPacked ? SimObjectId((uintptr_t)tireEmitter):
- tireEmitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
- for (S32 i = 0; i < MaxSounds; i++)
- {
- PACKDATA_ASSET_ARRAY(WheeledVehicleSounds, i);
- }
- stream->write(maxWheelSpeed);
- stream->write(engineTorque);
- stream->write(engineBrake);
- stream->write(brakeTorque);
- }
- void WheeledVehicleData::unpackData(BitStream* stream)
- {
- Parent::unpackData(stream);
- tireEmitter = stream->readFlag()?
- (ParticleEmitterData*)(uintptr_t)stream->readRangedU32(DataBlockObjectIdFirst,
- DataBlockObjectIdLast): 0;
- for (S32 i = 0; i < MaxSounds; i++)
- {
- UNPACKDATA_ASSET_ARRAY(WheeledVehicleSounds, i);
- }
- stream->read(&maxWheelSpeed);
- stream->read(&engineTorque);
- stream->read(&engineBrake);
- stream->read(&brakeTorque);
- }
- //----------------------------------------------------------------------------
- // Wheeled Vehicle Class
- //----------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- IMPLEMENT_CO_NETOBJECT_V1(WheeledVehicle);
- ConsoleDocClass( WheeledVehicle,
- "@brief A wheeled vehicle.\n"
- "@ingroup Vehicles\n"
- );
- WheeledVehicle::WheeledVehicle()
- {
- mDataBlock = 0;
- mBraking = false;
- mJetSound = NULL;
- mEngineSound = NULL;
- mSquealSound = NULL;
- mTailLightThread = 0;
- mSteeringThread = 0;
- for (S32 i = 0; i < WheeledVehicleData::MaxWheels; i++) {
- mWheel[i].springThread = 0;
- mWheel[i].Dy = mWheel[i].Dx = 0;
- mWheel[i].tire = 0;
- mWheel[i].spring = 0;
- mWheel[i].shapeInstance = 0;
- mWheel[i].steering = 0;
- mWheel[i].powered = true;
- mWheel[i].slipping = false;
- }
- }
- WheeledVehicle::~WheeledVehicle()
- {
- }
- void WheeledVehicle::initPersistFields()
- {
- Parent::initPersistFields();
- }
- //----------------------------------------------------------------------------
- bool WheeledVehicle::onAdd()
- {
- if(!Parent::onAdd())
- return false;
- addToScene();
- return true;
- }
- void WheeledVehicle::onRemove()
- {
- // Delete the wheel resources
- if (mDataBlock != NULL) {
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++) {
- if (!wheel->emitter.isNull())
- wheel->emitter->deleteWhenEmpty();
- delete wheel->shapeInstance;
- }
- }
- // Stop the sounds
- SFX_DELETE( mJetSound );
- SFX_DELETE( mEngineSound );
- SFX_DELETE( mSquealSound );
- //
- removeFromScene();
- Parent::onRemove();
- }
- //----------------------------------------------------------------------------
- bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
- {
- // Delete any existing wheel resources if we're switching
- // datablocks.
- if (mDataBlock)
- {
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (!wheel->emitter.isNull())
- {
- wheel->emitter->deleteWhenEmpty();
- wheel->emitter = 0;
- }
- delete wheel->shapeInstance;
- wheel->shapeInstance = 0;
- }
- }
- // Load up the new datablock
- mDataBlock = dynamic_cast<WheeledVehicleData*>(dptr);
- if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
- return false;
- // Set inertial tensor, default for the vehicle is sphere
- if (mDataBlock->massBox.x > 0 && mDataBlock->massBox.y > 0 && mDataBlock->massBox.z > 0)
- mRigid.setObjectInertia(mDataBlock->massBox);
- else
- mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents);
- // Initialize the wheels...
- for (S32 i = 0; i < mDataBlock->wheelCount; i++)
- {
- Wheel* wheel = &mWheel[i];
- wheel->data = &mDataBlock->wheel[i];
- wheel->tire = 0;
- wheel->spring = 0;
- wheel->surface.contact = false;
- wheel->surface.object = NULL;
- wheel->avel = 0;
- wheel->apos = 0;
- wheel->extension = 1;
- wheel->slip = 0;
- wheel->springThread = 0;
- wheel->emitter = 0;
- // Steering on the front tires by default
- if (wheel->data->pos.y > 0)
- wheel->steering = 1;
- // Build wheel animation threads
- if (wheel->data->springSequence != -1) {
- wheel->springThread = mShapeInstance->addThread();
- mShapeInstance->setSequence(wheel->springThread,wheel->data->springSequence,0);
- }
- // Each wheel get's it's own particle emitter
- if( mDataBlock->tireEmitter && isGhost() )
- {
- wheel->emitter = new ParticleEmitter;
- wheel->emitter->onNewDataBlock( mDataBlock->tireEmitter, false );
- wheel->emitter->registerObject();
- }
- }
- // Steering sequence
- if (mDataBlock->steeringSequence != -1) {
- mSteeringThread = mShapeInstance->addThread();
- mShapeInstance->setSequence(mSteeringThread,mDataBlock->steeringSequence,0);
- }
- else
- mSteeringThread = 0;
- // Brake light sequence
- if (mDataBlock->brakeLightSequence != -1) {
- mTailLightThread = mShapeInstance->addThread();
- mShapeInstance->setSequence(mTailLightThread,mDataBlock->brakeLightSequence,0);
- }
- else
- mTailLightThread = 0;
- if (isGhost())
- {
- // Create the sounds ahead of time. This reduces runtime
- // costs and makes the system easier to understand.
- SFX_DELETE( mEngineSound );
- SFX_DELETE( mSquealSound );
- SFX_DELETE( mJetSound );
- if ( mDataBlock->getWheeledVehicleSounds(WheeledVehicleData::EngineSound) )
- mEngineSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::EngineSound), &getTransform() );
- if ( mDataBlock->getWheeledVehicleSounds(WheeledVehicleData::SquealSound) )
- mSquealSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::SquealSound), &getTransform() );
- if ( mDataBlock->getWheeledVehicleSounds(WheeledVehicleData::JetSound) )
- mJetSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::JetSound), &getTransform() );
- }
- scriptOnNewDataBlock();
- return true;
- }
- //----------------------------------------------------------------------------
- S32 WheeledVehicle::getWheelCount()
- {
- // Return # of hubs defined on the car body
- return mDataBlock? mDataBlock->wheelCount: 0;
- }
- void WheeledVehicle::setWheelSteering(S32 wheel,F32 steering)
- {
- AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
- mWheel[wheel].steering = mClampF(steering,-1,1);
- setMaskBits(WheelMask);
- }
- void WheeledVehicle::setWheelPowered(S32 wheel,bool powered)
- {
- AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
- mWheel[wheel].powered = powered;
- setMaskBits(WheelMask);
- }
- void WheeledVehicle::setWheelTire(S32 wheel,WheeledVehicleTire* tire)
- {
- AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
- mWheel[wheel].tire = tire;
- setMaskBits(WheelMask);
- }
- void WheeledVehicle::setWheelSpring(S32 wheel,WheeledVehicleSpring* spring)
- {
- AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
- mWheel[wheel].spring = spring;
- setMaskBits(WheelMask);
- }
- void WheeledVehicle::getWheelInstAndTransform( U32 index, TSShapeInstance** inst, MatrixF* xfrm ) const
- {
- AssertFatal( index < WheeledVehicleData::MaxWheels,
- "WheeledVehicle::getWheelInstAndTransform() - Bad wheel index!" );
- const Wheel* wheel = &mWheel[index];
- *inst = wheel->shapeInstance;
-
- if ( !xfrm || !wheel->shapeInstance )
- return;
- MatrixF world = getRenderTransform();
- world.scale( mObjScale );
- // Steering & spring extension
- MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering));
- Point3F pos = wheel->data->pos;
- pos.z -= wheel->spring->length * wheel->extension;
- hub.setColumn(3,pos);
- world.mul(hub);
- // Wheel rotation
- MatrixF rot(EulerF(wheel->apos * M_2PI,0,0));
- world.mul(rot);
- // Rotation the tire to face the right direction
- // (could pre-calculate this)
- MatrixF wrot(EulerF(0,0,(wheel->data->pos.x > 0)? M_PI/2: -M_PI/2));
- world.mul(wrot);
- *xfrm = world;
- }
- //----------------------------------------------------------------------------
- void WheeledVehicle::processTick(const Move* move)
- {
- Parent::processTick(move);
- }
- void WheeledVehicle::updateMove(const Move* move)
- {
- Parent::updateMove(move);
- // Brake on trigger
- mBraking = move->trigger[2];
- // Set the tail brake light thread direction based on the brake state.
- if (mTailLightThread)
- mShapeInstance->setTimeScale(mTailLightThread, mBraking? 1.0f : -1.0f);
- }
- //----------------------------------------------------------------------------
- void WheeledVehicle::advanceTime(F32 dt)
- {
- PROFILE_SCOPE( WheeledVehicle_AdvanceTime );
- Parent::advanceTime(dt);
- // Stick the wheels to the ground. This is purely so they look
- // good while the vehicle is being interpolated.
- extendWheels();
- // Update wheel angular position and slip, this is a client visual
- // feature only, it has no affect on the physics.
- F32 slipTotal = 0;
- F32 torqueTotal = 0;
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- if (wheel->tire && wheel->spring) {
- // Update angular position
- wheel->apos += (wheel->avel * dt) / M_2PI;
- wheel->apos -= mFloor(wheel->apos);
- if (wheel->apos < 0)
- wheel->apos = 1 - wheel->apos;
- // Keep track of largest slip
- slipTotal += wheel->slip;
- torqueTotal += wheel->torqueScale;
- }
- // Update the sounds based on wheel slip and torque output
- updateSquealSound(slipTotal / mDataBlock->wheelCount);
- updateEngineSound(sIdleEngineVolume + (1 - sIdleEngineVolume) *
- (1 - (torqueTotal / mDataBlock->wheelCount)));
- updateJetSound();
- updateWheelThreads();
- updateWheelParticles(dt);
- // Update the steering animation: sequence time 0 is full right,
- // and time 0.5 is straight ahead.
- if (mSteeringThread) {
- F32 t = (mSteering.x * mFabs(mSteering.x)) / mDataBlock->maxSteeringAngle;
- mShapeInstance->setPos(mSteeringThread,0.5 - t * 0.5);
- }
- // Animate the tail light. The direction of the thread is
- // set based on vehicle braking.
- if (mTailLightThread)
- mShapeInstance->advanceTime(dt,mTailLightThread);
- }
- //----------------------------------------------------------------------------
- /** Update the rigid body forces on the vehicle
- This method calculates the forces acting on the body, including gravity,
- suspension & tire forces.
- */
- void WheeledVehicle::updateForces(F32 dt)
- {
- PROFILE_SCOPE( WheeledVehicle_UpdateForces );
- extendWheels();
- if (mDisableMove) return;
- F32 aMomentum = mMass / mDataBlock->wheelCount;
- // Get the current matrix and extact vectors
- MatrixF currMatrix;
- mRigid.getTransform(&currMatrix);
- Point3F bx,by,bz;
- currMatrix.getColumn(0,&bx);
- currMatrix.getColumn(1,&by);
- currMatrix.getColumn(2,&bz);
- // Steering angles from current steering wheel position
- F32 quadraticSteering = -(mSteering.x * mFabs(mSteering.x));
- F32 cosSteering,sinSteering;
- mSinCos(quadraticSteering, sinSteering, cosSteering);
- // Calculate Engine and brake torque values used later by in
- // wheel calculations.
- F32 engineTorque,brakeVel;
- if (mBraking)
- {
- brakeVel = (mDataBlock->brakeTorque / aMomentum) * dt;
- engineTorque = 0;
- }
- else
- {
- if (mThrottle)
- {
- engineTorque = mDataBlock->engineTorque * mThrottle;
- brakeVel = 0;
- // Double the engineTorque to help out the jets
- if (mThrottle > 0 && mJetting)
- engineTorque *= 2;
- }
- else
- {
- // Engine brake.
- brakeVel = (mDataBlock->engineBrake / aMomentum) * dt;
- engineTorque = 0;
- }
- }
- // Integrate forces, we'll do this ourselves here instead of
- // relying on the rigid class which does it during movement.
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- mRigid.clearForces();
- // Calculate vertical load for friction. Divide up the spring
- // forces across all the wheels that are in contact with
- // the ground.
- U32 contactCount = 0;
- F32 verticalLoad = 0;
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (wheel->tire && wheel->spring && wheel->surface.contact)
- {
- verticalLoad += wheel->spring->force * (1 - wheel->extension);
- contactCount++;
- }
- }
- if (contactCount)
- verticalLoad /= contactCount;
- // Sum up spring and wheel torque forces
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (!wheel->tire || !wheel->spring)
- continue;
- F32 Fy = 0;
- if (wheel->surface.contact)
- {
- // First, let's compute the wheel's position, and worldspace velocity
- Point3F pos, r, localVel;
- currMatrix.mulP(wheel->data->pos, &pos);
- mRigid.getOriginVector(pos,&r);
- mRigid.getVelocity(r, &localVel);
- // Spring force & damping
- F32 spring = wheel->spring->force * (1 - wheel->extension);
- if (wheel->extension == 0) //spring fully compressed
- {
- // Apply impulses to the rigid body to keep it from
- // penetrating the surface.
- F32 n = -mDot(localVel,Point3F(0,0,1));
- if (n >= 0)
- {
- // Collision impulse, straight forward force stuff.
- F32 d = mRigid.getZeroImpulse(r,Point3F(0,0,1));
- F32 j = n * (1 + mRigid.restitution) * d;
- mRigid.force += Point3F(0,0,1) * j;
- }
- }
- F32 damping = wheel->spring->damping * -(mDot(bz, localVel) / wheel->spring->length);
- if (damping < 0)
- damping = 0;
- // Anti-sway force based on difference in suspension extension
- F32 antiSway = 0;
- if (wheel->data->opposite != -1)
- {
- Wheel* oppositeWheel = &mWheel[wheel->data->opposite];
- if (oppositeWheel->surface.contact)
- antiSway = ((oppositeWheel->extension - wheel->extension) *
- wheel->spring->antiSway);
- if (antiSway < 0)
- antiSway = 0;
- }
- // Spring forces act straight up and are applied at the
- // spring's root position.
- Point3F t, forceVector = bz * (spring + damping + antiSway);
- mCross(r, forceVector, &t);
- mRigid.torque += t;
- mRigid.force += forceVector;
- // Tire direction vectors perpendicular to surface normal
- Point3F wheelXVec = bx * cosSteering;
- wheelXVec += by * sinSteering * wheel->steering;
- Point3F tireX, tireY;
- mCross(wheel->surface.normal, wheelXVec, &tireY);
- tireY.normalize();
- mCross(tireY, wheel->surface.normal, &tireX);
- tireX.normalize();
- // Velocity of tire at the surface contact
- Point3F wheelContact, wheelVelocity;
- mRigid.getOriginVector(wheel->surface.pos,&wheelContact);
- mRigid.getVelocity(wheelContact, &wheelVelocity);
- F32 xVelocity = mDot(tireX, wheelVelocity);
- F32 yVelocity = mDot(tireY, wheelVelocity);
- // Tires act as springs and generate lateral and longitudinal
- // forces to move the vehicle. These distortion/spring forces
- // are what convert wheel angular velocity into forces that
- // act on the rigid body.
- // Longitudinal tire deformation force
- F32 ddy = (wheel->avel * wheel->tire->radius - yVelocity) -
- wheel->tire->longitudinalRelaxation *
- mFabs(wheel->avel) * wheel->Dy;
- wheel->Dy += ddy * dt;
- Fy = (wheel->tire->longitudinalForce * wheel->Dy +
- wheel->tire->longitudinalDamping * ddy);
- // Lateral tire deformation force
- F32 ddx = xVelocity - wheel->tire->lateralRelaxation *
- mFabs(wheel->avel) * wheel->Dx;
- wheel->Dx += ddx * dt;
- F32 Fx = -(wheel->tire->lateralForce * wheel->Dx +
- wheel->tire->lateralDamping * ddx);
- // Vertical load on the tire
- verticalLoad = spring + damping + antiSway;
- if (verticalLoad < 0)
- verticalLoad = 0;
- // Adjust tire forces based on friction
- F32 surfaceFriction = 1;
- F32 mu = surfaceFriction * (wheel->slipping ? wheel->tire->kineticFriction : wheel->tire->staticFriction);
- F32 Fn = verticalLoad * mu; Fn *= Fn;
- F32 Fw = Fx * Fx + Fy * Fy;
- if (Fw > Fn)
- {
- F32 K = mSqrt(Fn / Fw);
- Fy *= K;
- Fx *= K;
- wheel->Dy *= K;
- wheel->Dx *= K;
- wheel->slip = 1 - K;
- wheel->slipping = true;
- }
- else
- {
- wheel->slipping = false;
- wheel->slip = 0;
- }
- // Tire forces act through the tire direction vectors parallel
- // to the surface and are applied at the wheel hub.
- forceVector = (tireX * Fx) + (tireY * Fy);
- pos -= bz * (wheel->spring->length * wheel->extension);
- mRigid.getOriginVector(pos,&r);
- mCross(r, forceVector, &t);
- mRigid.torque += t;
- mRigid.force += forceVector;
- }
- else
- {
- // Wheel not in contact with the ground
- wheel->torqueScale = 0;
- wheel->slip = 0;
- // Relax the tire deformation
- wheel->Dy += (-wheel->tire->longitudinalRelaxation *
- mFabs(wheel->avel) * wheel->Dy) * dt;
- wheel->Dx += (-wheel->tire->lateralRelaxation *
- mFabs(wheel->avel) * wheel->Dx) * dt;
- }
- // Adjust the wheel's angular velocity based on engine torque
- // and tire deformation forces.
- if (wheel->powered)
- {
- F32 maxAvel = mDataBlock->maxWheelSpeed / wheel->tire->radius;
- wheel->torqueScale = (mFabs(wheel->avel) > maxAvel) ? 0 :
- 1 - (mFabs(wheel->avel) / maxAvel);
- }
- else
- wheel->torqueScale = 0;
- wheel->avel += (((wheel->torqueScale * engineTorque) - Fy *
- wheel->tire->radius) / aMomentum) * dt;
- // Adjust the wheel's angular velocity based on brake torque.
- // This is done after avel update to make sure we come to a
- // complete stop.
- if (brakeVel > mFabs(wheel->avel))
- wheel->avel = 0;
- else
- if (wheel->avel > 0)
- wheel->avel -= brakeVel;
- else
- wheel->avel += brakeVel;
- }
- // Jet Force
- if (mJetting)
- mRigid.force += by * mDataBlock->jetForce;
- // Add in force from physical zones...
- mRigid.force += mAppliedForce;
- // Container drag & buoyancy
- mRigid.force += Point3F(0, 0, mRigid.mass * mNetGravity);
- mRigid.force -= mRigid.linVelocity * mDrag;
- mRigid.torque -= mRigid.angMomentum * mDrag;
- // If we've added anything other than gravity, then we're no
- // longer at rest. Could test this a little more efficiently...
- if (mRigid.atRest && (mRigid.force.len() || mRigid.torque.len()))
- mRigid.atRest = false;
- // Integrate and update velocity
- mRigid.linMomentum += mRigid.force * dt;
- mRigid.angMomentum += mRigid.torque * dt;
- mRigid.updateVelocity();
- // Since we've already done all the work, just need to clear this out.
- mRigid.clearForces();
- // If we're still atRest, make sure we're not accumulating anything
- if (mRigid.atRest)
- mRigid.setAtRest();
- }
- //----------------------------------------------------------------------------
- /** Extend the wheels
- The wheels are extended until they contact a surface. The extension
- is instantaneous. The wheels are extended before force calculations and
- also on during client side interpolation (so that the wheels are glued
- to the ground).
- */
- void WheeledVehicle::extendWheels(bool clientHack)
- {
- PROFILE_SCOPE( WheeledVehicle_ExtendWheels );
- disableCollision();
- MatrixF currMatrix;
-
- if(clientHack)
- currMatrix = getRenderTransform();
- else
- mRigid.getTransform(&currMatrix);
-
- // Does a single ray cast down for now... this will have to be
- // changed to something a little more complicated to avoid getting
- // stuck in cracks.
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (wheel->tire && wheel->spring)
- {
- wheel->extension = 1;
- // The ray is cast from the spring mount point to the tip of
- // the tire. If there is a collision the spring extension is
- // adjust to remove the tire radius.
- Point3F sp,vec;
- currMatrix.mulP(wheel->data->pos,&sp);
- currMatrix.mulV(VectorF(0,0,-wheel->spring->length),&vec);
- F32 ts = wheel->tire->radius / wheel->spring->length;
- Point3F ep = sp + (vec * (1 + ts));
- ts = ts / (1+ts);
- RayInfo rInfo;
- if (mContainer->castRay(sp, ep, sClientCollisionMask & ~PlayerObjectType, &rInfo))
- {
- wheel->surface.contact = true;
- wheel->extension = (rInfo.t < ts)? 0: (rInfo.t - ts) / (1 - ts);
- wheel->surface.normal = rInfo.normal;
- wheel->surface.pos = rInfo.point;
- wheel->surface.material = rInfo.material;
- wheel->surface.object = rInfo.object;
- }
- else
- {
- wheel->surface.contact = false;
- wheel->slipping = true;
- }
- }
- }
- enableCollision();
- }
- //----------------------------------------------------------------------------
- /** Update wheel steering and suspension threads.
- These animations are purely cosmetic and this method is only invoked
- on the client.
- */
- void WheeledVehicle::updateWheelThreads()
- {
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (wheel->tire && wheel->spring && wheel->springThread)
- {
- // Scale the spring animation time to match the current
- // position of the wheel. We'll also check to make sure
- // the animation is long enough, if it isn't, just stick
- // it at the end.
- F32 pos = wheel->extension * wheel->spring->length;
- if (pos > wheel->data->springLength)
- pos = 1;
- else
- pos /= wheel->data->springLength;
- mShapeInstance->setPos(wheel->springThread,pos);
- }
- }
- }
- //----------------------------------------------------------------------------
- /** Update wheel particles effects
- These animations are purely cosmetic and this method is only invoked
- on the client. Particles are emitted as long as the moving.
- */
- void WheeledVehicle::updateWheelParticles(F32 dt)
- {
- // OMG l33t hax
- extendWheels(true);
-
- Point3F vel = Parent::getVelocity();
- F32 speed = vel.len();
- // Don't bother if we're not moving.
- if (speed > 1.0f)
- {
- Point3F axis = vel;
- axis.normalize();
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- // Is this wheel in contact with the ground?
- if (wheel->tire && wheel->spring && !wheel->emitter.isNull() &&
- wheel->surface.contact && wheel->surface.object )
- {
- Material* material = ( wheel->surface.material ? dynamic_cast< Material* >( wheel->surface.material->getMaterial() ) : 0 );
- if( material)//&& material->mShowDust )
- {
- LinearColorF colorList[ ParticleData::PDC_NUM_KEYS ];
- for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x )
- colorList[ x ] = material->mEffectColor[ x ];
- for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x )
- colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 );
- wheel->emitter->setColors( colorList );
- // Emit the dust, the density (time) is scaled by the
- // the vehicles velocity.
- wheel->emitter->emitParticles( wheel->surface.pos, true,
- axis, vel, (U32)(3/*dt * (speed / mDataBlock->maxWheelSpeed) * 1000 * wheel->slip*/));
- }
- }
- }
- }
- }
- //----------------------------------------------------------------------------
- /** Update engine sound
- This method is only invoked by clients.
- */
- void WheeledVehicle::updateEngineSound(F32 level)
- {
- if ( !mEngineSound )
- return;
- if ( !mEngineSound->isPlaying() )
- mEngineSound->play();
- mEngineSound->setTransform( getTransform() );
- mEngineSound->setVelocity( getVelocity() );
- //mEngineSound->setVolume( level );
- // Adjust pitch
- F32 pitch = ((level-sIdleEngineVolume) * 1.3f);
- if (pitch < 0.4f)
- pitch = 0.4f;
- mEngineSound->setPitch( pitch );
- }
- //----------------------------------------------------------------------------
- /** Update wheel skid sound
- This method is only invoked by clients.
- */
- void WheeledVehicle::updateSquealSound(F32 level)
- {
- if ( !mSquealSound )
- return;
- if ( level < sMinSquealVolume )
- {
- mSquealSound->stop();
- return;
- }
- if ( !mSquealSound->isPlaying() )
- mSquealSound->play();
- mSquealSound->setTransform( getTransform() );
- mSquealSound->setVolume( level );
- }
- //----------------------------------------------------------------------------
- /** Update jet sound
- This method is only invoked by clients.
- */
- void WheeledVehicle::updateJetSound()
- {
- if ( !mJetSound )
- return;
- if ( !mJetting )
- {
- mJetSound->stop();
- return;
- }
- if ( !mJetSound->isPlaying() )
- mJetSound->play();
- mJetSound->setTransform( getTransform() );
- }
- //----------------------------------------------------------------------------
- U32 WheeledVehicle::getCollisionMask()
- {
- return sClientCollisionMask;
- }
- //----------------------------------------------------------------------------
- /** Build a collision polylist
- The polylist is filled with polygons representing the collision volume
- and the wheels.
- */
- bool WheeledVehicle::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
- {
- PROFILE_SCOPE( WheeledVehicle_BuildPolyList );
- // Parent will take care of body collision.
- Parent::buildPolyList(context, polyList,box,sphere);
- // Add wheels as boxes.
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++) {
- if (wheel->tire && wheel->spring) {
- Box3F wbox;
- F32 radius = wheel->tire->radius;
- wbox.minExtents.x = -(wbox.maxExtents.x = radius / 2);
- wbox.minExtents.y = -(wbox.maxExtents.y = radius);
- wbox.minExtents.z = -(wbox.maxExtents.z = radius);
- MatrixF mat = mObjToWorld;
- Point3F sp,vec;
- mObjToWorld.mulP(wheel->data->pos,&sp);
- mObjToWorld.mulV(VectorF(0,0,-wheel->spring->length),&vec);
- Point3F ep = sp + (vec * wheel->extension);
- mat.setColumn(3,ep);
- polyList->setTransform(&mat,Point3F(1,1,1));
- polyList->addBox(wbox);
- }
- }
- return !polyList->isEmpty();
- }
- void WheeledVehicle::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex )
- {
- Parent::prepBatchRender( state, mountedImageIndex );
- if ( mountedImageIndex != -1 )
- return;
- // Set up our render state *here*,
- // before the push world matrix, so
- // that wheel rendering will be correct.
- TSRenderState rdata;
- rdata.setSceneState( state );
- // We might have some forward lit materials
- // so pass down a query to gather lights.
- LightQuery query;
- query.init( getWorldSphere() );
- rdata.setLightQuery( &query );
- // Shape transform
- GFX->pushWorldMatrix();
- MatrixF mat = getRenderTransform();
- mat.scale( mObjScale );
- GFX->setWorldMatrix( mat );
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (wheel->shapeInstance)
- {
- GFX->pushWorldMatrix();
- // Steering & spring extension
- MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering));
- Point3F pos = wheel->data->pos;
- pos.z -= wheel->spring->length * wheel->extension;
- hub.setColumn(3,pos);
- GFX->multWorld(hub);
- // Wheel rotation
- MatrixF rot(EulerF(wheel->apos * M_2PI,0,0));
- GFX->multWorld(rot);
- // Rotation the tire to face the right direction
- // (could pre-calculate this)
- MatrixF wrot(EulerF(0,0,(wheel->data->pos.x > 0)? M_PI/2: -M_PI/2));
- GFX->multWorld(wrot);
- // Render!
- wheel->shapeInstance->animate();
- wheel->shapeInstance->render( rdata );
- if (mCloakLevel != 0.0f)
- wheel->shapeInstance->setAlphaAlways(1.0f - mCloakLevel);
- else
- wheel->shapeInstance->setAlphaAlways(1.0f);
- GFX->popWorldMatrix();
- }
- }
- GFX->popWorldMatrix();
- }
- //----------------------------------------------------------------------------
- void WheeledVehicle::writePacketData(GameConnection *connection, BitStream *stream)
- {
- Parent::writePacketData(connection, stream);
- stream->writeFlag(mBraking);
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- stream->write(wheel->avel);
- stream->write(wheel->Dy);
- stream->write(wheel->Dx);
- stream->writeFlag(wheel->slipping);
- }
- }
- void WheeledVehicle::readPacketData(GameConnection *connection, BitStream *stream)
- {
- Parent::readPacketData(connection, stream);
- mBraking = stream->readFlag();
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- stream->read(&wheel->avel);
- stream->read(&wheel->Dy);
- stream->read(&wheel->Dx);
- wheel->slipping = stream->readFlag();
- }
- // Rigid state is transmitted by the parent...
- setPosition(mRigid.linPosition,mRigid.angPosition);
- mDelta.pos = mRigid.linPosition;
- mDelta.rot[1] = mRigid.angPosition;
- }
- //----------------------------------------------------------------------------
- U32 WheeledVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
- {
- U32 retMask = Parent::packUpdate(con, mask, stream);
- // Update wheel datablock information
- if (stream->writeFlag(mask & WheelMask))
- {
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (stream->writeFlag(wheel->tire && wheel->spring))
- {
- stream->writeRangedU32(wheel->tire->getId(),
- DataBlockObjectIdFirst,DataBlockObjectIdLast);
- stream->writeRangedU32(wheel->spring->getId(),
- DataBlockObjectIdFirst,DataBlockObjectIdLast);
- stream->writeFlag(wheel->powered);
- // Steering must be sent with full precision as it's
- // used directly in state force calculations.
- stream->write(wheel->steering);
- }
- }
- }
- // The rest of the data is part of the control object packet update.
- // If we're controlled by this client, we don't need to send it.
- if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
- return retMask;
- stream->writeFlag(mBraking);
- if (stream->writeFlag(mask & PositionMask))
- {
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- stream->write(wheel->avel);
- stream->write(wheel->Dy);
- stream->write(wheel->Dx);
- }
- }
- return retMask;
- }
- void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
- {
- Parent::unpackUpdate(con,stream);
- // Update wheel datablock information
- if (stream->readFlag())
- {
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- if (stream->readFlag())
- {
- SimObjectId tid = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast);
- SimObjectId sid = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast);
- if (!Sim::findObject(tid,wheel->tire) || !Sim::findObject(sid,wheel->spring))
- {
- con->setLastError("Invalid packet WheeledVehicle::unpackUpdate()");
- return;
- }
- wheel->powered = stream->readFlag();
- stream->read(&wheel->steering);
- // Create an instance of the tire for rendering
- delete wheel->shapeInstance;
- wheel->shapeInstance = (wheel->tire->mShape == NULL) ? 0:
- new TSShapeInstance(wheel->tire->mShape);
- }
- }
- }
- // After this is data that we only need if we're not the
- // controlling client.
- if (stream->readFlag())
- return;
- mBraking = stream->readFlag();
- if (stream->readFlag())
- {
- Wheel* wend = &mWheel[mDataBlock->wheelCount];
- for (Wheel* wheel = mWheel; wheel < wend; wheel++)
- {
- stream->read(&wheel->avel);
- stream->read(&wheel->Dy);
- stream->read(&wheel->Dx);
- }
- }
- }
- //----------------------------------------------------------------------------
- // Console Methods
- //----------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- DefineEngineMethod( WheeledVehicle, setWheelSteering, bool, ( S32 wheel, F32 steering ),,
- "@brief Set how much the wheel is affected by steering.\n\n"
- "The steering factor controls how much the wheel is rotated by the vehicle "
- "steering. For example, most cars would have their front wheels set to 1.0, "
- "and their rear wheels set to 0 since only the front wheels should turn.\n\n"
- "Negative values will turn the wheel in the opposite direction to the steering "
- "angle.\n"
- "@param wheel index of the wheel to set (hub node #)\n"
- "@param steering steering factor from -1 (full inverse) to 1 (full)\n"
- "@return true if successful, false if failed\n\n" )
- {
- if ( wheel >= 0 && wheel < object->getWheelCount() ) {
- object->setWheelSteering( wheel, steering );
- return true;
- }
- else
- Con::warnf("setWheelSteering: wheel index %d out of bounds, vehicle has %d hubs",
- wheel, object->getWheelCount());
- return false;
- }
- DefineEngineMethod( WheeledVehicle, setWheelPowered, bool, ( S32 wheel, bool powered ),,
- "@brief Set whether the wheel is powered (has torque applied from the engine).\n\n"
- "A rear wheel drive car for example would set the front wheels to false, "
- "and the rear wheels to true.\n"
- "@param wheel index of the wheel to set (hub node #)\n"
- "@param powered flag indicating whether to power the wheel or not\n"
- "@return true if successful, false if failed\n\n" )
- {
- if ( wheel >= 0 && wheel < object->getWheelCount() ) {
- object->setWheelPowered( wheel, powered );
- return true;
- }
- else
- Con::warnf("setWheelPowered: wheel index %d out of bounds, vehicle has %d hubs",
- wheel, object->getWheelCount());
- return false;
- }
- DefineEngineMethod( WheeledVehicle, setWheelTire, bool, ( S32 wheel, WheeledVehicleTire* tire ),,
- "@brief Set the WheeledVehicleTire datablock for this wheel.\n"
- "@param wheel index of the wheel to set (hub node #)\n"
- "@param tire WheeledVehicleTire datablock\n"
- "@return true if successful, false if failed\n\n"
- "@tsexample\n"
- "%obj.setWheelTire( 0, FrontTire );\n"
- "@endtsexample\n" )
- {
- if (wheel >= 0 && wheel < object->getWheelCount()) {
- object->setWheelTire(wheel,tire);
- return true;
- }
- else {
- Con::warnf("setWheelTire: invalid tire datablock or wheel index, vehicle has %d hubs",
- object->getWheelCount());
- return false;
- }
- }
- DefineEngineMethod( WheeledVehicle, setWheelSpring, bool, ( S32 wheel, WheeledVehicleSpring* spring ),,
- "@brief Set the WheeledVehicleSpring datablock for this wheel.\n"
- "@param wheel index of the wheel to set (hub node #)\n"
- "@param spring WheeledVehicleSpring datablock\n"
- "@return true if successful, false if failed\n\n"
- "@tsexample\n"
- "%obj.setWheelSpring( 0, FrontSpring );\n"
- "@endtsexample\n" )
- {
- if (spring && wheel >= 0 && wheel < object->getWheelCount()) {
- object->setWheelSpring(wheel,spring);
- return true;
- }
- else {
- Con::warnf("setWheelSpring: invalid spring datablock or wheel index, vehicle has %d hubs",
- object->getWheelCount());
- return false;
- }
- }
- DefineEngineMethod( WheeledVehicle, getWheelCount, S32, (),,
- "@brief Get the number of wheels on this vehicle.\n"
- "@return the number of wheels (equal to the number of hub nodes defined in the model)\n\n" )
- {
- return object->getWheelCount();
- }
|