basic indexed geometry support, geometry data packing

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

View File

@@ -170,7 +170,7 @@ void TSubModel::SetSelfIllum( float const Threshold, bool const Includechildren,
int TSubModel::SeekFaceNormal(std::vector<unsigned int> const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, gfx::vertex_array const &Vertices)
{ // 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,127 +499,152 @@ 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;
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
/*
// 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
// 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!");
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!" );
}
else
{ // normalna lista wierzchołków
iNumVerts = std::atoi(token.c_str());
if (iNumVerts % 3)
*/
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
*/
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 {
// 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( iNumVerts );// z którego wierzchołka kopiować wektor normalny
std::vector<int> wsp; wsp.resize( m_geometry.vertex_count );// 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;
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[i] = -1; // wektory normalne nie są policzone dla tego wierzchołka
if ((i % 3) == 0) {
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[i / 3] = (
sg[idx / 3] = (
( maska == -1 ) ?
0 :
maska );
}
auto vertex { vertices + idx };
parser.getTokens(3, false);
parser
>> Vertices[i].position.x
>> Vertices[i].position.y
>> Vertices[i].position.z;
>> vertex->position.x
>> vertex->position.y
>> vertex->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 );
>> vertex->normal.x
>> vertex->normal.y
>> vertex->normal.z;
if( glm::length2( vertex->normal ) > 0.0f ) {
glm::normalize( vertex->normal );
}
else {
WriteLog( "Bad model: zero length normal vector specified in: \"" + pName + "\", vertex " + std::to_string(i), logtype::model );
WriteLog( "Bad model: zero length normal vector specified in submodel \"" + pName + "\", vertex " + std::to_string(idx), logtype::model );
}
wsp[i] = i; // wektory normalne "są już policzone"
wsp[idx] = idx; // wektory normalne "są już policzone"
}
parser.getTokens(2, false);
parser
>> Vertices[i].texture.s
>> Vertices[i].texture.t;
if (i % 3 == 2) {
>> vertex->texture.s
>> vertex->texture.t;
if (idx % 3 == 2) {
// jeżeli wczytano 3 punkty
if( true == degenerate( Vertices[ i ].position, Vertices[ i - 1 ].position, Vertices[ i - 2 ].position ) ) {
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
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 );
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 (i > 0) {
if (idx > 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)) {
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
iNumVerts -= 3; // czyli o 3 wierzchołki
i -= 3; // wczytanie kolejnego w to miejsce
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 );
}
}
}
}
/*
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 const vertex { vertices + ( i * 3 ) };
auto facenormal =
glm::cross(
Vertices[ i * 3 ].position - Vertices[ i * 3 + 1 ].position,
Vertices[ i * 3 ].position - Vertices[ i * 3 + 2 ].position );
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 < iNumVerts; ++vertexidx) {
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
Vertices[ vertexidx ].normal = Vertices[ wsp[ vertexidx ] ].normal;
vertex->normal = ( vertices + wsp[ vertexidx ] )->normal;
}
else {
// inaczej musimy dopiero policzyć
@@ -642,29 +663,32 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
}
*/
// i szukanie od kolejnego trójkąta
adjacenvertextidx = SeekFaceNormal(sg, adjacenvertextidx / 3 + 1, sg[faceidx], Vertices[vertexidx].position, Vertices);
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 );
}
Vertices[ vertexidx ].normal = (
vertex->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;
*/
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
}
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.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( m_geometry != null_handle ) {
for( auto const &vertex : GfxRenderer->Vertices( m_geometry ) ) {
vertex.serialize( Output );
}
}
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,67 +1806,103 @@ 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 );
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
auto normallength = glm::length2( vertex.normal );
if( ( false == submodel.m_normalizenormals )
&& ( std::abs( normallength - 1.0f ) > 0.01f ) ) {
&& ( 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'))
{
@@ -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 };

View File

@@ -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 (

View File

@@ -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 {

View File

@@ -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

View File

@@ -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( 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 };
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
m_buffer.emplace();
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 index buffer" );
throw std::bad_alloc();
}
m_vao->setup_ebo( *m_indexbuffer );
}
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_buffer->allocate(gl::buffer::ARRAY_BUFFER, datasize * sizeof(gfx::basic_vertex), GL_STATIC_DRAW);
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_buffercapacity = datasize;
}
if (!m_vao)
{
m_vao.emplace();
m_vao->setup_attrib(*m_buffer, 0, 3, GL_FLOAT, sizeof(basic_vertex), 0 * sizeof(float));
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_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();
}
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;
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

View File

@@ -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

View File

@@ -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();

View File

@@ -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;

View File

@@ -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 );
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; }
}
}

View File

@@ -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
};

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -350,12 +350,12 @@ shape_node::convert( TSubModel const *Submodel ) {
// NOTE: we set unlimited view range typical for terrain, because we don't expect to convert any other 3d models
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;

View File

@@ -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();

View File

@@ -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 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,9 +24,11 @@ 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_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 &);

View File

@@ -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>