afxModel.cpp 25 KB

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