mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
basic indexed geometry support, geometry data packing
This commit is contained in:
694
Model3d.cpp
694
Model3d.cpp
@@ -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)
|
||||
{ // 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 ) {
|
||||
// pętla po trójkątach, od trójkąta (f)
|
||||
if( Masks[ faceidx ] & Mask ) {
|
||||
@@ -208,16 +208,12 @@ inline void readMatrix(cParser &parser, float4x4 &matrix)
|
||||
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
|
||||
iNumVerts = 0;
|
||||
/*
|
||||
iVboPtr = Pos; // pozycja w VBO
|
||||
*/
|
||||
auto token { parser.getToken<std::string>() };
|
||||
if( token != "type:" ) {
|
||||
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: \"" };
|
||||
auto count { 10 };
|
||||
while( ( true == parser.getTokens() )
|
||||
@@ -230,7 +226,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
|
||||
}
|
||||
errormessage += "(...)\"";
|
||||
ErrorLog( errormessage );
|
||||
return 0;
|
||||
return { 0, 0 };
|
||||
}
|
||||
{
|
||||
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 ) {
|
||||
fCosHotspotAngle = std::cos( DegToRad( 0.5f * fCosHotspotAngle ) );
|
||||
}
|
||||
iNumVerts = 1;
|
||||
/*
|
||||
iFlags |= 0x4010; // rysowane w cyklu nieprzezroczystych, macierz musi zostać bez zmiany
|
||||
*/
|
||||
m_geometry.vertex_count = 1;
|
||||
iFlags |= 0x4030; // drawn both in solid (light point) and transparent (light glare) phases
|
||||
}
|
||||
else if (eType < TP_ROTATOR)
|
||||
@@ -471,7 +464,10 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
|
||||
// visibility range
|
||||
std::string discard;
|
||||
parser.getTokens(5, false);
|
||||
parser >> discard >> fSquareMaxDist >> discard >> fSquareMinDist >> discard;
|
||||
parser
|
||||
>> discard >> fSquareMaxDist
|
||||
>> discard >> fSquareMinDist
|
||||
>> discard;
|
||||
|
||||
if( fSquareMaxDist <= 0.0 ) {
|
||||
// 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)
|
||||
{ // wczytywanie wierzchołków
|
||||
parser.getTokens(2, false);
|
||||
parser >> discard >> token;
|
||||
// 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
|
||||
// parser.getToken(iNumVerts);
|
||||
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!");
|
||||
}
|
||||
else
|
||||
token = parser.getToken<std::string>();
|
||||
if( token == "numindices:" ) // optional block, potentially preceeding vertex list
|
||||
{
|
||||
m_geometry.index_count = parser.getToken<int>( false );
|
||||
Indices.resize( m_geometry.index_count );
|
||||
for( auto idx = 0; idx < m_geometry.index_count; ++idx ) {
|
||||
Indices[ idx ] = parser.getToken<unsigned int>( false );
|
||||
}
|
||||
token = parser.getToken<std::string>();
|
||||
}
|
||||
if( token == "numverts:" )
|
||||
{ // 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;
|
||||
Error("Mesh error, (iNumVertices=" + std::to_string(iNumVerts) + ")%3<>0");
|
||||
return 0;
|
||||
m_geometry.vertex_count = 0;
|
||||
Error("Bad model: incomplete triangle encountered in submodel \"" + pName + "\"");
|
||||
return { 0, 0 };
|
||||
}
|
||||
// Vertices=new GLVERTEX[iNumVerts];
|
||||
if (iNumVerts) {
|
||||
/*
|
||||
Vertices = new basic_vertex[iNumVerts];
|
||||
*/
|
||||
Vertices.resize( iNumVerts );
|
||||
int facecount = iNumVerts / 3;
|
||||
/*
|
||||
unsigned int *sg; // maski przynależności trójkątów do powierzchni
|
||||
sg = new unsigned int[iNumFaces]; // maski powierzchni: 0 oznacza brak użredniania wektorów normalnych
|
||||
int *wsp = new int[iNumVerts]; // z którego wierzchołka kopiować wektor normalny
|
||||
*/
|
||||
std::vector<unsigned int> sg; sg.resize( facecount ); // maski przynależności trójkątów do powierzchni
|
||||
std::vector<int> wsp; wsp.resize( iNumVerts );// z którego wierzchołka kopiować wektor normalny
|
||||
int maska = 0;
|
||||
int rawvertexcount = 0; // used to keep track of vertex indices in source file
|
||||
for (int i = 0; i < iNumVerts; ++i) {
|
||||
++rawvertexcount;
|
||||
// Ra: z konwersją na układ scenerii - będzie wydajniejsze wyświetlanie
|
||||
wsp[i] = -1; // wektory normalne nie są policzone dla tego wierzchołka
|
||||
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;
|
||||
if ( m_geometry.vertex_count ) {
|
||||
Vertices.resize( m_geometry.vertex_count );
|
||||
if( m_geometry.index_count ) {
|
||||
// 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 ) };
|
||||
for( auto idx = 0; idx < m_geometry.vertex_count; ++idx ) {
|
||||
auto vertex { vertices + idx };
|
||||
parser.getTokens( 3 + 3 + 2 + 3, false );
|
||||
parser
|
||||
>> vertex->position.x
|
||||
>> vertex->position.y
|
||||
>> vertex->position.z
|
||||
>> vertex->normal.x
|
||||
>> vertex->normal.y
|
||||
>> vertex->normal.z
|
||||
>> vertex->texture.s
|
||||
>> vertex->texture.t
|
||||
>> vertex->tangent.x
|
||||
>> vertex->tangent.y
|
||||
>> vertex->tangent.z;
|
||||
}
|
||||
else {
|
||||
// inaczej musimy dopiero policzyć
|
||||
auto const faceidx = vertexidx / 3; // numer trójkąta
|
||||
vertexnormal = glm::vec3(); // liczenie zaczynamy od zera
|
||||
auto adjacenvertextidx = vertexidx; // zaczynamy dodawanie wektorów normalnych od własnego
|
||||
while (adjacenvertextidx >= 0) {
|
||||
// sumowanie z wektorem normalnym sąsiada (włącznie ze sobą)
|
||||
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 ];
|
||||
}
|
||||
else {
|
||||
// legacy geometry, more or less incomplete
|
||||
// to stay on the safe side we'll potentially need to convert indexed geometry to raw triangle form, cal
|
||||
auto vertices { std::begin( Vertices ) };
|
||||
int facecount = m_geometry.vertex_count / 3;
|
||||
std::vector<unsigned int> sg; sg.resize( facecount ); // maski przynależności trójkątów do powierzchni
|
||||
std::vector<int> wsp; wsp.resize( m_geometry.vertex_count );// z którego wierzchołka kopiować wektor normalny
|
||||
int maska = 0;
|
||||
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 {
|
||||
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 );
|
||||
}
|
||||
*/
|
||||
// i szukanie od kolejnego trójkąta
|
||||
adjacenvertextidx = SeekFaceNormal(sg, adjacenvertextidx / 3 + 1, sg[faceidx], Vertices[vertexidx].position, Vertices);
|
||||
wsp[idx] = idx; // wektory normalne "są już policzone"
|
||||
}
|
||||
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
|
||||
if( glm::length2( vertexnormal ) == 0.0f ) {
|
||||
WriteLog( "Bad model: zero length normal vector generated for sub-model \"" + pName + "\"", logtype::model );
|
||||
}
|
||||
Vertices[ vertexidx ].normal = (
|
||||
glm::length2( vertexnormal ) > 0.0f ?
|
||||
glm::normalize( vertexnormal ) :
|
||||
facenormals[ vertexidx / 3 ] ); // przepisanie do wierzchołka trójkąta
|
||||
}
|
||||
}
|
||||
Vertices.resize( iNumVerts ); // in case we had some degenerate triangles along the way
|
||||
/*
|
||||
delete[] wsp;
|
||||
delete[] n;
|
||||
delete[] sg;
|
||||
*/
|
||||
}
|
||||
else {
|
||||
// inaczej musimy dopiero policzyć
|
||||
auto const faceidx = vertexidx / 3; // numer trójkąta
|
||||
vertexnormal = glm::vec3(); // liczenie zaczynamy od zera
|
||||
auto adjacenvertextidx = vertexidx; // zaczynamy dodawanie wektorów normalnych od własnego
|
||||
while (adjacenvertextidx >= 0) {
|
||||
// sumowanie z wektorem normalnym sąsiada (włącznie ze sobą)
|
||||
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 ];
|
||||
}
|
||||
/*
|
||||
else {
|
||||
ErrorLog( "Bad model: opposite normals in the same smoothing group, check sub-model \"" + pName + "\" for two-sided faces and/or scaling", logtype::model );
|
||||
}
|
||||
*/
|
||||
// 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
|
||||
{
|
||||
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
|
||||
}
|
||||
@@ -673,44 +697,46 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
|
||||
// dla smt_Mesh
|
||||
std::string discard;
|
||||
parser.getTokens(2, false);
|
||||
parser >> discard >> iNumVerts;
|
||||
/*
|
||||
// Vertices=new GLVERTEX[iNumVerts];
|
||||
Vertices = new basic_vertex[iNumVerts];
|
||||
*/
|
||||
Vertices.resize( iNumVerts );
|
||||
int i;
|
||||
parser >> discard >> m_geometry.vertex_count;
|
||||
Vertices.resize( m_geometry.vertex_count );
|
||||
int idx;
|
||||
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.getTokens(5, false);
|
||||
auto vertex { vertices + idx };
|
||||
parser.getTokens(5, false);
|
||||
parser
|
||||
>> Vertices[i].position.x
|
||||
>> Vertices[i].position.y
|
||||
>> Vertices[i].position.z
|
||||
>> vertex->position.x
|
||||
>> vertex->position.y
|
||||
>> vertex->position.z
|
||||
>> color // zakodowany kolor
|
||||
>> discard;
|
||||
Vertices[i].normal.x = ((color) & 0xff) / 255.0f; // R
|
||||
Vertices[i].normal.y = ((color >> 8) & 0xff) / 255.0f; // G
|
||||
Vertices[i].normal.z = ((color >> 16) & 0xff) / 255.0f; // B
|
||||
vertex->normal = { ( ( color ) & 0xff ) / 255.0f, // R
|
||||
( ( color >> 8 ) & 0xff ) / 255.0f, // G
|
||||
( ( color >> 16 ) & 0xff ) / 255.0f }; // B
|
||||
}
|
||||
}
|
||||
else if( eType == TP_FREESPOTLIGHT ) {
|
||||
// single light points only have single data point, duh
|
||||
Vertices.emplace_back();
|
||||
iNumVerts = 1;
|
||||
m_geometry.vertex_count = 1;
|
||||
}
|
||||
// Visible=true; //się potem wyłączy w razie potrzeby
|
||||
// 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
|
||||
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)
|
||||
{ // dodanie trójkątów do submodelu, używane przy tworzeniu E3D terenu
|
||||
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
|
||||
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 )
|
||||
{ // 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 ) {
|
||||
Child->serialize_geometry( Output );
|
||||
Child->serialize_geometry( Output, Packed, Indexed );
|
||||
}
|
||||
if( m_geometry != null_handle ) {
|
||||
for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) {
|
||||
vertex.serialize( Output );
|
||||
if( m_geometry.handle != null_handle ) {
|
||||
if( Packed ) {
|
||||
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 ) {
|
||||
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
|
||||
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
|
||||
// (the offsets are part of legacy system which we now need to work around for backward compatibility)
|
||||
if( Child )
|
||||
Child->create_geometry( Dataoffset, Bank );
|
||||
if( Child ) {
|
||||
Child->create_geometry( Indexoffset, Vertexoffset, Bank );
|
||||
}
|
||||
|
||||
if( false == Vertices.empty() ) {
|
||||
|
||||
tVboPtr = static_cast<int>( Dataoffset );
|
||||
Dataoffset += Vertices.size();
|
||||
m_geometry.index_offset = static_cast<int>( Indexoffset );
|
||||
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
|
||||
auto type = (
|
||||
eType < TP_ROTATOR ?
|
||||
eType :
|
||||
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
|
||||
float squaredradius {};
|
||||
// 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
|
||||
m_boundingradius *= m_boundingradius;
|
||||
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 );
|
||||
if( squaredradius > m_boundingradius ) {
|
||||
m_boundingradius = squaredradius;
|
||||
@@ -1216,8 +1305,9 @@ TSubModel::create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle co
|
||||
m_boundingradius );
|
||||
}
|
||||
|
||||
if( Next )
|
||||
Next->create_geometry( Dataoffset, Bank );
|
||||
if( Next ) {
|
||||
Next->create_geometry( Indexoffset, Vertexoffset, Bank );
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
float TSubModel::MaxY( float4x4 const &m ) {
|
||||
// tylko dla trójkątów liczymy
|
||||
if( eType != 4 ) { return 0; }
|
||||
if( eType != GL_TRIANGLES ) { return 0; }
|
||||
|
||||
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
|
||||
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,
|
||||
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() {
|
||||
|
||||
if (iFlags & 0x0200) {
|
||||
@@ -1412,8 +1494,8 @@ TSubModel::offset( float const Geometrytestoffsetthreshold ) const {
|
||||
// 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
|
||||
auto const &vertices { (
|
||||
m_geometry != null_handle ?
|
||||
GfxRenderer->Vertices( m_geometry ) :
|
||||
m_geometry.handle != null_handle ?
|
||||
GfxRenderer->Vertices( m_geometry.handle ) :
|
||||
Vertices ) };
|
||||
if( false == vertices.empty() ) {
|
||||
// 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, (int32_t)get_container_pos(transforms, *fMatrix));
|
||||
|
||||
sn_utils::ls_int32(s, iNumVerts);
|
||||
sn_utils::ls_int32(s, tVboPtr);
|
||||
sn_utils::ls_int32( s, m_geometry.vertex_count );
|
||||
sn_utils::ls_int32( s, m_geometry.vertex_offset );
|
||||
|
||||
if (m_material <= 0)
|
||||
sn_utils::ls_int32(s, m_material);
|
||||
else
|
||||
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);
|
||||
sn_utils::ls_float32(s, 1.f); // fVisible
|
||||
sn_utils::ls_float32(s, fLight);
|
||||
|
||||
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, 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();
|
||||
for (size_t i = 0; i < fill; i++)
|
||||
s.put(0);
|
||||
@@ -1597,9 +1681,21 @@ void TModel3d::SaveToBinFile(std::string const &FileName)
|
||||
for (size_t i = 0; i < transforms.size(); i++)
|
||||
transforms[i].serialize_float32(s);
|
||||
|
||||
sn_utils::ls_uint32(s, MAKE_ID4('V', 'N', 'T', '0'));
|
||||
sn_utils::ls_uint32(s, 8 + iNumVerts * 32);
|
||||
Root->serialize_geometry( s );
|
||||
auto const isindexed { m_indexcount > 0 };
|
||||
if( isindexed ) {
|
||||
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())
|
||||
{
|
||||
@@ -1648,12 +1744,11 @@ void TSubModel::deserialize(std::istream &s)
|
||||
iFlags = sn_utils::ld_int32(s);
|
||||
iMatrix = sn_utils::ld_int32(s);
|
||||
|
||||
iNumVerts = sn_utils::ld_int32(s);
|
||||
tVboPtr = sn_utils::ld_int32(s);
|
||||
m_geometry.vertex_count = sn_utils::ld_int32( s );
|
||||
m_geometry.vertex_offset = sn_utils::ld_int32( s );
|
||||
iTexture = sn_utils::ld_int32(s);
|
||||
|
||||
// fVisible = sn_utils::ld_float32(s);
|
||||
auto discard = sn_utils::ld_float32(s);
|
||||
auto discard = sn_utils::ld_float32(s); // fVisible
|
||||
fLight = sn_utils::ld_float32(s);
|
||||
|
||||
f4Ambient = sn_utils::d_vec4(s);
|
||||
@@ -1673,7 +1768,9 @@ void TSubModel::deserialize(std::istream &s)
|
||||
fCosFalloffAngle = sn_utils::ld_float32(s);
|
||||
fCosHotspotAngle = 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
|
||||
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;
|
||||
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)
|
||||
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);
|
||||
}
|
||||
}
|
||||
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.
|
||||
// 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: VNT chunk encountered before SUB chunk" );
|
||||
std::vector< std::pair<int, int> > submodeloffsets; // vertex data offset, submodel index
|
||||
submodeloffsets.reserve( iSubModelsCount );
|
||||
for( int submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) {
|
||||
auto const &submodel = Root[ submodelindex ];
|
||||
if( submodel.iNumVerts <= 0 ) { continue; }
|
||||
submodeloffsets.emplace_back( submodel.tVboPtr, submodelindex );
|
||||
for( auto submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) {
|
||||
auto const &submodelgeometry { Root[ submodelindex ].m_geometry };
|
||||
if( submodelgeometry.vertex_count <= 0 ) { continue; }
|
||||
submodeloffsets.emplace_back( submodelgeometry.vertex_offset, submodelindex );
|
||||
}
|
||||
std::sort(
|
||||
submodeloffsets.begin(),
|
||||
submodeloffsets.end(),
|
||||
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 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 ) {
|
||||
auto &submodel = Root[ submodeloffset.second ];
|
||||
gfx::vertex_array vertices; vertices.resize( submodel.iNumVerts );
|
||||
iNumVerts += submodel.iNumVerts;
|
||||
for( auto &vertex : vertices ) {
|
||||
vertex.deserialize( s );
|
||||
if( submodel.eType < TP_ROTATOR ) {
|
||||
// normal vectors debug routine
|
||||
auto normallength = glm::length2( vertex.normal );
|
||||
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
|
||||
WriteLog( "Bad model: non-unit normal vector(s) encountered during sub-model geometry deserialization", logtype::model );
|
||||
auto &submodel { Root[ submodeloffset.second ] };
|
||||
auto const &submodelgeometry { submodel.m_geometry };
|
||||
submodel.Vertices.resize( submodelgeometry.vertex_count );
|
||||
m_vertexcount += submodelgeometry.vertex_count;
|
||||
if( vertextype > 0 ) {
|
||||
// expanded chunk formats
|
||||
for( auto &vertex : submodel.Vertices ) {
|
||||
vertex.deserialize_packed( s, vertextype > 0 );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 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 ) {
|
||||
case TP_FREESPOTLIGHT:
|
||||
case TP_STARS: {
|
||||
type = GL_POINTS;
|
||||
break; }
|
||||
}
|
||||
}
|
||||
else if( ( type & 0x00FFFFFF ) == MAKE_ID4( 'I', 'D', 'X', 0 ) ) // geometry indices
|
||||
{
|
||||
// handled similarly to vertex data 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
|
||||
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: {
|
||||
type = submodel.eType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
submodel.m_geometry = GfxRenderer->Insert( vertices, m_geometrybank, type );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (type == MAKE_ID4('T', 'R', 'A', '0'))
|
||||
{
|
||||
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];
|
||||
if (Root[i].NextGet())
|
||||
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\\"...
|
||||
TSubModel *SubModel;
|
||||
std::string token = parser.getToken<std::string>();
|
||||
iNumVerts = 0; // w konstruktorze to jest
|
||||
while (token != "" || parser.eof())
|
||||
{
|
||||
std::string parent;
|
||||
@@ -1991,8 +2137,11 @@ void TModel3d::LoadFromTextFile(std::string const &FileName, bool dynamic)
|
||||
break;
|
||||
}
|
||||
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
|
||||
SubModel->Parent = AddToNamed(parent.c_str(), SubModel);
|
||||
|
||||
@@ -2031,12 +2180,13 @@ void TModel3d::Init()
|
||||
Root->InitialRotate(true);
|
||||
}
|
||||
iFlags |= Root->FlagsCheck() | 0x8000; // flagi całego modelu
|
||||
if (iNumVerts) {
|
||||
if (m_vertexcount) {
|
||||
if( m_geometrybank == null_handle ) {
|
||||
m_geometrybank = GfxRenderer->Create_Bank();
|
||||
}
|
||||
std::size_t dataoffset = 0;
|
||||
Root->create_geometry( dataoffset, m_geometrybank );
|
||||
std::size_t indexoffset = 0;
|
||||
std::size_t vertexoffset = 0;
|
||||
Root->create_geometry( indexoffset, vertexoffset, m_geometrybank );
|
||||
}
|
||||
// determine final bounding radius from the root-level siblings
|
||||
auto const *root { Root };
|
||||
|
||||
37
Model3d.h
37
Model3d.h
@@ -16,6 +16,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "material.h"
|
||||
#include "gl/query.h"
|
||||
|
||||
#define EU07_USE_GEOMETRYINDEXING
|
||||
|
||||
// Ra: specjalne typy submodeli, poza tym GL_TRIANGLES itp.
|
||||
const int TP_ROTATOR = 256;
|
||||
const int TP_FREESPOTLIGHT = 257;
|
||||
@@ -70,6 +72,13 @@ public:
|
||||
rescale,
|
||||
normalize
|
||||
};
|
||||
struct geometry_data {
|
||||
gfx::geometry_handle handle;
|
||||
int vertex_offset;
|
||||
int vertex_count;
|
||||
int index_offset;
|
||||
int index_count;
|
||||
};
|
||||
|
||||
private:
|
||||
int iNext{ 0 };
|
||||
@@ -99,8 +108,6 @@ private:
|
||||
float4x4 *fMatrix = nullptr; // pojedyncza precyzja wystarcza
|
||||
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
|
||||
float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum
|
||||
glm::vec4
|
||||
@@ -127,8 +134,6 @@ private:
|
||||
|
||||
TSubModel *Next { nullptr };
|
||||
TSubModel *Child { nullptr };
|
||||
public: // temporary access, clean this up during refactoring
|
||||
gfx::geometry_handle m_geometry { 0, 0 }; // geometry of the submodel
|
||||
private:
|
||||
material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak
|
||||
bool bWire { false }; // nie używane, ale wczytywane
|
||||
@@ -139,7 +144,9 @@ private:
|
||||
|
||||
public: // chwilowo
|
||||
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::index_array Indices;
|
||||
float m_boundingradius { 0 };
|
||||
std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
|
||||
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)
|
||||
TSubModel() = default;
|
||||
~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 NextAdd(TSubModel *SubModel);
|
||||
TSubModel * NextGet() { return Next; };
|
||||
@@ -177,7 +184,9 @@ public:
|
||||
std::tuple<TSubModel *, bool> find_replacable4();
|
||||
// locates particle emitter submodels and adds them to provided list
|
||||
void find_smoke_sources( nameoffset_sequence &Sourcelist ) const;
|
||||
#ifndef EU07_USE_GEOMETRYINDEXING
|
||||
int TriangleAdd(TModel3d *m, material_handle tex, int tri);
|
||||
#endif
|
||||
void SetRotate(float3 vNewRotateAxis, float fNewAngle);
|
||||
void SetRotateXYZ( Math3D::vector3 vNewAngles);
|
||||
void SetRotateXYZ(float3 vNewAngles);
|
||||
@@ -191,7 +200,7 @@ public:
|
||||
glm::vec3 offset( float const Geometrytestoffsetthreshold = 0.f ) const;
|
||||
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();
|
||||
void WillBeAnimated() {
|
||||
iFlags |= 0x4000; };
|
||||
@@ -233,7 +242,9 @@ public:
|
||||
std::vector<std::string>&,
|
||||
std::vector<std::string>&,
|
||||
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
|
||||
};
|
||||
|
||||
@@ -243,23 +254,23 @@ class TModel3d
|
||||
friend opengl33_renderer;
|
||||
|
||||
private:
|
||||
TSubModel *Root; // drzewo submodeli
|
||||
int iFlags; // Ra: czy submodele mają przezroczyste tekstury
|
||||
TSubModel *Root { nullptr }; // drzewo submodeli
|
||||
int iFlags { 0 }; // Ra: czy submodele mają przezroczyste tekstury
|
||||
public: // Ra: tymczasowo
|
||||
int iNumVerts; // ilość wierzchołków (gdy nie ma VBO, to m_nVertexCount=0)
|
||||
gfx::geometrybank_handle m_geometrybank;
|
||||
bool m_geometrycreated { false };
|
||||
int m_indexcount { 0 };
|
||||
int m_vertexcount { 0 }; // ilość wierzchołków
|
||||
private:
|
||||
std::vector<std::string> Textures; // nazwy tekstur
|
||||
std::vector<std::string> Names; // nazwy submodeli
|
||||
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 m_filename;
|
||||
nameoffset_sequence m_smokesources; // list of particle sources defined in the model
|
||||
|
||||
public:
|
||||
TModel3d();
|
||||
TModel3d() = default;
|
||||
~TModel3d();
|
||||
float bounding_radius() const {
|
||||
return (
|
||||
|
||||
137
geometrybank.cpp
137
geometrybank.cpp
@@ -19,39 +19,57 @@ http://mozilla.org/MPL/2.0/.
|
||||
namespace gfx {
|
||||
|
||||
void
|
||||
basic_vertex::serialize( std::ostream &s ) 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 );
|
||||
basic_vertex::serialize( std::ostream &s, bool const Tangent ) const {
|
||||
|
||||
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.y );
|
||||
if( Tangent ) {
|
||||
sn_utils::s_vec4( s, tangent );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_vertex::deserialize( std::istream &s ) {
|
||||
|
||||
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 );
|
||||
basic_vertex::deserialize( std::istream &s, bool const Tangent ) {
|
||||
|
||||
position = sn_utils::d_vec3( s );
|
||||
normal = sn_utils::d_vec3( s );
|
||||
texture.x = 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
|
||||
// Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”.
|
||||
// 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();
|
||||
|
||||
@@ -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
|
||||
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
bool
|
||||
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_();
|
||||
}
|
||||
|
||||
// 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 &
|
||||
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 };
|
||||
}
|
||||
|
||||
// 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
|
||||
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 }; }
|
||||
}
|
||||
|
||||
// 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
|
||||
bool
|
||||
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 );
|
||||
}
|
||||
|
||||
// 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
|
||||
gfx::vertex_array const &
|
||||
geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
@@ -24,8 +24,10 @@ struct basic_vertex {
|
||||
basic_vertex( glm::vec3 Position, glm::vec3 Normal, glm::vec2 Texture ) :
|
||||
position( Position ), normal( Normal ), texture( Texture )
|
||||
{}
|
||||
void serialize( std::ostream& ) const;
|
||||
void deserialize( std::istream& );
|
||||
void serialize( std::ostream&, bool const Tangent = false ) const;
|
||||
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
|
||||
@@ -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
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
@@ -90,9 +96,11 @@ public:
|
||||
~geometry_bank() {}
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
auto append( gfx::vertex_array &Vertices, gfx::geometry_handle const &Geometry ) -> bool;
|
||||
@@ -107,6 +115,8 @@ public:
|
||||
return count; }
|
||||
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
|
||||
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
|
||||
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
|
||||
|
||||
@@ -115,12 +125,20 @@ protected:
|
||||
struct geometry_chunk {
|
||||
unsigned int type; // kind of geometry used by the chunk
|
||||
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 ) :
|
||||
type( Type )
|
||||
{
|
||||
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>;
|
||||
@@ -162,8 +180,10 @@ public:
|
||||
void update();
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
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;
|
||||
// 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
|
||||
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
|
||||
@@ -175,6 +195,8 @@ public:
|
||||
while( First != Last ) {
|
||||
draw( *First, Streams );
|
||||
++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
|
||||
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
|
||||
// sets target texture unit for the texture data stream
|
||||
|
||||
@@ -33,7 +33,7 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
|
||||
auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ];
|
||||
chunkrecord.is_good = false;
|
||||
// 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
|
||||
// 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?
|
||||
@@ -43,51 +43,62 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
void opengl33_vaogeometrybank::setup_buffer()
|
||||
{
|
||||
if( !m_buffer ) {
|
||||
// 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
|
||||
// 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( m_vertexbuffer ) { return; }
|
||||
// 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
|
||||
// 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; }
|
||||
|
||||
std::size_t datasize{ 0 };
|
||||
auto chunkiterator = m_chunks.cbegin();
|
||||
for( auto &chunkrecord : m_chunkrecords ) {
|
||||
// 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.offset = datasize;
|
||||
chunkrecord.size = chunkiterator->vertices.size();
|
||||
datasize += chunkrecord.size;
|
||||
++chunkiterator;
|
||||
}
|
||||
// the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
|
||||
if( datasize == 0 ) { return; }
|
||||
// try to set up the buffer we need
|
||||
m_buffer.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_buffer->allocate(gl::buffer::ARRAY_BUFFER, datasize * sizeof(gfx::basic_vertex), GL_STATIC_DRAW);
|
||||
std::size_t
|
||||
vertexcount{ 0 },
|
||||
indexcount{ 0 };
|
||||
auto chunkiterator = m_chunks.cbegin();
|
||||
for( auto &chunkrecord : m_chunkrecords ) {
|
||||
// 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.vertex_offset = vertexcount;
|
||||
chunkrecord.vertex_count = chunkiterator->vertices.size();
|
||||
vertexcount += chunkrecord.vertex_count;
|
||||
chunkrecord.index_offset = indexcount;
|
||||
chunkrecord.index_count = chunkiterator->indices.size();
|
||||
indexcount += chunkrecord.index_count;
|
||||
++chunkiterator;
|
||||
}
|
||||
// the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
|
||||
if( vertexcount == 0 ) { return; }
|
||||
|
||||
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 ) {
|
||||
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();
|
||||
}
|
||||
m_buffercapacity = datasize;
|
||||
m_vao->setup_ebo( *m_indexbuffer );
|
||||
}
|
||||
|
||||
if (!m_vao)
|
||||
{
|
||||
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();
|
||||
else {
|
||||
gl::buffer::unbind( gl::buffer::ELEMENT_ARRAY_BUFFER );
|
||||
}
|
||||
// ...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
|
||||
@@ -100,23 +111,44 @@ opengl33_vaogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stre
|
||||
|
||||
auto &chunkrecord = m_chunkrecords.at(Geometry.chunk - 1);
|
||||
// sanity check; shouldn't be needed but, eh
|
||||
if( chunkrecord.size == 0 )
|
||||
if( chunkrecord.vertex_count == 0 )
|
||||
return 0;
|
||||
|
||||
m_vao->bind();
|
||||
|
||||
auto const &chunk = gfx::geometry_bank::chunk( Geometry );
|
||||
if( false == chunkrecord.is_good ) {
|
||||
// we may potentially need to upload new buffer data before we can draw it
|
||||
m_buffer->upload(gl::buffer::ARRAY_BUFFER, chunk.vertices.data(),
|
||||
chunkrecord.offset * sizeof( gfx::basic_vertex ),
|
||||
chunkrecord.size * sizeof( gfx::basic_vertex ));
|
||||
if( chunkrecord.index_count > 0 ) {
|
||||
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 ) );
|
||||
}
|
||||
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;
|
||||
}
|
||||
// render
|
||||
m_vao->bind();
|
||||
::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 );
|
||||
}
|
||||
/*
|
||||
m_vao->unbind();
|
||||
*/
|
||||
auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) };
|
||||
switch( chunk.type ) {
|
||||
case GL_TRIANGLES: { return chunkrecord.size / 3; }
|
||||
case GL_TRIANGLE_STRIP: { return chunkrecord.size - 2; }
|
||||
case GL_TRIANGLES: { return vertexcount / 3; }
|
||||
case GL_TRIANGLE_STRIP: { return vertexcount - 2; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
@@ -131,14 +163,11 @@ opengl33_vaogeometrybank::release_() {
|
||||
void
|
||||
opengl33_vaogeometrybank::delete_buffer() {
|
||||
|
||||
if( m_buffer ) {
|
||||
|
||||
m_vao.reset();
|
||||
m_buffer.reset();
|
||||
m_buffercapacity = 0;
|
||||
// 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
|
||||
}
|
||||
m_vao.reset();
|
||||
m_vertexbuffer.reset();
|
||||
m_indexbuffer.reset();
|
||||
// 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
|
||||
|
||||
@@ -32,9 +32,11 @@ public:
|
||||
|
||||
private:
|
||||
// types:
|
||||
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 size{ 0 }; // size of the chunk in the last established buffer
|
||||
struct chunk_record {
|
||||
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 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
|
||||
};
|
||||
|
||||
@@ -59,14 +61,10 @@ private:
|
||||
delete_buffer();
|
||||
|
||||
// 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::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
|
||||
// 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
|
||||
@@ -22,6 +22,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "application.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_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));
|
||||
|
||||
glfwSwapInterval( Global.VSync ? 1 : 0 );
|
||||
// glfwSwapInterval( Global.VSync ? 1 : 0 );
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
@@ -470,9 +472,6 @@ bool opengl33_renderer::Render()
|
||||
glfwMakeContextCurrent(m_window);
|
||||
gl::buffer::unbind();
|
||||
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" +=
|
||||
"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";
|
||||
*/
|
||||
}
|
||||
|
||||
// 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;
|
||||
for( auto const &shadowpass : m_shadowpass ) {
|
||||
shadowstats += shadowpass.draw_stats;
|
||||
}
|
||||
|
||||
m_debugstatstext =
|
||||
"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 )
|
||||
@@ -508,44 +544,6 @@ bool opengl33_renderer::Render()
|
||||
+ " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n"
|
||||
+ " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n"
|
||||
+ "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()
|
||||
@@ -799,10 +797,13 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
glViewport(0, 0, target_size.x, target_size.y);
|
||||
m_pfx_tonemapping->apply(*vp.main2_tex, nullptr);
|
||||
|
||||
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();
|
||||
@@ -1725,10 +1726,17 @@ gfx::geometrybank_handle opengl33_renderer::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::calculate_tangent(Vertices, Type);
|
||||
gfx::calculate_tangents(Vertices, 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
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
gfx::vertex_array const &opengl33_renderer::Vertices(gfx::geometry_handle const &Geometry) const
|
||||
{
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
// submodel-based parameters
|
||||
@@ -1852,7 +1863,7 @@ void opengl33_renderer::Bind_Material( material_handle const Material, TSubModel
|
||||
|
||||
if( entry.size == 1 ) {
|
||||
// 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++)
|
||||
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;
|
||||
|
||||
// main draw call
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
|
||||
// post-draw reset
|
||||
model_ubs.emission = 0.0f;
|
||||
@@ -2901,13 +2912,13 @@ void opengl33_renderer::Render(TSubModel *Submodel)
|
||||
// również 0
|
||||
Bind_Material_Shadow(Submodel->m_material);
|
||||
}
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
break;
|
||||
}
|
||||
case rendermode::pickscenery:
|
||||
{
|
||||
m_pick_shader->bind();
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
break;
|
||||
}
|
||||
case rendermode::pickcontrols:
|
||||
@@ -2916,7 +2927,7 @@ void opengl33_renderer::Render(TSubModel *Submodel)
|
||||
// control picking applies individual colour for each submodel
|
||||
m_pickcontrolsitems.emplace_back(Submodel);
|
||||
model_ubs.param[0] = glm::vec4(pick_color(m_pickcontrolsitems.size()), 1.0f);
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2942,7 +2953,7 @@ void opengl33_renderer::Render(TSubModel *Submodel)
|
||||
// main draw call
|
||||
model_ubs.param[1].x = m_pointsize;
|
||||
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3703,7 +3714,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel)
|
||||
model_ubs.emission = Submodel->f4Emision.a;
|
||||
|
||||
// main draw call
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
|
||||
model_ubs.emission = 0.0f;
|
||||
break;
|
||||
@@ -3718,7 +3729,7 @@ void opengl33_renderer::Render_Alpha(TSubModel *Submodel)
|
||||
{
|
||||
Bind_Material_Shadow(Submodel->m_material);
|
||||
}
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
break;
|
||||
}
|
||||
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[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[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->begin();
|
||||
|
||||
draw(Submodel->m_geometry);
|
||||
draw(Submodel->m_geometry.handle);
|
||||
|
||||
Submodel->occlusion_query->end();
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ class opengl33_renderer : public gfx_renderer {
|
||||
// main draw call. returns false on error
|
||||
bool
|
||||
Render() override;
|
||||
void
|
||||
SwapBuffers() override;
|
||||
inline
|
||||
float
|
||||
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
|
||||
gfx::geometrybank_handle
|
||||
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
|
||||
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
|
||||
@@ -66,6 +71,9 @@ class opengl33_renderer : public gfx_renderer {
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool
|
||||
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
|
||||
gfx::vertex_array const &
|
||||
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 );
|
||||
void SwapBuffers();
|
||||
// draws supplied geometry handles
|
||||
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);
|
||||
@@ -128,7 +135,6 @@ class opengl33_renderer : public gfx_renderer {
|
||||
|
||||
// members
|
||||
GLenum static const sunlight{0};
|
||||
std::size_t m_drawcount{0};
|
||||
|
||||
bool debug_ui_active = false;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace gfx {
|
||||
|
||||
// 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
|
||||
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 ];
|
||||
chunkrecord.is_good = false;
|
||||
// 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
|
||||
// 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?
|
||||
@@ -53,18 +53,25 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream
|
||||
|
||||
auto &chunkrecord { m_chunkrecords[ Geometry.chunk - 1 ] };
|
||||
// sanity check; shouldn't be needed but, eh
|
||||
if( chunkrecord.size == 0 ) { return 0; }
|
||||
if( chunkrecord.vertex_count == 0 ) { return 0; }
|
||||
// setup...
|
||||
if( m_activebuffer != m_buffer ) {
|
||||
if( m_activevertexbuffer != m_vertexbuffer ) {
|
||||
bind_buffer();
|
||||
}
|
||||
auto const &chunk = gfx::geometry_bank::chunk( Geometry );
|
||||
if( false == chunkrecord.is_good ) {
|
||||
// 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(
|
||||
GL_ARRAY_BUFFER,
|
||||
chunkrecord.offset * sizeof( gfx::basic_vertex ),
|
||||
chunkrecord.size * sizeof( gfx::basic_vertex ),
|
||||
chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ),
|
||||
chunkrecord.vertex_count * sizeof( gfx::basic_vertex ),
|
||||
chunk.vertices.data() );
|
||||
chunkrecord.is_good = true;
|
||||
}
|
||||
@@ -72,7 +79,22 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream
|
||||
bind_streams( Units, Streams );
|
||||
}
|
||||
// ...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
|
||||
/*
|
||||
::glDisableClientState( GL_VERTEX_ARRAY );
|
||||
@@ -80,9 +102,10 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream
|
||||
::glDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0;
|
||||
*/
|
||||
auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) };
|
||||
switch( chunk.type ) {
|
||||
case GL_TRIANGLES: { return chunkrecord.size / 3; }
|
||||
case GL_TRIANGLE_STRIP: { return chunkrecord.size - 2; }
|
||||
case GL_TRIANGLES: { return vertexcount / 3; }
|
||||
case GL_TRIANGLE_STRIP: { return vertexcount - 2; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
@@ -97,33 +120,57 @@ opengl_vbogeometrybank::release_() {
|
||||
void
|
||||
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
|
||||
// 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
|
||||
if( true == m_chunks.empty() ) { return; }
|
||||
|
||||
std::size_t datasize{ 0 };
|
||||
std::size_t
|
||||
vertexcount{ 0 },
|
||||
indexcount{ 0 };
|
||||
auto chunkiterator = m_chunks.cbegin();
|
||||
for( auto &chunkrecord : m_chunkrecords ) {
|
||||
// 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.offset = datasize;
|
||||
chunkrecord.size = chunkiterator->vertices.size();
|
||||
datasize += chunkrecord.size;
|
||||
chunkrecord.vertex_offset = vertexcount;
|
||||
chunkrecord.vertex_count = chunkiterator->vertices.size();
|
||||
vertexcount += chunkrecord.vertex_count;
|
||||
chunkrecord.index_offset = indexcount;
|
||||
chunkrecord.index_count = chunkiterator->indices.size();
|
||||
indexcount += chunkrecord.index_count;
|
||||
++chunkiterator;
|
||||
}
|
||||
// the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist
|
||||
if( datasize == 0 ) { return; }
|
||||
// try to set up the buffer we need
|
||||
::glGenBuffers( 1, &m_buffer );
|
||||
if( vertexcount == 0 ) { return; }
|
||||
// try to set up the buffers we need
|
||||
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();
|
||||
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
|
||||
// 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(
|
||||
GL_ARRAY_BUFFER,
|
||||
datasize * sizeof( gfx::basic_vertex ),
|
||||
vertexcount * sizeof( gfx::basic_vertex ),
|
||||
nullptr,
|
||||
GL_STATIC_DRAW );
|
||||
if( ::glGetError() == GL_OUT_OF_MEMORY ) {
|
||||
@@ -132,29 +179,32 @@ opengl_vbogeometrybank::setup_buffer() {
|
||||
delete_buffer();
|
||||
return;
|
||||
}
|
||||
m_buffercapacity = datasize;
|
||||
}
|
||||
|
||||
void
|
||||
opengl_vbogeometrybank::bind_buffer() {
|
||||
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_buffer );
|
||||
m_activebuffer = m_buffer;
|
||||
::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer );
|
||||
::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer );
|
||||
m_activevertexbuffer = m_vertexbuffer;
|
||||
m_activestreams = gfx::stream::none;
|
||||
}
|
||||
|
||||
void
|
||||
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 );
|
||||
if( m_activebuffer == m_buffer ) {
|
||||
m_activebuffer = 0;
|
||||
::glDeleteBuffers( 1, &m_vertexbuffer );
|
||||
if( m_activevertexbuffer == m_vertexbuffer ) {
|
||||
m_activevertexbuffer = 0;
|
||||
release_streams();
|
||||
}
|
||||
m_buffer = 0;
|
||||
m_buffercapacity = 0;
|
||||
m_vertexbuffer = 0;
|
||||
// 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
|
||||
}
|
||||
@@ -252,18 +302,32 @@ opengl_dlgeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_
|
||||
::glNewList( chunkrecord.list, GL_COMPILE );
|
||||
|
||||
::glBegin( chunk.type );
|
||||
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 ) ); }
|
||||
if( chunk.indices.size() > 0 ) {
|
||||
// indexed geometry
|
||||
for( auto const &index : chunk.indices ) {
|
||||
auto const &vertex { chunk.vertices[ index ] };
|
||||
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();
|
||||
::glEndList();
|
||||
|
||||
auto const vertexcount { ( chunk.indices.empty() ? chunk.vertices.size() : chunk.indices.size() ) };
|
||||
switch( chunk.type ) {
|
||||
case GL_TRIANGLES: { chunkrecord.primitive_count += chunk.vertices.size() / 3; break; }
|
||||
case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += chunk.vertices.size() - 2; break; }
|
||||
case GL_TRIANGLES: { chunkrecord.primitive_count += vertexcount / 3; break; }
|
||||
case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += vertexcount - 2; break; }
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,14 +27,16 @@ public:
|
||||
static
|
||||
void
|
||||
reset() {
|
||||
m_activebuffer = 0;
|
||||
m_activevertexbuffer = 0;
|
||||
m_activestreams = gfx::stream::none; }
|
||||
|
||||
private:
|
||||
// types:
|
||||
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 size{ 0 }; // size of the chunk in the last established buffer
|
||||
struct chunk_record {
|
||||
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 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
|
||||
};
|
||||
|
||||
@@ -67,11 +69,11 @@ private:
|
||||
release_streams();
|
||||
|
||||
// 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 std::vector<GLint> m_activetexturearrays;
|
||||
GLuint m_buffer { 0 }; // id of the buffer holding data on the opengl end
|
||||
std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer
|
||||
GLuint m_vertexbuffer { 0 }; // id of the buffer holding vertex data on the opengl end
|
||||
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
|
||||
|
||||
};
|
||||
|
||||
@@ -334,7 +334,6 @@ opengl_renderer::Render() {
|
||||
m_renderpass.draw_mode = rendermode::none; // force setup anew
|
||||
m_renderpass.draw_stats = debug_stats();
|
||||
m_geometry.primitives_count() = 0;
|
||||
m_debugtimestext.clear();
|
||||
Render_pass( rendermode::color );
|
||||
Timer::subsystem.gfx_color.stop();
|
||||
// add user interface
|
||||
@@ -348,12 +347,21 @@ opengl_renderer::Render() {
|
||||
// swapbuffers() will unbind current buffers so we prepare for it on our end
|
||||
gfx::opengl_vbogeometrybank::reset();
|
||||
}
|
||||
++m_framestamp;
|
||||
SwapBuffers();
|
||||
|
||||
return true; // for now always succeed
|
||||
}
|
||||
|
||||
void
|
||||
opengl_renderer::SwapBuffers() {
|
||||
|
||||
Timer::subsystem.gfx_swap.start();
|
||||
glfwSwapBuffers( m_window );
|
||||
Timer::subsystem.gfx_swap.stop();
|
||||
|
||||
m_drawcount = m_cellqueue.size();
|
||||
m_debugtimestext +=
|
||||
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.RenderShadows ) {
|
||||
@@ -380,10 +388,6 @@ opengl_renderer::Render() {
|
||||
+ " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n"
|
||||
+ " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n"
|
||||
+ "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
|
||||
@@ -1669,7 +1673,14 @@ opengl_renderer::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
|
||||
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 );
|
||||
}
|
||||
|
||||
// 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
|
||||
gfx::vertex_array const &
|
||||
opengl_renderer::Vertices( gfx::geometry_handle const &Geometry ) const {
|
||||
@@ -2749,7 +2767,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
|
||||
}
|
||||
|
||||
// main draw call
|
||||
m_geometry.draw( Submodel->m_geometry );
|
||||
m_geometry.draw( Submodel->m_geometry.handle );
|
||||
/*
|
||||
if( DebugModeFlag ) {
|
||||
auto const & vertices { m_geometry.vertices( Submodel->m_geometry ) };
|
||||
@@ -2811,7 +2829,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
|
||||
Bind_Material( Submodel->m_material );
|
||||
}
|
||||
// main draw call
|
||||
m_geometry.draw( Submodel->m_geometry );
|
||||
m_geometry.draw( Submodel->m_geometry.handle );
|
||||
// post-draw reset
|
||||
break;
|
||||
}
|
||||
@@ -2829,7 +2847,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
|
||||
Bind_Material( Submodel->m_material );
|
||||
}
|
||||
// main draw call
|
||||
m_geometry.draw( Submodel->m_geometry );
|
||||
m_geometry.draw( Submodel->m_geometry.handle );
|
||||
// post-draw reset
|
||||
break;
|
||||
}
|
||||
@@ -2917,7 +2935,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
|
||||
lightcolor[ 2 ],
|
||||
Submodel->fVisible * std::min( 1.f, lightlevel ) * 0.5f );
|
||||
::glDepthMask( GL_FALSE );
|
||||
m_geometry.draw( Submodel->m_geometry );
|
||||
m_geometry.draw( Submodel->m_geometry.handle );
|
||||
::glDepthMask( GL_TRUE );
|
||||
}
|
||||
::glPointSize( pointsize * resolutionratio );
|
||||
@@ -2926,7 +2944,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
|
||||
lightcolor[ 1 ],
|
||||
lightcolor[ 2 ],
|
||||
Submodel->fVisible * std::min( 1.f, lightlevel ) );
|
||||
m_geometry.draw( Submodel->m_geometry );
|
||||
m_geometry.draw( Submodel->m_geometry.handle );
|
||||
|
||||
// post-draw reset
|
||||
switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections );
|
||||
@@ -2958,7 +2976,7 @@ opengl_renderer::Render( TSubModel *Submodel ) {
|
||||
::glDisable( GL_LIGHTING );
|
||||
|
||||
// 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
|
||||
::glPopAttrib();
|
||||
@@ -3793,7 +3811,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
|
||||
}
|
||||
|
||||
// main draw call
|
||||
m_geometry.draw( Submodel->m_geometry );
|
||||
m_geometry.draw( Submodel->m_geometry.handle );
|
||||
|
||||
// post-draw reset
|
||||
if( opacity != 0.f ) {
|
||||
@@ -3839,7 +3857,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) {
|
||||
Bind_Material( Submodel->m_material );
|
||||
}
|
||||
// main draw call
|
||||
m_geometry.draw( Submodel->m_geometry );
|
||||
m_geometry.draw( Submodel->m_geometry.handle );
|
||||
// post-draw reset
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
// main draw call. returns false on error
|
||||
bool
|
||||
Render() override;
|
||||
void
|
||||
SwapBuffers() override;
|
||||
inline
|
||||
float
|
||||
Framerate() override { return m_framerate; }
|
||||
@@ -51,7 +53,10 @@ public:
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
gfx::geometrybank_handle
|
||||
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
|
||||
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
|
||||
@@ -60,6 +65,9 @@ public:
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool
|
||||
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
|
||||
gfx::vertex_array const &
|
||||
Vertices( gfx::geometry_handle const &Geometry ) const override;
|
||||
@@ -111,7 +119,6 @@ public:
|
||||
|
||||
// members
|
||||
GLenum static const sunlight { GL_LIGHT0 };
|
||||
std::size_t m_drawcount { 0 };
|
||||
|
||||
private:
|
||||
// types
|
||||
|
||||
@@ -25,17 +25,22 @@ public:
|
||||
virtual auto Init( GLFWwindow *Window ) -> bool = 0;
|
||||
// main draw call. returns false on error
|
||||
virtual auto Render() -> bool = 0;
|
||||
virtual void SwapBuffers() = 0;
|
||||
virtual auto Framerate() -> float = 0;
|
||||
// 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
|
||||
// creates a new geometry bank. returns: handle to the bank or NULL
|
||||
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;
|
||||
// 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;
|
||||
// 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;
|
||||
// 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
|
||||
virtual auto Vertices( gfx::geometry_handle const &Geometry ) const ->gfx::vertex_array const & = 0;
|
||||
// material methods
|
||||
|
||||
@@ -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
|
||||
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 };
|
||||
std::vector<world_vertex> importedvertices;
|
||||
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.normal = sourcevertex.normal;
|
||||
vertex.texture = sourcevertex.texture;
|
||||
|
||||
43
sn_utils.cpp
43
sn_utils.cpp
@@ -38,6 +38,18 @@ int32_t sn_utils::ld_int32(std::istream &s)
|
||||
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
|
||||
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) };
|
||||
}
|
||||
|
||||
glm::vec3 sn_utils::d_vec3( std::istream& s)
|
||||
glm::vec3 sn_utils::d_vec3( std::istream& s )
|
||||
{
|
||||
return {
|
||||
ld_float32(s),
|
||||
@@ -108,7 +120,7 @@ glm::vec3 sn_utils::d_vec3( std::istream& s)
|
||||
ld_float32(s) };
|
||||
}
|
||||
|
||||
glm::vec4 sn_utils::d_vec4( std::istream& s)
|
||||
glm::vec4 sn_utils::d_vec4( std::istream& s )
|
||||
{
|
||||
return {
|
||||
ld_float32(s),
|
||||
@@ -117,6 +129,14 @@ glm::vec4 sn_utils::d_vec4( std::istream& 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)
|
||||
{
|
||||
uint8_t buf[2];
|
||||
@@ -145,6 +165,20 @@ void sn_utils::ls_int32(std::ostream &s, int32_t v)
|
||||
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)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
@@ -185,6 +219,11 @@ void sn_utils::ls_float64(std::ostream &s, double t)
|
||||
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)
|
||||
{
|
||||
const char* buf = v.c_str();
|
||||
|
||||
@@ -10,9 +10,11 @@ public:
|
||||
static uint16_t ld_uint16(std::istream&);
|
||||
static uint32_t ld_uint32(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 double ld_float64(std::istream&);
|
||||
static uint8_t d_uint8(std::istream&);
|
||||
static std::string d_str(std::istream&);
|
||||
static bool d_bool(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_uint32(std::ostream&, uint32_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_float32(std::ostream&, float);
|
||||
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_dvec3(std::ostream&, glm::dvec3 const &);
|
||||
static void s_vec3(std::ostream&, glm::vec3 const &);
|
||||
|
||||
2
stdafx.h
2
stdafx.h
@@ -58,6 +58,7 @@
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <regex>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
@@ -92,6 +93,7 @@
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/epsilon.hpp>
|
||||
#include <glm/gtc/packing.hpp>
|
||||
#include <glm/gtx/rotate_vector.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
Reference in New Issue
Block a user