tsStatic.cpp

Engine/source/T3D/tsStatic.cpp

More...

Public Variables

Public Functions

ConsoleDocClass(TSStatic , "@brief A static object derived from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D model <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and placed within the game <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n\n</a>" "<a href="/coding/class/classtsstatic/">TSStatic</a> is the most basic 3D shape in Torque. Unlike <a href="/coding/class/classstaticshape/">StaticShape</a> it doesn'<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> make use of " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> datablock. It derrives directly from SceneObject. This makes <a href="/coding/class/classtsstatic/">TSStatic</a> extremely light " " weight, which is why the Tools use this class when you want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> drop in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> DTS or DAE <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "While <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classtsstatic/">TSStatic</a> doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> provide any motion -- it stays were you initally put it -- it does allow <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single ambient animation sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play when the object is first added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scene.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classtsstatic/">TSStatic</a>(Team1Base) {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " shapeName=\"art/shapes/desertStructures/station01.dts\";\n" "   playAmbient = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   receiveSunLight = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   receiveLMLighting = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useCustomAmbientLighting = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   customAmbientLighting = \"0 0 0 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   collisionType = \"Visible Mesh\";\n" "   decalType = \"Collision Mesh\";\n" "   allowPlayerStep = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   renderNormals = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   forceDetail = \"-1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   position = \"315.18 -180.418 244.313\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   rotation = \"0 0 1 195.952\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   scale = \"1 1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   isRenderEnabled = \"true\";\n" "   canSaveDynamicFields = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
DefineEngineMethod(TSStatic , changeMaterial , void , (const char *mapTo, Material *oldMat, Material *newMat) , ("", nullAsType< Material * >(), nullAsType< Material * >()) , "@brief Change one of the materials on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "This method changes materials per mapTo with others. The material that " "is being replaced is mapped <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> unmapped_mat as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> part of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transition.\n</a>" "@note Warning)
DefineEngineMethod(TSStatic , getModelFile , const char * , () )
DefineEngineMethod(TSStatic , getTargetCount , S32 , () , "Get the number of materials in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@return the number of materials in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@see getTargetName()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(TSStatic , getTargetName , const char * , (S32 index) , (0) , "Get the name of the indexed shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">material.\n</a>" "@param index index of the material <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get (valid range is 0 - getTargetCount()-1).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return the name of the indexed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">material.\n</a>" "@see getTargetCount()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
ImplementEnumType(TSMeshType , "Type of mesh data available in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@ingroup gameObjects" )

Detailed Description

Public Variables

F32 AnimSpeedMax 
 EndImplementEnumType 
bool gEditingMission 

For frame signal.

FRangeValidator percentValidator (0.0f, 1.0f)
FRangeValidator speedValidator (0.0f, AnimSpeedMax)

Public Functions

ConsoleDocClass(TSStatic , "@brief A static object derived from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D model <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and placed within the game <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n\n</a>" "<a href="/coding/class/classtsstatic/">TSStatic</a> is the most basic 3D shape in Torque. Unlike <a href="/coding/class/classstaticshape/">StaticShape</a> it doesn'<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> make use of " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> datablock. It derrives directly from SceneObject. This makes <a href="/coding/class/classtsstatic/">TSStatic</a> extremely light " " weight, which is why the Tools use this class when you want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> drop in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> DTS or DAE <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "While <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classtsstatic/">TSStatic</a> doesn '<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1aded116371789db1fd63c90ef00c95a3d">t</a> provide any motion -- it stays were you initally put it -- it does allow <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single ambient animation sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play when the object is first added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scene.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classtsstatic/">TSStatic</a>(Team1Base) {\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " shapeName=\"art/shapes/desertStructures/station01.dts\";\n" "   playAmbient = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   receiveSunLight = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   receiveLMLighting = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   useCustomAmbientLighting = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   customAmbientLighting = \"0 0 0 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   collisionType = \"Visible Mesh\";\n" "   decalType = \"Collision Mesh\";\n" "   allowPlayerStep = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   renderNormals = \"0\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   forceDetail = \"-1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   position = \"315.18 -180.418 244.313\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   rotation = \"0 0 1 195.952\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   scale = \"1 1 1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "   isRenderEnabled = \"true\";\n" "   canSaveDynamicFields = \"1\";\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )

DefineEngineMethod(TSStatic , changeMaterial , void , (const char *mapTo, Material *oldMat, Material *newMat) , ("", nullAsType< Material * >(), nullAsType< Material * >()) , "@brief Change one of the materials on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "This method changes materials per mapTo with others. The material that " "is being replaced is mapped <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> unmapped_mat as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> part of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transition.\n</a>" "@note Warning)

DefineEngineMethod(TSStatic , getModelFile , const char * , () )

DefineEngineMethod(TSStatic , getTargetCount , S32 , () , "Get the number of materials in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@return the number of materials in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@see getTargetName()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(TSStatic , getTargetName , const char * , (S32 index) , (0) , "Get the name of the indexed shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">material.\n</a>" "@param index index of the material <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get (valid range is 0 - getTargetCount()-1).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return the name of the indexed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">material.\n</a>" "@see getTargetCount()\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

IMPLEMENT_CO_NETOBJECT_V1(TSStatic )

ImplementEnumType(TSMeshType , "Type of mesh data available in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n</a>" "@ingroup gameObjects" )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  28
  29#include "platform/platform.h"
  30#include "T3D/tsStatic.h"
  31
  32#include "core/resourceManager.h"
  33#include "core/stream/bitStream.h"
  34#include "scene/sceneRenderState.h"
  35#include "scene/sceneManager.h"
  36#include "scene/sceneObjectLightingPlugin.h"
  37#include "lighting/lightManager.h"
  38#include "math/mathIO.h"
  39#include "ts/tsShapeInstance.h"
  40#include "ts/tsMaterialList.h"
  41#include "console/consoleTypes.h"
  42#include "T3D/shapeBase.h"
  43#include "sim/netConnection.h"
  44#include "gfx/gfxDevice.h"
  45#include "gfx/gfxTransformSaver.h"
  46#include "ts/tsRenderState.h"
  47#include "collision/boxConvex.h"
  48#include "T3D/physics/physicsPlugin.h"
  49#include "T3D/physics/physicsBody.h"
  50#include "T3D/physics/physicsCollision.h"
  51#include "materials/materialDefinition.h"
  52#include "materials/materialManager.h"
  53#include "materials/matInstance.h"
  54#include "materials/materialFeatureData.h"
  55#include "materials/materialFeatureTypes.h"
  56#include "console/engineAPI.h"
  57#include "T3D/accumulationVolume.h"
  58
  59#include "gui/editor/inspector/group.h"
  60#include "console/typeValidators.h"
  61using namespace Torque;
  62
  63extern bool gEditingMission;
  64#ifdef TORQUE_AFX_ENABLED
  65#include "afx/ce/afxZodiacMgr.h"
  66#endif
  67
  68IMPLEMENT_CO_NETOBJECT_V1(TSStatic);
  69
  70ConsoleDocClass(TSStatic,
  71   "@brief A static object derived from a 3D model file and placed within the game world.\n\n"
  72
  73   "TSStatic is the most basic 3D shape in Torque.  Unlike StaticShape it doesn't make use of "
  74   "a datablock.  It derrives directly from SceneObject.  This makes TSStatic extremely light "
  75   "weight, which is why the Tools use this class when you want to drop in a DTS or DAE object.\n\n"
  76
  77   "While a TSStatic doesn't provide any motion -- it stays were you initally put it -- it does allow for "
  78   "a single ambient animation sequence to play when the object is first added to the scene.\n\n"
  79
  80   "@tsexample\n"
  81   "new TSStatic(Team1Base) {\n"
  82   "   shapeName = \"art/shapes/desertStructures/station01.dts\";\n"
  83   "   playAmbient = \"1\";\n"
  84   "   receiveSunLight = \"1\";\n"
  85   "   receiveLMLighting = \"1\";\n"
  86   "   useCustomAmbientLighting = \"0\";\n"
  87   "   customAmbientLighting = \"0 0 0 1\";\n"
  88   "   collisionType = \"Visible Mesh\";\n"
  89   "   decalType = \"Collision Mesh\";\n"
  90   "   allowPlayerStep = \"1\";\n"
  91   "   renderNormals = \"0\";\n"
  92   "   forceDetail = \"-1\";\n"
  93   "   position = \"315.18 -180.418 244.313\";\n"
  94   "   rotation = \"0 0 1 195.952\";\n"
  95   "   scale = \"1 1 1\";\n"
  96   "   isRenderEnabled = \"true\";\n"
  97   "   canSaveDynamicFields = \"1\";\n"
  98   "};\n"
  99   "@endtsexample\n"
 100
 101   "@ingroup gameObjects\n"
 102);
 103
 104bool TSStatic::smUseStaticObjectFade = false;
 105F32 TSStatic::smStaticObjectFadeStart = 50;
 106F32 TSStatic::smStaticObjectFadeEnd = 75;
 107F32 TSStatic::smStaticObjectUnfadeableSize = 75;
 108
 109TSStatic::TSStatic()
 110   :
 111   cubeDescId(0),
 112   reflectorDesc(NULL)
 113{
 114   mNetFlags.set(Ghostable | ScopeAlways);
 115
 116   mTypeMask |= StaticObjectType | StaticShapeObjectType;
 117
 118   mShapeName = "";
 119   mShapeInstance = NULL;
 120
 121   mPlayAmbient = true;
 122   mAmbientThread = NULL;
 123
 124   mAllowPlayerStep = false;
 125
 126   mConvexList = new Convex;
 127
 128   mRenderNormalScalar = 0;
 129   mForceDetail = -1;
 130
 131   mMeshCulling = false;
 132   mUseOriginSort = false;
 133
 134   mUseAlphaFade = false;
 135   mAlphaFadeStart = 100.0f;
 136   mAlphaFadeEnd = 150.0f;
 137   mInvertAlphaFade = false;
 138   mAlphaFade = 1.0f;
 139   mPhysicsRep = NULL;
 140
 141   mCollisionType = CollisionMesh;
 142   mDecalType = CollisionMesh;
 143
 144   mIgnoreZodiacs = false;
 145   mHasGradients = false;
 146   mInvertGradientRange = false;
 147   mGradientRangeUser.set(0.0f, 180.0f);
 148#ifdef TORQUE_AFX_ENABLED
 149   afxZodiacData::convertGradientRangeFromDegrees(mGradientRange, mGradientRangeUser);
 150#endif
 151   mAnimOffset = 0.0f;
 152   mAnimSpeed = 1.0f;
 153
 154   mShapeAsset = StringTable->EmptyString();
 155   mShapeAssetId = StringTable->EmptyString();
 156}
 157
 158TSStatic::~TSStatic()
 159{
 160   delete mConvexList;
 161   mConvexList = NULL;
 162}
 163
 164ImplementEnumType(TSMeshType,
 165   "Type of mesh data available in a shape.\n"
 166   "@ingroup gameObjects")
 167{
 168   TSStatic::None, "None", "No mesh data."
 169},
 170   { TSStatic::Bounds,        "Bounds",         "Bounding box of the shape." },
 171   { TSStatic::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." },
 172   { TSStatic::VisibleMesh,   "Visible Mesh",   "Rendered mesh polygons." },
 173      EndImplementEnumType;
 174
 175FRangeValidator percentValidator(0.0f, 1.0f);
 176F32 AnimSpeedMax = 4.0f;
 177FRangeValidator speedValidator(0.0f, AnimSpeedMax);
 178
 179void TSStatic::initPersistFields()
 180{
 181   addFieldV("AnimOffset", TypeF32, Offset(mAnimOffset, TSStatic), &percentValidator,
 182      "Percent Animation Offset.");
 183
 184   addFieldV("AnimSpeed", TypeF32, Offset(mAnimSpeed, TSStatic), &speedValidator,
 185      "Percent Animation Speed.");
 186   addGroup("Shape");
 187
 188   addProtectedField("shapeAsset", TypeShapeAssetId, Offset(mShapeAssetId, TSStatic),
 189      &TSStatic::_setShapeAsset, &defaultProtectedGetFn,
 190      "The source shape asset.");
 191
 192   addProtectedField("shapeName", TypeShapeFilename, Offset(mShapeName, TSStatic),
 193      &TSStatic::_setShapeName, &defaultProtectedGetFn,
 194      "%Path and filename of the model file (.DTS, .DAE) to use for this TSStatic. Legacy field. Any loose files assigned here will attempt to be auto-imported in as an asset.");
 195
 196   endGroup("Shape");
 197
 198   addGroup("Materials");
 199   addProtectedField("skin", TypeRealString, Offset(mAppliedSkinName, TSStatic), &_setFieldSkin, &_getFieldSkin,
 200      "@brief The skin applied to the shape.\n\n"
 201
 202      "'Skinning' the shape effectively renames the material targets, allowing "
 203      "different materials to be used on different instances of the same model.\n\n"
 204
 205      "Any material targets that start with the old skin name have that part "
 206      "of the name replaced with the new skin name. The initial old skin name is "
 207      "\"base\". For example, if a new skin of \"blue\" was applied to a model "
 208      "that had material targets <i>base_body</i> and <i>face</i>, the new targets "
 209      "would be <i>blue_body</i> and <i>face</i>. Note that <i>face</i> was not "
 210      "renamed since it did not start with the old skin name of \"base\".\n\n"
 211
 212      "To support models that do not use the default \"base\" naming convention, "
 213      "you can also specify the part of the name to replace in the skin field "
 214      "itself. For example, if a model had a material target called <i>shapemat</i>, "
 215      "we could apply a new skin \"shape=blue\", and the material target would be "
 216      "renamed to <i>bluemat</i> (note \"shape\" has been replaced with \"blue\").\n\n"
 217
 218      "Multiple skin updates can also be applied at the same time by separating "
 219      "them with a semicolon. For example: \"base=blue;face=happy_face\".\n\n"
 220
 221      "Material targets are only renamed if an existing Material maps to that "
 222      "name, or if there is a diffuse texture in the model folder with the same "
 223      "name as the new target.\n\n");
 224   endGroup("Materials");
 225
 226   addGroup("Rendering");
 227
 228   addField("playAmbient", TypeBool, Offset(mPlayAmbient, TSStatic),
 229      "Enables automatic playing of the animation sequence named \"ambient\" (if it exists) when the TSStatic is loaded.");
 230   addField("meshCulling", TypeBool, Offset(mMeshCulling, TSStatic),
 231      "Enables detailed culling of meshes within the TSStatic. Should only be used "
 232      "with large complex shapes like buildings which contain many submeshes.");
 233   addField("originSort", TypeBool, Offset(mUseOriginSort, TSStatic),
 234      "Enables translucent sorting of the TSStatic by its origin instead of the bounds.");
 235
 236   endGroup("Rendering");
 237
 238   addGroup("Reflection");
 239   addField("cubeReflectorDesc", TypeRealString, Offset(cubeDescName, TSStatic),
 240      "References a ReflectorDesc datablock that defines performance and quality properties for dynamic reflections.\n");
 241   endGroup("Reflection");
 242
 243   addGroup("Collision");
 244
 245   addField("collisionType", TypeTSMeshType, Offset(mCollisionType, TSStatic),
 246      "The type of mesh data to use for collision queries.");
 247   addField("decalType", TypeTSMeshType, Offset(mDecalType, TSStatic),
 248      "The type of mesh data used to clip decal polygons against.");
 249   addField("allowPlayerStep", TypeBool, Offset(mAllowPlayerStep, TSStatic),
 250      "@brief Allow a Player to walk up sloping polygons in the TSStatic (based on the collisionType).\n\n"
 251      "When set to false, the slightest bump will stop the player from walking on top of the object.\n");
 252
 253   endGroup("Collision");
 254
 255   addGroup("AlphaFade");
 256   addField("alphaFadeEnable", TypeBool, Offset(mUseAlphaFade, TSStatic), "Turn on/off Alpha Fade");
 257   addField("alphaFadeStart", TypeF32, Offset(mAlphaFadeStart, TSStatic), "Distance of start Alpha Fade");
 258   addField("alphaFadeEnd", TypeF32, Offset(mAlphaFadeEnd, TSStatic), "Distance of end Alpha Fade");
 259   addField("alphaFadeInverse", TypeBool, Offset(mInvertAlphaFade, TSStatic), "Invert Alpha Fade's Start & End Distance");
 260   endGroup("AlphaFade");
 261
 262   addGroup("Debug");
 263
 264   addField("renderNormals", TypeF32, Offset(mRenderNormalScalar, TSStatic),
 265      "Debug rendering mode shows the normals for each point in the TSStatic's mesh.");
 266   addField("forceDetail", TypeS32, Offset(mForceDetail, TSStatic),
 267      "Forces rendering to a particular detail level.");
 268
 269   endGroup("Debug");
 270
 271   addGroup("AFX");
 272   addField("ignoreZodiacs", TypeBool, Offset(mIgnoreZodiacs, TSStatic));
 273   addField("useGradientRange", TypeBool, Offset(mHasGradients, TSStatic));
 274   addField("gradientRange", TypePoint2F, Offset(mGradientRangeUser, TSStatic));
 275   addField("invertGradientRange", TypeBool, Offset(mInvertGradientRange, TSStatic));
 276   endGroup("AFX");
 277   Parent::initPersistFields();
 278}
 279
 280void TSStatic::consoleInit()
 281{
 282   Parent::consoleInit();
 283
 284   // Vars for debug rendering while the RoadEditor is open, only used if smEditorOpen is true.
 285   Con::addVariable("$pref::useStaticObjectFade", TypeBool, &TSStatic::smUseStaticObjectFade, "Indicates if all statics should utilize the distance-based object fadeout logic.\n");
 286   Con::addVariable("$pref::staticObjectFadeStart", TypeF32, &TSStatic::smStaticObjectFadeStart, "Distance at which static object fading begins if $pref::useStaticObjectFade is on.\n");
 287   Con::addVariable("$pref::staticObjectFadeEnd", TypeF32, &TSStatic::smStaticObjectFadeEnd, "Distance at which static object fading should have fully faded if $pref::useStaticObjectFade is on.\n");
 288   Con::addVariable("$pref::staticObjectUnfadeableSize", TypeF32, &TSStatic::smStaticObjectUnfadeableSize, "Size of object where if the bounds is at or bigger than this, it will be ignored in the $pref::useStaticObjectFade logic. Useful for very large, distance-important objects.\n");
 289}
 290
 291bool TSStatic::_setShapeAsset(void* obj, const char* index, const char* data)
 292{
 293   TSStatic* ts = static_cast<TSStatic*>(obj);// ->setFile(FileName(data));
 294
 295   ts->mShapeAssetId = StringTable->insert(data);
 296
 297   return ts->setShapeAsset(ts->mShapeAssetId);
 298}
 299
 300bool TSStatic::_setShapeName(void* obj, const char* index, const char* data)
 301{
 302   TSStatic* ts = static_cast<TSStatic*>(obj);// ->setFile(FileName(data));
 303
 304   StringTableEntry assetId = ShapeAsset::getAssetIdByFilename(StringTable->insert(data));
 305   if (assetId != StringTable->EmptyString())
 306   {
 307      //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
 308      //the TSStatic
 309      if (ts->setShapeAsset(assetId))
 310      {
 311         if (assetId == StringTable->insert("Core_Rendering:noShape"))
 312         {
 313            ts->mShapeName = data;
 314            ts->mShapeAssetId = StringTable->EmptyString();
 315
 316            return true;
 317         }
 318         else
 319         {
 320            ts->mShapeAssetId = assetId;
 321            ts->mShapeName = StringTable->EmptyString();
 322
 323            return false;
 324         }
 325      }
 326    }
 327   else
 328   {
 329      ts->mShapeAsset = StringTable->EmptyString();
 330   }
 331
 332   return true;
 333}
 334
 335bool TSStatic::_setFieldSkin(void* object, const char* index, const char* data)
 336{
 337   TSStatic* ts = static_cast<TSStatic*>(object);
 338   if (ts)
 339      ts->setSkinName(data);
 340   return false;
 341}
 342
 343const char* TSStatic::_getFieldSkin(void* object, const char* data)
 344{
 345   TSStatic* ts = static_cast<TSStatic*>(object);
 346   return ts ? ts->mSkinNameHandle.getString() : "";
 347}
 348
 349void TSStatic::inspectPostApply()
 350{
 351   // Apply any transformations set in the editor
 352   Parent::inspectPostApply();
 353
 354   if (isServerObject())
 355   {
 356      setMaskBits(-1);
 357      prepCollision();
 358   }
 359
 360   _updateShouldTick();
 361}
 362
 363bool TSStatic::onAdd()
 364{
 365   PROFILE_SCOPE(TSStatic_onAdd);
 366
 367   if (isServerObject())
 368   {
 369      // Handle the old "usePolysoup" field
 370      SimFieldDictionary* fieldDict = getFieldDictionary();
 371
 372      if (fieldDict)
 373      {
 374         StringTableEntry slotName = StringTable->insert("usePolysoup");
 375
 376         SimFieldDictionary::Entry* entry = fieldDict->findDynamicField(slotName);
 377
 378         if (entry)
 379         {
 380            // Was "usePolysoup" set?
 381            bool usePolysoup = dAtob(entry->value);
 382
 383            // "usePolysoup" maps to the new VisibleMesh type
 384            if (usePolysoup)
 385               mCollisionType = VisibleMesh;
 386
 387            // Remove the field in favor on the new "collisionType" field
 388            fieldDict->setFieldValue(slotName, "");
 389         }
 390      }
 391   }
 392
 393   if (!Parent::onAdd())
 394      return false;
 395
 396   // Setup the shape.
 397   if (!_createShape())
 398   {
 399      Con::errorf("TSStatic::onAdd() - Shape creation failed!");
 400      return false;
 401   }
 402
 403   setRenderTransform(mObjToWorld);
 404
 405   // Register for the resource change signal.
 406   //ResourceManager::get().getChangedSignal().notify(this, &TSStatic::_onResourceChanged);
 407
 408   addToScene();
 409
 410   if (isClientObject())
 411   {
 412      mCubeReflector.unregisterReflector();
 413
 414      if (reflectorDesc)
 415         mCubeReflector.registerReflector(this, reflectorDesc);
 416   }
 417
 418   _updateShouldTick();
 419
 420   // Accumulation and environment mapping
 421   if (isClientObject() && mShapeInstance)
 422   {
 423      AccumulationVolume::addObject(this);
 424   }
 425
 426   return true;
 427}
 428
 429bool TSStatic::setShapeAsset(const StringTableEntry shapeAssetId)
 430{
 431   if (!mShapeAsset.isNull())
 432   {
 433      mShapeAsset->getChangedSignal().remove(this, &TSStatic::_onAssetChanged);
 434   }
 435
 436   if (ShapeAsset::getAssetById(shapeAssetId, &mShapeAsset))
 437   {
 438      //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
 439      //the TSStatic
 440      if (mShapeAsset.getAssetId() != StringTable->insert("Core_Rendering:noshape"))
 441      {
 442         mShapeName = StringTable->EmptyString();
 443
 444         mShapeAsset->getChangedSignal().notify(this, &TSStatic::_onAssetChanged);
 445      }
 446
 447      _createShape();
 448
 449      setMaskBits(-1);
 450
 451      return true;
 452   }
 453
 454   return false;
 455}
 456
 457bool TSStatic::_createShape()
 458{
 459   // Cleanup before we create.
 460   mCollisionDetails.clear();
 461   mDecalDetails.clear();
 462   mDecalDetailsPtr = 0;
 463   mLOSDetails.clear();
 464   SAFE_DELETE(mPhysicsRep);
 465   SAFE_DELETE(mShapeInstance);
 466   mAmbientThread = NULL;
 467   mShape = NULL;
 468
 469   if(!mShapeAsset.isNull())
 470   {
 471      //Special-case handling, usually because we set noShape
 472      mShape = mShapeAsset->getShapeResource();
 473   }
 474
 475   if (!mShape)
 476   {
 477      Con::errorf("TSStatic::_createShape() - Shape Asset %s had no valid shape!", mShapeAsset.getAssetId());
 478      return false;
 479   }
 480
 481   if (isClientObject() &&
 482      !mShape->preloadMaterialList(mShape.getPath()) &&
 483      NetConnection::filesWereDownloaded())
 484      return false;
 485
 486   mObjBox = mShape->mBounds;
 487   resetWorldBox();
 488
 489   mShapeInstance = new TSShapeInstance(mShape, isClientObject());
 490   if (isClientObject())
 491      mShapeInstance->cloneMaterialList();
 492
 493   if (isGhost())
 494   {
 495      // Reapply the current skin
 496      mAppliedSkinName = "";
 497      reSkin();
 498   }
 499
 500   prepCollision();
 501
 502   // Find the "ambient" animation if it exists
 503   S32 ambientSeq = mShape->findSequence("ambient");
 504
 505   if (ambientSeq > -1 && !mAmbientThread)
 506      mAmbientThread = mShapeInstance->addThread();
 507
 508   if ( mAmbientThread )
 509      mShapeInstance->setSequence(mAmbientThread, ambientSeq, mAnimOffset);
 510
 511   // Resolve CubeReflectorDesc.
 512   if (cubeDescName.isNotEmpty())
 513   {
 514      Sim::findObject(cubeDescName, reflectorDesc);
 515   }
 516   else if (cubeDescId > 0)
 517   {
 518      Sim::findObject(cubeDescId, reflectorDesc);
 519   }
 520
 521   //Set up the material slot vars for easy manipulation
 522   /*S32 materialCount = mShape->materialList->getMaterialNameList().size(); //mMeshAsset->getMaterialCount();
 523
 524   //Temporarily disabled until fixup of materialName->assetId lookup logic is sorted for easy persistance
 525   if (isServerObject())
 526   {
 527      char matFieldName[128];
 528
 529      for (U32 i = 0; i < materialCount; i++)
 530      {
 531         StringTableEntry materialname = StringTable->insert(mShape->materialList->getMaterialName(i).c_str());
 532
 533         dSprintf(matFieldName, 128, "MaterialSlot%d", i);
 534         StringTableEntry matFld = StringTable->insert(matFieldName);
 535
 536         setDataField(matFld, NULL, materialname);
 537      }
 538   }*/
 539
 540   return true;
 541}
 542
 543void TSStatic::onDynamicModified(const char* slotName, const char* newValue)
 544{
 545   if (FindMatch::isMatch("materialslot*", slotName, false))
 546   {
 547      if (!getShape())
 548         return;
 549
 550      S32 slot = -1;
 551      String outStr(String::GetTrailingNumber(slotName, slot));
 552
 553      if (slot == -1)
 554         return;
 555
 556      //Safe to assume the inbound value for the material will be a MaterialAsset, so lets do a lookup on the name
 557      MaterialAsset* matAsset = AssetDatabase.acquireAsset<MaterialAsset>(newValue);
 558      if (!matAsset)
 559         return;
 560
 561      bool found = false;
 562      for (U32 i = 0; i < mChangingMaterials.size(); i++)
 563      {
 564         if (mChangingMaterials[i].slot == slot)
 565         {
 566            mChangingMaterials[i].matAsset = matAsset;
 567            mChangingMaterials[i].assetId = newValue;
 568            found = true;
 569         }
 570      }
 571
 572      if (!found)
 573      {
 574         matMap newMatMap;
 575         newMatMap.slot = slot;
 576         newMatMap.matAsset = matAsset;
 577         newMatMap.assetId = newValue;
 578
 579         mChangingMaterials.push_back(newMatMap);
 580      }
 581
 582      setMaskBits(MaterialMask);
 583   }
 584
 585   Parent::onDynamicModified(slotName, newValue);
 586}
 587
 588void TSStatic::prepCollision()
 589{
 590   // Let the client know that the collision was updated
 591   setMaskBits(UpdateCollisionMask);
 592
 593   // Allow the ShapeInstance to prep its collision if it hasn't already
 594   if (mShapeInstance)
 595      mShapeInstance->prepCollision();
 596
 597   // Cleanup any old collision data
 598   mCollisionDetails.clear();
 599   mDecalDetails.clear();
 600   mDecalDetailsPtr = 0;
 601   mLOSDetails.clear();
 602   mConvexList->nukeList();
 603
 604   if (mCollisionType == CollisionMesh || mCollisionType == VisibleMesh)
 605   {
 606      mShape->findColDetails(mCollisionType == VisibleMesh, &mCollisionDetails, &mLOSDetails);
 607      if (mDecalType == mCollisionType)
 608      {
 609         mDecalDetailsPtr = &mCollisionDetails;
 610      }
 611      else if (mDecalType == CollisionMesh || mDecalType == VisibleMesh)
 612      {
 613         mShape->findColDetails(mDecalType == VisibleMesh, &mDecalDetails, 0);
 614         mDecalDetailsPtr = &mDecalDetails;
 615      }
 616   }
 617   else if (mDecalType == CollisionMesh || mDecalType == VisibleMesh)
 618   {
 619      mShape->findColDetails(mDecalType == VisibleMesh, &mDecalDetails, 0);
 620      mDecalDetailsPtr = &mDecalDetails;
 621   }
 622
 623   _updatePhysics();
 624}
 625
 626void TSStatic::_updatePhysics()
 627{
 628   SAFE_DELETE(mPhysicsRep);
 629
 630   if (!PHYSICSMGR || mCollisionType == None)
 631      return;
 632
 633   PhysicsCollision* colShape = NULL;
 634   if (mCollisionType == Bounds)
 635   {
 636      MatrixF offset(true);
 637      offset.setPosition(mShape->center);
 638      colShape = PHYSICSMGR->createCollision();
 639      colShape->addBox(getObjBox().getExtents() * 0.5f * mObjScale, offset);
 640   }
 641   else
 642      colShape = mShape->buildColShape(mCollisionType == VisibleMesh, getScale());
 643
 644   if (colShape)
 645   {
 646      PhysicsWorld* world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client");
 647      mPhysicsRep = PHYSICSMGR->createBody();
 648      mPhysicsRep->init(colShape, 0, 0, this, world);
 649      mPhysicsRep->setTransform(getTransform());
 650   }
 651}
 652
 653void TSStatic::onRemove()
 654{
 655   SAFE_DELETE(mPhysicsRep);
 656
 657   // Accumulation
 658   if (isClientObject() && mShapeInstance)
 659   {
 660      if (mShapeInstance->hasAccumulation())
 661         AccumulationVolume::removeObject(this);
 662   }
 663
 664   mConvexList->nukeList();
 665
 666   removeFromScene();
 667
 668   // Remove the resource change signal.
 669   //ResourceManager::get().getChangedSignal().remove(this, &TSStatic::_onResourceChanged);
 670
 671   delete mShapeInstance;
 672   mShapeInstance = NULL;
 673
 674   mAmbientThread = NULL;
 675   if (isClientObject())
 676      mCubeReflector.unregisterReflector();
 677
 678   if(!mShapeAsset.isNull())
 679      mShapeAsset->getChangedSignal().remove(this, &TSStatic::_onAssetChanged);
 680
 681   Parent::onRemove();
 682}
 683
 684void TSStatic::_onResourceChanged(const Torque::Path& path)
 685{
 686   if (path != Path(mShapeName))
 687      return;
 688
 689   _createShape();
 690   _updateShouldTick();
 691}
 692
 693void TSStatic::_onAssetChanged()
 694{
 695   _createShape();
 696   _updateShouldTick();
 697}
 698
 699void TSStatic::setSkinName(const char* name)
 700{
 701   if (!isGhost())
 702   {
 703      if (name[0] != '\0')
 704      {
 705         // Use tags for better network performance
 706         // Should be a tag, but we'll convert to one if it isn't.
 707         if (name[0] == StringTagPrefixByte)
 708            mSkinNameHandle = NetStringHandle(U32(dAtoi(name + 1)));
 709         else
 710            mSkinNameHandle = NetStringHandle(name);
 711      }
 712      else
 713         mSkinNameHandle = NetStringHandle();
 714
 715      setMaskBits(SkinMask);
 716   }
 717}
 718
 719void TSStatic::reSkin()
 720{
 721   if (isGhost() && mShapeInstance)
 722   {
 723      if (mSkinNameHandle.isValidString())
 724      {
 725         mShapeInstance->resetMaterialList();
 726         Vector<String> skins;
 727         String(mSkinNameHandle.getString()).split(";", skins);
 728
 729         for (S32 i = 0; i < skins.size(); i++)
 730         {
 731            String oldSkin(mAppliedSkinName.c_str());
 732            String newSkin(skins[i]);
 733
 734            // Check if the skin handle contains an explicit "old" base string. This
 735            // allows all models to support skinning, even if they don't follow the 
 736            // "base_xxx" material naming convention.
 737            S32 split = newSkin.find('=');    // "old=new" format skin?
 738            if (split != String::NPos)
 739            {
 740               oldSkin = newSkin.substr(0, split);
 741               newSkin = newSkin.erase(0, split + 1);
 742            }
 743            else
 744            {
 745               oldSkin = "";
 746            }
 747            mShapeInstance->reSkin(newSkin, oldSkin);
 748            mAppliedSkinName = newSkin;
 749         }
 750      }
 751      else
 752      {
 753         mShapeInstance->reSkin("", mAppliedSkinName);
 754         mAppliedSkinName = "";
 755      }
 756   }
 757}
 758
 759void TSStatic::processTick(const Move* move)
 760{
 761   if ( isServerObject() && mPlayAmbient && mAmbientThread )
 762   {
 763      mShapeInstance->setTimeScale(mAmbientThread, mAnimSpeed);
 764      mShapeInstance->advanceTime( TickSec, mAmbientThread );
 765   }
 766   if (isMounted())
 767   {
 768      MatrixF mat(true);
 769      mMount.object->getMountTransform(mMount.node, mMount.xfm, &mat);
 770      setTransform(mat);
 771   }
 772}
 773
 774void TSStatic::interpolateTick(F32 delta)
 775{
 776}
 777
 778void TSStatic::advanceTime(F32 dt)
 779{
 780   if ( mPlayAmbient && mAmbientThread )
 781   {
 782      mShapeInstance->setTimeScale(mAmbientThread, mAnimSpeed);
 783      mShapeInstance->advanceTime( dt, mAmbientThread );
 784   }
 785
 786   if (isMounted())
 787   {
 788      MatrixF mat(true);
 789      mMount.object->getRenderMountTransform(dt, mMount.node, mMount.xfm, &mat);
 790      setRenderTransform(mat);
 791   }
 792}
 793
 794void TSStatic::_updateShouldTick()
 795{
 796   bool shouldTick = (mPlayAmbient && mAmbientThread) || isMounted();
 797
 798   if (isTicking() != shouldTick)
 799      setProcessTick(shouldTick);
 800}
 801
 802void TSStatic::prepRenderImage(SceneRenderState* state)
 803{
 804   if (!mShapeInstance)
 805      return;
 806
 807   Point3F cameraOffset;
 808   getRenderTransform().getColumn(3, &cameraOffset);
 809   cameraOffset -= state->getDiffuseCameraPosition();
 810   F32 dist = cameraOffset.len();
 811   if (dist < 0.01f)
 812      dist = 0.01f;
 813
 814   if (mUseAlphaFade)
 815   {
 816      mAlphaFade = 1.0f;
 817      if ((mAlphaFadeStart < mAlphaFadeEnd) && mAlphaFadeStart > 0.1f)
 818      {
 819         if (mInvertAlphaFade)
 820         {
 821            if (dist <= mAlphaFadeStart)
 822            {
 823               return;
 824            }
 825            if (dist < mAlphaFadeEnd)
 826            {
 827               mAlphaFade = ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart));
 828            }
 829         }
 830         else
 831         {
 832            if (dist >= mAlphaFadeEnd)
 833            {
 834               return;
 835            }
 836            if (dist > mAlphaFadeStart)
 837            {
 838               mAlphaFade -= ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart));
 839            }
 840         }
 841      }
 842   }
 843   else if (smUseStaticObjectFade)
 844   {
 845      F32 boundsLen = getWorldSphere().radius;
 846
 847      if (boundsLen < smStaticObjectUnfadeableSize)
 848      {
 849         F32 distAdjust = (boundsLen) / (smStaticObjectUnfadeableSize);
 850         distAdjust = 1 - distAdjust;
 851
 852         dist *= distAdjust;
 853
 854         mAlphaFade = 1.0f;
 855         if ((smStaticObjectFadeStart < smStaticObjectFadeEnd) && smStaticObjectFadeStart > 0.1f)
 856         {
 857            if (dist >= smStaticObjectFadeEnd)
 858            {
 859               return;
 860            }
 861            if (dist > smStaticObjectFadeStart)
 862            {
 863               mAlphaFade -= ((dist - smStaticObjectFadeStart) / (smStaticObjectFadeEnd - smStaticObjectFadeStart));
 864            }
 865         }
 866      }
 867   }
 868
 869   F32 invScale = (1.0f / getMax(getMax(mObjScale.x, mObjScale.y), mObjScale.z));
 870
 871   // If we're currently rendering our own reflection we
 872   // don't want to render ourselves into it.
 873   if (mCubeReflector.isRendering())
 874      return;
 875
 876
 877   if (mForceDetail == -1)
 878      mShapeInstance->setDetailFromDistance(state, dist * invScale);
 879   else
 880      mShapeInstance->setCurrentDetail(mForceDetail);
 881
 882   if (mShapeInstance->getCurrentDetail() < 0)
 883      return;
 884
 885   GFXTransformSaver saver;
 886
 887   // Set up our TS render state.
 888   TSRenderState rdata;
 889   rdata.setSceneState(state);
 890   rdata.setFadeOverride(1.0f);
 891   rdata.setOriginSort(mUseOriginSort);
 892
 893   if (mCubeReflector.isEnabled())
 894      rdata.setCubemap(mCubeReflector.getCubemap());
 895
 896   // Acculumation
 897   rdata.setAccuTex(mAccuTex);
 898
 899   // If we have submesh culling enabled then prepare
 900   // the object space frustum to pass to the shape.
 901   Frustum culler;
 902   if (mMeshCulling)
 903   {
 904      culler = state->getCullingFrustum();
 905      MatrixF xfm(true);
 906      xfm.scale(Point3F::One / getScale());
 907      xfm.mul(getRenderWorldTransform());
 908      xfm.mul(culler.getTransform());
 909      culler.setTransform(xfm);
 910      rdata.setCuller(&culler);
 911   }
 912
 913   // We might have some forward lit materials
 914   // so pass down a query to gather lights.
 915   LightQuery query;
 916   query.init(getWorldSphere());
 917   rdata.setLightQuery(&query);
 918
 919   MatrixF mat = getRenderTransform();
 920   mat.scale(mObjScale);
 921   GFX->setWorldMatrix(mat);
 922
 923   if (state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery())
 924   {
 925      RenderPassManager* pass = state->getRenderPass();
 926      OccluderRenderInst* ri = pass->allocInst<OccluderRenderInst>();
 927
 928      ri->type = RenderPassManager::RIT_Occluder;
 929      ri->query = mCubeReflector.getOcclusionQuery();
 930      mObjToWorld.mulP(mObjBox.getCenter(), &ri->position);
 931      ri->scale.set(mObjBox.getExtents());
 932      ri->orientation = pass->allocUniqueXform(mObjToWorld);
 933      ri->isSphere = false;
 934      state->getRenderPass()->addInst(ri);
 935   }
 936
 937   if (mShapeInstance)
 938   {
 939      mShapeInstance->animate();
 940
 941      if (mUseAlphaFade || smUseStaticObjectFade)
 942      {
 943         mShapeInstance->setAlphaAlways(mAlphaFade);
 944         S32 s = mShapeInstance->mMeshObjects.size();
 945
 946         for (S32 x = 0; x < s; x++)
 947         {
 948            mShapeInstance->mMeshObjects[x].visible = mAlphaFade;
 949         }
 950      }
 951   }
 952   mShapeInstance->render(rdata);
 953#ifdef TORQUE_AFX_ENABLED
 954   if (!mIgnoreZodiacs && mDecalDetailsPtr != 0)
 955      afxZodiacMgr::renderPolysoupZodiacs(state, this);
 956#endif
 957   if (mRenderNormalScalar > 0)
 958   {
 959      ObjectRenderInst* ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
 960      ri->renderDelegate.bind(this, &TSStatic::_renderNormals);
 961      ri->type = RenderPassManager::RIT_Editor;
 962      state->getRenderPass()->addInst(ri);
 963   }
 964}
 965
 966void TSStatic::_renderNormals(ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat)
 967{
 968   PROFILE_SCOPE(TSStatic_RenderNormals);
 969
 970   GFXTransformSaver saver;
 971
 972   MatrixF mat = getRenderTransform();
 973   mat.scale(mObjScale);
 974   GFX->multWorld(mat);
 975
 976   S32 dl = mShapeInstance->getCurrentDetail();
 977   mShapeInstance->renderDebugNormals(mRenderNormalScalar, dl);
 978}
 979
 980void TSStatic::onScaleChanged()
 981{
 982   Parent::onScaleChanged();
 983
 984   if (mPhysicsRep)
 985   {
 986      // If the editor is enabled delay the scale operation
 987      // by a few milliseconds so that we're not rebuilding
 988      // during an active scale drag operation.
 989      if (gEditingMission)
 990         mPhysicsRep->queueCallback(500, Delegate<void()>(this, &TSStatic::_updatePhysics));
 991      else
 992         _updatePhysics();
 993   }
 994
 995   setMaskBits(ScaleMask);
 996}
 997
 998void TSStatic::setTransform(const MatrixF& mat)
 999{
1000   Parent::setTransform(mat);
1001   if (!isMounted())
1002      setMaskBits(TransformMask);
1003
1004   if (mPhysicsRep)
1005      mPhysicsRep->setTransform(mat);
1006
1007   // Accumulation
1008   if (isClientObject() && mShapeInstance)
1009   {
1010      if (mShapeInstance->hasAccumulation())
1011         AccumulationVolume::updateObject(this);
1012   }
1013
1014   // Since this is a static it's render transform changes 1
1015   // to 1 with it's collision transform... no interpolation.
1016   setRenderTransform(mat);
1017}
1018
1019U32 TSStatic::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
1020{
1021   U32 retMask = Parent::packUpdate(con, mask, stream);
1022
1023   if (stream->writeFlag(mask & TransformMask))
1024      mathWrite(*stream, getTransform());
1025
1026   if (stream->writeFlag(mask & ScaleMask))
1027   {
1028      // Only write one bit if the scale is one.
1029      if (stream->writeFlag(mObjScale != Point3F::One))
1030         mathWrite(*stream, mObjScale);
1031   }
1032
1033   if (stream->writeFlag(mask & UpdateCollisionMask))
1034      stream->write((U32)mCollisionType);
1035
1036   if (stream->writeFlag(mask & SkinMask))
1037      con->packNetStringHandleU(stream, mSkinNameHandle);
1038
1039   if (stream->writeFlag(mask & AdvancedStaticOptionsMask))
1040   {
1041      stream->writeString(mShapeAsset.getAssetId());
1042      stream->writeString(mShapeName);
1043
1044      stream->write((U32)mDecalType);
1045
1046      stream->writeFlag(mAllowPlayerStep);
1047      stream->writeFlag(mMeshCulling);
1048      stream->writeFlag(mUseOriginSort);
1049
1050      stream->write(mRenderNormalScalar);
1051
1052      stream->write(mForceDetail);
1053
1054   if (stream->writeFlag(mAnimOffset != 0.0f))
1055      stream->writeFloat(mAnimOffset, 7);
1056
1057   if (stream->writeFlag(mAnimSpeed != 1.0f))
1058      stream->writeSignedFloat(mAnimSpeed / AnimSpeedMax, 7);
1059
1060      stream->writeFlag(mPlayAmbient);
1061   }
1062
1063   if (stream->writeFlag(mUseAlphaFade))
1064   {
1065      stream->write(mAlphaFadeStart);
1066      stream->write(mAlphaFadeEnd);
1067      stream->write(mInvertAlphaFade);
1068   }
1069
1070   stream->writeFlag(mIgnoreZodiacs);
1071   if (stream->writeFlag(mHasGradients))
1072   {
1073      stream->writeFlag(mInvertGradientRange);
1074      stream->write(mGradientRange.x);
1075      stream->write(mGradientRange.y);
1076   }
1077   if (mLightPlugin)
1078      retMask |= mLightPlugin->packUpdate(this, AdvancedStaticOptionsMask, con, mask, stream);
1079
1080   if (stream->writeFlag(reflectorDesc != NULL))
1081   {
1082      stream->writeRangedU32(reflectorDesc->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
1083   }
1084
1085   stream->write(mOverrideColor);
1086
1087   if (stream->writeFlag(mask & MaterialMask))
1088   {
1089      stream->writeInt(mChangingMaterials.size(), 16);
1090
1091      for (U32 i = 0; i < mChangingMaterials.size(); i++)
1092      {
1093         stream->writeInt(mChangingMaterials[i].slot, 16);
1094
1095         NetStringHandle matNameStr = mChangingMaterials[i].assetId.c_str();
1096         con->packNetStringHandleU(stream, matNameStr);
1097      }
1098
1099      mChangingMaterials.clear();
1100   }
1101
1102   return retMask;
1103}
1104
1105void TSStatic::unpackUpdate(NetConnection* con, BitStream* stream)
1106{
1107   Parent::unpackUpdate(con, stream);
1108
1109   if (stream->readFlag()) // TransformMask
1110   {
1111      MatrixF mat;
1112      mathRead(*stream, &mat);
1113      setTransform(mat);
1114      setRenderTransform(mat);
1115   }
1116
1117   if (stream->readFlag()) // ScaleMask
1118   {
1119      if (stream->readFlag())
1120      {
1121         VectorF scale;
1122         mathRead(*stream, &scale);
1123         setScale(scale);
1124      }
1125      else
1126         setScale(Point3F::One);
1127   }
1128
1129   if (stream->readFlag()) // UpdateCollisionMask
1130   {
1131      U32 collisionType = CollisionMesh;
1132
1133      stream->read(&collisionType);
1134
1135      // Handle it if we have changed CollisionType's
1136      if ((MeshType)collisionType != mCollisionType)
1137      {
1138         mCollisionType = (MeshType)collisionType;
1139
1140         if (isProperlyAdded() && mShapeInstance)
1141            prepCollision();
1142      }
1143   }
1144
1145   if (stream->readFlag())    // SkinMask
1146   {
1147      NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);;
1148      if (mSkinNameHandle != skinDesiredNameHandle)
1149      {
1150         mSkinNameHandle = skinDesiredNameHandle;
1151         reSkin();
1152      }
1153   }
1154
1155   if (stream->readFlag()) // AdvancedStaticOptionsMask
1156   {
1157      char buffer[256];
1158      stream->readString(buffer);
1159      setShapeAsset(StringTable->insert(buffer));
1160
1161      mShapeName = stream->readSTString();
1162
1163      stream->read((U32*)&mDecalType);
1164
1165      mAllowPlayerStep = stream->readFlag();
1166      mMeshCulling = stream->readFlag();
1167      mUseOriginSort = stream->readFlag();
1168
1169      stream->read(&mRenderNormalScalar);
1170
1171      stream->read(&mForceDetail);
1172
1173   if (stream->readFlag())
1174      mAnimOffset = stream->readFloat(7);
1175
1176   if (stream->readFlag())
1177      mAnimSpeed = stream->readSignedFloat(7) * AnimSpeedMax;
1178
1179      mPlayAmbient = stream->readFlag();
1180
1181
1182   }
1183
1184   mUseAlphaFade = stream->readFlag();
1185   if (mUseAlphaFade)
1186   {
1187      stream->read(&mAlphaFadeStart);
1188      stream->read(&mAlphaFadeEnd);
1189      stream->read(&mInvertAlphaFade);
1190   }
1191
1192   mIgnoreZodiacs = stream->readFlag();
1193   mHasGradients = stream->readFlag();
1194   if (mHasGradients)
1195   {
1196      mInvertGradientRange = stream->readFlag();
1197      stream->read(&mGradientRange.x);
1198      stream->read(&mGradientRange.y);
1199   }
1200   if (mLightPlugin)
1201   {
1202      mLightPlugin->unpackUpdate(this, con, stream);
1203   }
1204
1205   if (stream->readFlag())
1206   {
1207      cubeDescId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
1208   }
1209
1210   stream->read(&mOverrideColor);
1211
1212   if (stream->readFlag())
1213   {
1214      mChangingMaterials.clear();
1215      U32 materialCount = stream->readInt(16);
1216
1217      for (U32 i = 0; i < materialCount; i++)
1218      {
1219         matMap newMatMap;
1220         newMatMap.slot = stream->readInt(16);
1221         newMatMap.assetId = String(con->unpackNetStringHandleU(stream).getString());
1222
1223         //do the lookup, now
1224         newMatMap.matAsset = AssetDatabase.acquireAsset<MaterialAsset>(newMatMap.assetId);
1225
1226         mChangingMaterials.push_back(newMatMap);
1227      }
1228
1229      updateMaterials();
1230   }
1231
1232   if (isProperlyAdded())
1233      _updateShouldTick();
1234   set_special_typing();
1235}
1236
1237//----------------------------------------------------------------------------
1238bool TSStatic::castRay(const Point3F& start, const Point3F& end, RayInfo* info)
1239{
1240   if (mCollisionType == None)
1241      return false;
1242
1243   if (!mShapeInstance)
1244      return false;
1245
1246   if (mCollisionType == Bounds)
1247   {
1248      F32 fst;
1249      if (!mObjBox.collideLine(start, end, &fst, &info->normal))
1250         return false;
1251
1252      info->t = fst;
1253      info->object = this;
1254      info->point.interpolate(start, end, fst);
1255      info->material = NULL;
1256      return true;
1257   }
1258   else
1259   {
1260      RayInfo shortest = *info;
1261      RayInfo localInfo;
1262      shortest.t = 1e8f;
1263      localInfo.generateTexCoord = info->generateTexCoord;
1264
1265      for (U32 i = 0; i < mLOSDetails.size(); i++)
1266      {
1267         mShapeInstance->animate(mLOSDetails[i]);
1268
1269         if (mShapeInstance->castRayOpcode(mLOSDetails[i], start, end, &localInfo))
1270         {
1271            localInfo.object = this;
1272
1273            if (localInfo.t < shortest.t)
1274               shortest = localInfo;
1275         }
1276      }
1277
1278      if (shortest.object == this)
1279      {
1280         // Copy out the shortest time...
1281         *info = shortest;
1282         return true;
1283      }
1284   }
1285
1286   return false;
1287}
1288
1289bool TSStatic::castRayRendered(const Point3F& start, const Point3F& end, RayInfo* info)
1290{
1291   if (!mShapeInstance)
1292      return false;
1293
1294   // Cast the ray against the currently visible detail
1295   RayInfo localInfo;
1296   if (info && info->generateTexCoord)
1297      localInfo.generateTexCoord = true;
1298   bool res = mShapeInstance->castRayOpcode(mShapeInstance->getCurrentDetail(), start, end, &localInfo);
1299
1300   if (res)
1301   {
1302      *info = localInfo;
1303      info->object = this;
1304      return true;
1305   }
1306
1307   return false;
1308}
1309
1310bool TSStatic::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF&)
1311{
1312   if (!mShapeInstance)
1313      return false;
1314
1315   // This is safe to set even if we're not outputing 
1316   polyList->setTransform(&mObjToWorld, mObjScale);
1317   polyList->setObject(this);
1318
1319   if (context == PLC_Export)
1320   {
1321      // Use highest detail level
1322      S32 dl = 0;
1323
1324      // Try to call on the client so we can export materials
1325      if (isServerObject() && getClientObject())
1326         dynamic_cast<TSStatic*>(getClientObject())->mShapeInstance->buildPolyList(polyList, dl);
1327      else
1328         mShapeInstance->buildPolyList(polyList, dl);
1329   }
1330   else if (context == PLC_Selection)
1331   {
1332      // Use the last rendered detail level
1333      S32 dl = mShapeInstance->getCurrentDetail();
1334      mShapeInstance->buildPolyListOpcode(dl, polyList, box);
1335   }
1336   else
1337   {
1338      // Figure out the mesh type we're looking for.
1339      MeshType meshType = (context == PLC_Decal) ? mDecalType : mCollisionType;
1340
1341      if (meshType == None)
1342         return false;
1343      else if (meshType == Bounds)
1344         polyList->addBox(mObjBox);
1345      else if (meshType == VisibleMesh)
1346         mShapeInstance->buildPolyList(polyList, 0);
1347      else if (context == PLC_Decal && mDecalDetailsPtr != 0)
1348      {
1349         for (U32 i = 0; i < mDecalDetailsPtr->size(); i++)
1350            mShapeInstance->buildPolyListOpcode((*mDecalDetailsPtr)[i], polyList, box);
1351      }
1352      else
1353      {
1354         // Everything else is done from the collision meshes
1355         // which may be built from either the visual mesh or
1356         // special collision geometry.
1357         for (U32 i = 0; i < mCollisionDetails.size(); i++)
1358            mShapeInstance->buildPolyListOpcode(mCollisionDetails[i], polyList, box);
1359      }
1360   }
1361
1362   return true;
1363}
1364
1365bool TSStatic::buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF&)
1366{
1367   if (!mShapeInstance)
1368      return false;
1369
1370   if (mCollisionType == Bounds)
1371   {
1372      ColladaUtils::ExportData::colMesh* colMesh;
1373      exportData->colMeshes.increment();
1374      colMesh = &exportData->colMeshes.last();
1375
1376      colMesh->mesh.setTransform(&mObjToWorld, mObjScale);
1377      colMesh->mesh.setObject(this);
1378
1379      colMesh->mesh.addBox(mObjBox);
1380
1381      colMesh->colMeshName = String::ToString("ColBox%d-1", exportData->colMeshes.size());
1382   }
1383   else if (mCollisionType == VisibleMesh)
1384   {
1385      ColladaUtils::ExportData::colMesh* colMesh;
1386      exportData->colMeshes.increment();
1387      colMesh = &exportData->colMeshes.last();
1388
1389      colMesh->mesh.setTransform(&mObjToWorld, mObjScale);
1390      colMesh->mesh.setObject(this);
1391
1392      mShapeInstance->buildPolyList(&colMesh->mesh, 0);
1393
1394      colMesh->colMeshName = String::ToString("ColMesh%d-1", exportData->colMeshes.size());
1395   }
1396   else if (mCollisionType == CollisionMesh)
1397   {
1398      // Everything else is done from the collision meshes
1399      // which may be built from either the visual mesh or
1400      // special collision geometry.
1401      for (U32 i = 0; i < mCollisionDetails.size(); i++)
1402      {
1403         ColladaUtils::ExportData::colMesh* colMesh;
1404         exportData->colMeshes.increment();
1405         colMesh = &exportData->colMeshes.last();
1406
1407         colMesh->mesh.setTransform(&mObjToWorld, mObjScale);
1408         colMesh->mesh.setObject(this);
1409
1410         mShapeInstance->buildPolyListOpcode(mCollisionDetails[i], &colMesh->mesh, box);
1411
1412         colMesh->colMeshName = String::ToString("ColMesh%d-1", exportData->colMeshes.size());
1413      }
1414   }
1415
1416   //Next, process the LOD levels and materials.
1417   if (isServerObject() && getClientObject())
1418   {
1419      TSStatic* clientShape = dynamic_cast<TSStatic*>(getClientObject());
1420
1421      exportData->meshData.increment();
1422
1423      //Prep a meshData for this shape in particular
1424      ColladaUtils::ExportData::meshLODData* meshData = &exportData->meshData.last();
1425
1426      //Fill out the info we'll need later to actually append our mesh data for the detail levels during the processing phase
1427      meshData->shapeInst = clientShape->mShapeInstance;
1428      meshData->originatingObject = this;
1429      meshData->meshTransform = mObjToWorld;
1430      meshData->scale = mObjScale;
1431
1432      //Iterate over all our detail levels
1433      for (U32 i = 0; i < clientShape->mShapeInstance->getNumDetails(); i++)
1434      {
1435         TSShape::Detail detail = clientShape->mShapeInstance->getShape()->details[i];
1436
1437         String detailName = String::ToLower(clientShape->mShapeInstance->getShape()->getName(detail.nameIndex));
1438
1439         //Skip it if it's a collision or line of sight element
1440         if (detailName.startsWith("col") || detailName.startsWith("los"))
1441            continue;
1442
1443         meshData->meshDetailLevels.increment();
1444
1445         ColladaUtils::ExportData::detailLevel* curDetail = &meshData->meshDetailLevels.last();
1446
1447         //Make sure we denote the size this detail level has
1448         curDetail->size = getNextPow2(detail.size);
1449      }
1450   }
1451
1452   return true;
1453}
1454
1455void TSStatic::buildConvex(const Box3F& box, Convex* convex)
1456{
1457   if (mCollisionType == None)
1458      return;
1459
1460   if (mShapeInstance == NULL)
1461      return;
1462
1463   // These should really come out of a pool
1464   mConvexList->collectGarbage();
1465
1466   if (mCollisionType == Bounds)
1467   {
1468      // Just return a box convex for the entire shape...
1469      Convex* cc = 0;
1470      CollisionWorkingList& wl = convex->getWorkingList();
1471      for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext)
1472      {
1473         if (itr->mConvex->getType() == BoxConvexType &&
1474            itr->mConvex->getObject() == this)
1475         {
1476            cc = itr->mConvex;
1477            break;
1478         }
1479      }
1480      if (cc)
1481         return;
1482
1483      // Create a new convex.
1484      BoxConvex* cp = new BoxConvex;
1485      mConvexList->registerObject(cp);
1486      convex->addToWorkingList(cp);
1487      cp->init(this);
1488
1489      mObjBox.getCenter(&cp->mCenter);
1490      cp->mSize.x = mObjBox.len_x() / 2.0f;
1491      cp->mSize.y = mObjBox.len_y() / 2.0f;
1492      cp->mSize.z = mObjBox.len_z() / 2.0f;
1493   }
1494   else  // CollisionMesh || VisibleMesh
1495   {
1496      TSStaticPolysoupConvex::smCurObject = this;
1497
1498      for (U32 i = 0; i < mCollisionDetails.size(); i++)
1499         mShapeInstance->buildConvexOpcode(mObjToWorld, mObjScale, mCollisionDetails[i], box, convex, mConvexList);
1500
1501      TSStaticPolysoupConvex::smCurObject = NULL;
1502   }
1503}
1504
1505SceneObject* TSStaticPolysoupConvex::smCurObject = NULL;
1506
1507TSStaticPolysoupConvex::TSStaticPolysoupConvex()
1508   : box(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f),
1509   normal(0.0f, 0.0f, 0.0f, 0.0f),
1510   idx(0),
1511   mesh(NULL)
1512{
1513   mType = TSPolysoupConvexType;
1514
1515   for (U32 i = 0; i < 4; ++i)
1516   {
1517      verts[i].set(0.0f, 0.0f, 0.0f);
1518   }
1519}
1520
1521Point3F TSStaticPolysoupConvex::support(const VectorF& vec) const
1522{
1523   F32 bestDot = mDot(verts[0], vec);
1524
1525   const Point3F* bestP = &verts[0];
1526   for (S32 i = 1; i < 4; i++)
1527   {
1528      F32 newD = mDot(verts[i], vec);
1529      if (newD > bestDot)
1530      {
1531         bestDot = newD;
1532         bestP = &verts[i];
1533      }
1534   }
1535
1536   return *bestP;
1537}
1538
1539Box3F TSStaticPolysoupConvex::getBoundingBox() const
1540{
1541   Box3F wbox = box;
1542   wbox.minExtents.convolve(mObject->getScale());
1543   wbox.maxExtents.convolve(mObject->getScale());
1544   mObject->getTransform().mul(wbox);
1545   return wbox;
1546}
1547
1548Box3F TSStaticPolysoupConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
1549{
1550   AssertISV(false, "TSStaticPolysoupConvex::getBoundingBox(m,p) - Not implemented. -- XEA");
1551   return box;
1552}
1553
1554void TSStaticPolysoupConvex::getPolyList(AbstractPolyList* list)
1555{
1556   // Transform the list into object space and set the pointer to the object
1557   MatrixF i(mObject->getTransform());
1558   Point3F iS(mObject->getScale());
1559   list->setTransform(&i, iS);
1560   list->setObject(mObject);
1561
1562   // Add only the original collision triangle
1563   S32 base = list->addPoint(verts[0]);
1564   list->addPoint(verts[2]);
1565   list->addPoint(verts[1]);
1566
1567   list->begin(0, (U32)idx ^ (uintptr_t)mesh);
1568   list->vertex(base + 2);
1569   list->vertex(base + 1);
1570   list->vertex(base + 0);
1571   list->plane(base + 0, base + 1, base + 2);
1572   list->end();
1573}
1574
1575void TSStaticPolysoupConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf)
1576{
1577   cf->material = 0;
1578   cf->mObject = mObject;
1579
1580   // For a tetrahedron this is pretty easy... first
1581   // convert everything into world space.
1582   Point3F tverts[4];
1583   mat.mulP(verts[0], &tverts[0]);
1584   mat.mulP(verts[1], &tverts[1]);
1585   mat.mulP(verts[2], &tverts[2]);
1586   mat.mulP(verts[3], &tverts[3]);
1587
1588   // points...
1589   S32 firstVert = cf->mVertexList.size();
1590   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0];
1591   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1];
1592   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2];
1593   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3];
1594
1595   //    edges...
1596   cf->mEdgeList.increment();
1597   cf->mEdgeList.last().vertex[0] = firstVert + 0;
1598   cf->mEdgeList.last().vertex[1] = firstVert + 1;
1599
1600   cf->mEdgeList.increment();
1601   cf->mEdgeList.last().vertex[0] = firstVert + 1;
1602   cf->mEdgeList.last().vertex[1] = firstVert + 2;
1603
1604   cf->mEdgeList.increment();
1605   cf->mEdgeList.last().vertex[0] = firstVert + 2;
1606   cf->mEdgeList.last().vertex[1] = firstVert + 0;
1607
1608   cf->mEdgeList.increment();
1609   cf->mEdgeList.last().vertex[0] = firstVert + 3;
1610   cf->mEdgeList.last().vertex[1] = firstVert + 0;
1611
1612   cf->mEdgeList.increment();
1613   cf->mEdgeList.last().vertex[0] = firstVert + 3;
1614   cf->mEdgeList.last().vertex[1] = firstVert + 1;
1615
1616   cf->mEdgeList.increment();
1617   cf->mEdgeList.last().vertex[0] = firstVert + 3;
1618   cf->mEdgeList.last().vertex[1] = firstVert + 2;
1619
1620   //    triangles...
1621   cf->mFaceList.increment();
1622   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]);
1623   cf->mFaceList.last().vertex[0] = firstVert + 2;
1624   cf->mFaceList.last().vertex[1] = firstVert + 1;
1625   cf->mFaceList.last().vertex[2] = firstVert + 0;
1626
1627   cf->mFaceList.increment();
1628   cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]);
1629   cf->mFaceList.last().vertex[0] = firstVert + 1;
1630   cf->mFaceList.last().vertex[1] = firstVert + 0;
1631   cf->mFaceList.last().vertex[2] = firstVert + 3;
1632
1633   cf->mFaceList.increment();
1634   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]);
1635   cf->mFaceList.last().vertex[0] = firstVert + 2;
1636   cf->mFaceList.last().vertex[1] = firstVert + 1;
1637   cf->mFaceList.last().vertex[2] = firstVert + 3;
1638
1639   cf->mFaceList.increment();
1640   cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]);
1641   cf->mFaceList.last().vertex[0] = firstVert + 0;
1642   cf->mFaceList.last().vertex[1] = firstVert + 2;
1643   cf->mFaceList.last().vertex[2] = firstVert + 3;
1644
1645   // All done!
1646}
1647
1648void TSStatic::onMount(SceneObject* obj, S32 node)
1649{
1650   Parent::onMount(obj, node);
1651   _updateShouldTick();
1652}
1653
1654void TSStatic::onUnmount(SceneObject* obj, S32 node)
1655{
1656   Parent::onUnmount(obj, node);
1657   setMaskBits(TransformMask);
1658   _updateShouldTick();
1659}
1660
1661U32 TSStatic::getNumDetails()
1662{
1663   if (isServerObject() && getClientObject())
1664   {
1665      TSStatic* clientShape = dynamic_cast<TSStatic*>(getClientObject());
1666      return clientShape->mShapeInstance->getNumDetails();
1667   }
1668   return 0;
1669};
1670
1671void TSStatic::updateMaterials()
1672{
1673   if (mChangingMaterials.empty() || !mShape)
1674      return;
1675
1676   TSMaterialList* pMatList = mShapeInstance->getMaterialList();
1677
1678   String path;
1679   if (mShapeAsset->isAssetValid())
1680      path = mShapeAsset->getShapeFilename();
1681   else
1682      path = mShapeName;
1683
1684   pMatList->setTextureLookupPath(path);
1685
1686   bool found = false;
1687   const Vector<String>& materialNames = pMatList->getMaterialNameList();
1688   for (S32 i = 0; i < materialNames.size(); i++)
1689   {
1690      if (found)
1691         break;
1692
1693      for (U32 m = 0; m < mChangingMaterials.size(); m++)
1694      {
1695         if (mChangingMaterials[m].slot == i)
1696         {
1697            //Fetch the actual material asset
1698            pMatList->renameMaterial(i, mChangingMaterials[m].matAsset->getMaterialDefinitionName());
1699            found = true;
1700            break;
1701         }
1702      }
1703   }
1704
1705   mChangingMaterials.clear();
1706
1707   // Initialize the material instances
1708   mShapeInstance->initMaterialList();
1709}
1710
1711void TSStatic::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
1712{
1713   if(!mShapeAsset.isNull() && mShapeAsset->getAssetId() != StringTable->insert("Core_Rendering:noShape"))
1714      usedAssetsList->push_back_unique(mShapeAsset->getAssetId());
1715
1716}
1717
1718//------------------------------------------------------------------------
1719//These functions are duplicated in tsStatic and shapeBase.
1720//They each function a little differently; but achieve the same purpose of gathering
1721//target names/counts without polluting simObject.
1722
1723void TSStatic::onInspect(GuiInspector* inspector)
1724{
1725   if (mShapeAsset == nullptr)
1726      return;
1727
1728   //Put the GameObject group before everything that'd be gameobject-effecting, for orginazational purposes
1729   GuiInspectorGroup* materialGroup = inspector->findExistentGroup(StringTable->insert("Materials"));
1730   if (!materialGroup)
1731      return;
1732
1733   GuiControl* stack = dynamic_cast<GuiControl*>(materialGroup->findObjectByInternalName(StringTable->insert("Stack")));
1734
1735   //Do this on both the server and client
1736   S32 materialCount = mShapeAsset->getShape()->materialList->getMaterialNameList().size(); //mMeshAsset->getMaterialCount();
1737
1738   if (isServerObject())
1739   {
1740      //we need to update the editor
1741      /*for (U32 i = 0; i < mFields.size(); i++)
1742      {
1743         //find any with the materialslot title and clear them out
1744         if (FindMatch::isMatch("MaterialSlot*", mFields[i].mFieldName, false))
1745         {
1746            setDataField(mFields[i].mFieldName, NULL, "");
1747            mFields.erase(i);
1748            continue;
1749         }
1750      }*/
1751
1752      //next, get a listing of our materials in the shape, and build our field list for them
1753      char matFieldName[128];
1754
1755      for (U32 i = 0; i < materialCount; i++)
1756      {
1757         StringTableEntry materialname = StringTable->insert(mShapeAsset->getShape()->materialList->getMaterialName(i).c_str());
1758
1759         //Iterate through our assetList to find the compliant entry in our matList
1760         for (U32 m = 0; m < mShapeAsset->getMaterialCount(); m++)
1761         {
1762            AssetPtr<MaterialAsset> matAsset = mShapeAsset->getMaterialAsset(m);
1763
1764            if (matAsset->getMaterialDefinitionName() == materialname)
1765            {
1766               dSprintf(matFieldName, 128, "MaterialSlot%d", i);
1767
1768               //addComponentField(matFieldName, "A material used in the shape file", "Material", matAsset->getAssetId(), "");
1769               //Con::executef(this, "onConstructComponentField", mTargetComponent, field->mFieldName);
1770               Con::printf("Added material field for MaterialSlot %d", i);
1771
1772               GuiInspectorField* fieldGui = materialGroup->constructField(TypeMaterialAssetPtr);
1773               fieldGui->init(inspector, materialGroup);
1774
1775               fieldGui->setSpecialEditField(true);
1776               fieldGui->setTargetObject(this);
1777
1778               StringTableEntry fldnm = StringTable->insert(matFieldName);
1779
1780               fieldGui->setSpecialEditVariableName(fldnm);
1781
1782               fieldGui->setInspectorField(NULL, fldnm);
1783               fieldGui->setDocs("");
1784
1785               if (fieldGui->registerObject())
1786               {
1787                  fieldGui->setValue(materialname);
1788
1789                  stack->addObject(fieldGui);
1790               }
1791               else
1792               {
1793                  SAFE_DELETE(fieldGui);
1794               }
1795
1796               /*if (materialGroup->isMethod("onConstructField"))
1797               {
1798                  //ensure our stack variable is bound if we need it
1799                  //Con::evaluatef("%d.stack = %d;", materialGroup->getId(), materialGroup->at(0)->getId());
1800
1801                  Con::executef(materialGroup, "onConstructField", matFieldName,
1802                     matFieldName, "material", matFieldName,
1803                     materialname, "", "", this);
1804               }*/
1805               break;
1806            }
1807         }
1808      }
1809   }
1810}
1811
1812DefineEngineMethod(TSStatic, getTargetName, const char*, (S32 index), (0),
1813   "Get the name of the indexed shape material.\n"
1814   "@param index index of the material to get (valid range is 0 - getTargetCount()-1).\n"
1815   "@return the name of the indexed material.\n"
1816   "@see getTargetCount()\n")
1817{
1818   TSStatic* obj = dynamic_cast<TSStatic*> (object);
1819   if (obj)
1820   {
1821      // Try to use the client object (so we get the reskinned targets in the Material Editor)
1822      if ((TSStatic*)obj->getClientObject())
1823         obj = (TSStatic*)obj->getClientObject();
1824
1825      return obj->getShapeInstance()->getTargetName(index);
1826   }
1827
1828   return "";
1829}
1830
1831DefineEngineMethod(TSStatic, getTargetCount, S32, (), ,
1832   "Get the number of materials in the shape.\n"
1833   "@return the number of materials in the shape.\n"
1834   "@see getTargetName()\n")
1835{
1836   TSStatic* obj = dynamic_cast<TSStatic*> (object);
1837   if (obj)
1838   {
1839      // Try to use the client object (so we get the reskinned targets in the Material Editor)
1840      if ((TSStatic*)obj->getClientObject())
1841         obj = (TSStatic*)obj->getClientObject();
1842
1843      return obj->getShapeInstance()->getTargetCount();
1844   }
1845
1846   return -1;
1847}
1848
1849// This method is able to change materials per map to with others. The material that is being replaced is being mapped to
1850// unmapped_mat as a part of this transition
1851
1852DefineEngineMethod(TSStatic, changeMaterial, void, (const char* mapTo, Material* oldMat, Material* newMat), ("", nullAsType<Material*>(), nullAsType<Material*>()),
1853   "@brief Change one of the materials on the shape.\n\n"
1854
1855   "This method changes materials per mapTo with others. The material that "
1856   "is being replaced is mapped to unmapped_mat as a part of this transition.\n"
1857
1858   "@note Warning, right now this only sort of works. It doesn't do a live "
1859   "update like it should.\n"
1860
1861   "@param mapTo the name of the material target to remap (from getTargetName)\n"
1862   "@param oldMat the old Material that was mapped \n"
1863   "@param newMat the new Material to map\n\n"
1864
1865   "@tsexample\n"
1866   "// remap the first material in the shape\n"
1867   "%mapTo = %obj.getTargetName( 0 );\n"
1868   "%obj.changeMaterial( %mapTo, 0, MyMaterial );\n"
1869   "@endtsexample\n")
1870{
1871   // if no valid new material, theres no reason for doing this
1872   if (!newMat)
1873   {
1874      Con::errorf("TSShape::changeMaterial failed: New material does not exist!");
1875      return;
1876   }
1877
1878   TSMaterialList* shapeMaterialList = object->getShape()->materialList;
1879
1880   // Check the mapTo name exists for this shape
1881   S32 matIndex = shapeMaterialList->getMaterialNameList().find_next(String(mapTo));
1882   if (matIndex < 0)
1883   {
1884      Con::errorf("TSShape::changeMaterial failed: Invalid mapTo name '%s'", mapTo);
1885      return;
1886   }
1887
1888   // Lets remap the old material off, so as to let room for our current material room to claim its spot
1889   if (oldMat)
1890      oldMat->mMapTo = String("unmapped_mat");
1891
1892   newMat->mMapTo = mapTo;
1893
1894   // Map the material by name in the matmgr
1895   MATMGR->mapMaterial(mapTo, newMat->getName());
1896
1897   // Replace instances with the new material being traded in. Lets make sure that we only
1898   // target the specific targets per inst, this is actually doing more than we thought
1899   delete shapeMaterialList->mMatInstList[matIndex];
1900   shapeMaterialList->mMatInstList[matIndex] = newMat->createMatInstance();
1901
1902   // Finish up preparing the material instances for rendering
1903   const GFXVertexFormat* flags = getGFXVertexFormat<GFXVertexPNTTB>();
1904   FeatureSet features = MATMGR->getDefaultFeatures();
1905   shapeMaterialList->getMaterialInst(matIndex)->init(features, flags);
1906}
1907
1908DefineEngineMethod(TSStatic, getModelFile, const char*, (), ,
1909   "@brief Get the model filename used by this shape.\n\n"
1910
1911   "@return the shape filename\n\n"
1912   "@tsexample\n"
1913   "// Acquire the model filename used on this shape.\n"
1914   "%modelFilename = %obj.getModelFile();\n"
1915   "@endtsexample\n"
1916)
1917{
1918   return object->getShapeFileName();
1919}
1920
1921void TSStatic::set_special_typing()
1922{
1923   if (mCollisionType == VisibleMesh || mCollisionType == CollisionMesh)
1924      mTypeMask |= InteriorLikeObjectType;
1925   else
1926      mTypeMask &= ~<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084ae7ac7d67ceb85fd2c0e1b83e6acd6634">InteriorLikeObjectType</a>;
1927}
1928
1929void TSStatic::onStaticModified(const char* slotName, const char* newValue)
1930{
1931#ifdef TORQUE_AFX_ENABLED
1932   if (slotName == afxZodiacData::GradientRangeSlot)
1933   {
1934      afxZodiacData::convertGradientRangeFromDegrees(mGradientRange, mGradientRangeUser);
1935      return;
1936   }
1937#endif
1938
1939   set_special_typing();
1940}
1941
1942void TSStatic::setSelectionFlags(U8 flags)
1943{
1944   Parent::setSelectionFlags(flags);
1945
1946   if (!mShapeInstance || !isClientObject())
1947      return;
1948
1949   if (!mShapeInstance->ownMaterialList())
1950      return;
1951
1952   TSMaterialList* pMatList = mShapeInstance->getMaterialList();
1953   for (S32 j = 0; j < pMatList->size(); j++)
1954   {
1955      BaseMatInstance* bmi = pMatList->getMaterialInst(j);
1956      bmi->setSelectionHighlighting(needsSelectionHighlighting());
1957   }
1958}
1959
1960