Torque3D Documentation / _generateds / wheeledVehicle.cpp

wheeledVehicle.cpp

Engine/source/T3D/vehicles/wheeledVehicle.cpp

More...

Public Functions

ConsoleDocClass(WheeledVehicle , "@brief A wheeled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )
ConsoleDocClass(WheeledVehicleData , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">WheeledVehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )
ConsoleDocClass(WheeledVehicleSpring , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classwheeledvehicle/">WheeledVehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spring.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )
ConsoleDocClass(WheeledVehicleTire , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classwheeledvehicle/">WheeledVehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tire.\n\n</a>" "Tires act as springs and generate lateral and longitudinal forces <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move " "the vehicle. These distortion/spring forces are what <a href="/coding/file/colladautils_8h/#colladautils_8h_1a24cd315eae41894b5bbf77c8c0b097d4">convert</a> wheel angular " "velocity into forces that act on the rigid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">body.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )
DefineEngineMethod(WheeledVehicle , getWheelCount , S32 , () , "@brief Get the number of wheels on this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n</a>" "@return the number of wheels (equal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the number of hub nodes defined in the model)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )
DefineEngineMethod(WheeledVehicle , setWheelPowered , bool , (S32 wheel, bool powered) , "@brief Set whether the wheel is powered (has torque applied from the engine).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "A rear wheel drive car <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> example would set the front wheels <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> false, " "and the rear wheels <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n</a>" " @param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set(hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param powered flag indicating whether <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> power the wheel or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not\n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" )
DefineEngineMethod(WheeledVehicle , setWheelSpring , bool , (S32 wheel, WheeledVehicleSpring *spring) , "@brief Set the <a href="/coding/class/structwheeledvehiclespring/">WheeledVehicleSpring</a> datablock <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">wheel.\n</a>" "@param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set (hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param spring <a href="/coding/class/structwheeledvehiclespring/">WheeledVehicleSpring</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%obj.setWheelSpring(0, FrontSpring);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )
DefineEngineMethod(WheeledVehicle , setWheelSteering , bool , (S32 wheel, F32 steering) , "@brief Set how much the wheel is affected by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">steering.\n\n</a>" "The steering factor controls how much the wheel is rotated by the vehicle " "steering. For example, most cars would have their front wheels set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1. 0, " "and their rear wheels set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 0 since only the front wheels should <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">turn.\n\n</a>" "Negative values will turn the wheel in the opposite direction <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the steering " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">angle.\n</a>" " @param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set(hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param steering steering factor from -1(full inverse) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1(full)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" )
DefineEngineMethod(WheeledVehicle , setWheelTire , bool , (S32 wheel, WheeledVehicleTire *tire) , "@brief Set the <a href="/coding/class/structwheeledvehicletire/">WheeledVehicleTire</a> datablock <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">wheel.\n</a>" "@param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set (hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param tire <a href="/coding/class/structwheeledvehicletire/">WheeledVehicleTire</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%obj.setWheelTire(0, FrontTire);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

Detailed Description

Public Variables

U32 sClientCollisionMask 
F32 sIdleEngineVolume 
F32 sMinSquealVolume 

Public Functions

ConsoleDocClass(WheeledVehicle , "@brief A wheeled <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

ConsoleDocClass(WheeledVehicleData , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">WheeledVehicle.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

ConsoleDocClass(WheeledVehicleSpring , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classwheeledvehicle/">WheeledVehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spring.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

ConsoleDocClass(WheeledVehicleTire , "@brief Defines the properties of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classwheeledvehicle/">WheeledVehicle</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tire.\n\n</a>" "Tires act as springs and generate lateral and longitudinal forces <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move " "the vehicle. These distortion/spring forces are what <a href="/coding/file/colladautils_8h/#colladautils_8h_1a24cd315eae41894b5bbf77c8c0b097d4">convert</a> wheel angular " "velocity into forces that act on the rigid <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">body.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Vehicles\n</a>" )

DefineEngineMethod(WheeledVehicle , getWheelCount , S32 , () , "@brief Get the number of wheels on this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vehicle.\n</a>" "@return the number of wheels (equal <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the number of hub nodes defined in the model)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )

DefineEngineMethod(WheeledVehicle , setWheelPowered , bool , (S32 wheel, bool powered) , "@brief Set whether the wheel is powered (has torque applied from the engine).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "A rear wheel drive car <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> example would set the front wheels <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> false, " "and the rear wheels <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n</a>" " @param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set(hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param powered flag indicating whether <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> power the wheel or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">not\n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" )

DefineEngineMethod(WheeledVehicle , setWheelSpring , bool , (S32 wheel, WheeledVehicleSpring *spring) , "@brief Set the <a href="/coding/class/structwheeledvehiclespring/">WheeledVehicleSpring</a> datablock <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">wheel.\n</a>" "@param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set (hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param spring <a href="/coding/class/structwheeledvehiclespring/">WheeledVehicleSpring</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%obj.setWheelSpring(0, FrontSpring);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

DefineEngineMethod(WheeledVehicle , setWheelSteering , bool , (S32 wheel, F32 steering) , "@brief Set how much the wheel is affected by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">steering.\n\n</a>" "The steering factor controls how much the wheel is rotated by the vehicle " "steering. For example, most cars would have their front wheels set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1. 0, " "and their rear wheels set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 0 since only the front wheels should <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">turn.\n\n</a>" "Negative values will turn the wheel in the opposite direction <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the steering " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">angle.\n</a>" " @param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set(hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param steering steering factor from -1(full inverse) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> 1(full)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" )

DefineEngineMethod(WheeledVehicle , setWheelTire , bool , (S32 wheel, WheeledVehicleTire *tire) , "@brief Set the <a href="/coding/class/structwheeledvehicletire/">WheeledVehicleTire</a> datablock <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">wheel.\n</a>" "@param wheel index of the wheel <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set (hub node #)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param tire <a href="/coding/class/structwheeledvehicletire/">WheeledVehicleTire</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "%obj.setWheelTire(0, FrontTire);\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleData )

IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleSpring )

IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleTire )

IMPLEMENT_CO_NETOBJECT_V1(WheeledVehicle )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "T3D/vehicles/wheeledVehicle.h"
  26
  27#include "math/mMath.h"
  28#include "math/mathIO.h"
  29#include "console/simBase.h"
  30#include "console/console.h"
  31#include "console/consoleTypes.h"
  32#include "console/engineAPI.h"
  33#include "collision/clippedPolyList.h"
  34#include "collision/planeExtractor.h"
  35#include "core/stream/bitStream.h"
  36#include "core/dnet.h"
  37#include "T3D/gameBase/gameConnection.h"
  38#include "ts/tsShapeInstance.h"
  39#include "T3D/fx/particleEmitter.h"
  40#include "sfx/sfxSystem.h"
  41#include "sfx/sfxTrack.h"
  42#include "sfx/sfxSource.h"
  43#include "sfx/sfxTypes.h"
  44#include "scene/sceneManager.h"
  45#include "core/resourceManager.h"
  46#include "materials/materialDefinition.h"
  47#include "materials/baseMatInstance.h"
  48#include "lighting/lightQuery.h"
  49
  50
  51// Collision masks are used to determine what type of objects the
  52// wheeled vehicle will collide with.
  53static U32 sClientCollisionMask =
  54      TerrainObjectType     | PlayerObjectType  | 
  55      StaticShapeObjectType | VehicleObjectType | 
  56      VehicleBlockerObjectType;
  57
  58// Misc. sound constants
  59static F32 sMinSquealVolume = 0.05f;
  60static F32 sIdleEngineVolume = 0.2f;
  61
  62
  63//----------------------------------------------------------------------------
  64// Vehicle Tire Data Block
  65//----------------------------------------------------------------------------
  66
  67IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleTire);
  68
  69ConsoleDocClass( WheeledVehicleTire,
  70   "@brief Defines the properties of a WheeledVehicle tire.\n\n"
  71   "Tires act as springs and generate lateral and longitudinal forces to move "
  72   "the vehicle. These distortion/spring forces are what convert wheel angular "
  73   "velocity into forces that act on the rigid body.\n"
  74   "@ingroup Vehicles\n"
  75);
  76
  77WheeledVehicleTire::WheeledVehicleTire()
  78{
  79   shape = 0;
  80   shapeName = "";
  81   staticFriction = 1;
  82   kineticFriction = 0.5f;
  83   restitution = 1;
  84   radius = 0.6f;
  85   lateralForce = 10;
  86   lateralDamping = 1;
  87   lateralRelaxation = 1;
  88   longitudinalForce = 10;
  89   longitudinalDamping = 1;
  90   longitudinalRelaxation = 1;
  91   mass = 1.f;
  92}
  93
  94bool WheeledVehicleTire::preload(bool server, String &errorStr)
  95{
  96   // Load up the tire shape.  ShapeBase has an option to force a
  97   // CRC check, this is left out here, but could be easily added.
  98   if (shapeName && shapeName[0]) 
  99   {
 100
 101      // Load up the shape resource
 102      shape = ResourceManager::get().load(shapeName);
 103      if (!bool(shape)) 
 104      {
 105         errorStr = String::ToString("WheeledVehicleTire: Couldn't load shape \"%s\"",shapeName);
 106         return false;
 107      }
 108
 109      // Determinw wheel radius from the shape's bounding box.
 110      // The tire should be built with it's hub axis along the
 111      // object's Y axis.
 112      radius = shape->mBounds.len_z() / 2;
 113   }
 114
 115   return true;
 116}
 117
 118void WheeledVehicleTire::initPersistFields()
 119{
 120   addField( "shapeFile",TypeShapeFilename,Offset(shapeName,WheeledVehicleTire),
 121      "The path to the shape to use for the wheel." );
 122   addField( "mass", TypeF32, Offset(mass, WheeledVehicleTire),
 123      "The mass of the wheel.\nCurrently unused." );
 124   addField( "radius", TypeF32, Offset(radius, WheeledVehicleTire),
 125      "@brief The radius of the wheel.\n\n"
 126      "The radius is determined from the bounding box of the shape provided "
 127      "in the shapefile field, and does not need to be specified in script. "
 128      "The tire should be built with its hub axis along the object's Y-axis." );
 129   addField( "staticFriction", TypeF32, Offset(staticFriction, WheeledVehicleTire),
 130      "Tire friction when the wheel is not slipping (has traction)." );
 131   addField( "kineticFriction", TypeF32, Offset(kineticFriction, WheeledVehicleTire),
 132      "Tire friction when the wheel is slipping (no traction)." );
 133   addField( "restitution", TypeF32, Offset(restitution, WheeledVehicleTire),
 134      "Tire restitution.\nCurrently unused." );
 135   addField( "lateralForce", TypeF32, Offset(lateralForce, WheeledVehicleTire),
 136      "@brief Tire force perpendicular to the direction of movement.\n\n"
 137      "Lateral force can in simple terms be considered left/right steering "
 138      "force. WheeledVehicles are acted upon by forces generated by their tires "
 139      "and the lateralForce measures the magnitude of the force exerted on the "
 140      "vehicle when the tires are deformed along the x-axis. With real wheeled "
 141      "vehicles, tires are constantly being deformed and it is the interplay of "
 142      "deformation forces which determines how a vehicle moves. In Torque's "
 143      "simulation of vehicle physics, tire deformation obviously can't be handled "
 144      "with absolute realism, but the interplay of a vehicle's velocity, its "
 145      "engine's torque and braking forces, and its wheels' friction, lateral "
 146      "deformation, lateralDamping, lateralRelaxation, longitudinal deformation, "
 147      "longitudinalDamping, and longitudinalRelaxation forces, along with its "
 148      "wheels' angular velocity are combined to create a robust real-time "
 149      "physical simulation.\n\n"
 150      "For this field, the larger the value supplied for the lateralForce, the "
 151      "larger the effect steering maneuvers can have. In Torque tire forces are "
 152      "applied at a vehicle's wheel hubs." );
 153   addField( "lateralDamping", TypeF32, Offset(lateralDamping, WheeledVehicleTire),
 154      "Damping force applied against lateral forces generated by the tire.\n\n"
 155      "@see lateralForce" );
 156   addField( "lateralRelaxation", TypeF32, Offset(lateralRelaxation, WheeledVehicleTire),
 157      "@brief Relaxing force applied against lateral forces generated by the tire.\n\n"
 158      "The lateralRelaxation force measures how strongly the tire effectively "
 159      "un-deforms.\n\n@see lateralForce" );
 160   addField( "longitudinalForce", TypeF32, Offset(longitudinalForce, WheeledVehicleTire),
 161      "@brief Tire force in the direction of movement.\n\n"
 162      "Longitudinal force can in simple terms be considered forward/backward "
 163      "movement force. WheeledVehicles are acted upon by forces generated by "
 164      "their tires and the longitudinalForce measures the magnitude of the "
 165      "force exerted on the vehicle when the tires are deformed along the y-axis.\n\n"
 166      "For this field, the larger the value, the larger the effect "
 167      "acceleration/deceleration inputs have.\n\n"
 168      "@see lateralForce" );
 169   addField( "longitudinalDamping", TypeF32, Offset(longitudinalDamping, WheeledVehicleTire),
 170      "Damping force applied against longitudinal forces generated by the tire.\n\n"
 171      "@see longitudinalForce" );
 172   addField( "longitudinalRelaxation", TypeF32, Offset(longitudinalRelaxation, WheeledVehicleTire),
 173      "@brief Relaxing force applied against longitudinal forces generated by the tire.\n\n"
 174      "The longitudinalRelaxation force measures how strongly the tire effectively "
 175      "un-deforms.\n\n"
 176      "@see longitudinalForce" );
 177
 178   Parent::initPersistFields();
 179}
 180
 181void WheeledVehicleTire::packData(BitStream* stream)
 182{
 183   Parent::packData(stream);
 184
 185   stream->writeString(shapeName);
 186   stream->write(mass);
 187   stream->write(staticFriction);
 188   stream->write(kineticFriction);
 189   stream->write(restitution);
 190   stream->write(radius);
 191   stream->write(lateralForce);
 192   stream->write(lateralDamping);
 193   stream->write(lateralRelaxation);
 194   stream->write(longitudinalForce);
 195   stream->write(longitudinalDamping);
 196   stream->write(longitudinalRelaxation);
 197}
 198
 199void WheeledVehicleTire::unpackData(BitStream* stream)
 200{
 201   Parent::unpackData(stream);
 202
 203   shapeName = stream->readSTString();
 204   stream->read(&mass);
 205   stream->read(&staticFriction);
 206   stream->read(&kineticFriction);
 207   stream->read(&restitution);
 208   stream->read(&radius);
 209   stream->read(&lateralForce);
 210   stream->read(&lateralDamping);
 211   stream->read(&lateralRelaxation);
 212   stream->read(&longitudinalForce);
 213   stream->read(&longitudinalDamping);
 214   stream->read(&longitudinalRelaxation);
 215}
 216
 217
 218//----------------------------------------------------------------------------
 219// Vehicle Spring Data Block
 220//----------------------------------------------------------------------------
 221
 222IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleSpring);
 223
 224ConsoleDocClass( WheeledVehicleSpring,
 225   "@brief Defines the properties of a WheeledVehicle spring.\n\n"
 226   "@ingroup Vehicles\n"
 227);
 228
 229WheeledVehicleSpring::WheeledVehicleSpring()
 230{
 231   length = 1;
 232   force = 10;
 233   damping = 1;
 234   antiSway = 1;
 235}
 236
 237void WheeledVehicleSpring::initPersistFields()
 238{
 239   addField( "length", TypeF32, Offset(length, WheeledVehicleSpring),
 240      "@brief Maximum spring length. ie. how far the wheel can extend from the "
 241      "root hub position.\n\n"
 242      "This should be set to the vertical (Z) distance the hub travels in the "
 243      "associated spring animation." );
 244   addField( "force", TypeF32, Offset(force, WheeledVehicleSpring),
 245      "@brief Maximum spring force (when compressed to minimum length, 0).\n\n"
 246      "Increasing this will make the vehicle suspension ride higher (for a given "
 247      "vehicle mass), and also make the vehicle more bouncy when landing jumps." );
 248   addField( "damping", TypeF32, Offset(damping, WheeledVehicleSpring),
 249      "@brief Force applied to slow changes to the extension of this spring.\n\n"
 250      "Increasing this makes the suspension stiffer which can help stabilise "
 251      "bouncy vehicles." );
 252   addField( "antiSwayForce", TypeF32, Offset(antiSway, WheeledVehicleSpring),
 253      "@brief Force applied to equalize extension of the spring on the opposite "
 254      "wheel.\n\n"
 255      "This force helps to keep the suspension balanced when opposite wheels "
 256      "are at different heights." );
 257
 258   Parent::initPersistFields();
 259}
 260
 261void WheeledVehicleSpring::packData(BitStream* stream)
 262{
 263   Parent::packData(stream);
 264
 265   stream->write(length);
 266   stream->write(force);
 267   stream->write(damping);
 268   stream->write(antiSway);
 269}
 270
 271void WheeledVehicleSpring::unpackData(BitStream* stream)
 272{
 273   Parent::unpackData(stream);
 274
 275   stream->read(&length);
 276   stream->read(&force);
 277   stream->read(&damping);
 278   stream->read(&antiSway);
 279}
 280
 281
 282//----------------------------------------------------------------------------
 283// Wheeled Vehicle Data Block
 284//----------------------------------------------------------------------------
 285
 286//----------------------------------------------------------------------------
 287
 288IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleData);
 289
 290ConsoleDocClass( WheeledVehicleData,
 291   "@brief Defines the properties of a WheeledVehicle.\n\n"
 292   "@ingroup Vehicles\n"
 293);
 294
 295WheeledVehicleData::WheeledVehicleData()
 296{
 297   tireEmitter = 0;
 298   maxWheelSpeed = 40;
 299   engineTorque = 1;
 300   engineBrake = 1;
 301   brakeTorque = 1;
 302   brakeLightSequence = -1;
 303   steeringSequence = -1;
 304   wheelCount = 0;
 305   dMemset(&wheel, 0, sizeof(wheel));
 306   for (S32 i = 0; i < MaxSounds; i++)
 307      sound[i] = 0;
 308}
 309
 310
 311//----------------------------------------------------------------------------
 312/** Load the vehicle shape
 313   Loads and extracts information from the vehicle shape.
 314
 315   Wheel Sequences
 316      spring#        Wheel spring motion: time 0 = wheel fully extended,
 317                     the hub must be displaced, but not directly animated
 318                     as it will be rotated in code.
 319   Other Sequences
 320      steering       Wheel steering: time 0 = full right, 0.5 = center
 321      brakeLight     Brake light, time 0 = off, 1 = braking
 322
 323   Wheel Nodes
 324      hub#           Wheel hub
 325
 326   The steering and animation sequences are optional.
 327*/
 328bool WheeledVehicleData::preload(bool server, String &errorStr)
 329{
 330   if (!Parent::preload(server, errorStr))
 331      return false;
 332
 333   // A temporary shape instance is created so that we can
 334   // animate the shape and extract wheel information.
 335   TSShapeInstance* si = new TSShapeInstance(mShape, false);
 336
 337   // Resolve objects transmitted from server
 338   if (!server) {
 339      for (S32 i = 0; i < MaxSounds; i++)
 340      {
 341         if (!sfxResolve(&sound[i], errorStr))
 342         {
 343            delete si;
 344            return false;
 345         }
 346      }
 347
 348      if (tireEmitter)
 349         Sim::findObject(SimObjectId((uintptr_t)tireEmitter),tireEmitter);
 350   }
 351
 352   // Extract wheel information from the shape
 353   TSThread* thread = si->addThread();
 354   Wheel* wp = wheel;
 355   char buff[10];
 356   for (S32 i = 0; i < MaxWheels; i++) {
 357
 358      // The wheel must have a hub node to operate at all.
 359      dSprintf(buff,sizeof(buff),"hub%d",i);
 360      wp->springNode = mShape->findNode(buff);
 361      if (wp->springNode != -1) {
 362
 363         // Check for spring animation.. If there is none we just grab
 364         // the current position of the hub. Otherwise we'll animate
 365         // and get the position at time 0.
 366         dSprintf(buff,sizeof(buff),"spring%d",i);
 367         wp->springSequence = mShape->findSequence(buff);
 368         if (wp->springSequence == -1)
 369            si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos);
 370         else {
 371            si->setSequence(thread,wp->springSequence,0);
 372            si->animate();
 373            si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos);
 374
 375            // Determin the length of the animation so we can scale it
 376            // according the actual wheel position.
 377            Point3F downPos;
 378            si->setSequence(thread,wp->springSequence,1);
 379            si->animate();
 380            si->mNodeTransforms[wp->springNode].getColumn(3, &downPos);
 381            wp->springLength = wp->pos.z - downPos.z;
 382            if (!wp->springLength)
 383               wp->springSequence = -1;
 384         }
 385
 386         // Match wheels that are mirrored along the Y axis.
 387         mirrorWheel(wp);
 388         wp++;
 389      }
 390   }
 391   wheelCount = wp - wheel;
 392
 393   // Check for steering. Should think about normalizing the
 394   // steering animation the way the suspension is, but I don't
 395   // think it's as critical.
 396   steeringSequence = mShape->findSequence("steering");
 397
 398   // Brakes
 399   brakeLightSequence = mShape->findSequence("brakelight");
 400
 401   // Extract collision planes from shape collision detail level
 402   if (collisionDetails[0] != -1) {
 403      MatrixF imat(1);
 404      SphereF sphere;
 405      sphere.center = mShape->center;
 406      sphere.radius = mShape->mRadius;
 407      PlaneExtractorPolyList polyList;
 408      polyList.mPlaneList = &rigidBody.mPlaneList;
 409      polyList.setTransform(&imat, Point3F(1,1,1));
 410      si->buildPolyList(&polyList,collisionDetails[0]);
 411   }
 412
 413   delete si;
 414   return true;
 415}
 416
 417
 418//----------------------------------------------------------------------------
 419/** Find a matching lateral wheel
 420   Looks for a matching wheeling mirrored along the Y axis, within some
 421   tolerance (current 0.5m), if one is found, the two wheels are lined up.
 422*/
 423bool WheeledVehicleData::mirrorWheel(Wheel* we)
 424{
 425   we->opposite = -1;
 426   for (Wheel* wp = wheel; wp != we; wp++)
 427      if (mFabs(wp->pos.y - we->pos.y) < 0.5) 
 428      {
 429         we->pos.x = -wp->pos.x;
 430         we->pos.y = wp->pos.y;
 431         we->pos.z = wp->pos.z;
 432         we->opposite = wp - wheel;
 433         wp->opposite = we - wheel;
 434         return true;
 435      }
 436   return false;
 437}
 438
 439
 440//----------------------------------------------------------------------------
 441
 442void WheeledVehicleData::initPersistFields()
 443{
 444   addField( "jetSound", TYPEID< SFXTrack >(), Offset(sound[JetSound], WheeledVehicleData),
 445      "Looping sound played when the vehicle is jetting." );
 446   addField( "engineSound", TYPEID< SFXTrack >(), Offset(sound[EngineSound], WheeledVehicleData),
 447      "@brief Looping engine sound.\n\n"
 448      "The pitch is dynamically adjusted based on the current engine RPM" );
 449   addField("squealSound", TYPEID< SFXTrack >(), Offset(sound[SquealSound], WheeledVehicleData),
 450      "@brief Looping sound played while any of the wheels is slipping.\n\n"
 451      "The volume is dynamically adjusted based on how much the wheels are slipping." );
 452   addField("WheelImpactSound", TYPEID< SFXTrack >(), Offset(sound[WheelImpactSound], WheeledVehicleData),
 453      "Sound played when the wheels impact the ground.\nCurrently unused." );
 454
 455   addField("tireEmitter",TYPEID< ParticleEmitterData >(), Offset(tireEmitter, WheeledVehicleData),
 456      "ParticleEmitterData datablock used to generate particles from each wheel "
 457      "when the vehicle is moving and the wheel is in contact with the ground.");
 458   addField("maxWheelSpeed", TypeF32, Offset(maxWheelSpeed, WheeledVehicleData),
 459      "@brief Maximum linear velocity of each wheel.\n\n"
 460      "This caps the maximum speed of the vehicle." );
 461   addField("engineTorque", TypeF32, Offset(engineTorque, WheeledVehicleData),
 462      "@brief Torque available from the engine at 100% throttle.\n\n"
 463      "This controls vehicle acceleration. ie. how fast it will reach maximum speed." );
 464   addField("engineBrake", TypeF32, Offset(engineBrake, WheeledVehicleData),
 465      "@brief Braking torque applied by the engine when the throttle and brake "
 466      "are both 0.\n\n"
 467      "This controls how quickly the vehicle will coast to a stop." );
 468   addField("brakeTorque", TypeF32, Offset(brakeTorque, WheeledVehicleData),
 469      "@brief Torque applied when braking.\n\n"
 470      "This controls how fast the vehicle will stop when the brakes are applied." );
 471   
 472   Parent::initPersistFields();
 473}
 474
 475
 476//----------------------------------------------------------------------------
 477
 478void WheeledVehicleData::packData(BitStream* stream)
 479{
 480   Parent::packData(stream);
 481
 482   if (stream->writeFlag(tireEmitter))
 483      stream->writeRangedU32(mPacked ? SimObjectId((uintptr_t)tireEmitter):
 484         tireEmitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
 485
 486   for (S32 i = 0; i < MaxSounds; i++)
 487      sfxWrite( stream, sound[ i ] );
 488
 489   stream->write(maxWheelSpeed);
 490   stream->write(engineTorque);
 491   stream->write(engineBrake);
 492   stream->write(brakeTorque);
 493}
 494
 495void WheeledVehicleData::unpackData(BitStream* stream)
 496{
 497   Parent::unpackData(stream);
 498
 499   tireEmitter = stream->readFlag()?
 500      (ParticleEmitterData*)(uintptr_t)stream->readRangedU32(DataBlockObjectIdFirst,
 501         DataBlockObjectIdLast): 0;
 502
 503   for (S32 i = 0; i < MaxSounds; i++)
 504      sfxRead( stream, &sound[ i ] );
 505
 506   stream->read(&maxWheelSpeed);
 507   stream->read(&engineTorque);
 508   stream->read(&engineBrake);
 509   stream->read(&brakeTorque);
 510}
 511
 512
 513//----------------------------------------------------------------------------
 514// Wheeled Vehicle Class
 515//----------------------------------------------------------------------------
 516
 517//----------------------------------------------------------------------------
 518
 519IMPLEMENT_CO_NETOBJECT_V1(WheeledVehicle);
 520
 521ConsoleDocClass( WheeledVehicle,
 522   "@brief A wheeled vehicle.\n"
 523   "@ingroup Vehicles\n"
 524);
 525
 526WheeledVehicle::WheeledVehicle()
 527{
 528   mDataBlock = 0;
 529   mBraking = false;
 530   mJetSound = NULL;
 531   mEngineSound = NULL;
 532   mSquealSound = NULL;
 533   mTailLightThread = 0;
 534   mSteeringThread = 0;
 535
 536   for (S32 i = 0; i < WheeledVehicleData::MaxWheels; i++) {
 537      mWheel[i].springThread = 0;
 538      mWheel[i].Dy = mWheel[i].Dx = 0;
 539      mWheel[i].tire = 0;
 540      mWheel[i].spring = 0;
 541      mWheel[i].shapeInstance = 0;
 542      mWheel[i].steering = 0;
 543      mWheel[i].powered = true;
 544      mWheel[i].slipping = false;
 545   }
 546}
 547
 548WheeledVehicle::~WheeledVehicle()
 549{
 550}
 551
 552void WheeledVehicle::initPersistFields()
 553{
 554   Parent::initPersistFields();
 555}
 556
 557
 558//----------------------------------------------------------------------------
 559
 560bool WheeledVehicle::onAdd()
 561{
 562   if(!Parent::onAdd())
 563      return false;
 564
 565   addToScene();
 566   if (isServerObject())
 567      scriptOnAdd();
 568   return true;
 569}
 570
 571void WheeledVehicle::onRemove()
 572{
 573   // Delete the wheel resources
 574   if (mDataBlock != NULL)  {
 575      Wheel* wend = &mWheel[mDataBlock->wheelCount];
 576      for (Wheel* wheel = mWheel; wheel < wend; wheel++) {
 577         if (!wheel->emitter.isNull())
 578            wheel->emitter->deleteWhenEmpty();
 579         delete wheel->shapeInstance;
 580      }
 581   }
 582
 583   // Stop the sounds
 584   SFX_DELETE( mJetSound );
 585   SFX_DELETE( mEngineSound );
 586   SFX_DELETE( mSquealSound );
 587
 588   //
 589   scriptOnRemove();
 590   removeFromScene();
 591   Parent::onRemove();
 592}
 593
 594
 595//----------------------------------------------------------------------------
 596
 597bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr, bool reload)
 598{
 599   // Delete any existing wheel resources if we're switching
 600   // datablocks.
 601   if (mDataBlock) 
 602   {
 603      Wheel* wend = &mWheel[mDataBlock->wheelCount];
 604      for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
 605      {
 606         if (!wheel->emitter.isNull()) 
 607         {
 608            wheel->emitter->deleteWhenEmpty();
 609            wheel->emitter = 0;
 610         }
 611         delete wheel->shapeInstance;
 612         wheel->shapeInstance = 0;
 613      }
 614   }
 615
 616   // Load up the new datablock
 617   mDataBlock = dynamic_cast<WheeledVehicleData*>(dptr);
 618   if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload))
 619      return false;
 620
 621   // Set inertial tensor, default for the vehicle is sphere
 622   if (mDataBlock->massBox.x > 0 && mDataBlock->massBox.y > 0 && mDataBlock->massBox.z > 0)
 623      mRigid.setObjectInertia(mDataBlock->massBox);
 624   else
 625      mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents);
 626
 627   // Initialize the wheels...
 628   for (S32 i = 0; i < mDataBlock->wheelCount; i++) 
 629   {
 630      Wheel* wheel = &mWheel[i];
 631      wheel->data = &mDataBlock->wheel[i];
 632      wheel->tire = 0;
 633      wheel->spring = 0;
 634
 635      wheel->surface.contact = false;
 636      wheel->surface.object  = NULL;
 637      wheel->avel = 0;
 638      wheel->apos = 0;
 639      wheel->extension = 1;
 640      wheel->slip = 0;
 641
 642      wheel->springThread = 0;
 643      wheel->emitter = 0;
 644
 645      // Steering on the front tires by default
 646      if (wheel->data->pos.y > 0)
 647         wheel->steering = 1;
 648
 649      // Build wheel animation threads
 650      if (wheel->data->springSequence != -1) {
 651         wheel->springThread = mShapeInstance->addThread();
 652         mShapeInstance->setSequence(wheel->springThread,wheel->data->springSequence,0);
 653      }
 654
 655      // Each wheel get's it's own particle emitter
 656      if( mDataBlock->tireEmitter && isGhost() )
 657      {
 658         wheel->emitter = new ParticleEmitter;
 659         wheel->emitter->onNewDataBlock( mDataBlock->tireEmitter, false );
 660         wheel->emitter->registerObject();
 661      }
 662   }
 663
 664   // Steering sequence
 665   if (mDataBlock->steeringSequence != -1) {
 666      mSteeringThread = mShapeInstance->addThread();
 667      mShapeInstance->setSequence(mSteeringThread,mDataBlock->steeringSequence,0);
 668   }
 669   else
 670      mSteeringThread = 0;
 671
 672   // Brake light sequence
 673   if (mDataBlock->brakeLightSequence != -1) {
 674      mTailLightThread = mShapeInstance->addThread();
 675      mShapeInstance->setSequence(mTailLightThread,mDataBlock->brakeLightSequence,0);
 676   }
 677   else
 678      mTailLightThread = 0;
 679
 680   if (isGhost()) 
 681   {
 682      // Create the sounds ahead of time.  This reduces runtime
 683      // costs and makes the system easier to understand.
 684
 685      SFX_DELETE( mEngineSound );
 686      SFX_DELETE( mSquealSound );
 687      SFX_DELETE( mJetSound );
 688
 689      if ( mDataBlock->sound[WheeledVehicleData::EngineSound] )
 690         mEngineSound = SFX->createSource( mDataBlock->sound[WheeledVehicleData::EngineSound], &getTransform() );
 691
 692      if ( mDataBlock->sound[WheeledVehicleData::SquealSound] )
 693         mSquealSound = SFX->createSource( mDataBlock->sound[WheeledVehicleData::SquealSound], &getTransform() );
 694
 695      if ( mDataBlock->sound[WheeledVehicleData::JetSound] )
 696         mJetSound = SFX->createSource( mDataBlock->sound[WheeledVehicleData::JetSound], &getTransform() );
 697   }
 698
 699   scriptOnNewDataBlock();
 700   return true;
 701}
 702
 703
 704//----------------------------------------------------------------------------
 705
 706S32 WheeledVehicle::getWheelCount()
 707{
 708   // Return # of hubs defined on the car body
 709   return mDataBlock? mDataBlock->wheelCount: 0;
 710}
 711
 712void WheeledVehicle::setWheelSteering(S32 wheel,F32 steering)
 713{
 714   AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
 715   mWheel[wheel].steering = mClampF(steering,-1,1);
 716   setMaskBits(WheelMask);
 717}
 718
 719void WheeledVehicle::setWheelPowered(S32 wheel,bool powered)
 720{
 721   AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
 722   mWheel[wheel].powered = powered;
 723   setMaskBits(WheelMask);
 724}
 725
 726void WheeledVehicle::setWheelTire(S32 wheel,WheeledVehicleTire* tire)
 727{
 728   AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
 729   mWheel[wheel].tire = tire;
 730   setMaskBits(WheelMask);
 731}
 732
 733void WheeledVehicle::setWheelSpring(S32 wheel,WheeledVehicleSpring* spring)
 734{
 735   AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds");
 736   mWheel[wheel].spring = spring;
 737   setMaskBits(WheelMask);
 738}
 739
 740void WheeledVehicle::getWheelInstAndTransform( U32 index, TSShapeInstance** inst, MatrixF* xfrm ) const
 741{
 742   AssertFatal( index < WheeledVehicleData::MaxWheels,
 743      "WheeledVehicle::getWheelInstAndTransform() - Bad wheel index!" );
 744
 745   const Wheel* wheel = &mWheel[index];
 746   *inst = wheel->shapeInstance;
 747   
 748   if ( !xfrm || !wheel->shapeInstance )
 749      return;
 750
 751   MatrixF world = getRenderTransform();
 752   world.scale( mObjScale );
 753
 754   // Steering & spring extension
 755   MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering));
 756   Point3F pos = wheel->data->pos;
 757   pos.z -= wheel->spring->length * wheel->extension;
 758   hub.setColumn(3,pos);
 759   world.mul(hub);
 760
 761   // Wheel rotation
 762   MatrixF rot(EulerF(wheel->apos * M_2PI,0,0));
 763   world.mul(rot);
 764
 765   // Rotation the tire to face the right direction
 766   // (could pre-calculate this)
 767   MatrixF wrot(EulerF(0,0,(wheel->data->pos.x > 0)? M_PI/2: -M_PI/2));
 768   world.mul(wrot);
 769
 770   *xfrm = world;
 771}
 772
 773//----------------------------------------------------------------------------
 774
 775void WheeledVehicle::processTick(const Move* move)
 776{
 777   Parent::processTick(move);
 778}
 779
 780void WheeledVehicle::updateMove(const Move* move)
 781{
 782   Parent::updateMove(move);
 783
 784   // Brake on trigger
 785   mBraking = move->trigger[2];
 786
 787   // Set the tail brake light thread direction based on the brake state.
 788   if (mTailLightThread)
 789      mShapeInstance->setTimeScale(mTailLightThread, mBraking? 1.0f : -1.0f);
 790}
 791
 792
 793//----------------------------------------------------------------------------
 794
 795void WheeledVehicle::advanceTime(F32 dt)
 796{
 797   PROFILE_SCOPE( WheeledVehicle_AdvanceTime );
 798
 799   Parent::advanceTime(dt);
 800
 801   // Stick the wheels to the ground.  This is purely so they look
 802   // good while the vehicle is being interpolated.
 803   extendWheels();
 804
 805   // Update wheel angular position and slip, this is a client visual
 806   // feature only, it has no affect on the physics.
 807   F32 slipTotal = 0;
 808   F32 torqueTotal = 0;
 809
 810   Wheel* wend = &mWheel[mDataBlock->wheelCount];
 811   for (Wheel* wheel = mWheel; wheel < wend; wheel++)
 812      if (wheel->tire && wheel->spring) {
 813         // Update angular position
 814         wheel->apos += (wheel->avel * dt) / M_2PI;
 815         wheel->apos -= mFloor(wheel->apos);
 816         if (wheel->apos < 0)
 817            wheel->apos = 1 - wheel->apos;
 818
 819         // Keep track of largest slip
 820         slipTotal += wheel->slip;
 821         torqueTotal += wheel->torqueScale;
 822      }
 823
 824   // Update the sounds based on wheel slip and torque output
 825   updateSquealSound(slipTotal / mDataBlock->wheelCount);
 826   updateEngineSound(sIdleEngineVolume + (1 - sIdleEngineVolume) *
 827      (1 - (torqueTotal / mDataBlock->wheelCount)));
 828   updateJetSound();
 829
 830   updateWheelThreads();
 831   updateWheelParticles(dt);
 832
 833   // Update the steering animation: sequence time 0 is full right,
 834   // and time 0.5 is straight ahead.
 835   if (mSteeringThread) {
 836      F32 t = (mSteering.x * mFabs(mSteering.x)) / mDataBlock->maxSteeringAngle;
 837      mShapeInstance->setPos(mSteeringThread,0.5 - t * 0.5);
 838   }
 839
 840   // Animate the tail light. The direction of the thread is
 841   // set based on vehicle braking.
 842   if (mTailLightThread)
 843      mShapeInstance->advanceTime(dt,mTailLightThread);
 844}
 845
 846
 847//----------------------------------------------------------------------------
 848/** Update the rigid body forces on the vehicle
 849   This method calculates the forces acting on the body, including gravity,
 850   suspension & tire forces.
 851*/
 852void WheeledVehicle::updateForces(F32 dt)
 853{
 854   PROFILE_SCOPE( WheeledVehicle_UpdateForces );
 855
 856   extendWheels();
 857
 858   if (mDisableMove) return;
 859   F32 aMomentum = mMass / mDataBlock->wheelCount;
 860
 861   // Get the current matrix and extact vectors
 862   MatrixF currMatrix;
 863   mRigid.getTransform(&currMatrix);
 864
 865   Point3F bx,by,bz;
 866   currMatrix.getColumn(0,&bx);
 867   currMatrix.getColumn(1,&by);
 868   currMatrix.getColumn(2,&bz);
 869
 870   // Steering angles from current steering wheel position
 871   F32 quadraticSteering = -(mSteering.x * mFabs(mSteering.x));
 872   F32 cosSteering,sinSteering;
 873   mSinCos(quadraticSteering, sinSteering, cosSteering);
 874
 875   // Calculate Engine and brake torque values used later by in
 876   // wheel calculations.
 877   F32 engineTorque,brakeVel;
 878   if (mBraking) 
 879   {
 880      brakeVel = (mDataBlock->brakeTorque / aMomentum) * dt;
 881      engineTorque = 0;
 882   }
 883   else 
 884   {
 885      if (mThrottle) 
 886      {
 887         engineTorque = mDataBlock->engineTorque * mThrottle;
 888         brakeVel = 0;
 889         // Double the engineTorque to help out the jets
 890         if (mThrottle > 0 && mJetting)
 891            engineTorque *= 2;
 892      }
 893      else 
 894      {
 895         // Engine brake.
 896         brakeVel = (mDataBlock->engineBrake / aMomentum) * dt;
 897         engineTorque = 0;
 898      }
 899   }
 900
 901   // Integrate forces, we'll do this ourselves here instead of
 902   // relying on the rigid class which does it during movement.
 903   Wheel* wend = &mWheel[mDataBlock->wheelCount];
 904   mRigid.clearForces();
 905
 906   // Calculate vertical load for friction.  Divide up the spring
 907   // forces across all the wheels that are in contact with
 908   // the ground.
 909   U32 contactCount = 0;
 910   F32 verticalLoad = 0;
 911   for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
 912   {
 913      if (wheel->tire && wheel->spring && wheel->surface.contact) 
 914      {
 915         verticalLoad += wheel->spring->force * (1 - wheel->extension);
 916         contactCount++;
 917      }
 918   }
 919   if (contactCount)
 920      verticalLoad /= contactCount;
 921
 922   // Sum up spring and wheel torque forces
 923   for (Wheel* wheel = mWheel; wheel < wend; wheel++)  
 924   {
 925      if (!wheel->tire || !wheel->spring)
 926         continue;
 927
 928      F32 Fy = 0;
 929      if (wheel->surface.contact) 
 930      {
 931
 932         // First, let's compute the wheel's position, and worldspace velocity
 933         Point3F pos, r, localVel;
 934         currMatrix.mulP(wheel->data->pos, &pos);
 935         mRigid.getOriginVector(pos,&r);
 936         mRigid.getVelocity(r, &localVel);
 937
 938         // Spring force & damping
 939         F32 spring  = wheel->spring->force * (1 - wheel->extension);
 940
 941         if (wheel->extension == 0) //spring fully compressed
 942         {
 943            // Apply impulses to the rigid body to keep it from
 944            // penetrating the surface.
 945            F32 n = -mDot(localVel,Point3F(0,0,1));
 946            if (n >= 0)
 947            {
 948               // Collision impulse, straight forward force stuff.
 949               F32 d = mRigid.getZeroImpulse(r,Point3F(0,0,1));
 950               F32 j = n * (1 + mRigid.restitution) * d;
 951               mRigid.force += Point3F(0,0,1) * j;
 952            }
 953         }
 954
 955         F32 damping = wheel->spring->damping * -(mDot(bz, localVel) / wheel->spring->length);
 956         if (damping < 0)
 957            damping = 0;
 958
 959         // Anti-sway force based on difference in suspension extension
 960         F32 antiSway = 0;
 961         if (wheel->data->opposite != -1)
 962         {
 963            Wheel* oppositeWheel = &mWheel[wheel->data->opposite];
 964            if (oppositeWheel->surface.contact)
 965               antiSway = ((oppositeWheel->extension - wheel->extension) *
 966                  wheel->spring->antiSway);
 967            if (antiSway < 0)
 968               antiSway = 0;
 969         }
 970
 971         // Spring forces act straight up and are applied at the
 972         // spring's root position.
 973         Point3F t, forceVector = bz * (spring + damping + antiSway);
 974         mCross(r, forceVector, &t);
 975         mRigid.torque += t;
 976         mRigid.force += forceVector;
 977
 978         // Tire direction vectors perpendicular to surface normal
 979         Point3F wheelXVec = bx * cosSteering;
 980         wheelXVec += by * sinSteering * wheel->steering;
 981         Point3F tireX, tireY;
 982         mCross(wheel->surface.normal, wheelXVec, &tireY);
 983         tireY.normalize();
 984         mCross(tireY, wheel->surface.normal, &tireX);
 985         tireX.normalize();
 986
 987         // Velocity of tire at the surface contact
 988         Point3F wheelContact, wheelVelocity;
 989         mRigid.getOriginVector(wheel->surface.pos,&wheelContact);
 990         mRigid.getVelocity(wheelContact, &wheelVelocity);
 991
 992         F32 xVelocity = mDot(tireX, wheelVelocity);
 993         F32 yVelocity = mDot(tireY, wheelVelocity);
 994
 995         // Tires act as springs and generate lateral and longitudinal
 996         // forces to move the vehicle. These distortion/spring forces
 997         // are what convert wheel angular velocity into forces that
 998         // act on the rigid body.
 999
1000         // Longitudinal tire deformation force
1001         F32 ddy = (wheel->avel * wheel->tire->radius - yVelocity) -
1002            wheel->tire->longitudinalRelaxation *
1003            mFabs(wheel->avel) * wheel->Dy;
1004         wheel->Dy += ddy * dt;
1005         Fy = (wheel->tire->longitudinalForce * wheel->Dy +
1006            wheel->tire->longitudinalDamping * ddy);
1007
1008         // Lateral tire deformation force
1009         F32 ddx = xVelocity - wheel->tire->lateralRelaxation *
1010            mFabs(wheel->avel) * wheel->Dx;
1011         wheel->Dx += ddx * dt;
1012         F32 Fx = -(wheel->tire->lateralForce * wheel->Dx +
1013            wheel->tire->lateralDamping * ddx);
1014
1015         // Vertical load on the tire
1016         verticalLoad = spring + damping + antiSway;
1017         if (verticalLoad < 0)
1018            verticalLoad = 0;
1019
1020         // Adjust tire forces based on friction
1021         F32 surfaceFriction = 1;
1022         F32 mu = surfaceFriction * (wheel->slipping ? wheel->tire->kineticFriction : wheel->tire->staticFriction);
1023         F32 Fn = verticalLoad * mu; Fn *= Fn;
1024         F32 Fw = Fx * Fx + Fy * Fy;
1025         if (Fw > Fn) 
1026         {
1027            F32 K = mSqrt(Fn / Fw);
1028            Fy *= K;
1029            Fx *= K;
1030            wheel->Dy *= K;
1031            wheel->Dx *= K;
1032            wheel->slip = 1 - K;
1033            wheel->slipping = true;
1034         }
1035         else 
1036         {
1037            wheel->slipping = false;
1038            wheel->slip = 0;
1039         }
1040
1041         // Tire forces act through the tire direction vectors parallel
1042         // to the surface and are applied at the wheel hub.
1043         forceVector = (tireX * Fx) + (tireY * Fy);
1044         pos -= bz * (wheel->spring->length * wheel->extension);
1045         mRigid.getOriginVector(pos,&r);
1046         mCross(r, forceVector, &t);
1047         mRigid.torque += t;
1048         mRigid.force += forceVector;
1049      }
1050      else 
1051      {
1052         // Wheel not in contact with the ground
1053         wheel->torqueScale  = 0;
1054         wheel->slip = 0;
1055
1056         // Relax the tire deformation
1057         wheel->Dy += (-wheel->tire->longitudinalRelaxation *
1058                       mFabs(wheel->avel) * wheel->Dy) * dt;
1059         wheel->Dx += (-wheel->tire->lateralRelaxation *
1060                       mFabs(wheel->avel) * wheel->Dx) * dt;
1061      }
1062
1063      // Adjust the wheel's angular velocity based on engine torque
1064      // and tire deformation forces.
1065      if (wheel->powered) 
1066      {
1067         F32 maxAvel = mDataBlock->maxWheelSpeed / wheel->tire->radius;
1068         wheel->torqueScale = (mFabs(wheel->avel) > maxAvel) ? 0 :
1069            1 - (mFabs(wheel->avel) / maxAvel);
1070      }
1071      else
1072         wheel->torqueScale = 0;
1073      wheel->avel += (((wheel->torqueScale * engineTorque) - Fy *
1074         wheel->tire->radius) / aMomentum) * dt;
1075
1076      // Adjust the wheel's angular velocity based on brake torque.
1077      // This is done after avel update to make sure we come to a
1078      // complete stop.
1079      if (brakeVel > mFabs(wheel->avel))
1080         wheel->avel = 0;
1081      else
1082         if (wheel->avel > 0)
1083            wheel->avel -= brakeVel;
1084         else
1085            wheel->avel += brakeVel;
1086   }
1087
1088   // Jet Force
1089   if (mJetting)
1090      mRigid.force += by * mDataBlock->jetForce;
1091
1092   // Add in force from physical zones...
1093   mRigid.force += mAppliedForce;
1094
1095   // Container drag & buoyancy
1096   mRigid.force  += Point3F(0, 0, mRigid.mass * mNetGravity);
1097   mRigid.force  -= mRigid.linVelocity * mDrag;
1098   mRigid.torque -= mRigid.angMomentum * mDrag;
1099
1100   // If we've added anything other than gravity, then we're no
1101   // longer at rest. Could test this a little more efficiently...
1102   if (mRigid.atRest && (mRigid.force.len() || mRigid.torque.len()))
1103      mRigid.atRest = false;
1104
1105   // Integrate and update velocity
1106   mRigid.linMomentum += mRigid.force * dt;
1107   mRigid.angMomentum += mRigid.torque * dt;
1108   mRigid.updateVelocity();
1109
1110   // Since we've already done all the work, just need to clear this out.
1111   mRigid.clearForces();
1112
1113   // If we're still atRest, make sure we're not accumulating anything
1114   if (mRigid.atRest)
1115      mRigid.setAtRest();
1116}
1117
1118
1119//----------------------------------------------------------------------------
1120/** Extend the wheels
1121   The wheels are extended until they contact a surface. The extension
1122   is instantaneous.  The wheels are extended before force calculations and
1123   also on during client side interpolation (so that the wheels are glued
1124   to the ground).
1125*/
1126void WheeledVehicle::extendWheels(bool clientHack)
1127{
1128   PROFILE_SCOPE( WheeledVehicle_ExtendWheels );
1129
1130   disableCollision();
1131
1132   MatrixF currMatrix;
1133   
1134   if(clientHack)
1135      currMatrix = getRenderTransform();
1136   else
1137      mRigid.getTransform(&currMatrix);
1138   
1139
1140   // Does a single ray cast down for now... this will have to be
1141   // changed to something a little more complicated to avoid getting
1142   // stuck in cracks.
1143   Wheel* wend = &mWheel[mDataBlock->wheelCount];
1144   for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1145   {
1146      if (wheel->tire && wheel->spring) 
1147      {
1148         wheel->extension = 1;
1149
1150         // The ray is cast from the spring mount point to the tip of
1151         // the tire.  If there is a collision the spring extension is
1152         // adjust to remove the tire radius.
1153         Point3F sp,vec;
1154         currMatrix.mulP(wheel->data->pos,&sp);
1155         currMatrix.mulV(VectorF(0,0,-wheel->spring->length),&vec);
1156         F32 ts = wheel->tire->radius / wheel->spring->length;
1157         Point3F ep = sp + (vec * (1 + ts));
1158         ts = ts / (1+ts);
1159
1160         RayInfo rInfo;
1161         if (mContainer->castRay(sp, ep, sClientCollisionMask & ~<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084aabc4c763464f732b143d331ec4c15dce">PlayerObjectType</a>, &rInfo)) 
1162         {
1163            wheel->surface.contact  = true;
1164            wheel->extension = (rInfo.t < ts)? 0: (rInfo.t - ts) / (1 - ts);
1165            wheel->surface.normal   = rInfo.normal;
1166            wheel->surface.pos      = rInfo.point;
1167            wheel->surface.material = rInfo.material;
1168            wheel->surface.object   = rInfo.object;
1169         }
1170         else 
1171         {
1172            wheel->surface.contact = false;
1173            wheel->slipping = true;
1174         }
1175      }
1176   }
1177   enableCollision();
1178}
1179
1180
1181//----------------------------------------------------------------------------
1182/** Update wheel steering and suspension threads.
1183   These animations are purely cosmetic and this method is only invoked
1184   on the client.
1185*/
1186void WheeledVehicle::updateWheelThreads()
1187{
1188   Wheel* wend = &mWheel[mDataBlock->wheelCount];
1189   for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1190   {
1191      if (wheel->tire && wheel->spring && wheel->springThread) 
1192      {
1193         // Scale the spring animation time to match the current
1194         // position of the wheel.  We'll also check to make sure
1195         // the animation is long enough, if it isn't, just stick
1196         // it at the end.
1197         F32 pos = wheel->extension * wheel->spring->length;
1198         if (pos > wheel->data->springLength)
1199            pos = 1;
1200         else
1201            pos /= wheel->data->springLength;
1202         mShapeInstance->setPos(wheel->springThread,pos);
1203      }
1204   }
1205}
1206
1207//----------------------------------------------------------------------------
1208/** Update wheel particles effects
1209   These animations are purely cosmetic and this method is only invoked
1210   on the client.  Particles are emitted as long as the moving.
1211*/
1212void WheeledVehicle::updateWheelParticles(F32 dt)
1213{
1214   // OMG l33t hax
1215   extendWheels(true);
1216   
1217   Point3F vel = Parent::getVelocity();
1218   F32 speed = vel.len();
1219
1220   // Don't bother if we're not moving.
1221   if (speed > 1.0f)  
1222   {
1223      Point3F axis = vel;
1224      axis.normalize();
1225
1226      Wheel* wend = &mWheel[mDataBlock->wheelCount];
1227      for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1228      {
1229         // Is this wheel in contact with the ground?
1230         if (wheel->tire && wheel->spring && !wheel->emitter.isNull() &&
1231               wheel->surface.contact && wheel->surface.object )
1232         {
1233            Material* material = ( wheel->surface.material ? dynamic_cast< Material* >( wheel->surface.material->getMaterial() ) : 0 );
1234
1235            if( material)//&& material->mShowDust )
1236            {
1237               LinearColorF colorList[ ParticleData::PDC_NUM_KEYS ];
1238
1239               for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x )
1240                  colorList[ x ] = material->mEffectColor[ x ];
1241               for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x )
1242                  colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 );
1243
1244               wheel->emitter->setColors( colorList );
1245
1246               // Emit the dust, the density (time) is scaled by the
1247               // the vehicles velocity.
1248               wheel->emitter->emitParticles( wheel->surface.pos, true,
1249                  axis, vel, (U32)(3/*dt * (speed / mDataBlock->maxWheelSpeed) * 1000 * wheel->slip*/));
1250            }
1251         }
1252      }
1253   }
1254}
1255
1256
1257//----------------------------------------------------------------------------
1258/** Update engine sound
1259   This method is only invoked by clients.
1260*/
1261void WheeledVehicle::updateEngineSound(F32 level)
1262{
1263   if ( !mEngineSound )
1264      return;
1265
1266   if ( !mEngineSound->isPlaying() )
1267      mEngineSound->play();
1268
1269   mEngineSound->setTransform( getTransform() );
1270   mEngineSound->setVelocity( getVelocity() );
1271   //mEngineSound->setVolume( level );
1272
1273   // Adjust pitch
1274   F32 pitch = ((level-sIdleEngineVolume) * 1.3f);
1275   if (pitch < 0.4f)  
1276      pitch = 0.4f;
1277
1278   mEngineSound->setPitch( pitch );
1279}
1280
1281
1282//----------------------------------------------------------------------------
1283/** Update wheel skid sound
1284   This method is only invoked by clients.
1285*/
1286void WheeledVehicle::updateSquealSound(F32 level)
1287{
1288   if ( !mSquealSound )
1289      return;
1290
1291   if ( level < sMinSquealVolume ) 
1292   {
1293      mSquealSound->stop();
1294      return;
1295   }
1296
1297   if ( !mSquealSound->isPlaying() )
1298      mSquealSound->play();
1299
1300   mSquealSound->setTransform( getTransform() );
1301   mSquealSound->setVolume( level );
1302}
1303
1304
1305//----------------------------------------------------------------------------
1306/** Update jet sound
1307   This method is only invoked by clients.
1308*/
1309void WheeledVehicle::updateJetSound()
1310{
1311   if ( !mJetSound )
1312      return;
1313
1314   if ( !mJetting ) 
1315   {
1316      mJetSound->stop();
1317      return;
1318   }
1319
1320   if ( !mJetSound->isPlaying() )
1321      mJetSound->play();
1322
1323   mJetSound->setTransform( getTransform() );
1324}
1325
1326
1327//----------------------------------------------------------------------------
1328
1329U32 WheeledVehicle::getCollisionMask()
1330{
1331   return sClientCollisionMask;
1332}
1333
1334
1335//----------------------------------------------------------------------------
1336/** Build a collision polylist
1337   The polylist is filled with polygons representing the collision volume
1338   and the wheels.
1339*/
1340bool WheeledVehicle::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
1341{
1342   PROFILE_SCOPE( WheeledVehicle_BuildPolyList );
1343
1344   // Parent will take care of body collision.
1345   Parent::buildPolyList(context, polyList,box,sphere);
1346
1347   // Add wheels as boxes.
1348   Wheel* wend = &mWheel[mDataBlock->wheelCount];
1349   for (Wheel* wheel = mWheel; wheel < wend; wheel++) {
1350      if (wheel->tire && wheel->spring) {
1351         Box3F wbox;
1352         F32 radius = wheel->tire->radius;
1353         wbox.minExtents.x = -(wbox.maxExtents.x = radius / 2);
1354         wbox.minExtents.y = -(wbox.maxExtents.y = radius);
1355         wbox.minExtents.z = -(wbox.maxExtents.z = radius);
1356         MatrixF mat = mObjToWorld;
1357
1358         Point3F sp,vec;
1359         mObjToWorld.mulP(wheel->data->pos,&sp);
1360         mObjToWorld.mulV(VectorF(0,0,-wheel->spring->length),&vec);
1361         Point3F ep = sp + (vec * wheel->extension);
1362         mat.setColumn(3,ep);
1363         polyList->setTransform(&mat,Point3F(1,1,1));
1364         polyList->addBox(wbox);
1365      }
1366   }
1367   return !polyList->isEmpty();
1368}
1369
1370void WheeledVehicle::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex )
1371{
1372   Parent::prepBatchRender( state, mountedImageIndex );
1373
1374   if ( mountedImageIndex != -1 )
1375      return;
1376
1377   // Set up our render state *here*, 
1378   // before the push world matrix, so 
1379   // that wheel rendering will be correct.
1380   TSRenderState rdata;
1381   rdata.setSceneState( state );
1382
1383   // We might have some forward lit materials
1384   // so pass down a query to gather lights.
1385   LightQuery query;
1386   query.init( getWorldSphere() );
1387   rdata.setLightQuery( &query );
1388
1389   // Shape transform
1390   GFX->pushWorldMatrix();
1391
1392   MatrixF mat = getRenderTransform();
1393   mat.scale( mObjScale );
1394   GFX->setWorldMatrix( mat );
1395
1396   Wheel* wend = &mWheel[mDataBlock->wheelCount];
1397   for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1398   {
1399      if (wheel->shapeInstance) 
1400      {
1401         GFX->pushWorldMatrix();
1402
1403         // Steering & spring extension
1404         MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering));
1405         Point3F pos = wheel->data->pos;
1406         pos.z -= wheel->spring->length * wheel->extension;
1407         hub.setColumn(3,pos);
1408
1409         GFX->multWorld(hub);
1410
1411         // Wheel rotation
1412         MatrixF rot(EulerF(wheel->apos * M_2PI,0,0));
1413         GFX->multWorld(rot);
1414
1415         // Rotation the tire to face the right direction
1416         // (could pre-calculate this)
1417         MatrixF wrot(EulerF(0,0,(wheel->data->pos.x > 0)? M_PI/2: -M_PI/2));
1418         GFX->multWorld(wrot);
1419
1420         // Render!
1421         wheel->shapeInstance->animate();
1422         wheel->shapeInstance->render( rdata );
1423
1424         if (mCloakLevel != 0.0f)
1425            wheel->shapeInstance->setAlphaAlways(1.0f - mCloakLevel);
1426         else
1427            wheel->shapeInstance->setAlphaAlways(1.0f);
1428
1429         GFX->popWorldMatrix();
1430      }
1431   }
1432
1433   GFX->popWorldMatrix();
1434
1435}
1436
1437//----------------------------------------------------------------------------
1438
1439void WheeledVehicle::writePacketData(GameConnection *connection, BitStream *stream)
1440{
1441   Parent::writePacketData(connection, stream);
1442   stream->writeFlag(mBraking);
1443
1444   Wheel* wend = &mWheel[mDataBlock->wheelCount];
1445   for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1446   {
1447      stream->write(wheel->avel);
1448      stream->write(wheel->Dy);
1449      stream->write(wheel->Dx);
1450      stream->writeFlag(wheel->slipping);
1451   }
1452}
1453
1454void WheeledVehicle::readPacketData(GameConnection *connection, BitStream *stream)
1455{
1456   Parent::readPacketData(connection, stream);
1457   mBraking = stream->readFlag();
1458
1459   Wheel* wend = &mWheel[mDataBlock->wheelCount];
1460   for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1461   {
1462      stream->read(&wheel->avel);
1463      stream->read(&wheel->Dy);
1464      stream->read(&wheel->Dx);
1465      wheel->slipping = stream->readFlag();
1466   }
1467
1468   // Rigid state is transmitted by the parent...
1469   setPosition(mRigid.linPosition,mRigid.angPosition);
1470   mDelta.pos = mRigid.linPosition;
1471   mDelta.rot[1] = mRigid.angPosition;
1472}
1473
1474
1475//----------------------------------------------------------------------------
1476
1477U32 WheeledVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
1478{
1479   U32 retMask = Parent::packUpdate(con, mask, stream);
1480
1481   // Update wheel datablock information
1482   if (stream->writeFlag(mask & WheelMask)) 
1483   {
1484      Wheel* wend = &mWheel[mDataBlock->wheelCount];
1485      for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1486      {
1487         if (stream->writeFlag(wheel->tire && wheel->spring)) 
1488         {
1489            stream->writeRangedU32(wheel->tire->getId(),
1490               DataBlockObjectIdFirst,DataBlockObjectIdLast);
1491            stream->writeRangedU32(wheel->spring->getId(),
1492               DataBlockObjectIdFirst,DataBlockObjectIdLast);
1493            stream->writeFlag(wheel->powered);
1494
1495            // Steering must be sent with full precision as it's
1496            // used directly in state force calculations.
1497            stream->write(wheel->steering);
1498         }
1499      }
1500   }
1501
1502   // The rest of the data is part of the control object packet update.
1503   // If we're controlled by this client, we don't need to send it.
1504   if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
1505      return retMask;
1506
1507   stream->writeFlag(mBraking);
1508
1509   if (stream->writeFlag(mask & PositionMask)) 
1510   {
1511      Wheel* wend = &mWheel[mDataBlock->wheelCount];
1512      for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1513      {
1514         stream->write(wheel->avel);
1515         stream->write(wheel->Dy);
1516         stream->write(wheel->Dx);
1517      }
1518   }
1519   return retMask;
1520}
1521
1522void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream)
1523{
1524   Parent::unpackUpdate(con,stream);
1525
1526   // Update wheel datablock information
1527   if (stream->readFlag()) 
1528   {
1529      Wheel* wend = &mWheel[mDataBlock->wheelCount];
1530      for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1531      {
1532         if (stream->readFlag()) 
1533         {
1534            SimObjectId tid = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast);
1535            SimObjectId sid = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast);
1536            if (!Sim::findObject(tid,wheel->tire) || !Sim::findObject(sid,wheel->spring)) 
1537            {
1538               con->setLastError("Invalid packet WheeledVehicle::unpackUpdate()");
1539               return;
1540            }
1541            wheel->powered = stream->readFlag();
1542            stream->read(&wheel->steering);
1543
1544            // Create an instance of the tire for rendering
1545            delete wheel->shapeInstance;
1546            wheel->shapeInstance = (wheel->tire->shape == NULL) ? 0:
1547               new TSShapeInstance(wheel->tire->shape);
1548         }
1549      }
1550   }
1551
1552   // After this is data that we only need if we're not the
1553   // controlling client.
1554   if (stream->readFlag())
1555      return;
1556
1557   mBraking = stream->readFlag();
1558
1559   if (stream->readFlag()) 
1560   {
1561      Wheel* wend = &mWheel[mDataBlock->wheelCount];
1562      for (Wheel* wheel = mWheel; wheel < wend; wheel++) 
1563      {
1564         stream->read(&wheel->avel);
1565         stream->read(&wheel->Dy);
1566         stream->read(&wheel->Dx);
1567      }
1568   }
1569}
1570
1571
1572//----------------------------------------------------------------------------
1573// Console Methods
1574//----------------------------------------------------------------------------
1575
1576//----------------------------------------------------------------------------
1577
1578DefineEngineMethod( WheeledVehicle, setWheelSteering, bool, ( S32 wheel, F32 steering ),,
1579   "@brief Set how much the wheel is affected by steering.\n\n"
1580   "The steering factor controls how much the wheel is rotated by the vehicle "
1581   "steering. For example, most cars would have their front wheels set to 1.0, "
1582   "and their rear wheels set to 0 since only the front wheels should turn.\n\n"
1583   "Negative values will turn the wheel in the opposite direction to the steering "
1584   "angle.\n"
1585   "@param wheel index of the wheel to set (hub node #)\n"
1586   "@param steering steering factor from -1 (full inverse) to 1 (full)\n"
1587   "@return true if successful, false if failed\n\n" )
1588{
1589   if ( wheel >= 0 && wheel < object->getWheelCount() ) {
1590      object->setWheelSteering( wheel, steering );
1591      return true;
1592   }
1593   else
1594      Con::warnf("setWheelSteering: wheel index %d out of bounds, vehicle has %d hubs",
1595         wheel, object->getWheelCount());
1596   return false;
1597}
1598
1599DefineEngineMethod( WheeledVehicle, setWheelPowered, bool, ( S32 wheel, bool powered ),,
1600   "@brief Set whether the wheel is powered (has torque applied from the engine).\n\n"
1601   "A rear wheel drive car for example would set the front wheels to false, "
1602   "and the rear wheels to true.\n"
1603   "@param wheel index of the wheel to set (hub node #)\n"
1604   "@param powered flag indicating whether to power the wheel or not\n"
1605   "@return true if successful, false if failed\n\n" )
1606{
1607   if ( wheel >= 0 && wheel < object->getWheelCount() ) {
1608      object->setWheelPowered( wheel, powered );
1609      return true;
1610   }
1611   else
1612      Con::warnf("setWheelPowered: wheel index %d out of bounds, vehicle has %d hubs",
1613         wheel, object->getWheelCount());
1614   return false;
1615}
1616
1617DefineEngineMethod( WheeledVehicle, setWheelTire, bool, ( S32 wheel, WheeledVehicleTire* tire ),,
1618   "@brief Set the WheeledVehicleTire datablock for this wheel.\n"
1619   "@param wheel index of the wheel to set (hub node #)\n"
1620   "@param tire WheeledVehicleTire datablock\n"
1621   "@return true if successful, false if failed\n\n"
1622   "@tsexample\n"
1623   "%obj.setWheelTire( 0, FrontTire );\n"
1624   "@endtsexample\n" )
1625{
1626   if (wheel >= 0 && wheel < object->getWheelCount()) {
1627      object->setWheelTire(wheel,tire);
1628      return true;
1629   }
1630   else {
1631      Con::warnf("setWheelTire: invalid tire datablock or wheel index, vehicle has %d hubs",
1632         object->getWheelCount());
1633      return false;
1634   }
1635}
1636
1637DefineEngineMethod( WheeledVehicle, setWheelSpring, bool, ( S32 wheel, WheeledVehicleSpring* spring ),,
1638   "@brief Set the WheeledVehicleSpring datablock for this wheel.\n"
1639   "@param wheel index of the wheel to set (hub node #)\n"
1640   "@param spring WheeledVehicleSpring datablock\n"
1641   "@return true if successful, false if failed\n\n"
1642   "@tsexample\n"
1643   "%obj.setWheelSpring( 0, FrontSpring );\n"
1644   "@endtsexample\n" )
1645{
1646   if (spring && wheel >= 0 && wheel < object->getWheelCount()) {
1647      object->setWheelSpring(wheel,spring);
1648      return true;
1649   }
1650   else {
1651      Con::warnf("setWheelSpring: invalid spring datablock or wheel index, vehicle has %d hubs",
1652         object->getWheelCount());
1653      return false;
1654   }
1655}
1656
1657DefineEngineMethod( WheeledVehicle, getWheelCount, S32, (),,
1658   "@brief Get the number of wheels on this vehicle.\n"
1659   "@return the number of wheels (equal to the number of hub nodes defined in the model)\n\n" )
1660{
1661   return object->getWheelCount();
1662}
1663