LCOV - code coverage report
Current view: top level - source/collada - GeomReindex.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 90 126 71.4 %
Date: 2023-01-19 00:18:29 Functions: 9 15 60.0 %

          Line data    Source code
       1             : /* Copyright (C) 2009 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       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.
       8             :  *
       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.
      13             :  *
      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/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "GeomReindex.h"
      21             : 
      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"
      29             : 
      30             : #include <cassert>
      31             : #include <vector>
      32             : #include <map>
      33             : #include <algorithm>
      34             : 
      35             : typedef std::pair<float, float> uv_pair_type;
      36             : 
      37        4032 : struct VertexData
      38             : {
      39        1002 :     VertexData(const float* pos, const float* norm, const std::vector<uv_pair_type> &uvs,
      40             :            const std::vector<FCDJointWeightPair>& weights)
      41        3006 :         : x(pos[0]), y(pos[1]), z(pos[2]),
      42        3006 :         nx(norm[0]), ny(norm[1]), nz(norm[2]),
      43             :         uvs(uvs),
      44        6012 :         weights(weights)
      45             :     {
      46        1002 :     }
      47             : 
      48             :     float x, y, z;
      49             :     float nx, ny, nz;
      50             :     std::vector<uv_pair_type> uvs;
      51             :     std::vector<FCDJointWeightPair> weights;
      52             : };
      53             : 
      54           0 : bool similar(float a, float b)
      55             : {
      56           0 :     return (fabsf(a - b) < 0.000001f);
      57             : }
      58             : 
      59           0 : bool operator==(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
      60             : {
      61           0 :     return (a.jointIndex == b.jointIndex && similar(a.weight, b.weight));
      62             : }
      63             : 
      64           0 : bool operator<(const FCDJointWeightPair& a, const FCDJointWeightPair& b)
      65             : {
      66             :     // Sort by decreasing weight, then by increasing joint ID
      67           0 :     if (a.weight > b.weight)
      68           0 :         return true;
      69           0 :     else if (a.weight < b.weight)
      70           0 :         return false;
      71           0 :     else if (a.jointIndex < b.jointIndex)
      72           0 :         return true;
      73             :     else
      74           0 :         return false;
      75             : }
      76             : 
      77           0 : bool operator==(const uv_pair_type& a, const uv_pair_type& b)
      78             : {
      79           0 :     return similar(a.first, b.first) && similar(a.second, b.second);
      80             : }
      81             : 
      82           0 : bool operator==(const VertexData& a, const VertexData& b)
      83             : {
      84           0 :     return (similar(a.x,  b.x)  && similar(a.y,  b.y)  && similar(a.z,  b.z)
      85           0 :          && similar(a.nx, b.nx) && similar(a.ny, b.ny) && similar(a.nz, b.nz)
      86           0 :          && (a.uvs == b.uvs)
      87           0 :          && (a.weights == b.weights));
      88             : }
      89             : 
      90       10044 : bool operator<(const VertexData& a, const VertexData& b)
      91             : {
      92             : #define CMP(f) if (a.f < b.f) return true; if (a.f > b.f) return false
      93       10044 :     CMP(x);  CMP(y);  CMP(z);
      94        1802 :     CMP(nx); CMP(ny); CMP(nz);
      95        1802 :     CMP(uvs);
      96        1290 :     CMP(weights);
      97             : #undef CMP
      98        1290 :     return false;
      99             : }
     100             : 
     101             : template <typename T>
     102           4 : struct InserterWithoutDuplicates
     103             : {
     104           4 :     InserterWithoutDuplicates(std::vector<T>& vec) : vec(vec)
     105             :     {
     106           4 :     }
     107             : 
     108        1002 :     size_t add(const T& val)
     109             :     {
     110        1002 :         typename std::map<T, size_t>::iterator it = btree.find(val);
     111        1002 :         if (it != btree.end())
     112         645 :             return it->second;
     113             : 
     114         357 :         size_t idx = vec.size();
     115         357 :         vec.push_back(val);
     116         357 :         btree.insert(std::make_pair(val, idx));
     117         357 :         return idx;
     118             :     }
     119             : 
     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))
     122             : 
     123             : private:
     124             :     InserterWithoutDuplicates& operator=(const InserterWithoutDuplicates&);
     125             : };
     126             : 
     127           0 : void CanonicaliseWeights(std::vector<FCDJointWeightPair>& weights)
     128             : {
     129             :     // Convert weight-lists into a standard format, so simple vector equality
     130             :     // can be used to determine equivalence
     131           0 :     std::sort(weights.begin(), weights.end());
     132           0 : }
     133             : 
     134           4 : void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
     135             : {
     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.
     140             : 
     141           4 :     FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
     142           4 :     FCDGeometryPolygonsInput* inputNormal   = polys->FindInput(FUDaeGeometryInput::NORMAL);
     143           4 :     FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);
     144             : 
     145           4 :     size_t numVertices = polys->GetFaceVertexCount();
     146             : 
     147           4 :     assert(inputPosition->GetIndexCount() == numVertices);
     148           4 :     assert(inputNormal  ->GetIndexCount() == numVertices);
     149           4 :     assert(inputTexcoord->GetIndexCount() == numVertices);
     150             : 
     151           4 :     const uint32* indicesPosition = inputPosition->GetIndices();
     152           4 :     const uint32* indicesNormal   = inputNormal->GetIndices();
     153           4 :     const uint32* indicesTexcoord = inputTexcoord->GetIndices();
     154             : 
     155           4 :     assert(indicesPosition);
     156           4 :     assert(indicesNormal);
     157           4 :     assert(indicesTexcoord); // TODO - should be optional, because textureless meshes aren't unreasonable
     158             : 
     159           8 :     FCDGeometrySourceList texcoordSources;
     160           4 :     polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
     161             : 
     162           4 :     FCDGeometrySource* sourcePosition = inputPosition->GetSource();
     163           4 :     FCDGeometrySource* sourceNormal   = inputNormal  ->GetSource();
     164             : 
     165           4 :     const float* dataPosition = sourcePosition->GetData();
     166           4 :     const float* dataNormal   = sourceNormal  ->GetData();
     167             : 
     168           4 :     if (skin)
     169             :     {
     170             : #ifndef NDEBUG
     171           0 :         size_t numVertexPositions = sourcePosition->GetDataCount() / sourcePosition->GetStride();
     172           0 :         assert(skin->GetInfluenceCount() == numVertexPositions);
     173             : #endif
     174             :     }
     175             : 
     176           4 :     uint32 stridePosition = sourcePosition->GetStride();
     177           4 :     uint32 strideNormal   = sourceNormal  ->GetStride();
     178             : 
     179           8 :     std::vector<uint32> indicesCombined;
     180           8 :     std::vector<VertexData> vertexes;
     181           8 :     InserterWithoutDuplicates<VertexData> inserter(vertexes);
     182             : 
     183        1006 :     for (size_t i = 0; i < numVertices; ++i)
     184             :     {
     185        2004 :         std::vector<FCDJointWeightPair> weights;
     186        1002 :         if (skin)
     187             :         {
     188           0 :             FCDSkinControllerVertex* influences = skin->GetVertexInfluence(indicesPosition[i]);
     189           0 :             assert(influences != NULL);
     190           0 :             for (size_t j = 0; j < influences->GetPairCount(); ++j)
     191             :             {
     192           0 :                 FCDJointWeightPair* pair = influences->GetPair(j);
     193           0 :                 assert(pair != NULL);
     194           0 :                 weights.push_back(*pair);
     195             :             }
     196           0 :             CanonicaliseWeights(weights);
     197             :         }
     198             : 
     199        2004 :         std::vector<uv_pair_type> uvs;
     200        2004 :         for (size_t set = 0; set < texcoordSources.size(); ++set)
     201             :         {
     202        1002 :             const float* dataTexcoord = texcoordSources[set]->GetData();
     203        1002 :             uint32 strideTexcoord = texcoordSources[set]->GetStride();
     204             : 
     205        1002 :             uv_pair_type p;
     206        1002 :             p.first = dataTexcoord[indicesTexcoord[i]*strideTexcoord];
     207        1002 :             p.second = dataTexcoord[indicesTexcoord[i]*strideTexcoord + 1];
     208        1002 :             uvs.push_back(p);
     209             :         }
     210             : 
     211             :         VertexData vtx (
     212        1002 :             &dataPosition[indicesPosition[i]*stridePosition],
     213        1002 :             &dataNormal  [indicesNormal  [i]*strideNormal],
     214             :             uvs,
     215             :             weights
     216        3006 :         );
     217        1002 :         size_t idx = inserter.add(vtx);
     218        1002 :         indicesCombined.push_back((uint32)idx);
     219             :     }
     220             : 
     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)
     224             : 
     225           8 :     FloatList newDataPosition;
     226           8 :     FloatList newDataNormal;
     227           8 :     FloatList newDataTexcoord;
     228           8 :     std::vector<std::vector<FCDJointWeightPair> > newWeightedMatches;
     229             : 
     230         361 :     for (size_t i = 0; i < vertexes.size(); ++i)
     231             :     {
     232         357 :         newDataPosition.push_back(vertexes[i].x);
     233         357 :         newDataPosition.push_back(vertexes[i].y);
     234         357 :         newDataPosition.push_back(vertexes[i].z);
     235         357 :         newDataNormal  .push_back(vertexes[i].nx);
     236         357 :         newDataNormal  .push_back(vertexes[i].ny);
     237         357 :         newDataNormal  .push_back(vertexes[i].nz);
     238         357 :         newWeightedMatches.push_back(vertexes[i].weights);
     239             :     }
     240             : 
     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           4 :     inputPosition->SetIndices(&indicesCombined.front(), indicesCombined.size());
     244           4 :     inputNormal  ->SetIndices(&indicesCombined.front(), indicesCombined.size());
     245           4 :     inputTexcoord->SetIndices(&indicesCombined.front(), indicesCombined.size());
     246             : 
     247           8 :     for (size_t set = 0; set < texcoordSources.size(); ++set)
     248             :     {
     249           4 :         newDataTexcoord.clear();
     250         361 :         for (size_t i = 0; i < vertexes.size(); ++i)
     251             :         {
     252         357 :             newDataTexcoord.push_back(vertexes[i].uvs[set].first);
     253         357 :             newDataTexcoord.push_back(vertexes[i].uvs[set].second);
     254             :         }
     255           4 :         texcoordSources[set]->SetData(newDataTexcoord, 2);
     256             :     }
     257             : 
     258           4 :     sourcePosition->SetData(newDataPosition, 3);
     259           4 :     sourceNormal  ->SetData(newDataNormal,   3);
     260             : 
     261           4 :     if (skin)
     262             :     {
     263           0 :         skin->SetInfluenceCount(newWeightedMatches.size());
     264           0 :         for (size_t i = 0; i < newWeightedMatches.size(); ++i)
     265             :         {
     266           0 :             skin->GetVertexInfluence(i)->SetPairCount(0);
     267           0 :             for (size_t j = 0; j < newWeightedMatches[i].size(); ++j)
     268           0 :                 skin->GetVertexInfluence(i)->AddPair(newWeightedMatches[i][j].jointIndex, newWeightedMatches[i][j].weight);
     269             :         }
     270             :     }
     271           4 : }

Generated by: LCOV version 1.13