Public Functions
ConsoleDocClass(Debris , "@brief Base debris class. Uses the <a href="/coding/class/structdebrisdata/">DebrisData</a> datablock <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> properties of individual debris <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n\n</a>" "<a href="/coding/class/classdebris/">Debris</a> is typically made up of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> shape and up <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> two particle emitters. In most cases <a href="/coding/class/classdebris/">Debris</a> objects are " "not created directly. They are usually produced automatically by other means, such as through the <a href="/coding/class/classexplosion/">Explosion</a> " "class. When an explosion goes off, its <a href="/coding/class/classexplosiondata/">ExplosionData</a> datablock determines what <a href="/coding/class/classdebris/">Debris</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">emit.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/classexplosiondata/">ExplosionData</a>(GrenadeLauncherExplosion)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "//Assiging debris <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">data\n</a>" " debris=GrenadeDebris;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "//Adjust how debris is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ejected\n</a>" " debrisThetaMin=10;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " debrisThetaMax=60;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " debrisNum=4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " debrisNumVariance=2;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " debrisVelocity=25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " debrisVelocityVariance=5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "//Note:other <a href="/coding/class/classexplosiondata/">ExplosionData</a> properties are not listed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">example\n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" " @note <a href="/coding/class/classdebris/">Debris</a> are client side only <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">DebrisData\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ExplosionData\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Explosion\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
ConsoleDocClass(DebrisData , "@brief Stores properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> an individual debris <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n\n</a>" "<a href="/coding/class/structdebrisdata/">DebrisData</a> defines the base properties <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classdebris/">Debris</a> object. Typically you'll want <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classdebris/">Debris</a> object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> consist of " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> shape and possibly up <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> two particle emitters. The <a href="/coding/class/structdebrisdata/">DebrisData</a> datablock provides the definition <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> these items, " "along with physical properties and how <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classdebris/">Debris</a> object will react <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> other game objects, such as water and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">terrain.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "datablock <a href="/coding/class/structdebrisdata/">DebrisData</a>(GrenadeDebris)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " shapeFile=\"art/shapes/weapons/ramrifle/debris.dts\";\n" " emitters[0] = GrenadeDebrisFireEmitter;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " elasticity = 0.4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " friction = 0.25;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " numBounces = 3;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " bounceVariance = 1;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " explodeOnMaxBounce = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " staticOnMaxBounce = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " snapOnMaxBounce = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " minSpinSpeed = 200;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " maxSpinSpeed = 600;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetime = 4;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " lifetimeVariance = 1.5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " velocity = 15;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " velocityVariance = 5;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " fade = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " useRadiusMass = true;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " baseRadius = 0.3;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " gravModifier = 1.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " terminalVelocity = 20;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " ignoreWater = false;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Debris\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">FX\n</a>" )
DefineEngineMethod(Debris , init , bool , (const char *inputPosition, const char *inputVelocity) , ("1.0 1.0 1.0", "1.0 0.0 0.0") , "@brief Manually set this piece of debris at the given position with the given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">velocity.\n\n</a>" "Usually you do not manually create <a href="/coding/class/classdebris/">Debris</a> objects as they are generated through other means, " "such as an Explosion. This method exists when you do manually create <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classdebris/">Debris</a> object and " "want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have it start <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">moving.\n</a>" " @param inputPosition Position <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> place the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">debris.\n</a>" " @param inputVelocity Velocity <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move the debris after it has been <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">placed.\n</a>" " @return Always returns <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">true.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Define the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n</a>" "% position)
IMPLEMENT_CO_DATABLOCK_V1(DebrisData )
IMPLEMENT_CO_NETOBJECT_V1(Debris )
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//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
26// Copyright (C) 2015 Faust Logic, Inc.
27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
28
29#include "platform/platform.h"
30#include "T3D/debris.h"
31
32#include "core/stream/bitStream.h"
33#include "math/mathUtils.h"
34#include "console/consoleTypes.h"
35#include "console/consoleObject.h"
36#include "sim/netConnection.h"
37#include "scene/sceneRenderState.h"
38#include "scene/sceneManager.h"
39#include "ts/tsShapeInstance.h"
40#include "ts/tsPartInstance.h"
41#include "T3D/fx/particleEmitter.h"
42#include "T3D/fx/explosion.h"
43#include "T3D/gameBase/gameProcess.h"
44#include "core/resourceManager.h"
45#include "gfx/gfxTransformSaver.h"
46#include "console/engineAPI.h"
47#include "lighting/lightQuery.h"
48
49
50const U32 csmStaticCollisionMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
51
52IMPLEMENT_CO_DATABLOCK_V1(DebrisData);
53
54ConsoleDocClass( DebrisData,
55 "@brief Stores properties for an individual debris type.\n\n"
56
57 "DebrisData defines the base properties for a Debris object. Typically you'll want a Debris object to consist of "
58 "a shape and possibly up to two particle emitters. The DebrisData datablock provides the definition for these items, "
59 "along with physical properties and how a Debris object will react to other game objects, such as water and terrain.\n"
60
61 "@tsexample\n"
62 "datablock DebrisData(GrenadeDebris)\n"
63 "{\n"
64 " shapeFile = \"art/shapes/weapons/ramrifle/debris.dts\";\n"
65 " emitters[0] = GrenadeDebrisFireEmitter;\n"
66 " elasticity = 0.4;\n"
67 " friction = 0.25;\n"
68 " numBounces = 3;\n"
69 " bounceVariance = 1;\n"
70 " explodeOnMaxBounce = false;\n"
71 " staticOnMaxBounce = false;\n"
72 " snapOnMaxBounce = false;\n"
73 " minSpinSpeed = 200;\n"
74 " maxSpinSpeed = 600;\n"
75 " lifetime = 4;\n"
76 " lifetimeVariance = 1.5;\n"
77 " velocity = 15;\n"
78 " velocityVariance = 5;\n"
79 " fade = true;\n"
80 " useRadiusMass = true;\n"
81 " baseRadius = 0.3;\n"
82 " gravModifier = 1.0;\n"
83 " terminalVelocity = 20;\n"
84 " ignoreWater = false;\n"
85 "};\n"
86 "@endtsexample\n\n"
87 "@see Debris\n\n"
88 "@ingroup FX\n"
89);
90
91DebrisData::DebrisData()
92{
93 dMemset( emitterList, 0, sizeof( emitterList ) );
94 dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
95
96 explosion = NULL;
97 explosionId = 0;
98
99 velocity = 0.0f;
100 velocityVariance = 0.0;
101 elasticity = 0.3f;
102 friction = 0.2f;
103 numBounces = 0;
104 bounceVariance = 0;
105 staticOnMaxBounce = false;
106 explodeOnMaxBounce = false;
107 snapOnMaxBounce = false;
108 lifetime = 3.0f;
109 lifetimeVariance = 0.0f;
110 minSpinSpeed = 0.0f;
111 maxSpinSpeed = 0.0f;
112 textureName = NULL;
113 shapeName = NULL;
114 fade = true;
115 useRadiusMass = false;
116 baseRadius = 1.0f;
117 gravModifier = 1.0f;
118 terminalVelocity = 0.0f;
119 ignoreWater = true;
120}
121
122//#define TRACK_DEBRIS_DATA_CLONES
123
124#ifdef TRACK_DEBRIS_DATA_CLONES
125static int debris_data_clones = 0;
126#endif
127
128DebrisData::DebrisData(const DebrisData& other, bool temp_clone) : GameBaseData(other, temp_clone)
129{
130#ifdef TRACK_DEBRIS_DATA_CLONES
131 debris_data_clones++;
132 if (debris_data_clones == 1)
133 Con::errorf("DebrisData -- Clones are on the loose!");
134#endif
135 velocity = other.velocity;
136 velocityVariance = other.velocityVariance;
137 friction = other.friction;
138 elasticity = other.elasticity;
139 lifetime = other.lifetime;
140 lifetimeVariance = other.lifetimeVariance;
141 numBounces = other.numBounces;
142 bounceVariance = other.bounceVariance;
143 minSpinSpeed = other.minSpinSpeed;
144 maxSpinSpeed = other.maxSpinSpeed;
145 explodeOnMaxBounce = other.explodeOnMaxBounce;
146 staticOnMaxBounce = other.staticOnMaxBounce;
147 snapOnMaxBounce = other.snapOnMaxBounce;
148 fade = other.fade;
149 useRadiusMass = other.useRadiusMass;
150 baseRadius = other.baseRadius;
151 gravModifier = other.gravModifier;
152 terminalVelocity = other.terminalVelocity;
153 ignoreWater = other.ignoreWater;
154 shapeName = other.shapeName;
155 shape = other.shape; // -- TSShape loaded using shapeName
156 textureName = other.textureName;
157 explosionId = other.explosionId; // -- for pack/unpack of explosion ptr
158 explosion = other.explosion;
159 dMemcpy( emitterList, other.emitterList, sizeof( emitterList ) );
160 dMemcpy( emitterIDList, other.emitterIDList, sizeof( emitterIDList ) ); // -- for pack/unpack of emitterList ptrs
161}
162
163DebrisData::~DebrisData()
164{
165 if (!isTempClone())
166 return;
167
168#ifdef TRACK_DEBRIS_DATA_CLONES
169 if (debris_data_clones > 0)
170 {
171 debris_data_clones--;
172 if (debris_data_clones == 0)
173 Con::errorf("DebrisData -- Clones eliminated!");
174 }
175 else
176 Con::errorf("DebrisData -- Too many clones deleted!");
177#endif
178}
179
180DebrisData* DebrisData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
181{
182 if (!owner || getSubstitutionCount() == 0)
183 return this;
184
185 DebrisData* sub_debris_db = new DebrisData(*this, true);
186 performSubstitutions(sub_debris_db, owner, index);
187
188 return sub_debris_db;
189}
190
191void DebrisData::onPerformSubstitutions()
192{
193 if( shapeName && shapeName[0] != '\0')
194 {
195 shape = ResourceManager::get().load(shapeName);
196 if( bool(shape) == false )
197 Con::errorf("DebrisData::onPerformSubstitutions(): failed to load shape \"%s\"", shapeName);
198 }
199}
200
201bool DebrisData::onAdd()
202{
203 if(!Parent::onAdd())
204 return false;
205
206 for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
207 {
208 if( !emitterList[i] && emitterIDList[i] != 0 )
209 {
210 if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
211 {
212 Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(emitter): 0x%x", emitterIDList[i]);
213 }
214 }
215 }
216
217 if (!explosion && explosionId != 0)
218 {
219 if (!Sim::findObject( SimObjectId( explosionId ), explosion ))
220 Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", explosionId);
221 }
222
223 // validate data
224
225 if( velocityVariance > velocity )
226 {
227 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName());
228 velocityVariance = velocity;
229 }
230 if( friction < -10.0f || friction > 10.0f )
231 {
232 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName());
233 friction = 0.2f;
234 }
235 if( elasticity < -10.0f || elasticity > 10.0f )
236 {
237 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName());
238 elasticity = 0.2f;
239 }
240 if( lifetime < 0.0f || lifetime > 1000.0f )
241 {
242 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName());
243 lifetime = 3.0f;
244 }
245 if( lifetimeVariance < 0.0f || lifetimeVariance > lifetime )
246 {
247 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName());
248 lifetimeVariance = 0.0f;
249 }
250 if( numBounces < 0 || numBounces > 10000 )
251 {
252 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: numBounces invalid", getName());
253 numBounces = 3;
254 }
255 if( bounceVariance < 0 || bounceVariance > numBounces )
256 {
257 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: bounceVariance invalid", getName());
258 bounceVariance = 0;
259 }
260 if( minSpinSpeed < -10000.0f || minSpinSpeed > 10000.0f || minSpinSpeed > maxSpinSpeed )
261 {
262 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName());
263 minSpinSpeed = maxSpinSpeed - 1.0f;
264 }
265 if( maxSpinSpeed < -10000.0f || maxSpinSpeed > 10000.0f )
266 {
267 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName());
268 maxSpinSpeed = 0.0f;
269 }
270
271 return true;
272}
273
274bool DebrisData::preload(bool server, String &errorStr)
275{
276 if (Parent::preload(server, errorStr) == false)
277 return false;
278
279 if( server ) return true;
280
281 if( shapeName && shapeName[0] != '\0' && !bool(shape) )
282 {
283 shape = ResourceManager::get().load(shapeName);
284 if( bool(shape) == false )
285 {
286 errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", shapeName);
287 return false;
288 }
289 else
290 {
291 TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
292 delete pDummy;
293 }
294
295 }
296
297 return true;
298}
299
300void DebrisData::initPersistFields()
301{
302 addGroup("Display");
303 addField("texture", TypeString, Offset(textureName, DebrisData),
304 "@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n");
305 addField("shapeFile", TypeShapeFilename, Offset(shapeName, DebrisData),
306 "@brief Object model to use for this debris object.\n\nThis shape is optional. You could have Debris made up of only particles.\n");
307 endGroup("Display");
308
309 addGroup("Datablocks");
310 addField("emitters", TYPEID< ParticleEmitterData >(), Offset(emitterList, DebrisData), DDC_NUM_EMITTERS,
311 "@brief List of particle emitters to spawn along with this debris object.\n\nThese are optional. You could have Debris made up of only a shape.\n");
312 addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, DebrisData),
313 "@brief ExplosionData to spawn along with this debris object.\n\nThis is optional as not all Debris explode.\n");
314 endGroup("Datablocks");
315
316 addGroup("Physical Properties");
317 addField("elasticity", TypeF32, Offset(elasticity, DebrisData),
318 "@brief A floating-point value specifying how 'bouncy' this object is.\n\nMust be in the range of -10 to 10.\n");
319 addField("friction", TypeF32, Offset(friction, DebrisData),
320 "@brief A floating-point value specifying how much velocity is lost to impact and sliding friction.\n\nMust be in the range of -10 to 10.\n");
321 addField("numBounces", TypeS32, Offset(numBounces, DebrisData),
322 "@brief How many times to allow this debris object to bounce until it either explodes, becomes static or snaps (defined in explodeOnMaxBounce, staticOnMaxBounce, snapOnMaxBounce).\n\n"
323 "Must be within the range of 0 to 10000.\n"
324 "@see bounceVariance\n");
325 addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData),
326 "@brief Allowed variance in the value of numBounces.\n\nMust be less than numBounces.\n@see numBounces\n");
327 addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData),
328 "@brief Minimum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 1000, and must be less than maxSpinSpeed.\n@see maxSpinSpeed\n");
329 addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData),
330 "@brief Maximum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 10000.\n@see minSpinSpeed\n");
331 addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData), "How much gravity affects debris.");
332 addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData), "Max velocity magnitude.");
333 addField("velocity", TypeF32, Offset(velocity, DebrisData),
334 "@brief Speed at which this debris object will move.\n\n@see velocityVariance\n");
335 addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData),
336 "@brief Allowed variance in the value of velocity\n\nMust be less than velocity.\n@see velocity\n");
337 addField("lifetime", TypeF32, Offset(lifetime, DebrisData),
338 "@brief Amount of time until this debris object is destroyed.\n\nMust be in the range of 0 to 1000.\n@see lifetimeVariance");
339 addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData),
340 "@brief Allowed variance in the value of lifetime.\n\nMust be less than lifetime.\n@see lifetime\n");
341 addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData),
342 "@brief Use mass calculations based on radius.\n\nAllows for the adjustment of elasticity and friction based on the Debris size.\n@see baseRadius\n");
343 addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData),
344 "@brief Radius at which the standard elasticity and friction apply.\n\nOnly used when useRaduisMass is true.\n@see useRadiusMass.\n");
345 endGroup("Physical Properties");
346
347 addGroup("Behavior");
348 addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData),
349 "@brief If true, this debris object will explode after it has bounced max times.\n\nBe sure to provide an ExplosionData datablock for this to take effect.\n@see explosion\n");
350 addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData), "If true, this debris object becomes static after it has bounced max times.");
351 addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData), "If true, this debris object will snap into a resting position on the last bounce.");
352 addField("fade", TypeBool, Offset(fade, DebrisData),
353 "@brief If true, this debris object will fade out when destroyed.\n\nThis fade occurs over the last second of the Debris' lifetime.\n");
354 addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there.");
355 endGroup("Behavior");
356
357 // disallow some field substitutions
358 onlyKeepClearSubstitutions("emitters"); // subs resolving to "~~", or "~0" are OK
359 onlyKeepClearSubstitutions("explosion");
360 Parent::initPersistFields();
361}
362
363void DebrisData::packData(BitStream* stream)
364{
365 Parent::packData(stream);
366
367 stream->write(elasticity);
368 stream->write(friction);
369 stream->write(numBounces);
370 stream->write(bounceVariance);
371 stream->write(minSpinSpeed);
372 stream->write(maxSpinSpeed);
373 stream->write(explodeOnMaxBounce);
374 stream->write(staticOnMaxBounce);
375 stream->write(snapOnMaxBounce);
376 stream->write(lifetime);
377 stream->write(lifetimeVariance);
378 stream->write(velocity);
379 stream->write(velocityVariance);
380 stream->write(fade);
381 stream->write(useRadiusMass);
382 stream->write(baseRadius);
383 stream->write(gravModifier);
384 stream->write(terminalVelocity);
385 stream->write(ignoreWater);
386
387 stream->writeString( textureName );
388 stream->writeString( shapeName );
389
390 for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
391 {
392 if( stream->writeFlag( emitterList[i] != NULL ) )
393 {
394 stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
395 }
396 }
397
398 if( stream->writeFlag( explosion ) )
399 {
400 stream->writeRangedU32(mPacked ? SimObjectId((uintptr_t)explosion):
401 explosion->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
402 }
403
404}
405
406void DebrisData::unpackData(BitStream* stream)
407{
408 Parent::unpackData(stream);
409
410 stream->read(&elasticity);
411 stream->read(&friction);
412 stream->read(&numBounces);
413 stream->read(&bounceVariance);
414 stream->read(&minSpinSpeed);
415 stream->read(&maxSpinSpeed);
416 stream->read(&explodeOnMaxBounce);
417 stream->read(&staticOnMaxBounce);
418 stream->read(&snapOnMaxBounce);
419 stream->read(&lifetime);
420 stream->read(&lifetimeVariance);
421 stream->read(&velocity);
422 stream->read(&velocityVariance);
423 stream->read(&fade);
424 stream->read(&useRadiusMass);
425 stream->read(&baseRadius);
426 stream->read(&gravModifier);
427 stream->read(&terminalVelocity);
428 stream->read(&ignoreWater);
429
430 textureName = stream->readSTString();
431 shapeName = stream->readSTString();
432
433 for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
434 {
435 if( stream->readFlag() )
436 {
437 emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
438 }
439 }
440
441 if(stream->readFlag())
442 {
443 explosionId = (S32)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
444 }
445 else
446 {
447 explosionId = 0;
448 }
449
450}
451
452
453IMPLEMENT_CO_NETOBJECT_V1(Debris);
454
455ConsoleDocClass( Debris,
456 "@brief Base debris class. Uses the DebrisData datablock for properties of individual debris objects.\n\n"
457
458 "Debris is typically made up of a shape and up to two particle emitters. In most cases Debris objects are "
459 "not created directly. They are usually produced automatically by other means, such as through the Explosion "
460 "class. When an explosion goes off, its ExplosionData datablock determines what Debris to emit.\n"
461
462 "@tsexample\n"
463 "datablock ExplosionData(GrenadeLauncherExplosion)\n"
464 "{\n"
465 " // Assiging debris data\n"
466 " debris = GrenadeDebris;\n\n"
467 " // Adjust how debris is ejected\n"
468 " debrisThetaMin = 10;\n"
469 " debrisThetaMax = 60;\n"
470 " debrisNum = 4;\n"
471 " debrisNumVariance = 2;\n"
472 " debrisVelocity = 25;\n"
473 " debrisVelocityVariance = 5;\n\n"
474 " // Note: other ExplosionData properties are not listed for this example\n"
475 "};\n"
476 "@endtsexample\n\n"
477
478 "@note Debris are client side only objects.\n"
479
480 "@see DebrisData\n"
481 "@see ExplosionData\n"
482 "@see Explosion\n"
483
484 "@ingroup FX\n"
485);
486
487DefineEngineMethod(Debris, init, bool, (const char* inputPosition, const char* inputVelocity),
488 ("1.0 1.0 1.0", "1.0 0.0 0.0"),
489 "@brief Manually set this piece of debris at the given position with the given velocity.\n\n"
490
491 "Usually you do not manually create Debris objects as they are generated through other means, "
492 "such as an Explosion. This method exists when you do manually create a Debris object and "
493 "want to have it start moving.\n"
494
495 "@param inputPosition Position to place the debris.\n"
496 "@param inputVelocity Velocity to move the debris after it has been placed.\n"
497 "@return Always returns true.\n"
498
499 "@tsexample\n"
500 "// Define the position\n"
501 "%position = \"1.0 1.0 1.0\";\n\n"
502 "// Define the velocity\n"
503 "%velocity = \"1.0 0.0 0.0\";\n\n"
504 "// Inform the debris object of its new position and velocity\n"
505 "%debris.init(%position,%velocity);\n"
506 "@endtsexample\n")
507{
508 Point3F pos;
509 dSscanf( inputPosition, "%f %f %f", &pos.x, &pos.y, &pos.z );
510
511 Point3F vel;
512 dSscanf( inputVelocity, "%f %f %f", &vel.x, &vel.y, &vel.z );
513
514 object->init( pos, vel );
515
516 return true;
517}
518
519Debris::Debris()
520{
521 mTypeMask |= DebrisObjectType | DynamicShapeObjectType;
522
523 mVelocity = Point3F( 0.0f, 0.0f, 4.0f );
524 mLifetime = gRandGen.randF( 1.0f, 10.0f );
525 mLastPos = getPosition();
526 mNumBounces = gRandGen.randI( 0, 1 );
527 mSize = 2.0f;
528 mElapsedTime = 0.0f;
529 mShape = NULL;
530 mPart = NULL;
531 mDataBlock = NULL;
532 mXRotSpeed = 0.0f;
533 mZRotSpeed = 0.0f;
534 mInitialTrans.identity();
535 mRadius = 0.2f;
536 mStatic = false;
537 mElasticity = 0.5f;
538 mFriction = 0.5f;
539
540 dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
541
542 // Only allocated client side.
543 mNetFlags.set( IsGhost );
544 ss_object = 0;
545 ss_index = 0;
546}
547
548Debris::~Debris()
549{
550 if( mShape )
551 {
552 delete mShape;
553 mShape = NULL;
554 }
555
556 if( mPart )
557 {
558 delete mPart;
559 mPart = NULL;
560 }
561
562 if (mDataBlock && mDataBlock->isTempClone())
563 {
564 delete mDataBlock;
565 mDataBlock = 0;
566 }
567}
568
569void Debris::initPersistFields()
570{
571 addGroup( "Debris" );
572
573 addField( "lifetime", TypeF32, Offset(mLifetime, Debris),
574 "@brief Length of time for this debris object to exist. When expired, the object will be deleted.\n\n"
575 "The initial lifetime value comes from the DebrisData datablock.\n"
576 "@see DebrisData::lifetime\n"
577 "@see DebrisData::lifetimeVariance\n");
578
579 endGroup( "Debris" );
580
581 Parent::initPersistFields();
582}
583
584void Debris::init( const Point3F &position, const Point3F &velocity )
585{
586 setPosition( position );
587 setVelocity( velocity );
588}
589
590bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
591{
592 mDataBlock = dynamic_cast< DebrisData* >( dptr );
593 if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
594 return false;
595
596 if (mDataBlock->isTempClone())
597 return true;
598 scriptOnNewDataBlock();
599 return true;
600
601}
602
603bool Debris::onAdd()
604{
605 if( !Parent::onAdd() )
606 {
607 return false;
608 }
609
610 if( !mDataBlock )
611 {
612 Con::errorf("Debris::onAdd - Fail - No datablock");
613 return false;
614 }
615
616 // create emitters
617 for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
618 {
619 if( mDataBlock->emitterList[i] != NULL )
620 {
621 ParticleEmitter * pEmitter = new ParticleEmitter;
622 pEmitter->onNewDataBlock(mDataBlock->emitterList[i]->cloneAndPerformSubstitutions(ss_object, ss_index), false);
623 if( !pEmitter->registerObject() )
624 {
625 Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
626 delete pEmitter;
627 pEmitter = NULL;
628 }
629 mEmitterList[i] = pEmitter;
630 }
631 }
632
633 // set particle sizes based on debris size
634 F32 sizeList[ParticleData::PDC_NUM_KEYS];
635
636 if( mEmitterList[0] )
637 {
638 sizeList[0] = mSize * 0.5;
639 sizeList[1] = mSize;
640 for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++)
641 sizeList[i] = mSize * 1.5;
642
643 mEmitterList[0]->setSizes( sizeList );
644 }
645
646 if( mEmitterList[1] )
647 {
648 sizeList[0] = 0.0;
649 sizeList[1] = mSize * 0.5;
650 for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++)
651 sizeList[i] = mSize;
652
653 mEmitterList[1]->setSizes( sizeList );
654 }
655
656 S32 bounceVar = (S32)mDataBlock->bounceVariance;
657 bounceVar = gRandGen.randI( -bounceVar, bounceVar );
658 mNumBounces = mDataBlock->numBounces + bounceVar;
659
660 F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance;
661 mLifetime = mDataBlock->lifetime + lifeVar;
662
663 F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
664 F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
665 zRotSpeed *= gRandGen.randF( 0.1f, 0.5f );
666
667 mRotAngles.set( xRotSpeed, 0.0f, zRotSpeed );
668
669 mElasticity = mDataBlock->elasticity;
670 mFriction = mDataBlock->friction;
671
672 // Setup our bounding box
673 if( mDataBlock->shape )
674 {
675 mObjBox = mDataBlock->shape->mBounds;
676 }
677 else
678 {
679 mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
680 }
681
682 if( mDataBlock->shape )
683 {
684 mShape = new TSShapeInstance( mDataBlock->shape, true);
685 }
686
687 if( mPart )
688 {
689 // use half radius becuase we want debris to stick in ground
690 mRadius = mPart->getRadius() * 0.5;
691 mObjBox = mPart->getBounds();
692 }
693
694 resetWorldBox();
695
696 mInitialTrans = getTransform();
697
698 if( mDataBlock->velocity != 0.0 )
699 {
700 F32 velocity = mDataBlock->velocity + gRandGen.randF( -mDataBlock->velocityVariance, mDataBlock->velocityVariance );
701
702 mVelocity.normalizeSafe();
703 mVelocity *= velocity;
704 }
705
706 // mass calculations
707 if( mDataBlock->useRadiusMass )
708 {
709 if( mRadius < mDataBlock->baseRadius )
710 {
711 mRadius = mDataBlock->baseRadius;
712 }
713
714 // linear falloff
715 F32 multFactor = mDataBlock->baseRadius / mRadius;
716
717 mElasticity *= multFactor;
718 mFriction *= multFactor;
719 mRotAngles *= multFactor;
720 }
721
722
723 // tell engine the debris exists
724 gClientSceneGraph->addObjectToScene(this);
725
726 removeFromProcessList();
727 ClientProcessList::get()->addObject(this);
728
729 NetConnection* pNC = NetConnection::getConnectionToServer();
730 AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
731 pNC->addObject(this);
732
733 return true;
734}
735
736void Debris::onRemove()
737{
738 for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
739 {
740 if( mEmitterList[i] )
741 {
742 mEmitterList[i]->deleteWhenEmpty();
743 mEmitterList[i] = NULL;
744 }
745 }
746
747 if( mPart )
748 {
749 TSShapeInstance *ss = mPart->getSourceShapeInstance();
750 if( ss )
751 {
752 ss->decDebrisRefCount();
753 if( ss->getDebrisRefCount() == 0 )
754 {
755 delete ss;
756 }
757 }
758 }
759
760 removeFromScene();
761
762 Parent::onRemove();
763}
764
765void Debris::processTick(const Move*)
766{
767 if (mLifetime <= 0.0)
768 deleteObject();
769}
770
771void Debris::advanceTime( F32 dt )
772{
773 mElapsedTime += dt;
774
775 mLifetime -= dt;
776 if( mLifetime <= 0.0 )
777 {
778 mLifetime = 0.0;
779 return;
780 }
781
782 mLastPos = getPosition();
783
784 if( !mStatic )
785 {
786 rotate( dt );
787
788 Point3F nextPos = getPosition();
789 computeNewState( nextPos, mVelocity, dt );
790
791 if( bounce( nextPos, dt ) )
792 {
793 --mNumBounces;
794 if( mNumBounces <= 0 )
795 {
796 if( mDataBlock->explodeOnMaxBounce )
797 {
798 explode();
799 mLifetime = 0.0;
800 }
801 if( mDataBlock->snapOnMaxBounce )
802 {
803 // orient debris so it's flat
804 MatrixF stat = getTransform();
805
806 Point3F dir;
807 stat.getColumn( 1, &dir );
808 dir.z = 0.0;
809
810 MatrixF newTrans = MathUtils::createOrientFromDir( dir );
811
812 // hack for shell casings to get them above ground. Need something better - bramage
813 newTrans.setPosition( getPosition() + Point3F( 0.0f, 0.0f, 0.10f ) );
814
815 setTransform( newTrans );
816 }
817 if( mDataBlock->staticOnMaxBounce )
818 {
819 mStatic = true;
820 }
821 }
822 }
823 else
824 {
825 setPosition( nextPos );
826 }
827 }
828
829 Point3F pos( getPosition( ) );
830 updateEmitters( pos, mVelocity, (U32)(dt * 1000.0));
831
832}
833
834void Debris::rotate( F32 dt )
835{
836 MatrixF curTrans = getTransform();
837 curTrans.setPosition( Point3F(0.0f, 0.0f, 0.0f) );
838
839 Point3F curAngles = mRotAngles * dt * M_PI_F/180.0f;
840 MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) );
841
842 curTrans.mul( rotMatrix );
843 curTrans.setPosition( getPosition() );
844 setTransform( curTrans );
845}
846
847bool Debris::bounce( const Point3F &nextPos, F32 dt )
848{
849 Point3F curPos = getPosition();
850
851 Point3F dir = nextPos - curPos;
852 if( dir.magnitudeSafe() == 0.0f ) return false;
853 dir.normalizeSafe();
854 Point3F extent = nextPos + dir * mRadius;
855 F32 totalDist = Point3F( extent - curPos ).magnitudeSafe();
856 F32 moveDist = Point3F( nextPos - curPos ).magnitudeSafe();
857 F32 movePercent = (moveDist / totalDist);
858
859 RayInfo rayInfo;
860 U32 collisionMask = csmStaticCollisionMask;
861 if( !mDataBlock->ignoreWater )
862 {
863 collisionMask |= WaterObjectType;
864 }
865
866 if( getContainer()->castRay( curPos, extent, collisionMask, &rayInfo ) )
867 {
868
869 Point3F reflection = mVelocity - rayInfo.normal * (mDot( mVelocity, rayInfo.normal ) * 2.0f);
870 mVelocity = reflection;
871
872 Point3F tangent = reflection - rayInfo.normal * mDot( reflection, rayInfo.normal );
873 mVelocity -= tangent * mFriction;
874
875 Point3F velDir = mVelocity;
876 velDir.normalizeSafe();
877
878 mVelocity *= mElasticity;
879
880 Point3F bouncePos = curPos + dir * rayInfo.t * movePercent;
881 bouncePos += mVelocity * dt;
882
883 setPosition( bouncePos );
884
885 mRotAngles *= mElasticity;
886
887 return true;
888
889 }
890
891 return false;
892
893}
894
895void Debris::explode()
896{
897
898 if( !mDataBlock->explosion ) return;
899
900 Point3F explosionPos = getPosition();
901
902 Explosion* pExplosion = new Explosion;
903 pExplosion->setSubstitutionData(ss_object, ss_index);
904 pExplosion->onNewDataBlock(mDataBlock->explosion->cloneAndPerformSubstitutions(ss_object, ss_index), false);
905
906 MatrixF trans( true );
907 trans.setPosition( getPosition() );
908
909 pExplosion->setTransform( trans );
910 pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1);
911 if (!pExplosion->registerObject())
912 delete pExplosion;
913}
914
915void Debris::computeNewState( Point3F &newPos, Point3F &newVel, F32 dt )
916{
917 // apply gravity
918 Point3F force = Point3F(0, 0, -9.81 * mDataBlock->gravModifier );
919
920 if( mDataBlock->terminalVelocity > 0.0001 )
921 {
922 if( newVel.magnitudeSafe() > mDataBlock->terminalVelocity )
923 {
924 newVel.normalizeSafe();
925 newVel *= mDataBlock->terminalVelocity;
926 }
927 else
928 {
929 newVel += force * dt;
930 }
931 }
932 else
933 {
934 newVel += force * dt;
935 }
936
937 newPos += newVel * dt;
938
939}
940
941void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms )
942{
943
944 Point3F axis = -vel;
945
946 if( axis.magnitudeSafe() == 0.0 )
947 {
948 axis = Point3F( 0.0, 0.0, 1.0 );
949 }
950 axis.normalizeSafe();
951
952
953 Point3F lastPos = mLastPos;
954
955 for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
956 {
957 if( mEmitterList[i] )
958 {
959 mEmitterList[i]->emitParticles( lastPos, pos, axis, vel, ms );
960 }
961 }
962
963}
964
965void Debris::prepRenderImage( SceneRenderState *state )
966{
967 if( !mPart && !mShape )
968 return;
969
970 Point3F cameraOffset;
971 mObjToWorld.getColumn(3,&cameraOffset);
972 cameraOffset -= state->getDiffuseCameraPosition();
973 F32 dist = cameraOffset.len();
974 F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
975
976 if( mShape )
977 {
978 mShape->setDetailFromDistance( state, dist * invScale );
979 if( mShape->getCurrentDetail() < 0 )
980 return;
981 }
982
983 if( mPart )
984 {
985 // get the shapeInstance that we are using for the debris parts
986 TSShapeInstance *si = mPart->getSourceShapeInstance();
987 if ( si )
988 si->setDetailFromDistance( state, dist * invScale );
989 }
990
991 prepBatchRender( state );
992}
993
994void Debris::prepBatchRender( SceneRenderState *state )
995{
996 if ( !mShape && !mPart )
997 return;
998
999 GFXTransformSaver saver;
1000
1001 F32 alpha = 1.0;
1002 if( mDataBlock->fade )
1003 {
1004 if( mLifetime < 1.0 ) alpha = mLifetime;
1005 }
1006
1007 Point3F cameraOffset;
1008 mObjToWorld.getColumn(3,&cameraOffset);
1009 cameraOffset -= state->getCameraPosition();
1010
1011 // Set up our TS render state.
1012 TSRenderState rdata;
1013 rdata.setSceneState( state );
1014
1015 // We might have some forward lit materials
1016 // so pass down a query to gather lights.
1017 LightQuery query;
1018 query.init( getWorldSphere() );
1019 rdata.setLightQuery( &query );
1020
1021 if( mShape )
1022 {
1023 MatrixF mat = getRenderTransform();
1024 GFX->setWorldMatrix( mat );
1025
1026 rdata.setFadeOverride( alpha );
1027 mShape->render( rdata );
1028 }
1029 else
1030 {
1031 if (mPart->getCurrentObjectDetail() != -1)
1032 {
1033 MatrixF mat = getRenderTransform();
1034 GFX->setWorldMatrix( mat );
1035
1036 rdata.setFadeOverride( alpha );
1037 mPart->render( rdata );
1038 }
1039 }
1040}
1041
1042void Debris::setSize( F32 size )
1043{
1044 mSize = size;
1045}
1046