ShapeAsset.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _SHAPE_ASSET_H_
  23. #include "ShapeAsset.h"
  24. #endif
  25. #ifndef _ASSET_MANAGER_H_
  26. #include "assets/assetManager.h"
  27. #endif
  28. #ifndef _CONSOLETYPES_H_
  29. #include "console/consoleTypes.h"
  30. #endif
  31. #ifndef _TAML_
  32. #include "persistence/taml/taml.h"
  33. #endif
  34. #ifndef _ASSET_PTR_H_
  35. #include "assets/assetPtr.h"
  36. #endif
  37. #include "core/resourceManager.h"
  38. // Debug Profiling.
  39. #include "platform/profiler.h"
  40. #include "T3D/assets/assetImporter.h"
  41. #ifdef TORQUE_TOOLS
  42. #include "ts/tsLastDetail.h"
  43. #endif
  44. #include "util/imposterCapture.h"
  45. #include "ts/tsShapeInstance.h"
  46. #include "gfx/bitmap/imageUtils.h"
  47. StringTableEntry ShapeAsset::smNoShapeAssetFallback = NULL;
  48. //-----------------------------------------------------------------------------
  49. IMPLEMENT_CONOBJECT(ShapeAsset);
  50. //-----------------------------------------------------------------------------
  51. // REFACTOR
  52. //-----------------------------------------------------------------------------
  53. IMPLEMENT_STRUCT(AssetPtr<ShapeAsset>, AssetPtrShapeAsset, , "")
  54. END_IMPLEMENT_STRUCT
  55. ConsoleType(ShapeAssetPtr, TypeShapeAssetPtr, AssetPtr<ShapeAsset>, ASSET_ID_FIELD_PREFIX)
  56. ConsoleGetType(TypeShapeAssetPtr)
  57. {
  58. // Fetch asset Id.
  59. return (*((AssetPtr<ShapeAsset>*)dptr)).getAssetId();
  60. }
  61. ConsoleSetType(TypeShapeAssetPtr)
  62. {
  63. // Was a single argument specified?
  64. if (argc == 1)
  65. {
  66. // Yes, so fetch field value.
  67. const char* pFieldValue = argv[0];
  68. // Fetch asset pointer.
  69. AssetPtr<ShapeAsset>* pAssetPtr = dynamic_cast<AssetPtr<ShapeAsset>*>((AssetPtrBase*)(dptr));
  70. // Is the asset pointer the correct type?
  71. if (pAssetPtr == NULL)
  72. {
  73. Con::warnf("(TypeShapeAssetPtr) - Failed to set asset Id '%d'.", pFieldValue);
  74. return;
  75. }
  76. // Set asset.
  77. pAssetPtr->setAssetId(pFieldValue);
  78. return;
  79. }
  80. // Warn.
  81. Con::warnf("(TypeShapeAssetPtr) - Cannot set multiple args to a single asset.");
  82. }
  83. //-----------------------------------------------------------------------------
  84. ConsoleType(assetIdString, TypeShapeAssetId, const char*, ASSET_ID_FIELD_PREFIX)
  85. ConsoleGetType(TypeShapeAssetId)
  86. {
  87. // Fetch asset Id.
  88. return *((const char**)(dptr));
  89. }
  90. ConsoleSetType(TypeShapeAssetId)
  91. {
  92. // Was a single argument specified?
  93. if (argc == 1)
  94. {
  95. // Yes, so fetch field value.
  96. *((const char**)dptr) = StringTable->insert(argv[0]);
  97. return;
  98. }
  99. // Warn.
  100. Con::warnf("(TypeAssetId) - Cannot set multiple args to a single asset.");
  101. }
  102. //-----------------------------------------------------------------------------
  103. // REFACTOR END
  104. //-----------------------------------------------------------------------------
  105. const String ShapeAsset::mErrCodeStrings[] =
  106. {
  107. "TooManyVerts",
  108. "TooManyBones",
  109. "MissingAnimatons",
  110. "UnKnown"
  111. };
  112. //-----------------------------------------------------------------------------
  113. ShapeAsset::ShapeAsset()
  114. {
  115. mShapeFile = StringTable->EmptyString();
  116. mConstructorFileName = StringTable->EmptyString();
  117. mDiffuseImposterFileName = StringTable->EmptyString();
  118. mNormalImposterFileName = StringTable->EmptyString();
  119. mLoadedState = AssetErrCode::NotLoaded;
  120. }
  121. //-----------------------------------------------------------------------------
  122. ShapeAsset::~ShapeAsset()
  123. {
  124. }
  125. //-----------------------------------------------------------------------------
  126. void ShapeAsset::consoleInit()
  127. {
  128. Parent::consoleInit();
  129. Con::addVariable("$Core::NoShapeAssetFallback", TypeString, &smNoShapeAssetFallback,
  130. "The assetId of the shape to display when the requested shape asset is missing.\n"
  131. "@ingroup GFX\n");
  132. smNoShapeAssetFallback = StringTable->insert(Con::getVariable("$Core::NoShapeAssetFallback"));
  133. }
  134. //-----------------------------------------------------------------------------
  135. void ShapeAsset::initPersistFields()
  136. {
  137. docsURL;
  138. // Call parent.
  139. Parent::initPersistFields();
  140. addProtectedField("fileName", TypeAssetLooseFilePath, Offset(mShapeFile, ShapeAsset),
  141. &setShapeFile, &getShapeFile, "Path to the shape file we want to render");
  142. addProtectedField("constuctorFileName", TypeAssetLooseFilePath, Offset(mConstructorFileName, ShapeAsset),
  143. &setShapeConstructorFile, &getShapeConstructorFile, "Path to the shape file we want to render");
  144. addProtectedField("diffuseImposterFileName", TypeAssetLooseFilePath, Offset(mDiffuseImposterFileName, ShapeAsset),
  145. &setDiffuseImposterFile, &getDiffuseImposterFile, "Path to the diffuse imposter file we want to render");
  146. addProtectedField("normalImposterFileName", TypeAssetLooseFilePath, Offset(mNormalImposterFileName, ShapeAsset),
  147. &setNormalImposterFile, &getNormalImposterFile, "Path to the normal imposter file we want to render");
  148. }
  149. void ShapeAsset::setDataField(StringTableEntry slotName, StringTableEntry array, StringTableEntry value)
  150. {
  151. Parent::setDataField(slotName, array, value);
  152. //Now, if it's a material slot of some fashion, set it up
  153. StringTableEntry matSlotName = StringTable->insert("materialAsset");
  154. if (String(slotName).startsWith(matSlotName))
  155. {
  156. StringTableEntry matId = StringTable->insert(value);
  157. mMaterialAssetIds.push_back(matId);
  158. }
  159. }
  160. void ShapeAsset::initializeAsset()
  161. {
  162. // Call parent.
  163. Parent::initializeAsset();
  164. if (mShapeFile == StringTable->EmptyString())
  165. return;
  166. ResourceManager::get().getChangedSignal().notify(this, &ShapeAsset::_onResourceChanged);
  167. //Ensure our path is expando'd if it isn't already
  168. mShapeFile = getOwned() ? expandAssetFilePath(mShapeFile) : mShapeFile;
  169. mConstructorFileName = getOwned() ? expandAssetFilePath(mConstructorFileName) : mConstructorFileName;
  170. if (!Torque::FS::IsFile(mConstructorFileName))
  171. Con::errorf("ShapeAsset::initializeAsset (%s) could not find %s!", getAssetName(), mConstructorFileName);
  172. mDiffuseImposterFileName = getOwned() ? expandAssetFilePath(mDiffuseImposterFileName) : mDiffuseImposterFileName;
  173. if (mDiffuseImposterFileName == StringTable->EmptyString())
  174. {
  175. String diffusePath = String(mShapeFile) + "_imposter.dds";
  176. mDiffuseImposterFileName = StringTable->insert(diffusePath.c_str());
  177. }
  178. mNormalImposterFileName = getOwned() ? expandAssetFilePath(mNormalImposterFileName) : mNormalImposterFileName;
  179. if (mNormalImposterFileName == StringTable->EmptyString())
  180. {
  181. String normalPath = String(mShapeFile) + "_imposter_normals.dds";
  182. mNormalImposterFileName = StringTable->insert(normalPath.c_str());
  183. }
  184. }
  185. void ShapeAsset::setShapeFile(const char* pShapeFile)
  186. {
  187. // Sanity!
  188. AssertFatal(pShapeFile != NULL, "Cannot use a NULL shape file.");
  189. // Fetch image file.
  190. pShapeFile = StringTable->insert(pShapeFile, true);
  191. // Ignore no change,
  192. if (pShapeFile == mShapeFile)
  193. return;
  194. mShapeFile = getOwned() ? expandAssetFilePath(pShapeFile) : pShapeFile;
  195. // Refresh the asset.
  196. refreshAsset();
  197. }
  198. void ShapeAsset::setShapeConstructorFile(const char* pShapeConstructorFile)
  199. {
  200. // Sanity!
  201. AssertFatal(pShapeConstructorFile != NULL, "Cannot use a NULL shape constructor file.");
  202. // Fetch image file.
  203. pShapeConstructorFile = StringTable->insert(pShapeConstructorFile, true);
  204. // Ignore no change,
  205. if (pShapeConstructorFile == mConstructorFileName)
  206. return;
  207. mConstructorFileName = getOwned() ? expandAssetFilePath(pShapeConstructorFile) : pShapeConstructorFile;
  208. // Refresh the asset.
  209. refreshAsset();
  210. }
  211. void ShapeAsset::setDiffuseImposterFile(const char* pImageFile)
  212. {
  213. // Sanity!
  214. AssertFatal(pImageFile != NULL, "Cannot use a NULL image file.");
  215. // Fetch image file.
  216. pImageFile = StringTable->insert(pImageFile, true);
  217. // Ignore no change,
  218. if (pImageFile == mDiffuseImposterFileName)
  219. return;
  220. mDiffuseImposterFileName = getOwned() ? expandAssetFilePath(pImageFile) : pImageFile;
  221. // Refresh the asset.
  222. refreshAsset();
  223. }
  224. void ShapeAsset::setNormalImposterFile(const char* pImageFile)
  225. {
  226. // Sanity!
  227. AssertFatal(pImageFile != NULL, "Cannot use a NULL image file.");
  228. // Fetch image file.
  229. pImageFile = StringTable->insert(pImageFile, true);
  230. // Ignore no change,
  231. if (pImageFile == mNormalImposterFileName)
  232. return;
  233. mNormalImposterFileName = getOwned() ? expandAssetFilePath(pImageFile) : pImageFile;
  234. // Refresh the asset.
  235. refreshAsset();
  236. }
  237. void ShapeAsset::_onResourceChanged(const Torque::Path &path)
  238. {
  239. if (path != Torque::Path(mShapeFile) )
  240. return;
  241. refreshAsset();
  242. }
  243. U32 ShapeAsset::load()
  244. {
  245. if (mLoadedState == AssetErrCode::Ok) return mLoadedState;
  246. mMaterialAssets.clear();
  247. mMaterialAssetIds.clear();
  248. //First, load any material, animation, etc assets we may be referencing in our asset
  249. // Find any asset dependencies.
  250. AssetManager::typeAssetDependsOnHash::Iterator assetDependenciesItr = mpOwningAssetManager->getDependedOnAssets()->find(mpAssetDefinition->mAssetId);
  251. // Does the asset have any dependencies?
  252. if (assetDependenciesItr != mpOwningAssetManager->getDependedOnAssets()->end())
  253. {
  254. // Iterate all dependencies.
  255. while (assetDependenciesItr != mpOwningAssetManager->getDependedOnAssets()->end() && assetDependenciesItr->key == mpAssetDefinition->mAssetId)
  256. {
  257. StringTableEntry assetType = mpOwningAssetManager->getAssetType(assetDependenciesItr->value);
  258. if (assetType == StringTable->insert("MaterialAsset"))
  259. {
  260. mMaterialAssetIds.push_front(assetDependenciesItr->value);
  261. //Force the asset to become initialized if it hasn't been already
  262. AssetPtr<MaterialAsset> matAsset = assetDependenciesItr->value;
  263. mMaterialAssets.push_front(matAsset);
  264. }
  265. else if (assetType == StringTable->insert("ShapeAnimationAsset"))
  266. {
  267. mAnimationAssetIds.push_back(assetDependenciesItr->value);
  268. //Force the asset to become initialized if it hasn't been already
  269. AssetPtr<ShapeAnimationAsset> animAsset = assetDependenciesItr->value;
  270. mAnimationAssets.push_back(animAsset);
  271. }
  272. // Next dependency.
  273. assetDependenciesItr++;
  274. }
  275. }
  276. mShape = ResourceManager::get().load(mShapeFile);
  277. if (!mShape)
  278. {
  279. mLoadedState = BadFileReference;
  280. return mLoadedState; //if it failed to load, bail out
  281. }
  282. // Construct billboards if not done already
  283. if (GFXDevice::devicePresent())
  284. mShape->setupBillboardDetails(mShapeFile, mDiffuseImposterFileName, mNormalImposterFileName);
  285. //If they exist, grab our imposters here and bind them to our shapeAsset
  286. bool hasBlends = false;
  287. //Now that we've successfully loaded our shape and have any materials and animations loaded
  288. //we need to set up the animations we're using on our shape
  289. for (S32 i = mAnimationAssets.size()-1; i >= 0; --i)
  290. {
  291. String srcName = mAnimationAssets[i]->getAnimationName();
  292. String srcPath(mAnimationAssets[i]->getAnimationFilename());
  293. //SplitSequencePathAndName(srcPath, srcName);
  294. if (!mShape->addSequence(srcPath, srcName, srcName,
  295. mAnimationAssets[i]->getStartFrame(), mAnimationAssets[i]->getEndFrame(), mAnimationAssets[i]->getPadRotation(), mAnimationAssets[i]->getPadTransforms()))
  296. {
  297. mLoadedState = MissingAnimatons;
  298. return mLoadedState;
  299. }
  300. if (mAnimationAssets[i]->isBlend())
  301. hasBlends = true;
  302. }
  303. //if any of our animations are blends, set those up now
  304. if (hasBlends)
  305. {
  306. for (U32 i=0; i < mAnimationAssets.size(); ++i)
  307. {
  308. if (mAnimationAssets[i]->isBlend() && mAnimationAssets[i]->getBlendAnimationName() != StringTable->EmptyString())
  309. {
  310. //gotta do a bit of logic here.
  311. //First, we need to make sure the anim asset we depend on for our blend is loaded
  312. AssetPtr<ShapeAnimationAsset> blendAnimAsset = mAnimationAssets[i]->getBlendAnimationName();
  313. U32 assetStatus = ShapeAnimationAsset::getAssetErrCode(blendAnimAsset);
  314. if (assetStatus != AssetBase::Ok)
  315. {
  316. Con::errorf("ShapeAsset::initializeAsset - Unable to acquire reference animation asset %s for asset %s to blend!", mAnimationAssets[i]->getBlendAnimationName(), mAnimationAssets[i]->getAssetName());
  317. {
  318. mLoadedState = MissingAnimatons;
  319. return mLoadedState;
  320. }
  321. }
  322. String refAnimName = blendAnimAsset->getAnimationName();
  323. if (!mShape->setSequenceBlend(mAnimationAssets[i]->getAnimationName(), true, blendAnimAsset->getAnimationName(), mAnimationAssets[i]->getBlendFrame()))
  324. {
  325. Con::errorf("ShapeAnimationAsset::initializeAsset - Unable to set animation clip %s for asset %s to blend!", mAnimationAssets[i]->getAnimationName(), mAnimationAssets[i]->getAssetName());
  326. {
  327. mLoadedState = MissingAnimatons;
  328. return mLoadedState;
  329. }
  330. }
  331. }
  332. }
  333. }
  334. mLoadedState = Ok;
  335. return mLoadedState;
  336. }
  337. //------------------------------------------------------------------------------
  338. //Utility function to 'fill out' bindings and resources with a matching asset if one exists
  339. U32 ShapeAsset::getAssetByFilename(StringTableEntry fileName, AssetPtr<ShapeAsset>* shapeAsset)
  340. {
  341. AssetQuery query;
  342. S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, fileName);
  343. if (foundAssetcount == 0)
  344. {
  345. //Didn't work, so have us fall back to a placeholder asset
  346. shapeAsset->setAssetId(ShapeAsset::smNoShapeAssetFallback);
  347. if (shapeAsset->isNull())
  348. {
  349. //Well that's bad, loading the fallback failed.
  350. Con::warnf("ShapeAsset::getAssetByFilename - Finding of asset associated with file %s failed with no fallback asset", fileName);
  351. return AssetErrCode::Failed;
  352. }
  353. //handle noshape not being loaded itself
  354. if ((*shapeAsset)->mLoadedState == BadFileReference)
  355. {
  356. Con::warnf("ShapeAsset::getAssetByFilename - Finding of associated with file %s failed, and fallback asset reported error of Bad File Reference.", fileName);
  357. return AssetErrCode::BadFileReference;
  358. }
  359. Con::warnf("ShapeAsset::getAssetByFilename - Finding of associated with file %s failed, utilizing fallback asset", fileName);
  360. (*shapeAsset)->mLoadedState = AssetErrCode::UsingFallback;
  361. return AssetErrCode::UsingFallback;
  362. }
  363. else
  364. {
  365. //acquire and bind the asset, and return it out
  366. shapeAsset->setAssetId(query.mAssetList[0]);
  367. return (*shapeAsset)->mLoadedState;
  368. }
  369. }
  370. StringTableEntry ShapeAsset::getAssetIdByFilename(StringTableEntry fileName)
  371. {
  372. if (fileName == StringTable->EmptyString())
  373. return StringTable->EmptyString();
  374. StringTableEntry shapeAssetId = ShapeAsset::smNoShapeAssetFallback;
  375. AssetQuery query;
  376. S32 foundAssetcount = AssetDatabase.findAssetLooseFile(&query, fileName);
  377. if (foundAssetcount != 0)
  378. {
  379. //acquire and bind the asset, and return it out
  380. shapeAssetId = query.mAssetList[0];
  381. }
  382. else
  383. {
  384. foundAssetcount = AssetDatabase.findAssetType(&query, "ShapeAsset");
  385. if (foundAssetcount != 0)
  386. {
  387. // loop all image assets and see if we can find one
  388. // using the same image file/named target.
  389. for (auto shapeAsset : query.mAssetList)
  390. {
  391. AssetPtr<ShapeAsset> temp = shapeAsset;
  392. if (temp.notNull())
  393. {
  394. if (temp->getShapeFile() == fileName)
  395. {
  396. return shapeAsset;
  397. }
  398. else
  399. {
  400. Torque::Path temp1 = temp->getShapeFile();
  401. Torque::Path temp2 = fileName;
  402. if (temp1.getPath() == temp2.getPath() && temp1.getFileName() == temp2.getFileName())
  403. {
  404. return shapeAsset;
  405. }
  406. }
  407. }
  408. }
  409. }
  410. else
  411. {
  412. AssetPtr<ShapeAsset> shapeAsset = shapeAssetId; //ensures the fallback is loaded
  413. }
  414. }
  415. return shapeAssetId;
  416. }
  417. U32 ShapeAsset::getAssetById(StringTableEntry assetId, AssetPtr<ShapeAsset>* shapeAsset)
  418. {
  419. (*shapeAsset) = assetId;
  420. if (shapeAsset->notNull())
  421. {
  422. return (*shapeAsset)->mLoadedState;
  423. }
  424. else
  425. {
  426. //Didn't work, so have us fall back to a placeholder asset
  427. shapeAsset->setAssetId(ShapeAsset::smNoShapeAssetFallback);
  428. if (shapeAsset->isNull())
  429. {
  430. //Well that's bad, loading the fallback failed.
  431. Con::warnf("ShapeAsset::getAssetById - Finding of asset with id %s failed with no fallback asset", assetId);
  432. return AssetErrCode::Failed;
  433. }
  434. //handle noshape not being loaded itself
  435. if ((*shapeAsset)->mLoadedState == BadFileReference)
  436. {
  437. Con::warnf("ShapeAsset::getAssetById - Finding of asset with id %s failed, and fallback asset reported error of Bad File Reference.", assetId);
  438. return AssetErrCode::BadFileReference;
  439. }
  440. Con::warnf("ShapeAsset::getAssetById - Finding of asset with id %s failed, utilizing fallback asset", assetId);
  441. (*shapeAsset)->mLoadedState = AssetErrCode::UsingFallback;
  442. return AssetErrCode::UsingFallback;
  443. }
  444. }
  445. //------------------------------------------------------------------------------
  446. void ShapeAsset::copyTo(SimObject* object)
  447. {
  448. // Call to parent.
  449. Parent::copyTo(object);
  450. }
  451. void ShapeAsset::onAssetRefresh(void)
  452. {
  453. // Ignore if not yet added to the sim.
  454. if (!isProperlyAdded())
  455. return;
  456. if (mShapeFile == StringTable->EmptyString())
  457. return;
  458. // Call parent.
  459. Parent::onAssetRefresh();
  460. load();
  461. }
  462. void ShapeAsset::onTamlPreWrite(void)
  463. {
  464. // Call parent.
  465. Parent::onTamlPreWrite();
  466. // ensure paths are collapsed.
  467. mShapeFile = collapseAssetFilePath(mShapeFile);
  468. mConstructorFileName = collapseAssetFilePath(mConstructorFileName);
  469. mDiffuseImposterFileName = collapseAssetFilePath(mDiffuseImposterFileName);
  470. mNormalImposterFileName = collapseAssetFilePath(mNormalImposterFileName);
  471. }
  472. void ShapeAsset::onTamlPostWrite(void)
  473. {
  474. // Call parent.
  475. Parent::onTamlPostWrite();
  476. // ensure paths are expanded.
  477. mShapeFile = expandAssetFilePath(mShapeFile);
  478. mConstructorFileName = expandAssetFilePath(mConstructorFileName);
  479. mDiffuseImposterFileName = expandAssetFilePath(mDiffuseImposterFileName);
  480. mNormalImposterFileName = expandAssetFilePath(mNormalImposterFileName);
  481. }
  482. void ShapeAsset::SplitSequencePathAndName(String& srcPath, String& srcName)
  483. {
  484. srcName = "";
  485. // Determine if there is a sequence name at the end of the source string, and
  486. // if so, split the filename from the sequence name
  487. S32 split = srcPath.find(' ', 0, String::Right);
  488. S32 split2 = srcPath.find('\t', 0, String::Right);
  489. if ((split == String::NPos) || (split2 > split))
  490. split = split2;
  491. if (split != String::NPos)
  492. {
  493. split2 = split + 1;
  494. while ((srcPath[split2] != '\0') && dIsspace(srcPath[split2]))
  495. split2++;
  496. // now 'split' is at the end of the path, and 'split2' is at the start of the sequence name
  497. srcName = srcPath.substr(split2);
  498. srcPath = srcPath.erase(split, srcPath.length() - split);
  499. }
  500. }
  501. ShapeAnimationAsset* ShapeAsset::getAnimation(S32 index)
  502. {
  503. if (index < mAnimationAssets.size())
  504. {
  505. return mAnimationAssets[index];
  506. }
  507. return nullptr;
  508. }
  509. #ifdef TORQUE_TOOLS
  510. const char* ShapeAsset::generateCachedPreviewImage(S32 resolution, String overrideMaterial)
  511. {
  512. if (!mShape)
  513. return "";
  514. // We're gonna render... make sure we can.
  515. bool sceneBegun = GFX->canCurrentlyRender();
  516. if (!sceneBegun)
  517. GFX->beginScene();
  518. // We need to create our own instance to render with.
  519. TSShapeInstance* shape = new TSShapeInstance(mShape, true);
  520. if (overrideMaterial.isNotEmpty())
  521. {
  522. Material *tMat = dynamic_cast<Material*>(Sim::findObject(overrideMaterial));
  523. if (tMat)
  524. shape->reSkin(tMat->mMapTo, mShape->materialList->getMaterialName(0));
  525. }
  526. // Animate the shape once.
  527. shape->animate(0);
  528. GBitmap* imposter = NULL;
  529. GBitmap* imposterNrml = NULL;
  530. ImposterCapture* imposterCap = new ImposterCapture();
  531. static const MatrixF topXfm(EulerF(-M_PI_F / 2.0f, 0, 0));
  532. static const MatrixF bottomXfm(EulerF(M_PI_F / 2.0f, 0, 0));
  533. MatrixF angMat;
  534. PROFILE_START(ShapeAsset_generateCachedPreviewImage);
  535. //dMemset(destBmp.getWritableBits(mip), 0, destBmp.getWidth(mip) * destBmp.getHeight(mip) * GFXFormat_getByteSize(format));
  536. F32 rotX = -(mDegToRad(60.0) - 0.5f * M_PI_F);
  537. F32 rotZ = -(mDegToRad(45.0) - 0.5f * M_PI_F);
  538. // We capture the images in a particular order which must
  539. // match the order expected by the imposter renderer.
  540. imposterCap->begin(shape, 0, resolution, mShape->mRadius, mShape->center);
  541. angMat.mul(MatrixF(EulerF(rotX, 0, 0)),
  542. MatrixF(EulerF(0, 0, rotZ)));
  543. imposterCap->capture(angMat, &imposter, &imposterNrml);
  544. imposterCap->end();
  545. PROFILE_END(); // ShapeAsset_generateCachedPreviewImage
  546. delete imposterCap;
  547. delete shape;
  548. String dumpPath = String(mShapeFile) + ".png";
  549. char* returnBuffer = Con::getReturnBuffer(128);
  550. dSprintf(returnBuffer, 128, "%s", dumpPath.c_str());
  551. imposter->writeBitmap("png", dumpPath);
  552. delete imposter;
  553. delete imposterNrml;
  554. // If we did a begin then end it now.
  555. if (!sceneBegun)
  556. GFX->endScene();
  557. return returnBuffer;
  558. }
  559. #endif
  560. DefineEngineMethod(ShapeAsset, getMaterialCount, S32, (), ,
  561. "Gets the number of materials for this shape asset.\n"
  562. "@return Material count.\n")
  563. {
  564. return object->getMaterialCount();
  565. }
  566. DefineEngineMethod(ShapeAsset, getAnimationCount, S32, (), ,
  567. "Gets the number of animations for this shape asset.\n"
  568. "@return Animation count.\n")
  569. {
  570. return object->getAnimationCount();
  571. }
  572. DefineEngineMethod(ShapeAsset, getAnimation, ShapeAnimationAsset*, (S32 index), (0),
  573. "Gets a particular shape animation asset for this shape.\n"
  574. "@param animation asset index.\n"
  575. "@return Shape Animation Asset.\n")
  576. {
  577. return object->getAnimation(index);
  578. }
  579. DefineEngineMethod(ShapeAsset, getShapePath, const char*, (), ,
  580. "Gets the shape's file path\n"
  581. "@return The filename of the shape file")
  582. {
  583. return object->getShapeFile();
  584. }
  585. DefineEngineMethod(ShapeAsset, getShapeConstructorFilePath, const char*, (), ,
  586. "Gets the shape's constructor file.\n"
  587. "@return The filename of the shape constructor file")
  588. {
  589. return object->getShapeConstructorFile();
  590. }
  591. DefineEngineMethod(ShapeAsset, getStatusString, String, (), , "get status string")\
  592. {
  593. return ShapeAsset::getAssetErrstrn(object->getStatus());
  594. }
  595. #ifdef TORQUE_TOOLS
  596. DefineEngineMethod(ShapeAsset, generateCachedPreviewImage, const char*, (S32 resolution, const char* overrideMaterialName), (256, ""),
  597. "Generates a baked preview image of the given shapeAsset. Only really used for generating Asset Browser icons.\n"
  598. "@param resolution Optional field for what resolution to bake the preview image at. Must be pow2\n"
  599. "@param overrideMaterialName Optional field for overriding the material used when rendering the shape for the bake.")
  600. {
  601. object->load();
  602. return object->generateCachedPreviewImage(resolution, overrideMaterialName);
  603. }
  604. DefineEngineStaticMethod(ShapeAsset, getAssetIdByFilename, const char*, (const char* filePath), (""),
  605. "Queries the Asset Database to see if any asset exists that is associated with the provided file path.\n"
  606. "@return The AssetId of the associated asset, if any.")
  607. {
  608. return ShapeAsset::getAssetIdByFilename(StringTable->insert(filePath));
  609. }
  610. #endif
  611. #ifdef TORQUE_TOOLS
  612. //-----------------------------------------------------------------------------
  613. // GuiInspectorTypeAssetId
  614. //-----------------------------------------------------------------------------
  615. IMPLEMENT_CONOBJECT(GuiInspectorTypeShapeAssetPtr);
  616. ConsoleDocClass(GuiInspectorTypeShapeAssetPtr,
  617. "@brief Inspector field type for Shapes\n\n"
  618. "Editor use only.\n\n"
  619. "@internal"
  620. );
  621. void GuiInspectorTypeShapeAssetPtr::consoleInit()
  622. {
  623. Parent::consoleInit();
  624. ConsoleBaseType::getType(TypeShapeAssetPtr)->setInspectorFieldType("GuiInspectorTypeShapeAssetPtr");
  625. }
  626. GuiControl* GuiInspectorTypeShapeAssetPtr::constructEditControl()
  627. {
  628. // Create base filename edit controls
  629. GuiControl* retCtrl = Parent::constructEditControl();
  630. if (retCtrl == NULL)
  631. return retCtrl;
  632. // Change filespec
  633. char szBuffer[512];
  634. const char* previewImage;
  635. if (mInspector->getInspectObject() != nullptr)
  636. {
  637. dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.showDialog(\"ShapeAsset\", \"AssetBrowser.changeAsset\", %s, %s);",
  638. mInspector->getIdString(), mCaption);
  639. mBrowseButton->setField("Command", szBuffer);
  640. setDataField(StringTable->insert("targetObject"), NULL, mInspector->getInspectObject()->getIdString());
  641. previewImage = mInspector->getInspectObject()->getDataField(mCaption, NULL);
  642. }
  643. else
  644. {
  645. //if we don't have a target object, we'll be manipulating the desination value directly
  646. dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.showDialog(\"ShapeAsset\", \"AssetBrowser.changeAsset\", %s, \"%s\");",
  647. mInspector->getIdString(), mVariableName);
  648. mBrowseButton->setField("Command", szBuffer);
  649. previewImage = Con::getVariable(mVariableName);
  650. }
  651. mLabel = new GuiTextCtrl();
  652. mLabel->registerObject();
  653. mLabel->setControlProfile(mProfile);
  654. mLabel->setText(mCaption);
  655. addObject(mLabel);
  656. //
  657. GuiTextEditCtrl* editTextCtrl = static_cast<GuiTextEditCtrl*>(retCtrl);
  658. GuiControlProfile* toolEditProfile;
  659. if (Sim::findObject("ToolsGuiTextEditProfile", toolEditProfile))
  660. editTextCtrl->setControlProfile(toolEditProfile);
  661. GuiControlProfile* toolDefaultProfile = nullptr;
  662. Sim::findObject("ToolsGuiDefaultProfile", toolDefaultProfile);
  663. //
  664. mPreviewImage = new GuiBitmapCtrl();
  665. mPreviewImage->registerObject();
  666. if (toolDefaultProfile)
  667. mPreviewImage->setControlProfile(toolDefaultProfile);
  668. updatePreviewImage();
  669. addObject(mPreviewImage);
  670. //
  671. mPreviewBorderButton = new GuiBitmapButtonCtrl();
  672. mPreviewBorderButton->registerObject();
  673. if (toolDefaultProfile)
  674. mPreviewBorderButton->setControlProfile(toolDefaultProfile);
  675. mPreviewBorderButton->_setBitmap(StringTable->insert("ToolsModule:cubemapBtnBorder_n_image"));
  676. mPreviewBorderButton->setField("Command", szBuffer); //clicking the preview does the same thing as the edit button, for simplicity
  677. addObject(mPreviewBorderButton);
  678. //
  679. // Create "Open in Editor" button
  680. mEditButton = new GuiBitmapButtonCtrl();
  681. dSprintf(szBuffer, sizeof(szBuffer), "ShapeEditorPlugin.openShapeAssetId(%d.getText());", retCtrl->getId());
  682. mEditButton->setField("Command", szBuffer);
  683. mEditButton->setText("Edit");
  684. mEditButton->setSizing(horizResizeLeft, vertResizeAspectTop);
  685. mEditButton->setDataField(StringTable->insert("Profile"), NULL, "ToolsGuiButtonProfile");
  686. mEditButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
  687. mEditButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
  688. mEditButton->setDataField(StringTable->insert("tooltip"), NULL, "Open this asset in the Shape Editor");
  689. mEditButton->registerObject();
  690. addObject(mEditButton);
  691. //
  692. mUseHeightOverride = true;
  693. mHeightOverride = 72;
  694. return retCtrl;
  695. }
  696. bool GuiInspectorTypeShapeAssetPtr::updateRects()
  697. {
  698. S32 rowSize = 18;
  699. S32 dividerPos, dividerMargin;
  700. mInspector->getDivider(dividerPos, dividerMargin);
  701. Point2I fieldExtent = getExtent();
  702. Point2I fieldPos = getPosition();
  703. mEditCtrlRect.set(0, 0, fieldExtent.x, fieldExtent.y);
  704. mLabel->resize(Point2I(mProfile->mTextOffset.x, 0), Point2I(fieldExtent.x, rowSize));
  705. RectI previewRect = RectI(Point2I(mProfile->mTextOffset.x, rowSize), Point2I(50, 50));
  706. mPreviewBorderButton->resize(previewRect.point, previewRect.extent);
  707. mPreviewImage->resize(previewRect.point, previewRect.extent);
  708. S32 editPos = previewRect.point.x + previewRect.extent.x + 10;
  709. mEdit->resize(Point2I(editPos, rowSize * 1.5), Point2I(fieldExtent.x - editPos - 5, rowSize));
  710. mEditButton->resize(Point2I(fieldExtent.x - 105, previewRect.point.y + previewRect.extent.y - rowSize), Point2I(100, rowSize));
  711. mBrowseButton->setHidden(true);
  712. return true;
  713. }
  714. void GuiInspectorTypeShapeAssetPtr::updateValue()
  715. {
  716. Parent::updateValue();
  717. updatePreviewImage();
  718. }
  719. void GuiInspectorTypeShapeAssetPtr::updatePreviewImage()
  720. {
  721. const char* previewImage;
  722. if (mInspector->getInspectObject() != nullptr)
  723. previewImage = mInspector->getInspectObject()->getDataField(mCaption, NULL);
  724. else
  725. previewImage = Con::getVariable(mVariableName);
  726. //if what we're working with isn't even a valid asset, don't present like we found a good one
  727. if (!AssetDatabase.isDeclaredAsset(previewImage))
  728. {
  729. mPreviewImage->_setBitmap(StringTable->EmptyString());
  730. return;
  731. }
  732. String shpPreviewAssetId = String(previewImage) + "_PreviewImage";
  733. shpPreviewAssetId.replace(":", "_");
  734. shpPreviewAssetId = "ToolsModule:" + shpPreviewAssetId;
  735. if (AssetDatabase.isDeclaredAsset(shpPreviewAssetId.c_str()))
  736. {
  737. mPreviewImage->setBitmap(StringTable->insert(shpPreviewAssetId.c_str()));
  738. }
  739. if (mPreviewImage->getBitmapAsset().isNull())
  740. mPreviewImage->_setBitmap(StringTable->insert("ToolsModule:genericAssetIcon_image"));
  741. }
  742. void GuiInspectorTypeShapeAssetPtr::setPreviewImage(StringTableEntry assetId)
  743. {
  744. //if what we're working with isn't even a valid asset, don't present like we found a good one
  745. if (!AssetDatabase.isDeclaredAsset(assetId))
  746. {
  747. mPreviewImage->_setBitmap(StringTable->EmptyString());
  748. return;
  749. }
  750. String shpPreviewAssetId = String(assetId) + "_PreviewImage";
  751. shpPreviewAssetId.replace(":", "_");
  752. shpPreviewAssetId = "ToolsModule:" + shpPreviewAssetId;
  753. if (AssetDatabase.isDeclaredAsset(shpPreviewAssetId.c_str()))
  754. {
  755. mPreviewImage->setBitmap(StringTable->insert(shpPreviewAssetId.c_str()));
  756. }
  757. if (mPreviewImage->getBitmapAsset().isNull())
  758. mPreviewImage->_setBitmap(StringTable->insert("ToolsModule:genericAssetIcon_image"));
  759. }
  760. IMPLEMENT_CONOBJECT(GuiInspectorTypeShapeAssetId);
  761. ConsoleDocClass(GuiInspectorTypeShapeAssetId,
  762. "@brief Inspector field type for Shapes\n\n"
  763. "Editor use only.\n\n"
  764. "@internal"
  765. );
  766. void GuiInspectorTypeShapeAssetId::consoleInit()
  767. {
  768. Parent::consoleInit();
  769. ConsoleBaseType::getType(TypeShapeAssetId)->setInspectorFieldType("GuiInspectorTypeShapeAssetId");
  770. }
  771. #endif