OgreImporter.cpp 50 KB

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