Improve 3D viewer rendering performance

* Use GL vertex buffers and index buffers for 3D model rendering
* Use material or average vertex color for bounding boxes instead of red
* Reinstate bounding box rendering with GL vertex/index buffers
* Use compact vertex/index data representation
  - 8-bit normals
  - 8-bit colors
  - 16-bit or 32-bit indices, depending on model size

This should improve performance a bit on lower end GPUs with less memory
and bandwidth.

Fixes #4112
This commit is contained in:
Oleg Endo 2020-04-17 16:10:04 +00:00 committed by Ian McInerney
parent 0dfdc37ae7
commit ae6fbc9c60
4 changed files with 499 additions and 328 deletions

View File

@ -310,9 +310,13 @@ void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event )
glTranslatef( -model_center.x, -model_center.y, -model_center.z ); glTranslatef( -model_center.x, -model_center.y, -model_center.z );
m_ogl_3dmodel->BeginDrawMulti();
m_ogl_3dmodel->Draw_opaque(); m_ogl_3dmodel->Draw_opaque();
m_ogl_3dmodel->Draw_transparent(); m_ogl_3dmodel->Draw_transparent();
m_ogl_3dmodel->EndDrawMulti();
glPopMatrix(); glPopMatrix();
} }

View File

@ -1079,6 +1079,8 @@ void C3D_RENDER_OGL_LEGACY::render_solder_mask_layer( PCB_LAYER_ID aLayerID,
void C3D_RENDER_OGL_LEGACY::render_3D_models( bool aRenderTopOrBot, void C3D_RENDER_OGL_LEGACY::render_3D_models( bool aRenderTopOrBot,
bool aRenderTransparentOnly ) bool aRenderTransparentOnly )
{ {
C_OGL_3DMODEL::BeginDrawMulti();
// Go for all modules // Go for all modules
for( auto module : m_boardAdapter.GetBoard()->Modules() ) for( auto module : m_boardAdapter.GetBoard()->Modules() )
{ {
@ -1088,6 +1090,8 @@ void C3D_RENDER_OGL_LEGACY::render_3D_models( bool aRenderTopOrBot,
|| ( !aRenderTopOrBot && module->IsFlipped() ) ) || ( !aRenderTopOrBot && module->IsFlipped() ) )
render_3D_module( module, aRenderTransparentOnly ); render_3D_module( module, aRenderTransparentOnly );
} }
C_OGL_3DMODEL::EndDrawMulti();
} }
@ -1122,33 +1126,30 @@ void C3D_RENDER_OGL_LEGACY::render_3D_module( const MODULE* module,
modelunit_to_3d_units_factor ); modelunit_to_3d_units_factor );
// Get the list of model files for this model // Get the list of model files for this model
auto sM = module->Models().begin(); for (auto& sM : module->Models ())
auto eM = module->Models().end();
while( sM != eM )
{ {
if( !sM->m_Filename.empty() ) if( !sM.m_Filename.empty() )
{ {
// Check if the model is present in our cache map // Check if the model is present in our cache map
if( m_3dmodel_map.find( sM->m_Filename ) != m_3dmodel_map.end() ) auto cache_i = m_3dmodel_map.find( sM.m_Filename );
if( cache_i != m_3dmodel_map.end() )
{ {
// It is not present, try get it from cache if( const C_OGL_3DMODEL *modelPtr = cache_i->second )
const C_OGL_3DMODEL *modelPtr = m_3dmodel_map[ sM->m_Filename ];
if( modelPtr )
{ {
if( ( (!aRenderTransparentOnly) && modelPtr->Have_opaque() ) || if( ( (!aRenderTransparentOnly) && modelPtr->Have_opaque() ) ||
( aRenderTransparentOnly && modelPtr->Have_transparent() ) ) ( aRenderTransparentOnly && modelPtr->Have_transparent() ) )
{ {
glPushMatrix(); glPushMatrix();
glTranslatef( sM->m_Offset.x, sM->m_Offset.y, sM->m_Offset.z ); // FIXME: don't do this over and over again unless the
// values have changed. cache the matrix somewhere.
glRotatef( -sM->m_Rotation.z, 0.0f, 0.0f, 1.0f ); glm::mat4 mtx( 1 );
glRotatef( -sM->m_Rotation.y, 0.0f, 1.0f, 0.0f ); mtx = glm::translate( mtx, { sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z } );
glRotatef( -sM->m_Rotation.x, 1.0f, 0.0f, 0.0f ); mtx = glm::rotate( mtx, glm::radians( (float)-sM.m_Rotation.z ), { 0.0f, 0.0f, 1.0f } );
mtx = glm::rotate( mtx, glm::radians( (float)-sM.m_Rotation.y ), { 0.0f, 1.0f, 0.0f } );
glScalef( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ); mtx = glm::rotate( mtx, glm::radians( (float)-sM.m_Rotation.x ), { 1.0f, 0.0f, 0.0f } );
mtx = glm::scale( mtx, { sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z } );
glMultMatrixf( glm::value_ptr( mtx ) );
if( aRenderTransparentOnly ) if( aRenderTransparentOnly )
modelPtr->Draw_transparent(); modelPtr->Draw_transparent();
@ -1160,17 +1161,16 @@ void C3D_RENDER_OGL_LEGACY::render_3D_module( const MODULE* module,
glEnable( GL_BLEND ); glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glLineWidth( 1 );
modelPtr->Draw_bboxes();
glDisable( GL_LIGHTING ); glDisable( GL_LIGHTING );
glColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); glLineWidth( 1 );
modelPtr->Draw_bboxes();
glLineWidth( 4 ); glLineWidth( 4 );
modelPtr->Draw_bbox(); modelPtr->Draw_bbox();
glEnable( GL_LIGHTING ); glEnable( GL_LIGHTING );
glDisable( GL_BLEND );
} }
glPopMatrix(); glPopMatrix();
@ -1178,8 +1178,6 @@ void C3D_RENDER_OGL_LEGACY::render_3D_module( const MODULE* module,
} }
} }
} }
++sM;
} }
glPopMatrix(); glPopMatrix();

View File

@ -1,6 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2020 Oleg Endo <olegendo@gcc.gnu.org>
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt> * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
* *
@ -27,357 +28,461 @@
* @brief * @brief
*/ */
#include <GL/glew.h>
#include "c_ogl_3dmodel.h" #include "c_ogl_3dmodel.h"
#include "ogl_legacy_utils.h" #include "ogl_legacy_utils.h"
#include "../common_ogl/ogl_utils.h" #include "../common_ogl/ogl_utils.h"
#include "../3d_math.h" #include "../3d_math.h"
#include <wx/debug.h> #include <wx/debug.h>
#include <chrono>
const wxChar * C_OGL_3DMODEL::m_logTrace = wxT( "KI_TRACE_EDA_OGL_3DMODEL" );
void C_OGL_3DMODEL::MakeBbox( const CBBOX &aBox, unsigned int aIdxOffset,
VERTEX *aVtxOut, GLuint *aIdxOut,
const glm::vec4 &aColor )
{
aVtxOut[0].m_pos = { aBox.Min().x, aBox.Min().y, aBox.Min().z };
aVtxOut[1].m_pos = { aBox.Max().x, aBox.Min().y, aBox.Min().z };
aVtxOut[2].m_pos = { aBox.Max().x, aBox.Max().y, aBox.Min().z };
aVtxOut[3].m_pos = { aBox.Min().x, aBox.Max().y, aBox.Min().z };
aVtxOut[4].m_pos = { aBox.Min().x, aBox.Min().y, aBox.Max().z };
aVtxOut[5].m_pos = { aBox.Max().x, aBox.Min().y, aBox.Max().z };
aVtxOut[6].m_pos = { aBox.Max().x, aBox.Max().y, aBox.Max().z };
aVtxOut[7].m_pos = { aBox.Min().x, aBox.Max().y, aBox.Max().z };
for( unsigned int i = 0; i < 8; ++i )
aVtxOut[i].m_color = aVtxOut[i].m_cad_color = glm::clamp( aColor * 255.0f, 0.0f, 255.0f );
#define bbox_line( vtx_a, vtx_b )\
do { *aIdxOut++ = vtx_a + aIdxOffset; \
*aIdxOut++ = vtx_b + aIdxOffset; } while( 0 )
bbox_line( 0, 1 );
bbox_line( 1, 2 );
bbox_line( 2, 3 );
bbox_line( 3, 0 );
bbox_line( 4, 5 );
bbox_line( 5, 6 );
bbox_line( 6, 7 );
bbox_line( 7, 4 );
bbox_line( 0, 4 );
bbox_line( 1, 5 );
bbox_line( 2, 6 );
bbox_line( 3, 7 );
#undef bbox_line
}
C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel, C_OGL_3DMODEL::C_OGL_3DMODEL( const S3DMODEL &a3DModel,
MATERIAL_MODE aMaterialMode ) MATERIAL_MODE aMaterialMode )
{ {
m_ogl_idx_list_meshes = 0; wxLogTrace( m_logTrace, wxT( "C_OGL_3DMODEL::C_OGL_3DMODEL %u meshes %u materials" ),
m_ogl_idx_list_opaque = 0; static_cast<unsigned int>( a3DModel.m_MeshesSize ),
m_ogl_idx_list_transparent = 0; static_cast<unsigned int>( a3DModel.m_MaterialsSize ) );
m_nr_meshes = 0;
m_meshs_bbox = NULL; auto start_time = std::chrono::high_resolution_clock::now();
// Validate a3DModel pointers // Validate a3DModel pointers
wxASSERT( a3DModel.m_Materials != NULL ); wxASSERT( a3DModel.m_Materials != nullptr );
wxASSERT( a3DModel.m_Meshes != NULL ); wxASSERT( a3DModel.m_Meshes != nullptr );
wxASSERT( a3DModel.m_MaterialsSize > 0 ); wxASSERT( a3DModel.m_MaterialsSize > 0 );
wxASSERT( a3DModel.m_MeshesSize > 0 ); wxASSERT( a3DModel.m_MeshesSize > 0 );
if( (a3DModel.m_Materials != NULL) && (a3DModel.m_Meshes != NULL) && m_material_mode = aMaterialMode;
(a3DModel.m_MaterialsSize > 0) && (a3DModel.m_MeshesSize > 0) )
if( a3DModel.m_Materials == nullptr || a3DModel.m_Meshes == nullptr
|| a3DModel.m_MaterialsSize == 0 || a3DModel.m_MeshesSize == 0 )
return;
// create empty bbox for each mesh. it will be updated when the vertices
// are copied.
m_meshes_bbox.resize( a3DModel.m_MeshesSize );
// copy materials for later use during rendering.
m_materials.reserve( a3DModel.m_MaterialsSize );
for( unsigned int i = 0; i < a3DModel.m_MaterialsSize; ++i )
m_materials.emplace_back( a3DModel.m_Materials[i] );
// build temporary vertex and index buffers for bounding boxes.
// the first box is the outer box.
std::vector<VERTEX> bbox_tmp_vertices( ( m_meshes_bbox.size() + 1 ) * bbox_vtx_count );
std::vector<GLuint> bbox_tmp_indices( ( m_meshes_bbox.size() + 1 ) * bbox_idx_count );
// group all meshes by material.
// for each material create a combined vertex and index buffer.
// some models might have many sub-meshes. so iterate over the
// input meshes only once.
struct MESH_GROUP
{ {
m_nr_meshes = a3DModel.m_MeshesSize; std::vector<VERTEX> m_vertices;
std::vector<GLuint> m_indices;
};
m_meshs_bbox = new CBBOX[a3DModel.m_MeshesSize]; std::vector<MESH_GROUP> mesh_groups( m_materials.size() );
// Generate m_MeshesSize auxiliar lists to render the meshes
m_ogl_idx_list_meshes = glGenLists( a3DModel.m_MeshesSize );
// Render each mesh of the model
// /////////////////////////////////////////////////////////////////////
for( unsigned int mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i ) for( unsigned int mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i )
{ {
if( glIsList( m_ogl_idx_list_meshes + mesh_i ) ) const auto& mesh = a3DModel.m_Meshes[mesh_i];
// silently ignore meshes that have invalid material references
// or invalid geometry.
if( mesh.m_MaterialIdx >= m_materials.size()
|| mesh.m_Positions == nullptr
|| mesh.m_FaceIdx == nullptr
|| mesh.m_Normals == nullptr
|| mesh.m_FaceIdxSize == 0
|| mesh.m_VertexSize == 0 )
continue;
auto& mesh_group = mesh_groups[mesh.m_MaterialIdx];
auto& material = m_materials[mesh.m_MaterialIdx];
if( material.IsTransparent() )
m_have_transparent_meshes = true;
else
m_have_opaque_meshes = true;
const unsigned int vtx_offset = mesh_group.m_vertices.size();
mesh_group.m_vertices.resize( mesh_group.m_vertices.size() + mesh.m_VertexSize );
// copy vertex data and update the bounding box.
// use material color for mesh bounding box or some sort of average
// vertex color.
glm::vec3 avg_color = material.m_Diffuse;
for( unsigned int vtx_i = 0; vtx_i < mesh.m_VertexSize; ++vtx_i )
{ {
const SMESH &mesh = a3DModel.m_Meshes[mesh_i]; m_meshes_bbox[mesh_i].Union( mesh.m_Positions[vtx_i] );
// Validate the mesh pointers auto& vtx_out = mesh_group.m_vertices[vtx_offset + vtx_i];
wxASSERT( mesh.m_Positions != NULL );
wxASSERT( mesh.m_FaceIdx != NULL );
wxASSERT( mesh.m_Normals != NULL );
if( (mesh.m_Positions != NULL) && vtx_out.m_pos = mesh.m_Positions[vtx_i];
(mesh.m_Normals != NULL) && vtx_out.m_nrm = glm::clamp( glm::vec4( mesh.m_Normals[vtx_i], 1.0f ) * 127.0f,
(mesh.m_FaceIdx != NULL) && -127.0f, 127.0f );
(mesh.m_FaceIdxSize > 0) && (mesh.m_VertexSize > 0) )
vtx_out.m_tex_uv = mesh.m_Texcoords != nullptr
? mesh.m_Texcoords[vtx_i]
: glm::vec2 (0);
if( mesh.m_Color != nullptr )
{ {
SFVEC4F *pColorRGBA = NULL; avg_color = ( avg_color + mesh.m_Color[vtx_i] ) * 0.5f;
// Create the bbox for this mesh vtx_out.m_color =
// ///////////////////////////////////////////////////////// glm::clamp( glm::vec4( mesh.m_Color[vtx_i],
m_meshs_bbox[mesh_i].Reset(); 1 - material.m_Transparency ) * 255.0f,
0.0f, 255.0f );
for( unsigned int vertex_i = 0; vtx_out.m_cad_color =
vertex_i < mesh.m_VertexSize; glm::clamp( glm::vec4( MaterialDiffuseToColorCAD( mesh.m_Color[vtx_i] ),
++vertex_i ) 1 ) * 255.0f,
0.0f, 255.0f );
}
else
{ {
m_meshs_bbox[mesh_i].Union( mesh.m_Positions[vertex_i] ); // the mesh will be rendered with other meshes that might have
// vertex colors. thus, we can't enable/disable vertex colors
// for individual meshes during rendering.
// if there are no vertex colors, use material color instead.
vtx_out.m_color =
glm::clamp( glm::vec4( material.m_Diffuse,
1 - material.m_Transparency ) * 255.0f,
0.0f, 255.0f );
vtx_out.m_cad_color =
glm::clamp( glm::vec4 ( MaterialDiffuseToColorCAD( material.m_Diffuse ),
1 ) * 255.0f,
0.0f, 255.0f );
}
} }
// Make sure we start with client state disabled if( m_meshes_bbox[mesh_i].IsInitialized() )
// ///////////////////////////////////////////////////////// {
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // generate geometry for the bounding box
glDisableClientState( GL_COLOR_ARRAY ); MakeBbox( m_meshes_bbox[mesh_i], ( mesh_i + 1 ) * bbox_vtx_count,
&bbox_tmp_vertices[( mesh_i + 1 ) * bbox_vtx_count],
&bbox_tmp_indices[( mesh_i + 1 ) * bbox_idx_count],
{ avg_color, 1.0f } );
// bump the outer bounding box
m_model_bbox.Union( m_meshes_bbox[mesh_i] );
}
// Enable arrays client states // append indices of this mesh to the mesh group.
// ///////////////////////////////////////////////////////// const unsigned int idx_offset = mesh_group.m_indices.size();
unsigned int use_idx_count = mesh.m_FaceIdxSize;
if( use_idx_count % 3 != 0 )
{
wxLogTrace( m_logTrace, wxT( " index count %u not multiple of 3, truncating" ),
static_cast<unsigned int>( use_idx_count ) );
use_idx_count = ( use_idx_count / 3 ) * 3;
}
mesh_group.m_indices.resize( mesh_group.m_indices.size() + use_idx_count );
for( unsigned int idx_i = 0; idx_i < use_idx_count; ++idx_i )
{
if( mesh.m_FaceIdx[idx_i] >= mesh.m_VertexSize )
{
wxLogTrace( m_logTrace, wxT( " index %u out of range (%u)" ),
static_cast<unsigned int>( mesh.m_FaceIdx[idx_i] ),
static_cast<unsigned int>( mesh.m_VertexSize ) );
// FIXME: should skip this triangle
}
mesh_group.m_indices[idx_offset + idx_i] = mesh.m_FaceIdx[idx_i] + vtx_offset;
}
}
// generate geometry for the outer bounding box
if( m_model_bbox.IsInitialized() )
MakeBbox( m_model_bbox, 0, &bbox_tmp_vertices[0], &bbox_tmp_indices[0],
{ 0.0f, 1.0f, 0.0f, 1.0f } );
// create bounding box buffers
glGenBuffers( 1, &m_bbox_vertex_buffer );
glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof( VERTEX ) * bbox_tmp_vertices.size(),
bbox_tmp_vertices.data(), GL_STATIC_DRAW );
glGenBuffers( 1, &m_bbox_index_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );
if( bbox_tmp_vertices.size() <= std::numeric_limits<GLushort>::max() )
{
m_bbox_index_buffer_type = GL_UNSIGNED_SHORT;
auto u16buf = std::make_unique<GLushort[]>( bbox_tmp_indices.size() );
for( unsigned int i = 0; i < bbox_tmp_indices.size(); ++i )
u16buf[i] = static_cast<GLushort>( bbox_tmp_indices[i] );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( GLushort ) * bbox_tmp_indices.size(),
u16buf.get(), GL_STATIC_DRAW );
}
else
{
m_bbox_index_buffer_type = GL_UNSIGNED_INT;
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof( GLuint ) * bbox_tmp_indices.size(),
bbox_tmp_indices.data(), GL_STATIC_DRAW );
}
// merge the mesh group geometry data.
unsigned int total_vertex_count = 0;
unsigned int total_index_count = 0;
for( auto& mg : mesh_groups )
{
total_vertex_count += mg.m_vertices.size();
total_index_count += mg.m_indices.size();
}
wxLogTrace( m_logTrace, wxT( " total %u vertices, %u indices" ),
total_vertex_count, total_index_count );
glGenBuffers( 1, &m_vertex_buffer );
glBindBuffer( GL_ARRAY_BUFFER, m_vertex_buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof( VERTEX ) * total_vertex_count,
nullptr, GL_STATIC_DRAW );
unsigned int idx_size = 0;
if( total_vertex_count <= std::numeric_limits<GLushort>::max() )
{
m_index_buffer_type = GL_UNSIGNED_SHORT;
idx_size = sizeof( GLushort );
}
else
{
m_index_buffer_type = GL_UNSIGNED_INT;
idx_size = sizeof( GLuint );
}
// temporary index buffer which will contain either GLushort or GLuint
// type indices. allocate with a bit of meadow at the end.
auto tmp_idx = std::make_unique<GLuint[]>(
( idx_size * total_index_count + 8 ) / sizeof( GLuint ) );
unsigned int prev_vtx_count = 0;
unsigned int idx_offset = 0;
unsigned int vtx_offset = 0;
for( unsigned int mg_i = 0; mg_i < mesh_groups.size (); ++mg_i )
{
auto& mg = mesh_groups[mg_i];
auto& mat = m_materials[mg_i];
if( m_index_buffer_type == GL_UNSIGNED_SHORT )
{
auto idx_out = reinterpret_cast<GLushort*>(
reinterpret_cast<uintptr_t>( tmp_idx.get() ) + idx_offset );
for( auto idx : mg.m_indices )
*idx_out++ = static_cast<GLushort>( idx + prev_vtx_count );
}
else if( m_index_buffer_type == GL_UNSIGNED_INT )
{
auto idx_out = reinterpret_cast<GLuint*>(
reinterpret_cast<uintptr_t>( tmp_idx.get() ) + idx_offset );
for( auto idx : mg.m_indices )
*idx_out++ = static_cast<GLuint>( idx + prev_vtx_count );
}
glBufferSubData( GL_ARRAY_BUFFER,
vtx_offset,
mg.m_vertices.size() * sizeof( VERTEX ),
mg.m_vertices.data() );
mat.m_render_idx_buffer_offset = idx_offset;
mat.m_render_idx_count = mg.m_indices.size();
prev_vtx_count += mg.m_vertices.size();
idx_offset += mg.m_indices.size() * idx_size;
vtx_offset += mg.m_vertices.size() * sizeof( VERTEX );
}
glGenBuffers( 1, &m_index_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_index_buffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, idx_size * total_index_count,
tmp_idx.get(), GL_STATIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
auto end_time = std::chrono::high_resolution_clock::now();
wxLogTrace( m_logTrace, wxT( " loaded in %u ms\n" ),
(unsigned int)std::chrono::duration_cast<std::chrono::milliseconds> (
end_time - start_time).count() );
}
void C_OGL_3DMODEL::BeginDrawMulti()
{
glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY ); glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, mesh.m_Positions );
glNormalPointer( GL_FLOAT, 0, mesh.m_Normals );
if( mesh.m_Color != NULL )
{
glEnableClientState( GL_COLOR_ARRAY ); glEnableClientState( GL_COLOR_ARRAY );
float transparency = 0.0f;
if( mesh.m_MaterialIdx < a3DModel.m_MaterialsSize )
transparency = a3DModel.m_Materials[mesh.m_MaterialIdx].m_Transparency;
if( ( transparency > FLT_EPSILON )
&& ( aMaterialMode == MATERIAL_MODE::NORMAL ) )
{
// Create a new array of RGBA colors
pColorRGBA = new SFVEC4F[mesh.m_VertexSize];
// Copy RGB array and add the Alpha value
for( unsigned int i = 0; i < mesh.m_VertexSize; ++i )
pColorRGBA[i] = SFVEC4F( mesh.m_Color[i],
1.0f - transparency );
// Load an RGBA array
glColorPointer( 4, GL_FLOAT, 0, pColorRGBA );
}
else
{
switch( aMaterialMode )
{
case MATERIAL_MODE::NORMAL:
case MATERIAL_MODE::DIFFUSE_ONLY:
// load the original RGB color array
glColorPointer( 3, GL_FLOAT, 0, mesh.m_Color );
break;
case MATERIAL_MODE::CAD_MODE:
// Create a new array of RGBA colors
pColorRGBA = new SFVEC4F[mesh.m_VertexSize];
// Copy RGB array and add the Alpha value
for( unsigned int i = 0; i < mesh.m_VertexSize; ++i )
{
pColorRGBA[i] =
SFVEC4F( MaterialDiffuseToColorCAD( mesh.m_Color[i] ),
1.0f );
}
// Load an RGBA array
glColorPointer( 4, GL_FLOAT, 0, pColorRGBA );
break;
default:
break;
}
}
}
if( mesh.m_Texcoords != NULL )
{
glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer( 2, GL_FLOAT, 0, mesh.m_Texcoords );
}
// Compile the display list to store triangles
// /////////////////////////////////////////////////////////
glNewList( m_ogl_idx_list_meshes + mesh_i, GL_COMPILE );
// Set material properties
// /////////////////////////////////////////////////////////
if( mesh.m_Color != NULL )
{
// This enables the use of the Color Pointer information
glEnable( GL_COLOR_MATERIAL ); glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
} }
else
{
glDisable( GL_COLOR_MATERIAL );
}
if( mesh.m_MaterialIdx < a3DModel.m_MaterialsSize ) void C_OGL_3DMODEL::EndDrawMulti()
{
glDisable( GL_COLOR_MATERIAL );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
}
void C_OGL_3DMODEL::Draw( bool aTransparent ) const
{
glBindBuffer( GL_ARRAY_BUFFER, m_vertex_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_index_buffer );
glVertexPointer( 3, GL_FLOAT, sizeof( VERTEX ),
reinterpret_cast<const void*>( offsetof( VERTEX, m_pos ) ) );
glNormalPointer( GL_BYTE, sizeof( VERTEX ),
reinterpret_cast<const void*>( offsetof( VERTEX, m_nrm ) ) );
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( VERTEX ),
reinterpret_cast<const void*>(
m_material_mode == MATERIAL_MODE::CAD_MODE
? offsetof( VERTEX, m_cad_color )
: offsetof( VERTEX, m_color ) ) );
glTexCoordPointer( 2, GL_FLOAT, sizeof( VERTEX ),
reinterpret_cast<const void*>( offsetof( VERTEX, m_tex_uv ) ) );
// BeginDrawMulti();
for( auto& mat : m_materials )
{ {
switch( aMaterialMode ) if( mat.IsTransparent() != aTransparent )
continue;
switch( m_material_mode )
{ {
case MATERIAL_MODE::NORMAL: case MATERIAL_MODE::NORMAL:
OGL_SetMaterial( a3DModel.m_Materials[mesh.m_MaterialIdx] ); OGL_SetMaterial( mat );
break; break;
case MATERIAL_MODE::DIFFUSE_ONLY: case MATERIAL_MODE::DIFFUSE_ONLY:
OGL_SetDiffuseOnlyMaterial( OGL_SetDiffuseOnlyMaterial( mat.m_Diffuse );
a3DModel.m_Materials[mesh.m_MaterialIdx].m_Diffuse );
break; break;
case MATERIAL_MODE::CAD_MODE: case MATERIAL_MODE::CAD_MODE:
OGL_SetDiffuseOnlyMaterial( OGL_SetDiffuseOnlyMaterial( MaterialDiffuseToColorCAD( mat.m_Diffuse ) );
MaterialDiffuseToColorCAD(
a3DModel.m_Materials[mesh.m_MaterialIdx].m_Diffuse ) );
break; break;
default: default:
break; break;
} }
glDrawElements( GL_TRIANGLES, mat.m_render_idx_count, m_index_buffer_type,
reinterpret_cast<const void*>( mat.m_render_idx_buffer_offset ) );
} }
// Draw mesh // EndDrawMulti();
// /////////////////////////////////////////////////////////
glDrawElements( GL_TRIANGLES, mesh.m_FaceIdxSize,
GL_UNSIGNED_INT, mesh.m_FaceIdx );
glDisable( GL_COLOR_MATERIAL );
glEndList();
// Disable arrays client states
// /////////////////////////////////////////////////////////
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glDisableClientState( GL_VERTEX_ARRAY );
glFlush();
delete [] pColorRGBA;
}
}
}// for each mesh
m_ogl_idx_list_opaque = glGenLists( 1 );
// Check if the generated list is valid
if( glIsList( m_ogl_idx_list_opaque ) )
{
bool have_opaque_meshes = false;
bool have_transparent_meshes = false;
// Compile the model display list
glNewList( m_ogl_idx_list_opaque, GL_COMPILE );
// Render each mesh display list (opaque first)
// /////////////////////////////////////////////////////////////////
for( unsigned int mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i )
{
const SMESH &mesh = a3DModel.m_Meshes[mesh_i];
if( mesh.m_MaterialIdx < a3DModel.m_MaterialsSize )
{
const SMATERIAL &material = a3DModel.m_Materials[mesh.m_MaterialIdx];
if( material.m_Transparency == 0.0f )
{
have_opaque_meshes = true; // Flag that we have at least one opaque mesh
glCallList( m_ogl_idx_list_meshes + mesh_i );
}
else
{
have_transparent_meshes = true; // Flag that we found a transparent mesh
}
}
}
glEndList();
if( !have_opaque_meshes )
{
// If we dont have opaque meshes, we can free the list
glDeleteLists( m_ogl_idx_list_opaque, 1 );
m_ogl_idx_list_opaque = 0;
}
if( have_transparent_meshes )
{
m_ogl_idx_list_transparent = glGenLists( 1 );
// Check if the generated list is valid
if( glIsList( m_ogl_idx_list_transparent ) )
{
// Compile the model display list
glNewList( m_ogl_idx_list_transparent, GL_COMPILE );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Render each mesh display list
// /////////////////////////////////////////////////////////
for( unsigned mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i )
{
const SMESH &mesh = a3DModel.m_Meshes[mesh_i];
if( mesh.m_MaterialIdx < a3DModel.m_MaterialsSize )
{
const SMATERIAL &material = a3DModel.m_Materials[mesh.m_MaterialIdx];
// Render the transparent mesh if it have a transparency value
if( material.m_Transparency != 0.0f )
glCallList( m_ogl_idx_list_meshes + mesh_i );
}
}
glDisable( GL_BLEND );
glEndList();
}
else
{
m_ogl_idx_list_transparent = 0;
}
}
}
else
{
m_ogl_idx_list_opaque = 0;
}
// Create the main bbox
// /////////////////////////////////////////////////////////////////////
m_model_bbox.Reset();
for( unsigned int mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i )
m_model_bbox.Union( m_meshs_bbox[mesh_i] );
glFlush();
}
} }
void C_OGL_3DMODEL::Draw_opaque() const
{
if( glIsList( m_ogl_idx_list_opaque ) )
glCallList( m_ogl_idx_list_opaque );
}
void C_OGL_3DMODEL::Draw_transparent() const
{
if( glIsList( m_ogl_idx_list_transparent ) )
glCallList( m_ogl_idx_list_transparent );
}
C_OGL_3DMODEL::~C_OGL_3DMODEL() C_OGL_3DMODEL::~C_OGL_3DMODEL()
{ {
if( glIsList( m_ogl_idx_list_opaque ) ) glDeleteBuffers( 1, &m_vertex_buffer );
glDeleteLists( m_ogl_idx_list_opaque, 1 ); glDeleteBuffers( 1, &m_index_buffer );
glDeleteBuffers( 1, &m_bbox_vertex_buffer );
if( glIsList( m_ogl_idx_list_transparent ) ) glDeleteBuffers( 1, &m_bbox_index_buffer );
glDeleteLists( m_ogl_idx_list_transparent, 1 );
if( glIsList( m_ogl_idx_list_meshes ) )
glDeleteLists( m_ogl_idx_list_meshes, m_nr_meshes );
m_ogl_idx_list_meshes = 0;
m_ogl_idx_list_opaque = 0;
m_ogl_idx_list_transparent = 0;
delete[] m_meshs_bbox;
m_meshs_bbox = NULL;
} }
void C_OGL_3DMODEL::Draw_bbox() const void C_OGL_3DMODEL::Draw_bbox() const
{ {
OGL_draw_bbox( m_model_bbox ); glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );
glVertexPointer( 3, GL_FLOAT, sizeof( VERTEX ),
reinterpret_cast<const void*>( offsetof( VERTEX, m_pos ) ) );
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( VERTEX ),
reinterpret_cast<const void*>( offsetof( VERTEX, m_color ) ) );
glDrawElements( GL_LINES, bbox_idx_count, m_bbox_index_buffer_type,
reinterpret_cast<const void*>( 0 ) );
} }
void C_OGL_3DMODEL::Draw_bboxes() const void C_OGL_3DMODEL::Draw_bboxes() const
{ {
for( unsigned int mesh_i = 0; mesh_i < m_nr_meshes; ++mesh_i ) glBindBuffer( GL_ARRAY_BUFFER, m_bbox_vertex_buffer );
OGL_draw_bbox( m_meshs_bbox[mesh_i] ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_bbox_index_buffer );
glVertexPointer( 3, GL_FLOAT, sizeof( VERTEX ),
reinterpret_cast<const void*>( offsetof( VERTEX, m_pos ) ) );
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( VERTEX ),
reinterpret_cast<const void*>( offsetof( VERTEX, m_color ) ) );
unsigned int idx_size = m_bbox_index_buffer_type == GL_UNSIGNED_SHORT
? sizeof( GLushort ) : sizeof( GLuint );
glDrawElements( GL_LINES, bbox_idx_count * m_meshes_bbox.size(), m_bbox_index_buffer_type,
reinterpret_cast<const void*>( bbox_idx_count * idx_size ) );
} }
bool C_OGL_3DMODEL::Have_opaque() const
{
return glIsList( m_ogl_idx_list_opaque );
}
bool C_OGL_3DMODEL::Have_transparent() const
{
return glIsList( m_ogl_idx_list_transparent );
}

View File

@ -1,6 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2020 Oleg Endo <olegendo@gcc.gnu.org>
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt> * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
* *
@ -30,6 +31,7 @@
#ifndef _C_OGL_3DMODEL_H_ #ifndef _C_OGL_3DMODEL_H_
#define _C_OGL_3DMODEL_H_ #define _C_OGL_3DMODEL_H_
#include <vector>
#include <plugins/3dapi/c3dmodel.h> #include <plugins/3dapi/c3dmodel.h>
#include "../../common_ogl/openGL_includes.h" #include "../../common_ogl/openGL_includes.h"
#include "../3d_render_raytracing/shapes3D/cbbox.h" #include "../3d_render_raytracing/shapes3D/cbbox.h"
@ -51,22 +53,22 @@ public:
/** /**
* @brief Draw_opaque - render the model into the current context * @brief Draw_opaque - render the model into the current context
*/ */
void Draw_opaque() const; void Draw_opaque() const { Draw( false ); }
/** /**
* @brief Draw_transparent - render the model into the current context * @brief Draw_transparent - render the model into the current context
*/ */
void Draw_transparent() const; void Draw_transparent() const { Draw( true ); }
/** /**
* @brief Have_opaque - return true if have opaque meshs to render * @brief Have_opaque - return true if have opaque meshs to render
*/ */
bool Have_opaque() const; bool Have_opaque() const { return m_have_opaque_meshes; }
/** /**
* @brief Have_transparent - return true if have transparent meshs to render * @brief Have_transparent - return true if have transparent meshs to render
*/ */
bool Have_transparent() const; bool Have_transparent() const { return m_have_transparent_meshes; }
/** /**
* @brief Draw_bbox - draw main bounding box of the model * @brief Draw_bbox - draw main bounding box of the model
@ -84,14 +86,76 @@ public:
*/ */
const CBBOX &GetBBox() const { return m_model_bbox; } const CBBOX &GetBBox() const { return m_model_bbox; }
/**
* @brief BeginDrawMulti - set some basic render states before drawing multiple models
*/
static void BeginDrawMulti();
/**
* @brief EndDrawMulti - cleanup render states after drawing multiple models
*/
static void EndDrawMulti();
private: private:
GLuint m_ogl_idx_list_opaque; ///< display list for rendering opaque meshes static const wxChar *m_logTrace;
GLuint m_ogl_idx_list_transparent; ///< display list for rendering transparent meshes
GLuint m_ogl_idx_list_meshes; ///< display lists for all meshes. // the material mode that was used to generate the rendering data.
unsigned int m_nr_meshes; ///< number of meshes of this model // FIXME: this can be selected at run-time and does not require re-creation
// of the whole model objects.
MATERIAL_MODE m_material_mode;
CBBOX m_model_bbox; ///< global bounding box for this model CBBOX m_model_bbox; ///< global bounding box for this model
CBBOX *m_meshs_bbox; ///< individual bbox for each mesh std::vector<CBBOX> m_meshes_bbox; ///< individual bbox for each mesh
// unified vertex format for mesh rendering.
struct VERTEX
{
glm::vec3 m_pos;
glm::u8vec4 m_nrm; // only 3 components used
glm::u8vec4 m_color; // regular color
glm::u8vec4 m_cad_color; // "CAD" mode rendering color
glm::vec2 m_tex_uv;
};
// vertex buffer and index buffers that include all the individual meshes
// lumped together.
GLuint m_vertex_buffer = 0;
GLuint m_index_buffer = 0;
GLenum m_index_buffer_type = GL_INVALID_ENUM;
// internal material definition
// all meshes are grouped by material for rendering purposes.
struct MATERIAL : SMATERIAL
{
unsigned int m_render_idx_buffer_offset = 0;
unsigned int m_render_idx_count = 0;
MATERIAL( const SMATERIAL &aOther ) : SMATERIAL( aOther ) { }
bool IsTransparent() const { return m_Transparency > FLT_EPSILON; }
};
std::vector<MATERIAL> m_materials;
// a model can consist of transparent and opaque parts. remember which
// ones are present during initial buffer and data setup. use it later
// during rendering.
bool m_have_opaque_meshes = false;
bool m_have_transparent_meshes = false;
// vertex buffer and index buffer for the bounding boxes.
// the first box is always the outer box, followed by inner boxes (one for each mesh).
static constexpr unsigned int bbox_vtx_count = 8;
static constexpr unsigned int bbox_idx_count = 24;
GLuint m_bbox_vertex_buffer = 0;
GLuint m_bbox_index_buffer = 0;
GLenum m_bbox_index_buffer_type = GL_INVALID_ENUM;
static void MakeBbox( const CBBOX &aBox, unsigned int aIdxOffset,
VERTEX *aVtxOut, GLuint *aIdxOut,
const glm::vec4 &aColor );
void Draw( bool aTransparent ) const;
}; };
#endif // _C_OGL_3DMODEL_H_ #endif // _C_OGL_3DMODEL_H_