Универсальный способ сборки VertexBuffer'а при экспорте без дублирования вершин
Автор: Frankinshtein
Обычная сложность на этапе экспорта/загрузки модели – это сборка правильного вершинного и индексного буфера, потому как, обычно:
количество позиций (вертексов) != количеству нормалей != количеству текстурных координат (а если каналов еще несколько?) и тд.
Этот способ универсален и позволяет собрать правильный Vertex и Index Buffer только из абсолютно уникальных вершин модели.
Недостатком является, его медленная скорость работы на highpoly моделях, потому лучше выполнять сборку на этапе экспорта или подготовки модели.
////file : indexator.h #pragma once #include <vector> #include <map> #include <assert.h> template<class VERTEX> class CIndexator { typedef std::map<VERTEX, unsigned int> VertSet; VertSet m_Vertexs; unsigned int m_uiIndex; public: CIndexator():m_uiIndex( 0){} ~CIndexator( ){} unsigned int PushVertex( const VERTEX &v); unsigned int GetIndex( const VERTEX &v) const; void GetVertices( std::vector<VERTEX> &) const; };
Псевдокод для работы с классом:
//наш вертекс struct vertex { enum{ELEMENTS = 8};//8 = 3 позиции + 3 нормали + 2 текст. координаты union { struct { float px, py, pz;//позиции float nx, ny, nz;//нормали float s, t;//текст. координаты }; float v[ELEMENTS]; }; /*добавить в него можно что угодно, но не забываем про bool operator < (const vertex &r) const */ /*оператор "bool operator < (const vertex &r) const ", необходим для работы с std::map, тут мы сравниваем 2 вертекса, для корректной вставки в дерево*/ bool operator < (const vertex &r) const { for ( int i=0; i < vertex::ELEMENTS; ++i) { if ( v[i]!=r.v[i]) //при необходимости, можно добавить EPSILON return v[i] < r.v[i]; } return false; } }; ////////////////////// vertex GetVertexFromModel( int triangle, int v) { vertex v; ........................... //заполняем вертекс return v; } CIndexator<vertex> indexator; std::vector<unsigned int> myIB;//здесь будет конечный Index Buffer std::vector<vertex> myVB;//здесь будет конечный Vertex Buffer for ( int t = 0; t < triangles_count; ++t) { for ( int v=0; v<3; ++v) { //получаем вертекс для данного треугольника и его номера вершины vertex vert = GetVertexFromModel( t, v); unsigned int index = indexator.PushVertex( vert); myIB.push_back ( index); } } indexator.GetVertices( myVB); //уникальный VB готов (myVB) //индексы для него готовы (myIB)
Продолжение файла indexator.h
//file:indexator.h template<class VERTEX> unsigned int CIndexator<VERTEX>::PushVertex(const VERTEX &v) { VertSet::iterator i = m_Vertexs.find( v); if ( i==m_Vertexs.end( ))//не нашли вершину { unsigned int index = m_uiIndex++; m_Vertexs[v] = index; return index; } return i->second; } template<class VERTEX> unsigned int CIndexator<VERTEX>::GetIndex( const VERTEX &v) const { VertSet::const_iterator i = m_Vertexs.find( v); if ( i!=m_Vertexs.end( )) return i->second; assert( !"can't find vertex!"); return 0; } template<class VERTEX> void CIndexator<VERTEX>::GetVertices( std::vector<VERTEX> &vSorted) const { vSorted.resize( m_Vertexs.size( )); for ( VertSet::const_iterator i=m_Vertexs.begin( ); i!=m_Vertexs.end( ); ++i) { unsigned int ind = i->second; vSorted[ind] = i->first; } }
9 февраля 2007 (Обновление: 15 фев. 2007)