Added bigger Ashoka pillar variation with 4 lions. Textures modified.
[0ad.git] / source / collada / GeomReindex.cpp
blob1e6527eb6a694e01b3472690a6b11cd9a298caed
1 /* Copyright (C) 2009 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "GeomReindex.h"
22 #include "FCollada.h"
23 #include "FCDocument/FCDEntity.h"
24 #include "FCDocument/FCDGeometryMesh.h"
25 #include "FCDocument/FCDGeometryPolygons.h"
26 #include "FCDocument/FCDGeometryPolygonsInput.h"
27 #include "FCDocument/FCDGeometrySource.h"
28 #include "FCDocument/FCDSkinController.h"
30 #include <cassert>
31 #include <vector>
32 #include <map>
33 #include <algorithm>
35 typedef std::pair<float, float> uv_pair_type;
37 struct VertexData
39 VertexData(const float* pos, const float* norm, const std::vector<uv_pair_type> &uvs,
40 const std::vector<FCDJointWeightPair>& weights)
41 : x(pos[0]), y(pos[1]), z(pos[2]),
42 nx(norm[0]), ny(norm[1]), nz(norm[2]),
43 uvs(uvs),
44 weights(weights)
48 float x, y, z;
49 float nx, ny, nz;
50 std::vector<uv_pair_type> uvs;
51 std::vector<FCDJointWeightPair> weights;
54 bool similar(float a, float b)
56 return (fabsf(a - b) < 0.000001f);
59 bool operator==(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
61 return (a.jointIndex == b.jointIndex && similar(a.weight, b.weight));
64 bool operator<(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
66 // Sort by decreasing weight, then by increasing joint ID
67 if (a.weight > b.weight)
68 return true;
69 else if (a.weight < b.weight)
70 return false;
71 else if (a.jointIndex < b.jointIndex)
72 return true;
73 else
74 return false;
77 bool operator==(const uv_pair_type& a, const uv_pair_type& b)
79 return similar(a.first, b.first) && similar(a.second, b.second);
82 bool operator==(const VertexData& a, const VertexData& b)
84 return (similar(a.x, b.x) && similar(a.y, b.y) && similar(a.z, b.z)
85 && similar(a.nx, b.nx) && similar(a.ny, b.ny) && similar(a.nz, b.nz)
86 && (a.uvs == b.uvs)
87 && (a.weights == b.weights));
90 bool operator<(const VertexData& a, const VertexData& b)
92 #define CMP(f) if (a.f < b.f) return true; if (a.f > b.f) return false
93 CMP(x); CMP(y); CMP(z);
94 CMP(nx); CMP(ny); CMP(nz);
95 CMP(uvs);
96 CMP(weights);
97 #undef CMP
98 return false;
101 template <typename T>
102 struct InserterWithoutDuplicates
104 InserterWithoutDuplicates(std::vector<T>& vec) : vec(vec)
108 size_t add(const T& val)
110 typename std::map<T, size_t>::iterator it = btree.find(val);
111 if (it != btree.end())
112 return it->second;
114 size_t idx = vec.size();
115 vec.push_back(val);
116 btree.insert(std::make_pair(val, idx));
117 return idx;
120 std::vector<T>& vec;
121 std::map<T, size_t> btree; // for faster lookups (so we can build a duplicate-free list in O(n log n) instead of O(n^2))
123 private:
124 InserterWithoutDuplicates& operator=(const InserterWithoutDuplicates&);
127 void CanonicaliseWeights(std::vector<FCDJointWeightPair>& weights)
129 // Convert weight-lists into a standard format, so simple vector equality
130 // can be used to determine equivalence
131 std::sort(weights.begin(), weights.end());
134 void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
136 // Given geometry with:
137 // positions, normals, texcoords, bone blends
138 // each with their own data array and index array, change it to
139 // have a single optimised index array shared by all vertexes.
141 FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
142 FCDGeometryPolygonsInput* inputNormal = polys->FindInput(FUDaeGeometryInput::NORMAL);
143 FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);
145 size_t numVertices = polys->GetFaceVertexCount();
147 assert(inputPosition->GetIndexCount() == numVertices);
148 assert(inputNormal ->GetIndexCount() == numVertices);
149 assert(inputTexcoord->GetIndexCount() == numVertices);
151 const uint32* indicesPosition = inputPosition->GetIndices();
152 const uint32* indicesNormal = inputNormal->GetIndices();
153 const uint32* indicesTexcoord = inputTexcoord->GetIndices();
155 assert(indicesPosition);
156 assert(indicesNormal);
157 assert(indicesTexcoord); // TODO - should be optional, because textureless meshes aren't unreasonable
159 FCDGeometrySourceList texcoordSources;
160 polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
162 FCDGeometrySource* sourcePosition = inputPosition->GetSource();
163 FCDGeometrySource* sourceNormal = inputNormal ->GetSource();
165 const float* dataPosition = sourcePosition->GetData();
166 const float* dataNormal = sourceNormal ->GetData();
168 if (skin)
170 #ifndef NDEBUG
171 size_t numVertexPositions = sourcePosition->GetDataCount() / sourcePosition->GetStride();
172 assert(skin->GetInfluenceCount() == numVertexPositions);
173 #endif
176 uint32 stridePosition = sourcePosition->GetStride();
177 uint32 strideNormal = sourceNormal ->GetStride();
179 std::vector<uint32> indicesCombined;
180 std::vector<VertexData> vertexes;
181 InserterWithoutDuplicates<VertexData> inserter(vertexes);
183 for (size_t i = 0; i < numVertices; ++i)
185 std::vector<FCDJointWeightPair> weights;
186 if (skin)
188 FCDSkinControllerVertex* influences = skin->GetVertexInfluence(indicesPosition[i]);
189 assert(influences != NULL);
190 for (size_t j = 0; j < influences->GetPairCount(); ++j)
192 FCDJointWeightPair* pair = influences->GetPair(j);
193 assert(pair != NULL);
194 weights.push_back(*pair);
196 CanonicaliseWeights(weights);
199 std::vector<uv_pair_type> uvs;
200 for (size_t set = 0; set < texcoordSources.size(); ++set)
202 const float* dataTexcoord = texcoordSources[set]->GetData();
203 uint32 strideTexcoord = texcoordSources[set]->GetStride();
205 uv_pair_type p;
206 p.first = dataTexcoord[indicesTexcoord[i]*strideTexcoord];
207 p.second = dataTexcoord[indicesTexcoord[i]*strideTexcoord + 1];
208 uvs.push_back(p);
211 VertexData vtx (
212 &dataPosition[indicesPosition[i]*stridePosition],
213 &dataNormal [indicesNormal [i]*strideNormal],
214 uvs,
215 weights
217 size_t idx = inserter.add(vtx);
218 indicesCombined.push_back((uint32)idx);
221 // TODO: rearrange indicesCombined (and rearrange vertexes to match) to use
222 // the vertex cache efficiently
223 // (<http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html> etc)
225 FloatList newDataPosition;
226 FloatList newDataNormal;
227 FloatList newDataTexcoord;
228 std::vector<std::vector<FCDJointWeightPair> > newWeightedMatches;
230 for (size_t i = 0; i < vertexes.size(); ++i)
232 newDataPosition.push_back(vertexes[i].x);
233 newDataPosition.push_back(vertexes[i].y);
234 newDataPosition.push_back(vertexes[i].z);
235 newDataNormal .push_back(vertexes[i].nx);
236 newDataNormal .push_back(vertexes[i].ny);
237 newDataNormal .push_back(vertexes[i].nz);
238 newWeightedMatches.push_back(vertexes[i].weights);
241 // (Slightly wasteful to duplicate this array so many times, but FCollada
242 // doesn't seem to support multiple inputs with the same source data)
243 inputPosition->SetIndices(&indicesCombined.front(), indicesCombined.size());
244 inputNormal ->SetIndices(&indicesCombined.front(), indicesCombined.size());
245 inputTexcoord->SetIndices(&indicesCombined.front(), indicesCombined.size());
247 for (size_t set = 0; set < texcoordSources.size(); ++set)
249 newDataTexcoord.clear();
250 for (size_t i = 0; i < vertexes.size(); ++i)
252 newDataTexcoord.push_back(vertexes[i].uvs[set].first);
253 newDataTexcoord.push_back(vertexes[i].uvs[set].second);
255 texcoordSources[set]->SetData(newDataTexcoord, 2);
258 sourcePosition->SetData(newDataPosition, 3);
259 sourceNormal ->SetData(newDataNormal, 3);
261 if (skin)
263 skin->SetInfluenceCount(newWeightedMatches.size());
264 for (size_t i = 0; i < newWeightedMatches.size(); ++i)
266 skin->GetVertexInfluence(i)->SetPairCount(0);
267 for (size_t j = 0; j < newWeightedMatches[i].size(); ++j)
268 skin->GetVertexInfluence(i)->AddPair(newWeightedMatches[i][j].jointIndex, newWeightedMatches[i][j].weight);