Torque3D Documentation / _generateds / pxMultiActor.cpp

pxMultiActor.cpp

Engine/source/T3D/physics/physx/pxMultiActor.cpp

More...

Classes:

Public Functions

ConsoleDocClass(PxMultiActor , "@brief Represents a destructible physical object simulated using <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysX.\n\n</a>" "Usually it is prefered <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/class/classphysicsshape/">PhysicsShape</a> and not <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> because " "it is not PhysX specific and much easier <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">setup.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActorData.\n</a>" "@ingroup Physics" )
ConsoleDocClass(PxMultiActorData , "@brief Defines the properties of a type of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActor.\n\n</a>" "Usually it is prefered <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/class/classphysicsshape/">PhysicsShape</a> rather than <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> because " "a <a href="/coding/class/classphysicsshape/">PhysicsShape</a> is not PhysX specific and can be much easier <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">setup.\n\n</a>" "For more information, refer <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> Nvidia 's PhysX <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">docs.\n\n</a>" " @ingroup Physics" )
ConsoleMethod(PxMultiActor , listMeshes , void , 3 , 3 , "(enum Hidden/Shown/All)" "@brief Lists all meshes of the provided type in the console <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">window.\n\n</a>" "@param All Lists all of the %<a href="/coding/class/classpxmultiactor/">PxMultiActor</a>'s <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">meshes.\n</a>" "@param Hidden Lists all of the %<a href="/coding/class/classpxmultiactor/">PxMultiActor</a>'s hidden <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">meshes.\n</a>" "@param Shown Lists all of the %<a href="/coding/class/classpxmultiactor/">PxMultiActor</a>'s visible <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">meshes.\n</a>" )
ConsoleMethod(PxMultiActor , setAllHidden , void , 3 , 3 , "( bool )" "@brief Hides or unhides all meshes contained in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActor.\n\n</a>" "Hidden meshes will not be rendered." )
ConsoleMethod(PxMultiActor , setBroken , void , 3 , 3 , "( bool )" "@brief Sets the <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> a broken or unbroken <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" )
ConsoleMethod(PxMultiActor , setMeshHidden , void , 4 , 4 , "(string meshName, bool isHidden)" "@brief Prevents the provided mesh from being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">rendered.\n\n</a>" )
ConsoleMethod(PxMultiActorData , dumpModel , void , 2 , 2 , "@brief Dumps model hierarchy and details <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> a <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n\n</a>" "The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> will be created as \'model.dump\' in the game folder. " "If model.dump already exists, it will be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">overwritten.\n\n</a>" )
ConsoleMethod(PxMultiActorData , reload , void , 2 , 2 , "" "@brief Reloads all data used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActorData.\n\n</a>" "If the reload sucessfully completes, all <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> 's will be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">notified.\n\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(PxMultiActor , "@brief Represents a destructible physical object simulated using <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PhysX.\n\n</a>" "Usually it is prefered <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/class/classphysicsshape/">PhysicsShape</a> and not <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> because " "it is not PhysX specific and much easier <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">setup.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActorData.\n</a>" "@ingroup Physics" )

ConsoleDocClass(PxMultiActorData , "@brief Defines the properties of a type of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActor.\n\n</a>" "Usually it is prefered <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/class/classphysicsshape/">PhysicsShape</a> rather than <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> because " "a <a href="/coding/class/classphysicsshape/">PhysicsShape</a> is not PhysX specific and can be much easier <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">setup.\n\n</a>" "For more information, refer <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> Nvidia 's PhysX <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">docs.\n\n</a>" " @ingroup Physics" )

ConsoleMethod(PxMultiActor , listMeshes , void , 3 , 3 , "(enum Hidden/Shown/All)" "@brief Lists all meshes of the provided type in the console <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">window.\n\n</a>" "@param All Lists all of the %<a href="/coding/class/classpxmultiactor/">PxMultiActor</a>'s <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">meshes.\n</a>" "@param Hidden Lists all of the %<a href="/coding/class/classpxmultiactor/">PxMultiActor</a>'s hidden <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">meshes.\n</a>" "@param Shown Lists all of the %<a href="/coding/class/classpxmultiactor/">PxMultiActor</a>'s visible <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">meshes.\n</a>" )

ConsoleMethod(PxMultiActor , setAllHidden , void , 3 , 3 , "( bool )" "@brief Hides or unhides all meshes contained in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActor.\n\n</a>" "Hidden meshes will not be rendered." )

ConsoleMethod(PxMultiActor , setBroken , void , 3 , 3 , "( bool )" "@brief Sets the <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> a broken or unbroken <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">state.\n\n</a>" )

ConsoleMethod(PxMultiActor , setMeshHidden , void , 4 , 4 , "(string meshName, bool isHidden)" "@brief Prevents the provided mesh from being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">rendered.\n\n</a>" )

ConsoleMethod(PxMultiActorData , dumpModel , void , 2 , 2 , "@brief Dumps model hierarchy and details <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> a <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n\n</a>" "The <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> will be created as \'model.dump\' in the game folder. " "If model.dump already exists, it will be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">overwritten.\n\n</a>" )

ConsoleMethod(PxMultiActorData , reload , void , 2 , 2 , "" "@brief Reloads all data used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">PxMultiActorData.\n\n</a>" "If the reload sucessfully completes, all <a href="/coding/class/classpxmultiactor/">PxMultiActor</a> 's will be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">notified.\n\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(PxMultiActorData )

IMPLEMENT_CO_NETOBJECT_V1(PxMultiActor )

   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/physics/physX/pxMultiActor.h"
  26
  27#include "console/consoleTypes.h"
  28#include "core/stream/fileStream.h"
  29#include "core/stream/bitStream.h"
  30#include "core/resourceManager.h"
  31#include "core/strings/stringUnit.h"
  32#include "sim/netConnection.h"
  33#include "math/mathIO.h"
  34#include "math/mathUtils.h"
  35#include "gfx/gfxTransformSaver.h"
  36#include "gfx/gfxDrawUtil.h"
  37#include "gfx/primBuilder.h"
  38#include "collision/collision.h"
  39#include "collision/abstractPolyList.h"
  40#include "ts/tsShapeInstance.h"
  41#include "ts/tsPartInstance.h"
  42#include "lighting/lightManager.h"
  43#include "scene/sceneManager.h"
  44#include "scene/sceneRenderState.h"
  45#include "scene/sceneObjectLightingPlugin.h"
  46#include "T3D/objectTypes.h"
  47#include "T3D/containerQuery.h"
  48#include "T3D/fx/particleEmitter.h"
  49#include "T3D/debris.h"
  50#include "renderInstance/renderPassManager.h"
  51#include "gui/worldEditor/editor.h" // For gEditingMission
  52#include "T3D/physics/physX/px.h"
  53#include "T3D/physics/physX/pxWorld.h"
  54#include "T3D/physics/physX/pxMaterial.h"
  55#include "T3D/physics/physX/pxCasts.h"
  56#include "T3D/physics/physx/pxUtils.h"
  57#include "sfx/sfxSystem.h"
  58
  59#include <NXU_helper.h>
  60#include <nxu_schema.h>
  61#include <NXU_customcopy.h>
  62
  63
  64class PxMultiActor_Notify : public NXU_userNotify
  65{
  66protected:
  67
  68   Vector<NxActor*> mActors;
  69
  70   Vector<NxShape*> mShapes;
  71
  72   Vector<NxJoint*> mJoints;
  73
  74   const NxMat34 mTransform;
  75
  76   const Point3F mScale;
  77
  78   F32 mMassScale;
  79
  80   NxCompartment *mCompartment;
  81
  82   PxMaterial *mMaterial;
  83
  84   Vector<String> *mActorUserProperties;
  85
  86   Vector<String> *mJointUserProperties;
  87
  88public:
  89
  90   void NXU_notifyJoint( NxJoint *joint, const char *userProperties )
  91   {
  92      if ( mJointUserProperties )
  93         mJointUserProperties->push_back( userProperties );
  94      mJoints.push_back( joint );
  95   }
  96
  97   bool NXU_preNotifyJoint( NxJointDesc &joint, const char *userProperties )
  98   {
  99      joint.localAnchor[0].x *= mScale.x;
 100      joint.localAnchor[0].y *= mScale.y;
 101      joint.localAnchor[0].z *= mScale.z;
 102
 103      joint.localAnchor[1].x *= mScale.x;
 104      joint.localAnchor[1].y *= mScale.y;
 105      joint.localAnchor[1].z *= mScale.z;
 106
 107      // The PhysX exporter from 3dsMax doesn't allow creation
 108      // of fixed joints.  It also doesn't seem to export the
 109      // joint names!  So look for joints which all all the
 110      // motion axes are locked... make those fixed joints.
 111      if ( joint.getType() == NX_JOINT_D6 )
 112      {
 113         NxD6JointDesc *d6Joint = static_cast<NxD6JointDesc*>( &joint );
 114
 115         if (  d6Joint->xMotion == NX_D6JOINT_MOTION_LOCKED &&
 116               d6Joint->yMotion == NX_D6JOINT_MOTION_LOCKED &&
 117               d6Joint->zMotion == NX_D6JOINT_MOTION_LOCKED &&
 118               d6Joint->swing1Motion == NX_D6JOINT_MOTION_LOCKED &&
 119               d6Joint->swing2Motion == NX_D6JOINT_MOTION_LOCKED &&
 120               d6Joint->twistMotion == NX_D6JOINT_MOTION_LOCKED )
 121         {
 122            // Ok... build a new fixed joint.
 123            NxFixedJointDesc fixed;
 124            fixed.actor[0] = joint.actor[0];
 125            fixed.actor[1] = joint.actor[1];
 126            fixed.localNormal[0] = joint.localNormal[0];
 127            fixed.localNormal[1] = joint.localNormal[1];
 128            fixed.localAxis[0] = joint.localAxis[0];
 129            fixed.localAxis[1] = joint.localAxis[1];
 130            fixed.localAnchor[0] = joint.localAnchor[0];
 131            fixed.localAnchor[1] = joint.localAnchor[1];
 132            fixed.maxForce = joint.maxForce;
 133            fixed.maxTorque = joint.maxTorque;
 134            fixed.name = joint.name;
 135            fixed.userData = joint.userData;
 136            fixed.jointFlags = joint.jointFlags;
 137
 138            // What scene are we adding this to?
 139            NxActor *actor = fixed.actor[0] ? fixed.actor[0] : fixed.actor[1];
 140            NxScene &scene = actor->getScene();
 141
 142            NxJoint* theJoint = scene.createJoint( fixed );
 143            mJoints.push_back( theJoint );
 144            if ( mJointUserProperties )
 145               mJointUserProperties->push_back( userProperties );
 146
 147            // Don't generate this joint.
 148            return false;
 149         }
 150      }
 151
 152      return true;
 153   }
 154
 155   void NXU_notifyActor( NxActor *actor, const char *userProperties )
 156   {
 157      mActors.push_back( actor );
 158
 159      // Save the shapes.
 160      for ( U32 i=0; i < actor->getNbShapes(); i++ )
 161         mShapes.push_back( actor->getShapes()[i] );
 162
 163      mActorUserProperties->push_back( userProperties );
 164   };
 165
 166   bool NXU_preNotifyMaterial( NxMaterialDesc &t, const char *userProperties )
 167   {
 168      // Don't generate materials if we have one defined!
 169      return !mMaterial;
 170   }
 171
 172   bool NXU_preNotifyActor( NxActorDesc &actor, const char *userProperties )
 173   {
 174         // Set the right compartment.
 175         actor.compartment = mCompartment;
 176
 177         if ( actor.shapes.size() == 0 )
 178            Con::warnf( "PxMultiActor_Notify::NXU_preNotifyActor, got an actor (%s) with no shapes, was this intentional?", actor.name );
 179
 180         // For every shape, cast to its particular type
 181         // and apply the scale to size, mass and localPosition.
 182         for( S32 i = 0; i < actor.shapes.size(); i++ )
 183         {
 184            // If we have material then set it.
 185            if ( mMaterial )
 186               actor.shapes[i]->materialIndex = mMaterial->getMaterialId();
 187
 188            switch( actor.shapes[i]->getType() )
 189            {
 190               case NX_SHAPE_BOX:
 191               {
 192                  NxBoxShapeDesc *boxDesc = (NxBoxShapeDesc*)actor.shapes[i];
 193
 194                  boxDesc->mass *= mMassScale;
 195
 196                  boxDesc->dimensions.x *= mScale.x;
 197                  boxDesc->dimensions.y *= mScale.y;
 198                  boxDesc->dimensions.z *= mScale.z;
 199
 200                  boxDesc->localPose.t.x *= mScale.x;
 201                  boxDesc->localPose.t.y *= mScale.y;
 202                  boxDesc->localPose.t.z *= mScale.z;
 203                  break;
 204               }
 205
 206               case NX_SHAPE_SPHERE:
 207               {
 208                  NxSphereShapeDesc *sphereDesc = (NxSphereShapeDesc*)actor.shapes[i];
 209
 210                  sphereDesc->mass *= mMassScale;
 211
 212                  // TODO: Spheres do not work with non-uniform
 213                  // scales very well... how do we fix this?
 214                  sphereDesc->radius *= mScale.x;
 215
 216                  sphereDesc->localPose.t.x *= mScale.x;
 217                  sphereDesc->localPose.t.y *= mScale.y;
 218                  sphereDesc->localPose.t.z *= mScale.z;
 219                  break;
 220               }
 221
 222               case NX_SHAPE_CAPSULE:
 223               {
 224                  NxCapsuleShapeDesc *capsuleDesc = (NxCapsuleShapeDesc*)actor.shapes[i];
 225
 226                  capsuleDesc->mass *= mMassScale;
 227
 228                  // TODO: Capsules do not work with non-uniform
 229                  // scales very well... how do we fix this?
 230                  capsuleDesc->radius *= mScale.x;
 231                  capsuleDesc->height *= mScale.y;
 232
 233                  capsuleDesc->localPose.t.x *= mScale.x;
 234                  capsuleDesc->localPose.t.y *= mScale.y;
 235                  capsuleDesc->localPose.t.z *= mScale.z;
 236                  break;
 237               }
 238
 239               default:
 240               {
 241                  static String lookup[] =
 242                  {
 243                     "PLANE",
 244                     "SPHERE",
 245                     "BOX",
 246                     "CAPSULE",
 247                     "WHEEL",
 248                     "CONVEX",
 249                     "MESH",
 250                     "HEIGHTFIELD"
 251                  };
 252
 253                  Con::warnf( "PxMultiActor_Notify::NXU_preNotifyActor, unsupported shape type (%s), on Actor (%s)", lookup[actor.shapes[i]->getType()].c_str(), actor.name );
 254
 255                  delete actor.shapes[i];
 256                  actor.shapes.erase( actor.shapes.begin() + i );
 257                  --i;
 258                  break;
 259               }
 260            }
 261         }
 262
 263         NxBodyDesc *body = const_cast<NxBodyDesc*>( actor.body );
 264         if ( body )
 265         {
 266            // Must scale all of these parameters, else there will be odd results!
 267            body->mass *= mMassScale;
 268            body->massLocalPose.t.multiply( mMassScale, body->massLocalPose.t );
 269            body->massSpaceInertia.multiply( mMassScale, body->massSpaceInertia );
 270
 271            // Ragdoll damping!
 272            //body->sleepDamping = 1.7f;
 273            //body->linearDamping = 0.4f;
 274            //body->angularDamping = 0.08f;
 275            //body->wakeUpCounter = 0.3f;
 276         }
 277
 278      return   true;
 279   };
 280
 281public:
 282
 283   PxMultiActor_Notify(    NxCompartment *compartment,
 284                           PxMaterial *material,
 285                           const NxMat34& mat,
 286                           const Point3F& scale,
 287                           Vector<String> *actorProps = NULL,
 288                           Vector<String> *jointProps = NULL )
 289      :  mCompartment( compartment ),
 290         mMaterial( material ),
 291         mScale( scale ),
 292         mTransform( mat ),
 293         mActorUserProperties( actorProps ),
 294         mJointUserProperties( jointProps )
 295   {
 296      const F32 unit = VectorF( 1.0f, 1.0f, 1.0f ).len();
 297      mMassScale = mScale.len() / unit;
 298   }
 299
 300   virtual ~PxMultiActor_Notify()
 301   {
 302   }
 303
 304   const Vector<NxActor*>& getActors() { return mActors; }
 305   const Vector<NxShape*>& getShapes() { return mShapes; }
 306   const Vector<NxJoint*>& getJoints() { return mJoints; }
 307};
 308
 309ConsoleDocClass( PxMultiActorData,
 310   
 311   "@brief Defines the properties of a type of PxMultiActor.\n\n"
 312
 313   "Usually it is prefered to use PhysicsShape rather than PxMultiActor because "
 314   "a PhysicsShape is not PhysX specific and can be much easier to setup.\n\n"
 315
 316   "For more information, refer to Nvidia's PhysX docs.\n\n"
 317   
 318   "@ingroup Physics"
 319);
 320
 321IMPLEMENT_CO_DATABLOCK_V1(PxMultiActorData);
 322
 323PxMultiActorData::PxMultiActorData()
 324 : material( NULL ),
 325   collection( NULL ),
 326   waterDragScale( 1.0f ),
 327   buoyancyDensity( 1.0f ),
 328   angularDrag( 0.0f ),
 329   linearDrag( 0.0f ),
 330   clientOnly( false ),
 331   singlePlayerOnly( false ),
 332   shapeName( StringTable->insert( "" ) ),
 333   physXStream( StringTable->insert( "" ) ),
 334   breakForce( 0.0f )
 335{
 336   for ( S32 i = 0; i < MaxCorrectionNodes; i++ )
 337      correctionNodeNames[i] = StringTable->insert( "" );
 338
 339   for ( S32 i = 0; i < MaxCorrectionNodes; i++ )
 340      correctionNodes[i] = -1;
 341
 342   for ( S32 i = 0; i < NumMountPoints; i++ )
 343   {
 344      mountNodeNames[i] = StringTable->insert( "" );
 345      mountPointNode[i] = -1;
 346   }
 347}
 348
 349PxMultiActorData::~PxMultiActorData()
 350{
 351   if ( collection )
 352      NXU::releaseCollection( collection );
 353}
 354
 355void PxMultiActorData::initPersistFields()
 356{
 357   Parent::initPersistFields();
 358
 359   addGroup("Media");
 360      addField( "shapeName", TypeFilename, Offset( shapeName, PxMultiActorData ),
 361         "@brief Path to the .DAE or .DTS file to render.\n\n");
 362   endGroup("Media");
 363
 364   // PhysX collision properties.
 365   addGroup( "Physics" );
 366
 367      addField( "physXStream", TypeFilename, Offset( physXStream, PxMultiActorData ),
 368         "@brief .XML file containing data such as actors, shapes, and joints.\n\n"
 369         "These files can be created using a free PhysX plugin for 3DS Max.\n\n");
 370      addField( "material", TYPEID< PxMaterial >(), Offset( material, PxMultiActorData ),
 371         "@brief An optional PxMaterial to be used for the PxMultiActor.\n\n"
 372         "Defines properties such as friction and restitution. "
 373         "Unrelated to the material used for rendering. The physXStream will contain "
 374         "defined materials that can be customized in 3DS Max. "
 375         "To override the material for all physics shapes in the physXStream, specify a material here.\n\n");
 376
 377      addField( "noCorrection", TypeBool, Offset( noCorrection, PxMultiActorData ),
 378         "@hide" );
 379
 380      UTF8 buff[256];
 381      for ( S32 i=0; i < MaxCorrectionNodes; i++ )
 382      {
 383         //dSprintf( buff, sizeof(buff), "correctionNode%d", i );
 384         addField( buff, TypeString, Offset( correctionNodeNames[i], PxMultiActorData ), "@hide" );
 385      }
 386
 387      for ( S32 i=0; i < NumMountPoints; i++ )
 388      {
 389         //dSprintf( buff, sizeof(buff), "mountNode%d", i );
 390         addField( buff, TypeString, Offset( mountNodeNames[i], PxMultiActorData ), "@hide" );
 391      }
 392
 393      addField( "angularDrag", TypeF32, Offset( angularDrag, PxMultiActorData ),
 394         "@brief Value used to help calculate rotational drag force while submerged in water.\n\n");
 395      addField( "linearDrag", TypeF32, Offset( linearDrag, PxMultiActorData ),
 396         "@brief Value used to help calculate linear drag force while submerged in water.\n\n");
 397      addField( "waterDragScale", TypeF32, Offset( waterDragScale, PxMultiActorData ),
 398         "@brief Scale to apply to linear and angular dampening while submerged in water.\n\n ");
 399      addField( "buoyancyDensity", TypeF32, Offset( buoyancyDensity, PxMultiActorData ),
 400         "@brief The density used to calculate buoyant forces.\n\n"
 401         "The result of the calculated buoyancy is relative to the density of the WaterObject the PxMultiActor is within.\n\n"
 402         "@note This value is necessary because Torque 3D does its own buoyancy simulation. It is not handled by PhysX."
 403         "@see WaterObject::density");
 404
 405   endGroup( "Physics" );
 406
 407   addField( "clientOnly", TypeBool, Offset( clientOnly, PxMultiActorData ),
 408      "@hide");
 409   addField( "singlePlayerOnly", TypeBool, Offset( singlePlayerOnly, PxMultiActorData ),
 410      "@hide");
 411   addField( "breakForce", TypeF32, Offset( breakForce, PxMultiActorData ),
 412      "@brief Force required to break an actor.\n\n"
 413      "This value does not apply to joints. "
 414      "If an actor is associated with a joint it will break whenever the joint does. "
 415      "This allows an actor \"not\" associated with a joint to also be breakable.\n\n");
 416}
 417
 418void PxMultiActorData::packData(BitStream* stream)
 419{
 420   Parent::packData(stream);
 421
 422   stream->writeString( shapeName );
 423   stream->writeString( physXStream );
 424
 425   if( stream->writeFlag( material ) )
 426      stream->writeRangedU32( packed ? SimObjectId( material ) : material->getId(),
 427                              DataBlockObjectIdFirst,  DataBlockObjectIdLast );
 428
 429   if ( !stream->writeFlag( noCorrection ) )
 430   {
 431      // Write the correction node indices for the client.
 432      for ( S32 i = 0; i < MaxCorrectionNodes; i++ )
 433         stream->write( correctionNodes[i] );
 434   }
 435
 436   for ( S32 i = 0; i < NumMountPoints; i++ )
 437      stream->write( mountPointNode[i] );
 438
 439   stream->write( waterDragScale );
 440   stream->write( buoyancyDensity );
 441   stream->write( angularDrag );
 442   stream->write( linearDrag );
 443
 444   stream->writeFlag( clientOnly );
 445   stream->writeFlag( singlePlayerOnly );
 446   stream->write( breakForce );
 447}
 448
 449void PxMultiActorData::unpackData(BitStream* stream)
 450{
 451   Parent::unpackData(stream);
 452
 453   shapeName = stream->readSTString();
 454   physXStream = stream->readSTString();
 455
 456   if( stream->readFlag() )
 457      material = (PxMaterial*)stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
 458
 459   noCorrection = stream->readFlag();
 460   if ( !noCorrection )
 461   {
 462      for ( S32 i = 0; i < MaxCorrectionNodes; i++ )
 463         stream->read( &correctionNodes[i] );
 464   }
 465
 466   for ( S32 i = 0; i < NumMountPoints; i++ )
 467      stream->read( &mountPointNode[i] );
 468
 469   stream->read( &waterDragScale );
 470   stream->read( &buoyancyDensity );
 471   stream->read( &angularDrag );
 472   stream->read( &linearDrag );
 473
 474   clientOnly = stream->readFlag();
 475   singlePlayerOnly = stream->readFlag();
 476   stream->read( &breakForce );
 477}
 478
 479bool PxMultiActorData::preload( bool server, String &errorBuffer )
 480{
 481   if ( !Parent::preload( server, errorBuffer ) )
 482      return false;
 483
 484   // If the stream is null, exit.
 485   if ( !physXStream || !physXStream[0] )
 486   {
 487      errorBuffer = "PxMultiActorData::preload: physXStream is unset!";
 488      return false;
 489   }
 490
 491   // Set up our buffer for the binary stream filename path.
 492   UTF8 binPhysXStream[260] = { 0 };
 493   const UTF8* ext = dStrrchr( physXStream, '.' );
 494
 495   // Copy the xml stream path except for the extension.
 496   if ( ext )
 497      dStrncpy( binPhysXStream, physXStream, getMin( 260, ext - physXStream ) );
 498   else
 499      dStrncpy( binPhysXStream, physXStream, 260 );
 500
 501   // Concatenate the binary extension.
 502   dStrcat( binPhysXStream, ".nxb" );
 503
 504   // Get the modified times of the two files.
 505   FileTime xmlTime = {0}, binTime = {0};
 506   Platform::getFileTimes( physXStream, NULL, &xmlTime );
 507   Platform::getFileTimes( binPhysXStream, NULL, &binTime );
 508
 509   // If the binary is newer... load that.
 510   if ( Platform::compareFileTimes( binTime, xmlTime ) >= 0 )
 511      _loadCollection( binPhysXStream, true );
 512
 513   // If the binary failed... then load the xml.
 514   if ( !collection )
 515   {
 516      _loadCollection( physXStream, false );
 517
 518      // If loaded... resave the xml in binary format
 519      // for quicker subsequent loads.
 520      if ( collection )
 521         NXU::saveCollection( collection, binPhysXStream, NXU::FT_BINARY );
 522   }
 523
 524   // If it still isn't loaded then we've failed!
 525   if ( !collection )
 526   {
 527      errorBuffer = String::ToString( "PxMultiActorDatas::preload: could not load '%s'!", physXStream );
 528      return false;
 529   }
 530
 531   if (!shapeName || shapeName[0] == '\0')
 532   {
 533      errorBuffer = "PxMultiActorDatas::preload: no shape name!";
 534      return false;
 535   }
 536
 537   shape = ResourceManager::get().load( shapeName );
 538
 539   if (bool(shape) == false)
 540   {
 541      errorBuffer = String::ToString( "PxMultiActorData::preload: unable to load shape: %s", shapeName );
 542      return false;
 543   }
 544
 545   // Find the client side material.
 546   if ( !server && material )
 547      Sim::findObject( SimObjectId(material), material );
 548
 549   // Get the ignore node indexes from the names.
 550   for ( S32 i = 0; i < MaxCorrectionNodes; i++ )
 551   {
 552      if( !correctionNodeNames[i] || !correctionNodeNames[i][0] )
 553         continue;
 554
 555      correctionNodes[i] = shape->findNode( correctionNodeNames[i] );
 556   }
 557
 558   // Resolve mount point node indexes
 559   for ( S32 i = 0; i < NumMountPoints; i++) 
 560   {
 561      char fullName[256];
 562
 563      if ( !mountNodeNames[i] || !mountNodeNames[i][0] )
 564      {
 565         dSprintf(fullName,sizeof(fullName),"mount%d",i);
 566         mountPointNode[i] = shape->findNode(fullName);
 567      }  
 568      else      
 569         mountPointNode[i] = shape->findNode(mountNodeNames[i]);            
 570   }
 571
 572   // Register for file change notification to reload the collection
 573   if ( server )
 574      Torque::FS::AddChangeNotification( physXStream, this, &PxMultiActorData::_onFileChanged );
 575
 576   return true;
 577}
 578
 579void PxMultiActorData::_onFileChanged( const Torque::Path &path )
 580{
 581   reload();
 582}
 583
 584void PxMultiActorData::reload()
 585{
 586   bool result = _loadCollection( physXStream, false );
 587
 588   if ( !result )
 589      Con::errorf( "PxMultiActorData::reload(), _loadCollection failed..." );
 590
 591   // Inform MultiActors who use this datablock to reload.
 592   mReloadSignal.trigger();
 593}
 594
 595bool PxMultiActorData::_loadCollection( const UTF8 *path, bool isBinary )
 596{
 597   if ( collection )
 598   {
 599      NXU::releaseCollection( collection );
 600      collection = NULL;
 601   }
 602
 603   FileStream fs;
 604   if ( !fs.open( path, Torque::FS::File::Read ) )
 605      return false;
 606
 607   // Load the data into memory.
 608   U32 size = fs.getStreamSize();
 609   FrameTemp<U8> buff( size );
 610   fs.read( size, buff );
 611
 612   // If the stream didn't read anything, there's a problem.
 613   if ( size <= 0 )
 614      return false;
 615
 616   // Ok... try to load it.
 617   collection = NXU::loadCollection(   path,
 618                                       isBinary ? NXU::FT_BINARY : NXU::FT_XML,
 619                                       buff,
 620                                       size );
 621
 622   return collection != NULL;
 623}
 624
 625
 626bool PxMultiActorData::createActors(   NxScene *scene,
 627                                          NxCompartment *compartment,
 628                                          const NxMat34 *nxMat,
 629                                          const Point3F& scale,
 630                                          Vector<NxActor*> *outActors,
 631                                          Vector<NxShape*> *outShapes,
 632                                          Vector<NxJoint*> *outJoints,
 633                                          Vector<String> *outActorUserProperties,
 634                                          Vector<String> *outJointUserProperties )
 635{
 636   if ( !scene )
 637   {
 638      Con::errorf( "PxMultiActorData::createActor() - returned null NxScene" );
 639      return NULL;
 640   }
 641
 642   PxMultiActor_Notify pxNotify( compartment, material, *nxMat, scale, outActorUserProperties, outJointUserProperties );
 643
 644   NXU::instantiateCollection( collection, *gPhysicsSDK, scene, nxMat, &pxNotify );
 645
 646   *outActors = pxNotify.getActors();
 647   *outJoints = pxNotify.getJoints();
 648   if ( outShapes )
 649      *outShapes = pxNotify.getShapes();
 650
 651   if ( outActors->empty() )
 652   {
 653      Con::errorf( "PxMultiActorData::createActors() - NXUStream notifier returned empty actors or joints!" );
 654      return false;
 655   }
 656
 657   return true;
 658}
 659
 660ConsoleDocClass( PxMultiActor,
 661   
 662   "@brief Represents a destructible physical object simulated using PhysX.\n\n"
 663
 664   "Usually it is prefered to use PhysicsShape and not PxMultiActor because "
 665   "it is not PhysX specific and much easier to setup.\n"
 666   
 667   "@see PxMultiActorData.\n"   
 668   "@ingroup Physics"
 669);
 670
 671IMPLEMENT_CO_NETOBJECT_V1(PxMultiActor);
 672
 673PxMultiActor::PxMultiActor()
 674 : mShapeInstance( NULL ),
 675   mRootActor( NULL ),
 676   mWorld( NULL ),
 677   mStartImpulse( 0, 0, 0 ),
 678   mResetXfm( true ),
 679   mActorScale( 0, 0, 0 ),
 680   mDebugRender( false ),
 681   mIsDummy( false ),
 682   mBroken( false ),
 683   mDataBlock( NULL )
 684{
 685   mNetFlags.set( Ghostable | ScopeAlways );
 686
 687   mTypeMask |= StaticObjectType | StaticShapeObjectType;
 688
 689   //mUserData.setObject( this );
 690}
 691
 692void PxMultiActor::initPersistFields()
 693{
 694   Parent::initPersistFields();
 695
 696   /*
 697   // We're overloading these fields from SceneObject
 698   // in order to force it to go thru setTransform!
 699   removeField( "position" );
 700   removeField( "rotation" );
 701   removeField( "scale" );
 702
 703   addGroup( "Transform" );
 704
 705      addProtectedField( "position", TypeMatrixPosition, 0,
 706         &PxMultiActor::_setPositionField,
 707         &PxMultiActor::_getPositionField,
 708         "" );
 709
 710      addProtectedField( "rotation", TypeMatrixRotation, 0,
 711         &PxMultiActor::_setRotationField,
 712         &PxMultiActor::_getRotationField,
 713         "" );
 714
 715      addField( "scale", TypePoint3F, Offset( mObjScale, PxMultiActor ) );
 716
 717   endGroup( "Transform" );
 718   */
 719
 720   //addGroup("Physics");
 721   //   addField( "AngularDrag", TypeF32, )
 722   //endGroup("Physics");
 723
 724   addGroup( "Debug" );
 725      addField( "debugRender", TypeBool, Offset( mDebugRender, PxMultiActor ), "@hide");
 726      addField( "broken", TypeBool, Offset( mBroken, PxMultiActor ), "@hide");
 727   endGroup( "Debug" );
 728
 729   //addGroup("Collision");
 730   //endGroup("Collision");
 731}
 732
 733bool PxMultiActor::onAdd()
 734{
 735   PROFILE_SCOPE( PxMultiActor_OnAdd );
 736
 737   if (!Parent::onAdd() || !mDataBlock )
 738      return false;
 739
 740   mIsDummy = isClientObject() && PHYSICSMGR->isSinglePlayer(); //&& mDataBlock->singlePlayerOnly;
 741
 742   mShapeInstance = new TSShapeInstance( mDataBlock->shape, isClientObject() );
 743
 744   mObjBox = mDataBlock->shape->bounds;
 745   resetWorldBox();
 746
 747   addToScene();
 748
 749   String worldName = isServerObject() ? "server" : "client";
 750
 751   // SinglePlayer objects only have server-side physics representations.
 752   if ( mIsDummy )
 753      worldName = "server";
 754
 755   mWorld = dynamic_cast<PxWorld*>( PHYSICSMGR->getWorld( worldName ) );
 756   if ( !mWorld || !mWorld->getScene() )
 757   {
 758      Con::errorf( "PxMultiActor::onAdd() - PhysXWorld not initialized!" );
 759      return false;
 760   }
 761
 762   applyWarp( getTransform(), true, false );
 763   mResetXfm = getTransform();
 764
 765   if ( !_createActors( getTransform() ) )
 766   {
 767      Con::errorf( "PxMultiActor::onAdd(), _createActors failed" );
 768      return false;
 769   }
 770
 771   if ( !mIsDummy )
 772      mDataBlock->mReloadSignal.notify( this, &PxMultiActor::onFileNotify );
 773
 774   // If the editor is on... let it know!
 775   //if ( gEditingMission )
 776      //onEditorEnable(); // TODO: Fix this up.
 777
 778   PhysicsPlugin::getPhysicsResetSignal().notify( this, &PxMultiActor::onPhysicsReset, 1050.0f );
 779
 780   setAllBroken( false );
 781
 782   if ( isServerObject() )
 783      scriptOnAdd();
 784
 785   return true;
 786}
 787
 788
 789void PxMultiActor::onRemove()
 790{
 791   removeFromScene();
 792
 793   _destroyActors();
 794   mWorld = NULL;
 795
 796   SAFE_DELETE( mShapeInstance );
 797
 798   PhysicsPlugin::getPhysicsResetSignal().remove( this, &PxMultiActor::onPhysicsReset );
 799
 800   if ( !mIsDummy && mDataBlock )
 801      mDataBlock->mReloadSignal.remove( this, &PxMultiActor::onFileNotify );
 802
 803   Parent::onRemove();
 804}
 805
 806void PxMultiActor::_destroyActors()
 807{
 808   // Dummies don't have physics objects.
 809   if ( mIsDummy || !mWorld )
 810      return;
 811
 812   mWorld->releaseWriteLock();
 813
 814   // Clear the root actor.
 815   mRootActor = NULL;
 816
 817   // Clear the relative transforms.
 818   //mRelXfms.clear();
 819
 820   // The shapes are owned by the actors, so
 821   // we just need to clear them.
 822   mShapes.clear();
 823
 824   // Release the joints first.
 825   for( S32 i = 0; i < mJoints.size(); i++ )
 826   {
 827      NxJoint *joint = mJoints[i];
 828
 829      if ( !joint )
 830         continue;
 831
 832      // We allocate per joint userData and we must free it.
 833      PxUserData *jointData = PxUserData::getData( *joint );
 834      if ( jointData )
 835         delete jointData;
 836
 837      mWorld->releaseJoint( *joint );
 838   }
 839   mJoints.clear();
 840
 841   // Now release the actors.
 842   for( S32 i = 0; i < mActors.size(); i++ )
 843   {
 844      NxActor *actor = mActors[i];
 845
 846      PxUserData *actorData = PxUserData::getData( *actor );
 847      if ( actorData )
 848         delete actorData;
 849
 850      if ( actor )
 851         mWorld->releaseActor( *actor );
 852   }
 853   mActors.clear();
 854}
 855
 856bool PxMultiActor::_createActors( const MatrixF &xfm )
 857{
 858   if ( mIsDummy )
 859   {
 860      // Dummies don't have physics objects, but
 861      // they do handle actor deltas.
 862
 863      PxMultiActor *serverObj = static_cast<PxMultiActor*>( mServerObject.getObject() );
 864      mActorDeltas.setSize( serverObj->mActors.size() );
 865      dMemset( mActorDeltas.address(), 0, mActorDeltas.memSize() );
 866
 867      return true;
 868   }
 869
 870   NxMat34 nxMat;
 871   nxMat.setRowMajor44( xfm );
 872
 873   // Store the scale for comparison in setScale().
 874   mActorScale = getScale();
 875
 876   // Release the write lock so we can create actors.
 877   mWorld->releaseWriteLock();
 878
 879   Vector<String> actorUserProperties;
 880   Vector<String> jointUserProperties;
 881   bool created = mDataBlock->createActors(  mWorld->getScene(),
 882                                             NULL,
 883                                             &nxMat,
 884                                             mActorScale,
 885                                             &mActors,
 886                                             &mShapes,
 887                                             &mJoints,
 888                                             &actorUserProperties,
 889                                             &jointUserProperties  );
 890
 891   // Debug output...
 892   //for ( U32 i = 0; i < mJoints.size(); i++ )
 893   //   Con::printf( "Joint0 name: '%s'", mJoints[i]->getName() );
 894   //for ( U32 i = 0; i < actorUserProperties.size(); i++ )
 895      //Con::printf( "actor%i UserProperties: '%s'", i, actorUserProperties[i].c_str() );
 896   //for ( U32 i = 0; i < jointUserProperties.size(); i++ )
 897     // Con::printf( "joint%i UserProperties: '%s'", i, jointUserProperties[i].c_str() );
 898
 899   if ( !created )
 900   {
 901      Con::errorf( "PxMultiActor::_createActors() - failed!" );
 902      return false;
 903   }
 904
 905   // Make the first actor the root actor by default, but
 906   // if we have a kinematic actor then use that.
 907   mRootActor = mActors[0];
 908   for ( S32 i = 0; i < mActors.size(); i++ )
 909   {
 910      if ( mActors[i]->readBodyFlag( NX_BF_KINEMATIC ) )
 911      {
 912         mRootActor = mActors[i];
 913         break;
 914      }
 915   }
 916
 917   mDelta.pos = mDelta.lastPos = getPosition();
 918   mDelta.rot = mDelta.lastRot = getTransform();
 919
 920   bool *usedActors = new bool[mActors.size()];
 921   dMemset( usedActors, 0, sizeof(bool) * mActors.size() );
 922
 923   TSShape *shape = mShapeInstance->getShape();
 924
 925   // Should already be done when actors are destroyed.
 926   mMappedActors.clear();
 927   Vector<String> mappedActorProperties;
 928
 929   // Remap the actors to the shape instance's bone indices.
 930   for( S32 i = 0; i < mShapeInstance->mNodeTransforms.size(); i++ )
 931   {
 932      if ( !shape )
 933         break;
 934
 935      UTF8 comparisonName[260] = { 0 };
 936      NxActor *actor = NULL;
 937      NxActor *pushActor = NULL;
 938      String actorProperties;
 939
 940      S32 nodeNameIdx = shape->nodes[i].nameIndex;
 941      const UTF8 *nodeName = shape->getName( nodeNameIdx );
 942
 943      S32 dl = -1;
 944      dStrcpy( comparisonName, String::GetTrailingNumber( nodeName, dl ) ); //, ext - nodeName );
 945      dSprintf( comparisonName, sizeof( comparisonName ), "%s_pxactor", comparisonName );
 946
 947      //String test( nodeName );
 948      //AssertFatal( test.find("gableone",0,String::NoCase) == String::NPos, "found it" );
 949
 950      // If we find an actor that corresponds to this node we will
 951      // push it back into the remappedActors vector, otherwise
 952      // we will push back NULL.
 953      for ( S32 j = 0; j < mActors.size(); j++ )
 954      {
 955         actor = mActors[j];
 956         const UTF8 *actorName = actor->getName();
 957
 958         if ( dStricmp( comparisonName, actorName ) == 0 )
 959         {
 960            pushActor = actor;
 961            actorProperties = actorUserProperties[j];
 962            usedActors[j] = true;
 963            break;
 964         }
 965      }
 966
 967      mMappedActors.push_back( pushActor );
 968      mappedActorProperties.push_back( actorProperties );
 969      if ( !pushActor )
 970         dl = -1;
 971      mMappedActorDL.push_back( dl );
 972
 973      // Increase the sleep tolerance.
 974      if ( pushActor )
 975      {
 976         //pushActor->raiseBodyFlag( NX_BF_ENERGY_SLEEP_TEST );
 977         //pushActor->setSleepEnergyThreshold( 2 );
 978         //pushActor->userData = NULL;
 979      }
 980   }
 981
 982   // Delete any unused/orphaned actors.
 983   for ( S32 i = 0; i < mActors.size(); i++ )
 984   {
 985      if ( usedActors[i] )
 986         continue;
 987
 988      NxActor *actor = mActors[i];
 989
 990      Con::errorf( "PxMultiActor::_createActors() - Orphan NxActor - '%s'!", actor->getName() );
 991
 992      if ( actor == mRootActor )
 993      {
 994         Con::errorf( "PxMultiActor::_createActors() - root actor (%s) was orphan, cannot continue.", actor->getName() );
 995         return false;
 996      }
 997
 998      // Remove references to shapes of the deleted actor.
 999      for ( S32 i = 0; i < mShapes.size(); i++ )
1000      {
1001         if ( &(mShapes[i]->getActor()) == actor )
1002         {
1003            mShapes.erase_fast(i);
1004            i--;
1005         }
1006      }
1007
1008      mWorld->releaseActor( *actor );
1009   }
1010
1011   // Done with this helper.
1012   delete [] usedActors;
1013
1014   // Repopulate mActors with one entry per real actor we own.
1015   mActors.clear();
1016   mMappedToActorIndex.clear();
1017   actorUserProperties.clear();
1018   for ( S32 i = 0; i < mMappedActors.size(); i++ )
1019   {
1020      S32 index = -1;
1021      if ( mMappedActors[i] )
1022      {
1023         index = mActors.push_back_unique( mMappedActors[i] );
1024         while ( index >= actorUserProperties.size() )
1025            actorUserProperties.push_back( String::EmptyString );
1026         actorUserProperties[index] = mappedActorProperties[i];
1027      }
1028      mMappedToActorIndex.push_back( index );
1029   }
1030
1031   if ( mActors.size() == 0 )
1032   {
1033      Con::errorf( "PxMultiActor::_createActors, got zero actors! Were all actors orphans?" );
1034      return false;
1035   }
1036
1037   // Initialize the actor deltas.
1038   mActorDeltas.setSize( mActors.size() );
1039   dMemset( mActorDeltas.address(), 0, mActorDeltas.memSize() );
1040
1041   // Assign user data for actors.
1042   for ( U32 i = 0; i < mActors.size(); i++ )
1043   {
1044      NxActor *actor = mActors[i];
1045      if ( !actor )
1046         continue;
1047
1048      actor->userData = _createActorUserData( actor, actorUserProperties[i] );
1049   }
1050
1051   //NxActor *actor1;
1052   //NxActor *actor2;
1053   //PxUserData *pUserData;
1054
1055   // Allocate user data for joints.
1056   for ( U32 i = 0; i < mJoints.size(); i++ )
1057   {
1058      NxJoint *joint = mJoints[i];
1059      if ( !joint )
1060         continue;
1061
1062      joint->userData = _createJointUserData( joint, jointUserProperties[i] );
1063
1064      /*
1065      // Set actors attached to joints as not-pushable (by the player).
1066      joint->getActors( &actor1, &actor2 );
1067      if ( actor1 )
1068      {
1069         pUserData = PxUserData::getData( *actor1 );
1070         if ( pUserData )
1071            pUserData->mCanPush = false;
1072      }
1073      if ( actor2 )
1074      {
1075         pUserData = PxUserData::getData( *actor2 );
1076         if ( pUserData )
1077            pUserData->mCanPush = false;
1078      }
1079      */
1080   }
1081
1082   // Set actors and meshes to the unbroken state.
1083   setAllBroken( false );
1084
1085   return true;
1086}
1087
1088PxUserData* PxMultiActor::_createActorUserData( NxActor *actor, String &userProperties )
1089{
1090   PxUserData *actorData = new PxUserData();
1091   actorData->setObject( this );
1092
1093   // We use this for saving relative xfms for 'broken' actors.
1094   NxMat34 actorPose = actor->getGlobalPose();
1095   NxMat34 actorSpaceXfm;
1096   actorPose.getInverse( actorSpaceXfm );
1097
1098   const String actorName( actor->getName() );
1099
1100   static const String showStr( "PxBrokenShow" );
1101   static const String hideStr( "PxBrokenHide" );
1102
1103   // 3DSMax saves out double newlines, replace them with one.
1104   userProperties.replace( "\r\n", "\n" );
1105
1106   U32 propertyCount = StringUnit::getUnitCount( userProperties, "\n" );
1107   for ( U32 i = 0; i < propertyCount; i++ )
1108   {
1109      String propertyStr = StringUnit::getUnit( userProperties, i, "\n" );
1110      U32 wordCount = StringUnit::getUnitCount( propertyStr, "=" );
1111
1112      if ( wordCount == 0 )
1113      {
1114         // We sometimes get empty lines between properties,
1115         // which doesn't break anything.
1116         continue;
1117      }
1118
1119      if ( wordCount != 2 )
1120      {
1121         Con::warnf( "PxMultiActor::_createActorUserData, malformed UserProperty string (%s) for actor (%s)", propertyStr.c_str(), actorName.c_str() );
1122         continue;
1123      }
1124
1125      String propertyName = StringUnit::getUnit( propertyStr, 0, "=" );
1126      String propertyValue = StringUnit::getUnit( propertyStr, 1, "=" );
1127
1128      Vector<NxActor*> *dstVector = NULL;
1129      if ( propertyName.equal( showStr, String::NoCase ) )
1130         dstVector = &actorData->mBrokenActors;
1131      else if ( propertyName.equal( hideStr, String::NoCase ) )
1132         dstVector = &actorData->mUnbrokenActors;
1133
1134      if ( !dstVector )
1135         continue;
1136
1137      U32 valueCount = StringUnit::getUnitCount( propertyValue, "," );
1138      for ( U32 j = 0; j < valueCount; j++ )
1139      {
1140         String val = StringUnit::getUnit( propertyValue, j, "," );
1141
1142         NxActor *pActor = _findActor( val );
1143         if ( !pActor )
1144            Con::warnf( "PxMultiActor::_createActorUserData, actor (%s) was not found when parsing UserProperties for actor (%s)", val.c_str(), actorName.c_str() );
1145         else
1146         {
1147            dstVector->push_back( pActor );
1148
1149            if ( dstVector == &actorData->mBrokenActors )
1150            {
1151               NxMat34 relXfm = pActor->getGlobalPose();
1152               relXfm.multiply( relXfm, actorSpaceXfm );
1153               actorData->mRelXfm.push_back( relXfm );
1154            }
1155         }
1156      }
1157   }
1158
1159   // Only add a contact signal to this actor if
1160   // we have objects we can break.
1161   if (  actorData->mBrokenActors.size() > 0 &&
1162         mDataBlock->breakForce > 0.0f )
1163   {
1164      actor->setContactReportFlags( NX_NOTIFY_ON_START_TOUCH_FORCE_THRESHOLD | NX_NOTIFY_FORCES );
1165      actor->setContactReportThreshold( mDataBlock->breakForce );
1166      actorData->getContactSignal().notify( this, &PxMultiActor::_onContact );
1167   }
1168
1169   return actorData;
1170}
1171
1172PxUserData* PxMultiActor::_createJointUserData( NxJoint *joint, String &userProperties )
1173{
1174   PxUserData *jointData = new PxUserData();
1175   jointData->setObject( this );
1176
1177   // We use this for saving relative xfms for 'broken' actors.
1178   NxActor *actor0;
1179   NxActor *actor1;
1180   joint->getActors( &actor0, &actor1 );
1181   NxMat34 actorPose = actor0->getGlobalPose();
1182   NxMat34 actorSpaceXfm;
1183   actorPose.getInverse( actorSpaceXfm );
1184
1185   // The PxMultiActor will live longer than the joint
1186   // so this notify shouldn't ever need to be removed. Although if someone
1187   // other than this multiactor were to register for this notify and their
1188   // lifetime could be shorter, then 'they' might have to.
1189   jointData->getOnJointBreakSignal().notify( this, &PxMultiActor::_onJointBreak );
1190
1191   // JCFHACK: put this in userProperties too.
1192   Sim::findObject( "JointBreakEmitter", jointData->mParticleEmitterData );
1193
1194   String showStr( "PxBrokenShow" );
1195   String hideStr( "PxBrokenHide" );
1196
1197   // Max saves out double newlines, replace them with one.
1198   userProperties.replace( "\r\n", "\n" );
1199
1200   U32 propertyCount = StringUnit::getUnitCount( userProperties, "\n" );
1201   for ( U32 i = 0; i < propertyCount; i++ )
1202   {
1203      String propertyStr = StringUnit::getUnit( userProperties, i, "\n" );
1204      U32 wordCount = StringUnit::getUnitCount( propertyStr, "=" );
1205
1206      if ( wordCount == 0 )
1207      {
1208         // We sometimes get empty lines between properties,
1209         // which doesn't break anything.
1210         continue;
1211      }
1212
1213      if ( wordCount != 2 )
1214      {
1215         Con::warnf( "PxMultiActor::_createJointUserData, malformed UserProperty string (%s) for joint (%s)", propertyStr.c_str(), joint->getName() );
1216         continue;
1217      }
1218
1219      String propertyName = StringUnit::getUnit( propertyStr, 0, "=" );
1220      String propertyValue = StringUnit::getUnit( propertyStr, 1, "=" );
1221
1222      Vector<NxActor*> *dstVector = NULL;
1223      if ( propertyName.equal( showStr, String::NoCase ) )
1224         dstVector = &jointData->mBrokenActors;
1225      else if ( propertyName.equal( hideStr, String::NoCase ) )
1226         dstVector = &jointData->mUnbrokenActors;
1227
1228      if ( !dstVector )
1229         continue;
1230
1231      U32 valueCount = StringUnit::getUnitCount( propertyValue, "," );
1232      for ( U32 j = 0; j < valueCount; j++ )
1233      {
1234         String val = StringUnit::getUnit( propertyValue, j, "," );
1235
1236         NxActor *pActor = _findActor( val );
1237         if ( !pActor )
1238            Con::warnf( "PxMultiActor::_createJointUserData, actor (%s) was not found when parsing UserProperties for joint (%s)", val.c_str(), joint->getName() );
1239         else
1240         {
1241            dstVector->push_back( pActor );
1242
1243            if ( dstVector == &jointData->mBrokenActors )
1244            {
1245               NxMat34 relXfm = pActor->getGlobalPose();
1246               relXfm.multiply( relXfm, actorSpaceXfm );
1247               jointData->mRelXfm.push_back( relXfm );
1248            }
1249         }
1250      }
1251   }
1252
1253   return jointData;
1254}
1255
1256NxActor* PxMultiActor::_findActor( const String &actorName ) const
1257{
1258   for ( U32 i = 0; i < mActors.size(); i++ )
1259   {
1260      NxActor *actor = mActors[i];
1261      if ( !actor )
1262         continue;
1263
1264      if ( dStricmp( actor->getName(), actorName ) == 0 )
1265         return actor;
1266   }
1267
1268   return NULL;
1269}
1270
1271String PxMultiActor::_getMeshName( const NxActor *actor ) const
1272{
1273   String meshName = actor->getName();
1274   meshName.replace( "_pxactor", "" );
1275   //meshName = StringUnit::getUnit( meshName, 0, "_" );
1276   return meshName;
1277}
1278
1279bool PxMultiActor::onNewDataBlock( GameBaseData *dptr, bool reload )
1280{
1281   mDataBlock = dynamic_cast<PxMultiActorData*>(dptr);
1282
1283   if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
1284      return false;
1285
1286   // JCF: if we supported it, we would recalculate the value of mIsDummy now,
1287   // but that would really hose everything since an object that was a dummy
1288   // wouldn't have any actors and would need to create them, etc...
1289
1290   scriptOnNewDataBlock();
1291
1292   return true;
1293}
1294
1295void PxMultiActor::inspectPostApply()
1296{
1297   // Make sure we call the parent... else
1298   // we won't get transform and scale updates!
1299   Parent::inspectPostApply();
1300
1301   //setMaskBits( LightMask );
1302   setMaskBits( UpdateMask );
1303}
1304
1305void PxMultiActor::onStaticModified( const char *slotName, const char *newValue )
1306{
1307   if ( isProperlyAdded() && dStricmp( slotName, "broken" ) == 0 )
1308      setAllBroken( dAtob(newValue) );
1309}
1310
1311void PxMultiActor::onDeleteNotify( SimObject *obj )
1312{
1313   Parent::onDeleteNotify(obj);
1314   if ( obj == mMount.object )
1315      unmount();
1316}
1317
1318void PxMultiActor::onFileNotify()
1319{
1320   // Destroy the existing actors and recreate them...
1321
1322   mWorld->getPhysicsResults();
1323   _destroyActors();
1324   _createActors( mResetXfm );
1325}
1326
1327void PxMultiActor::onPhysicsReset( PhysicsResetEvent reset )
1328{
1329   // Dummies don't create or destroy actors, they just reuse the
1330   // server object's ones.
1331   if ( mIsDummy )
1332      return;
1333
1334   // Store the reset transform for later use.
1335   if ( reset == PhysicsResetEvent_Store )
1336   {
1337      mRootActor->getGlobalPose().getRowMajor44( mResetXfm );
1338   }
1339   else if ( reset == PhysicsResetEvent_Restore )
1340   {
1341      // Destroy the existing actors and recreate them to
1342      // ensure they are in the proper mission startup state.
1343      mWorld->getPhysicsResults();
1344
1345      _destroyActors();
1346      _createActors( mResetXfm );
1347   }
1348
1349   for ( U32 i = 0; i < mActors.size(); i++ )
1350   {
1351      if ( !mActors[i] )
1352         continue;
1353
1354      mActors[i]->wakeUp();
1355   }
1356}
1357
1358void PxMultiActor::prepRenderImage( SceneRenderState *state )
1359{
1360   PROFILE_SCOPE( PxMultiActor_PrepRenderImage );
1361
1362   if ( !mShapeInstance )
1363      return;
1364
1365   Point3F cameraOffset;
1366   getTransform().getColumn(3,&cameraOffset);
1367   cameraOffset -= state->getDiffuseCameraPosition();
1368   F32 dist = cameraOffset.len();
1369   if ( dist < 0.01f )
1370      dist = 0.01f;
1371
1372   F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
1373
1374   S32 dl = mShapeInstance->setDetailFromDistance( state, dist * invScale );
1375   if ( dl < 0 )
1376      return;
1377
1378   GFXTransformSaver saver;
1379
1380   // Set up our TS render state here.
1381   TSRenderState rdata;
1382   rdata.setSceneState( state );
1383
1384   // We might have some forward lit materials
1385   // so pass down a query to gather lights.
1386   LightQuery query;
1387   query.init( getWorldSphere() );
1388   rdata.setLightQuery( &query );
1389
1390   MatrixF mat = getRenderTransform();
1391   mat.scale( getScale() );
1392   GFX->setWorldMatrix( mat );
1393
1394   if ( mDebugRender || Con::getBoolVariable( "$PxDebug::render", false ) )
1395   {
1396      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
1397      ri->renderDelegate.bind( this, &PxMultiActor::_debugRender );
1398      ri->type = RenderPassManager::RIT_Object;
1399      state->getRenderPass()->addInst( ri );
1400   }
1401   else
1402      mShapeInstance->render( rdata );
1403}
1404
1405void PxMultiActor::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat )
1406{
1407   if ( mShapeInstance )
1408   {
1409      GFXTransformSaver saver;
1410
1411      MatrixF mat = getRenderTransform();
1412      mat.scale( mObjScale );
1413      GFX->multWorld( mat );
1414
1415      //mShapeInstance->renderDebugNodes();
1416   }
1417
1418   Vector<NxActor*> *actors = &mActors;
1419   if ( mIsDummy )
1420   {
1421      PxMultiActor *serverObj = static_cast<PxMultiActor*>( mServerObject.getObject() );
1422      if ( serverObj )
1423         actors = &serverObj->mActors;
1424   }
1425
1426   if ( !actors )
1427      return;
1428
1429   for ( U32 i = 0; i < actors->size(); i++ )
1430   {
1431      NxActor *pActor = (*actors)[i];
1432      if ( !pActor )
1433         continue;
1434
1435      PxUtils::drawActor( pActor );
1436   }
1437}
1438
1439void PxMultiActor::_onJointBreak( NxReal breakForce, NxJoint &brokenJoint )
1440{
1441   // Dummies do not have physics objects
1442   // and shouldn't receive this callback.
1443   if ( mIsDummy )
1444      return;
1445
1446   NxActor *actor0 = NULL;
1447   NxActor *actor1 = NULL;
1448   brokenJoint.getActors( &actor0, &actor1 );
1449   NxMat34 parentPose = actor0->getGlobalPose();
1450
1451   Point3F jointPos = pxCast<Point3F>( brokenJoint.getGlobalAnchor() );
1452
1453   PxUserData *jointData = PxUserData::getData( brokenJoint );
1454   setBroken( parentPose, NxVec3( 0.0f ), jointData, true );
1455
1456   // NOTE: We do not NULL the joint in the list,
1457   // or release it here, as we allow it to be released
1458   // by the _destroyActors function on a reset or destruction
1459   // of the PxMultiActor.
1460}
1461
1462void PxMultiActor::_onContact(   PhysicsUserData *us,
1463                                 PhysicsUserData *them,
1464                                 const Point3F &hitPoint,
1465                                 const Point3F &hitForce )
1466{
1467   PxUserData *data = (PxUserData*)us;
1468   if (  data &&
1469         !data->mIsBroken &&
1470         hitForce.len() > mDataBlock->breakForce )
1471      setAllBroken( true );
1472}
1473
1474U32 PxMultiActor::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
1475{
1476   U32 retMask = Parent::packUpdate(con, mask, stream);
1477
1478   stream->writeFlag( mDebugRender );
1479
1480   stream->writeFlag( mask & SleepMask );
1481
1482   if ( stream->writeFlag( mask & WarpMask ) )
1483   {
1484      stream->writeAffineTransform( getTransform() );
1485   }
1486   else if ( stream->writeFlag( mask & MoveMask ) )
1487   {
1488      /*
1489      stream->writeAffineTransform( getTransform() );
1490
1491      NxActor *actor = mActors[ mDataBlock->correctionNodes[0] ];
1492
1493      const NxVec3& linVel = actor->getLinearVelocity();
1494      stream->write( linVel.x );
1495      stream->write( linVel.y );
1496      stream->write( linVel.z );
1497      */
1498   }
1499
1500   // This internally uses the mask passed to it.
1501   if ( mLightPlugin )
1502      retMask |= mLightPlugin->packUpdate( this, LightMask, con, mask, stream );
1503
1504   return retMask;
1505}
1506
1507
1508void PxMultiActor::unpackUpdate(NetConnection *con, BitStream *stream)
1509{
1510   Parent::unpackUpdate(con, stream);
1511
1512   mDebugRender = stream->readFlag();
1513
1514   if ( stream->readFlag() ) // SleepMask
1515   {
1516      for ( S32 i = 0; i < mActors.size(); i++ )
1517      {
1518         NxActor *actor = mActors[i];
1519
1520         if ( !actor )
1521            continue;
1522
1523         if ( actor )
1524            actor->putToSleep();
1525      }
1526   }
1527
1528   if ( stream->readFlag() ) // WarpMask
1529   {
1530      // If we set a warp mask,
1531      // we need to instantly move
1532      // the actor to the new position
1533      // without applying any corrections.
1534      MatrixF mat;
1535      stream->readAffineTransform( &mat );
1536
1537      applyWarp( mat, true, false );
1538   }
1539   else if ( stream->readFlag() ) // MoveMask
1540   {
1541      /*
1542      MatrixF mat;
1543      stream->readAffineTransform( &mat );
1544
1545      NxVec3 linVel, angVel;
1546      stream->read( &linVel.x );
1547      stream->read( &linVel.y );
1548      stream->read( &linVel.z );
1549
1550      applyCorrection( mat, linVel, angVel );
1551      */
1552   }
1553/*
1554   if ( stream->readFlag() ) // ImpulseMask
1555   {
1556      // TODO : Set up correction nodes.
1557
1558      NxVec3 linVel;
1559      stream->read( &linVel.x );
1560      stream->read( &linVel.y );
1561      stream->read( &linVel.z );
1562
1563      NxActor *actor = mActors[ mDataBlock->correctionNodes[0] ];
1564
1565      if ( actor )
1566      {
1567         mWorld->releaseWriteLock();
1568         actor->setLinearVelocity( linVel );
1569         mStartImpulse.zero();
1570      }
1571      else
1572         mStartImpulse.set( linVel.x, linVel.y, linVel.z );
1573   }
1574*/
1575   if ( mLightPlugin )
1576      mLightPlugin->unpackUpdate( this, con, stream );
1577}
1578
1579void PxMultiActor::setScale( const VectorF& scale )
1580{
1581   if ( scale == getScale() )
1582      return;
1583
1584   // This is so that the level
1585   // designer can change the scale
1586   // of a PhysXSingleActor in the editor
1587   // and have the PhysX representation updated properly.
1588
1589   // First we call the parent's setScale
1590   // so that the ScaleMask can be set.
1591   Parent::setScale( scale );
1592
1593   // Check to see if the scale has really changed.
1594   if ( !isProperlyAdded() || mActorScale.equal( scale ) )
1595      return;
1596
1597   // Recreate the physics actors.
1598   _destroyActors();
1599   _createActors( getTransform() );
1600}
1601
1602void PxMultiActor::applyWarp( const MatrixF& newMat, bool interpRender, bool sweep )
1603{
1604   // Do we have actors to move?
1605   if ( mRootActor )
1606   {
1607      // Get ready to change the physics state.
1608      mWorld->releaseWriteLock();
1609
1610      /// Convert the new transform to nx.
1611      NxMat34 destXfm;
1612      destXfm.setRowMajor44( newMat );
1613
1614      // Get the inverse of the root actor transform
1615      // so we can move all the actors relative to it.
1616      NxMat34 rootInverseXfm;
1617      mRootActor->getGlobalPose().getInverse( rootInverseXfm );
1618
1619      // Offset all the actors.
1620      MatrixF tMat;
1621      NxMat34 newXfm, relXfm;
1622      for ( S32 i = 0; i < mActors.size(); i++ )
1623      {
1624         NxActor *actor = mActors[i];
1625         if ( !actor )
1626            continue;
1627
1628         const bool isKinematic = actor->readBodyFlag( NX_BF_KINEMATIC );
1629
1630         // Stop any velocity on it.
1631         if ( !isKinematic )
1632         {
1633            actor->setAngularVelocity( NxVec3( 0.0f ) );
1634            actor->setLinearVelocity( NxVec3( 0.0f ) );
1635         }
1636
1637         // Get the transform relative to the current root.
1638         relXfm.multiply( actor->getGlobalPose(), rootInverseXfm );
1639
1640         /*
1641         if ( sweep )
1642         {
1643            actor->getGl obalPose().getRowMajor44( mResetPos[i] );
1644            sweepTest( &newMat );
1645         }
1646         */
1647
1648         //
1649         newXfm.multiply( relXfm, destXfm );
1650         //if ( isKinematic )
1651            //actor->moveGlobalPose( newXfm );
1652         //else
1653            actor->setGlobalPose( newXfm );
1654
1655         // Reset the delta.
1656         Delta &delta = mActorDeltas[i];
1657         delta.pos = pxCast<Point3F>( newXfm.t );
1658         newXfm.getRowMajor44( tMat );
1659         delta.rot.set( tMat );
1660
1661         if ( !interpRender )
1662         {
1663            mActorDeltas[i].lastPos = mActorDeltas[i].pos;
1664            mActorDeltas[i].lastRot = mActorDeltas[i].rot;
1665         }
1666      }
1667   }
1668
1669   Parent::setTransform( newMat );
1670
1671   mDelta.pos = newMat.getPosition();
1672   mDelta.rot = newMat;
1673
1674   if ( !interpRender )
1675   {
1676      mDelta.lastPos = mDelta.pos;
1677      mDelta.lastRot = mDelta.rot;
1678   }
1679}
1680
1681/*
1682bool PxMultiActor::_setPositionField( void *obj, const char *data )
1683{
1684   PxMultiActor *object = reinterpret_cast<PxMultiActor*>( obj );
1685
1686   MatrixF transform( object->getTransform() );
1687   Con::setData( TypeMatrixPosition, &transform, 0, 1, &data );
1688
1689   object->setTransform( transform );
1690
1691   return false;
1692}
1693
1694const char* PxMultiActor::_getPositionField( void *obj, const char *data )
1695{
1696   PxMultiActor *object = reinterpret_cast<PxMultiActor*>( obj );
1697   return Con::getData( TypeMatrixPosition,
1698                        &object->mObjToWorld,
1699                        0 );
1700}
1701
1702bool PxMultiActor::_setRotationField( void *obj, const char *data )
1703{
1704   PxMultiActor *object = reinterpret_cast<PxMultiActor*>( obj );
1705
1706   MatrixF transform( object->getTransform() );
1707   Con::setData( TypeMatrixRotation, &transform, 0, 1, &data );
1708
1709   object->setTransform( transform );
1710
1711   return false;
1712}
1713
1714const char* PxMultiActor::_getRotationField( void *obj, const char *data )
1715{
1716   PxMultiActor *object = reinterpret_cast<PxMultiActor*>( obj );
1717   return Con::getData( TypeMatrixRotation,
1718                        &object->mObjToWorld,
1719                        0 );
1720}
1721*/
1722
1723void PxMultiActor::setTransform( const MatrixF& mat )
1724{
1725   applyWarp( mat, false, true );
1726   setMaskBits( WarpMask );
1727}
1728
1729void PxMultiActor::mountObject( SceneObject *obj, U32 node )
1730{
1731   if (obj->getObjectMount())
1732      obj->unmount();
1733
1734   obj->mountObject( this, (node >= 0 && node < PxMultiActorData::NumMountPoints) ? node: 0 );
1735}
1736
1737
1738void PxMultiActor::unmountObject( SceneObject *obj )
1739{
1740   obj->unmountObject( this );
1741}
1742
1743bool PxMultiActor::_getNodeTransform( U32 nodeIdx, MatrixF *outXfm )
1744{   
1745   if ( !mShapeInstance )
1746      return false;
1747
1748   PxMultiActor *actorOwner = this;
1749   if ( mIsDummy )
1750   {
1751      actorOwner = static_cast<PxMultiActor*>( mServerObject.getObject() );
1752      if ( !actorOwner )
1753         return false;
1754   }
1755
1756   TSShape *shape = mShapeInstance->getShape();
1757   String nodeName = shape->getNodeName( nodeIdx );
1758
1759   NxActor *pActor = NULL;
1760   UTF8 comparisonName[260] = { 0 };
1761   S32 dummy = -1;
1762
1763   // Convert the passed node name to a valid actor name.
1764   dStrcpy( comparisonName, String::GetTrailingNumber( nodeName, dummy ) );
1765   dSprintf( comparisonName, sizeof( comparisonName ), "%s_pxactor", comparisonName );
1766
1767   // If we have an actor with that name, we are done.
1768   pActor = actorOwner->_findActor( comparisonName );
1769   if ( pActor )
1770   {
1771      pActor->getGlobalPose().getRowMajor44( *outXfm );
1772      return true;
1773   }
1774
1775   // Check if the parent node has an actor...
1776
1777   S32 parentIdx = shape->nodes[nodeIdx].parentIndex;
1778   if ( parentIdx == -1 )
1779      return false;
1780
1781   const String &parentName = shape->getNodeName( parentIdx );
1782   dStrcpy( comparisonName, String::GetTrailingNumber( parentName, dummy ) );
1783   dSprintf( comparisonName, sizeof( comparisonName ), "%s_pxactor", comparisonName );
1784
1785   pActor = actorOwner->_findActor( comparisonName );
1786   if ( !pActor )
1787      return false;
1788
1789   MatrixF actorMat;
1790   pActor->getGlobalPose().getRowMajor44( actorMat );
1791
1792   MatrixF nmat;
1793   QuatF q;
1794   TSTransform::setMatrix( shape->defaultRotations[nodeIdx].getQuatF(&q),shape->defaultTranslations[nodeIdx],&nmat);
1795   *outXfm->mul( actorMat, nmat );
1796   
1797   return true;
1798}
1799
1800void PxMultiActor::getMountTransform(U32 mountPoint,MatrixF* mat)
1801{
1802   // Returns mount point to world space transform
1803   if (mountPoint < PxMultiActorData::NumMountPoints) {
1804      S32 ni = mDataBlock->mountPointNode[mountPoint];
1805      if (ni != -1) {
1806         if ( _getNodeTransform( ni, mat ) )
1807            return;
1808      }
1809   }
1810   *mat = mObjToWorld;
1811}
1812
1813void PxMultiActor::getRenderMountTransform(U32 mountPoint,MatrixF* mat)
1814{
1815   // Returns mount point to world space transform
1816   if (mountPoint < PxMultiActorData::NumMountPoints) {
1817      S32 ni = mDataBlock->mountPointNode[mountPoint];
1818      if (ni != -1) {
1819         if ( _getNodeTransform( ni, mat ) )
1820            return;
1821      }
1822   }
1823   *mat = getRenderTransform();
1824}
1825
1826void PxMultiActor::processTick( const Move *move )
1827{
1828   PROFILE_SCOPE( PxMultiActor_ProcessTick );
1829
1830   // Set the last pos/rot to the
1831   // values of the previous tick for interpolateTick.
1832   mDelta.lastPos = mDelta.pos;
1833   mDelta.lastRot = mDelta.rot;
1834
1835   /*
1836   NxActor *corrActor = mActors[ mDataBlock->correctionNodes[0] ];
1837
1838   if ( corrActor->isSleeping() || corrActor->readBodyFlag( NX_BF_FROZEN ) )
1839   {
1840      if ( !mSleepingLastTick )
1841         setMaskBits( WarpMask | SleepMask );
1842
1843      mSleepingLastTick = true;
1844
1845      // HACK!  Refactor sleeping so that we don't
1846      // sleep when only one correction actor does.
1847      _updateBounds();
1848
1849      return;
1850   }
1851
1852   mSleepingLastTick = false;
1853   */
1854
1855   MatrixF mat;
1856   Vector<NxActor*> *actors;
1857
1858   if ( mIsDummy )
1859   {
1860      PxMultiActor *serverObj = static_cast<PxMultiActor*>( mServerObject.getObject() );
1861      if ( !serverObj )
1862         return;
1863
1864      mat = serverObj->getTransform();
1865      actors = &serverObj->mActors;
1866   }
1867   else
1868   {
1869      // Container buoyancy & drag
1870      _updateContainerForces();
1871
1872      // Save the transform from the root actor.
1873      mRootActor->getGlobalPose().getRowMajor44( mat );
1874      actors = &mActors;
1875   }
1876
1877   // Update the transform and the root delta.
1878   Parent::setTransform( mat );
1879   mDelta.pos = mat.getPosition();
1880   mDelta.rot.set( mat );
1881
1882   // On the client we update the individual
1883   // actor deltas as well for interpolation.
1884   if ( isClientObject() )
1885   {
1886      PROFILE_SCOPE( PxMultiActor_ProcessTick_UpdateDeltas );
1887
1888      for ( U32 i = 0; i < mActorDeltas.size(); i++ )
1889      {
1890         if ( !(*actors)[i] )
1891            continue;
1892
1893         Delta &delta = mActorDeltas[i];
1894
1895         // Store the last position.
1896         delta.lastPos = delta.pos;
1897         delta.lastRot = delta.rot;
1898
1899         // Get the new position.
1900         (*actors)[i]->getGlobalPose().getRowMajor44( (NxF32*)mat );
1901
1902         // Calculate the delta between the current
1903         // global pose and the last global pose.
1904         delta.pos = mat.getPosition();
1905         delta.rot.set( mat );
1906      }
1907   }
1908
1909   // Update the bounding box to match the physics.
1910   _updateBounds();
1911
1912   // Set the MoveMask so this will be updated to the client.
1913   //setMaskBits( MoveMask );
1914}
1915
1916void PxMultiActor::interpolateTick( F32 delta )
1917{
1918   PROFILE_SCOPE( PxMultiActor_InterpolateTick );
1919
1920   Point3F interpPos;
1921   QuatF interpRot;
1922   {
1923       // Interpolate the position based on the delta.
1924      interpPos.interpolate( mDelta.pos, mDelta.lastPos, delta );
1925
1926      // Interpolate the rotation based on the delta.
1927      interpRot.interpolate( mDelta.rot, mDelta.lastRot, delta );
1928
1929      // Set up the interpolated transform.
1930      MatrixF interpMat;
1931      interpRot.setMatrix( &interpMat );
1932      interpMat.setPosition( interpPos );
1933
1934      Parent::setRenderTransform( interpMat );
1935   }
1936
1937   PxMultiActor *srcObj = NULL;
1938   if ( mIsDummy )
1939      srcObj = static_cast<PxMultiActor*>( mServerObject.getObject() );
1940   else
1941      srcObj = this;
1942
1943   // JCF: to disable applying NxActor positions to the renderable mesh
1944   // you can uncomment this line.
1945   //srcObj = NULL;
1946   if ( mShapeInstance && srcObj != NULL )
1947   {
1948      mShapeInstance->animate();
1949      getDynamicXfms( srcObj, delta );
1950   }
1951}
1952
1953/*
1954void PxMultiActor::sweepTest( MatrixF *mat )
1955{
1956   NxVec3 nxCurrPos = getPosition();
1957
1958   // If the position is zero,
1959   // the parent hasn't been updated yet
1960   // and we don't even need to do the sweep test.
1961   // This is a fix for a problem that was happening
1962   // where on the add of the PhysXSingleActor, it would
1963   // set the position to a very small value because it would be getting a hit
1964   // even though the current position was 0.
1965   if ( nxCurrPos.isZero() )
1966      return;
1967
1968   // Set up the flags and the query structure.
1969   NxU32 flags = NX_SF_STATICS | NX_SF_DYNAMICS;
1970
1971   NxSweepQueryHit sweepResult;
1972   dMemset( &sweepResult, 0, sizeof( sweepResult ) );
1973
1974   NxVec3 nxNewPos = mat->getPosition();
1975
1976   // Get the velocity which will be our sweep direction and distance.
1977   NxVec3 nxDir = nxNewPos - nxCurrPos;
1978   if ( nxDir.isZero() )
1979      return;
1980
1981   NxActor *corrActor = mActors[ mDataBlock->correctionNodes[0] ];
1982
1983   // Get the scene and do the sweep.
1984   corrActor->wakeUp();
1985   corrActor->linearSweep( nxDir, flags, NULL, 1, &sweepResult, NULL );
1986
1987   if ( sweepResult.hitShape && sweepResult.t < nxDir.magnitude() )
1988   {
1989      nxDir.normalize();
1990      nxDir *= sweepResult.t;
1991      nxCurrPos += nxDir;
1992
1993      mat->setPosition( Point3F( nxCurrPos.x, nxCurrPos.y, nxCurrPos.z ) );
1994   }
1995}
1996*/
1997
1998/*
1999void PxMultiActor::applyCorrection( const MatrixF& mat, const NxVec3& linVel, const NxVec3& angVel )
2000{
2001   // Sometimes the actor hasn't been
2002   // created yet during the call from unpackUpdate.
2003   NxActor *corrActor = mActors[ mDataBlock->correctionNodes[0] ];
2004
2005   if ( !corrActor || mForceSleep )
2006      return;
2007
2008   NxVec3 newPos = mat.getPosition();
2009   NxVec3 currPos = getPosition();
2010
2011   NxVec3 offset = newPos - currPos;
2012
2013   // If the difference isn't large enough,
2014   // just set the new transform, no correction.
2015   if ( offset.magnitude() > 0.3f )
2016   {
2017      // If we're going to set the linear or angular velocity,
2018      // we do it before we add a corrective force, since it would be overwritten otherwise.
2019      NxVec3 currLinVel, currAngVel;
2020      currLinVel = corrActor->getLinearVelocity();
2021      currAngVel = corrActor->getAngularVelocity();
2022
2023      // Scale the corrective force by half,
2024      // otherwise it will over correct and oscillate.
2025      NxVec3 massCent = corrActor->getCMassGlobalPosition();
2026      corrActor->addForceAtPos( offset, massCent, NX_SMOOTH_VELOCITY_CHANGE );
2027
2028      // If the linear velocity is divergent enough, change to server linear velocity.
2029      if ( (linVel - currLinVel).magnitude() > 0.3f )
2030         corrActor->setLinearVelocity( linVel );
2031      // Same for angular.
2032      if ( (angVel - currAngVel).magnitude() > 0.3f )
2033         corrActor->setAngularVelocity( angVel );
2034   }
2035
2036   Parent::setTransform( mat );
2037}
2038*/
2039
2040void PxMultiActor::_updateBounds()
2041{
2042   PROFILE_SCOPE( PxMultiActor_UpdateBounds );
2043
2044   if ( mIsDummy )
2045   {
2046      PxMultiActor *serverObj = static_cast<PxMultiActor*>( mServerObject.getObject() );
2047      if ( !serverObj )
2048         return;
2049
2050      mWorldBox = serverObj->getWorldBox();
2051      mWorldSphere = serverObj->getWorldSphere();
2052      mObjBox = serverObj->getObjBox();
2053      mRenderWorldBox = serverObj->getRenderWorldBox();
2054      mRenderWorldSphere = mWorldSphere;
2055
2056      return;
2057   }
2058
2059   NxBounds3 bounds;
2060   bounds.setEmpty();
2061
2062   NxBounds3 shapeBounds;
2063
2064   for ( U32 i = 0; i < mActors.size(); i++ )
2065   {
2066      NxActor *pActor = mActors[i];
2067
2068      if ( !pActor || pActor->readActorFlag( NX_AF_DISABLE_COLLISION ) )
2069         continue;
2070
2071      NxShape *const* pShapeArray = pActor->getShapes();
2072      U32 shapeCount = pActor->getNbShapes();
2073      for ( U32 i = 0; i < shapeCount; i++ )
2074      {
2075         // Get the shape's bounds.
2076         pShapeArray[i]->getWorldBounds( shapeBounds );
2077
2078         // Combine them into the total bounds.
2079         bounds.combine( shapeBounds );
2080      }
2081   }
2082
2083   mWorldBox = pxCast<Box3F>( bounds );
2084
2085   mWorldBox.getCenter(&mWorldSphere.center);
2086   mWorldSphere.radius = (mWorldBox.maxExtents - mWorldSphere.center).len();
2087
2088   mObjBox = mWorldBox;
2089   mWorldToObj.mul(mObjBox);
2090
2091   mRenderWorldBox = mWorldBox;
2092   mRenderWorldSphere = mWorldSphere;
2093}
2094
2095void PxMultiActor::getDynamicXfms( PxMultiActor *srcObj, F32 dt )
2096{
2097   PROFILE_SCOPE( PxMultiActor_getDynamicXfms );
2098
2099   Vector<MatrixF> *torqueXfms = &mShapeInstance->mNodeTransforms;
2100   const MatrixF &objectXfm = getRenderWorldTransform();
2101
2102   AssertFatal( torqueXfms->size() == srcObj->mMappedActors.size(), "The two skeletons are different!" );
2103
2104   TSShape *shape = mShapeInstance->getShape();
2105
2106   // TODO: We're currently preparing deltas and getting
2107   // dynamic xforms even if the object isn't visible.
2108   // we should probably try to delay all this until
2109   // we're about to render.
2110   //
2111   /*
2112   // TODO: Set up deltas!
2113   if ( mCurrPos.empty() || mCurrRot.empty() )
2114      _prepareDeltas();
2115   */
2116
2117   MatrixF globalXfm;
2118   MatrixF mat, tmp;
2119   QuatF newRot;
2120   Point3F newPos;
2121
2122   S32 dl = mShapeInstance->getCurrentDetail();
2123   if ( dl < 0 )
2124      return;
2125
2126   const String &detailName = shape->getName( shape->details[dl].nameIndex );
2127   S32 detailSize = -1;
2128   String::GetTrailingNumber( detailName, detailSize );
2129
2130   for( S32 i = 0; i < srcObj->mMappedActors.size(); i++ )
2131   {
2132      NxActor *actor = srcObj->mMappedActors[i];
2133
2134      if ( !actor || actor->readBodyFlag( NX_BF_KINEMATIC ) )
2135         continue;
2136
2137      // see if the node at this index is part of the
2138      // currently visible detail level.
2139      if ( srcObj->mMappedActorDL[i] != detailSize )
2140         continue;
2141
2142      // Get the right actor delta structure.
2143      U32 index = srcObj->mMappedToActorIndex[i];
2144      const Delta &delta = mActorDeltas[index];
2145
2146      // Do the interpolation.
2147      newRot.interpolate( delta.rot, delta.lastRot, dt );
2148      newRot.setMatrix( &globalXfm );
2149      newPos.interpolate( delta.pos, delta.lastPos, dt );
2150      globalXfm.setPosition( newPos );
2151
2152      (*torqueXfms)[i].mul( objectXfm, globalXfm );
2153   }
2154}
2155
2156void PxMultiActor::applyImpulse( const Point3F &pos, const VectorF &vec )
2157{
2158   // TODO : Implement this based on correction nodes.
2159   /*
2160   if ( !mWorld || !mActor )
2161      return;
2162
2163   mWorld->releaseWriteLock();
2164
2165   NxVec3 linVel = mActor->getLinearVelocity();
2166   NxVec3 nxVel( vel.x, vel.y, vel.z );
2167
2168   mActor->setLinearVelocity(linVel + nxVel);
2169   */
2170
2171   // JCF: something more complex is required to apply forces / breakage
2172   // on only individual actors, and we don't have enough data to do that
2173   // within this method.
2174
2175   if ( vec.len() > mDataBlock->breakForce )
2176      setAllBroken( true );
2177
2178   NxVec3 nxvec = pxCast<NxVec3>( vec );
2179   NxVec3 nxpos = pxCast<NxVec3>( pos );
2180
2181   for ( U32 i = 0; i < mActors.size(); i++ )
2182   {
2183      NxActor *actor = mActors[i];
2184      if ( actor->isDynamic() &&
2185           !actor->readBodyFlag( NX_BF_KINEMATIC ) &&
2186           !actor->readActorFlag( NX_AF_DISABLE_COLLISION ) )
2187      {
2188         actor->addForceAtPos( nxvec, nxpos, NX_IMPULSE );
2189      }
2190   }
2191
2192   //setMaskBits( ImpulseMask );
2193}
2194
2195void PxMultiActor::applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude )
2196{
2197   mWorld->releaseWriteLock();
2198
2199   // Find all currently enabled actors hit by the impulse radius...
2200   Vector<NxActor*> hitActors;
2201   NxVec3 nxorigin = pxCast<NxVec3>(origin);
2202   NxSphere impulseSphere( nxorigin, radius );
2203
2204   for ( U32 i = 0; i < mActors.size(); i++ )
2205   {
2206      NxActor *pActor = mActors[i];
2207
2208      if ( pActor->readActorFlag( NX_AF_DISABLE_COLLISION ) ||
2209           !pActor->isDynamic() ||
2210           pActor->readBodyFlag( NX_BF_KINEMATIC ) )
2211         continue;
2212
2213      U32 numShapes = pActor->getNbShapes();
2214      NxShape *const* pShapeArray = pActor->getShapes();
2215
2216      for ( U32 j = 0; j < numShapes; j++ )
2217      {
2218         const NxShape *pShape = pShapeArray[j];
2219
2220         if ( pShape->checkOverlapSphere( impulseSphere ) )
2221         {
2222            hitActors.push_back( pActor );
2223            break;
2224         }
2225      }
2226   }
2227
2228   // Apply forces to hit actors, but swap out for broken
2229   // actors first if appropriate...
2230   for ( U32 i = 0; i < hitActors.size(); i++ )
2231   {
2232      NxActor *pActor = hitActors[i];
2233
2234      PxUserData *pUserData = PxUserData::getData( *pActor );
2235
2236      // TODO: We should calculate the real force accounting
2237      // for falloff before we break things with it.
2238
2239      // If we have enough force, and this is an actor that
2240      // can be 'broken' by impacts, break it now.
2241      if ( pUserData &&
2242           //pUserData->mCanPush &&
2243           pUserData->mBrokenActors.size() > 0 &&
2244           magnitude > mDataBlock->breakForce )
2245      {
2246         setBroken(  pActor->getGlobalPose(),
2247                     pActor->getLinearVelocity(),
2248                     pUserData,
2249                     true );
2250
2251         // apply force that would have been applied to this actor
2252         // to the broken actors we just enabled.
2253
2254         for ( U32 j = 0; j < pUserData->mBrokenActors.size(); j++ )
2255         {
2256            NxActor *pBrokenActor = pUserData->mBrokenActors[j];
2257            _applyActorRadialForce( pBrokenActor, nxorigin, radius, magnitude );
2258         }
2259      }
2260      else
2261      {
2262         // Apply force to the actor.
2263         _applyActorRadialForce( pActor, nxorigin, radius, magnitude );
2264      }
2265   }
2266}
2267
2268void PxMultiActor::_applyActorRadialForce( NxActor *inActor, const NxVec3 &origin, F32 radius, F32 magnitude )
2269{
2270   // TODO: We're not getting a good torque force
2271   // out of explosions because we're not picking
2272   // the nearest point on the actor to the origin
2273   // of the radial force.
2274
2275   NxVec3 force = inActor->getCMassGlobalPosition() - origin;
2276   NxF32 dist = force.magnitude();
2277   force.normalize();
2278
2279   if ( dist == 0.0f )
2280      force *= magnitude;
2281   else
2282      force *= mClampF( radius / dist, 0.0f, 1.0f ) * magnitude;      
2283
2284   // HACK: Make the position we push the force thru between the
2285   // actor pos and its center of mass.  This gives us some
2286   // rotational force as well as make the covered structure
2287   // explode better.
2288   NxVec3 forcePos = ( inActor->getGlobalPosition() + inActor->getCMassGlobalPosition() ) / 2.0f;
2289   inActor->addForceAtPos( force, forcePos, NX_VELOCITY_CHANGE );
2290}
2291
2292void PxMultiActor::_updateContainerForces()
2293{
2294   if ( !mWorld->getEnabled() )
2295      return;
2296
2297   PROFILE_SCOPE( PxMultiActor_updateContainerForces );
2298
2299   // Update container drag and buoyancy properties ( for each Actor )
2300
2301   for ( U32 i = 0; i < mActors.size(); i++ )
2302   {
2303      NxActor *pActor = mActors[i];
2304
2305      if (  !pActor ||
2306            pActor->readBodyFlag(NX_BF_KINEMATIC) ||
2307            pActor->readActorFlag(NX_AF_DISABLE_COLLISION) )
2308         continue;
2309
2310      // Get world bounds of this actor ( the combination of all shape bounds )
2311      NxShape *const* shapes = pActor->getShapes();
2312      NxBounds3 bounds;
2313      bounds.setEmpty();
2314      NxBounds3 shapeBounds;
2315
2316      for ( U32 i = 0; i < pActor->getNbShapes(); i++ )
2317      {
2318         NxShape *pShape = shapes[i];
2319         pShape->getWorldBounds(shapeBounds);
2320
2321         bounds.combine( shapeBounds );
2322      }
2323
2324      Box3F boundsBox = pxCast<Box3F>(bounds);
2325
2326      ContainerQueryInfo info;
2327      info.box = boundsBox;
2328      info.mass = pActor->getMass();
2329
2330      // Find and retreive physics info from intersecting WaterObject(s)
2331      mContainer->findObjects( boundsBox, WaterObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a1af121084d5760a7eaef4bb8828ce1cf">PhysicalZoneObjectType, findRouter, &info );
2332
2333      // Calculate buoyancy and drag
2334      F32 angDrag = mDataBlock->angularDrag;
2335      F32 linDrag = mDataBlock->linearDrag;
2336      F32 buoyancy = 0.0f;
2337
2338      if ( true ) //info.waterCoverage >= 0.1f)
2339      {
2340         F32 waterDragScale = info.waterViscosity * mDataBlock->waterDragScale;
2341         F32 powCoverage = mPow( info.waterCoverage, 0.25f );
2342
2343         if ( info.waterCoverage > 0.0f )
2344         {
2345            //angDrag = mBuildAngDrag * waterDragScale;
2346            //linDrag = mBuildLinDrag * waterDragScale;
2347            angDrag = mLerp( angDrag, angDrag * waterDragScale, powCoverage );
2348            linDrag = mLerp( linDrag, linDrag * waterDragScale, powCoverage );
2349         }
2350
2351         buoyancy = ( info.waterDensity / mDataBlock->buoyancyDensity ) * mPow( info.waterCoverage, 2.0f );
2352      }
2353
2354      // Apply drag (dampening)
2355      pActor->setLinearDamping( linDrag );
2356      pActor->setAngularDamping( angDrag );
2357
2358      // Apply buoyancy force
2359      if ( buoyancy != 0 )
2360      {
2361         // A little hackery to prevent oscillation
2362         // Based on this blog post:
2363         // (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html)
2364         // JCF: disabled!
2365         NxVec3 gravity;
2366         mWorld->getScene()->getGravity(gravity);
2367         //NxVec3 velocity = pActor->getLinearVelocity();
2368
2369         NxVec3 buoyancyForce = buoyancy * -gravity * TickSec * pActor->getMass();
2370         //F32 currHeight = getPosition().z;
2371         //const F32 C = 2.0f;
2372         //const F32 M = 0.1f;
2373
2374         //if ( currHeight + velocity.z * TickSec * C > info.waterHeight )
2375         //   buoyancyForce *= M;
2376
2377         pActor->addForceAtPos( buoyancyForce, pActor->getCMassGlobalPosition(), NX_IMPULSE );
2378      }
2379
2380      // Apply physical zone forces
2381      if ( info.appliedForce.len() > 0.001f )
2382         pActor->addForceAtPos( pxCast<NxVec3>(info.appliedForce), pActor->getCMassGlobalPosition(), NX_IMPULSE );
2383   }
2384}
2385
2386/*
2387ConsoleMethod( PxMultiActor, applyImpulse, void, 3, 3, "applyImpulse - takes a velocity vector to apply")
2388{
2389   VectorF vec;
2390   dSscanf( argv[2],"%g %g %g",
2391           &vec.x,&vec.y,&vec.z );
2392
2393   object->applyImpulse( vec );
2394}
2395*/
2396
2397void PxMultiActor::setAllBroken( bool isBroken )
2398{
2399   PROFILE_SCOPE( PxMultiActor_SetAllBroken );
2400
2401   if ( mIsDummy )
2402   {
2403      PxMultiActor *serverObj = static_cast<PxMultiActor*>( mServerObject.getObject() );
2404      serverObj->setAllBroken( isBroken );
2405      return;
2406   }
2407
2408   mWorld->releaseWriteLock();
2409
2410   NxActor *actor0 = NULL;
2411   NxActor *actor1 = NULL;
2412   NxMat34 parentPose;
2413
2414   for ( U32 i = 0; i < mJoints.size(); i++ )
2415   {
2416      NxJoint *joint = mJoints[i];
2417      if ( !joint )
2418         continue;
2419
2420      PxUserData *jointData = PxUserData::getData( *joint );
2421      if ( !jointData )
2422         continue;
2423
2424      joint->getActors( &actor0, &actor1 );
2425      parentPose = actor0->getGlobalPose();
2426
2427      setBroken( parentPose, NxVec3(0.0f), jointData, isBroken );
2428   }
2429
2430   for ( U32 i = 0; i < mActors.size(); i++ )
2431   {
2432      NxActor *actor = mActors[i];
2433      if ( !actor )
2434         continue;
2435
2436      PxUserData *actorData = PxUserData::getData( *actor );
2437      if ( !actorData )
2438         continue;
2439
2440      setBroken(  actor->getGlobalPose(),
2441                  actor->getLinearVelocity(),
2442                  actorData,
2443                  isBroken );
2444   }
2445}
2446
2447void PxMultiActor::setBroken( const NxMat34 &parentPose,
2448                              const NxVec3 &parentVel,
2449                              PxUserData *userData,
2450                              bool isBroken )
2451{
2452   PROFILE_SCOPE( PxMultiActor_SetBroken );
2453
2454   // TODO: This function is highly inefficent and
2455   // way too complex to follow... the hacked single
2456   // player mode doesn't help.
2457
2458   // Be careful not to set something broken twice.
2459   if (  isBroken &&
2460         userData->mIsBroken == isBroken )
2461      return;
2462
2463   userData->mIsBroken = isBroken;
2464
2465   Vector<NxActor*> *hideActors = NULL;
2466   Vector<NxActor*> *showActors = NULL;
2467
2468   if ( isBroken )
2469   {
2470      hideActors = &userData->mUnbrokenActors;
2471      showActors = &userData->mBrokenActors;
2472   }
2473   else
2474   {
2475      hideActors = &userData->mBrokenActors;
2476      showActors = &userData->mUnbrokenActors;
2477   }
2478
2479   NxActor *pActor = NULL;
2480   MatrixF tMat;
2481   for ( U32 i = 0; i < hideActors->size(); i++ )
2482   {
2483      pActor = (*hideActors)[i];
2484
2485      pActor->raiseActorFlag( NX_AF_DISABLE_COLLISION );
2486      pActor->raiseBodyFlag( NX_BF_KINEMATIC );
2487      pActor->putToSleep();
2488
2489      NxShape *const* pShapeArray = pActor->getShapes();
2490      U32 shapeCount = pActor->getNbShapes();
2491      for ( U32 i = 0; i < shapeCount; i++ )
2492         pShapeArray[i]->setFlag( NX_SF_DISABLE_RAYCASTING, true );
2493
2494      setMeshHidden( _getMeshName( pActor ), true );
2495   }
2496
2497   // Get the client side delta array.
2498   Vector<Delta> *actorDeltas = NULL;
2499   if ( isClientObject() )
2500      actorDeltas = &mActorDeltas;
2501   else if ( isServerObject() && PHYSICSMGR->isSinglePlayer() )
2502   {
2503      PxMultiActor *clientObj = static_cast<PxMultiActor*>( getClientObject() );
2504      if ( clientObj )
2505         actorDeltas = &clientObj->mActorDeltas;
2506   }
2507   U32 index;
2508
2509   for ( U32 i = 0; i < showActors->size(); i++ )
2510   {
2511      pActor = (*showActors)[i];
2512
2513      if ( showActors == &userData->mBrokenActors )
2514      {
2515         NxMat34 pose;
2516         pose.multiply( parentPose, userData->mRelXfm[i] );
2517         pActor->setGlobalPose( pose );
2518
2519         if ( actorDeltas )
2520         {
2521            for ( U32 j=0; j < mMappedActors.size(); j++ )
2522            {
2523               if ( mMappedActors[j] == pActor )
2524               {
2525                  index = mMappedToActorIndex[j];
2526
2527                  // Reset the delta.
2528                  Delta &delta = (*actorDeltas)[index];
2529                  delta.pos = pxCast<Point3F>( pose.t );
2530                  pose.getRowMajor44( tMat );
2531                  delta.rot.set( tMat );
2532                  delta.lastPos = delta.pos;
2533                  delta.lastRot = delta.rot;
2534
2535                  break;
2536               }
2537            }
2538         }
2539      }
2540
2541      pActor->clearActorFlag( NX_AF_DISABLE_COLLISION );
2542      pActor->clearBodyFlag( NX_BF_KINEMATIC );
2543      pActor->setLinearVelocity( parentVel );
2544      pActor->wakeUp();
2545
2546      NxShape *const* pShapeArray = pActor->getShapes();
2547      U32 shapeCount = pActor->getNbShapes();
2548      for ( U32 i = 0; i < shapeCount; i++ )
2549         pShapeArray[i]->setFlag( NX_SF_DISABLE_RAYCASTING, false );
2550
2551      setMeshHidden( _getMeshName(pActor), false );
2552   }
2553}
2554
2555void PxMultiActor::setAllHidden( bool hide )
2556{
2557   for ( U32 i = 0; i < mShapeInstance->mMeshObjects.size(); i++ )
2558      mShapeInstance->setMeshForceHidden( i, hide );
2559}
2560
2561ConsoleMethod( PxMultiActor, setAllHidden, void, 3, 3, "( bool )"
2562               "@brief Hides or unhides all meshes contained in the PxMultiActor.\n\n"
2563               "Hidden meshes will not be rendered.")
2564{
2565   object->setAllHidden( dAtob(argv[2]) );
2566}
2567
2568void PxMultiActor::setMeshHidden( String namePrefix, bool hidden )
2569{
2570   if ( isServerObject() && PHYSICSMGR->isSinglePlayer() )
2571   {
2572      PxMultiActor *clientObj = static_cast<PxMultiActor*>( getClientObject() );
2573      if ( clientObj )
2574         clientObj->setMeshHidden( namePrefix, hidden );
2575   }
2576
2577   for ( U32 i = 0; i < mShapeInstance->mMeshObjects.size(); i++ )
2578   {
2579      String meshName = mShapeInstance->getShape()->getMeshName( i );
2580
2581      if ( meshName.find( namePrefix ) != String::NPos )
2582      {
2583         mShapeInstance->setMeshForceHidden( i, hidden );
2584         return;
2585      }
2586   }
2587
2588   Con::warnf( "PxMultiActor::setMeshHidden - could not find mesh containing substring (%s)", namePrefix.c_str() );
2589}
2590
2591ConsoleMethod( PxMultiActor, setBroken, void, 3, 3, "( bool )"
2592             "@brief Sets the PxMultiActor to a broken or unbroken state.\n\n")
2593{
2594   object->setAllBroken( dAtob( argv[2] ) );
2595}
2596
2597void PxMultiActorData::dumpModel()
2598{
2599   TSShapeInstance *inst = new TSShapeInstance( shape, true );
2600
2601   String path = Platform::getMainDotCsDir();
2602   path += "/model.dump";
2603
2604   FileStream *st;
2605   if((st = FileStream::createAndOpen( path, Torque::FS::File::Write )) != NULL)
2606   {
2607      if ( inst )
2608         inst->dump( *st );
2609      else
2610         Con::errorf( "PxMultiActor::dumpModel, no ShapeInstance." );
2611
2612      delete st;
2613   }
2614   else
2615      Con::errorf( "PxMultiActor::dumpModel, error opening dump file." );
2616}
2617
2618ConsoleMethod( PxMultiActorData, dumpModel, void, 2, 2, 
2619             "@brief Dumps model hierarchy and details to a file.\n\n"
2620             "The file will be created as \'model.dump\' in the game folder. "
2621             "If model.dump already exists, it will be overwritten.\n\n")
2622{
2623   object->dumpModel();
2624}
2625
2626ConsoleMethod( PxMultiActor, setMeshHidden, void, 4, 4, "(string meshName, bool isHidden)"
2627             "@brief Prevents the provided mesh from being rendered.\n\n")
2628{
2629   object->setMeshHidden( argv[2], dAtob( argv[3] ) );
2630}
2631
2632void PxMultiActor::listMeshes( const String &state ) const
2633{
2634   if ( mShapeInstance )
2635      mShapeInstance->listMeshes( state );
2636}
2637
2638ConsoleMethod( PxMultiActor, listMeshes, void, 3, 3, "(enum Hidden/Shown/All)"
2639             "@brief Lists all meshes of the provided type in the console window.\n\n"
2640             "@param All Lists all of the %PxMultiActor's meshes.\n"
2641             "@param Hidden Lists all of the %PxMultiActor's hidden meshes.\n"
2642             "@param Shown Lists all of the %PxMultiActor's visible meshes.\n")
2643{
2644   object->listMeshes( argv[2] );
2645};
2646
2647ConsoleMethod( PxMultiActorData, reload, void, 2, 2, ""
2648              "@brief Reloads all data used for the PxMultiActorData.\n\n"
2649              "If the reload sucessfully completes, all PxMultiActor's will be notified.\n\n")
2650{
2651   object->reload();
2652}
2653