I've been writing my own OpengGL renderer for my own engine, built atop of Irrlicht. I went with using CMesh/CAnimatedMesh/CSkinnedMesh implementations, using raw data out of then in order to construct OpenGL data that is fed to VBOs and consequentially bound to VAO.
Then I simply render desired object through glDrawArrays(), with appropriate VAO bound.
However, I get some bizarre stuff as a result - vertices data for mesh gets completely messed up, resulting in a jagged set of rendered triangles that only seemingly look like the original mesh from afar.
OpenGL EXT wrapper that I'm using is GLFW/GL3W, the one that comes with OpenGL SuperBible source code - I'm properly calling gl3wInit() on the first call to OpenGL functionality, so everything is initialized fine.
Some code to clear things out:
0) CGRenderSourceData class declaration (cpp file contains only constructor and destructor):
Code: Select all
class CGRenderSourceData
{
friend class IGMaterialRenderer;
#ifdef _BUILD_WITH_OPENGL_3_
friend class CGOpenGL3MaterialRenderer;
#endif // _BUILD_WITH_OPENGL_3_
#ifdef _BUILD_WITH_OPENGL_4_
friend class CGOpenGL4MaterialRenderer;
#endif // _BUILD_WITH_OPENGL_4_
#ifdef _BUILD_WITH_OPENGL_ES_2_
friend class CGOpenGLES2MaterialRenderer;
#endif // _BUILD_WITH_OPENGL_ES_2_
#ifdef _BUILD_WITH_OPENGL_ES_3_
friend class CGOpenGLES3MaterialRenderer;
#endif // _BUILD_WITH_OPENGL_ES_3_
#ifdef _BUILD_WITH_D3D_9_
friend class CGD3D9MaterialRenderer;
#endif // _BUILD_WITH_D3D_9_
#ifdef _BUILD_WITH_D3D_11_
friend class CGD3D11MaterialRenderer;
#endif // _BUILD_WITH_D3D_11_
private: /// CONSTRUCTION.
CGRenderSourceData();
~CGRenderSourceData();
// Disallow copy functionality.
CLASS_PROHIBIT_COPYING( CGRenderSourceData );
private: /// DATA
// Core for the render pipeline (is used as VAO in OpenGL, stream source for D3D, and so on).
DEFINE_CLASS_POINTER_RO( void, VertexHandleObject );
// Size of vertex element.
DEFINE_CLASS_PROPERTY_RO( size_t, i, VertexSize, 0 );
// Used as static/dynamic GPU-side data buffer for vertex data.
DEFINE_CLASS_POINTER_RO( void, VertexBuffer );
// Amount of vertices being stored.
DEFINE_CLASS_PROPERTY_RO( unsigned int, i, VertexCount, 0U );
// Shortcut to vertices, stored within mesh.
DEFINE_CLASS_POINTER_RO( void, Vertices );
// Size of index element.
DEFINE_CLASS_PROPERTY_RO( size_t, i, IndexSize, 0 );
// GAPI-specific index type.
DEFINE_CLASS_PROPERTY_RO( int, i, IndexType, 0 );
// Used as static GPU-side data buffer for vertex indexing during render.
DEFINE_CLASS_POINTER_RO( void, IndexBuffer );
// Amount of indices being stored.
DEFINE_CLASS_PROPERTY_RO( unsigned int, i, IndexCount, 0U );
// Shortcut to indices, stored within mesh.
DEFINE_CLASS_POINTER_RO( void, Indices );
};
Code: Select all
CGRenderSourceData* CGOpenGL4MaterialRenderer::createRenderSourceData( irr::video::E_VERTEX_TYPE _vertexType, void* _vertexData, unsigned int _verticesCount, irr::video::E_INDEX_TYPE _indexType, void* _indexData, unsigned int _indicesCount, bool _animated )
{
CGRenderSourceData* _newRenderSourceData = new CGRenderSourceData();
GLuint _vao = 0;
GLuint _buffers[ 2 ];
// Create VAO.
glGenVertexArrays( 1, &_vao );
_newRenderSourceData->m_pVertexHandleObject = reinterpret_cast< void* >( _vao ); // Store VAO as vertex handle object.
// Generate buffers for vertex and index data.
glGenBuffers( 2, _buffers );
_newRenderSourceData->m_pVertexBuffer = reinterpret_cast< void* >( _buffers[ 0 ] ); // Store vertex buffer.
_newRenderSourceData->m_pIndexBuffer = reinterpret_cast< void* >( _buffers[ 1 ] ); // Store index buffer.
resetRenderSourceData( _newRenderSourceData, _vertexType, _vertexData, _verticesCount, _indexType, _indexData, _indicesCount, _animated );
// Return resulting object.
return _newRenderSourceData;
}
// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void CGOpenGL4MaterialRenderer::resetRenderSourceData( CGRenderSourceData* _data, irr::video::E_VERTEX_TYPE _vertexType, void* _vertexData, unsigned int _verticesCount, irr::video::E_INDEX_TYPE _indexType, void* _indexData, unsigned int _indicesCount, bool _animated )
{
if( _data )
{
// Get actual data handles.
GLuint _vao = reinterpret_cast< GLuint >( _data->m_pVertexHandleObject );
GLuint _buffers[ 2 ];
_buffers[ 0 ] = reinterpret_cast< GLuint >( _data->m_pVertexBuffer );
_buffers[ 1 ] = reinterpret_cast< GLuint >( _data->m_pIndexBuffer );
// Bind VAO.
glBindVertexArray( _vao );
// Configure buffer for vertex data.
glBindBuffer( GL_ARRAY_BUFFER, _buffers[ 0 ] );
size_t _bufferSize = 0;
_data->m_iVertexSize = getVertexPitchFromType( _vertexType );
_bufferSize = _data->m_iVertexSize * _verticesCount;
unsigned char* _vertexBuffer = new unsigned char[ _bufferSize + 1 ];
memcpy( _vertexBuffer, _vertexData, _bufferSize );
*( _vertexBuffer + _bufferSize ) = '\0';
GLenum _vertexDataUsage = 0;
if( _animated )
{
_vertexDataUsage = GL_DYNAMIC_DRAW;
}
else
{
_vertexDataUsage = GL_STATIC_DRAW;
}
// Fill buffer with vertex data.
glBufferData( GL_ARRAY_BUFFER, _bufferSize, _vertexBuffer, _vertexDataUsage );
// Define attributes.
switch( _vertexType )
{
case irr::video::EVT_STANDARD:
{
// 1st attribute - position.
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( irr::video::S3DVertex ), (void*)offsetof( irr::video::S3DVertex, Pos ) );
// 2nd attribute - normal.
glEnableVertexAttribArray( 1 );
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof( irr::video::S3DVertex ), (void*)offsetof( irr::video::S3DVertex, Normal ) );
// 3rd attribute - color.
glEnableVertexAttribArray( 2 );
glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof( irr::video::S3DVertex ), (void*)offsetof( irr::video::S3DVertex, Color ) );
// 4th attribute - texcoords.
glEnableVertexAttribArray( 3 );
glVertexAttribPointer( 3, 2, GL_FLOAT, GL_FALSE, sizeof( irr::video::S3DVertex ), (void*)offsetof( irr::video::S3DVertex, TCoords ) );
} break;
/// other vertex types ..................
default:
{
ASSERT( false, "[ CGOpenGL4MaterialRenderer::resetRenderSourceData() ] Unknown vertex type." );
} break;
}
_data->m_pVertices = _vertexData;
glBindBuffer( GL_ARRAY_BUFFER, 0 );
NULLIFY_PTR( _vertexBuffer );
// Configure buffer for index data.
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _buffers[ 1 ] );
switch( _indexType )
{
case irr::video::EIT_16BIT:
{
_data->m_iIndexSize = sizeof( irr::u16 );
_data->m_iIndexType = GL_UNSIGNED_SHORT;
} break;
case irr::video::EIT_32BIT:
{
_data->m_iIndexSize = sizeof( irr::u32 );
_data->m_iIndexType = GL_UNSIGNED_INT;
} break;
default:
{
ASSERT( false, "[ CGOpenGL4MaterialRenderer::resetRenderSourceData() ] Unknown index type." );
_data->m_iIndexSize = 0;
} break;
}
_bufferSize = _data->m_iIndexSize * _indicesCount;
// Fill buffer with index data.
glBufferData( GL_ELEMENT_ARRAY_BUFFER, _bufferSize, _indexData, GL_STATIC_DRAW );
_data->m_pIndices = _indexData;
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
// Clear bound VAO points.
glBindVertexArray( 0 );
// Store vertices and indices counters.
_data->m_iVertexCount = _verticesCount;
_data->m_iIndexCount = _indicesCount;
}
}
Code: Select all
void CGOpenGL4MaterialRenderer::renderSourceData( const std::vector< CGRenderSourceData* > _data )
{
if( !_data.empty() )
{
GLenum _mode = GL_TRIANGLES;
gizmo::E_SCENE_OBJECT_RENDER_MODE _renderMode = gizmo::ESORM_TRIANGLES;
if( m_pActiveMaterialInstance )
{
// Determine corresponding OpenGL render mode.
_renderMode = m_pActiveMaterialInstance->getRenderMode();
switch ( _renderMode )
{
case gizmo::ESORM_POINTS:
{
_mode = GL_POINTS;
} break;
case gizmo::ESORM_LINE_STRIP:
{
_mode = GL_LINE_STRIP;
} break;
case gizmo::ESORM_LINE_LOOP:
{
_mode = GL_LINE_LOOP;
} break;
case gizmo::ESORM_LINES:
{
_mode = GL_LINES;
} break;
case gizmo::ESORM_TRIANGLE_STRIP:
{
_mode = GL_TRIANGLE_STRIP;
} break;
case gizmo::ESORM_TRIANGLE_FAN:
{
_mode = GL_TRIANGLE_FAN;
} break;
case gizmo::ESORM_TRIANGLES:
{
_mode = GL_TRIANGLES;
} break;
default:
{
ASSERT( false, "[ CGOpenGL4MaterialRenderer::renderSourceData() ] Unhandled render mode: %d.", ( int )( m_pActiveMaterialInstance->getRenderMode() ) );
_mode = GL_TRIANGLES;
} break;
}
// Setup back face render mode.
if( m_pActiveMaterialInstance->isRenderBackFace() )
{
glDisable( GL_CULL_FACE );
}
else
{
glEnable( GL_CULL_FACE );
glCullFace( GL_BACK );
}
// Bind textures.
for( unsigned int i = 0; i < gizmo::k_iMaxBoundMaterialTexturesCount; ++i )
{
int _constantID = m_pActiveMaterialInstance->getTextureLayer( i )->getConstantID();
GLuint _textureID = m_pActiveMaterialInstance->getTextureLayer( i )->getIntData()[ 0 ];
if( ( _constantID != -1 ) && ( _textureID != 0 ) )
{
glActiveTexture( GL_TEXTURE0 + i );
glBindTexture( GL_TEXTURE_2D, _textureID );
glUniform1i( _constantID, i );
}
}
}
glFrontFace( GL_CCW );
for( unsigned int i = 0, n = _data.size(); i < n; ++i )
{
GLuint _vao = reinterpret_cast< GLuint >( _data[ i ]->m_pVertexHandleObject );
// Bind VAO and index buffer.
glBindVertexArray( _vao );
// Issue draw command.
glDrawArrays( _mode, 0, _data[ i ]->m_iIndexCount );
irr::core::stringc _lastError = getLastError();
if( !_lastError.empty() )
{
LOG( "GLSL", bullhorn::log::ELL_ERROR, "[ CGOpenGL4MaterialRenderer::renderSourceData() ] Error occured: %s.", _lastError.c_str() );
}
}
// Clear state data.
glDisable( GL_CULL_FACE );
glBindVertexArray( 0 );
}
Code: Select all
void CGMeshSceneObject::initializeMesh( irr::scene::IAnimatedMesh* _mesh, bool _animated )
{
m_bAnimated = _animated;
gizmo::render::IGMaterialRenderer* _renderer = gizmo::macros::SCENE_MANAGER()->getDefaultMaterialRenderer();
if( _mesh && _renderer )
{
// Reset mesh normals.
gizmo::macros::IRRLICHT_SCENE_MANAGER()->getMeshManipulator()->recalculateNormals( _mesh, true, true );
// Create per-buffer render data.
for( unsigned int i = 0, n = _mesh->getMeshBufferCount(); i < n; ++i )
{
irr::scene::IMeshBuffer* _meshBuffer = _mesh->getMeshBuffer( i );
irr::video::E_VERTEX_TYPE _vertexType = _meshBuffer->getVertexType();
void* _vertexData = _meshBuffer->getVertices();
unsigned int _vertexCount = _meshBuffer->getVertexCount();
irr::video::E_INDEX_TYPE _indexType = _meshBuffer->getIndexType();
void* _indexData = _meshBuffer->getVertices();
unsigned int _indexCount = _meshBuffer->getIndexCount();
gizmo::render::CGRenderSourceData* _newRenderSourceData = _renderer->createRenderSourceData( _vertexType, _vertexData, _vertexCount, _indexType, _indexData, _indexCount, _animated );
m_oRenderSourceData.push_back( _newRenderSourceData );
}
m_pMesh = static_cast< irr::scene::ISkinnedMesh* >( _mesh );
}
}
VS
Code: Select all
#version 400 core
layout ( location = 0 ) in vec3 position;
layout ( location = 1 ) in vec3 normal;
layout ( location = 2 ) in vec4 color;
layout ( location = 3 ) in vec2 texcoord;
uniform mat4 mWorldTransform;
uniform mat4 mViewTransform;
uniform mat4 mProjTransform;
out vec2 tCoord;
out vec4 tColor;
void main( void )
{
gl_Position = mProjTransform * mViewTransform * mWorldTransform * vec4( position.xyz, 1 );
tCoord = texcoord;
tColor = color;
}
Code: Select all
#version 400 core
in vec2 tCoord;
in vec4 tColor;
uniform sampler2D sTexture0;
out vec4 fragColor;
void main( void )
{
fragColor = texture( sTexture0, tCoord );
}
Render mode: GL_TRIANGLES
Render mode: GL_TRIANGLE_STRIP
Render mode: GL_LINES
As you can see from the screenshots, each mode, more or less, has lots of triangles either messed up or completely absent.
Further clarification points:
1 ) Shaders I'm writing are compiled/linked/applied just fine.
2 ) Render process does not output any errors. That's why i'm completely clueless what's going on.
3 ) When glEnable( GL_DEPTH_TEST ) is applied before glDrawArrays() commands, the screen becomes completely black - note that original Irrlicht's COpenGLDriver is properly initialized on createDeviceEx(), with DepthBuffer bit enabled.
4 ) When glIsEnabled( GL_DEPTH_TEST ) is applied at the same time ( without calling glEnable() ), the result is GL_FALSE (presumably because of some 2D drawing the engine is performing using COpenGLDriver's functionality, like draw2DImage()).
5 ) Without glFrontFace ( GL_CCW ) call mesh is even more trashed, but I suspect that it's not the case here (but I'm wondering why it's GL_CW at the beginning - that's not OpenGL standard).
6 ) If glDrawElements() is called with index buffer ignored and mesh buffer's indices data supplied instead, the output is just 1 triangle that's not even amongst the triangles that form the original mesh buffer.
7 ) Mesh is loaded through IGSceneManager::getMesh() - no special magic is used for mesh creation, it's used for OpenGL data creation straight out-of-the-box.
8 ) Transformation matrices are properly applied before renderSourceData() method is called - actually, these screens are taked from the sample app that has a camera rotated around the mesh, and it works correct.
9 ) When any mesh is drawn, any GUI after this is not drawn - it's as it wasn't drawn at all. That's also a point of interest for me, but I suspect that it will fix itself once I re-write GUI to use GL 4+ renderer as well.
10 ) Mesh is not animated here, so data is set for VBO only once, upon creation - no further data copying is made after that (for now).
I suspect that it's probably an issue with the filling process of either VBO, or vertex attribute pointers - or even both of them.
I'm in desperate need of solving this issue, so any heads-up will be very much appreciated.
Thanks on advance.