16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-23 15:39:19 +02:00

basic indexed geometry support, geometry data packing

This commit is contained in:
tmj-fstate
2020-10-18 03:08:59 +02:00
parent af3a724ede
commit 5d252745bd
17 changed files with 975 additions and 512 deletions

View File

@@ -170,7 +170,7 @@ void TSubModel::SetSelfIllum( float const Threshold, bool const Includechildren,
int TSubModel::SeekFaceNormal(std::vector<unsigned int> const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, gfx::vertex_array const &Vertices) int TSubModel::SeekFaceNormal(std::vector<unsigned int> const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, gfx::vertex_array const &Vertices)
{ // szukanie punktu stycznego do (pt), zwraca numer wierzchołka, a nie trójkąta { // szukanie punktu stycznego do (pt), zwraca numer wierzchołka, a nie trójkąta
int facecount = iNumVerts / 3; // bo maska powierzchni jest jedna na trójkąt int facecount = m_geometry.vertex_count / 3;
for( int faceidx = Startface; faceidx < facecount; ++faceidx ) { for( int faceidx = Startface; faceidx < facecount; ++faceidx ) {
// pętla po trójkątach, od trójkąta (f) // pętla po trójkątach, od trójkąta (f)
if( Masks[ faceidx ] & Mask ) { if( Masks[ faceidx ] & Mask ) {
@@ -208,16 +208,12 @@ inline void readMatrix(cParser &parser, float4x4 &matrix)
parser >> matrix(x)[y]; parser >> matrix(x)[y];
}; };
int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic) std::pair<int, int> TSubModel::Load( cParser &parser, bool dynamic )
{ // Ra: VBO tworzone na poziomie modelu, a nie submodeli { // Ra: VBO tworzone na poziomie modelu, a nie submodeli
iNumVerts = 0;
/*
iVboPtr = Pos; // pozycja w VBO
*/
auto token { parser.getToken<std::string>() }; auto token { parser.getToken<std::string>() };
if( token != "type:" ) { if( token != "type:" ) {
std::string errormessage { std::string errormessage {
"Bad model: expected submodel type definition not found while loading model \"" + Model->NameGet() + "\"" "Bad model: expected submodel type definition not found while loading model \"" + pRoot->NameGet() + "\""
+ "\ncurrent model data stream content: \"" }; + "\ncurrent model data stream content: \"" };
auto count { 10 }; auto count { 10 };
while( ( true == parser.getTokens() ) while( ( true == parser.getTokens() )
@@ -230,7 +226,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
} }
errormessage += "(...)\""; errormessage += "(...)\"";
ErrorLog( errormessage ); ErrorLog( errormessage );
return 0; return { 0, 0 };
} }
{ {
auto const type { parser.getToken<std::string>() }; auto const type { parser.getToken<std::string>() };
@@ -352,10 +348,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
if( fCosHotspotAngle > 1.0 ) { if( fCosHotspotAngle > 1.0 ) {
fCosHotspotAngle = std::cos( DegToRad( 0.5f * fCosHotspotAngle ) ); fCosHotspotAngle = std::cos( DegToRad( 0.5f * fCosHotspotAngle ) );
} }
iNumVerts = 1; m_geometry.vertex_count = 1;
/*
iFlags |= 0x4010; // rysowane w cyklu nieprzezroczystych, macierz musi zostać bez zmiany
*/
iFlags |= 0x4030; // drawn both in solid (light point) and transparent (light glare) phases iFlags |= 0x4030; // drawn both in solid (light point) and transparent (light glare) phases
} }
else if (eType < TP_ROTATOR) else if (eType < TP_ROTATOR)
@@ -471,7 +464,10 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
// visibility range // visibility range
std::string discard; std::string discard;
parser.getTokens(5, false); parser.getTokens(5, false);
parser >> discard >> fSquareMaxDist >> discard >> fSquareMinDist >> discard; parser
>> discard >> fSquareMaxDist
>> discard >> fSquareMinDist
>> discard;
if( fSquareMaxDist <= 0.0 ) { if( fSquareMaxDist <= 0.0 ) {
// 15km to więcej, niż się obecnie wyświetla // 15km to więcej, niż się obecnie wyświetla
@@ -503,168 +499,196 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
} }
if (eType < TP_ROTATOR) if (eType < TP_ROTATOR)
{ // wczytywanie wierzchołków { // wczytywanie wierzchołków
parser.getTokens(2, false); token = parser.getToken<std::string>();
parser >> discard >> token; if( token == "numindices:" ) // optional block, potentially preceeding vertex list
// Ra 15-01: to wczytać jako tekst - jeśli pierwszy znak zawiera "*", to {
// dalej będzie nazwa wcześniejszego submodelu, z którego należy wziąć m_geometry.index_count = parser.getToken<int>( false );
// wierzchołki Indices.resize( m_geometry.index_count );
// zapewni to jakąś zgodność wstecz, bo zamiast liczby będzie ciąg, którego for( auto idx = 0; idx < m_geometry.index_count; ++idx ) {
// wartość powinna być uznana jako zerowa Indices[ idx ] = parser.getToken<unsigned int>( false );
// parser.getToken(iNumVerts); }
if (token.front() == '*') token = parser.getToken<std::string>();
{ // jeśli pierwszy znak jest gwiazdką, poszukać }
// submodelu o nazwie bez tej gwiazdki i wziąć z if( token == "numverts:" )
// niego wierzchołki
Error("Vertices reference not yet supported!");
}
else
{ // normalna lista wierzchołków { // normalna lista wierzchołków
iNumVerts = std::atoi(token.c_str()); /*
if (iNumVerts % 3) // Ra 15-01: to wczytać jako tekst - jeśli pierwszy znak zawiera "*", to
// dalej będzie nazwa wcześniejszego submodelu, z którego należy wziąć wierzchołki
// zapewni to jakąś zgodność wstecz, bo zamiast liczby będzie ciąg, którego
// wartość powinna być uznana jako zerowa
token = parser.getToken<std::string>();
if( token.front() == '*' ) {
// jeśli pierwszy znak jest gwiazdką, poszukać submodelu o nazwie bez tej gwiazdki i wziąć z niego wierzchołki
Error( "Vertices reference not yet supported!" );
}
*/
m_geometry.vertex_count = parser.getToken<int>(false);
if( ( m_geometry.index_count <= 0 )
&& ( m_geometry.vertex_count % 3 != 0 ) )
{ {
iNumVerts = 0; m_geometry.vertex_count = 0;
Error("Mesh error, (iNumVertices=" + std::to_string(iNumVerts) + ")%3<>0"); Error("Bad model: incomplete triangle encountered in submodel \"" + pName + "\"");
return 0; return { 0, 0 };
} }
// Vertices=new GLVERTEX[iNumVerts]; if ( m_geometry.vertex_count ) {
if (iNumVerts) { Vertices.resize( m_geometry.vertex_count );
/* if( m_geometry.index_count ) {
Vertices = new basic_vertex[iNumVerts]; // indexed geometry chunks are expected to come with pre-generated normals and tangents, to avoid hassle required to generate them manually
*/ auto vertices { std::begin( Vertices ) };
Vertices.resize( iNumVerts ); for( auto idx = 0; idx < m_geometry.vertex_count; ++idx ) {
int facecount = iNumVerts / 3; auto vertex { vertices + idx };
/* parser.getTokens( 3 + 3 + 2 + 3, false );
unsigned int *sg; // maski przynależności trójkątów do powierzchni parser
sg = new unsigned int[iNumFaces]; // maski powierzchni: 0 oznacza brak użredniania wektorów normalnych >> vertex->position.x
int *wsp = new int[iNumVerts]; // z którego wierzchołka kopiować wektor normalny >> vertex->position.y
*/ >> vertex->position.z
std::vector<unsigned int> sg; sg.resize( facecount ); // maski przynależności trójkątów do powierzchni >> vertex->normal.x
std::vector<int> wsp; wsp.resize( iNumVerts );// z którego wierzchołka kopiować wektor normalny >> vertex->normal.y
int maska = 0; >> vertex->normal.z
int rawvertexcount = 0; // used to keep track of vertex indices in source file >> vertex->texture.s
for (int i = 0; i < iNumVerts; ++i) { >> vertex->texture.t
++rawvertexcount; >> vertex->tangent.x
// Ra: z konwersją na układ scenerii - będzie wydajniejsze wyświetlanie >> vertex->tangent.y
wsp[i] = -1; // wektory normalne nie są policzone dla tego wierzchołka >> vertex->tangent.z;
if ((i % 3) == 0) {
// jeśli będzie maska -1, to dalej będą wierzchołki z wektorami normalnymi, podanymi jawnie
maska = parser.getToken<int>(false); // maska powierzchni trójkąta
// dla maski -1 będzie 0, czyli nie ma wspólnych wektorów normalnych
sg[i / 3] = (
( maska == -1 ) ?
0 :
maska );
}
parser.getTokens(3, false);
parser
>> Vertices[i].position.x
>> Vertices[i].position.y
>> Vertices[i].position.z;
if (maska == -1)
{ // jeśli wektory normalne podane jawnie
parser.getTokens(3, false);
parser
>> Vertices[i].normal.x
>> Vertices[i].normal.y
>> Vertices[i].normal.z;
if( glm::length2( Vertices[ i ].normal ) > 0.0f ) {
glm::normalize( Vertices[ i ].normal );
}
else {
WriteLog( "Bad model: zero length normal vector specified in: \"" + pName + "\", vertex " + std::to_string(i), logtype::model );
}
wsp[i] = i; // wektory normalne "są już policzone"
}
parser.getTokens(2, false);
parser
>> Vertices[i].texture.s
>> Vertices[i].texture.t;
if (i % 3 == 2) {
// jeżeli wczytano 3 punkty
if( true == degenerate( Vertices[ i ].position, Vertices[ i - 1 ].position, Vertices[ i - 2 ].position ) ) {
// jeżeli punkty się nakładają na siebie
--facecount; // o jeden trójkąt mniej
iNumVerts -= 3; // czyli o 3 wierzchołki
i -= 3; // wczytanie kolejnego w to miejsce
WriteLog("Bad model: degenerated triangle ignored in: \"" + pName + "\", vertices " + std::to_string(rawvertexcount-2) + "-" + std::to_string(rawvertexcount), logtype::model );
}
if (i > 0) {
// jeśli pierwszy trójkąt będzie zdegenerowany, to zostanie usunięty i nie ma co sprawdzać
if ((glm::length(Vertices[i ].position - Vertices[i - 1].position) > 1000.0)
|| (glm::length(Vertices[i - 1].position - Vertices[i - 2].position) > 1000.0)
|| (glm::length(Vertices[i - 2].position - Vertices[i ].position) > 1000.0)) {
// jeżeli są dalej niż 2km od siebie //Ra 15-01:
// obiekt wstawiany nie powinien być większy niż 300m (trójkąty terenu w E3D mogą mieć 1.5km)
--facecount; // o jeden trójkąt mniej
iNumVerts -= 3; // czyli o 3 wierzchołki
i -= 3; // wczytanie kolejnego w to miejsce
WriteLog( "Bad model: too large triangle ignored in: \"" + pName + "\"", logtype::model );
}
}
}
}
/*
glm::vec3 *n = new glm::vec3[iNumFaces]; // tablica wektorów normalnych dla trójkątów
*/
std::vector<glm::vec3> facenormals; facenormals.reserve( facecount );
for( int i = 0; i < facecount; ++i ) {
// pętla po trójkątach - będzie szybciej, jak wstępnie przeliczymy normalne trójkątów
auto facenormal =
glm::cross(
Vertices[ i * 3 ].position - Vertices[ i * 3 + 1 ].position,
Vertices[ i * 3 ].position - Vertices[ i * 3 + 2 ].position );
facenormals.emplace_back(
glm::length2( facenormal ) > 0.0f ?
glm::normalize( facenormal ) :
glm::vec3() );
}
glm::vec3 vertexnormal; // roboczy wektor normalny
for (int vertexidx = 0; vertexidx < iNumVerts; ++vertexidx) {
// pętla po wierzchołkach trójkątów
if( wsp[ vertexidx ] >= 0 ) {
// jeśli już był liczony wektor normalny z użyciem tego wierzchołka to wystarczy skopiować policzony wcześniej
Vertices[ vertexidx ].normal = Vertices[ wsp[ vertexidx ] ].normal;
} }
else { }
// inaczej musimy dopiero policzyć else {
auto const faceidx = vertexidx / 3; // numer trójkąta // legacy geometry, more or less incomplete
vertexnormal = glm::vec3(); // liczenie zaczynamy od zera // to stay on the safe side we'll potentially need to convert indexed geometry to raw triangle form, cal
auto adjacenvertextidx = vertexidx; // zaczynamy dodawanie wektorów normalnych od własnego auto vertices { std::begin( Vertices ) };
while (adjacenvertextidx >= 0) { int facecount = m_geometry.vertex_count / 3;
// sumowanie z wektorem normalnym sąsiada (włącznie ze sobą) std::vector<unsigned int> sg; sg.resize( facecount ); // maski przynależności trójkątów do powierzchni
if( glm::dot( vertexnormal, facenormals[ adjacenvertextidx / 3 ] ) > -0.99f ) { std::vector<int> wsp; wsp.resize( m_geometry.vertex_count );// z którego wierzchołka kopiować wektor normalny
wsp[ adjacenvertextidx ] = vertexidx; // informacja, że w tym wierzchołku jest już policzony wektor normalny int maska = 0;
vertexnormal += facenormals[ adjacenvertextidx / 3 ]; int vertexidx = 0; // used to keep track of vertex indices in source file
for (auto idx = 0; idx < m_geometry.vertex_count; ++idx) {
++vertexidx;
// Ra: z konwersją na układ scenerii - będzie wydajniejsze wyświetlanie
wsp[idx] = -1; // wektory normalne nie są policzone dla tego wierzchołka
if ((idx % 3) == 0) {
// jeśli będzie maska -1, to dalej będą wierzchołki z wektorami normalnymi, podanymi jawnie
maska = parser.getToken<int>(false); // maska powierzchni trójkąta
// dla maski -1 będzie 0, czyli nie ma wspólnych wektorów normalnych
sg[idx / 3] = (
( maska == -1 ) ?
0 :
maska );
}
auto vertex { vertices + idx };
parser.getTokens(3, false);
parser
>> vertex->position.x
>> vertex->position.y
>> vertex->position.z;
if (maska == -1)
{ // jeśli wektory normalne podane jawnie
parser.getTokens(3, false);
parser
>> vertex->normal.x
>> vertex->normal.y
>> vertex->normal.z;
if( glm::length2( vertex->normal ) > 0.0f ) {
glm::normalize( vertex->normal );
} }
/*
else { else {
ErrorLog( "Bad model: opposite normals in the same smoothing group, check sub-model \"" + pName + "\" for two-sided faces and/or scaling", logtype::model ); WriteLog( "Bad model: zero length normal vector specified in submodel \"" + pName + "\", vertex " + std::to_string(idx), logtype::model );
} }
*/ wsp[idx] = idx; // wektory normalne "są już policzone"
// i szukanie od kolejnego trójkąta }
adjacenvertextidx = SeekFaceNormal(sg, adjacenvertextidx / 3 + 1, sg[faceidx], Vertices[vertexidx].position, Vertices); parser.getTokens(2, false);
parser
>> vertex->texture.s
>> vertex->texture.t;
if (idx % 3 == 2) {
// jeżeli wczytano 3 punkty
if( true == degenerate( vertex->position, (vertex - 1)->position, (vertex - 2)->position ) ) {
// jeżeli punkty się nakładają na siebie
--facecount; // o jeden trójkąt mniej
m_geometry.vertex_count -= 3; // czyli o 3 wierzchołki
idx -= 3; // wczytanie kolejnego w to miejsce
WriteLog("Bad model: degenerated triangle ignored in: \"" + pName + "\", vertices " + std::to_string(vertexidx-2) + "-" + std::to_string(vertexidx), logtype::model );
}
if (idx > 0) {
// jeśli pierwszy trójkąt będzie zdegenerowany, to zostanie usunięty i nie ma co sprawdzać
if ((glm::length((vertex )->position - (vertex-1)->position) > 1000.0)
|| (glm::length((vertex-1)->position - (vertex-2)->position) > 1000.0)
|| (glm::length((vertex-2)->position - (vertex )->position) > 1000.0)) {
// jeżeli są dalej niż 2km od siebie //Ra 15-01:
// obiekt wstawiany nie powinien być większy niż 300m (trójkąty terenu w E3D mogą mieć 1.5km)
--facecount; // o jeden trójkąt mniej
m_geometry.vertex_count -= 3; // czyli o 3 wierzchołki
idx -= 3; // wczytanie kolejnego w to miejsce
WriteLog( "Bad model: too large triangle ignored in: \"" + pName + "\"", logtype::model );
}
}
}
}
std::vector<glm::vec3> facenormals; facenormals.reserve( facecount );
for( int i = 0; i < facecount; ++i ) {
// pętla po trójkątach - będzie szybciej, jak wstępnie przeliczymy normalne trójkątów
auto const vertex { vertices + ( i * 3 ) };
auto facenormal =
glm::cross(
vertex->position - (vertex + 1)->position,
vertex->position - (vertex + 2)->position );
facenormals.emplace_back(
glm::length2( facenormal ) > 0.0f ?
glm::normalize( facenormal ) :
glm::vec3() );
}
glm::vec3 vertexnormal; // roboczy wektor normalny
for (int vertexidx = 0; vertexidx < m_geometry.vertex_count; ++vertexidx) {
// pętla po wierzchołkach trójkątów
auto vertex { vertices + vertexidx };
if( wsp[ vertexidx ] >= 0 ) {
// jeśli już był liczony wektor normalny z użyciem tego wierzchołka to wystarczy skopiować policzony wcześniej
vertex->normal = ( vertices + wsp[ vertexidx ] )->normal;
} }
// Ra 15-01: należało by jeszcze uwzględnić skalowanie wprowadzane przez transformy, aby normalne po przeskalowaniu były jednostkowe else {
if( glm::length2( vertexnormal ) == 0.0f ) { // inaczej musimy dopiero policzyć
WriteLog( "Bad model: zero length normal vector generated for sub-model \"" + pName + "\"", logtype::model ); auto const faceidx = vertexidx / 3; // numer trójkąta
} vertexnormal = glm::vec3(); // liczenie zaczynamy od zera
Vertices[ vertexidx ].normal = ( auto adjacenvertextidx = vertexidx; // zaczynamy dodawanie wektorów normalnych od własnego
glm::length2( vertexnormal ) > 0.0f ? while (adjacenvertextidx >= 0) {
glm::normalize( vertexnormal ) : // sumowanie z wektorem normalnym sąsiada (włącznie ze sobą)
facenormals[ vertexidx / 3 ] ); // przepisanie do wierzchołka trójkąta if( glm::dot( vertexnormal, facenormals[ adjacenvertextidx / 3 ] ) > -0.99f ) {
} wsp[ adjacenvertextidx ] = vertexidx; // informacja, że w tym wierzchołku jest już policzony wektor normalny
} vertexnormal += facenormals[ adjacenvertextidx / 3 ];
Vertices.resize( iNumVerts ); // in case we had some degenerate triangles along the way }
/* /*
delete[] wsp; else {
delete[] n; ErrorLog( "Bad model: opposite normals in the same smoothing group, check sub-model \"" + pName + "\" for two-sided faces and/or scaling", logtype::model );
delete[] sg; }
*/ */
} // i szukanie od kolejnego trójkąta
adjacenvertextidx = SeekFaceNormal(sg, adjacenvertextidx / 3 + 1, sg[faceidx], vertex->position, Vertices);
}
// Ra 15-01: należało by jeszcze uwzględnić skalowanie wprowadzane przez transformy, aby normalne po przeskalowaniu były jednostkowe
if( glm::length2( vertexnormal ) == 0.0f ) {
WriteLog( "Bad model: zero length normal vector generated for sub-model \"" + pName + "\"", logtype::model );
}
vertex->normal = (
glm::length2( vertexnormal ) > 0.0f ?
glm::normalize( vertexnormal ) :
facenormals[ vertexidx / 3 ] ); // przepisanie do wierzchołka trójkąta
}
}
Vertices.resize( m_geometry.vertex_count ); // in case we had some degenerate triangles along the way
gfx::calculate_tangents( Vertices, GL_TRIANGLES );
gfx::calculate_indices( Indices, Vertices );
// update values potentially changed by indexing
m_geometry.index_count = Indices.size();
m_geometry.vertex_count = Vertices.size();
}
}
else // gdy brak wierzchołków else // gdy brak wierzchołków
{ {
eType = TP_ROTATOR; // submodel pomocniczy, ma tylko macierz przekształcenia eType = TP_ROTATOR; // submodel pomocniczy, ma tylko macierz przekształcenia
/*iVboPtr =*/ iNumVerts = 0; // dla formalności // dla formalności
m_geometry.vertex_offset = 0;
m_geometry.vertex_count = 0;
} }
} // obsługa submodelu z własną listą wierzchołków } // obsługa submodelu z własną listą wierzchołków
} }
@@ -673,44 +697,46 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
// dla smt_Mesh // dla smt_Mesh
std::string discard; std::string discard;
parser.getTokens(2, false); parser.getTokens(2, false);
parser >> discard >> iNumVerts; parser >> discard >> m_geometry.vertex_count;
/* Vertices.resize( m_geometry.vertex_count );
// Vertices=new GLVERTEX[iNumVerts]; int idx;
Vertices = new basic_vertex[iNumVerts];
*/
Vertices.resize( iNumVerts );
int i;
unsigned int color; unsigned int color;
for (i = 0; i < iNumVerts; ++i) auto vertices { std::begin( Vertices ) };
for (idx = 0; idx < m_geometry.vertex_count; ++idx)
{ {
if (i % 3 == 0) if (idx % 3 == 0)
{ {
parser.ignoreToken(); // maska powierzchni trójkąta parser.ignoreToken(); // maska powierzchni trójkąta
} }
parser.getTokens(5, false); auto vertex { vertices + idx };
parser.getTokens(5, false);
parser parser
>> Vertices[i].position.x >> vertex->position.x
>> Vertices[i].position.y >> vertex->position.y
>> Vertices[i].position.z >> vertex->position.z
>> color // zakodowany kolor >> color // zakodowany kolor
>> discard; >> discard;
Vertices[i].normal.x = ((color) & 0xff) / 255.0f; // R vertex->normal = { ( ( color ) & 0xff ) / 255.0f, // R
Vertices[i].normal.y = ((color >> 8) & 0xff) / 255.0f; // G ( ( color >> 8 ) & 0xff ) / 255.0f, // G
Vertices[i].normal.z = ((color >> 16) & 0xff) / 255.0f; // B ( ( color >> 16 ) & 0xff ) / 255.0f }; // B
} }
} }
else if( eType == TP_FREESPOTLIGHT ) { else if( eType == TP_FREESPOTLIGHT ) {
// single light points only have single data point, duh // single light points only have single data point, duh
Vertices.emplace_back(); Vertices.emplace_back();
iNumVerts = 1; m_geometry.vertex_count = 1;
} }
// Visible=true; //się potem wyłączy w razie potrzeby // Visible=true; //się potem wyłączy w razie potrzeby
// iFlags|=0x0200; //wczytano z pliku tekstowego (jest właścicielem tablic) // iFlags|=0x0200; //wczytano z pliku tekstowego (jest właścicielem tablic)
if (iNumVerts < 1) if (m_geometry.vertex_count < 1)
iFlags &= ~0x3F; // cykl renderowania uzależniony od potomnych iFlags &= ~0x3F; // cykl renderowania uzależniony od potomnych
return iNumVerts; // do określenia wielkości VBO
return {
m_geometry.index_count,
m_geometry.vertex_count }; // do określenia wielkości VBO
}; };
/*
int TSubModel::TriangleAdd(TModel3d *m, material_handle tex, int tri) int TSubModel::TriangleAdd(TModel3d *m, material_handle tex, int tri)
{ // dodanie trójkątów do submodelu, używane przy tworzeniu E3D terenu { // dodanie trójkątów do submodelu, używane przy tworzeniu E3D terenu
TSubModel *s = this; TSubModel *s = this;
@@ -740,6 +766,7 @@ int TSubModel::TriangleAdd(TModel3d *m, material_handle tex, int tri)
s->iNumVerts += tri; // aktualizacja ilości wierzchołków s->iNumVerts += tri; // aktualizacja ilości wierzchołków
return s->iNumVerts - tri; // zwraca pozycję tych trójkątów w submodelu return s->iNumVerts - tri; // zwraca pozycję tych trójkątów w submodelu
}; };
*/
/* /*
basic_vertex *TSubModel::TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ) basic_vertex *TSubModel::TrianglePtr(int tex, int pos, glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular )
{ // zwraca wskaźnik do wypełnienia tabeli wierzchołków, używane przy tworzeniu E3D terenu { // zwraca wskaźnik do wypełnienia tabeli wierzchołków, używane przy tworzeniu E3D terenu
@@ -1156,49 +1183,111 @@ void TSubModel::RaAnimation(glm::mat4 &m, TAnimType a)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void TSubModel::serialize_geometry( std::ostream &Output ) const { void TSubModel::serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed ) const {
if( Child ) { if( Child ) {
Child->serialize_geometry( Output ); Child->serialize_geometry( Output, Packed, Indexed );
} }
if( m_geometry != null_handle ) { if( m_geometry.handle != null_handle ) {
for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) { if( Packed ) {
vertex.serialize( Output ); for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) {
vertex.serialize_packed( Output, Indexed );
}
}
else {
for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) {
vertex.serialize( Output, Indexed );
}
} }
} }
if( Next ) { if( Next ) {
Next->serialize_geometry( Output ); Next->serialize_geometry( Output, Packed, Indexed );
}
};
int TSubModel::index_size() const {
int size { 1 };
if( Child ) {
size = std::max( size, Child->index_size() );
}
if( ( size < 4 ) && ( m_geometry.handle != null_handle ) ) {
auto const indexcount { GfxRenderer->Indices( m_geometry.handle ).size() };
size = (
indexcount >= ( 1 << 16 ) ? 4 :
indexcount >= ( 1 << 8 ) ? 2 :
1 );
}
if( ( size < 4 ) && ( Next ) ) {
size = std::max( size, Next->index_size() );
}
return size;
}
void TSubModel::serialize_indices( std::ostream &Output, int const Size ) const {
if( Child ) {
Child->serialize_indices( Output, Size );
}
if( m_geometry.handle != null_handle ) {
switch( Size ) {
case 1: {
for( auto const &index : GfxRenderer->Indices( m_geometry.handle ) ) {
sn_utils::s_uint8( Output, index );
}
break;
}
case 2: {
for( auto const &index : GfxRenderer->Indices( m_geometry.handle ) ) {
sn_utils::ls_uint16( Output, index );
}
break;
}
case 4: {
for( auto const &index : GfxRenderer->Indices( m_geometry.handle ) ) {
sn_utils::ls_uint32( Output, index );
}
break;
}
default: { break; }
}
}
if( Next ) {
Next->serialize_indices( Output, Size );
} }
}; };
void void
TSubModel::create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle const &Bank ) { TSubModel::create_geometry( std::size_t &Indexoffset, std::size_t &Vertexoffset, gfx::geometrybank_handle const &Bank ) {
// data offset is used to determine data offset of each submodel into single shared geometry bank // data offset is used to determine data offset of each submodel into single shared geometry bank
// (the offsets are part of legacy system which we now need to work around for backward compatibility) // (the offsets are part of legacy system which we now need to work around for backward compatibility)
if( Child ) if( Child ) {
Child->create_geometry( Dataoffset, Bank ); Child->create_geometry( Indexoffset, Vertexoffset, Bank );
}
if( false == Vertices.empty() ) { if( false == Vertices.empty() ) {
tVboPtr = static_cast<int>( Dataoffset ); m_geometry.index_offset = static_cast<int>( Indexoffset );
Dataoffset += Vertices.size(); Indexoffset += Indices.size();
m_geometry.vertex_offset = static_cast<int>( Vertexoffset );
Vertexoffset += Vertices.size();
// conveniently all relevant custom node types use GL_POINTS, or we'd have to determine the type on individual basis // conveniently all relevant custom node types use GL_POINTS, or we'd have to determine the type on individual basis
auto type = ( auto type = (
eType < TP_ROTATOR ? eType < TP_ROTATOR ?
eType : eType :
GL_POINTS ); GL_POINTS );
m_geometry = GfxRenderer->Insert( Vertices, Bank, type ); m_geometry.handle = GfxRenderer->Insert( Indices, Vertices, Bank, type );
} }
if( m_geometry != NULL ) { if( m_geometry.handle != NULL ) {
// calculate bounding radius while we're at it // calculate bounding radius while we're at it
float squaredradius {}; float squaredradius {};
// if this happens to be root node it may already have non-squared radius of the largest child // if this happens to be root node it may already have non-squared radius of the largest child
// since we're comparing squared radii, we need to square it back for correct results // since we're comparing squared radii, we need to square it back for correct results
m_boundingradius *= m_boundingradius; m_boundingradius *= m_boundingradius;
auto const submodeloffset { offset( std::numeric_limits<float>::max() ) }; auto const submodeloffset { offset( std::numeric_limits<float>::max() ) };
for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) { for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) {
squaredradius = glm::length2( submodeloffset + vertex.position ); squaredradius = glm::length2( submodeloffset + vertex.position );
if( squaredradius > m_boundingradius ) { if( squaredradius > m_boundingradius ) {
m_boundingradius = squaredradius; m_boundingradius = squaredradius;
@@ -1216,8 +1305,9 @@ TSubModel::create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle co
m_boundingradius ); m_boundingradius );
} }
if( Next ) if( Next ) {
Next->create_geometry( Dataoffset, Bank ); Next->create_geometry( Indexoffset, Vertexoffset, Bank );
}
} }
void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ) void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular )
@@ -1294,13 +1384,13 @@ void TSubModel::ParentMatrix( float4x4 *m ) const {
// obliczenie maksymalnej wysokości, na początek ślizgu w pantografie // obliczenie maksymalnej wysokości, na początek ślizgu w pantografie
float TSubModel::MaxY( float4x4 const &m ) { float TSubModel::MaxY( float4x4 const &m ) {
// tylko dla trójkątów liczymy // tylko dla trójkątów liczymy
if( eType != 4 ) { return 0; } if( eType != GL_TRIANGLES ) { return 0; }
auto maxy { 0.0f }; auto maxy { 0.0f };
// binary and text models invoke this function at different stages, either after or before geometry data was sent to the geometry manager // binary and text models invoke this function at different stages, either after or before geometry data was sent to the geometry manager
if( m_geometry != null_handle ) { if( m_geometry.handle != null_handle ) {
for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) { for( auto const &vertex : GfxRenderer->Vertices( m_geometry.handle ) ) {
maxy = std::max( maxy = std::max(
maxy, maxy,
m[ 0 ][ 1 ] * vertex.position.x m[ 0 ][ 1 ] * vertex.position.x
@@ -1325,14 +1415,6 @@ float TSubModel::MaxY( float4x4 const &m ) {
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
TModel3d::TModel3d()
{
Root = NULL;
iFlags = 0;
iSubModelsCount = 0;
iNumVerts = 0; // nie ma jeszcze wierzchołków
};
TModel3d::~TModel3d() { TModel3d::~TModel3d() {
if (iFlags & 0x0200) { if (iFlags & 0x0200) {
@@ -1412,8 +1494,8 @@ TSubModel::offset( float const Geometrytestoffsetthreshold ) const {
// for such cases we resort to an estimate from submodel geometry // for such cases we resort to an estimate from submodel geometry
// TODO: do proper bounding area calculation for submodel when loading mesh and grab the centre point from it here // TODO: do proper bounding area calculation for submodel when loading mesh and grab the centre point from it here
auto const &vertices { ( auto const &vertices { (
m_geometry != null_handle ? m_geometry.handle != null_handle ?
GfxRenderer->Vertices( m_geometry ) : GfxRenderer->Vertices( m_geometry.handle ) :
Vertices ) }; Vertices ) };
if( false == vertices.empty() ) { if( false == vertices.empty() ) {
// transformation matrix for the submodel can still contain rotation and/or scaling, // transformation matrix for the submodel can still contain rotation and/or scaling,
@@ -1523,16 +1605,15 @@ void TSubModel::serialize(std::ostream &s,
sn_utils::ls_int32(s, iFlags); sn_utils::ls_int32(s, iFlags);
sn_utils::ls_int32(s, (int32_t)get_container_pos(transforms, *fMatrix)); sn_utils::ls_int32(s, (int32_t)get_container_pos(transforms, *fMatrix));
sn_utils::ls_int32(s, iNumVerts); sn_utils::ls_int32( s, m_geometry.vertex_count );
sn_utils::ls_int32(s, tVboPtr); sn_utils::ls_int32( s, m_geometry.vertex_offset );
if (m_material <= 0) if (m_material <= 0)
sn_utils::ls_int32(s, m_material); sn_utils::ls_int32(s, m_material);
else else
sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, m_materialname)); sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, m_materialname));
// sn_utils::ls_float32(s, fVisible); sn_utils::ls_float32(s, 1.f); // fVisible
sn_utils::ls_float32(s, 1.f);
sn_utils::ls_float32(s, fLight); sn_utils::ls_float32(s, fLight);
sn_utils::s_vec4(s, f4Ambient); sn_utils::s_vec4(s, f4Ambient);
@@ -1554,6 +1635,9 @@ void TSubModel::serialize(std::ostream &s,
sn_utils::ls_float32(s, fCosHotspotAngle); sn_utils::ls_float32(s, fCosHotspotAngle);
sn_utils::ls_float32(s, fCosViewAngle); sn_utils::ls_float32(s, fCosViewAngle);
sn_utils::ls_int32( s, m_geometry.index_count );
sn_utils::ls_int32( s, m_geometry.index_offset );
size_t fill = end - s.tellp(); size_t fill = end - s.tellp();
for (size_t i = 0; i < fill; i++) for (size_t i = 0; i < fill; i++)
s.put(0); s.put(0);
@@ -1597,9 +1681,21 @@ void TModel3d::SaveToBinFile(std::string const &FileName)
for (size_t i = 0; i < transforms.size(); i++) for (size_t i = 0; i < transforms.size(); i++)
transforms[i].serialize_float32(s); transforms[i].serialize_float32(s);
sn_utils::ls_uint32(s, MAKE_ID4('V', 'N', 'T', '0')); auto const isindexed { m_indexcount > 0 };
sn_utils::ls_uint32(s, 8 + iNumVerts * 32); if( isindexed ) {
Root->serialize_geometry( s ); auto const indexsize { Root->index_size() };
sn_utils::ls_uint32( s, MAKE_ID4( 'I', 'D', 'X', '0' + indexsize ) );
sn_utils::ls_uint32( s, 8 + m_indexcount * indexsize );
Root->serialize_indices( s, indexsize );
sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '1' ) );
sn_utils::ls_uint32( s, 8 + m_vertexcount * 20 );
Root->serialize_geometry( s, true, true );
}
else {
sn_utils::ls_uint32( s, MAKE_ID4( 'V', 'N', 'T', '0' ) );
sn_utils::ls_uint32( s, 8 + m_vertexcount * 32 );
Root->serialize_geometry( s, false, false );
}
if (textures.size()) if (textures.size())
{ {
@@ -1648,12 +1744,11 @@ void TSubModel::deserialize(std::istream &s)
iFlags = sn_utils::ld_int32(s); iFlags = sn_utils::ld_int32(s);
iMatrix = sn_utils::ld_int32(s); iMatrix = sn_utils::ld_int32(s);
iNumVerts = sn_utils::ld_int32(s); m_geometry.vertex_count = sn_utils::ld_int32( s );
tVboPtr = sn_utils::ld_int32(s); m_geometry.vertex_offset = sn_utils::ld_int32( s );
iTexture = sn_utils::ld_int32(s); iTexture = sn_utils::ld_int32(s);
// fVisible = sn_utils::ld_float32(s); auto discard = sn_utils::ld_float32(s); // fVisible
auto discard = sn_utils::ld_float32(s);
fLight = sn_utils::ld_float32(s); fLight = sn_utils::ld_float32(s);
f4Ambient = sn_utils::d_vec4(s); f4Ambient = sn_utils::d_vec4(s);
@@ -1673,7 +1768,9 @@ void TSubModel::deserialize(std::istream &s)
fCosFalloffAngle = sn_utils::ld_float32(s); fCosFalloffAngle = sn_utils::ld_float32(s);
fCosHotspotAngle = sn_utils::ld_float32(s); fCosHotspotAngle = sn_utils::ld_float32(s);
fCosViewAngle = sn_utils::ld_float32(s); fCosViewAngle = sn_utils::ld_float32(s);
// HACK: the values will be 0 also when reading legacy chunk
m_geometry.index_count = sn_utils::ld_int32( s );
m_geometry.index_offset = sn_utils::ld_int32( s );
// necessary rotations were already done during t3d->e3d conversion // necessary rotations were already done during t3d->e3d conversion
m_rotation_init_done = true; m_rotation_init_done = true;
} }
@@ -1693,7 +1790,7 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic)
uint32_t size = sn_utils::ld_uint32(s) - 8; uint32_t size = sn_utils::ld_uint32(s) - 8;
std::streampos end = s.tellg() + (std::streampos)size; std::streampos end = s.tellg() + (std::streampos)size;
if ((type & 0x00FFFFFF) == MAKE_ID4('S', 'U', 'B', 0)) if ((type & 0x00FFFFFF) == MAKE_ID4('S', 'U', 'B', 0)) // submodels
{ {
if (Root != nullptr) if (Root != nullptr)
throw std::runtime_error("e3d: duplicated SUB chunk"); throw std::runtime_error("e3d: duplicated SUB chunk");
@@ -1709,68 +1806,104 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic)
Root[i].deserialize(s); Root[i].deserialize(s);
} }
} }
else if (type == MAKE_ID4('V', 'N', 'T', '0')) else if ((type & 0x00FFFFFF) == MAKE_ID4('V', 'N', 'T', 0)) // geometry vertices
{ {
/*
if (m_pVNT != nullptr)
throw std::runtime_error("e3d: duplicated VNT chunk");
size_t vt_cnt = size / 32;
iNumVerts = (int)vt_cnt;
m_nVertexCount = (int)vt_cnt;
m_pVNT.resize( vt_cnt );
for (size_t i = 0; i < vt_cnt; i++)
m_pVNT[i].deserialize(s);
*/
// we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk. // we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk.
// geometry associated with chunks isn't stored in the same order as the chunks themselves, so we need to sort that out first // geometry associated with chunks isn't stored in the same order as the chunks themselves, so we need to sort that out first
if( Root == nullptr ) if( Root == nullptr )
throw std::runtime_error( "e3d: VNT chunk encountered before SUB chunk" ); throw std::runtime_error( "e3d: VNT chunk encountered before SUB chunk" );
std::vector< std::pair<int, int> > submodeloffsets; // vertex data offset, submodel index std::vector< std::pair<int, int> > submodeloffsets; // vertex data offset, submodel index
submodeloffsets.reserve( iSubModelsCount ); submodeloffsets.reserve( iSubModelsCount );
for( int submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) { for( auto submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) {
auto const &submodel = Root[ submodelindex ]; auto const &submodelgeometry { Root[ submodelindex ].m_geometry };
if( submodel.iNumVerts <= 0 ) { continue; } if( submodelgeometry.vertex_count <= 0 ) { continue; }
submodeloffsets.emplace_back( submodel.tVboPtr, submodelindex ); submodeloffsets.emplace_back( submodelgeometry.vertex_offset, submodelindex );
} }
std::sort( std::sort(
submodeloffsets.begin(), std::begin( submodeloffsets ),
submodeloffsets.end(), std::end( submodeloffsets ),
[]( std::pair<int, int> const &Left, std::pair<int, int> const &Right ) { []( std::pair<int, int> const &Left, std::pair<int, int> const &Right ) {
return (Left.first) < (Right.first); } ); return (Left.first) < (Right.first); } );
// once sorted we can grab geometry as it comes, and assign it to the chunks it belongs to // once sorted we can grab geometry as it comes, and assign it to the chunks it belongs to
size_t const vertextype { ( ( ( type & 0xFF000000 ) >> 24 ) - '0' ) };
for( auto const &submodeloffset : submodeloffsets ) { for( auto const &submodeloffset : submodeloffsets ) {
auto &submodel = Root[ submodeloffset.second ]; auto &submodel { Root[ submodeloffset.second ] };
gfx::vertex_array vertices; vertices.resize( submodel.iNumVerts ); auto const &submodelgeometry { submodel.m_geometry };
iNumVerts += submodel.iNumVerts; submodel.Vertices.resize( submodelgeometry.vertex_count );
for( auto &vertex : vertices ) { m_vertexcount += submodelgeometry.vertex_count;
vertex.deserialize( s ); if( vertextype > 0 ) {
if( submodel.eType < TP_ROTATOR ) { // expanded chunk formats
// normal vectors debug routine for( auto &vertex : submodel.Vertices ) {
auto normallength = glm::length2( vertex.normal ); vertex.deserialize_packed( s, vertextype > 0 );
if( ( false == submodel.m_normalizenormals ) }
&& ( std::abs( normallength - 1.0f ) > 0.01f ) ) { }
submodel.m_normalizenormals = TSubModel::normalize; // we don't know if uniform scaling would suffice else {
WriteLog( "Bad model: non-unit normal vector(s) encountered during sub-model geometry deserialization", logtype::model ); // legacy vnt0 format
for( auto &vertex : submodel.Vertices ) {
vertex.deserialize( s, vertextype > 0 );
if( submodel.eType < TP_ROTATOR ) {
// normal vectors debug routine
if( ( false == submodel.m_normalizenormals )
&& ( std::abs( glm::length2( vertex.normal ) - 1.0f ) > 0.01f ) ) {
submodel.m_normalizenormals = TSubModel::normalize; // we don't know if uniform scaling would suffice
WriteLog( "Bad model: non-unit normal vector(s) encountered during sub-model geometry deserialization", logtype::model );
}
} }
} }
} }
// remap geometry type for custom type submodels }
int type; }
switch( submodel.eType ) { else if( ( type & 0x00FFFFFF ) == MAKE_ID4( 'I', 'D', 'X', 0 ) ) // geometry indices
case TP_FREESPOTLIGHT: {
case TP_STARS: { // handled similarly to vertex data chunk
type = GL_POINTS; // we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk.
break; } // geometry associated with chunks isn't stored in the same order as the chunks themselves, so we need to sort that out first
if( Root == nullptr )
throw std::runtime_error( "e3d: IDX chunk encountered before SUB chunk" );
std::vector< std::pair<int, int> > submodeloffsets; // index data offset, submodel index
submodeloffsets.reserve( iSubModelsCount );
for( auto submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) {
auto const &submodelgeometry { Root[ submodelindex ].m_geometry };
if( submodelgeometry.index_count <= 0 ) { continue; }
submodeloffsets.emplace_back( submodelgeometry.index_offset, submodelindex );
}
std::sort(
std::begin( submodeloffsets ),
std::end( submodeloffsets ),
[]( std::pair<int, int> const &Left, std::pair<int, int> const &Right ) {
return (Left.first) < (Right.first); } );
// once sorted we can grab indices in a continuous read, and assign them to the chunks they belong to
size_t const indexsize { ( ( ( type & 0xFF000000 ) >> 24 ) - '0' ) };
for( auto const &submodeloffset : submodeloffsets ) {
auto &submodel { Root[ submodeloffset.second ] };
auto const &submodelgeometry { submodel.m_geometry };
submodel.Indices.resize( submodelgeometry.index_count );
m_indexcount += submodelgeometry.index_count;
switch( indexsize ) {
case 1: {
for( auto &index : submodel.Indices ) {
index = sn_utils::d_uint8( s );
}
break;
}
case 2: {
for( auto &index : submodel.Indices ) {
index = sn_utils::ld_uint16( s );
}
break;
}
case 4: {
for( auto &index : submodel.Indices ) {
index = sn_utils::ld_uint32( s );
}
break;
}
default: { default: {
type = submodel.eType;
break; break;
} }
} }
submodel.m_geometry = GfxRenderer->Insert( vertices, m_geometrybank, type );
} }
}
}
else if (type == MAKE_ID4('T', 'R', 'A', '0')) else if (type == MAKE_ID4('T', 'R', 'A', '0'))
{ {
if( false == Matrices.empty() ) if( false == Matrices.empty() )
@@ -1820,6 +1953,20 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic)
Root[i].ChildGet()->Parent = &Root[i]; Root[i].ChildGet()->Parent = &Root[i];
if (Root[i].NextGet()) if (Root[i].NextGet())
Root[i].NextGet()->Parent = Root[i].Parent; Root[i].NextGet()->Parent = Root[i].Parent;
// remap geometry type for custom type submodels
int type;
switch( Root[i].eType ) {
case TP_FREESPOTLIGHT:
case TP_STARS: {
type = GL_POINTS;
break; }
default: {
type = Root[i].eType;
break;
}
}
Root[i].m_geometry.handle = GfxRenderer->Insert( Root[i].Indices, Root[i].Vertices, m_geometrybank, type );
} }
} }
@@ -1980,7 +2127,6 @@ void TModel3d::LoadFromTextFile(std::string const &FileName, bool dynamic)
cParser parser(FileName, cParser::buffer_FILE); // Ra: tu powinno być "models\\"... cParser parser(FileName, cParser::buffer_FILE); // Ra: tu powinno być "models\\"...
TSubModel *SubModel; TSubModel *SubModel;
std::string token = parser.getToken<std::string>(); std::string token = parser.getToken<std::string>();
iNumVerts = 0; // w konstruktorze to jest
while (token != "" || parser.eof()) while (token != "" || parser.eof())
{ {
std::string parent; std::string parent;
@@ -1991,8 +2137,11 @@ void TModel3d::LoadFromTextFile(std::string const &FileName, bool dynamic)
break; break;
} }
SubModel = new TSubModel(); SubModel = new TSubModel();
iNumVerts += SubModel->Load(parser, this, /*iNumVerts,*/ dynamic); {
auto const result { SubModel->Load( parser, dynamic ) };
m_indexcount += result.first;
m_vertexcount += result.second;
}
// będzie potrzebne do wyliczenia pozycji, np. pantografu // będzie potrzebne do wyliczenia pozycji, np. pantografu
SubModel->Parent = AddToNamed(parent.c_str(), SubModel); SubModel->Parent = AddToNamed(parent.c_str(), SubModel);
@@ -2031,12 +2180,13 @@ void TModel3d::Init()
Root->InitialRotate(true); Root->InitialRotate(true);
} }
iFlags |= Root->FlagsCheck() | 0x8000; // flagi całego modelu iFlags |= Root->FlagsCheck() | 0x8000; // flagi całego modelu
if (iNumVerts) { if (m_vertexcount) {
if( m_geometrybank == null_handle ) { if( m_geometrybank == null_handle ) {
m_geometrybank = GfxRenderer->Create_Bank(); m_geometrybank = GfxRenderer->Create_Bank();
} }
std::size_t dataoffset = 0; std::size_t indexoffset = 0;
Root->create_geometry( dataoffset, m_geometrybank ); std::size_t vertexoffset = 0;
Root->create_geometry( indexoffset, vertexoffset, m_geometrybank );
} }
// determine final bounding radius from the root-level siblings // determine final bounding radius from the root-level siblings
auto const *root { Root }; auto const *root { Root };

View File

@@ -16,6 +16,8 @@ http://mozilla.org/MPL/2.0/.
#include "material.h" #include "material.h"
#include "gl/query.h" #include "gl/query.h"
#define EU07_USE_GEOMETRYINDEXING
// Ra: specjalne typy submodeli, poza tym GL_TRIANGLES itp. // Ra: specjalne typy submodeli, poza tym GL_TRIANGLES itp.
const int TP_ROTATOR = 256; const int TP_ROTATOR = 256;
const int TP_FREESPOTLIGHT = 257; const int TP_FREESPOTLIGHT = 257;
@@ -70,6 +72,13 @@ public:
rescale, rescale,
normalize normalize
}; };
struct geometry_data {
gfx::geometry_handle handle;
int vertex_offset;
int vertex_count;
int index_offset;
int index_count;
};
private: private:
int iNext{ 0 }; int iNext{ 0 };
@@ -99,8 +108,6 @@ private:
float4x4 *fMatrix = nullptr; // pojedyncza precyzja wystarcza float4x4 *fMatrix = nullptr; // pojedyncza precyzja wystarcza
int iMatrix; // w pliku binarnym jest numer matrycy int iMatrix; // w pliku binarnym jest numer matrycy
}; };
int iNumVerts { -1 }; // ilość wierzchołków (1 dla FreeSpotLight)
int tVboPtr; // początek na liście wierzchołków albo indeksów
int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak
float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum
glm::vec4 glm::vec4
@@ -127,8 +134,6 @@ private:
TSubModel *Next { nullptr }; TSubModel *Next { nullptr };
TSubModel *Child { nullptr }; TSubModel *Child { nullptr };
public: // temporary access, clean this up during refactoring
gfx::geometry_handle m_geometry { 0, 0 }; // geometry of the submodel
private: private:
material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak
bool bWire { false }; // nie używane, ale wczytywane bool bWire { false }; // nie używane, ale wczytywane
@@ -139,7 +144,9 @@ private:
public: // chwilowo public: // chwilowo
float3 v_TransVector { 0.0f, 0.0f, 0.0f }; float3 v_TransVector { 0.0f, 0.0f, 0.0f };
geometry_data m_geometry { /*this,*/ { 0, 0 }, 0, 0, 0, 0 };
gfx::vertex_array Vertices; gfx::vertex_array Vertices;
gfx::index_array Indices;
float m_boundingradius { 0 }; float m_boundingradius { 0 };
std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
TAnimType b_aAnim{ TAnimType::at_None }; // kody animacji oddzielnie, bo zerowane TAnimType b_aAnim{ TAnimType::at_None }; // kody animacji oddzielnie, bo zerowane
@@ -166,7 +173,7 @@ public:
static std::string *pasText; // tekst dla wyświetlacza (!!!! do przemyślenia) static std::string *pasText; // tekst dla wyświetlacza (!!!! do przemyślenia)
TSubModel() = default; TSubModel() = default;
~TSubModel(); ~TSubModel();
int Load(cParser &Parser, TModel3d *Model, /*int Pos,*/ bool dynamic); std::pair<int, int> Load(cParser &Parser, /*TModel3d *Model, int Pos,*/ bool dynamic);
void ChildAdd(TSubModel *SubModel); void ChildAdd(TSubModel *SubModel);
void NextAdd(TSubModel *SubModel); void NextAdd(TSubModel *SubModel);
TSubModel * NextGet() { return Next; }; TSubModel * NextGet() { return Next; };
@@ -177,7 +184,9 @@ public:
std::tuple<TSubModel *, bool> find_replacable4(); std::tuple<TSubModel *, bool> find_replacable4();
// locates particle emitter submodels and adds them to provided list // locates particle emitter submodels and adds them to provided list
void find_smoke_sources( nameoffset_sequence &Sourcelist ) const; void find_smoke_sources( nameoffset_sequence &Sourcelist ) const;
#ifndef EU07_USE_GEOMETRYINDEXING
int TriangleAdd(TModel3d *m, material_handle tex, int tri); int TriangleAdd(TModel3d *m, material_handle tex, int tri);
#endif
void SetRotate(float3 vNewRotateAxis, float fNewAngle); void SetRotate(float3 vNewRotateAxis, float fNewAngle);
void SetRotateXYZ( Math3D::vector3 vNewAngles); void SetRotateXYZ( Math3D::vector3 vNewAngles);
void SetRotateXYZ(float3 vNewAngles); void SetRotateXYZ(float3 vNewAngles);
@@ -191,7 +200,7 @@ public:
glm::vec3 offset( float const Geometrytestoffsetthreshold = 0.f ) const; glm::vec3 offset( float const Geometrytestoffsetthreshold = 0.f ) const;
inline void Hide() { iVisible = 0; }; inline void Hide() { iVisible = 0; };
void create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle const &Bank ); void create_geometry( std::size_t &Indexoffset, std::size_t &Vertexoffset, gfx::geometrybank_handle const &Bank );
int FlagsCheck(); int FlagsCheck();
void WillBeAnimated() { void WillBeAnimated() {
iFlags |= 0x4000; }; iFlags |= 0x4000; };
@@ -233,7 +242,9 @@ public:
std::vector<std::string>&, std::vector<std::string>&,
std::vector<std::string>&, std::vector<std::string>&,
std::vector<float4x4>&); std::vector<float4x4>&);
void serialize_geometry( std::ostream &Output ) const; void serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed ) const;
int index_size() const;
void serialize_indices( std::ostream &Output, int const Size ) const;
// places contained geometry in provided ground node // places contained geometry in provided ground node
}; };
@@ -243,23 +254,23 @@ class TModel3d
friend opengl33_renderer; friend opengl33_renderer;
private: private:
TSubModel *Root; // drzewo submodeli TSubModel *Root { nullptr }; // drzewo submodeli
int iFlags; // Ra: czy submodele mają przezroczyste tekstury int iFlags { 0 }; // Ra: czy submodele mają przezroczyste tekstury
public: // Ra: tymczasowo public: // Ra: tymczasowo
int iNumVerts; // ilość wierzchołków (gdy nie ma VBO, to m_nVertexCount=0)
gfx::geometrybank_handle m_geometrybank; gfx::geometrybank_handle m_geometrybank;
bool m_geometrycreated { false }; int m_indexcount { 0 };
int m_vertexcount { 0 }; // ilość wierzchołków
private: private:
std::vector<std::string> Textures; // nazwy tekstur std::vector<std::string> Textures; // nazwy tekstur
std::vector<std::string> Names; // nazwy submodeli std::vector<std::string> Names; // nazwy submodeli
std::vector<float4x4> Matrices; // submodel matrices std::vector<float4x4> Matrices; // submodel matrices
int iSubModelsCount; // Ra: używane do tworzenia binarnych int iSubModelsCount { 0 }; // Ra: używane do tworzenia binarnych
std::string asBinary; // nazwa pod którą zapisać model binarny std::string asBinary; // nazwa pod którą zapisać model binarny
std::string m_filename; std::string m_filename;
nameoffset_sequence m_smokesources; // list of particle sources defined in the model nameoffset_sequence m_smokesources; // list of particle sources defined in the model
public: public:
TModel3d(); TModel3d() = default;
~TModel3d(); ~TModel3d();
float bounding_radius() const { float bounding_radius() const {
return ( return (

View File

@@ -19,39 +19,57 @@ http://mozilla.org/MPL/2.0/.
namespace gfx { namespace gfx {
void void
basic_vertex::serialize( std::ostream &s ) const { basic_vertex::serialize( std::ostream &s, bool const Tangent ) const {
sn_utils::ls_float32( s, position.x );
sn_utils::ls_float32( s, position.y );
sn_utils::ls_float32( s, position.z );
sn_utils::ls_float32( s, normal.x );
sn_utils::ls_float32( s, normal.y );
sn_utils::ls_float32( s, normal.z );
sn_utils::s_vec3( s, position );
sn_utils::s_vec3( s, normal );
sn_utils::ls_float32( s, texture.x ); sn_utils::ls_float32( s, texture.x );
sn_utils::ls_float32( s, texture.y ); sn_utils::ls_float32( s, texture.y );
if( Tangent ) {
sn_utils::s_vec4( s, tangent );
}
} }
void void
basic_vertex::deserialize( std::istream &s ) { basic_vertex::deserialize( std::istream &s, bool const Tangent ) {
position.x = sn_utils::ld_float32( s );
position.y = sn_utils::ld_float32( s );
position.z = sn_utils::ld_float32( s );
normal.x = sn_utils::ld_float32( s );
normal.y = sn_utils::ld_float32( s );
normal.z = sn_utils::ld_float32( s );
position = sn_utils::d_vec3( s );
normal = sn_utils::d_vec3( s );
texture.x = sn_utils::ld_float32( s ); texture.x = sn_utils::ld_float32( s );
texture.y = sn_utils::ld_float32( s ); texture.y = sn_utils::ld_float32( s );
if( Tangent ) {
tangent = sn_utils::d_vec4( s );
}
}
void
basic_vertex::serialize_packed( std::ostream &s, bool const Tangent ) const {
sn_utils::ls_uint64( s, glm::packHalf4x16( { position, 0.f } ) );
sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( { normal, 0.f } ) );
sn_utils::ls_uint16( s, glm::packHalf1x16( texture.x ) );
sn_utils::ls_uint16( s, glm::packHalf1x16( texture.y ) );
if( Tangent ) {
sn_utils::ls_uint32( s, glm::packSnorm3x10_1x2( tangent ) );
}
}
void
basic_vertex::deserialize_packed( std::istream &s, bool const Tangent ) {
position = glm::unpackHalf4x16( sn_utils::ld_uint64( s ) );
normal = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) );
texture.x = glm::unpackHalf1x16( sn_utils::ld_uint16( s ) );
texture.y = glm::unpackHalf1x16( sn_utils::ld_uint16( s ) );
if( Tangent ) {
tangent = glm::unpackSnorm3x10_1x2( sn_utils::ld_uint32( s ) );
}
} }
// based on // based on
// Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. // Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”.
// Terathon Software, 2001. http://terathon.com/code/tangent.html // Terathon Software, 2001. http://terathon.com/code/tangent.html
void calculate_tangent(vertex_array &vertices, int type) void calculate_tangents(vertex_array &vertices, int type)
{ {
size_t vertex_count = vertices.size(); size_t vertex_count = vertices.size();
@@ -159,9 +177,46 @@ void calculate_tangent(vertex_array &vertices, int type)
} }
} }
void calculate_indices( index_array &Indices, vertex_array &Vertices ) {
Indices.resize( Vertices.size() );
std::iota( std::begin( Indices ), std::end( Indices ), 0 );
// gather instances of used verices, replace the original vertex bank with it after you're done
vertex_array indexedvertices;
indexedvertices.reserve( std::max<size_t>( 100, Vertices.size() / 3 ) ); // optimistic guesstimate, but should reduce re-allocation somewhat
auto const matchtolerance { 1e-5f };
for( auto idx = 0; idx < Indices.size(); ++idx ) {
if( Indices[ idx ] < idx ) {
// this index is pointing to a vertex out of linear order, i.e. it's an already processed duplicate we can skip
continue;
}
// due to duplicate removal our vertex will likely have different index in the processed set
Indices[ idx ] = indexedvertices.size();
// see if there's any pointers in the remaining index subrange to similar enough vertices
// if found, remap these to use our current vertex instead
auto vertex { Vertices[ idx ] };
auto matchiter { std::cbegin( Vertices ) + idx };
for( auto matchidx = idx + 1; matchidx < Indices.size(); ++matchidx ) {
++matchiter;
if( ( glm::all( glm::epsilonEqual( vertex.position, matchiter->position, matchtolerance ) ) )
&& ( glm::all( glm::epsilonEqual( vertex.normal, matchiter->normal, matchtolerance ) ) )
&& ( glm::all( glm::epsilonEqual( vertex.texture, matchiter->texture, matchtolerance ) ) )
&& ( vertex.tangent.w == matchiter->tangent.w ) ) {
Indices[ matchidx ] = Indices[ idx ];
// HACK, TODO: tangent math winged/adapted from opengl-tutorial.org 13, check if it makes any sense
vertex.tangent += glm::vec4{ glm::vec3( matchiter->tangent ), 0.f };
}
}
vertex.tangent = { glm::normalize( glm::vec3{ vertex.tangent } ), vertex.tangent.w };
indexedvertices.emplace_back( vertex );
}
// done indexing, swap the source vertex bank with the processed one
Vertices.swap( indexedvertices );
}
// generic geometry bank class, allows storage, update and drawing of geometry chunks // generic geometry bank class, allows storage, update and drawing of geometry chunks
// creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk // creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
gfx::geometry_handle gfx::geometry_handle
geometry_bank::create( gfx::vertex_array &Vertices, unsigned int const Type ) { geometry_bank::create( gfx::vertex_array &Vertices, unsigned int const Type ) {
@@ -176,6 +231,21 @@ geometry_bank::create( gfx::vertex_array &Vertices, unsigned int const Type ) {
return chunkhandle; return chunkhandle;
} }
// creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
gfx::geometry_handle
geometry_bank::create( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int const Type ) {
if( true == Vertices.empty() ) { return { 0, 0 }; }
m_chunks.emplace_back( Indices, Vertices, Type );
// NOTE: handle is effectively (index into chunk array + 1) this leaves value of 0 to serve as error/empty handle indication
gfx::geometry_handle chunkhandle { 0, static_cast<std::uint32_t>(m_chunks.size()) };
// template method implementation
create_( chunkhandle );
// all done
return chunkhandle;
}
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // replaces data of specified chunk with the supplied vertex data, starting from specified offset
bool bool
geometry_bank::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { geometry_bank::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) {
@@ -225,6 +295,14 @@ geometry_bank::release() {
release_(); release_();
} }
// provides direct access to indexdata of specfied chunk
index_array const &
geometry_bank::indices( gfx::geometry_handle const &Geometry ) const {
return geometry_bank::chunk( Geometry ).indices;
}
// provides direct access to vertex data of specfied chunk
vertex_array const & vertex_array const &
geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const { geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const {
@@ -251,7 +329,7 @@ geometrybank_manager::create_bank() {
return { static_cast<std::uint32_t>( m_geometrybanks.size() ), 0 }; return { static_cast<std::uint32_t>( m_geometrybanks.size() ), 0 };
} }
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle gfx::geometry_handle
geometrybank_manager::create_chunk( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { geometrybank_manager::create_chunk( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) {
@@ -261,6 +339,16 @@ geometrybank_manager::create_chunk( gfx::vertex_array &Vertices, gfx::geometryba
else { return { 0, 0 }; } else { return { 0, 0 }; }
} }
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle
geometrybank_manager::create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) {
auto const newchunkhandle = bank( Geometry ).first->create( Indices, Vertices, Type );
if( newchunkhandle.chunk != 0 ) { return { Geometry.bank, newchunkhandle.chunk }; }
else { return { 0, 0 }; }
}
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // replaces data of specified chunk with the supplied vertex data, starting from specified offset
bool bool
geometrybank_manager::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) { geometrybank_manager::replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset ) {
@@ -286,6 +374,13 @@ geometrybank_manager::draw( gfx::geometry_handle const &Geometry, unsigned int c
m_primitivecount += bankrecord.first->draw( Geometry, m_units, Streams ); m_primitivecount += bankrecord.first->draw( Geometry, m_units, Streams );
} }
// provides direct access to index data of specfied chunk
gfx::index_array const &
geometrybank_manager::indices( gfx::geometry_handle const &Geometry ) const {
return bank( Geometry ).first->indices( Geometry );
}
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
gfx::vertex_array const & gfx::vertex_array const &
geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const { geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const {

View File

@@ -24,8 +24,10 @@ struct basic_vertex {
basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) : basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) :
position( Position ), normal( Normal ), texture( Texture ) position( Position ), normal( Normal ), texture( Texture )
{} {}
void serialize( std::ostream& ) const; void serialize( std::ostream&, bool const Tangent = false ) const;
void deserialize( std::istream& ); void deserialize( std::istream&, bool const Tangent = false );
void serialize_packed( std::ostream&, bool const Tangent = false ) const;
void deserialize_packed( std::istream&, bool const Tangent = false );
}; };
// data streams carried in a vertex // data streams carried in a vertex
@@ -45,9 +47,13 @@ struct stream_units {
std::vector<GLint> texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream std::vector<GLint> texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream
}; };
using vertex_array = std::vector<basic_vertex>; using basic_index = std::uint32_t;
void calculate_tangent( vertex_array &vertices, int type ); using vertex_array = std::vector<basic_vertex>;
using index_array = std::vector<basic_index>;
void calculate_tangents( vertex_array &vertices, int type );
void calculate_indices( index_array &Indices, vertex_array &Vertices );
// generic geometry bank class, allows storage, update and drawing of geometry chunks // generic geometry bank class, allows storage, update and drawing of geometry chunks
@@ -90,9 +96,11 @@ public:
~geometry_bank() {} ~geometry_bank() {}
// methods: // methods:
// creates a new geometry chunk of specified type from supplied vertex data. returns: handle to the chunk or NULL // creates a new geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
auto create( gfx::vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle; auto create( gfx::vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle;
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // creates a new indexed geometry chunk of specified type from supplied data. returns: handle to the chunk or NULL
auto create( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int const Type ) -> gfx::geometry_handle;
// replaces vertex data of specified chunk with the supplied data, starting from specified offset
auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool;
// adds supplied vertex data at the end of specified chunk // adds supplied vertex data at the end of specified chunk
auto append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) -> bool; auto append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) -> bool;
@@ -107,6 +115,8 @@ public:
return count; } return count; }
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time // frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
void release(); void release();
// provides direct access to index data of specfied chunk
auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &;
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
@@ -115,12 +125,20 @@ protected:
struct geometry_chunk { struct geometry_chunk {
unsigned int type; // kind of geometry used by the chunk unsigned int type; // kind of geometry used by the chunk
gfx::vertex_array vertices; // geometry data gfx::vertex_array vertices; // geometry data
// NOTE: constructor doesn't copy provided vertex data, but moves it gfx::index_array indices; // index data
// NOTE: constructor doesn't copy provided geometry data, but moves it
geometry_chunk( gfx::vertex_array &Vertices, unsigned int Type ) : geometry_chunk( gfx::vertex_array &Vertices, unsigned int Type ) :
type( Type ) type( Type )
{ {
vertices.swap( Vertices ); vertices.swap( Vertices );
} }
// NOTE: constructor doesn't copy provided geometry data, but moves it
geometry_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, unsigned int Type ) :
type( Type )
{
vertices.swap( Vertices );
indices.swap( Indices );
}
}; };
using geometrychunk_sequence = std::vector<geometry_chunk>; using geometrychunk_sequence = std::vector<geometry_chunk>;
@@ -162,8 +180,10 @@ public:
void update(); void update();
// creates a new geometry bank. returns: handle to the bank or NULL // creates a new geometry bank. returns: handle to the bank or NULL
auto create_bank() -> gfx::geometrybank_handle; auto create_bank() -> gfx::geometrybank_handle;
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
auto create_chunk( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle; auto create_chunk( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle;
// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
auto create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) -> gfx::geometry_handle;
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // replaces data of specified chunk with the supplied vertex data, starting from specified offset
auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; auto replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool;
// adds supplied vertex data at the end of specified chunk // adds supplied vertex data at the end of specified chunk
@@ -175,6 +195,8 @@ public:
while( First != Last ) { while( First != Last ) {
draw( *First, Streams ); draw( *First, Streams );
++First; } } ++First; } }
// provides direct access to index data of specfied chunk
auto indices( gfx::geometry_handle const &Geometry ) const -> gfx::index_array const &;
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &; auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
// sets target texture unit for the texture data stream // sets target texture unit for the texture data stream

View File

@@ -33,7 +33,7 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
chunkrecord.is_good = false; chunkrecord.is_good = false;
// if the overall length of the chunk didn't change we can get away with reusing the old buffer... // if the overall length of the chunk didn't change we can get away with reusing the old buffer...
if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.size ) { if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) {
// ...but otherwise we'll need to allocate a new one // ...but otherwise we'll need to allocate a new one
// TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one,
// but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it?
@@ -43,51 +43,62 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
void opengl33_vaogeometrybank::setup_buffer() void opengl33_vaogeometrybank::setup_buffer()
{ {
if( !m_buffer ) { if( m_vertexbuffer ) { return; }
// if there's no buffer, we'll have to make one // if there's no buffer, we'll have to make one
// NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data
// may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste
if( true == m_chunks.empty() ) { return; } if( true == m_chunks.empty() ) { return; }
std::size_t datasize{ 0 }; std::size_t
auto chunkiterator = m_chunks.cbegin(); vertexcount{ 0 },
for( auto &chunkrecord : m_chunkrecords ) { indexcount{ 0 };
// fill records for all chunks, based on the chunk data auto chunkiterator = m_chunks.cbegin();
chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one for( auto &chunkrecord : m_chunkrecords ) {
chunkrecord.offset = datasize; // fill records for all chunks, based on the chunk data
chunkrecord.size = chunkiterator->vertices.size(); chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one
datasize += chunkrecord.size; chunkrecord.vertex_offset = vertexcount;
++chunkiterator; chunkrecord.vertex_count = chunkiterator->vertices.size();
} vertexcount += chunkrecord.vertex_count;
// the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist chunkrecord.index_offset = indexcount;
if( datasize == 0 ) { return; } chunkrecord.index_count = chunkiterator->indices.size();
// try to set up the buffer we need indexcount += chunkrecord.index_count;
m_buffer.emplace(); ++chunkiterator;
}
// NOTE: we're using static_draw since it's generally true for all we have implemented at the moment // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
// TODO: allow to specify usage hint at the object creation, and pass it here if( vertexcount == 0 ) { return; }
m_buffer->allocate(gl::buffer::ARRAY_BUFFER, datasize * sizeof(gfx::basic_vertex), GL_STATIC_DRAW);
if( !m_vao ) {
m_vao.emplace();
}
m_vao->bind();
// try to set up the buffers we need:
// optional index buffer...
if( indexcount > 0 ) {
m_indexbuffer.emplace();
m_indexbuffer->allocate( gl::buffer::ELEMENT_ARRAY_BUFFER, indexcount * sizeof( gfx::basic_index ), GL_STATIC_DRAW );
if( ::glGetError() == GL_OUT_OF_MEMORY ) { if( ::glGetError() == GL_OUT_OF_MEMORY ) {
ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" ); ErrorLog( "openGL error: out of memory; failed to create a geometry index buffer" );
throw std::bad_alloc(); throw std::bad_alloc();
} }
m_buffercapacity = datasize; m_vao->setup_ebo( *m_indexbuffer );
} }
else {
if (!m_vao) gl::buffer::unbind( gl::buffer::ELEMENT_ARRAY_BUFFER );
{
m_vao.emplace();
m_vao->setup_attrib(*m_buffer, 0, 3, GL_FLOAT, sizeof(basic_vertex), 0 * sizeof(float));
// NOTE: normal and color streams share the data
m_vao->setup_attrib(*m_buffer, 1, 3, GL_FLOAT, sizeof(basic_vertex), 3 * sizeof(float));
m_vao->setup_attrib(*m_buffer, 2, 2, GL_FLOAT, sizeof(basic_vertex), 6 * sizeof(float));
m_vao->setup_attrib(*m_buffer, 3, 4, GL_FLOAT, sizeof(basic_vertex), 8 * sizeof(float));
m_buffer->unbind(gl::buffer::ARRAY_BUFFER);
m_vao->unbind();
} }
// ...and geometry buffer
m_vertexbuffer.emplace();
// NOTE: we're using static_draw since it's generally true for all we have implemented at the moment
// TODO: allow to specify usage hint at the object creation, and pass it here
m_vertexbuffer->allocate( gl::buffer::ARRAY_BUFFER, vertexcount * sizeof( gfx::basic_vertex ), GL_STATIC_DRAW );
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" );
throw std::bad_alloc();
}
m_vao->setup_attrib( *m_vertexbuffer, 0, 3, GL_FLOAT, sizeof( basic_vertex ), 0 * sizeof( float ) );
// NOTE: normal and color streams share the data
m_vao->setup_attrib( *m_vertexbuffer, 1, 3, GL_FLOAT, sizeof( basic_vertex ), 3 * sizeof( float ) );
m_vao->setup_attrib( *m_vertexbuffer, 2, 2, GL_FLOAT, sizeof( basic_vertex ), 6 * sizeof( float ) );
m_vao->setup_attrib( *m_vertexbuffer, 3, 4, GL_FLOAT, sizeof( basic_vertex ), 8 * sizeof( float ) );
} }
// draw() subclass details // draw() subclass details
@@ -100,23 +111,44 @@ opengl33_vaogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stre
auto &chunkrecord = m_chunkrecords.at(Geometry.chunk - 1); auto &chunkrecord = m_chunkrecords.at(Geometry.chunk - 1);
// sanity check; shouldn't be needed but, eh // sanity check; shouldn't be needed but, eh
if( chunkrecord.size == 0 ) if( chunkrecord.vertex_count == 0 )
return 0; return 0;
m_vao->bind();
auto const &chunk = gfx::geometry_bank::chunk( Geometry ); auto const &chunk = gfx::geometry_bank::chunk( Geometry );
if( false == chunkrecord.is_good ) { if( false == chunkrecord.is_good ) {
// we may potentially need to upload new buffer data before we can draw it // we may potentially need to upload new buffer data before we can draw it
m_buffer->upload(gl::buffer::ARRAY_BUFFER, chunk.vertices.data(), if( chunkrecord.index_count > 0 ) {
chunkrecord.offset * sizeof( gfx::basic_vertex ), m_indexbuffer->upload( gl::buffer::ELEMENT_ARRAY_BUFFER, chunk.indices.data(), chunkrecord.index_offset * sizeof( gfx::basic_index ), chunkrecord.index_count * sizeof( gfx::basic_index ) );
chunkrecord.size * sizeof( gfx::basic_vertex )); }
m_vertexbuffer->upload( gl::buffer::ARRAY_BUFFER, chunk.vertices.data(), chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ), chunkrecord.vertex_count * sizeof( gfx::basic_vertex ) );
chunkrecord.is_good = true; chunkrecord.is_good = true;
} }
// render // render
m_vao->bind(); if( chunkrecord.index_count > 0 ) {
::glDrawArrays( chunk.type, chunkrecord.offset, chunkrecord.size ); /*
::glDrawElementsBaseVertex(
chunk.type,
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
chunkrecord.vertex_offset );
*/
::glDrawRangeElementsBaseVertex(
chunk.type,
0, chunkrecord.vertex_count,
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
chunkrecord.vertex_offset );
}
else {
::glDrawArrays( chunk.type, chunkrecord.vertex_offset, chunkrecord.vertex_count );
}
/*
m_vao->unbind();
*/
auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) };
switch( chunk.type ) { switch( chunk.type ) {
case GL_TRIANGLES: { return chunkrecord.size / 3; } case GL_TRIANGLES: { return vertexcount / 3; }
case GL_TRIANGLE_STRIP: { return chunkrecord.size - 2; } case GL_TRIANGLE_STRIP: { return vertexcount - 2; }
default: { return 0; } default: { return 0; }
} }
} }
@@ -131,14 +163,11 @@ opengl33_vaogeometrybank::release_() {
void void
opengl33_vaogeometrybank::delete_buffer() { opengl33_vaogeometrybank::delete_buffer() {
if( m_buffer ) { m_vao.reset();
m_vertexbuffer.reset();
m_vao.reset(); m_indexbuffer.reset();
m_buffer.reset(); // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well
m_buffercapacity = 0; // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice
// NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well
// instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice
}
} }
} // namespace gfx } // namespace gfx

View File

@@ -32,9 +32,11 @@ public:
private: private:
// types: // types:
struct chunk_record{ struct chunk_record {
std::size_t offset{ 0 }; // beginning of the chunk data as offset from the beginning of the last established buffer std::size_t vertex_offset{ 0 }; // beginning of the chunk vertex data as offset from the beginning of the last established buffer
std::size_t size{ 0 }; // size of the chunk in the last established buffer std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer
std::size_t index_offset{ 0 };
std::size_t index_count{ 0 };
bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end
}; };
@@ -59,14 +61,10 @@ private:
delete_buffer(); delete_buffer();
// members: // members:
std::optional<gl::buffer> m_buffer; // buffer data on the opengl end std::optional<gl::buffer> m_vertexbuffer; // vertex buffer data on the opengl end
std::optional<gl::buffer> m_indexbuffer; // index buffer data on the opengl end
std::optional<gl::vao> m_vao; std::optional<gl::vao> m_vao;
std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer
chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order
// vectors for glMultiDrawArrays in class scope
// to don't waste time on reallocating
std::vector<GLint> m_offsets;
std::vector<GLsizei> m_counts;
}; };
} // namespace gfx } // namespace gfx

View File

@@ -22,6 +22,8 @@ http://mozilla.org/MPL/2.0/.
#include "application.h" #include "application.h"
#include "AnimModel.h" #include "AnimModel.h"
//#define EU07_DEBUG_OPENGL
int const EU07_PICKBUFFERSIZE{ 1024 }; // size of (square) textures bound with the pick framebuffer int const EU07_PICKBUFFERSIZE{ 1024 }; // size of (square) textures bound with the pick framebuffer
int const EU07_REFLECTIONFIDELITYOFFSET { 250 }; // artificial increase of range for reflection pass detail reduction int const EU07_REFLECTIONFIDELITYOFFSET { 250 }; // artificial increase of range for reflection pass detail reduction
@@ -324,7 +326,7 @@ bool opengl33_renderer::init_viewport(viewport_config &vp)
WriteLog("init viewport: " + std::to_string(vp.width) + " x " + std::to_string(vp.height)); WriteLog("init viewport: " + std::to_string(vp.width) + " x " + std::to_string(vp.height));
glfwSwapInterval( Global.VSync ? 1 : 0 ); // glfwSwapInterval( Global.VSync ? 1 : 0 );
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1);
@@ -470,9 +472,6 @@ bool opengl33_renderer::Render()
glfwMakeContextCurrent(m_window); glfwMakeContextCurrent(m_window);
gl::buffer::unbind(); gl::buffer::unbind();
m_current_viewport = &(*m_viewports.front()); m_current_viewport = &(*m_viewports.front());
m_drawcount = m_cellqueue.size();
m_debugtimestext.clear();
/* /*
m_debugtimestext += "cpu: " + to_string(Timer::subsystem.gfx_color.average(), 2) + " ms (" + std::to_string(m_cellqueue.size()) + " sectors)\n" += m_debugtimestext += "cpu: " + to_string(Timer::subsystem.gfx_color.average(), 2) + " ms (" + std::to_string(m_cellqueue.size()) + " sectors)\n" +=
"cpu swap: " + to_string(Timer::subsystem.gfx_swap.average(), 2) + " ms\n" += "uilayer: " + to_string(Timer::subsystem.gfx_gui.average(), 2) + "ms\n" += "cpu swap: " + to_string(Timer::subsystem.gfx_swap.average(), 2) + " ms\n" += "uilayer: " + to_string(Timer::subsystem.gfx_gui.average(), 2) + "ms\n" +=
@@ -486,11 +485,48 @@ bool opengl33_renderer::Render()
m_debugtimestext += "gpu: " + to_string((double)(m_gllasttime / 1000ULL) / 1000.0, 3) + "ms"; m_debugtimestext += "gpu: " + to_string((double)(m_gllasttime / 1000ULL) / 1000.0, 3) + "ms";
*/ */
} }
// swapbuffers() could unbind current buffers so we prepare for it on our end
gfx::opengl_vbogeometrybank::reset();
++m_framestamp;
SwapBuffers();
Timer::subsystem.gfx_total.stop();
return true; // for now always succeed
}
void opengl33_renderer::SwapBuffers()
{
Timer::subsystem.gfx_swap.start();
for (auto &viewport : m_viewports) {
if (viewport->window)
glfwSwapBuffers(viewport->window);
}
Timer::subsystem.gfx_swap.stop();
m_debugtimestext.clear();
m_debugtimestext =
"cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n"
+ " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n";
if( Global.gfx_shadowmap_enabled ) {
m_debugtimestext +=
" shadows: " + to_string( Timer::subsystem.gfx_shadows.average(), 2 ) + " ms\n";
}
m_debugtimestext += " swap: " + to_string( Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n";
if( !Global.gfx_usegles ) {
if (m_gllasttime)
m_debugtimestext += "gpu frame total: " + to_string((double)(m_gllasttime / 1000ULL) / 1000.0, 3) + " ms\n";
}
m_debugtimestext += "uilayer: " + to_string( Timer::subsystem.gfx_gui.average(), 2 ) + " ms\n";
if( DebugModeFlag )
m_debugtimestext += m_textures.info();
debug_stats shadowstats; debug_stats shadowstats;
for( auto const &shadowpass : m_shadowpass ) { for( auto const &shadowpass : m_shadowpass ) {
shadowstats += shadowpass.draw_stats; shadowstats += shadowpass.draw_stats;
} }
m_debugstatstext = m_debugstatstext =
"triangles: " + to_string( static_cast<int>(m_geometry.primitives_count()), 7 ) + "\n" "triangles: " + to_string( static_cast<int>(m_geometry.primitives_count()), 7 ) + "\n"
+ "vehicles: " + to_string( m_colorpass.draw_stats.dynamics, 7 ) + " +" + to_string( shadowstats.dynamics, 7 ) + "vehicles: " + to_string( m_colorpass.draw_stats.dynamics, 7 ) + " +" + to_string( shadowstats.dynamics, 7 )
@@ -508,44 +544,6 @@ bool opengl33_renderer::Render()
+ " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n" + " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n"
+ " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n" + " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n"
+ "particles: " + to_string( m_colorpass.draw_stats.particles, 7 ); + "particles: " + to_string( m_colorpass.draw_stats.particles, 7 );
++m_framestamp;
SwapBuffers();
Timer::subsystem.gfx_total.stop();
m_debugtimestext +=
"cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n"
+ " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n";
if( Global.gfx_shadowmap_enabled ) {
m_debugtimestext +=
" shadows: " + to_string( Timer::subsystem.gfx_shadows.average(), 2 ) + " ms\n";
}
m_debugtimestext += " swap: " + to_string( Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n";
if( !Global.gfx_usegles ) {
if (m_gllasttime)
m_debugtimestext += "gpu frame total: " + to_string((double)(m_gllasttime / 1000ULL) / 1000.0, 3) + " ms\n";
}
m_debugtimestext += "uilayer: " + to_string( Timer::subsystem.gfx_gui.average(), 2 ) + " ms\n";
if( DebugModeFlag )
m_debugtimestext += m_textures.info();
return true; // for now always succeed
}
void opengl33_renderer::SwapBuffers()
{
Timer::subsystem.gfx_swap.start();
for (auto &viewport : m_viewports) {
if (viewport->window)
glfwSwapBuffers(viewport->window);
}
// swapbuffers() could unbind current buffers so we prepare for it on our end
gfx::opengl_vbogeometrybank::reset();
Timer::subsystem.gfx_swap.stop();
} }
void opengl33_renderer::draw_debug_ui() void opengl33_renderer::draw_debug_ui()
@@ -799,10 +797,13 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
glEnable(GL_FRAMEBUFFER_SRGB); glEnable(GL_FRAMEBUFFER_SRGB);
glViewport(0, 0, target_size.x, target_size.y); glViewport(0, 0, target_size.x, target_size.y);
m_pfx_tonemapping->apply(*vp.main2_tex, nullptr);
if( Global.gfx_postfx_chromaticaberration_enabled ) { if( Global.gfx_postfx_chromaticaberration_enabled ) {
m_pfx_chromaticaberration->apply( *vp.main2_tex, nullptr ); m_pfx_tonemapping->apply( *vp.main2_tex, vp.main_fb.get() );
m_pfx_chromaticaberration->apply( *vp.main_tex, nullptr );
}
else {
m_pfx_tonemapping->apply( *vp.main2_tex, nullptr );
} }
opengl_texture::reset_unit_cache(); opengl_texture::reset_unit_cache();
@@ -1725,10 +1726,17 @@ gfx::geometrybank_handle opengl33_renderer::Create_Bank()
return m_geometry.create_bank(); return m_geometry.create_bank();
} }
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle opengl33_renderer::Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type )
{
// NOTE: we expect indexed geometry to come with calculated tangents
return m_geometry.create_chunk( Indices, Vertices, Geometry, Type );
}
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle opengl33_renderer::Insert(gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type) gfx::geometry_handle opengl33_renderer::Insert(gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type)
{ {
gfx::calculate_tangent(Vertices, Type); gfx::calculate_tangents(Vertices, Type);
return m_geometry.create_chunk(Vertices, Geometry, Type); return m_geometry.create_chunk(Vertices, Geometry, Type);
} }
@@ -1736,7 +1744,7 @@ gfx::geometry_handle opengl33_renderer::Insert(gfx::vertex_array &Vertices, gfx:
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // replaces data of specified chunk with the supplied vertex data, starting from specified offset
bool opengl33_renderer::Replace(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset) bool opengl33_renderer::Replace(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset)
{ {
gfx::calculate_tangent(Vertices, Type); gfx::calculate_tangents(Vertices, Type);
return m_geometry.replace(Vertices, Geometry, Offset); return m_geometry.replace(Vertices, Geometry, Offset);
} }
@@ -1744,15 +1752,20 @@ bool opengl33_renderer::Replace(gfx::vertex_array &Vertices, gfx::geometry_handl
// adds supplied vertex data at the end of specified chunk // adds supplied vertex data at the end of specified chunk
bool opengl33_renderer::Append(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type) bool opengl33_renderer::Append(gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type)
{ {
gfx::calculate_tangent(Vertices, Type); gfx::calculate_tangents(Vertices, Type);
return m_geometry.append(Vertices, Geometry); return m_geometry.append(Vertices, Geometry);
} }
// provides direct access to index data of specfied chunk
gfx::index_array const & opengl33_renderer::Indices(gfx::geometry_handle const &Geometry) const
{
return m_geometry.indices(Geometry);
}
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
gfx::vertex_array const &opengl33_renderer::Vertices(gfx::geometry_handle const &Geometry) const gfx::vertex_array const &opengl33_renderer::Vertices(gfx::geometry_handle const &Geometry) const
{ {
return m_geometry.vertices(Geometry); return m_geometry.vertices(Geometry);
} }
@@ -1792,10 +1805,8 @@ void opengl33_renderer::Bind_Material( material_handle const Material, TSubModel
memcpy(&model_ubs.param[0], &material.params[0], sizeof(model_ubs.param)); memcpy(&model_ubs.param[0], &material.params[0], sizeof(model_ubs.param));
for (size_t i = 0; i < material.params_state.size(); i++) for( auto const &entry : material.params_state )
{ {
gl::shader::param_entry entry = material.params_state[i];
glm::vec4 src(1.0f); glm::vec4 src(1.0f);
// submodel-based parameters // submodel-based parameters
@@ -1852,7 +1863,7 @@ void opengl33_renderer::Bind_Material( material_handle const Material, TSubModel
if( entry.size == 1 ) { if( entry.size == 1 ) {
// HACK: convert color to luminosity, if it's passed as single value // HACK: convert color to luminosity, if it's passed as single value
src == glm::vec4 { colors::RGBtoHSV( glm::vec3 { src } ).s }; src == glm::vec4 { colors::RGBtoHSV( glm::vec3 { src } ).z };
} }
for (size_t j = 0; j < entry.size; j++) for (size_t j = 0; j < entry.size; j++)
model_ubs.param[entry.location][entry.offset + j] = src[j]; model_ubs.param[entry.location][entry.offset + j] = src[j];
@@ -2881,7 +2892,7 @@ void opengl33_renderer::Render(TSubModel *Submodel)
model_ubs.emission = Submodel->f4Emision.a; model_ubs.emission = Submodel->f4Emision.a;
// main draw call // main draw call
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
// post-draw reset // post-draw reset
model_ubs.emission = 0.0f; model_ubs.emission = 0.0f;
@@ -2901,13 +2912,13 @@ void opengl33_renderer::Render(TSubModel *Submodel)
// również 0 // również 0
Bind_Material_Shadow(Submodel->m_material); Bind_Material_Shadow(Submodel->m_material);
} }
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
break; break;
} }
case rendermode::pickscenery: case rendermode::pickscenery:
{ {
m_pick_shader->bind(); m_pick_shader->bind();
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
break; break;
} }
case rendermode::pickcontrols: case rendermode::pickcontrols:
@@ -2916,7 +2927,7 @@ void opengl33_renderer::Render(TSubModel *Submodel)
// control picking applies individual colour for each submodel // control picking applies individual colour for each submodel
m_pickcontrolsitems.emplace_back(Submodel); m_pickcontrolsitems.emplace_back(Submodel);
model_ubs.param[0] = glm::vec4(pick_color(m_pickcontrolsitems.size()), 1.0f); model_ubs.param[0] = glm::vec4(pick_color(m_pickcontrolsitems.size()), 1.0f);
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
break; break;
} }
default: default:
@@ -2942,7 +2953,7 @@ void opengl33_renderer::Render(TSubModel *Submodel)
// main draw call // main draw call
model_ubs.param[1].x = m_pointsize; model_ubs.param[1].x = m_pointsize;
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
} }
break; break;
} }
@@ -3703,7 +3714,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel)
model_ubs.emission = Submodel->f4Emision.a; model_ubs.emission = Submodel->f4Emision.a;
// main draw call // main draw call
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
model_ubs.emission = 0.0f; model_ubs.emission = 0.0f;
break; break;
@@ -3718,7 +3729,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel)
{ {
Bind_Material_Shadow(Submodel->m_material); Bind_Material_Shadow(Submodel->m_material);
} }
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
break; break;
} }
default: default:
@@ -3841,7 +3852,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel)
model_ubs.param[1].x = pointsize * resolutionratio * fogfactor * 4.0f; model_ubs.param[1].x = pointsize * resolutionratio * fogfactor * 4.0f;
model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel) * 0.5f); model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel) * 0.5f);
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
} }
model_ubs.param[1].x = pointsize * resolutionratio * 4.0f; model_ubs.param[1].x = pointsize * resolutionratio * 4.0f;
model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel)); model_ubs.param[0] = glm::vec4(glm::vec3(lightcolor), Submodel->fVisible * std::min(1.f, lightlevel));
@@ -3850,7 +3861,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel)
Submodel->occlusion_query.emplace(gl::query::ANY_SAMPLES_PASSED); Submodel->occlusion_query.emplace(gl::query::ANY_SAMPLES_PASSED);
Submodel->occlusion_query->begin(); Submodel->occlusion_query->begin();
draw(Submodel->m_geometry); draw(Submodel->m_geometry.handle);
Submodel->occlusion_query->end(); Submodel->occlusion_query->end();

View File

@@ -49,6 +49,8 @@ class opengl33_renderer : public gfx_renderer {
// main draw call. returns false on error // main draw call. returns false on error
bool bool
Render() override; Render() override;
void
SwapBuffers() override;
inline inline
float float
Framerate() override { return m_framerate; } Framerate() override { return m_framerate; }
@@ -57,7 +59,10 @@ class opengl33_renderer : public gfx_renderer {
// creates a new geometry bank. returns: handle to the bank or NULL // creates a new geometry bank. returns: handle to the bank or NULL
gfx::geometrybank_handle gfx::geometrybank_handle
Create_Bank() override; Create_Bank() override;
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle
Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override;
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle gfx::geometry_handle
Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override;
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // replaces data of specified chunk with the supplied vertex data, starting from specified offset
@@ -66,6 +71,9 @@ class opengl33_renderer : public gfx_renderer {
// adds supplied vertex data at the end of specified chunk // adds supplied vertex data at the end of specified chunk
bool bool
Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override; Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override;
// provides direct access to index data of specfied chunk
gfx::index_array const &
Indices( gfx::geometry_handle const &Geometry ) const override;
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
gfx::vertex_array const & gfx::vertex_array const &
Vertices( gfx::geometry_handle const &Geometry ) const override; Vertices( gfx::geometry_handle const &Geometry ) const override;
@@ -118,7 +126,6 @@ class opengl33_renderer : public gfx_renderer {
opengl_material & Material( material_handle const Material ); opengl_material & Material( material_handle const Material );
void SwapBuffers();
// draws supplied geometry handles // draws supplied geometry handles
void Draw_Geometry(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end); void Draw_Geometry(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end);
void Draw_Geometry(const gfx::geometrybank_handle &handle); void Draw_Geometry(const gfx::geometrybank_handle &handle);
@@ -128,7 +135,6 @@ class opengl33_renderer : public gfx_renderer {
// members // members
GLenum static const sunlight{0}; GLenum static const sunlight{0};
std::size_t m_drawcount{0};
bool debug_ui_active = false; bool debug_ui_active = false;

View File

@@ -16,7 +16,7 @@ namespace gfx {
// opengl vbo-based variant of the geometry bank // opengl vbo-based variant of the geometry bank
GLuint opengl_vbogeometrybank::m_activebuffer { 0 }; // buffer bound currently on the opengl end, if any GLuint opengl_vbogeometrybank::m_activevertexbuffer { 0 }; // buffer bound currently on the opengl end, if any
unsigned int opengl_vbogeometrybank::m_activestreams { gfx::stream::none }; // currently enabled data type pointers unsigned int opengl_vbogeometrybank::m_activestreams { gfx::stream::none }; // currently enabled data type pointers
std::vector<GLint> opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays std::vector<GLint> opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays
@@ -37,7 +37,7 @@ opengl_vbogeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
chunkrecord.is_good = false; chunkrecord.is_good = false;
// if the overall length of the chunk didn't change we can get away with reusing the old buffer... // if the overall length of the chunk didn't change we can get away with reusing the old buffer...
if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.size ) { if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) {
// ...but otherwise we'll need to allocate a new one // ...but otherwise we'll need to allocate a new one
// TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one,
// but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it?
@@ -53,18 +53,25 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream
auto &chunkrecord { m_chunkrecords[ Geometry.chunk - 1 ] }; auto &chunkrecord { m_chunkrecords[ Geometry.chunk - 1 ] };
// sanity check; shouldn't be needed but, eh // sanity check; shouldn't be needed but, eh
if( chunkrecord.size == 0 ) { return 0; } if( chunkrecord.vertex_count == 0 ) { return 0; }
// setup... // setup...
if( m_activebuffer != m_buffer ) { if( m_activevertexbuffer != m_vertexbuffer ) {
bind_buffer(); bind_buffer();
} }
auto const &chunk = gfx::geometry_bank::chunk( Geometry ); auto const &chunk = gfx::geometry_bank::chunk( Geometry );
if( false == chunkrecord.is_good ) { if( false == chunkrecord.is_good ) {
// we may potentially need to upload new buffer data before we can draw it // we may potentially need to upload new buffer data before we can draw it
if( chunkrecord.index_count > 0 ) {
::glBufferSubData(
GL_ELEMENT_ARRAY_BUFFER,
chunkrecord.index_offset * sizeof( gfx::basic_index ),
chunkrecord.index_count * sizeof( gfx::basic_index ),
chunk.indices.data() );
}
::glBufferSubData( ::glBufferSubData(
GL_ARRAY_BUFFER, GL_ARRAY_BUFFER,
chunkrecord.offset * sizeof( gfx::basic_vertex ), chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ),
chunkrecord.size * sizeof( gfx::basic_vertex ), chunkrecord.vertex_count * sizeof( gfx::basic_vertex ),
chunk.vertices.data() ); chunk.vertices.data() );
chunkrecord.is_good = true; chunkrecord.is_good = true;
} }
@@ -72,7 +79,22 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream
bind_streams( Units, Streams ); bind_streams( Units, Streams );
} }
// ...render... // ...render...
::glDrawArrays( chunk.type, chunkrecord.offset, chunkrecord.size ); if( chunkrecord.index_count > 0 ) {
/*
::glDrawElementsBaseVertex(
chunk.type,
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
chunkrecord.vertex_offset );
*/
::glDrawRangeElementsBaseVertex(
chunk.type,
0, chunkrecord.vertex_count,
chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast<void const *>( chunkrecord.index_offset * sizeof( gfx::basic_index ) ),
chunkrecord.vertex_offset );
}
else {
::glDrawArrays( chunk.type, chunkrecord.vertex_offset, chunkrecord.vertex_count );
}
// ...post-render cleanup // ...post-render cleanup
/* /*
::glDisableClientState( GL_VERTEX_ARRAY ); ::glDisableClientState( GL_VERTEX_ARRAY );
@@ -80,9 +102,10 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream
::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); ::glDisableClientState( GL_TEXTURE_COORD_ARRAY );
::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0; ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0;
*/ */
auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) };
switch( chunk.type ) { switch( chunk.type ) {
case GL_TRIANGLES: { return chunkrecord.size / 3; } case GL_TRIANGLES: { return vertexcount / 3; }
case GL_TRIANGLE_STRIP: { return chunkrecord.size - 2; } case GL_TRIANGLE_STRIP: { return vertexcount - 2; }
default: { return 0; } default: { return 0; }
} }
} }
@@ -97,33 +120,57 @@ opengl_vbogeometrybank::release_() {
void void
opengl_vbogeometrybank::setup_buffer() { opengl_vbogeometrybank::setup_buffer() {
if( m_buffer != 0 ) { return; } if( m_vertexbuffer != 0 ) { return; }
// if there's no buffer, we'll have to make one // if there's no buffer, we'll have to make one
// NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data
// may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste
if( true == m_chunks.empty() ) { return; } if( true == m_chunks.empty() ) { return; }
std::size_t datasize{ 0 }; std::size_t
vertexcount{ 0 },
indexcount{ 0 };
auto chunkiterator = m_chunks.cbegin(); auto chunkiterator = m_chunks.cbegin();
for( auto &chunkrecord : m_chunkrecords ) { for( auto &chunkrecord : m_chunkrecords ) {
// fill records for all chunks, based on the chunk data // fill records for all chunks, based on the chunk data
chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one
chunkrecord.offset = datasize; chunkrecord.vertex_offset = vertexcount;
chunkrecord.size = chunkiterator->vertices.size(); chunkrecord.vertex_count = chunkiterator->vertices.size();
datasize += chunkrecord.size; vertexcount += chunkrecord.vertex_count;
chunkrecord.index_offset = indexcount;
chunkrecord.index_count = chunkiterator->indices.size();
indexcount += chunkrecord.index_count;
++chunkiterator; ++chunkiterator;
} }
// the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
if( datasize == 0 ) { return; } if( vertexcount == 0 ) { return; }
// try to set up the buffer we need // try to set up the buffers we need
::glGenBuffers( 1, &m_buffer ); if( ( indexcount > 0 ) && ( m_indexbuffer == 0 ) ) {
::glGenBuffers( 1, &m_indexbuffer );
if( m_indexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call
}
if( m_vertexbuffer == 0 ) {
::glGenBuffers( 1, &m_vertexbuffer );
if( m_vertexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call
}
bind_buffer(); bind_buffer();
if( m_buffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call
// NOTE: we're using static_draw since it's generally true for all we have implemented at the moment // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment
// TODO: allow to specify usage hint at the object creation, and pass it here // TODO: allow to specify usage hint at the object creation, and pass it here
if( indexcount > 0 ) {
::glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
indexcount * sizeof( gfx::basic_index ),
nullptr,
GL_STATIC_DRAW );
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
// TBD: throw a bad_alloc?
ErrorLog( "openGL error: out of memory; failed to create a geometry index buffer" );
delete_buffer();
return;
}
}
::glBufferData( ::glBufferData(
GL_ARRAY_BUFFER, GL_ARRAY_BUFFER,
datasize * sizeof( gfx::basic_vertex ), vertexcount * sizeof( gfx::basic_vertex ),
nullptr, nullptr,
GL_STATIC_DRAW ); GL_STATIC_DRAW );
if( ::glGetError() == GL_OUT_OF_MEMORY ) { if( ::glGetError() == GL_OUT_OF_MEMORY ) {
@@ -132,29 +179,32 @@ opengl_vbogeometrybank::setup_buffer() {
delete_buffer(); delete_buffer();
return; return;
} }
m_buffercapacity = datasize;
} }
void void
opengl_vbogeometrybank::bind_buffer() { opengl_vbogeometrybank::bind_buffer() {
::glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer );
m_activebuffer = m_buffer; ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer );
m_activevertexbuffer = m_vertexbuffer;
m_activestreams = gfx::stream::none; m_activestreams = gfx::stream::none;
} }
void void
opengl_vbogeometrybank::delete_buffer() { opengl_vbogeometrybank::delete_buffer() {
if( m_buffer != 0 ) { if( m_indexbuffer != 0 ) {
::glDeleteBuffers( 1, &m_indexbuffer );
m_indexbuffer = 0;
}
if( m_vertexbuffer != 0 ) {
::glDeleteBuffers( 1, &m_buffer ); ::glDeleteBuffers( 1, &m_vertexbuffer );
if( m_activebuffer == m_buffer ) { if( m_activevertexbuffer == m_vertexbuffer ) {
m_activebuffer = 0; m_activevertexbuffer = 0;
release_streams(); release_streams();
} }
m_buffer = 0; m_vertexbuffer = 0;
m_buffercapacity = 0;
// NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well
// instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice
} }
@@ -252,18 +302,32 @@ opengl_dlgeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_
::glNewList( chunkrecord.list, GL_COMPILE ); ::glNewList( chunkrecord.list, GL_COMPILE );
::glBegin( chunk.type ); ::glBegin( chunk.type );
for( auto const &vertex : chunk.vertices ) { if( chunk.indices.size() > 0 ) {
if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } // indexed geometry
else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } for( auto const &index : chunk.indices ) {
if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } auto const &vertex { chunk.vertices[ index ] };
if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); }
else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); }
if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } }
if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); }
}
}
else {
// raw geometry
for( auto const &vertex : chunk.vertices ) {
if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); }
else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); }
if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } }
if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); }
}
} }
::glEnd(); ::glEnd();
::glEndList(); ::glEndList();
auto const vertexcount { ( chunk.indices.empty() ? chunk.vertices.size() : chunk.indices.size() ) };
switch( chunk.type ) { switch( chunk.type ) {
case GL_TRIANGLES: { chunkrecord.primitive_count += chunk.vertices.size() / 3; break; } case GL_TRIANGLES: { chunkrecord.primitive_count += vertexcount / 3; break; }
case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += chunk.vertices.size() - 2; break; } case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += vertexcount - 2; break; }
default: { break; } default: { break; }
} }
} }

View File

@@ -27,14 +27,16 @@ public:
static static
void void
reset() { reset() {
m_activebuffer = 0; m_activevertexbuffer = 0;
m_activestreams = gfx::stream::none; } m_activestreams = gfx::stream::none; }
private: private:
// types: // types:
struct chunk_record{ struct chunk_record {
std::size_t offset{ 0 }; // beginning of the chunk data as offset from the beginning of the last established buffer std::size_t vertex_offset{ 0 }; // beginning of the chunk vertex data as offset from the beginning of the last established buffer
std::size_t size{ 0 }; // size of the chunk in the last established buffer std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer
std::size_t index_offset{ 0 };
std::size_t index_count{ 0 };
bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end
}; };
@@ -67,11 +69,11 @@ private:
release_streams(); release_streams();
// members: // members:
static GLuint m_activebuffer; // buffer bound currently on the opengl end, if any static GLuint m_activevertexbuffer; // buffer bound currently on the opengl end, if any
static unsigned int m_activestreams; static unsigned int m_activestreams;
static std::vector<GLint> m_activetexturearrays; static std::vector<GLint> m_activetexturearrays;
GLuint m_buffer { 0 }; // id of the buffer holding data on the opengl end GLuint m_vertexbuffer { 0 }; // id of the buffer holding vertex data on the opengl end
std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer GLuint m_indexbuffer { 0 }; // id of the buffer holding index data on the opengl end
chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order
}; };

View File

@@ -334,7 +334,6 @@ opengl_renderer::Render() {
m_renderpass.draw_mode = rendermode::none; // force setup anew m_renderpass.draw_mode = rendermode::none; // force setup anew
m_renderpass.draw_stats = debug_stats(); m_renderpass.draw_stats = debug_stats();
m_geometry.primitives_count() = 0; m_geometry.primitives_count() = 0;
m_debugtimestext.clear();
Render_pass( rendermode::color ); Render_pass( rendermode::color );
Timer::subsystem.gfx_color.stop(); Timer::subsystem.gfx_color.stop();
// add user interface // add user interface
@@ -348,12 +347,21 @@ opengl_renderer::Render() {
// swapbuffers() will unbind current buffers so we prepare for it on our end // swapbuffers() will unbind current buffers so we prepare for it on our end
gfx::opengl_vbogeometrybank::reset(); gfx::opengl_vbogeometrybank::reset();
} }
++m_framestamp;
SwapBuffers();
return true; // for now always succeed
}
void
opengl_renderer::SwapBuffers() {
Timer::subsystem.gfx_swap.start(); Timer::subsystem.gfx_swap.start();
glfwSwapBuffers( m_window ); glfwSwapBuffers( m_window );
Timer::subsystem.gfx_swap.stop(); Timer::subsystem.gfx_swap.stop();
m_drawcount = m_cellqueue.size(); m_debugtimestext.clear();
m_debugtimestext += m_debugtimestext =
"cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n" "cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n"
+ " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n"; + " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n";
if( Global.RenderShadows ) { if( Global.RenderShadows ) {
@@ -380,10 +388,6 @@ opengl_renderer::Render() {
+ " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n" + " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n"
+ " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n" + " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n"
+ "particles: " + to_string( m_colorpass.draw_stats.particles, 7 ); + "particles: " + to_string( m_colorpass.draw_stats.particles, 7 );
++m_framestamp;
return true; // for now always succeed
} }
// runs jobs needed to generate graphics for specified render pass // runs jobs needed to generate graphics for specified render pass
@@ -1669,7 +1673,14 @@ opengl_renderer::Create_Bank() {
return m_geometry.create_bank(); return m_geometry.create_bank();
} }
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle
opengl_renderer::Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) {
return m_geometry.create_chunk( Indices, Vertices, Geometry, Type );
}
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle gfx::geometry_handle
opengl_renderer::Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) { opengl_renderer::Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) {
@@ -1690,6 +1701,13 @@ opengl_renderer::Append( gfx::vertex_array &Vertices, gfx::geometry_handle const
return m_geometry.append( Vertices, Geometry ); return m_geometry.append( Vertices, Geometry );
} }
// provides direct access to vertex data of specfied chunk
gfx::index_array const &
opengl_renderer::Indices( gfx::geometry_handle const &Geometry ) const {
return m_geometry.indices( Geometry );
}
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
gfx::vertex_array const & gfx::vertex_array const &
opengl_renderer::Vertices( gfx::geometry_handle const &Geometry ) const { opengl_renderer::Vertices( gfx::geometry_handle const &Geometry ) const {
@@ -2749,7 +2767,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
} }
// main draw call // main draw call
m_geometry.draw( Submodel->m_geometry ); m_geometry.draw( Submodel->m_geometry.handle );
/* /*
if( DebugModeFlag ) { if( DebugModeFlag ) {
auto const & vertices { m_geometry.vertices( Submodel->m_geometry ) }; auto const & vertices { m_geometry.vertices( Submodel->m_geometry ) };
@@ -2811,7 +2829,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
Bind_Material( Submodel->m_material ); Bind_Material( Submodel->m_material );
} }
// main draw call // main draw call
m_geometry.draw( Submodel->m_geometry ); m_geometry.draw( Submodel->m_geometry.handle );
// post-draw reset // post-draw reset
break; break;
} }
@@ -2829,7 +2847,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
Bind_Material( Submodel->m_material ); Bind_Material( Submodel->m_material );
} }
// main draw call // main draw call
m_geometry.draw( Submodel->m_geometry ); m_geometry.draw( Submodel->m_geometry.handle );
// post-draw reset // post-draw reset
break; break;
} }
@@ -2917,7 +2935,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
lightcolor[ 2 ], lightcolor[ 2 ],
Submodel->fVisible * std::min( 1.f, lightlevel ) * 0.5f ); Submodel->fVisible * std::min( 1.f, lightlevel ) * 0.5f );
::glDepthMask( GL_FALSE ); ::glDepthMask( GL_FALSE );
m_geometry.draw( Submodel->m_geometry ); m_geometry.draw( Submodel->m_geometry.handle );
::glDepthMask( GL_TRUE ); ::glDepthMask( GL_TRUE );
} }
::glPointSize( pointsize * resolutionratio ); ::glPointSize( pointsize * resolutionratio );
@@ -2926,7 +2944,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
lightcolor[ 1 ], lightcolor[ 1 ],
lightcolor[ 2 ], lightcolor[ 2 ],
Submodel->fVisible * std::min( 1.f, lightlevel ) ); Submodel->fVisible * std::min( 1.f, lightlevel ) );
m_geometry.draw( Submodel->m_geometry ); m_geometry.draw( Submodel->m_geometry.handle );
// post-draw reset // post-draw reset
switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections ); switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections );
@@ -2958,7 +2976,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
::glDisable( GL_LIGHTING ); ::glDisable( GL_LIGHTING );
// main draw call // main draw call
m_geometry.draw( Submodel->m_geometry, gfx::color_streams ); m_geometry.draw( Submodel->m_geometry.handle, gfx::color_streams );
// post-draw reset // post-draw reset
::glPopAttrib(); ::glPopAttrib();
@@ -3793,7 +3811,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
} }
// main draw call // main draw call
m_geometry.draw( Submodel->m_geometry ); m_geometry.draw( Submodel->m_geometry.handle );
// post-draw reset // post-draw reset
if( opacity != 0.f ) { if( opacity != 0.f ) {
@@ -3839,7 +3857,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
Bind_Material( Submodel->m_material ); Bind_Material( Submodel->m_material );
} }
// main draw call // main draw call
m_geometry.draw( Submodel->m_geometry ); m_geometry.draw( Submodel->m_geometry.handle );
// post-draw reset // post-draw reset
break; break;
} }

View File

@@ -43,6 +43,8 @@ public:
// main draw call. returns false on error // main draw call. returns false on error
bool bool
Render() override; Render() override;
void
SwapBuffers() override;
inline inline
float float
Framerate() override { return m_framerate; } Framerate() override { return m_framerate; }
@@ -51,7 +53,10 @@ public:
// creates a new geometry bank. returns: handle to the bank or NULL // creates a new geometry bank. returns: handle to the bank or NULL
gfx::geometrybank_handle gfx::geometrybank_handle
Create_Bank() override; Create_Bank() override;
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle
Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override;
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
gfx::geometry_handle gfx::geometry_handle
Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override; Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) override;
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // replaces data of specified chunk with the supplied vertex data, starting from specified offset
@@ -60,6 +65,9 @@ public:
// adds supplied vertex data at the end of specified chunk // adds supplied vertex data at the end of specified chunk
bool bool
Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override; Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) override;
// provides direct access to index data of specfied chunk
gfx::index_array const &
Indices( gfx::geometry_handle const &Geometry ) const override;
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
gfx::vertex_array const & gfx::vertex_array const &
Vertices( gfx::geometry_handle const &Geometry ) const override; Vertices( gfx::geometry_handle const &Geometry ) const override;
@@ -111,7 +119,6 @@ public:
// members // members
GLenum static const sunlight { GL_LIGHT0 }; GLenum static const sunlight { GL_LIGHT0 };
std::size_t m_drawcount { 0 };
private: private:
// types // types

View File

@@ -25,17 +25,22 @@ public:
virtual auto Init( GLFWwindow *Window ) -> bool = 0; virtual auto Init( GLFWwindow *Window ) -> bool = 0;
// main draw call. returns false on error // main draw call. returns false on error
virtual auto Render() -> bool = 0; virtual auto Render() -> bool = 0;
virtual void SwapBuffers() = 0;
virtual auto Framerate() -> float = 0; virtual auto Framerate() -> float = 0;
// geometry methods // geometry methods
// NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself // NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself
// creates a new geometry bank. returns: handle to the bank or NULL // creates a new geometry bank. returns: handle to the bank or NULL
virtual auto Create_Bank() -> gfx::geometrybank_handle = 0; virtual auto Create_Bank() -> gfx::geometrybank_handle = 0;
// creates a new geometry chunk of specified type from supplied vertex data, in specified bank. returns: handle to the chunk or NULL // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
virtual auto Insert( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle = 0;
// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL
virtual auto Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle = 0; virtual auto Insert( gfx::vertex_array &Vertices, gfx::geometrybank_handle const &Geometry, int const Type ) -> gfx::geometry_handle = 0;
// replaces data of specified chunk with the supplied vertex data, starting from specified offset // replaces data of specified chunk with the supplied vertex data, starting from specified offset
virtual auto Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0 ) -> bool = 0; virtual auto Replace( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type, std::size_t const Offset = 0 ) -> bool = 0;
// adds supplied vertex data at the end of specified chunk // adds supplied vertex data at the end of specified chunk
virtual auto Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) -> bool = 0; virtual auto Append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry, int const Type ) -> bool = 0;
// provides direct access to index data of specfied chunk
virtual auto Indices( gfx::geometry_handle const &Geometry ) const->gfx::index_array const & = 0;
// provides direct access to vertex data of specfied chunk // provides direct access to vertex data of specfied chunk
virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0; virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0;
// material methods // material methods

View File

@@ -350,12 +350,12 @@ shape_node::convert( TSubModel const *Submodel ) {
// NOTE: we set unlimited view range typical for terrain, because we don't expect to convert any other 3d models // NOTE: we set unlimited view range typical for terrain, because we don't expect to convert any other 3d models
m_data.rangesquared_max = std::numeric_limits<double>::max(); m_data.rangesquared_max = std::numeric_limits<double>::max();
if( Submodel->m_geometry == null_handle ) { return *this; } if( Submodel->m_geometry.handle == null_handle ) { return *this; }
int vertexcount { 0 }; int vertexcount { 0 };
std::vector<world_vertex> importedvertices; std::vector<world_vertex> importedvertices;
world_vertex vertex, vertex1, vertex2; world_vertex vertex, vertex1, vertex2;
for( auto const &sourcevertex : GfxRenderer->Vertices( Submodel->m_geometry ) ) { for( auto const &sourcevertex : GfxRenderer->Vertices( Submodel->m_geometry.handle ) ) {
vertex.position = sourcevertex.position; vertex.position = sourcevertex.position;
vertex.normal = sourcevertex.normal; vertex.normal = sourcevertex.normal;
vertex.texture = sourcevertex.texture; vertex.texture = sourcevertex.texture;

View File

@@ -38,6 +38,18 @@ int32_t sn_utils::ld_int32(std::istream &s)
return reinterpret_cast<int32_t&>(v); return reinterpret_cast<int32_t&>(v);
} }
// deserialize little endian uint64
uint64_t sn_utils::ld_uint64(std::istream &s)
{
uint8_t buf[8];
s.read((char*)buf, 8);
uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
return reinterpret_cast<uint64_t&>(v);
}
// deserialize little endian int64 // deserialize little endian int64
int64_t sn_utils::ld_int64(std::istream &s) int64_t sn_utils::ld_int64(std::istream &s)
{ {
@@ -100,7 +112,7 @@ glm::dvec3 sn_utils::d_dvec3(std::istream& s)
ld_float64(s) }; ld_float64(s) };
} }
glm::vec3 sn_utils::d_vec3( std::istream& s) glm::vec3 sn_utils::d_vec3( std::istream& s )
{ {
return { return {
ld_float32(s), ld_float32(s),
@@ -108,7 +120,7 @@ glm::vec3 sn_utils::d_vec3( std::istream& s)
ld_float32(s) }; ld_float32(s) };
} }
glm::vec4 sn_utils::d_vec4( std::istream& s) glm::vec4 sn_utils::d_vec4( std::istream& s )
{ {
return { return {
ld_float32(s), ld_float32(s),
@@ -117,6 +129,14 @@ glm::vec4 sn_utils::d_vec4( std::istream& s)
ld_float32(s) }; ld_float32(s) };
} }
uint8_t sn_utils::d_uint8( std::istream& s ) {
uint8_t buf;
s.read((char*)buf, 1);
return buf;
}
void sn_utils::ls_uint16(std::ostream &s, uint16_t v) void sn_utils::ls_uint16(std::ostream &s, uint16_t v)
{ {
uint8_t buf[2]; uint8_t buf[2];
@@ -145,6 +165,20 @@ void sn_utils::ls_int32(std::ostream &s, int32_t v)
s.write((char*)buf, 4); s.write((char*)buf, 4);
} }
void sn_utils::ls_uint64(std::ostream &s, uint64_t v)
{
uint8_t buf[8];
buf[0] = v;
buf[1] = v >> 8;
buf[2] = v >> 16;
buf[3] = v >> 24;
buf[4] = v >> 32;
buf[5] = v >> 40;
buf[6] = v >> 48;
buf[7] = v >> 56;
s.write((char*)buf, 8);
}
void sn_utils::ls_int64(std::ostream &s, int64_t v) void sn_utils::ls_int64(std::ostream &s, int64_t v)
{ {
uint8_t buf[8]; uint8_t buf[8];
@@ -185,6 +219,11 @@ void sn_utils::ls_float64(std::ostream &s, double t)
s.write((char*)buf, 8); s.write((char*)buf, 8);
} }
void sn_utils::s_uint8(std::ostream &s, uint8_t v)
{
s.write((char*)v, 1);
}
void sn_utils::s_str(std::ostream &s, std::string v) void sn_utils::s_str(std::ostream &s, std::string v)
{ {
const char* buf = v.c_str(); const char* buf = v.c_str();

View File

@@ -10,9 +10,11 @@ public:
static uint16_t ld_uint16(std::istream&); static uint16_t ld_uint16(std::istream&);
static uint32_t ld_uint32(std::istream&); static uint32_t ld_uint32(std::istream&);
static int32_t ld_int32(std::istream&); static int32_t ld_int32(std::istream&);
static int64_t ld_int64(std::istream&); static uint64_t ld_uint64(std::istream&);
static int64_t ld_int64(std::istream&);
static float ld_float32(std::istream&); static float ld_float32(std::istream&);
static double ld_float64(std::istream&); static double ld_float64(std::istream&);
static uint8_t d_uint8(std::istream&);
static std::string d_str(std::istream&); static std::string d_str(std::istream&);
static bool d_bool(std::istream&); static bool d_bool(std::istream&);
static glm::dvec3 d_dvec3(std::istream&); static glm::dvec3 d_dvec3(std::istream&);
@@ -22,10 +24,12 @@ public:
static void ls_uint16(std::ostream&, uint16_t); static void ls_uint16(std::ostream&, uint16_t);
static void ls_uint32(std::ostream&, uint32_t); static void ls_uint32(std::ostream&, uint32_t);
static void ls_int32(std::ostream&, int32_t); static void ls_int32(std::ostream&, int32_t);
static void ls_uint64(std::ostream&, uint64_t);
static void ls_int64(std::ostream&, int64_t); static void ls_int64(std::ostream&, int64_t);
static void ls_float32(std::ostream&, float); static void ls_float32(std::ostream&, float);
static void ls_float64(std::ostream&, double); static void ls_float64(std::ostream&, double);
static void s_str(std::ostream&, std::string); static void s_uint8(std::ostream&, uint8_t);
static void s_str(std::ostream&, std::string);
static void s_bool(std::ostream&, bool); static void s_bool(std::ostream&, bool);
static void s_dvec3(std::ostream&, glm::dvec3 const &); static void s_dvec3(std::ostream&, glm::dvec3 const &);
static void s_vec3(std::ostream&, glm::vec3 const &); static void s_vec3(std::ostream&, glm::vec3 const &);

View File

@@ -58,6 +58,7 @@
#include <random> #include <random>
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <numeric>
#include <regex> #include <regex>
#include <limits> #include <limits>
#include <memory> #include <memory>
@@ -92,6 +93,7 @@
#include <glm/gtc/matrix_access.hpp> #include <glm/gtc/matrix_access.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/epsilon.hpp> #include <glm/gtc/epsilon.hpp>
#include <glm/gtc/packing.hpp>
#include <glm/gtx/rotate_vector.hpp> #include <glm/gtx/rotate_vector.hpp>
#include <glm/gtx/norm.hpp> #include <glm/gtx/norm.hpp>
#include <glm/gtx/string_cast.hpp> #include <glm/gtx/string_cast.hpp>