612 lines
22 KiB
C++
612 lines
22 KiB
C++
/*
|
|
* 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-2020 Mario Luzeiro <mrluzeiro@ua.pt>
|
|
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you may find one here:
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
/**
|
|
* @file 3d_model.cpp
|
|
*/
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <gal/opengl/kiglew.h> // Must be included first
|
|
|
|
#include "3d_model.h"
|
|
#include "../common_ogl/ogl_utils.h"
|
|
#include "../3d_math.h"
|
|
#include <utility>
|
|
#include <wx/debug.h>
|
|
#include <wx/log.h>
|
|
#include <chrono>
|
|
#include <memory>
|
|
|
|
|
|
/*
|
|
* Flag to enable connectivity profiling.
|
|
*
|
|
* @ingroup trace_env_vars
|
|
*/
|
|
const wxChar* MODEL_3D::m_logTrace = wxT( "KI_TRACE_EDA_OGL_3DMODEL" );
|
|
|
|
|
|
void MODEL_3D::MakeBbox( const BBOX_3D& 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
|
|
}
|
|
|
|
|
|
MODEL_3D::MODEL_3D( const S3DMODEL& a3DModel, MATERIAL_MODE aMaterialMode )
|
|
{
|
|
wxLogTrace( m_logTrace, wxT( "MODEL_3D::MODEL_3D %u meshes %u materials" ),
|
|
static_cast<unsigned int>( a3DModel.m_MeshesSize ),
|
|
static_cast<unsigned int>( a3DModel.m_MaterialsSize ) );
|
|
|
|
auto start_time = std::chrono::high_resolution_clock::now();
|
|
GLuint buffers[8];
|
|
|
|
/**
|
|
* WARNING: Horrible hack here!
|
|
* Somehow, buffer values are being shared between pcbnew and the 3d viewer, which then frees
|
|
* the buffer, resulting in errors in pcbnew. To resolve this temporarily, we generate
|
|
* extra buffers in 3dviewer and use the higher numbers. These are freed on close.
|
|
* todo: Correctly separate the OpenGL contexts to prevent overlapping buffer vals
|
|
*/
|
|
glGenBuffers( 6, buffers );
|
|
m_bbox_vertex_buffer = buffers[2];
|
|
m_bbox_index_buffer = buffers[3];
|
|
m_vertex_buffer = buffers[4];
|
|
m_index_buffer = buffers[5];
|
|
|
|
// Validate a3DModel pointers
|
|
wxASSERT( a3DModel.m_Materials != nullptr );
|
|
wxASSERT( a3DModel.m_Meshes != nullptr );
|
|
wxASSERT( a3DModel.m_MaterialsSize > 0 );
|
|
wxASSERT( a3DModel.m_MeshesSize > 0 );
|
|
|
|
m_materialMode = aMaterialMode;
|
|
|
|
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
|
|
{
|
|
std::vector<VERTEX> m_vertices;
|
|
std::vector<GLuint> m_indices;
|
|
};
|
|
|
|
std::vector<MESH_GROUP> mesh_groups( m_materials.size() );
|
|
|
|
for( unsigned int mesh_i = 0; mesh_i < a3DModel.m_MeshesSize; ++mesh_i )
|
|
{
|
|
const SMESH& 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;
|
|
}
|
|
|
|
MESH_GROUP& mesh_group = mesh_groups[mesh.m_MaterialIdx];
|
|
MATERIAL& material = m_materials[mesh.m_MaterialIdx];
|
|
|
|
if( material.IsTransparent() && m_materialMode != MATERIAL_MODE::DIFFUSE_ONLY )
|
|
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;
|
|
|
|
BBOX_3D &mesh_bbox = m_meshes_bbox[mesh_i];
|
|
|
|
for( unsigned int vtx_i = 0; vtx_i < mesh.m_VertexSize; ++vtx_i )
|
|
{
|
|
mesh_bbox.Union( mesh.m_Positions[vtx_i] );
|
|
|
|
VERTEX& vtx_out = mesh_group.m_vertices[vtx_offset + vtx_i];
|
|
|
|
vtx_out.m_pos = mesh.m_Positions[vtx_i];
|
|
vtx_out.m_nrm = glm::clamp( glm::vec4( mesh.m_Normals[vtx_i], 1.0f ) * 127.0f,
|
|
-127.0f, 127.0f );
|
|
|
|
vtx_out.m_tex_uv = mesh.m_Texcoords != nullptr ? mesh.m_Texcoords[vtx_i]
|
|
: glm::vec2 (0);
|
|
|
|
if( mesh.m_Color != nullptr )
|
|
{
|
|
avg_color = ( avg_color + mesh.m_Color[vtx_i] ) * 0.5f;
|
|
|
|
vtx_out.m_color =
|
|
glm::clamp( glm::vec4( mesh.m_Color[vtx_i],
|
|
1 - material.m_Transparency ) * 255.0f,
|
|
0.0f, 255.0f );
|
|
|
|
vtx_out.m_cad_color =
|
|
glm::clamp( glm::vec4( MaterialDiffuseToColorCAD( mesh.m_Color[vtx_i] ),
|
|
1 ) * 255.0f, 0.0f, 255.0f );
|
|
}
|
|
else
|
|
{
|
|
// 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 );
|
|
}
|
|
}
|
|
|
|
if( mesh_bbox.IsInitialized() )
|
|
{
|
|
// generate geometry for the bounding box
|
|
MakeBbox( mesh_bbox, ( 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( mesh_bbox );
|
|
|
|
// add to the material group
|
|
material.m_bbox.Union( mesh_bbox );
|
|
}
|
|
|
|
|
|
// 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 );
|
|
|
|
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( const MESH_GROUP& 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 );
|
|
|
|
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 )
|
|
{
|
|
MESH_GROUP& mg = mesh_groups[mg_i];
|
|
MATERIAL& mat = m_materials[mg_i];
|
|
uintptr_t tmp_idx_ptr = reinterpret_cast<uintptr_t>( tmp_idx.get() );
|
|
|
|
if( m_index_buffer_type == GL_UNSIGNED_SHORT )
|
|
{
|
|
GLushort* idx_out = reinterpret_cast<GLushort*>( tmp_idx_ptr + idx_offset );
|
|
|
|
for( GLuint idx : mg.m_indices )
|
|
*idx_out++ = static_cast<GLushort>( idx + prev_vtx_count );
|
|
}
|
|
else if( m_index_buffer_type == GL_UNSIGNED_INT )
|
|
{
|
|
GLuint* idx_out = reinterpret_cast<GLuint*>( tmp_idx_ptr + idx_offset );
|
|
|
|
for( GLuint 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 );
|
|
}
|
|
|
|
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 MODEL_3D::BeginDrawMulti( bool aUseColorInformation )
|
|
{
|
|
glEnableClientState( GL_VERTEX_ARRAY );
|
|
glEnableClientState( GL_NORMAL_ARRAY );
|
|
|
|
if( aUseColorInformation )
|
|
{
|
|
glEnableClientState( GL_COLOR_ARRAY );
|
|
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
glEnable( GL_COLOR_MATERIAL );
|
|
}
|
|
|
|
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
|
|
}
|
|
|
|
|
|
void MODEL_3D::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 MODEL_3D::Draw( bool aTransparent, float aOpacity, bool aUseSelectedMaterial,
|
|
const SFVEC3F& aSelectionColor,
|
|
const glm::mat4 *aModelWorldMatrix,
|
|
const SFVEC3F *aCameraWorldPos ) const
|
|
{
|
|
if( aOpacity <= FLT_EPSILON )
|
|
return;
|
|
|
|
if( !glBindBuffer )
|
|
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw" );
|
|
|
|
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_materialMode == 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 ) ) );
|
|
|
|
const SFVEC4F param = SFVEC4F( 1.0f, 1.0f, 1.0f, aOpacity );
|
|
|
|
glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (const float*)¶m.x );
|
|
|
|
std::vector<const MODEL_3D::MATERIAL *> materialsToRender;
|
|
|
|
materialsToRender.reserve( m_materials.size() );
|
|
|
|
if( aModelWorldMatrix && aCameraWorldPos )
|
|
{
|
|
// Sort Material groups
|
|
|
|
std::vector<std::pair<const MODEL_3D::MATERIAL*, float>> materialsSorted;
|
|
|
|
// Calculate the distance to the camera for each material group
|
|
for( const MODEL_3D::MATERIAL& mat : m_materials )
|
|
{
|
|
if( mat.m_render_idx_count == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( ( mat.IsTransparent() != aTransparent )
|
|
&& ( aOpacity >= 1.0f )
|
|
&& m_materialMode != MATERIAL_MODE::DIFFUSE_ONLY )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const BBOX_3D& bBox = mat.m_bbox;
|
|
const SFVEC3F& bBoxCenter = bBox.GetCenter();
|
|
const SFVEC3F bBoxWorld = *aModelWorldMatrix * glm::vec4( bBoxCenter, 1.0f );
|
|
|
|
const float distanceToCamera = glm::length( *aCameraWorldPos - bBoxWorld );
|
|
|
|
materialsSorted.emplace_back( &mat, distanceToCamera );
|
|
}
|
|
|
|
// Sort from back to front
|
|
std::sort( materialsSorted.begin(), materialsSorted.end(),
|
|
[&]( std::pair<const MODEL_3D::MATERIAL*, float>& a,
|
|
std::pair<const MODEL_3D::MATERIAL*, float>& b ) {
|
|
// If A is inside B, then A is rendered first
|
|
if( b.first->m_bbox.Inside( a.first->m_bbox ) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if( a.first->m_bbox.Inside( b.first->m_bbox ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return a.second > b.second;
|
|
} );
|
|
|
|
for( const std::pair<const MODEL_3D::MATERIAL*, float>& mat : materialsSorted )
|
|
{
|
|
materialsToRender.push_back( mat.first );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( const MODEL_3D::MATERIAL& mat : m_materials )
|
|
{
|
|
// There is at least one default material created in case a mesh has no declared materials.
|
|
// Most meshes have a material, so usually the first material will have nothing to render and is skip.
|
|
// See S3D::GetModel for more details.
|
|
if( mat.m_render_idx_count == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( ( mat.IsTransparent() != aTransparent )
|
|
&& ( aOpacity >= 1.0f )
|
|
&& m_materialMode != MATERIAL_MODE::DIFFUSE_ONLY )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
materialsToRender.push_back( &mat );
|
|
}
|
|
}
|
|
|
|
for( const MODEL_3D::MATERIAL* mat : materialsToRender )
|
|
{
|
|
switch( m_materialMode )
|
|
{
|
|
case MATERIAL_MODE::NORMAL:
|
|
OglSetMaterial( *mat, aOpacity, aUseSelectedMaterial, aSelectionColor );
|
|
break;
|
|
|
|
case MATERIAL_MODE::DIFFUSE_ONLY:
|
|
OglSetDiffuseMaterial( mat->m_Diffuse, aOpacity, aUseSelectedMaterial, aSelectionColor );
|
|
break;
|
|
|
|
case MATERIAL_MODE::CAD_MODE:
|
|
OglSetDiffuseMaterial( MaterialDiffuseToColorCAD( mat->m_Diffuse ), aOpacity,
|
|
aUseSelectedMaterial, aSelectionColor );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
glDrawElements( GL_TRIANGLES, mat->m_render_idx_count, m_index_buffer_type,
|
|
reinterpret_cast<const void*>(
|
|
static_cast<uintptr_t>( mat->m_render_idx_buffer_offset ) ) );
|
|
}
|
|
}
|
|
|
|
|
|
MODEL_3D::~MODEL_3D()
|
|
{
|
|
if( glDeleteBuffers )
|
|
{
|
|
glDeleteBuffers( 1, &m_vertex_buffer );
|
|
glDeleteBuffers( 1, &m_index_buffer );
|
|
glDeleteBuffers( 1, &m_bbox_vertex_buffer );
|
|
glDeleteBuffers( 1, &m_bbox_index_buffer );
|
|
}
|
|
}
|
|
|
|
|
|
void MODEL_3D::DrawBbox() const
|
|
{
|
|
if( !glBindBuffer )
|
|
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw 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 MODEL_3D::DrawBboxes() const
|
|
{
|
|
if( !glBindBuffer )
|
|
throw std::runtime_error( "The OpenGL context no longer exists: unable to draw bboxes" );
|
|
|
|
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 ) ) );
|
|
|
|
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*>(
|
|
static_cast<uintptr_t>( bbox_idx_count * idx_size ) ) );
|
|
}
|
|
|