wheeledVehicle.cpp
Engine/source/T3D/vehicles/wheeledVehicle.cpp
Public Variables
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