OgreImporter.cpp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include <Urho3D/Core/Context.h>
  4. #include <Urho3D/Core/ProcessUtils.h>
  5. #include <Urho3D/Core/StringUtils.h>
  6. #include <Urho3D/Graphics/Tangent.h>
  7. #include <Urho3D/IO/File.h>
  8. #include <Urho3D/IO/FileSystem.h>
  9. #include <Urho3D/Resource/XMLFile.h>
  10. #include "OgreImporterUtils.h"
  11. #ifdef WIN32
  12. #include <Urho3D/Engine/WinWrapped.h>
  13. #endif
  14. #include <Urho3D/DebugNew.h>
  15. static const int VERTEX_CACHE_SIZE = 32;
  16. SharedPtr<Context> context_(new Context());
  17. SharedPtr<XMLFile> meshFile_(new XMLFile(context_));
  18. SharedPtr<XMLFile> skelFile_(new XMLFile(context_));
  19. Vector<ModelIndexBuffer> indexBuffers_;
  20. Vector<ModelVertexBuffer> vertexBuffers_;
  21. Vector<Vector<ModelSubGeometryLodLevel>> subGeometries_;
  22. Vector<Vector3> subGeometryCenters_;
  23. Vector<ModelBone> bones_;
  24. Vector<ModelMorph> morphs_;
  25. Vector<String> materialNames_;
  26. BoundingBox boundingBox_;
  27. unsigned maxBones_ = 64;
  28. unsigned numSubMeshes_ = 0;
  29. bool useOneBuffer_ = true;
  30. int main(int argc, char** argv);
  31. void Run(const Vector<String>& arguments);
  32. void LoadSkeleton(const String& skeletonFileName);
  33. void LoadMesh(const String& inputFileName, bool generateTangents, bool splitSubMeshes, bool exportMorphs);
  34. void WriteOutput(const String& outputFileName, bool exportAnimations, bool rotationsOnly, bool saveMaterialList);
  35. void OptimizeIndices(ModelSubGeometryLodLevel* subGeom, ModelVertexBuffer* vb, ModelIndexBuffer* ib);
  36. void CalculateScore(ModelVertex& vertex);
  37. String SanitateAssetName(const String& name);
  38. int main(int argc, char** argv)
  39. {
  40. Vector<String> arguments;
  41. #ifdef WIN32
  42. arguments = ParseArguments(GetCommandLineW());
  43. #else
  44. arguments = ParseArguments(argc, argv);
  45. #endif
  46. Run(arguments);
  47. return 0;
  48. }
  49. void Run(const Vector<String>& arguments)
  50. {
  51. if (arguments.Size() < 2)
  52. {
  53. ErrorExit(
  54. "Usage: OgreImporter <input file> <output file> [options]\n\n"
  55. "Options:\n"
  56. "-l Output a material list file\n"
  57. "-na Do not output animations\n"
  58. "-nm Do not output morphs\n"
  59. "-r Output only rotations from animations\n"
  60. "-s Split each submesh into own vertex buffer\n"
  61. "-t Generate tangents\n"
  62. "-mb <x> Maximum number of bones per submesh, default 64\n"
  63. );
  64. }
  65. bool generateTangents = false;
  66. bool splitSubMeshes = false;
  67. bool exportAnimations = true;
  68. bool exportMorphs = true;
  69. bool rotationsOnly = false;
  70. bool saveMaterialList = false;
  71. if (arguments.Size() > 2)
  72. {
  73. for (unsigned i = 2; i < arguments.Size(); ++i)
  74. {
  75. if (arguments[i].Length() > 1 && arguments[i][0] == '-')
  76. {
  77. String argument = arguments[i].Substring(1).ToLower();
  78. if (argument == "l")
  79. saveMaterialList = true;
  80. else if (argument == "r")
  81. rotationsOnly = true;
  82. else if (argument == "s")
  83. splitSubMeshes = true;
  84. else if (argument == "t")
  85. generateTangents = true;
  86. else if (argument.Length() == 2 && argument[0] == 'n')
  87. {
  88. switch (tolower(argument[1]))
  89. {
  90. case 'a':
  91. exportAnimations = false;
  92. break;
  93. case 'm':
  94. exportMorphs = false;
  95. break;
  96. }
  97. break;
  98. }
  99. else if (argument == "mb" && i < arguments.Size() - 1)
  100. {
  101. maxBones_ = ToU32(arguments[i + 1]);
  102. if (maxBones_ < 1)
  103. maxBones_ = 1;
  104. ++i;
  105. }
  106. }
  107. }
  108. }
  109. LoadMesh(arguments[0], generateTangents, splitSubMeshes, exportMorphs);
  110. WriteOutput(arguments[1], exportAnimations, rotationsOnly, saveMaterialList);
  111. PrintLine("Finished");
  112. }
  113. void LoadSkeleton(const String& skeletonFileName)
  114. {
  115. // Process skeleton first (if found)
  116. XMLElement skeletonRoot;
  117. File skeletonFileSource(context_);
  118. skeletonFileSource.Open(skeletonFileName);
  119. if (!skelFile_->Load(skeletonFileSource))
  120. PrintLine("Failed to load skeleton " + skeletonFileName);
  121. skeletonRoot = skelFile_->GetRoot();
  122. if (skeletonRoot)
  123. {
  124. XMLElement bonesRoot = skeletonRoot.GetChild("bones");
  125. XMLElement bone = bonesRoot.GetChild("bone");
  126. while (bone)
  127. {
  128. unsigned index = bone.GetI32("id");
  129. String name = bone.GetAttribute("name");
  130. if (index >= bones_.Size())
  131. bones_.Resize(index + 1);
  132. // Convert from right- to left-handed
  133. XMLElement position = bone.GetChild("position");
  134. float x = position.GetFloat("x");
  135. float y = position.GetFloat("y");
  136. float z = position.GetFloat("z");
  137. Vector3 pos(x, y, -z);
  138. XMLElement rotation = bone.GetChild("rotation");
  139. XMLElement axis = rotation.GetChild("axis");
  140. float angle = -rotation.GetFloat("angle") * M_RADTODEG;
  141. x = axis.GetFloat("x");
  142. y = axis.GetFloat("y");
  143. z = axis.GetFloat("z");
  144. Vector3 axisVec(x, y, -z);
  145. Quaternion rot(angle, axisVec);
  146. bones_[index].name_ = name;
  147. bones_[index].parentIndex_ = index; // Fill in the correct parent later
  148. bones_[index].bindPosition_ = pos;
  149. bones_[index].bindRotation_ = rot;
  150. bones_[index].bindScale_ = Vector3::ONE;
  151. bones_[index].collisionMask_ = 0;
  152. bones_[index].radius_ = 0.0f;
  153. bone = bone.GetNext("bone");
  154. }
  155. // Go through the bone hierarchy
  156. XMLElement boneHierarchy = skeletonRoot.GetChild("bonehierarchy");
  157. XMLElement boneParent = boneHierarchy.GetChild("boneparent");
  158. while (boneParent)
  159. {
  160. String bone = boneParent.GetAttribute("bone");
  161. String parent = boneParent.GetAttribute("parent");
  162. unsigned i = 0, j = 0;
  163. for (i = 0; i < bones_.Size() && bones_[i].name_ != bone; ++i);
  164. for (j = 0; j < bones_.Size() && bones_[j].name_ != parent; ++j);
  165. if (i >= bones_.Size() || j >= bones_.Size())
  166. ErrorExit("Found indeterminate parent bone assignment");
  167. bones_[i].parentIndex_ = j;
  168. boneParent = boneParent.GetNext("boneparent");
  169. }
  170. // Calculate bone derived positions
  171. for (unsigned i = 0; i < bones_.Size(); ++i)
  172. {
  173. Vector3 derivedPosition = bones_[i].bindPosition_;
  174. Quaternion derivedRotation = bones_[i].bindRotation_;
  175. Vector3 derivedScale = bones_[i].bindScale_;
  176. unsigned index = bones_[i].parentIndex_;
  177. if (index != i)
  178. {
  179. for (;;)
  180. {
  181. derivedPosition = bones_[index].bindPosition_ + (bones_[index].bindRotation_ * (bones_[index].bindScale_ * derivedPosition));
  182. derivedRotation = bones_[index].bindRotation_ * derivedRotation;
  183. derivedScale = bones_[index].bindScale_ * derivedScale;
  184. if (bones_[index].parentIndex_ != index)
  185. index = bones_[index].parentIndex_;
  186. else
  187. break;
  188. }
  189. }
  190. bones_[i].derivedPosition_ = derivedPosition;
  191. bones_[i].derivedRotation_ = derivedRotation;
  192. bones_[i].derivedScale_ = derivedScale;
  193. bones_[i].worldTransform_ = Matrix3x4(derivedPosition, derivedRotation, derivedScale);
  194. bones_[i].inverseWorldTransform_ = bones_[i].worldTransform_.Inverse();
  195. }
  196. PrintLine("Processed skeleton");
  197. }
  198. }
  199. void LoadMesh(const String& inputFileName, bool generateTangents, bool splitSubMeshes, bool exportMorphs)
  200. {
  201. File meshFileSource(context_);
  202. meshFileSource.Open(inputFileName);
  203. if (!meshFile_->Load(meshFileSource))
  204. ErrorExit("Could not load input file " + inputFileName);
  205. XMLElement root = meshFile_->GetRoot("mesh");
  206. XMLElement subMeshes = root.GetChild("submeshes");
  207. XMLElement skeletonLink = root.GetChild("skeletonlink");
  208. if (root.IsNull())
  209. ErrorExit("Could not load input file " + inputFileName);
  210. String skeletonName = skeletonLink.GetAttribute("name");
  211. if (!skeletonName.Empty())
  212. LoadSkeleton(GetPath(inputFileName) + GetFileName(skeletonName) + ".skeleton.xml");
  213. // Check whether there's benefit of avoiding 32bit indices by splitting each submesh into own buffer
  214. XMLElement subMesh = subMeshes.GetChild("submesh");
  215. unsigned totalVertices = 0;
  216. unsigned maxSubMeshVertices = 0;
  217. while (subMesh)
  218. {
  219. materialNames_.Push(subMesh.GetAttribute("material"));
  220. XMLElement geometry = subMesh.GetChild("geometry");
  221. if (geometry)
  222. {
  223. unsigned vertices = geometry.GetI32("vertexcount");
  224. totalVertices += vertices;
  225. if (maxSubMeshVertices < vertices)
  226. maxSubMeshVertices = vertices;
  227. }
  228. ++numSubMeshes_;
  229. subMesh = subMesh.GetNext("submesh");
  230. }
  231. XMLElement sharedGeometry = root.GetChild("sharedgeometry");
  232. if (sharedGeometry)
  233. {
  234. unsigned vertices = sharedGeometry.GetI32("vertexcount");
  235. totalVertices += vertices;
  236. if (maxSubMeshVertices < vertices)
  237. maxSubMeshVertices = vertices;
  238. }
  239. if (!sharedGeometry && (splitSubMeshes || (totalVertices > 65535 && maxSubMeshVertices <= 65535)))
  240. {
  241. useOneBuffer_ = false;
  242. vertexBuffers_.Resize(numSubMeshes_);
  243. indexBuffers_.Resize(numSubMeshes_);
  244. }
  245. else
  246. {
  247. vertexBuffers_.Resize(1);
  248. indexBuffers_.Resize(1);
  249. }
  250. subMesh = subMeshes.GetChild("submesh");
  251. unsigned indexStart = 0;
  252. unsigned vertexStart = 0;
  253. unsigned subMeshIndex = 0;
  254. Vector<unsigned> vertexStarts;
  255. vertexStarts.Resize(numSubMeshes_);
  256. while (subMesh)
  257. {
  258. XMLElement geometry = subMesh.GetChild("geometry");
  259. XMLElement faces = subMesh.GetChild("faces");
  260. // If no submesh vertexbuffer, process the shared geometry, but do it only once
  261. unsigned vertices = 0;
  262. if (!geometry)
  263. {
  264. vertexStart = 0;
  265. if (!subMeshIndex)
  266. geometry = root.GetChild("sharedgeometry");
  267. }
  268. if (geometry)
  269. vertices = geometry.GetI32("vertexcount");
  270. ModelSubGeometryLodLevel subGeometryLodLevel;
  271. ModelVertexBuffer* vBuf;
  272. ModelIndexBuffer* iBuf;
  273. if (useOneBuffer_)
  274. {
  275. vBuf = &vertexBuffers_[0];
  276. if (vertices)
  277. vBuf->vertices_.Resize(vertexStart + vertices);
  278. iBuf = &indexBuffers_[0];
  279. subGeometryLodLevel.vertexBuffer_ = 0;
  280. subGeometryLodLevel.indexBuffer_ = 0;
  281. }
  282. else
  283. {
  284. vertexStart = 0;
  285. indexStart = 0;
  286. vBuf = &vertexBuffers_[subMeshIndex];
  287. vBuf->vertices_.Resize(vertices);
  288. iBuf = &indexBuffers_[subMeshIndex];
  289. subGeometryLodLevel.vertexBuffer_ = subMeshIndex;
  290. subGeometryLodLevel.indexBuffer_ = subMeshIndex;
  291. }
  292. // Store the start vertex for later use
  293. vertexStarts[subMeshIndex] = vertexStart;
  294. // Ogre may have multiple buffers in one submesh. These will be merged into one
  295. XMLElement bufferDef;
  296. if (geometry)
  297. bufferDef = geometry.GetChild("vertexbuffer");
  298. while (bufferDef)
  299. {
  300. if (bufferDef.HasAttribute("positions"))
  301. vBuf->elementMask_ |= VertexElements::Position;
  302. if (bufferDef.HasAttribute("normals"))
  303. vBuf->elementMask_ |= VertexElements::Normal;
  304. if (bufferDef.HasAttribute("texture_coords"))
  305. {
  306. vBuf->elementMask_ |= VertexElements::TexCoord1;
  307. if (bufferDef.GetI32("texture_coords") > 1)
  308. vBuf->elementMask_ |= VertexElements::TexCoord2;
  309. }
  310. unsigned vertexNum = vertexStart;
  311. if (vertices)
  312. {
  313. XMLElement vertex = bufferDef.GetChild("vertex");
  314. while (vertex)
  315. {
  316. XMLElement position = vertex.GetChild("position");
  317. if (position)
  318. {
  319. // Convert from right- to left-handed
  320. float x = position.GetFloat("x");
  321. float y = position.GetFloat("y");
  322. float z = position.GetFloat("z");
  323. Vector3 vec(x, y, -z);
  324. vBuf->vertices_[vertexNum].position_ = vec;
  325. boundingBox_.Merge(vec);
  326. }
  327. XMLElement normal = vertex.GetChild("normal");
  328. if (normal)
  329. {
  330. // Convert from right- to left-handed
  331. float x = normal.GetFloat("x");
  332. float y = normal.GetFloat("y");
  333. float z = normal.GetFloat("z");
  334. Vector3 vec(x, y, -z);
  335. vBuf->vertices_[vertexNum].normal_ = vec;
  336. }
  337. XMLElement uv = vertex.GetChild("texcoord");
  338. if (uv)
  339. {
  340. float x = uv.GetFloat("u");
  341. float y = uv.GetFloat("v");
  342. Vector2 vec(x, y);
  343. vBuf->vertices_[vertexNum].texCoord1_ = vec;
  344. if (!!(vBuf->elementMask_ & VertexElements::TexCoord2))
  345. {
  346. uv = uv.GetNext("texcoord");
  347. if (uv)
  348. {
  349. float x = uv.GetFloat("u");
  350. float y = uv.GetFloat("v");
  351. Vector2 vec(x, y);
  352. vBuf->vertices_[vertexNum].texCoord2_ = vec;
  353. }
  354. }
  355. }
  356. vertexNum++;
  357. vertex = vertex.GetNext("vertex");
  358. }
  359. }
  360. bufferDef = bufferDef.GetNext("vertexbuffer");
  361. }
  362. unsigned triangles = faces.GetI32("count");
  363. unsigned indices = triangles * 3;
  364. XMLElement triangle = faces.GetChild("face");
  365. while (triangle)
  366. {
  367. unsigned v1 = triangle.GetI32("v1");
  368. unsigned v2 = triangle.GetI32("v2");
  369. unsigned v3 = triangle.GetI32("v3");
  370. iBuf->indices_.Push(v3 + vertexStart);
  371. iBuf->indices_.Push(v2 + vertexStart);
  372. iBuf->indices_.Push(v1 + vertexStart);
  373. triangle = triangle.GetNext("face");
  374. }
  375. subGeometryLodLevel.indexStart_ = indexStart;
  376. subGeometryLodLevel.indexCount_ = indices;
  377. if (vertexStart + vertices > 65535)
  378. iBuf->indexSize_ = sizeof(unsigned);
  379. XMLElement boneAssignments = subMesh.GetChild("boneassignments");
  380. if (bones_.Size())
  381. {
  382. if (boneAssignments)
  383. {
  384. XMLElement boneAssignment = boneAssignments.GetChild("vertexboneassignment");
  385. while (boneAssignment)
  386. {
  387. unsigned vertex = boneAssignment.GetI32("vertexindex") + vertexStart;
  388. unsigned bone = boneAssignment.GetI32("boneindex");
  389. float weight = boneAssignment.GetFloat("weight");
  390. BoneWeightAssignment assign{static_cast<unsigned char>(bone), weight};
  391. // Source data might have 0 weights. Disregard these
  392. if (assign.weight_ > 0.0f)
  393. {
  394. subGeometryLodLevel.boneWeights_[vertex].Push(assign);
  395. // Require skinning weight to be sufficiently large before vertex contributes to bone hitbox
  396. if (assign.weight_ > 0.33f)
  397. {
  398. // Check distance of vertex from bone to get bone max. radius information
  399. Vector3 bonePos = bones_[bone].derivedPosition_;
  400. Vector3 vertexPos = vBuf->vertices_[vertex].position_;
  401. float distance = (bonePos - vertexPos).Length();
  402. if (distance > bones_[bone].radius_)
  403. {
  404. bones_[bone].collisionMask_ |= 1;
  405. bones_[bone].radius_ = distance;
  406. }
  407. // Build the hitbox for the bone
  408. bones_[bone].boundingBox_.Merge(bones_[bone].inverseWorldTransform_ * (vertexPos));
  409. bones_[bone].collisionMask_ |= 2;
  410. }
  411. }
  412. boneAssignment = boneAssignment.GetNext("vertexboneassignment");
  413. }
  414. }
  415. if ((subGeometryLodLevel.boneWeights_.Size()) && bones_.Size())
  416. {
  417. vBuf->elementMask_ |= VertexElements::BlendWeights | VertexElements::BlendIndices;
  418. bool sorted = false;
  419. // If amount of bones is larger than supported by HW skinning, must remap per submesh
  420. if (bones_.Size() > maxBones_)
  421. {
  422. HashMap<unsigned, unsigned> usedBoneMap;
  423. unsigned remapIndex = 0;
  424. for (HashMap<unsigned, Vector<BoneWeightAssignment>>::Iterator i =
  425. subGeometryLodLevel.boneWeights_.Begin(); i != subGeometryLodLevel.boneWeights_.End(); ++i)
  426. {
  427. // Sort the bone assigns by weight
  428. Sort(i->second_.Begin(), i->second_.End(), CompareWeights);
  429. // Use only the first 4 weights
  430. for (unsigned j = 0; j < i->second_.Size() && j < 4; ++j)
  431. {
  432. unsigned originalIndex = i->second_[j].boneIndex_;
  433. if (!usedBoneMap.Contains(originalIndex))
  434. {
  435. usedBoneMap[originalIndex] = remapIndex;
  436. remapIndex++;
  437. }
  438. i->second_[j].boneIndex_ = usedBoneMap[originalIndex];
  439. }
  440. }
  441. // If still too many bones in one subgeometry, error
  442. if (usedBoneMap.Size() > maxBones_)
  443. ErrorExit("Too many bones (limit " + String(maxBones_) + ") in submesh " + String(subMeshIndex + 1));
  444. // Write mapping of vertex buffer bone indices to original bone indices
  445. subGeometryLodLevel.boneMapping_.Resize(usedBoneMap.Size());
  446. for (HashMap<unsigned, unsigned>::Iterator j = usedBoneMap.Begin(); j != usedBoneMap.End(); ++j)
  447. subGeometryLodLevel.boneMapping_[j->second_] = j->first_;
  448. sorted = true;
  449. }
  450. for (HashMap<unsigned, Vector<BoneWeightAssignment>>::Iterator i = subGeometryLodLevel.boneWeights_.Begin();
  451. i != subGeometryLodLevel.boneWeights_.End(); ++i)
  452. {
  453. // Sort the bone assigns by weight, if not sorted yet in bone remapping pass
  454. if (!sorted)
  455. Sort(i->second_.Begin(), i->second_.End(), CompareWeights);
  456. float totalWeight = 0.0f;
  457. float normalizationFactor = 0.0f;
  458. // Calculate normalization factor in case there are more than 4 blend weights, or they do not add up to 1
  459. for (unsigned j = 0; j < i->second_.Size() && j < 4; ++j)
  460. totalWeight += i->second_[j].weight_;
  461. if (totalWeight > 0.0f)
  462. normalizationFactor = 1.0f / totalWeight;
  463. for (unsigned j = 0; j < i->second_.Size() && j < 4; ++j)
  464. {
  465. vBuf->vertices_[i->first_].blendIndices_[j] = i->second_[j].boneIndex_;
  466. vBuf->vertices_[i->first_].blendWeights_[j] = i->second_[j].weight_ * normalizationFactor;
  467. }
  468. // If there are less than 4 blend weights, fill rest with zero
  469. for (unsigned j = i->second_.Size(); j < 4; ++j)
  470. {
  471. vBuf->vertices_[i->first_].blendIndices_[j] = 0;
  472. vBuf->vertices_[i->first_].blendWeights_[j] = 0.0f;
  473. }
  474. vBuf->vertices_[i->first_].hasBlendWeights_ = true;
  475. }
  476. }
  477. }
  478. else if (boneAssignments)
  479. PrintLine("No skeleton loaded, skipping skinning information");
  480. // Calculate center for the subgeometry
  481. Vector3 center = Vector3::ZERO;
  482. for (unsigned i = 0; i < iBuf->indices_.Size(); i += 3)
  483. {
  484. center += vBuf->vertices_[iBuf->indices_[i]].position_;
  485. center += vBuf->vertices_[iBuf->indices_[i + 1]].position_;
  486. center += vBuf->vertices_[iBuf->indices_[i + 2]].position_;
  487. }
  488. if (iBuf->indices_.Size())
  489. center /= (float)iBuf->indices_.Size();
  490. subGeometryCenters_.Push(center);
  491. indexStart += indices;
  492. vertexStart += vertices;
  493. OptimizeIndices(&subGeometryLodLevel, vBuf, iBuf);
  494. PrintLine("Processed submesh " + String(subMeshIndex + 1) + ": " + String(vertices) + " vertices " +
  495. String(triangles) + " triangles");
  496. Vector<ModelSubGeometryLodLevel> thisSubGeometry;
  497. thisSubGeometry.Push(subGeometryLodLevel);
  498. subGeometries_.Push(thisSubGeometry);
  499. subMesh = subMesh.GetNext("submesh");
  500. subMeshIndex++;
  501. }
  502. // Process LOD levels, if any
  503. XMLElement lods = root.GetChild("levelofdetail");
  504. if (lods)
  505. {
  506. try
  507. {
  508. // For now, support only generated LODs, where the vertices are the same
  509. XMLElement lod = lods.GetChild("lodgenerated");
  510. while (lod)
  511. {
  512. float distance = M_EPSILON;
  513. if (lod.HasAttribute("fromdepthsquared"))
  514. distance = sqrtf(lod.GetFloat("fromdepthsquared"));
  515. if (lod.HasAttribute("value"))
  516. distance = lod.GetFloat("value");
  517. XMLElement lodSubMesh = lod.GetChild("lodfacelist");
  518. while (lodSubMesh)
  519. {
  520. unsigned subMeshIndex = lodSubMesh.GetI32("submeshindex");
  521. unsigned triangles = lodSubMesh.GetI32("numfaces");
  522. ModelSubGeometryLodLevel newLodLevel;
  523. ModelSubGeometryLodLevel& originalLodLevel = subGeometries_[subMeshIndex][0];
  524. // Copy all initial values
  525. newLodLevel = originalLodLevel;
  526. ModelVertexBuffer* vBuf;
  527. ModelIndexBuffer* iBuf;
  528. if (useOneBuffer_)
  529. {
  530. vBuf = &vertexBuffers_[0];
  531. iBuf = &indexBuffers_[0];
  532. }
  533. else
  534. {
  535. vBuf = &vertexBuffers_[subMeshIndex];
  536. iBuf = &indexBuffers_[subMeshIndex];
  537. }
  538. unsigned indexStart = iBuf->indices_.Size();
  539. unsigned indexCount = triangles * 3;
  540. unsigned vertexStart = vertexStarts[subMeshIndex];
  541. newLodLevel.distance_ = distance;
  542. newLodLevel.indexStart_ = indexStart;
  543. newLodLevel.indexCount_ = indexCount;
  544. // Append indices to the original index buffer
  545. XMLElement triangle = lodSubMesh.GetChild("face");
  546. while (triangle)
  547. {
  548. unsigned v1 = triangle.GetI32("v1");
  549. unsigned v2 = triangle.GetI32("v2");
  550. unsigned v3 = triangle.GetI32("v3");
  551. iBuf->indices_.Push(v3 + vertexStart);
  552. iBuf->indices_.Push(v2 + vertexStart);
  553. iBuf->indices_.Push(v1 + vertexStart);
  554. triangle = triangle.GetNext("face");
  555. }
  556. OptimizeIndices(&newLodLevel, vBuf, iBuf);
  557. subGeometries_[subMeshIndex].Push(newLodLevel);
  558. PrintLine("Processed LOD level for submesh " + String(subMeshIndex + 1) + ": distance " + String(distance));
  559. lodSubMesh = lodSubMesh.GetNext("lodfacelist");
  560. }
  561. lod = lod.GetNext("lodgenerated");
  562. }
  563. }
  564. catch (...) {}
  565. }
  566. // Process poses/morphs
  567. // First find out all pose definitions
  568. if (exportMorphs)
  569. {
  570. try
  571. {
  572. Vector<XMLElement> poses;
  573. XMLElement posesRoot = root.GetChild("poses");
  574. if (posesRoot)
  575. {
  576. XMLElement pose = posesRoot.GetChild("pose");
  577. while (pose)
  578. {
  579. poses.Push(pose);
  580. pose = pose.GetNext("pose");
  581. }
  582. }
  583. // Then process animations using the poses
  584. XMLElement animsRoot = root.GetChild("animations");
  585. if (animsRoot)
  586. {
  587. XMLElement anim = animsRoot.GetChild("animation");
  588. while (anim)
  589. {
  590. String name = anim.GetAttribute("name");
  591. float length = anim.GetFloat("length");
  592. HashSet<unsigned> usedPoses;
  593. XMLElement tracks = anim.GetChild("tracks");
  594. if (tracks)
  595. {
  596. XMLElement track = tracks.GetChild("track");
  597. while (track)
  598. {
  599. XMLElement keyframes = track.GetChild("keyframes");
  600. if (keyframes)
  601. {
  602. XMLElement keyframe = keyframes.GetChild("keyframe");
  603. while (keyframe)
  604. {
  605. float time = keyframe.GetFloat("time");
  606. XMLElement poseref = keyframe.GetChild("poseref");
  607. // Get only the end pose
  608. if (poseref && time == length)
  609. usedPoses.Insert(poseref.GetI32("poseindex"));
  610. keyframe = keyframe.GetNext("keyframe");
  611. }
  612. }
  613. track = track.GetNext("track");
  614. }
  615. }
  616. if (usedPoses.Size())
  617. {
  618. ModelMorph newMorph;
  619. newMorph.name_ = name;
  620. if (useOneBuffer_)
  621. newMorph.buffers_.Resize(1);
  622. else
  623. newMorph.buffers_.Resize(usedPoses.Size());
  624. unsigned bufIndex = 0;
  625. for (HashSet<unsigned>::Iterator i = usedPoses.Begin(); i != usedPoses.End(); ++i)
  626. {
  627. XMLElement pose = poses[*i];
  628. unsigned targetSubMesh = pose.GetI32("index");
  629. XMLElement poseOffset = pose.GetChild("poseoffset");
  630. if (useOneBuffer_)
  631. newMorph.buffers_[bufIndex].vertexBuffer_ = 0;
  632. else
  633. newMorph.buffers_[bufIndex].vertexBuffer_ = targetSubMesh;
  634. newMorph.buffers_[bufIndex].elementMask_ = VertexElements::Position;
  635. ModelVertexBuffer* vBuf = &vertexBuffers_[newMorph.buffers_[bufIndex].vertexBuffer_];
  636. while (poseOffset)
  637. {
  638. // Convert from right- to left-handed
  639. unsigned vertexIndex = poseOffset.GetI32("index") + vertexStarts[targetSubMesh];
  640. float x = poseOffset.GetFloat("x");
  641. float y = poseOffset.GetFloat("y");
  642. float z = poseOffset.GetFloat("z");
  643. Vector3 vec(x, y, -z);
  644. if (vBuf->morphCount_ == 0)
  645. {
  646. vBuf->morphStart_ = vertexIndex;
  647. vBuf->morphCount_ = 1;
  648. }
  649. else
  650. {
  651. unsigned first = vBuf->morphStart_;
  652. unsigned last = first + vBuf->morphCount_ - 1;
  653. if (vertexIndex < first)
  654. first = vertexIndex;
  655. if (vertexIndex > last)
  656. last = vertexIndex;
  657. vBuf->morphStart_ = first;
  658. vBuf->morphCount_ = last - first + 1;
  659. }
  660. ModelVertex newVertex;
  661. newVertex.position_ = vec;
  662. newMorph.buffers_[bufIndex].vertices_.Push(MakePair(vertexIndex, newVertex));
  663. poseOffset = poseOffset.GetNext("poseoffset");
  664. }
  665. if (!useOneBuffer_)
  666. ++bufIndex;
  667. }
  668. morphs_.Push(newMorph);
  669. PrintLine("Processed morph " + name + " with " + String(usedPoses.Size()) + " sub-poses");
  670. }
  671. anim = anim.GetNext("animation");
  672. }
  673. }
  674. }
  675. catch (...) {}
  676. }
  677. // Check any of the buffers for vertices with missing blend weight assignments
  678. for (unsigned i = 0; i < vertexBuffers_.Size(); ++i)
  679. {
  680. if (!!(vertexBuffers_[i].elementMask_ & VertexElements::BlendWeights))
  681. {
  682. for (unsigned j = 0; j < vertexBuffers_[i].vertices_.Size(); ++j)
  683. if (!vertexBuffers_[i].vertices_[j].hasBlendWeights_)
  684. ErrorExit("Found a vertex with missing skinning information");
  685. }
  686. }
  687. // Tangent generation
  688. if (generateTangents)
  689. {
  690. for (unsigned i = 0; i < subGeometries_.Size(); ++i)
  691. {
  692. for (unsigned j = 0; j < subGeometries_[i].Size(); ++j)
  693. {
  694. ModelVertexBuffer& vBuf = vertexBuffers_[subGeometries_[i][j].vertexBuffer_];
  695. ModelIndexBuffer& iBuf = indexBuffers_[subGeometries_[i][j].indexBuffer_];
  696. unsigned indexStart = subGeometries_[i][j].indexStart_;
  697. unsigned indexCount = subGeometries_[i][j].indexCount_;
  698. // If already has tangents, do not regenerate
  699. if (!!(vBuf.elementMask_ & VertexElements::Tangent) || vBuf.vertices_.Empty() || iBuf.indices_.Empty())
  700. continue;
  701. vBuf.elementMask_ |= VertexElements::Tangent;
  702. if ((vBuf.elementMask_ & (VertexElements::Position | VertexElements::Normal | VertexElements::TexCoord1))
  703. != (VertexElements::Position | VertexElements::Normal | VertexElements::TexCoord1))
  704. ErrorExit("To generate tangents, positions normals and texcoords are required");
  705. GenerateTangents(&vBuf.vertices_[0], sizeof(ModelVertex), &iBuf.indices_[0], sizeof(unsigned), indexStart,
  706. indexCount, offsetof(ModelVertex, normal_), offsetof(ModelVertex, texCoord1_), offsetof(ModelVertex,
  707. tangent_));
  708. PrintLine("Generated tangents");
  709. }
  710. }
  711. }
  712. }
  713. void WriteOutput(const String& outputFileName, bool exportAnimations, bool rotationsOnly, bool saveMaterialList)
  714. {
  715. /// \todo Use save functions of Model & Animation classes
  716. // Begin serialization
  717. {
  718. File dest(context_);
  719. if (!dest.Open(outputFileName, FILE_WRITE))
  720. ErrorExit("Could not open output file " + outputFileName);
  721. // ID
  722. dest.WriteFileID("UMD2");
  723. // Vertexbuffers
  724. dest.WriteU32(vertexBuffers_.Size());
  725. for (unsigned i = 0; i < vertexBuffers_.Size(); ++i)
  726. vertexBuffers_[i].WriteData(dest);
  727. // Indexbuffers
  728. dest.WriteU32(indexBuffers_.Size());
  729. for (unsigned i = 0; i < indexBuffers_.Size(); ++i)
  730. indexBuffers_[i].WriteData(dest);
  731. // Subgeometries
  732. dest.WriteU32(subGeometries_.Size());
  733. for (unsigned i = 0; i < subGeometries_.Size(); ++i)
  734. {
  735. // Write bone mapping info from the first LOD level. It does not change for further LODs
  736. dest.WriteU32(subGeometries_[i][0].boneMapping_.Size());
  737. for (unsigned k = 0; k < subGeometries_[i][0].boneMapping_.Size(); ++k)
  738. dest.WriteU32(subGeometries_[i][0].boneMapping_[k]);
  739. // Lod levels for this subgeometry
  740. dest.WriteU32(subGeometries_[i].Size());
  741. for (unsigned j = 0; j < subGeometries_[i].Size(); ++j)
  742. {
  743. dest.WriteFloat(subGeometries_[i][j].distance_);
  744. dest.WriteU32((unsigned)subGeometries_[i][j].primitiveType_);
  745. dest.WriteU32(subGeometries_[i][j].vertexBuffer_);
  746. dest.WriteU32(subGeometries_[i][j].indexBuffer_);
  747. dest.WriteU32(subGeometries_[i][j].indexStart_);
  748. dest.WriteU32(subGeometries_[i][j].indexCount_);
  749. }
  750. }
  751. // Morphs
  752. dest.WriteU32(morphs_.Size());
  753. for (unsigned i = 0; i < morphs_.Size(); ++i)
  754. morphs_[i].WriteData(dest);
  755. // Skeleton
  756. dest.WriteU32(bones_.Size());
  757. for (unsigned i = 0; i < bones_.Size(); ++i)
  758. {
  759. dest.WriteString(bones_[i].name_);
  760. dest.WriteU32(bones_[i].parentIndex_);
  761. dest.WriteVector3(bones_[i].bindPosition_);
  762. dest.WriteQuaternion(bones_[i].bindRotation_);
  763. dest.WriteVector3(bones_[i].bindScale_);
  764. Matrix3x4 offsetMatrix(bones_[i].derivedPosition_, bones_[i].derivedRotation_, bones_[i].derivedScale_);
  765. offsetMatrix = offsetMatrix.Inverse();
  766. dest.Write(offsetMatrix.Data(), sizeof(Matrix3x4));
  767. dest.WriteU8(bones_[i].collisionMask_);
  768. if (bones_[i].collisionMask_ & 1u)
  769. dest.WriteFloat(bones_[i].radius_);
  770. if (bones_[i].collisionMask_ & 2u)
  771. dest.WriteBoundingBox(bones_[i].boundingBox_);
  772. }
  773. // Bounding box
  774. dest.WriteBoundingBox(boundingBox_);
  775. // Geometry centers
  776. for (unsigned i = 0; i < subGeometryCenters_.Size(); ++i)
  777. dest.WriteVector3(subGeometryCenters_[i]);
  778. }
  779. if (saveMaterialList)
  780. {
  781. String materialListName = ReplaceExtension(outputFileName, ".txt");
  782. File listFile(context_);
  783. if (listFile.Open(materialListName, FILE_WRITE))
  784. {
  785. for (unsigned i = 0; i < materialNames_.Size(); ++i)
  786. {
  787. // Assume the materials will be located inside the standard Materials subdirectory
  788. listFile.WriteLine("Materials/" + ReplaceExtension(SanitateAssetName(materialNames_[i]), ".xml"));
  789. }
  790. }
  791. else
  792. PrintLine("Warning: could not write material list file " + materialListName);
  793. }
  794. XMLElement skeletonRoot = skelFile_->GetRoot("skeleton");
  795. if (skeletonRoot && exportAnimations)
  796. {
  797. // Go through animations
  798. XMLElement animationsRoot = skeletonRoot.GetChild("animations");
  799. if (animationsRoot)
  800. {
  801. XMLElement animation = animationsRoot.GetChild("animation");
  802. while (animation)
  803. {
  804. ModelAnimation newAnimation;
  805. newAnimation.name_ = animation.GetAttribute("name");
  806. newAnimation.length_ = animation.GetFloat("length");
  807. XMLElement tracksRoot = animation.GetChild("tracks");
  808. XMLElement track = tracksRoot.GetChild("track");
  809. while (track)
  810. {
  811. String trackName = track.GetAttribute("bone");
  812. ModelBone* bone = nullptr;
  813. for (unsigned i = 0; i < bones_.Size(); ++i)
  814. {
  815. if (bones_[i].name_ == trackName)
  816. {
  817. bone = &bones_[i];
  818. break;
  819. }
  820. }
  821. if (!bone)
  822. ErrorExit("Found animation track for unknown bone " + trackName);
  823. AnimationTrack newAnimationTrack;
  824. newAnimationTrack.name_ = trackName;
  825. if (!rotationsOnly)
  826. newAnimationTrack.channelMask_ = AnimationChannels::Position | AnimationChannels::Rotation;
  827. else
  828. newAnimationTrack.channelMask_ = AnimationChannels::Rotation;
  829. XMLElement keyFramesRoot = track.GetChild("keyframes");
  830. XMLElement keyFrame = keyFramesRoot.GetChild("keyframe");
  831. while (keyFrame)
  832. {
  833. AnimationKeyFrame newKeyFrame;
  834. // Convert from right- to left-handed
  835. XMLElement position = keyFrame.GetChild("translate");
  836. float x = position.GetFloat("x");
  837. float y = position.GetFloat("y");
  838. float z = position.GetFloat("z");
  839. Vector3 pos(x, y, -z);
  840. XMLElement rotation = keyFrame.GetChild("rotate");
  841. XMLElement axis = rotation.GetChild("axis");
  842. float angle = -rotation.GetFloat("angle") * M_RADTODEG;
  843. x = axis.GetFloat("x");
  844. y = axis.GetFloat("y");
  845. z = axis.GetFloat("z");
  846. Vector3 axisVec(x, y, -z);
  847. Quaternion rot(angle, axisVec);
  848. // Transform from bind-pose relative into absolute
  849. pos = bone->bindPosition_ + pos;
  850. rot = bone->bindRotation_ * rot;
  851. newKeyFrame.time_ = keyFrame.GetFloat("time");
  852. newKeyFrame.position_ = pos;
  853. newKeyFrame.rotation_ = rot;
  854. newAnimationTrack.keyFrames_.Push(newKeyFrame);
  855. keyFrame = keyFrame.GetNext("keyframe");
  856. }
  857. // Make sure keyframes are sorted from beginning to end
  858. Sort(newAnimationTrack.keyFrames_.Begin(), newAnimationTrack.keyFrames_.End(), CompareKeyFrames);
  859. // Do not add tracks with no keyframes
  860. if (newAnimationTrack.keyFrames_.Size())
  861. newAnimation.tracks_.Push(newAnimationTrack);
  862. track = track.GetNext("track");
  863. }
  864. // Write each animation into a separate file
  865. String animationFileName = outputFileName.Replaced(".mdl", "");
  866. animationFileName += "_" + newAnimation.name_ + ".ani";
  867. File dest(context_);
  868. if (!dest.Open(animationFileName, FILE_WRITE))
  869. ErrorExit("Could not open output file " + animationFileName);
  870. dest.WriteFileID("UANI");
  871. dest.WriteString(newAnimation.name_);
  872. dest.WriteFloat(newAnimation.length_);
  873. dest.WriteU32(newAnimation.tracks_.Size());
  874. for (unsigned i = 0; i < newAnimation.tracks_.Size(); ++i)
  875. {
  876. AnimationTrack& track = newAnimation.tracks_[i];
  877. dest.WriteString(track.name_);
  878. dest.WriteU8(ToU8(track.channelMask_));
  879. dest.WriteU32(track.keyFrames_.Size());
  880. for (unsigned j = 0; j < track.keyFrames_.Size(); ++j)
  881. {
  882. AnimationKeyFrame& keyFrame = track.keyFrames_[j];
  883. dest.WriteFloat(keyFrame.time_);
  884. if (!!(track.channelMask_ & AnimationChannels::Position))
  885. dest.WriteVector3(keyFrame.position_);
  886. if (!!(track.channelMask_ & AnimationChannels::Rotation))
  887. dest.WriteQuaternion(keyFrame.rotation_);
  888. if (!!(track.channelMask_ & AnimationChannels::Scale))
  889. dest.WriteVector3(keyFrame.scale_);
  890. }
  891. }
  892. animation = animation.GetNext("animation");
  893. PrintLine("Processed animation " + newAnimation.name_);
  894. }
  895. }
  896. }
  897. }
  898. void OptimizeIndices(ModelSubGeometryLodLevel* subGeom, ModelVertexBuffer* vb, ModelIndexBuffer* ib)
  899. {
  900. Vector<Triangle> oldTriangles;
  901. Vector<Triangle> newTriangles;
  902. if (subGeom->indexCount_ % 3)
  903. {
  904. PrintLine("Index count is not divisible by 3, skipping index optimization");
  905. return;
  906. }
  907. for (unsigned i = 0; i < vb->vertices_.Size(); ++i)
  908. {
  909. vb->vertices_[i].useCount_ = 0;
  910. vb->vertices_[i].cachePosition_ = -1;
  911. }
  912. for (unsigned i = subGeom->indexStart_; i < subGeom->indexStart_ + subGeom->indexCount_; i += 3)
  913. {
  914. Triangle triangle{ib->indices_[i], ib->indices_[i + 1], ib->indices_[i + 2]};
  915. vb->vertices_[triangle.v0_].useCount_++;
  916. vb->vertices_[triangle.v1_].useCount_++;
  917. vb->vertices_[triangle.v2_].useCount_++;
  918. oldTriangles.Push(triangle);
  919. }
  920. for (unsigned i = 0; i < vb->vertices_.Size(); ++i)
  921. CalculateScore(vb->vertices_[i]);
  922. Vector<unsigned> vertexCache;
  923. while (oldTriangles.Size())
  924. {
  925. unsigned bestTriangle = M_MAX_UNSIGNED;
  926. float bestTriangleScore = -1.0f;
  927. // Find the best triangle at this point
  928. for (unsigned i = 0; i < oldTriangles.Size(); ++i)
  929. {
  930. Triangle& triangle = oldTriangles[i];
  931. float triangleScore =
  932. vb->vertices_[triangle.v0_].score_ +
  933. vb->vertices_[triangle.v1_].score_ +
  934. vb->vertices_[triangle.v2_].score_;
  935. if (triangleScore > bestTriangleScore)
  936. {
  937. bestTriangle = i;
  938. bestTriangleScore = triangleScore;
  939. }
  940. }
  941. if (bestTriangle == M_MAX_UNSIGNED)
  942. {
  943. PrintLine("Could not find next triangle, aborting index optimization");
  944. return;
  945. }
  946. // Add the best triangle
  947. Triangle triangleCopy = oldTriangles[bestTriangle];
  948. newTriangles.Push(triangleCopy);
  949. oldTriangles.Erase(oldTriangles.Begin() + bestTriangle);
  950. // Reduce the use count
  951. vb->vertices_[triangleCopy.v0_].useCount_--;
  952. vb->vertices_[triangleCopy.v1_].useCount_--;
  953. vb->vertices_[triangleCopy.v2_].useCount_--;
  954. // Model the LRU cache behaviour
  955. // Erase the triangle vertices from the middle of the cache, if they were there
  956. for (unsigned i = 0; i < vertexCache.Size(); ++i)
  957. {
  958. if ((vertexCache[i] == triangleCopy.v0_) ||
  959. (vertexCache[i] == triangleCopy.v1_) ||
  960. (vertexCache[i] == triangleCopy.v2_))
  961. {
  962. vertexCache.Erase(vertexCache.Begin() + i);
  963. --i;
  964. }
  965. }
  966. // Then push them to the front
  967. vertexCache.Insert(vertexCache.Begin(), triangleCopy.v0_);
  968. vertexCache.Insert(vertexCache.Begin(), triangleCopy.v1_);
  969. vertexCache.Insert(vertexCache.Begin(), triangleCopy.v2_);
  970. // Update positions & scores of all vertices in the cache
  971. // Give position -1 if vertex is going to be erased
  972. for (unsigned i = 0; i < vertexCache.Size(); ++i)
  973. {
  974. ModelVertex& vertex = vb->vertices_[vertexCache[i]];
  975. if (i >= VERTEX_CACHE_SIZE)
  976. vertex.cachePosition_ = -1;
  977. else
  978. vertex.cachePosition_ = i;
  979. CalculateScore(vertex);
  980. }
  981. // Finally erase the extra vertices
  982. if (vertexCache.Size() > VERTEX_CACHE_SIZE)
  983. vertexCache.Resize(VERTEX_CACHE_SIZE);
  984. }
  985. // Rewrite the index data now
  986. unsigned i = subGeom->indexStart_;
  987. for (unsigned j = 0; j < newTriangles.Size(); ++j)
  988. {
  989. ib->indices_[i++] = newTriangles[j].v0_;
  990. ib->indices_[i++] = newTriangles[j].v1_;
  991. ib->indices_[i++] = newTriangles[j].v2_;
  992. }
  993. }
  994. void CalculateScore(ModelVertex& vertex)
  995. {
  996. // Linear-Speed Vertex Cache Optimisation by Tom Forsyth from
  997. // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
  998. const float cacheDecayPower = 1.5f;
  999. const float lastTriScore = 0.75f;
  1000. const float valenceBoostScale = 2.0f;
  1001. const float valenceBoostPower = 0.5f;
  1002. if (vertex.useCount_ == 0)
  1003. {
  1004. // No tri needs this vertex!
  1005. vertex.score_ = -1.0f;
  1006. return;
  1007. }
  1008. float score = 0.0f;
  1009. int cachePosition = vertex.cachePosition_;
  1010. if (cachePosition < 0)
  1011. {
  1012. // Vertex is not in FIFO cache - no score.
  1013. }
  1014. else
  1015. {
  1016. if (cachePosition < 3)
  1017. {
  1018. // This vertex was used in the last triangle,
  1019. // so it has a fixed score, whichever of the three
  1020. // it's in. Otherwise, you can get very different
  1021. // answers depending on whether you add
  1022. // the triangle 1,2,3 or 3,1,2 - which is silly.
  1023. score = lastTriScore;
  1024. }
  1025. else
  1026. {
  1027. // Points for being high in the cache.
  1028. const float scaler = 1.0f / (VERTEX_CACHE_SIZE - 3);
  1029. score = 1.0f - (cachePosition - 3) * scaler;
  1030. score = powf(score, cacheDecayPower);
  1031. }
  1032. }
  1033. // Bonus points for having a low number of tris still to
  1034. // use the vert, so we get rid of lone verts quickly.
  1035. float valenceBoost = powf((float)vertex.useCount_, -valenceBoostPower);
  1036. score += valenceBoostScale * valenceBoost;
  1037. vertex.score_ = score;
  1038. }
  1039. String SanitateAssetName(const String& name)
  1040. {
  1041. String fixedName = name;
  1042. fixedName.Replace("<", "");
  1043. fixedName.Replace(">", "");
  1044. fixedName.Replace("?", "");
  1045. fixedName.Replace("*", "");
  1046. fixedName.Replace(":", "");
  1047. fixedName.Replace("\"", "");
  1048. fixedName.Replace("/", "");
  1049. fixedName.Replace("\\", "");
  1050. fixedName.Replace("|", "");
  1051. return fixedName;
  1052. }