mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 10:13:19 +02:00
The use of printf, wxLogDebug, and std::err/std::out causes excessive debugging output which makes finding specific debugging messages more difficult than it needs to be. There is still some debugging output in test code that really needs to be moved into a unit test. Add debugging output section to the coding policy regarding debugging output.
1548 lines
65 KiB
C++
1548 lines
65 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
|
|
* Copyright (C) 1992-2016 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 c3d_render_createscene.cpp
|
|
* @brief
|
|
*/
|
|
|
|
|
|
#include "c3d_render_raytracing.h"
|
|
#include "shapes3D/cplane.h"
|
|
#include "shapes3D/croundseg.h"
|
|
#include "shapes3D/clayeritem.h"
|
|
#include "shapes3D/ccylinder.h"
|
|
#include "shapes3D/ctriangle.h"
|
|
#include "shapes2D/citemlayercsg2d.h"
|
|
#include "shapes2D/cring2d.h"
|
|
#include "shapes2D/cpolygon2d.h"
|
|
#include "shapes2D/cfilledcircle2d.h"
|
|
#include "accelerators/cbvh_pbrt.h"
|
|
#include "3d_fastmath.h"
|
|
#include "3d_math.h"
|
|
|
|
#include <class_board.h>
|
|
#include <class_module.h>
|
|
|
|
#include <base_units.h>
|
|
#include <profile.h> // To use GetRunningMicroSecs or another profiling utility
|
|
|
|
/**
|
|
* Scale convertion from 3d model units to pcb units
|
|
*/
|
|
#define UNITS3D_TO_UNITSPCB (IU_PER_MM)
|
|
|
|
void C3D_RENDER_RAYTRACING::setupMaterials()
|
|
{
|
|
double mmTo3Dunits = IU_PER_MM * m_boardAdapter.BiuTo3Dunits();
|
|
|
|
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
|
|
{
|
|
m_board_normal_perturbator = CBOARDNORMAL( 0.5f * mmTo3Dunits );
|
|
|
|
m_copper_normal_perturbator = CCOPPERNORMAL( 4.0f * mmTo3Dunits,
|
|
&m_board_normal_perturbator );
|
|
|
|
m_solder_mask_normal_perturbator = CSOLDERMASKNORMAL( &m_board_normal_perturbator );
|
|
|
|
m_plastic_normal_perturbator = CPLASTICNORMAL( 0.15f * mmTo3Dunits );
|
|
|
|
m_plastic_shine_normal_perturbator = CPLASTICSHINENORMAL( 1.0f * mmTo3Dunits );
|
|
|
|
m_brushed_metal_normal_perturbator = CMETALBRUSHEDNORMAL( 1.0f * mmTo3Dunits );
|
|
}
|
|
|
|
// http://devernay.free.fr/cours/opengl/materials.html
|
|
|
|
// Copper
|
|
m_materials.m_Copper = CBLINN_PHONG_MATERIAL(
|
|
ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_CopperColor ) *
|
|
(SFVEC3F)(0.18f), // ambient
|
|
SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
|
|
glm::clamp( ((SFVEC3F)(1.0f) -
|
|
ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_CopperColor ) ),
|
|
SFVEC3F( 0.0f ),
|
|
SFVEC3F( 0.35f ) ), // specular
|
|
0.4f * 128.0f, // shiness
|
|
0.0f, // transparency
|
|
0.0f );
|
|
|
|
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
|
|
m_materials.m_Copper.SetNormalPerturbator( &m_copper_normal_perturbator );
|
|
|
|
m_materials.m_Paste = CBLINN_PHONG_MATERIAL(
|
|
ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_SolderPasteColor ) *
|
|
ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_SolderPasteColor ), // ambient
|
|
SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
|
|
ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_SolderPasteColor ) *
|
|
ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_SolderPasteColor ), // specular
|
|
0.10f * 128.0f, // shiness
|
|
0.0f, // transparency
|
|
0.0f );
|
|
|
|
m_materials.m_SilkS = CBLINN_PHONG_MATERIAL( ConvertSRGBToLinear( SFVEC3F( 0.11f ) ), // ambient
|
|
SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
|
|
glm::clamp(
|
|
( ( SFVEC3F )( 1.0f )
|
|
- ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SilkScreenColorTop ) ),
|
|
SFVEC3F( 0.0f ),
|
|
SFVEC3F( 0.10f ) ), // specular
|
|
0.078125f * 128.0f, // shiness
|
|
0.0f, // transparency
|
|
0.0f );
|
|
|
|
const float solderMaskTop_gray =
|
|
( m_boardAdapter.m_SolderMaskColorTop.r + m_boardAdapter.m_SolderMaskColorTop.g
|
|
+ m_boardAdapter.m_SolderMaskColorTop.b )
|
|
/ 3.0f;
|
|
|
|
const float solderMaskTop_transparency = solderMaskTop_gray * 0.40f + 0.005f;
|
|
|
|
m_materials.m_SolderMask = CBLINN_PHONG_MATERIAL(
|
|
ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorTop ) * 0.10f, // ambient
|
|
SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
|
|
glm::clamp(
|
|
( ( SFVEC3F )( 1.0f )
|
|
- ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorTop ) ),
|
|
SFVEC3F( 0.0f ),
|
|
SFVEC3F( solderMaskTop_gray * 2.0f ) ), // specular
|
|
0.85f * 128.0f, // shiness
|
|
solderMaskTop_transparency, // transparency
|
|
0.16f ); // reflection
|
|
|
|
const float solderMaskBot_gray =
|
|
( m_boardAdapter.m_SolderMaskColorBot.r + m_boardAdapter.m_SolderMaskColorBot.g
|
|
+ m_boardAdapter.m_SolderMaskColorBot.b )
|
|
/ 3.0f;
|
|
|
|
const float solderMaskBot_transparency = solderMaskBot_gray * 0.40f + 0.005f;
|
|
|
|
m_materials.m_SolderMask = CBLINN_PHONG_MATERIAL(
|
|
ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorBot ) * 0.10f, // ambient
|
|
SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
|
|
glm::clamp(
|
|
( ( SFVEC3F )( 1.0f )
|
|
- ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorBot ) ),
|
|
SFVEC3F( 0.0f ),
|
|
SFVEC3F( solderMaskBot_gray * 2.0f ) ), // specular
|
|
0.85f * 128.0f, // shiness
|
|
solderMaskBot_transparency, // transparency
|
|
0.16f );
|
|
|
|
m_materials.m_SolderMask.SetCastShadows( true );
|
|
m_materials.m_SolderMask.SetNrRefractionsSamples( 1 );
|
|
m_materials.m_SolderMask.SetNrReflectionsSamples( 2 );
|
|
|
|
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
|
|
m_materials.m_SolderMask.SetNormalPerturbator( &m_solder_mask_normal_perturbator );
|
|
|
|
m_materials.m_EpoxyBoard = CBLINN_PHONG_MATERIAL(
|
|
ConvertSRGBToLinear( SFVEC3F( 16.0f / 255.0f,
|
|
14.0f / 255.0f,
|
|
10.0f / 255.0f ) ), // ambient
|
|
SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
|
|
ConvertSRGBToLinear( SFVEC3F( 10.0f / 255.0f,
|
|
8.0f / 255.0f,
|
|
10.0f / 255.0f ) ), // specular
|
|
0.1f * 128.0f, // shiness
|
|
0.10f, // transparency
|
|
0.0f ); // reflection
|
|
|
|
m_materials.m_EpoxyBoard.SetAbsorvance( 10.0f );
|
|
m_materials.m_EpoxyBoard.SetNrRefractionsSamples( 3 );
|
|
|
|
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
|
|
m_materials.m_EpoxyBoard.SetNormalPerturbator( &m_board_normal_perturbator );
|
|
|
|
SFVEC3F bgTop = ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_BgColorTop );
|
|
//SFVEC3F bgBot = (SFVEC3F)m_boardAdapter.m_BgColorBot;
|
|
|
|
m_materials.m_Floor = CBLINN_PHONG_MATERIAL(
|
|
bgTop * 0.125f, // ambient
|
|
SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
|
|
(SFVEC3F(1.0f) - bgTop) / 3.0f, // specular
|
|
0.10f * 128.0f, // shiness
|
|
0.0f, // transparency
|
|
0.50f ); // reflection
|
|
m_materials.m_Floor.SetCastShadows( false );
|
|
}
|
|
|
|
|
|
|
|
/** Function create_3d_object_from
|
|
* @brief Creates on or more 3D objects form a 2D object and Z positions. It try
|
|
* optimize some types of objects that will be faster to trace than the
|
|
* CLAYERITEM object.
|
|
* @param aObject2D
|
|
* @param aZMin
|
|
* @param aZMax
|
|
*/
|
|
void C3D_RENDER_RAYTRACING::create_3d_object_from( CCONTAINER& aDstContainer,
|
|
const COBJECT2D* aObject2D, float aZMin, float aZMax, const CMATERIAL* aMaterial,
|
|
const SFVEC3F& aObjColor )
|
|
{
|
|
switch( aObject2D->GetObjectType() )
|
|
{
|
|
case OBJECT2D_TYPE::DUMMYBLOCK:
|
|
{
|
|
m_stats_converted_dummy_to_plane++;
|
|
#if 1
|
|
CXYPLANE* objPtr;
|
|
objPtr = new CXYPLANE( CBBOX(
|
|
SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMin ),
|
|
SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMin ) ) );
|
|
objPtr->SetMaterial( aMaterial );
|
|
objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
|
|
aDstContainer.Add( objPtr );
|
|
|
|
objPtr = new CXYPLANE( CBBOX(
|
|
SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMax ),
|
|
SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMax ) ) );
|
|
objPtr->SetMaterial( aMaterial );
|
|
objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
|
|
aDstContainer.Add( objPtr );
|
|
#else
|
|
objPtr = new CDUMMYBLOCK( CBBOX(
|
|
SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMin ),
|
|
SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMax ) ) );
|
|
objPtr->SetMaterial( aMaterial );
|
|
aDstContainer.Add( objPtr );
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case OBJECT2D_TYPE::ROUNDSEG:
|
|
{
|
|
m_stats_converted_roundsegment2d_to_roundsegment++;
|
|
|
|
const CROUNDSEGMENT2D* aRoundSeg2D = static_cast<const CROUNDSEGMENT2D*>( aObject2D );
|
|
CROUNDSEG* objPtr = new CROUNDSEG( *aRoundSeg2D, aZMin, aZMax );
|
|
objPtr->SetMaterial( aMaterial );
|
|
objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
|
|
aDstContainer.Add( objPtr );
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
{
|
|
CLAYERITEM* objPtr = new CLAYERITEM( aObject2D, aZMin, aZMax );
|
|
objPtr->SetMaterial( aMaterial );
|
|
objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
|
|
aDstContainer.Add( objPtr );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void C3D_RENDER_RAYTRACING::reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
|
|
{
|
|
m_reloadRequested = false;
|
|
|
|
m_model_materials.clear();
|
|
|
|
COBJECT2D_STATS::Instance().ResetStats();
|
|
COBJECT3D_STATS::Instance().ResetStats();
|
|
|
|
unsigned stats_startReloadTime = GetRunningMicroSecs();
|
|
|
|
m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
|
|
|
|
SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter3DU();
|
|
m_camera.SetBoardLookAtPos( camera_pos );
|
|
|
|
m_object_container.Clear();
|
|
m_containerWithObjectsToDelete.Clear();
|
|
|
|
setupMaterials();
|
|
|
|
// Create and add the outline board
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
|
|
delete m_outlineBoard2dObjects;
|
|
|
|
m_outlineBoard2dObjects = new CCONTAINER2D;
|
|
|
|
const int outlineCount = m_boardAdapter.GetBoardPoly().OutlineCount();
|
|
|
|
if( outlineCount > 0 )
|
|
{
|
|
float divFactor = 0.0f;
|
|
|
|
if( m_boardAdapter.GetStats_Nr_Vias() )
|
|
divFactor = m_boardAdapter.GetStats_Med_Via_Hole_Diameter3DU() * 18.0f;
|
|
else
|
|
if( m_boardAdapter.GetStats_Nr_Holes() )
|
|
divFactor = m_boardAdapter.GetStats_Med_Hole_Diameter3DU() * 8.0f;
|
|
|
|
SHAPE_POLY_SET boardPolyCopy = m_boardAdapter.GetBoardPoly();
|
|
boardPolyCopy.Fracture( SHAPE_POLY_SET::PM_FAST );
|
|
|
|
for( int iOutlinePolyIdx = 0; iOutlinePolyIdx < outlineCount; iOutlinePolyIdx++ )
|
|
{
|
|
Convert_path_polygon_to_polygon_blocks_and_dummy_blocks(
|
|
boardPolyCopy,
|
|
*m_outlineBoard2dObjects,
|
|
m_boardAdapter.BiuTo3Dunits(),
|
|
divFactor,
|
|
*dynamic_cast<const BOARD_ITEM*>( m_boardAdapter.GetBoard() ),
|
|
iOutlinePolyIdx );
|
|
}
|
|
|
|
if( m_boardAdapter.GetFlag( FL_SHOW_BOARD_BODY ) )
|
|
{
|
|
const LIST_OBJECT2D &listObjects = m_outlineBoard2dObjects->GetList();
|
|
|
|
for( LIST_OBJECT2D::const_iterator object2d_iterator = listObjects.begin();
|
|
object2d_iterator != listObjects.end();
|
|
++object2d_iterator )
|
|
{
|
|
const COBJECT2D *object2d_A = static_cast<const COBJECT2D *>(*object2d_iterator);
|
|
|
|
std::vector<const COBJECT2D *> *object2d_B = new std::vector<const COBJECT2D *>();
|
|
|
|
// Check if there are any THT that intersects this outline object part
|
|
if( !m_boardAdapter.GetThroughHole_Outer().GetList().empty() )
|
|
{
|
|
|
|
CONST_LIST_OBJECT2D intersectionList;
|
|
m_boardAdapter.GetThroughHole_Outer().GetListObjectsIntersects(
|
|
object2d_A->GetBBox(),
|
|
intersectionList );
|
|
|
|
if( !intersectionList.empty() )
|
|
{
|
|
for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin();
|
|
hole != intersectionList.end();
|
|
++hole )
|
|
{
|
|
const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*hole);
|
|
|
|
if( object2d_A->Intersects( hole2d->GetBBox() ) )
|
|
//if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) )
|
|
object2d_B->push_back( hole2d );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( object2d_B->empty() )
|
|
{
|
|
delete object2d_B;
|
|
object2d_B = CSGITEM_EMPTY;
|
|
}
|
|
|
|
if( object2d_B == CSGITEM_EMPTY )
|
|
{
|
|
#if 0
|
|
create_3d_object_from( m_object_container, object2d_A,
|
|
m_boardAdapter.GetLayerBottomZpos3DU( F_Cu ),
|
|
m_boardAdapter.GetLayerBottomZpos3DU( B_Cu ),
|
|
&m_materials.m_EpoxyBoard,
|
|
g_epoxyColor );
|
|
#else
|
|
CLAYERITEM *objPtr = new CLAYERITEM( object2d_A,
|
|
m_boardAdapter.GetLayerBottomZpos3DU( F_Cu ),
|
|
m_boardAdapter.GetLayerBottomZpos3DU( B_Cu ) );
|
|
|
|
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
|
objPtr->SetColor( ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_BoardBodyColor ) );
|
|
m_object_container.Add( objPtr );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D(
|
|
object2d_A,
|
|
object2d_B,
|
|
CSGITEM_FULL,
|
|
(const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
|
|
|
|
m_containerWithObjectsToDelete.Add( itemCSG2d );
|
|
|
|
CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d,
|
|
m_boardAdapter.GetLayerBottomZpos3DU( F_Cu ),
|
|
m_boardAdapter.GetLayerBottomZpos3DU( B_Cu ) );
|
|
|
|
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
|
objPtr->SetColor( ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_BoardBodyColor ) );
|
|
m_object_container.Add( objPtr );
|
|
}
|
|
}
|
|
|
|
// Add cylinders of the board body to container
|
|
// Note: This is actually a workarround for the holes in the board.
|
|
// The issue is because if a hole is in a border of a divided polygon ( ex
|
|
// a polygon or dummyblock) it will cut also the render of the hole.
|
|
// So this will add a full hole.
|
|
// In fact, that is not need if the hole have copper.
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
if( !m_boardAdapter.GetThroughHole_Outer().GetList().empty() )
|
|
{
|
|
const LIST_OBJECT2D &holeList = m_boardAdapter.GetThroughHole_Outer().GetList();
|
|
|
|
for( LIST_OBJECT2D::const_iterator hole = holeList.begin();
|
|
hole != holeList.end();
|
|
++hole )
|
|
{
|
|
const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*hole);
|
|
|
|
switch( hole2d->GetObjectType() )
|
|
{
|
|
case OBJECT2D_TYPE::FILLED_CIRCLE:
|
|
{
|
|
const float radius = hole2d->GetBBox().GetExtent().x * 0.5f * 0.999f;
|
|
|
|
CVCYLINDER *objPtr = new CVCYLINDER(
|
|
hole2d->GetCentroid(),
|
|
NextFloatDown( m_boardAdapter.GetLayerBottomZpos3DU( F_Cu ) ),
|
|
NextFloatUp( m_boardAdapter.GetLayerBottomZpos3DU( B_Cu ) ),
|
|
radius );
|
|
|
|
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
|
objPtr->SetColor( ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_BoardBodyColor ) );
|
|
|
|
m_object_container.Add( objPtr );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Add layers maps (except B_Mask and F_Mask)
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
|
|
for( MAP_CONTAINER_2D::const_iterator ii = m_boardAdapter.GetMapLayers().begin();
|
|
ii != m_boardAdapter.GetMapLayers().end();
|
|
++ii )
|
|
{
|
|
PCB_LAYER_ID layer_id = static_cast<PCB_LAYER_ID>(ii->first);
|
|
|
|
// Mask kayers are not processed here because they are a special case
|
|
if( (layer_id == B_Mask) || (layer_id == F_Mask) )
|
|
continue;
|
|
|
|
CMATERIAL *materialLayer = &m_materials.m_SilkS;
|
|
SFVEC3F layerColor = SFVEC3F( 0.0f, 0.0f, 0.0f );
|
|
|
|
switch( layer_id )
|
|
{
|
|
case B_Adhes:
|
|
case F_Adhes:
|
|
break;
|
|
|
|
case B_Paste:
|
|
case F_Paste:
|
|
materialLayer = &m_materials.m_Paste;
|
|
|
|
if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
|
|
layerColor = m_boardAdapter.m_SolderPasteColor;
|
|
else
|
|
layerColor = m_boardAdapter.GetLayerColor( layer_id );
|
|
break;
|
|
|
|
case B_SilkS:
|
|
materialLayer = &m_materials.m_SilkS;
|
|
|
|
if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
|
|
layerColor = m_boardAdapter.m_SilkScreenColorBot;
|
|
else
|
|
layerColor = m_boardAdapter.GetLayerColor( layer_id );
|
|
break;
|
|
case F_SilkS:
|
|
materialLayer = &m_materials.m_SilkS;
|
|
|
|
if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
|
|
layerColor = m_boardAdapter.m_SilkScreenColorTop;
|
|
else
|
|
layerColor = m_boardAdapter.GetLayerColor( layer_id );
|
|
break;
|
|
|
|
case Dwgs_User:
|
|
case Cmts_User:
|
|
case Eco1_User:
|
|
case Eco2_User:
|
|
case Edge_Cuts:
|
|
case Margin:
|
|
break;
|
|
|
|
case B_CrtYd:
|
|
case F_CrtYd:
|
|
break;
|
|
|
|
case B_Fab:
|
|
case F_Fab:
|
|
break;
|
|
|
|
default:
|
|
materialLayer = &m_materials.m_Copper;
|
|
|
|
if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
|
|
layerColor = m_boardAdapter.m_CopperColor;
|
|
else
|
|
layerColor = m_boardAdapter.GetLayerColor( layer_id );
|
|
break;
|
|
}
|
|
|
|
const CBVHCONTAINER2D *container2d = static_cast<const CBVHCONTAINER2D *>(ii->second);
|
|
const LIST_OBJECT2D &listObject2d = container2d->GetList();
|
|
|
|
for( LIST_OBJECT2D::const_iterator itemOnLayer = listObject2d.begin();
|
|
itemOnLayer != listObject2d.end();
|
|
++itemOnLayer )
|
|
{
|
|
const COBJECT2D *object2d_A = static_cast<const COBJECT2D *>(*itemOnLayer);
|
|
|
|
// not yet used / implemented (can be used in future to clip the objects in the board borders
|
|
COBJECT2D *object2d_C = CSGITEM_FULL;
|
|
|
|
std::vector<const COBJECT2D *> *object2d_B = CSGITEM_EMPTY;
|
|
|
|
object2d_B = new std::vector<const COBJECT2D*>();
|
|
|
|
// Subtract holes but not in SolderPaste
|
|
// (can be added as an option in future)
|
|
if( !( ( layer_id == B_Paste ) || ( layer_id == F_Paste ) ) )
|
|
{
|
|
// Check if there are any layerhole that intersects this object
|
|
// Eg: a segment is cutted by a via hole or THT hole.
|
|
// /////////////////////////////////////////////////////////////
|
|
const MAP_CONTAINER_2D &layerHolesMap = m_boardAdapter.GetMapLayersHoles();
|
|
|
|
if( layerHolesMap.find(layer_id) != layerHolesMap.end() )
|
|
{
|
|
MAP_CONTAINER_2D::const_iterator ii_hole = layerHolesMap.find(layer_id);
|
|
|
|
const CBVHCONTAINER2D *containerLayerHoles2d =
|
|
static_cast<const CBVHCONTAINER2D *>(ii_hole->second);
|
|
|
|
CONST_LIST_OBJECT2D intersectionList;
|
|
containerLayerHoles2d->GetListObjectsIntersects( object2d_A->GetBBox(),
|
|
intersectionList );
|
|
|
|
if( !intersectionList.empty() )
|
|
{
|
|
for( CONST_LIST_OBJECT2D::const_iterator holeOnLayer =
|
|
intersectionList.begin();
|
|
holeOnLayer != intersectionList.end();
|
|
++holeOnLayer )
|
|
{
|
|
const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*holeOnLayer);
|
|
|
|
//if( object2d_A->Intersects( hole2d->GetBBox() ) )
|
|
//if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) )
|
|
object2d_B->push_back( hole2d );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if there are any THT that intersects this object
|
|
// /////////////////////////////////////////////////////////////
|
|
|
|
// If we're processing a silk screen layer and the flag is set, then
|
|
// clip the silk screening at the outer edge of the annular ring, rather
|
|
// than the at the outer edge of the copper plating.
|
|
const CBVHCONTAINER2D& throughHoleOuter =
|
|
( m_boardAdapter.GetFlag( FL_CLIP_SILK_ON_VIA_ANNULUS )
|
|
&& ( ( layer_id == B_SilkS ) || ( layer_id == F_SilkS ) ) ) ?
|
|
m_boardAdapter.GetThroughHole_Outer_Ring() :
|
|
m_boardAdapter.GetThroughHole_Outer();
|
|
|
|
if( !throughHoleOuter.GetList().empty() )
|
|
{
|
|
CONST_LIST_OBJECT2D intersectionList;
|
|
|
|
throughHoleOuter.GetListObjectsIntersects(
|
|
object2d_A->GetBBox(), intersectionList );
|
|
|
|
if( !intersectionList.empty() )
|
|
{
|
|
for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin();
|
|
hole != intersectionList.end();
|
|
++hole )
|
|
{
|
|
const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*hole);
|
|
|
|
//if( object2d_A->Intersects( hole2d->GetBBox() ) )
|
|
//if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) )
|
|
object2d_B->push_back( hole2d );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
const MAP_CONTAINER_2D& mapLayers = m_boardAdapter.GetMapLayers();
|
|
|
|
if( m_boardAdapter.GetFlag( FL_SUBTRACT_MASK_FROM_SILK ) &&
|
|
( ( ( layer_id == B_SilkS ) &&
|
|
( mapLayers.find( B_Mask ) != mapLayers.end() ) ) ||
|
|
( ( layer_id == F_SilkS ) &&
|
|
( mapLayers.find( F_Mask ) != mapLayers.end() ) ) ) )
|
|
{
|
|
const PCB_LAYER_ID layerMask_id = ( layer_id == B_SilkS ) ? B_Mask : F_Mask;
|
|
|
|
const CBVHCONTAINER2D *containerMaskLayer2d =
|
|
static_cast<const CBVHCONTAINER2D*>( mapLayers.at( layerMask_id ) );
|
|
|
|
CONST_LIST_OBJECT2D intersectionList;
|
|
|
|
if( containerMaskLayer2d ) // can be null if B_Mask or F_Mask is not shown
|
|
containerMaskLayer2d->GetListObjectsIntersects( object2d_A->GetBBox(),
|
|
intersectionList );
|
|
|
|
if( !intersectionList.empty() )
|
|
{
|
|
for( CONST_LIST_OBJECT2D::const_iterator objOnLayer =
|
|
intersectionList.begin();
|
|
objOnLayer != intersectionList.end();
|
|
++objOnLayer )
|
|
{
|
|
const COBJECT2D* obj2d = static_cast<const COBJECT2D*>( *objOnLayer );
|
|
|
|
object2d_B->push_back( obj2d );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( object2d_B->empty() )
|
|
{
|
|
delete object2d_B;
|
|
object2d_B = CSGITEM_EMPTY;
|
|
}
|
|
|
|
if( (object2d_B == CSGITEM_EMPTY) &&
|
|
(object2d_C == CSGITEM_FULL) )
|
|
{
|
|
#if 0
|
|
create_3d_object_from( m_object_container,
|
|
object2d_A,
|
|
m_boardAdapter.GetLayerBottomZpos3DU( layer_id ),
|
|
m_boardAdapter.GetLayerTopZpos3DU( layer_id ),
|
|
materialLayer,
|
|
layerColor );
|
|
#else
|
|
CLAYERITEM *objPtr = new CLAYERITEM( object2d_A,
|
|
m_boardAdapter.GetLayerBottomZpos3DU( layer_id ),
|
|
m_boardAdapter.GetLayerTopZpos3DU( layer_id ) );
|
|
objPtr->SetMaterial( materialLayer );
|
|
objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
|
|
m_object_container.Add( objPtr );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if 1
|
|
CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A,
|
|
object2d_B,
|
|
object2d_C,
|
|
object2d_A->GetBoardItem() );
|
|
m_containerWithObjectsToDelete.Add( itemCSG2d );
|
|
|
|
CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d,
|
|
m_boardAdapter.GetLayerBottomZpos3DU( layer_id ),
|
|
m_boardAdapter.GetLayerTopZpos3DU( layer_id ) );
|
|
|
|
objPtr->SetMaterial( materialLayer );
|
|
objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
|
|
|
|
m_object_container.Add( objPtr );
|
|
#endif
|
|
}
|
|
}
|
|
}// for each layer on map
|
|
|
|
|
|
|
|
// Add Mask layer
|
|
// Solder mask layers are "negative" layers so the elements that we have
|
|
// (in the container) should remove the board outline.
|
|
// We will check for all objects in the outline if it intersects any object
|
|
// in the layer container and also any hole.
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
if( m_boardAdapter.GetFlag( FL_SOLDERMASK ) &&
|
|
(m_outlineBoard2dObjects->GetList().size() >= 1) )
|
|
{
|
|
const CMATERIAL *materialLayer = &m_materials.m_SolderMask;
|
|
|
|
for( MAP_CONTAINER_2D::const_iterator ii = m_boardAdapter.GetMapLayers().begin();
|
|
ii != m_boardAdapter.GetMapLayers().end();
|
|
++ii )
|
|
{
|
|
PCB_LAYER_ID layer_id = static_cast<PCB_LAYER_ID>(ii->first);
|
|
|
|
const CBVHCONTAINER2D *containerLayer2d =
|
|
static_cast<const CBVHCONTAINER2D *>(ii->second);
|
|
|
|
// Only get the Solder mask layers
|
|
if( !((layer_id == B_Mask) || (layer_id == F_Mask)) )
|
|
continue;
|
|
|
|
SFVEC3F layerColor;
|
|
if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
|
|
{
|
|
if( layer_id == B_Mask )
|
|
layerColor = m_boardAdapter.m_SolderMaskColorBot;
|
|
else
|
|
layerColor = m_boardAdapter.m_SolderMaskColorTop;
|
|
}
|
|
else
|
|
layerColor = m_boardAdapter.GetLayerColor( layer_id );
|
|
|
|
const float zLayerMin = m_boardAdapter.GetLayerBottomZpos3DU( layer_id );
|
|
const float zLayerMax = m_boardAdapter.GetLayerTopZpos3DU( layer_id );
|
|
|
|
// Get the outline board objects
|
|
const LIST_OBJECT2D &listObjects = m_outlineBoard2dObjects->GetList();
|
|
|
|
for( LIST_OBJECT2D::const_iterator object2d_iterator = listObjects.begin();
|
|
object2d_iterator != listObjects.end();
|
|
++object2d_iterator )
|
|
{
|
|
const COBJECT2D *object2d_A = static_cast<const COBJECT2D *>(*object2d_iterator);
|
|
|
|
std::vector<const COBJECT2D *> *object2d_B = new std::vector<const COBJECT2D *>();
|
|
|
|
// Check if there are any THT that intersects this outline object part
|
|
if( !m_boardAdapter.GetThroughHole_Outer().GetList().empty() )
|
|
{
|
|
|
|
CONST_LIST_OBJECT2D intersectionList;
|
|
|
|
m_boardAdapter.GetThroughHole_Outer().GetListObjectsIntersects(
|
|
object2d_A->GetBBox(),
|
|
intersectionList );
|
|
|
|
if( !intersectionList.empty() )
|
|
{
|
|
for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin();
|
|
hole != intersectionList.end();
|
|
++hole )
|
|
{
|
|
const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*hole);
|
|
|
|
if( object2d_A->Intersects( hole2d->GetBBox() ) )
|
|
//if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) )
|
|
object2d_B->push_back( hole2d );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if there are any objects in the layer to subtract with the
|
|
// corrent object
|
|
if( !containerLayer2d->GetList().empty() )
|
|
{
|
|
CONST_LIST_OBJECT2D intersectionList;
|
|
|
|
containerLayer2d->GetListObjectsIntersects( object2d_A->GetBBox(),
|
|
intersectionList );
|
|
|
|
if( !intersectionList.empty() )
|
|
{
|
|
for( CONST_LIST_OBJECT2D::const_iterator obj = intersectionList.begin();
|
|
obj != intersectionList.end();
|
|
++obj )
|
|
{
|
|
const COBJECT2D *obj2d = static_cast<const COBJECT2D *>(*obj);
|
|
|
|
//if( object2d_A->Intersects( obj2d->GetBBox() ) )
|
|
//if( object2d_A->GetBBox().Intersects( obj2d->GetBBox() ) )
|
|
object2d_B->push_back( obj2d );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( object2d_B->empty() )
|
|
{
|
|
delete object2d_B;
|
|
object2d_B = CSGITEM_EMPTY;
|
|
}
|
|
|
|
if( object2d_B == CSGITEM_EMPTY )
|
|
{
|
|
#if 0
|
|
create_3d_object_from( m_object_container,
|
|
object2d_A,
|
|
zLayerMin,
|
|
zLayerMax,
|
|
materialLayer,
|
|
layerColor );
|
|
#else
|
|
CLAYERITEM *objPtr = new CLAYERITEM( object2d_A,
|
|
zLayerMin,
|
|
zLayerMax );
|
|
|
|
objPtr->SetMaterial( materialLayer );
|
|
objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
|
|
|
|
m_object_container.Add( objPtr );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A,
|
|
object2d_B,
|
|
CSGITEM_FULL,
|
|
object2d_A->GetBoardItem() );
|
|
|
|
m_containerWithObjectsToDelete.Add( itemCSG2d );
|
|
|
|
CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d,
|
|
zLayerMin,
|
|
zLayerMax );
|
|
objPtr->SetMaterial( materialLayer );
|
|
objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
|
|
|
|
m_object_container.Add( objPtr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
add_3D_vias_and_pads_to_container();
|
|
|
|
#ifdef PRINT_STATISTICS_3D_VIEWER
|
|
unsigned stats_endConvertTime = GetRunningMicroSecs();
|
|
unsigned stats_startLoad3DmodelsTime = stats_endConvertTime;
|
|
#endif
|
|
|
|
|
|
load_3D_models();
|
|
|
|
|
|
#ifdef PRINT_STATISTICS_3D_VIEWER
|
|
unsigned stats_endLoad3DmodelsTime = GetRunningMicroSecs();
|
|
#endif
|
|
|
|
// Add floor
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_BACKFLOOR ) )
|
|
{
|
|
CBBOX boardBBox = m_boardAdapter.GetBBox3DU();
|
|
|
|
if( boardBBox.IsInitialized() )
|
|
{
|
|
boardBBox.Scale( 3.0f );
|
|
|
|
if( m_object_container.GetList().size() > 0 )
|
|
{
|
|
CBBOX containerBBox = m_object_container.GetBBox();
|
|
|
|
containerBBox.Scale( 1.3f );
|
|
|
|
const SFVEC3F centerBBox = containerBBox.GetCenter();
|
|
|
|
// Floor triangles
|
|
const float minZ = glm::min( containerBBox.Min().z,
|
|
boardBBox.Min().z );
|
|
|
|
const SFVEC3F v1 = SFVEC3F( -RANGE_SCALE_3D * 4.0f,
|
|
-RANGE_SCALE_3D * 4.0f,
|
|
minZ ) +
|
|
SFVEC3F( centerBBox.x,
|
|
centerBBox.y,
|
|
0.0f );
|
|
|
|
const SFVEC3F v3 = SFVEC3F( +RANGE_SCALE_3D * 4.0f,
|
|
+RANGE_SCALE_3D * 4.0f,
|
|
minZ ) +
|
|
SFVEC3F( centerBBox.x,
|
|
centerBBox.y,
|
|
0.0f );
|
|
|
|
const SFVEC3F v2 = SFVEC3F( v1.x, v3.y, v1.z );
|
|
const SFVEC3F v4 = SFVEC3F( v3.x, v1.y, v1.z );
|
|
|
|
SFVEC3F backgroundColor =
|
|
ConvertSRGBToLinear( static_cast<SFVEC3F>( m_boardAdapter.m_BgColorTop ) );
|
|
|
|
CTRIANGLE *newTriangle1 = new CTRIANGLE( v1, v2, v3 );
|
|
CTRIANGLE *newTriangle2 = new CTRIANGLE( v3, v4, v1 );
|
|
|
|
m_object_container.Add( newTriangle1 );
|
|
m_object_container.Add( newTriangle2 );
|
|
|
|
newTriangle1->SetMaterial( (const CMATERIAL *)&m_materials.m_Floor );
|
|
newTriangle2->SetMaterial( (const CMATERIAL *)&m_materials.m_Floor );
|
|
|
|
newTriangle1->SetColor( backgroundColor );
|
|
newTriangle2->SetColor( backgroundColor );
|
|
|
|
// Ceiling triangles
|
|
const float maxZ = glm::max( containerBBox.Max().z,
|
|
boardBBox.Max().z );
|
|
|
|
const SFVEC3F v5 = SFVEC3F( v1.x, v1.y, maxZ );
|
|
const SFVEC3F v6 = SFVEC3F( v2.x, v2.y, maxZ );
|
|
const SFVEC3F v7 = SFVEC3F( v3.x, v3.y, maxZ );
|
|
const SFVEC3F v8 = SFVEC3F( v4.x, v4.y, maxZ );
|
|
|
|
CTRIANGLE *newTriangle3 = new CTRIANGLE( v7, v6, v5 );
|
|
CTRIANGLE *newTriangle4 = new CTRIANGLE( v5, v8, v7 );
|
|
|
|
m_object_container.Add( newTriangle3 );
|
|
m_object_container.Add( newTriangle4 );
|
|
|
|
newTriangle3->SetMaterial( (const CMATERIAL *)&m_materials.m_Floor );
|
|
newTriangle4->SetMaterial( (const CMATERIAL *)&m_materials.m_Floor );
|
|
|
|
newTriangle3->SetColor( backgroundColor );
|
|
newTriangle4->SetColor( backgroundColor );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Init initial lights
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
m_lights.Clear();
|
|
|
|
auto IsColorZero = [] ( const SFVEC3F& aSource )
|
|
{
|
|
return ( ( aSource.r < ( 1.0f / 255.0f ) ) &&
|
|
( aSource.g < ( 1.0f / 255.0f ) ) &&
|
|
( aSource.b < ( 1.0f / 255.0f ) ) );
|
|
};
|
|
|
|
m_camera_light = new CDIRECTIONALLIGHT( SFVEC3F( 0.0f, 0.0f, 0.0f ),
|
|
m_boardAdapter.m_raytrace_lightColorCamera );
|
|
m_camera_light->SetCastShadows( false );
|
|
|
|
if( !IsColorZero( m_boardAdapter.m_raytrace_lightColorCamera ) )
|
|
m_lights.Add( m_camera_light );
|
|
|
|
const SFVEC3F& boardCenter = m_boardAdapter.GetBBox3DU().GetCenter();
|
|
|
|
if( !IsColorZero( m_boardAdapter.m_raytrace_lightColorTop ) )
|
|
m_lights.Add( new CPOINTLIGHT( SFVEC3F( boardCenter.x, boardCenter.y, +RANGE_SCALE_3D * 2.0f ),
|
|
m_boardAdapter.m_raytrace_lightColorTop ) );
|
|
|
|
if( !IsColorZero( m_boardAdapter.m_raytrace_lightColorBottom ) )
|
|
m_lights.Add( new CPOINTLIGHT( SFVEC3F( boardCenter.x, boardCenter.y, -RANGE_SCALE_3D * 2.0f ),
|
|
m_boardAdapter.m_raytrace_lightColorBottom ) );
|
|
|
|
wxASSERT( m_boardAdapter.m_raytrace_lightColor.size()
|
|
== m_boardAdapter.m_raytrace_lightSphericalCoords.size() );
|
|
|
|
for( size_t i = 0; i < m_boardAdapter.m_raytrace_lightColor.size(); ++i )
|
|
{
|
|
if( !IsColorZero( m_boardAdapter.m_raytrace_lightColor[i] ) )
|
|
{
|
|
const SFVEC2F sc = m_boardAdapter.m_raytrace_lightSphericalCoords[i];
|
|
|
|
m_lights.Add( new CDIRECTIONALLIGHT( SphericalToCartesian( glm::pi<float>() * sc.x,
|
|
glm::pi<float>() * sc.y ),
|
|
m_boardAdapter.m_raytrace_lightColor[i] ) );
|
|
}
|
|
}
|
|
|
|
|
|
// Create an accelerator
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
if( m_accelerator )
|
|
{
|
|
delete m_accelerator;
|
|
}
|
|
m_accelerator = 0;
|
|
|
|
m_accelerator = new CBVH_PBRT( m_object_container );
|
|
|
|
if( aStatusReporter )
|
|
{
|
|
// Calculation time in seconds
|
|
const double calculation_time = (double)( GetRunningMicroSecs() -
|
|
stats_startReloadTime ) / 1e6;
|
|
|
|
aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Based on draw3DViaHole from
|
|
// 3d_draw_helper_functions.cpp
|
|
void C3D_RENDER_RAYTRACING::insert3DViaHole( const VIA* aVia )
|
|
{
|
|
PCB_LAYER_ID top_layer, bottom_layer;
|
|
int radiusBUI = (aVia->GetDrillValue() / 2);
|
|
|
|
aVia->LayerPair( &top_layer, &bottom_layer );
|
|
|
|
float topZ = m_boardAdapter.GetLayerBottomZpos3DU( top_layer ) +
|
|
m_boardAdapter.GetCopperThickness3DU();
|
|
|
|
float botZ = m_boardAdapter.GetLayerBottomZpos3DU( bottom_layer ) -
|
|
m_boardAdapter.GetCopperThickness3DU();
|
|
|
|
const SFVEC2F center = SFVEC2F( aVia->GetStart().x * m_boardAdapter.BiuTo3Dunits(),
|
|
-aVia->GetStart().y * m_boardAdapter.BiuTo3Dunits() );
|
|
|
|
CRING2D *ring = new CRING2D( center,
|
|
radiusBUI * m_boardAdapter.BiuTo3Dunits(),
|
|
( radiusBUI + m_boardAdapter.GetCopperThicknessBIU() ) *
|
|
m_boardAdapter.BiuTo3Dunits(),
|
|
*aVia );
|
|
|
|
m_containerWithObjectsToDelete.Add( ring );
|
|
|
|
|
|
CLAYERITEM *objPtr = new CLAYERITEM( ring, topZ, botZ );
|
|
|
|
objPtr->SetMaterial( &m_materials.m_Copper );
|
|
|
|
if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
|
|
objPtr->SetColor( ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_CopperColor ) );
|
|
else
|
|
objPtr->SetColor( ConvertSRGBToLinear(
|
|
m_boardAdapter.GetItemColor( LAYER_VIAS + static_cast<int>( aVia->GetViaType() ) ) ) );
|
|
|
|
m_object_container.Add( objPtr );
|
|
}
|
|
|
|
|
|
// Based on draw3DPadHole from
|
|
// 3d_draw_helper_functions.cpp
|
|
void C3D_RENDER_RAYTRACING::insert3DPadHole( const D_PAD* aPad )
|
|
{
|
|
const COBJECT2D *object2d_A = NULL;
|
|
|
|
SFVEC3F objColor;
|
|
|
|
if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
|
|
objColor = (SFVEC3F)m_boardAdapter.m_CopperColor;
|
|
else
|
|
objColor = m_boardAdapter.GetItemColor( LAYER_PADS_TH );
|
|
|
|
const wxSize drillsize = aPad->GetDrillSize();
|
|
const bool hasHole = drillsize.x && drillsize.y;
|
|
|
|
if( !hasHole )
|
|
return;
|
|
|
|
const float topZ = m_boardAdapter.GetLayerBottomZpos3DU( F_Cu ) +
|
|
m_boardAdapter.GetCopperThickness3DU();
|
|
|
|
const float botZ = m_boardAdapter.GetLayerBottomZpos3DU( B_Cu ) -
|
|
m_boardAdapter.GetCopperThickness3DU();
|
|
|
|
if( drillsize.x == drillsize.y ) // usual round hole
|
|
{
|
|
SFVEC2F center = SFVEC2F( aPad->GetPosition().x * m_boardAdapter.BiuTo3Dunits(),
|
|
-aPad->GetPosition().y * m_boardAdapter.BiuTo3Dunits() );
|
|
|
|
CRING2D *ring = new CRING2D( center,
|
|
( drillsize.x / 2 ) * m_boardAdapter.BiuTo3Dunits(),
|
|
(( drillsize.x / 2 ) +
|
|
m_boardAdapter.GetCopperThicknessBIU() ) *
|
|
m_boardAdapter.BiuTo3Dunits(),
|
|
*aPad );
|
|
|
|
m_containerWithObjectsToDelete.Add( ring );
|
|
|
|
object2d_A = ring;
|
|
}
|
|
else // Oblong hole
|
|
{
|
|
wxPoint ends_offset;
|
|
int width;
|
|
|
|
if( drillsize.x > drillsize.y ) // Horizontal oval
|
|
{
|
|
ends_offset.x = ( drillsize.x - drillsize.y ) / 2;
|
|
width = drillsize.y;
|
|
}
|
|
else // Vertical oval
|
|
{
|
|
ends_offset.y = ( drillsize.y - drillsize.x ) / 2;
|
|
width = drillsize.x;
|
|
}
|
|
|
|
RotatePoint( &ends_offset, aPad->GetOrientation() );
|
|
|
|
wxPoint start = aPad->GetPosition() + ends_offset;
|
|
wxPoint end = aPad->GetPosition() - ends_offset;
|
|
|
|
CROUNDSEGMENT2D *innerSeg = new CROUNDSEGMENT2D(
|
|
SFVEC2F( start.x * m_boardAdapter.BiuTo3Dunits(),
|
|
-start.y * m_boardAdapter.BiuTo3Dunits() ),
|
|
SFVEC2F( end.x * m_boardAdapter.BiuTo3Dunits(),
|
|
-end.y * m_boardAdapter.BiuTo3Dunits() ),
|
|
width * m_boardAdapter.BiuTo3Dunits(),
|
|
*aPad );
|
|
|
|
CROUNDSEGMENT2D *outerSeg = new CROUNDSEGMENT2D(
|
|
SFVEC2F( start.x * m_boardAdapter.BiuTo3Dunits(),
|
|
-start.y * m_boardAdapter.BiuTo3Dunits() ),
|
|
SFVEC2F( end.x * m_boardAdapter.BiuTo3Dunits(),
|
|
-end.y * m_boardAdapter.BiuTo3Dunits() ),
|
|
( width + m_boardAdapter.GetCopperThicknessBIU() * 2 ) *
|
|
m_boardAdapter.BiuTo3Dunits(),
|
|
*aPad );
|
|
|
|
// NOTE: the round segment width is the "diameter", so we double the thickness
|
|
|
|
std::vector<const COBJECT2D *> *object2d_B = new std::vector<const COBJECT2D *>();
|
|
object2d_B->push_back( innerSeg );
|
|
|
|
CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( outerSeg,
|
|
object2d_B,
|
|
CSGITEM_FULL,
|
|
*aPad );
|
|
|
|
m_containerWithObjectsToDelete.Add( itemCSG2d );
|
|
m_containerWithObjectsToDelete.Add( innerSeg );
|
|
m_containerWithObjectsToDelete.Add( outerSeg );
|
|
|
|
object2d_A = itemCSG2d;
|
|
}
|
|
|
|
|
|
if( object2d_A )
|
|
{
|
|
std::vector<const COBJECT2D *> *object2d_B = new std::vector<const COBJECT2D *>();
|
|
|
|
// Check if there are any other THT that intersects this hole
|
|
// It will use the non inflated holes
|
|
if( !m_boardAdapter.GetThroughHole_Inner().GetList().empty() )
|
|
{
|
|
|
|
CONST_LIST_OBJECT2D intersectionList;
|
|
m_boardAdapter.GetThroughHole_Inner().GetListObjectsIntersects( object2d_A->GetBBox(),
|
|
intersectionList );
|
|
|
|
if( !intersectionList.empty() )
|
|
{
|
|
for( CONST_LIST_OBJECT2D::const_iterator hole = intersectionList.begin();
|
|
hole != intersectionList.end();
|
|
++hole )
|
|
{
|
|
const COBJECT2D *hole2d = static_cast<const COBJECT2D *>(*hole);
|
|
|
|
if( object2d_A->Intersects( hole2d->GetBBox() ) )
|
|
//if( object2d_A->GetBBox().Intersects( hole2d->GetBBox() ) )
|
|
object2d_B->push_back( hole2d );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( object2d_B->empty() )
|
|
{
|
|
delete object2d_B;
|
|
object2d_B = CSGITEM_EMPTY;
|
|
}
|
|
|
|
if( object2d_B == CSGITEM_EMPTY )
|
|
{
|
|
CLAYERITEM *objPtr = new CLAYERITEM( object2d_A, topZ, botZ );
|
|
|
|
objPtr->SetMaterial( &m_materials.m_Copper );
|
|
objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
|
|
m_object_container.Add( objPtr );
|
|
}
|
|
else
|
|
{
|
|
CITEMLAYERCSG2D *itemCSG2d = new CITEMLAYERCSG2D( object2d_A,
|
|
object2d_B,
|
|
CSGITEM_FULL,
|
|
(const BOARD_ITEM &)*aPad );
|
|
|
|
m_containerWithObjectsToDelete.Add( itemCSG2d );
|
|
|
|
CLAYERITEM *objPtr = new CLAYERITEM( itemCSG2d, topZ, botZ );
|
|
|
|
objPtr->SetMaterial( &m_materials.m_Copper );
|
|
objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
|
|
|
|
m_object_container.Add( objPtr );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void C3D_RENDER_RAYTRACING::add_3D_vias_and_pads_to_container()
|
|
{
|
|
// Insert plated vertical holes inside the board
|
|
// /////////////////////////////////////////////////////////////////////////
|
|
|
|
// Insert vias holes (vertical cylinders)
|
|
for( auto track : m_boardAdapter.GetBoard()->Tracks() )
|
|
{
|
|
if( track->Type() == PCB_VIA_T )
|
|
{
|
|
const VIA *via = static_cast<const VIA*>(track);
|
|
insert3DViaHole( via );
|
|
}
|
|
}
|
|
|
|
// Insert pads holes (vertical cylinders)
|
|
for( auto module : m_boardAdapter.GetBoard()->Modules() )
|
|
{
|
|
for( auto pad : module->Pads() )
|
|
if( pad->GetAttribute () != PAD_ATTRIB_HOLE_NOT_PLATED )
|
|
{
|
|
insert3DPadHole( pad );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void C3D_RENDER_RAYTRACING::load_3D_models()
|
|
{
|
|
// Go for all modules
|
|
for( auto module : m_boardAdapter.GetBoard()->Modules() )
|
|
{
|
|
if((!module->Models().empty() ) &&
|
|
m_boardAdapter.ShouldModuleBeDisplayed((MODULE_ATTR_T)module->GetAttributes() ) )
|
|
{
|
|
double zpos = m_boardAdapter.GetModulesZcoord3DIU( module->IsFlipped() );
|
|
|
|
wxPoint pos = module->GetPosition();
|
|
|
|
glm::mat4 moduleMatrix = glm::mat4( 1.0f );
|
|
|
|
moduleMatrix = glm::translate( moduleMatrix,
|
|
SFVEC3F( pos.x * m_boardAdapter.BiuTo3Dunits(),
|
|
-pos.y * m_boardAdapter.BiuTo3Dunits(),
|
|
zpos ) );
|
|
|
|
if( module->GetOrientation() )
|
|
{
|
|
moduleMatrix = glm::rotate( moduleMatrix,
|
|
( (float)(module->GetOrientation() / 10.0f) / 180.0f ) *
|
|
glm::pi<float>(),
|
|
SFVEC3F( 0.0f, 0.0f, 1.0f ) );
|
|
}
|
|
|
|
|
|
if( module->IsFlipped() )
|
|
{
|
|
moduleMatrix = glm::rotate( moduleMatrix,
|
|
glm::pi<float>(),
|
|
SFVEC3F( 0.0f, 1.0f, 0.0f ) );
|
|
|
|
moduleMatrix = glm::rotate( moduleMatrix,
|
|
glm::pi<float>(),
|
|
SFVEC3F( 0.0f, 0.0f, 1.0f ) );
|
|
}
|
|
|
|
const double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3Dunits() *
|
|
UNITS3D_TO_UNITSPCB;
|
|
|
|
moduleMatrix = glm::scale( moduleMatrix,
|
|
SFVEC3F( modelunit_to_3d_units_factor,
|
|
modelunit_to_3d_units_factor,
|
|
modelunit_to_3d_units_factor ) );
|
|
|
|
|
|
// Get the list of model files for this model
|
|
S3D_CACHE* cacheMgr = m_boardAdapter.Get3DCacheManager();
|
|
auto sM = module->Models().begin();
|
|
auto eM = module->Models().end();
|
|
|
|
while( sM != eM )
|
|
{
|
|
if( ( static_cast<float>( sM->m_Opacity ) > FLT_EPSILON ) &&
|
|
( sM->m_Show && !sM->m_Filename.empty() ) )
|
|
{
|
|
// get it from cache
|
|
const S3DMODEL *modelPtr = cacheMgr->GetModel( sM->m_Filename );
|
|
|
|
// only add it if the return is not NULL
|
|
if( modelPtr )
|
|
{
|
|
glm::mat4 modelMatrix = moduleMatrix;
|
|
|
|
modelMatrix = glm::translate( modelMatrix,
|
|
SFVEC3F( sM->m_Offset.x,
|
|
sM->m_Offset.y,
|
|
sM->m_Offset.z ) );
|
|
|
|
modelMatrix = glm::rotate( modelMatrix,
|
|
(float)-( sM->m_Rotation.z / 180.0f ) *
|
|
glm::pi<float>(),
|
|
SFVEC3F( 0.0f, 0.0f, 1.0f ) );
|
|
|
|
modelMatrix = glm::rotate( modelMatrix,
|
|
(float)-( sM->m_Rotation.y / 180.0f ) *
|
|
glm::pi<float>(),
|
|
SFVEC3F( 0.0f, 1.0f, 0.0f ) );
|
|
|
|
modelMatrix = glm::rotate( modelMatrix,
|
|
(float)-( sM->m_Rotation.x / 180.0f ) *
|
|
glm::pi<float>(),
|
|
SFVEC3F( 1.0f, 0.0f, 0.0f ) );
|
|
|
|
modelMatrix = glm::scale( modelMatrix,
|
|
SFVEC3F( sM->m_Scale.x,
|
|
sM->m_Scale.y,
|
|
sM->m_Scale.z ) );
|
|
|
|
add_3D_models( modelPtr, modelMatrix, (float)sM->m_Opacity );
|
|
}
|
|
}
|
|
|
|
++sM;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void C3D_RENDER_RAYTRACING::add_3D_models( const S3DMODEL *a3DModel,
|
|
const glm::mat4 &aModelMatrix,
|
|
float aModuleOpacity )
|
|
{
|
|
|
|
// Validate a3DModel pointers
|
|
wxASSERT( a3DModel != NULL );
|
|
|
|
if( a3DModel == NULL )
|
|
return;
|
|
|
|
wxASSERT( a3DModel->m_Materials != NULL );
|
|
wxASSERT( a3DModel->m_Meshes != NULL );
|
|
wxASSERT( a3DModel->m_MaterialsSize > 0 );
|
|
wxASSERT( a3DModel->m_MeshesSize > 0 );
|
|
wxASSERT( aModuleOpacity > 0.0f );
|
|
wxASSERT( aModuleOpacity <= 1.0f );
|
|
|
|
if( aModuleOpacity > 1.0f )
|
|
{
|
|
aModuleOpacity = 1.0f;
|
|
}
|
|
|
|
if( (a3DModel->m_Materials != NULL) && (a3DModel->m_Meshes != NULL) &&
|
|
(a3DModel->m_MaterialsSize > 0) && (a3DModel->m_MeshesSize > 0) )
|
|
{
|
|
|
|
MODEL_MATERIALS *materialVector;
|
|
|
|
// Try find if the materials already exists in the map list
|
|
if( m_model_materials.find( a3DModel ) != m_model_materials.end() )
|
|
{
|
|
// Found it, so get the pointer
|
|
materialVector = &m_model_materials[a3DModel];
|
|
}
|
|
else
|
|
{
|
|
// Materials was not found in the map, so it will create a new for
|
|
// this model.
|
|
|
|
m_model_materials[a3DModel] = MODEL_MATERIALS();
|
|
materialVector = &m_model_materials[a3DModel];
|
|
|
|
materialVector->resize( a3DModel->m_MaterialsSize );
|
|
|
|
for( unsigned int imat = 0;
|
|
imat < a3DModel->m_MaterialsSize;
|
|
++imat )
|
|
{
|
|
if( m_boardAdapter.MaterialModeGet() == MATERIAL_MODE::NORMAL )
|
|
{
|
|
const SMATERIAL &material = a3DModel->m_Materials[imat];
|
|
|
|
// http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJtaW4oc3FydCh4LTAuMzUpKjAuNDAtMC4wNSwxLjApIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiMC4wNzA3NzM2NzMyMzY1OTAxMiIsIjEuNTY5NTcxNjI5MjI1NDY5OCIsIi0wLjI3NDYzNTMyMTc1OTkyOTMiLCIwLjY0NzcwMTg4MTkyNTUzNjIiXSwic2l6ZSI6WzY0NCwzOTRdfV0-
|
|
|
|
float reflectionFactor = 0.0f;
|
|
|
|
if( (material.m_Shininess - 0.35f) > FLT_EPSILON )
|
|
{
|
|
reflectionFactor = glm::clamp( glm::sqrt( (material.m_Shininess - 0.35f) ) *
|
|
0.40f - 0.05f,
|
|
0.0f,
|
|
0.5f );
|
|
}
|
|
|
|
CBLINN_PHONG_MATERIAL &blinnMaterial = (*materialVector)[imat];
|
|
|
|
blinnMaterial = CBLINN_PHONG_MATERIAL(
|
|
ConvertSRGBToLinear( material.m_Ambient ),
|
|
ConvertSRGBToLinear( material.m_Emissive ),
|
|
ConvertSRGBToLinear( material.m_Specular ),
|
|
material.m_Shininess * 180.0f,
|
|
material.m_Transparency,
|
|
reflectionFactor );
|
|
|
|
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
|
|
{
|
|
// Guess material type and apply a normal perturbator
|
|
|
|
if( ( RGBtoGray(material.m_Diffuse) < 0.3f ) &&
|
|
( material.m_Shininess < 0.36f ) &&
|
|
( material.m_Transparency == 0.0f ) &&
|
|
( (glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) < 0.15f) &&
|
|
(glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) < 0.15f) &&
|
|
(glm::abs( material.m_Diffuse.r - material.m_Diffuse.b ) < 0.15f) ) )
|
|
{
|
|
// This may be a black plastic..
|
|
|
|
if( material.m_Shininess < 0.26f )
|
|
blinnMaterial.SetNormalPerturbator( &m_plastic_normal_perturbator );
|
|
else
|
|
blinnMaterial.SetNormalPerturbator( &m_plastic_shine_normal_perturbator );
|
|
}
|
|
else
|
|
{
|
|
if( ( RGBtoGray(material.m_Diffuse) > 0.3f ) &&
|
|
( material.m_Shininess < 0.30f ) &&
|
|
( material.m_Transparency == 0.0f ) &&
|
|
( (glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) > 0.25f) ||
|
|
(glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) > 0.25f) ||
|
|
(glm::abs( material.m_Diffuse.r - material.m_Diffuse.b ) > 0.25f) ) )
|
|
{
|
|
// This may be a color plastic ...
|
|
blinnMaterial.SetNormalPerturbator( &m_plastic_shine_normal_perturbator );
|
|
}
|
|
else
|
|
{
|
|
if( ( RGBtoGray(material.m_Diffuse) > 0.6f ) &&
|
|
( material.m_Shininess > 0.35f ) &&
|
|
( material.m_Transparency == 0.0f ) &&
|
|
( (glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) < 0.40f) &&
|
|
(glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) < 0.40f) &&
|
|
(glm::abs( material.m_Diffuse.r - material.m_Diffuse.b ) < 0.40f) ) )
|
|
{
|
|
// This may be a brushed metal
|
|
blinnMaterial.SetNormalPerturbator( &m_brushed_metal_normal_perturbator );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(*materialVector)[imat] = CBLINN_PHONG_MATERIAL( SFVEC3F( 0.2f ),
|
|
SFVEC3F( 0.0f ),
|
|
SFVEC3F( 0.0f ),
|
|
0.0f,
|
|
0.0f,
|
|
0.0f );
|
|
}
|
|
}
|
|
}
|
|
|
|
const glm::mat3 normalMatrix = glm::transpose( glm::inverse( glm::mat3( aModelMatrix ) ) );
|
|
|
|
for( unsigned int mesh_i = 0;
|
|
mesh_i < a3DModel->m_MeshesSize;
|
|
++mesh_i )
|
|
{
|
|
const SMESH &mesh = a3DModel->m_Meshes[mesh_i];
|
|
|
|
// Validate the mesh pointers
|
|
wxASSERT( mesh.m_Positions != NULL );
|
|
wxASSERT( mesh.m_FaceIdx != NULL );
|
|
wxASSERT( mesh.m_Normals != NULL );
|
|
wxASSERT( mesh.m_FaceIdxSize > 0 );
|
|
wxASSERT( (mesh.m_FaceIdxSize % 3) == 0 );
|
|
|
|
|
|
if( (mesh.m_Positions != NULL) &&
|
|
(mesh.m_Normals != NULL) &&
|
|
(mesh.m_FaceIdx != NULL) &&
|
|
(mesh.m_FaceIdxSize > 0) &&
|
|
(mesh.m_VertexSize > 0) &&
|
|
((mesh.m_FaceIdxSize % 3) == 0) &&
|
|
(mesh.m_MaterialIdx < a3DModel->m_MaterialsSize) )
|
|
{
|
|
const CBLINN_PHONG_MATERIAL &blinn_material = (*materialVector)[mesh.m_MaterialIdx];
|
|
|
|
const float moduleTransparency = 1.0f - ( ( 1.0f - blinn_material.GetTransparency() ) * aModuleOpacity );
|
|
|
|
// Add all face triangles
|
|
for( unsigned int faceIdx = 0;
|
|
faceIdx < mesh.m_FaceIdxSize;
|
|
faceIdx += 3 )
|
|
{
|
|
const unsigned int idx0 = mesh.m_FaceIdx[faceIdx + 0];
|
|
const unsigned int idx1 = mesh.m_FaceIdx[faceIdx + 1];
|
|
const unsigned int idx2 = mesh.m_FaceIdx[faceIdx + 2];
|
|
|
|
wxASSERT( idx0 < mesh.m_VertexSize );
|
|
wxASSERT( idx1 < mesh.m_VertexSize );
|
|
wxASSERT( idx2 < mesh.m_VertexSize );
|
|
|
|
if( ( idx0 < mesh.m_VertexSize ) &&
|
|
( idx1 < mesh.m_VertexSize ) &&
|
|
( idx2 < mesh.m_VertexSize ) )
|
|
{
|
|
const SFVEC3F &v0 = mesh.m_Positions[idx0];
|
|
const SFVEC3F &v1 = mesh.m_Positions[idx1];
|
|
const SFVEC3F &v2 = mesh.m_Positions[idx2];
|
|
|
|
const SFVEC3F &n0 = mesh.m_Normals[idx0];
|
|
const SFVEC3F &n1 = mesh.m_Normals[idx1];
|
|
const SFVEC3F &n2 = mesh.m_Normals[idx2];
|
|
|
|
// Transform vertex with the model matrix
|
|
const SFVEC3F vt0 = SFVEC3F( aModelMatrix * glm::vec4( v0, 1.0f) );
|
|
const SFVEC3F vt1 = SFVEC3F( aModelMatrix * glm::vec4( v1, 1.0f) );
|
|
const SFVEC3F vt2 = SFVEC3F( aModelMatrix * glm::vec4( v2, 1.0f) );
|
|
|
|
const SFVEC3F nt0 = glm::normalize( SFVEC3F( normalMatrix * n0 ) );
|
|
const SFVEC3F nt1 = glm::normalize( SFVEC3F( normalMatrix * n1 ) );
|
|
const SFVEC3F nt2 = glm::normalize( SFVEC3F( normalMatrix * n2 ) );
|
|
|
|
CTRIANGLE *newTriangle = new CTRIANGLE( vt0, vt2, vt1,
|
|
nt0, nt2, nt1 );
|
|
|
|
m_object_container.Add( newTriangle );
|
|
newTriangle->SetMaterial( (const CMATERIAL *)&blinn_material );
|
|
|
|
newTriangle->SetModelTransparency( moduleTransparency );
|
|
|
|
if( mesh.m_Color == NULL )
|
|
{
|
|
const SFVEC3F diffuseColor =
|
|
a3DModel->m_Materials[mesh.m_MaterialIdx].m_Diffuse;
|
|
|
|
if( m_boardAdapter.MaterialModeGet() == MATERIAL_MODE::CAD_MODE )
|
|
newTriangle->SetColor( ConvertSRGBToLinear( MaterialDiffuseToColorCAD( diffuseColor ) ) );
|
|
else
|
|
newTriangle->SetColor( ConvertSRGBToLinear( diffuseColor ) );
|
|
}
|
|
else
|
|
{
|
|
if( m_boardAdapter.MaterialModeGet() == MATERIAL_MODE::CAD_MODE )
|
|
newTriangle->SetColor( ConvertSRGBToLinear( MaterialDiffuseToColorCAD( mesh.m_Color[idx0] ) ),
|
|
ConvertSRGBToLinear( MaterialDiffuseToColorCAD( mesh.m_Color[idx1] ) ),
|
|
ConvertSRGBToLinear( MaterialDiffuseToColorCAD( mesh.m_Color[idx2] ) ) );
|
|
else
|
|
newTriangle->SetColor( ConvertSRGBToLinear( mesh.m_Color[idx0] ),
|
|
ConvertSRGBToLinear( mesh.m_Color[idx1] ),
|
|
ConvertSRGBToLinear( mesh.m_Color[idx2] ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|