afxModel.cpp

Engine/source/afx/ce/afxModel.cpp

More...

Public Defines

define
BAD_ANIM_ID() 999999999
define
myOffset(field) (field, )

Public Functions

ConsoleDocClass(afxModel , "@brief A Model effect as defined by an <a href="/coding/class/structafxmodeldata/">afxModelData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock.\n\n</a>" "A Model effect is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightweight client-only geometry object useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> effect-driven " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">props.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
ConsoleDocClass(afxModelData , "@brief A datablock that specifies <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Model <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effect.\n\n</a>" "A Model effect is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightweight client-only geometry object useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> effect-driven props." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

Detailed Description

Public Defines

BAD_ANIM_ID() 999999999
myOffset(field) (field, )

Public Functions

ConsoleDocClass(afxModel , "@brief A Model effect as defined by an <a href="/coding/class/structafxmodeldata/">afxModelData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock.\n\n</a>" "A Model effect is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightweight client-only geometry object useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> effect-driven " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">props.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )

ConsoleDocClass(afxModelData , "@brief A datablock that specifies <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> Model <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effect.\n\n</a>" "A Model effect is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> lightweight client-only geometry object useful <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> effect-driven props." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(afxModelData )

IMPLEMENT_CO_NETOBJECT_V1(afxModel )

  1
  2
  3//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  4// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  5// Copyright (C) 2015 Faust Logic, Inc.
  6//
  7// Permission is hereby granted, free of charge, to any person obtaining a copy
  8// of this software and associated documentation files (the "Software"), to
  9// deal in the Software without restriction, including without limitation the
 10// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 11// sell copies of the Software, and to permit persons to whom the Software is
 12// furnished to do so, subject to the following conditions:
 13//
 14// The above copyright notice and this permission notice shall be included in
 15// all copies or substantial portions of the Software.
 16//
 17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 22// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 23// IN THE SOFTWARE.
 24//
 25//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 26
 27#include "afx/arcaneFX.h"
 28
 29#include "T3D/objectTypes.h"
 30#include "T3D/gameBase/gameProcess.h"
 31#include "core/resourceManager.h"
 32#include "sim/netConnection.h"
 33#include "scene/sceneRenderState.h"
 34#include "scene/sceneManager.h"
 35#include "ts/tsShapeInstance.h"
 36#include "ts/tsMaterialList.h"
 37
 38#include "afx/ce/afxModel.h"
 39
 40//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 41// afxModelData
 42
 43IMPLEMENT_CO_DATABLOCK_V1(afxModelData);
 44 
 45ConsoleDocClass( afxModelData,
 46   "@brief A datablock that specifies a Model effect.\n\n"
 47
 48   "A Model effect is a lightweight client-only geometry object useful for effect-driven props."
 49   "\n\n"
 50
 51   "@ingroup afxEffects\n"
 52   "@ingroup AFX\n"
 53   "@ingroup Datablocks\n"
 54);
 55
 56afxModelData::afxModelData()
 57{
 58  shapeName = ST_NULLSTRING;
 59  sequence = ST_NULLSTRING;
 60  seq_rate = 1.0f;
 61  seq_offset = 0.0f;
 62  alpha_mult = 1.0f;
 63  use_vertex_alpha = false;
 64  force_on_material_flags = 0;
 65  force_off_material_flags = 0;
 66  texture_filtering = true;
 67  fog_mult = 1.0f;
 68  remap_txr_tags = ST_NULLSTRING;
 69  remap_buffer = 0;
 70
 71  overrideLightingOptions = false;
 72  receiveSunLight = true;
 73  receiveLMLighting = true;
 74  useAdaptiveSelfIllumination = false;
 75  useCustomAmbientLighting = false;
 76  customAmbientForSelfIllumination = false;
 77  customAmbientLighting = LinearColorF(0.0f, 0.0f, 0.0f);
 78  shadowEnable = false;
 79
 80  shadowSize = 128;
 81  shadowMaxVisibleDistance = 80.0f;
 82  shadowProjectionDistance = 10.0f;
 83  shadowSphereAdjust = 1.0;
 84}
 85
 86afxModelData::afxModelData(const afxModelData& other, bool temp_clone) : GameBaseData(other, temp_clone)
 87{
 88  shapeName = other.shapeName;
 89  shape = other.shape; // --
 90  sequence = other.sequence;
 91  seq_rate = other.seq_rate;
 92  seq_offset = other.seq_offset;
 93  alpha_mult = other.alpha_mult;
 94  use_vertex_alpha = other.use_vertex_alpha;
 95  force_on_material_flags = other.force_on_material_flags;
 96  force_off_material_flags = other.force_off_material_flags;
 97  texture_filtering = other.texture_filtering;
 98  fog_mult = other.fog_mult;
 99  remap_txr_tags = other.remap_txr_tags;
100  remap_buffer = other.remap_buffer;
101  overrideLightingOptions = other.overrideLightingOptions;
102  receiveSunLight = other.receiveSunLight;
103  receiveLMLighting = other.receiveLMLighting;
104  useAdaptiveSelfIllumination = other.useAdaptiveSelfIllumination;
105  useCustomAmbientLighting = other.useCustomAmbientLighting;
106  customAmbientForSelfIllumination = other.customAmbientForSelfIllumination;
107  customAmbientLighting = other.customAmbientLighting;
108  shadowEnable = other.shadowEnable;
109
110  shadowSize = other.shadowSize;
111  shadowMaxVisibleDistance = other.shadowMaxVisibleDistance;
112  shadowProjectionDistance = other.shadowProjectionDistance;
113  shadowSphereAdjust = other.shadowSphereAdjust;
114}
115
116afxModelData::~afxModelData()
117{
118   if (remap_buffer)
119      dFree(remap_buffer);
120}
121
122bool afxModelData::preload(bool server, String &errorStr)
123{
124  if (Parent::preload(server, errorStr) == false)
125    return false;
126  
127  // don't need to do this stuff on the server
128  if (server) 
129    return true;
130  
131  if (shapeName != ST_NULLSTRING && !shape)
132  {
133    shape = ResourceManager::get().load(shapeName);
134    if (!shape)
135    {
136      errorStr = String::ToString("afxModelData::load: Failed to load shape \"%s\"", shapeName);
137      return false;
138    }
139
140    // just parse up the string and collect the remappings in txr_tag_remappings.
141    if (remap_txr_tags != ST_NULLSTRING)
142    {
143       txr_tag_remappings.clear();
144       if (remap_buffer)
145          dFree(remap_buffer);
146
147       remap_buffer = dStrdup(remap_txr_tags);
148
149       char* remap_token = dStrtok(remap_buffer, " \t");
150       while (remap_token != NULL)
151       {
152          char* colon = dStrchr(remap_token, ':');
153          if (colon)
154          {
155             *colon = '\0';
156             txr_tag_remappings.increment();
157             txr_tag_remappings.last().old_tag = remap_token;
158             txr_tag_remappings.last().new_tag = colon+1;
159          }
160          remap_token = dStrtok(NULL, " \t");
161       }
162    }
163
164    // this little hack messes things up when remapping texture tags
165    if (txr_tag_remappings.size() == 0)
166    {
167      // this little hack forces the textures to preload
168      TSShapeInstance* pDummy = new TSShapeInstance(shape);
169      delete pDummy;
170    }
171  }
172
173  return true;
174}
175
176#define myOffset(field) Offset(field, afxModelData)
177
178void afxModelData::initPersistFields()
179{
180  addField("shapeFile",             TypeFilename, myOffset(shapeName),
181    "The name of a .dts format file to use for the model.");
182  addField("sequence",              TypeFilename, myOffset(sequence),
183    "The name of an animation sequence to play in the model.");
184  addField("sequenceRate",          TypeF32,      myOffset(seq_rate),
185    "The rate of playback for the sequence.");
186  addField("sequenceOffset",        TypeF32,      myOffset(seq_offset),
187    "An offset in seconds indicating a starting point for the animation sequence "
188    "specified by the sequence field. A rate of 1.0 (rather than sequenceRate) is used "
189    "to convert from seconds to the thread offset.");
190  addField("alphaMult",             TypeF32,      myOffset(alpha_mult),
191    "An alpha multiplier used to set maximum opacity of the model.");
192
193  addField("fogMult",               TypeF32,      myOffset(fog_mult),
194    "");
195  addField("remapTextureTags",      TypeString,   myOffset(remap_txr_tags),
196    "Rename one or more texture tags in the model. Texture tags are what link a "
197    "model's textures to materials.\n"
198    "Field should be a string containing space-separated remapping tokens. A remapping "
199    "token is two names separated by a colon, ':'. The first name should be a texture-tag "
200    "that exists in the model, while the second is a new name to replace it. The string "
201    "can have any number of remapping tokens as long as the total string length does not "
202    "exceed 255.");
203  addField("shadowEnable",                  TypeBool,   myOffset(shadowEnable),
204    "Sets whether the model casts a shadow.");
205
206  addField("useVertexAlpha",        TypeBool,     myOffset(use_vertex_alpha),
207    "deprecated");
208  addField("forceOnMaterialFlags",  TypeS32,      myOffset(force_on_material_flags),
209    "deprecated");
210  addField("forceOffMaterialFlags", TypeS32,      myOffset(force_off_material_flags),
211    "deprecated");
212  addField("textureFiltering",      TypeBool,     myOffset(texture_filtering),
213    "deprecated");
214  addField("overrideLightingOptions",       TypeBool,   myOffset(overrideLightingOptions),
215    "deprecated");
216  addField("receiveSunLight",               TypeBool,   myOffset(receiveSunLight),
217    "");
218  addField("receiveLMLighting",             TypeBool,   myOffset(receiveLMLighting),
219    "deprecated");
220  addField("useAdaptiveSelfIllumination",   TypeBool,   myOffset(useAdaptiveSelfIllumination),
221    "deprecated");
222  addField("useCustomAmbientLighting",      TypeBool,   myOffset(useCustomAmbientLighting),
223    "deprecated");
224  addField("customAmbientSelfIllumination", TypeBool,   myOffset(customAmbientForSelfIllumination),
225    "deprecated");
226  addField("customAmbientLighting",         TypeColorF, myOffset(customAmbientLighting),
227    "deprecated");
228  addField("shadowSize",                    TypeS32,    myOffset(shadowSize),
229    "deprecated");
230  addField("shadowMaxVisibleDistance",      TypeF32,    myOffset(shadowMaxVisibleDistance),
231    "deprecated");
232  addField("shadowProjectionDistance",      TypeF32,    myOffset(shadowProjectionDistance),
233    "deprecated");
234  addField("shadowSphereAdjust",            TypeF32,    myOffset(shadowSphereAdjust),
235    "deprecated");
236
237  Parent::initPersistFields();
238
239  // Material Flags
240  Con::setIntVariable("$MaterialFlags::S_Wrap",              TSMaterialList::S_Wrap);
241  Con::setIntVariable("$MaterialFlags::T_Wrap",              TSMaterialList::T_Wrap);
242  Con::setIntVariable("$MaterialFlags::Translucent",         TSMaterialList::Translucent);
243  Con::setIntVariable("$MaterialFlags::Additive",            TSMaterialList::Additive);
244  Con::setIntVariable("$MaterialFlags::Subtractive",         TSMaterialList::Subtractive);
245  Con::setIntVariable("$MaterialFlags::SelfIlluminating",    TSMaterialList::SelfIlluminating);
246  Con::setIntVariable("$MaterialFlags::NeverEnvMap",         TSMaterialList::NeverEnvMap);
247  Con::setIntVariable("$MaterialFlags::NoMipMap",            TSMaterialList::NoMipMap);
248  Con::setIntVariable("$MaterialFlags::MipMap_ZeroBorder",   TSMaterialList::MipMap_ZeroBorder);
249  Con::setIntVariable("$MaterialFlags::AuxiliaryMap",        TSMaterialList::AuxiliaryMap);
250
251#if defined(AFX_CAP_AFXMODEL_TYPE)
252  Con::setIntVariable("$TypeMasks::afxModelObjectType",      afxModelObjectType);
253#endif
254}
255
256void afxModelData::packData(BitStream* stream)
257{
258  Parent::packData(stream);
259
260  stream->writeString(shapeName);
261  stream->writeString(sequence);
262  stream->write(seq_rate);  
263  stream->write(seq_offset);
264  stream->write(alpha_mult); 
265  stream->write(use_vertex_alpha); 
266  stream->write(force_on_material_flags);
267  stream->write(force_off_material_flags);
268  stream->writeFlag(texture_filtering);
269  stream->write(fog_mult);
270
271  stream->writeString(remap_txr_tags);
272
273  stream->writeFlag(overrideLightingOptions);
274  stream->writeFlag(receiveSunLight);
275  stream->writeFlag(useAdaptiveSelfIllumination);
276  stream->writeFlag(useCustomAmbientLighting);
277  stream->writeFlag(customAmbientForSelfIllumination);
278  stream->write(customAmbientLighting);
279  stream->writeFlag(receiveLMLighting);
280  stream->writeFlag(shadowEnable);
281
282  stream->write(shadowSize);
283  stream->write(shadowMaxVisibleDistance);
284  stream->write(shadowProjectionDistance);
285  stream->write(shadowSphereAdjust);
286}
287
288void afxModelData::unpackData(BitStream* stream)
289{
290  Parent::unpackData(stream);
291
292  shapeName = stream->readSTString();
293  sequence = stream->readSTString();
294  stream->read(&seq_rate);
295  stream->read(&seq_offset);
296  stream->read(&alpha_mult);
297  stream->read(&use_vertex_alpha);
298  stream->read(&force_on_material_flags);
299  stream->read(&force_off_material_flags);
300  texture_filtering = stream->readFlag();
301  stream->read(&fog_mult);
302
303  remap_txr_tags = stream->readSTString();
304
305  overrideLightingOptions = stream->readFlag();
306  receiveSunLight = stream->readFlag();
307  useAdaptiveSelfIllumination = stream->readFlag();
308  useCustomAmbientLighting = stream->readFlag();
309  customAmbientForSelfIllumination = stream->readFlag();
310  stream->read(&customAmbientLighting);
311  receiveLMLighting = stream->readFlag();
312  shadowEnable = stream->readFlag();
313
314  stream->read(&shadowSize);
315  stream->read(&shadowMaxVisibleDistance);
316  stream->read(&shadowProjectionDistance);
317  stream->read(&shadowSphereAdjust);
318}
319
320void afxModelData::onPerformSubstitutions() 
321{ 
322  if (shapeName != ST_NULLSTRING)
323  {
324    shape = ResourceManager::get().load(shapeName);
325    if (!shape)
326    {
327      Con::errorf("afxModelData::onPerformSubstitutions: Failed to load shape \"%s\"", shapeName);
328      return;
329    }
330
331    // REMAP-TEXTURE-TAGS ISSUES?
332  }
333}
334
335//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
336//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
337// afxModel
338
339IMPLEMENT_CO_NETOBJECT_V1(afxModel);
340
341ConsoleDocClass( afxModel,
342   "@brief A Model effect as defined by an afxModelData datablock.\n\n"
343
344   "A Model effect is a lightweight client-only geometry object useful for effect-driven "
345   "props.\n"
346
347   "@ingroup afxEffects\n"
348   "@ingroup AFX\n"
349);
350
351afxModel::afxModel()
352{
353  mTypeMask |= DynamicShapeObjectType;
354#if defined(AFX_CAP_AFXMODEL_TYPE)
355  mTypeMask |= afxModelObjectType;
356#endif
357
358  shape_inst = 0;
359
360  main_seq_thread = 0;
361  main_seq_id = -1;
362  seq_rate_factor = 1.0f;
363  last_anim_tag = 0;
364
365  seq_animates_vis = false;
366  fade_amt = 1.0f;
367  is_visible = true;
368  sort_priority = 0;
369  mDataBlock = NULL;
370  mNetFlags.set( IsGhost );
371}
372
373afxModel::~afxModel()
374{
375  delete shape_inst;
376}
377
378void afxModel::setSequenceRateFactor(F32 factor)
379{
380  seq_rate_factor = factor;
381  if (shape_inst != NULL && main_seq_thread != NULL)
382    shape_inst->setTimeScale(main_seq_thread, seq_rate_factor*mDataBlock->seq_rate);
383}
384
385//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
386
387bool afxModel::onNewDataBlock(GameBaseData* dptr, bool reload)
388{
389  mDataBlock = dynamic_cast<afxModelData*>(dptr);
390  if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
391    return false;
392
393  return true;
394}
395
396bool afxModel::onAdd()
397{
398  // first check if we have a server connection, if we don't then this is on the server
399  // and we should exit, then check if the parent fails to add the object
400  NetConnection* conn = NetConnection::getConnectionToServer();
401  if (!conn || !Parent::onAdd())
402    return false;
403
404  // setup our bounding box
405  if (mDataBlock->shape)
406    mObjBox = mDataBlock->shape->mBounds;
407  else
408    mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
409
410  // setup the shape instance and sequence
411  if (mDataBlock->shape)
412  {
413     if (/*isClientObject() && */mDataBlock->txr_tag_remappings.size() > 0)
414     {
415        // temporarily substitute material tags with alternates
416        TSMaterialList* mat_list = mDataBlock->shape->materialList;
417        if (mat_list)
418        {
419           for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
420           {
421              afxModelData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
422              Vector<String> & mat_names = (Vector<String>&) mat_list->getMaterialNameList();
423              for (S32 j = 0; j < mat_names.size(); j++) 
424              {
425                 if (mat_names[j].compare(remap->old_tag, dStrlen(remap->old_tag), String::NoCase) == 0)
426                 {
427                    //Con::printf("REMAP TEXTURE TAG [%s] TO [%s]", remap->old_tag, remap->new_tag);
428                    mat_names[j] = String(remap->new_tag);
429                    mat_names[j].insert(0,'#');
430                    break;
431                 }
432              }
433           }
434        }
435     }
436
437    shape_inst = new TSShapeInstance(mDataBlock->shape);
438
439    if (true) // isClientObject())
440    {
441       shape_inst->cloneMaterialList();
442
443       // restore the material tags to original form
444       if (mDataBlock->txr_tag_remappings.size() > 0)
445       {
446          TSMaterialList* mat_list = mDataBlock->shape->materialList;
447          if (mat_list)
448          {
449             for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
450             {
451                afxModelData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
452                Vector<String> & mat_names = (Vector<String>&) mat_list->getMaterialNameList();
453                for (S32 j = 0; j < mat_names.size(); j++) 
454                {
455                   if (mat_names[j].compare(remap->new_tag, dStrlen(remap->new_tag)) == 0)
456                   {
457                      //Con::printf("UNREMAP TEXTURE TAG [%s] TO [%s]", remap->new_tag, remap->old_tag);
458                      mat_names[j] = String(remap->old_tag);
459                      break;
460                   }
461                }
462             }
463          }
464       }
465    }
466
467    if (mDataBlock->sequence == ST_NULLSTRING)
468    {
469      main_seq_thread = 0;
470      main_seq_id = -1;
471    }
472    else
473    {
474      // here we start the default animation sequence
475      TSShape* shape = shape_inst->getShape();
476      main_seq_id = shape->findSequence(mDataBlock->sequence);
477      if (main_seq_id != -1)
478      {      
479        main_seq_thread = shape_inst->addThread();
480      
481        F32 seq_pos = 0.0f;
482        if (mDataBlock->seq_offset > 0.0f && mDataBlock->seq_offset < shape_inst->getDuration(main_seq_thread))
483          seq_pos = mDataBlock->seq_offset / shape_inst->getDuration(main_seq_thread);
484
485        shape_inst->setTimeScale(main_seq_thread, seq_rate_factor*mDataBlock->seq_rate);
486        shape_inst->setSequence(main_seq_thread, main_seq_id, seq_pos);
487        seq_animates_vis = shape->sequences[main_seq_id].visMatters.testAll();
488      }
489    }
490
491    // deal with material changes
492    if (shape_inst && (mDataBlock->force_on_material_flags | mDataBlock->force_off_material_flags))
493    {
494      shape_inst->cloneMaterialList();
495      TSMaterialList* mats = shape_inst->getMaterialList();
496      if (mDataBlock->force_on_material_flags != 0)
497      {
498        for (U32 i = 0; i < mats->size(); i++)
499          mats->setFlags(i, mats->getFlags(i) | mDataBlock->force_on_material_flags);
500      }
501
502      if (mDataBlock->force_off_material_flags != 0)
503      {
504        for (U32 i = 0; i < mats->size(); i++)
505          mats->setFlags(i, mats->getFlags(i) & ~mDataBlock->force_off_material_flags);
506      }
507    }
508  }
509
510  resetWorldBox();
511
512  if (mDataBlock->shape)
513  {
514    // Scan out the collision hulls...
515    static const String sCollisionStr( "collision-" );
516
517    for (U32 i = 0; i < mDataBlock->shape->details.size(); i++)
518    {
519      const String &name = mDataBlock->shape->names[mDataBlock->shape->details[i].nameIndex];
520
521      if (name.compare( sCollisionStr, sCollisionStr.length(), String::NoCase ) == 0)
522      {
523        mCollisionDetails.push_back(i);
524
525        // The way LOS works is that it will check to see if there is a LOS detail that matches
526        // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in
527        // the future). If it can't find a matching LOS it will simply use the collision instead.
528        // We check for any "unmatched" LOS's further down
529        mLOSDetails.increment();
530
531        char buff[128];
532        dSprintf(buff, sizeof(buff), "LOS-%d", i + 1 + 8/*MaxCollisionShapes*/);
533        U32 los = mDataBlock->shape->findDetail(buff);
534        if (los == -1)
535          mLOSDetails.last() = i;
536        else
537          mLOSDetails.last() = los;
538      }
539    }
540
541    // Snag any "unmatched" LOS details
542    static const String sLOSStr( "LOS-" );
543
544    for (U32 i = 0; i < mDataBlock->shape->details.size(); i++)
545    {
546      const String &name = mDataBlock->shape->names[mDataBlock->shape->details[i].nameIndex];
547
548      if (name.compare( sLOSStr, sLOSStr.length(), String::NoCase ) == 0)
549      {
550        // See if we already have this LOS
551        bool found = false;
552        for (U32 j = 0; j < mLOSDetails.size(); j++)
553        {
554          if (mLOSDetails[j] == i)
555          {
556            found = true;
557            break;
558          }
559        }
560
561        if (!found)
562          mLOSDetails.push_back(i);
563      }
564    }
565
566    // Compute the hull accelerators (actually, just force the shape to compute them)
567    for (U32 i = 0; i < mCollisionDetails.size(); i++)
568      shape_inst->getShape()->getAccelerator(mCollisionDetails[i]);
569  }
570
571  // tell engine the model exists
572  gClientSceneGraph->addObjectToScene(this);
573  removeFromProcessList();
574  ClientProcessList::get()->addObject(this);
575  conn->addObject(this);
576
577  return true;
578}
579
580void afxModel::onRemove()
581{
582  mSceneManager->removeObjectFromScene(this);
583  getContainer()->removeObject(this);
584  Parent::onRemove();
585}
586
587//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
588
589void afxModel::advanceTime(F32 dt)
590{
591  if (main_seq_thread)
592    shape_inst->advanceTime(dt, main_seq_thread);
593
594  for (S32 i = 0; i < blend_clips.size(); i++)
595    shape_inst->advanceTime(dt, blend_clips[i].thread);
596}
597
598//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
599
600void afxModel::prepRenderImage(SceneRenderState* state)
601{
602  if (!is_visible || !shape_inst)
603    return;
604  
605  // calculate distance to camera
606  Point3F cameraOffset;
607  getRenderTransform().getColumn(3, &cameraOffset);
608  cameraOffset -= state->getCameraPosition();   
609  F32 dist = cameraOffset.len();
610  if (dist < 0.01f)
611    dist = 0.01f;
612
613  F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
614  shape_inst->setDetailFromDistance(state, dist*invScale);
615  if ( shape_inst->getCurrentDetail() < 0 )
616    return;
617
618  renderObject(state);
619}
620
621bool afxModel::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
622{
623  if (shape_inst)
624  {
625    RayInfo shortest;
626    shortest.t = 1e8;
627
628    info->object = NULL;
629    if (mLOSDetails.size() > 0)
630    {
631      for (U32 i = 0; i < mLOSDetails.size(); i++)
632      {
633        shape_inst->animate(mLOSDetails[i]);
634        if (shape_inst->castRay(start, end, info, mLOSDetails[i]))
635        {
636          info->object = this;
637          if (info->t < shortest.t)
638            shortest = *info;
639        }
640      }
641    }
642    else
643    {
644      if (mCollisionDetails.size() > 0)
645      {
646        for (U32 i = 0; i < mCollisionDetails.size(); i++)
647        {
648          shape_inst->animate(mCollisionDetails[i]);
649          if (shape_inst->castRay(start, end, info, mCollisionDetails[i]))
650          {
651            info->object = this;
652            if (info->t < shortest.t)
653              shortest = *info;
654          }
655        }
656      }
657    }
658
659    if (info->object == this) 
660    {
661      // Copy out the shortest time...
662      *info = shortest;
663      return true;
664    }
665  }
666
667  return false;
668}
669
670U32 afxModel::unique_anim_tag_counter = 1;
671#define BAD_ANIM_ID  999999999
672
673U32 afxModel::setAnimClip(const char* clip, F32 pos, F32 rate, F32 trans)
674{
675  if (!shape_inst)
676    return 0;
677
678  TSShape* shape = shape_inst->getShape();
679
680  S32 seq_id = shape->findSequence(clip);
681  if (seq_id == -1)
682  {
683    Con::errorf("afxModel::setAnimClip() -- failed to find a sequence matching the name, \"%s\".", clip);
684    return 0;
685  }
686
687  // JTF Note: test if this blend implementation is working
688  if (shape->sequences[seq_id].isBlend())
689  {
690    BlendThread blend_clip;
691    blend_clip.tag = ((unique_anim_tag_counter++) | 0x80000000);
692
693    blend_clip.thread = shape_inst->addThread();
694    shape_inst->setSequence(blend_clip.thread, seq_id, pos);
695    shape_inst->setTimeScale(blend_clip.thread, rate);
696
697    blend_clips.push_back(blend_clip);
698
699    return blend_clip.tag;
700  }
701
702  if (!main_seq_thread)
703  {
704    main_seq_thread = shape_inst->addThread();
705    shape_inst->setTimeScale(main_seq_thread, seq_rate_factor*rate);
706    shape_inst->setSequence(main_seq_thread, seq_id, pos);
707    seq_animates_vis = shape->sequences[seq_id].visMatters.testAll();
708  }
709  else
710  {
711    shape_inst->setTimeScale(main_seq_thread, seq_rate_factor*rate);
712
713    F32 transTime = (trans < 0) ? 0.25 : trans;
714    if (transTime > 0.0f)
715      shape_inst->transitionToSequence(main_seq_thread, seq_id, pos, transTime, true);
716    else
717      shape_inst->setSequence(main_seq_thread, seq_id, pos);
718
719    seq_animates_vis = shape->sequences[seq_id].visMatters.testAll();
720  }
721
722  last_anim_tag = unique_anim_tag_counter++;
723
724  return last_anim_tag;
725}
726
727void afxModel::resetAnimation(U32 tag)
728{
729  // check if this is a blended clip
730  if ((tag & 0x80000000) != 0)
731  {
732    for (S32 i = 0; i < blend_clips.size(); i++)
733    {
734      if (blend_clips[i].tag == tag)
735      {
736        if (blend_clips[i].thread)
737        {
738          //Con::printf("DESTROY THREAD %d of %d tag=%d" , i, blend_clips.size(), tag & 0x7fffffff);
739          shape_inst->destroyThread(blend_clips[i].thread);
740        }
741        blend_clips.erase_fast(i);
742        break;
743      }
744    }
745    return;  
746  }
747
748  if (tag != 0 && tag == last_anim_tag)
749  {
750    // restore original non-animated state
751    if (main_seq_id == -1)
752    {
753      shape_inst->destroyThread(main_seq_thread);
754      main_seq_thread = 0;
755    }
756    // restore original sequence
757    else
758    {
759      shape_inst->setTimeScale(main_seq_thread, seq_rate_factor*mDataBlock->seq_rate);
760      shape_inst->transitionToSequence(main_seq_thread, main_seq_id , 0.0f, 0.25f, true);
761    }
762    last_anim_tag = 0;
763  }
764}
765
766F32 afxModel::getAnimClipDuration(const char* clip)
767{
768  if (!shape_inst)
769    return 0.0f;
770
771  TSShape* shape = shape_inst->getShape();
772  S32 seq_id = shape->findSequence(clip);
773  return (seq_id != -1) ? shape->sequences[seq_id].duration : 0.0f;
774}
775
776//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
777