ADDED: PCB 3D image raytracing rendering from CLI.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/3691
This commit is contained in:
Alex Shvartzkop 2024-03-03 21:11:42 +03:00 committed by dsa-t
parent 7721bdc441
commit f6f0b9a661
31 changed files with 1357 additions and 359 deletions

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 2023 CERN
* Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2024 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
@ -32,7 +32,7 @@
#include "../3d_rendering/raytracing/accelerators/container_3d.h"
#include "../3d_rendering/raytracing/shapes3D/bbox_3d.h"
#include <gal/3d/camera.h>
#include "../3d_enums.h"
#include <3d_enums.h>
#include "../3d_cache/3d_cache.h"
#include "../common_ogl/ogl_attr_list.h"
#include "../3d_viewer/eda_3d_viewer_settings.h"

View File

@ -29,7 +29,7 @@
#include "../common_ogl/ogl_utils.h"
#include "eda_3d_canvas.h"
#include <eda_3d_viewer_frame.h>
#include <3d_rendering/raytracing/render_3d_raytrace.h>
#include <3d_rendering/raytracing/render_3d_raytrace_gl.h>
#include <3d_rendering/opengl/render_3d_opengl.h>
#include <3d_viewer_id.h>
#include <advanced_config.h>
@ -119,7 +119,7 @@ EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttrib
m_is_currently_painting.clear();
m_3d_render_raytracing = new RENDER_3D_RAYTRACE( this, m_boardAdapter, m_camera );
m_3d_render_raytracing = new RENDER_3D_RAYTRACE_GL( this, m_boardAdapter, m_camera );
m_3d_render_opengl = new RENDER_3D_OPENGL( this, m_boardAdapter, m_camera );
wxASSERT( m_3d_render_raytracing != nullptr );
@ -979,66 +979,24 @@ bool EDA_3D_CANVAS::SetView3D( VIEW3D_TYPE aRequestedView )
return true;
case VIEW3D_TYPE::VIEW3D_RIGHT:
m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
m_camera.SetT0_and_T1_current_T();
m_camera.Reset_T1();
m_camera.RotateZ_T1( glm::radians( -90.0f ) );
m_camera.RotateX_T1( glm::radians( -90.0f ) );
request_start_moving_camera();
return true;
case VIEW3D_TYPE::VIEW3D_LEFT:
m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
m_camera.SetT0_and_T1_current_T();
m_camera.Reset_T1();
m_camera.RotateZ_T1( glm::radians( 90.0f ) );
m_camera.RotateX_T1( glm::radians( -90.0f ) );
request_start_moving_camera();
return true;
case VIEW3D_TYPE::VIEW3D_FRONT:
m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
m_camera.SetT0_and_T1_current_T();
m_camera.Reset_T1();
m_camera.RotateX_T1( glm::radians( -90.0f ) );
request_start_moving_camera();
return true;
case VIEW3D_TYPE::VIEW3D_BACK:
case VIEW3D_TYPE::VIEW3D_FLIP:
m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
m_camera.SetT0_and_T1_current_T();
m_camera.Reset_T1();
m_camera.RotateX_T1( glm::radians( -90.0f ) );
// The rotation angle should be 180.
// We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
// using 180 deg if the previous rotated position was already 180 deg
m_camera.RotateZ_T1( glm::radians( 179.999f ) );
m_camera.ViewCommand_T1( aRequestedView );
request_start_moving_camera();
return true;
case VIEW3D_TYPE::VIEW3D_TOP:
m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
m_camera.SetT0_and_T1_current_T();
m_camera.Reset_T1();
request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
return true;
case VIEW3D_TYPE::VIEW3D_BOTTOM:
m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
m_camera.SetT0_and_T1_current_T();
m_camera.Reset_T1();
m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
m_camera.ViewCommand_T1( aRequestedView );
request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
return true;
case VIEW3D_TYPE::VIEW3D_FLIP:
m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
m_camera.SetT0_and_T1_current_T();
m_camera.RotateY_T1( glm::radians( 179.999f ) );
request_start_moving_camera();
return true;
default:
return false;
}

View File

@ -2,7 +2,7 @@
* 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-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2024 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
@ -38,7 +38,7 @@
class WX_INFOBAR;
class wxStatusBar;
class BOARD;
class RENDER_3D_RAYTRACE;
class RENDER_3D_RAYTRACE_GL;
class RENDER_3D_OPENGL;
@ -97,7 +97,7 @@ public:
}
/**
* @return the current render ( a RENDER_3D_RAYTRACE* or a RENDER_3D_OPENGL* render )
* @return the current render ( a RENDER_3D_RAYTRACE_GL* or a RENDER_3D_OPENGL* render )
*/
RENDER_3D_BASE* GetCurrentRender() const { return m_3d_render; }
@ -311,7 +311,7 @@ private:
BOARD_ADAPTER& m_boardAdapter; // Pre-computed 3D info and settings
RENDER_3D_BASE* m_3d_render;
RENDER_3D_RAYTRACE* m_3d_render_raytracing;
RENDER_3D_RAYTRACE_GL* m_3d_render_raytracing;
RENDER_3D_OPENGL* m_3d_render_opengl;
bool m_opengl_supports_raytracing;

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2020 Oleg Endo <olegendo@gcc.gnu.org>
* Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2015-2024 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
@ -30,7 +30,7 @@
#include <plugins/3dapi/c3dmodel.h>
#include "../../common_ogl/openGL_includes.h"
#include "../raytracing/shapes3D/bbox_3d.h"
#include "../../3d_enums.h"
#include <3d_enums.h>
#include <wx/chartype.h>

View File

@ -47,7 +47,8 @@
RENDER_3D_OPENGL::RENDER_3D_OPENGL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdapter,
CAMERA& aCamera ) :
RENDER_3D_BASE( aCanvas, aAdapter, aCamera )
RENDER_3D_BASE( aAdapter, aCamera ),
m_canvas( aCanvas )
{
wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
@ -454,7 +455,7 @@ bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
REPORTER* aWarningReporter )
{
// Initialize OpenGL
if( !m_is_opengl_initialized )
if( !m_canvasInitialized )
{
if( !initializeOpenGL() )
return false;
@ -823,7 +824,7 @@ bool RENDER_3D_OPENGL::initializeOpenGL()
// Use this mode if you want see the triangle lines (debug proposes)
//glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
m_is_opengl_initialized = true;
m_canvasInitialized = true;
return true;
}
@ -1021,7 +1022,7 @@ void RENDER_3D_OPENGL::get3dModelsFromFootprint( std::list<MODELTORENDER> &aDstR
const SFVEC3F offset = SFVEC3F( sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z );
const SFVEC3F rotation = SFVEC3F( sM.m_Rotation.x, sM.m_Rotation.y, sM.m_Rotation.z );
const SFVEC3F scale = SFVEC3F( sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z );
std::vector<float> key = { offset.x, offset.y, offset.z,
rotation.x, rotation.y, rotation.z,
scale.x, scale.y, scale.z };
@ -1134,7 +1135,7 @@ void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatr
const SFVEC3F bBoxWorld = mtr.m_modelWorldMat * glm::vec4( bBoxCenter, 1.0f );
const float distanceToCamera = glm::length( cameraPos - bBoxWorld );
transparentModelList.emplace_back( &mtr, distanceToCamera );
}
@ -1224,7 +1225,7 @@ void RENDER_3D_OPENGL::renderModel( const glm::mat4 &aCameraViewMatrix,
aModelToRender.m_model->DrawBbox();
glEnable( GL_LIGHTING );
if( !wasBlendEnabled )
glDisable( GL_BLEND );
}

View File

@ -2,7 +2,7 @@
* 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) 2015-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2015-2024 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
@ -208,6 +208,8 @@ private:
SMATERIAL m_GrayMaterial;
} m_materials;
EDA_3D_CANVAS* m_canvas;
MAP_OGL_DISP_LISTS m_layers;
OPENGL_RENDER_LIST* m_platedPadsFront;
OPENGL_RENDER_LIST* m_platedPadsBack;

View File

@ -23,7 +23,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "render_3d_raytrace.h"
#include "render_3d_raytrace_base.h"
#include "shapes3D/plane_3d.h"
#include "shapes3D/round_segment_3d.h"
#include "shapes3D/layer_item_3d.h"
@ -72,7 +72,7 @@ static float TransparencyControl( float aGrayColorValue, float aTransparency )
#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
void RENDER_3D_RAYTRACE::setupMaterials()
void RENDER_3D_RAYTRACE_BASE::setupMaterials()
{
MATERIAL::SetDefaultRefractionRayCount( m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_refractions );
MATERIAL::SetDefaultReflectionRayCount( m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_reflections );
@ -176,7 +176,7 @@ void RENDER_3D_RAYTRACE::setupMaterials()
}
void RENDER_3D_RAYTRACE::createObject( CONTAINER_3D& aDstContainer, const OBJECT_2D* aObject2D,
void RENDER_3D_RAYTRACE_BASE::createObject( CONTAINER_3D& aDstContainer, const OBJECT_2D* aObject2D,
float aZMin, float aZMax, const MATERIAL* aMaterial,
const SFVEC3F& aObjColor )
{
@ -227,7 +227,7 @@ void RENDER_3D_RAYTRACE::createObject( CONTAINER_3D& aDstContainer, const OBJECT
}
void RENDER_3D_RAYTRACE::createItemsFromContainer( const BVH_CONTAINER_2D* aContainer2d,
void RENDER_3D_RAYTRACE_BASE::createItemsFromContainer( const BVH_CONTAINER_2D* aContainer2d,
PCB_LAYER_ID aLayer_id,
const MATERIAL* aMaterialLayer,
const SFVEC3F& aLayerColor,
@ -358,7 +358,7 @@ void RENDER_3D_RAYTRACE::createItemsFromContainer( const BVH_CONTAINER_2D* aCont
extern void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline );
void RENDER_3D_RAYTRACE::Reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter,
void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter,
bool aOnlyLoadCopperAndShapes )
{
m_reloadRequested = false;
@ -962,7 +962,7 @@ void RENDER_3D_RAYTRACE::Reload( REPORTER* aStatusReporter, REPORTER* aWarningRe
}
void RENDER_3D_RAYTRACE::insertHole( const PCB_VIA* aVia )
void RENDER_3D_RAYTRACE_BASE::insertHole( const PCB_VIA* aVia )
{
PCB_LAYER_ID top_layer, bottom_layer;
int radiusBUI = ( aVia->GetDrillValue() / 2 );
@ -993,7 +993,7 @@ void RENDER_3D_RAYTRACE::insertHole( const PCB_VIA* aVia )
}
void RENDER_3D_RAYTRACE::insertHole( const PAD* aPad )
void RENDER_3D_RAYTRACE_BASE::insertHole( const PAD* aPad )
{
const OBJECT_2D* object2d_A = nullptr;
@ -1164,7 +1164,7 @@ void RENDER_3D_RAYTRACE::insertHole( const PAD* aPad )
}
void RENDER_3D_RAYTRACE::addPadsAndVias()
void RENDER_3D_RAYTRACE_BASE::addPadsAndVias()
{
if( !m_boardAdapter.GetBoard() )
return;
@ -1193,7 +1193,7 @@ void RENDER_3D_RAYTRACE::addPadsAndVias()
}
void RENDER_3D_RAYTRACE::load3DModels( CONTAINER_3D& aDstContainer, bool aSkipMaterialInformation )
void RENDER_3D_RAYTRACE_BASE::load3DModels( CONTAINER_3D& aDstContainer, bool aSkipMaterialInformation )
{
if( !m_boardAdapter.GetBoard() )
return;
@ -1315,7 +1315,7 @@ void RENDER_3D_RAYTRACE::load3DModels( CONTAINER_3D& aDstContainer, bool aSkipMa
}
MODEL_MATERIALS* RENDER_3D_RAYTRACE::getModelMaterial( const S3DMODEL* a3DModel )
MODEL_MATERIALS* RENDER_3D_RAYTRACE_BASE::getModelMaterial( const S3DMODEL* a3DModel )
{
MODEL_MATERIALS* materialVector;
@ -1418,7 +1418,7 @@ MODEL_MATERIALS* RENDER_3D_RAYTRACE::getModelMaterial( const S3DMODEL* a3DModel
}
void RENDER_3D_RAYTRACE::addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
void RENDER_3D_RAYTRACE_BASE::addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
const glm::mat4& aModelMatrix, float aFPOpacity,
bool aSkipMaterialInformation, BOARD_ITEM* aBoardItem )
{

View File

@ -22,32 +22,28 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <gal/opengl/kiglew.h> // Must be included first
#include <algorithm>
#include <atomic>
#include <chrono>
#include <thread>
#include "render_3d_raytrace.h"
#include "render_3d_raytrace_base.h"
#include "mortoncodes.h"
#include "../color_rgba.h"
#include "3d_fastmath.h"
#include "3d_math.h"
#include "../common_ogl/ogl_utils.h"
#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
#include <wx/log.h>
RENDER_3D_RAYTRACE::RENDER_3D_RAYTRACE( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdapter, CAMERA& aCamera ) :
RENDER_3D_BASE( aCanvas, aAdapter, aCamera ),
m_postShaderSsao( aCamera )
RENDER_3D_RAYTRACE_BASE::RENDER_3D_RAYTRACE_BASE( BOARD_ADAPTER& aAdapter, CAMERA& aCamera ) :
RENDER_3D_BASE( aAdapter, aCamera ),
m_postShaderSsao( aCamera )
{
wxLogTrace( m_logTrace, wxT( "RENDER_3D_RAYTRACE::RENDER_3D_RAYTRACE" ) );
wxLogTrace( m_logTrace, wxT( "RENDER_3D_RAYTRACE_BASE::RENDER_3D_RAYTRACE_BASE" ) );
m_openglSupportsVertexBufferObjects = false;
m_pboId = GL_NONE;
m_pboDataSize = 0;
//m_pboId = GL_NONE;
//m_pboDataSize = 0;
m_accelerator = nullptr;
m_convertedDummyBlockCount = 0;
m_converted2dRoundSegmentCount = 0;
@ -62,6 +58,7 @@ RENDER_3D_RAYTRACE::RENDER_3D_RAYTRACE( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& a
m_xoffset = 0;
m_yoffset = 0;
m_is_canvas_initialized = false;
m_isPreview = false;
m_renderState = RT_RENDER_STATE_MAX; // Set to an initial invalid state
m_renderStartTime = 0;
@ -69,9 +66,9 @@ RENDER_3D_RAYTRACE::RENDER_3D_RAYTRACE( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& a
}
RENDER_3D_RAYTRACE::~RENDER_3D_RAYTRACE()
RENDER_3D_RAYTRACE_BASE::~RENDER_3D_RAYTRACE_BASE()
{
wxLogTrace( m_logTrace, wxT( "RENDER_3D_RAYTRACE::~RENDER_3D_RAYTRACE" ) );
wxLogTrace( m_logTrace, wxT( "RENDER_3D_RAYTRACE_BASE::~RENDER_3D_RAYTRACE_BASE" ) );
delete m_accelerator;
m_accelerator = nullptr;
@ -84,43 +81,16 @@ RENDER_3D_RAYTRACE::~RENDER_3D_RAYTRACE()
delete[] m_shaderBuffer;
m_shaderBuffer = nullptr;
deletePbo();
}
int RENDER_3D_RAYTRACE::GetWaitForEditingTimeOut()
int RENDER_3D_RAYTRACE_BASE::GetWaitForEditingTimeOut()
{
return 200; // ms
}
void RENDER_3D_RAYTRACE::deletePbo()
{
// Delete PBO if it was created
if( m_openglSupportsVertexBufferObjects )
{
if( glIsBufferARB( m_pboId ) )
glDeleteBuffers( 1, &m_pboId );
m_pboId = GL_NONE;
}
}
void RENDER_3D_RAYTRACE::SetCurWindowSize( const wxSize& aSize )
{
if( m_windowSize != aSize )
{
m_windowSize = aSize;
glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
initializeNewWindowSize();
}
}
void RENDER_3D_RAYTRACE::restartRenderState()
void RENDER_3D_RAYTRACE_BASE::restartRenderState()
{
m_renderStartTime = GetRunningMicroSecs();
@ -145,151 +115,13 @@ static inline void SetPixel( GLubyte* p, const COLOR_RGBA& v )
}
static inline SFVEC4F premultiplyAlpha( const SFVEC4F& aInput )
SFVEC4F RENDER_3D_RAYTRACE_BASE::premultiplyAlpha( const SFVEC4F& aInput )
{
return SFVEC4F( aInput.r * aInput.a, aInput.g * aInput.a, aInput.b * aInput.a, aInput.a );
}
bool RENDER_3D_RAYTRACE::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
REPORTER* aWarningReporter )
{
bool requestRedraw = false;
// Initialize openGL if need
if( !m_is_opengl_initialized )
{
if( !initializeOpenGL() )
return false;
//aIsMoving = true;
requestRedraw = true;
// It will assign the first time the windows size, so it will now
// revert to preview mode the first time the Redraw is called
m_oldWindowsSize = m_windowSize;
initializeBlockPositions();
}
std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
// Reload board if it was requested
if( m_reloadRequested )
{
if( aStatusReporter )
aStatusReporter->Report( _( "Loading..." ) );
//aIsMoving = true;
requestRedraw = true;
Reload( aStatusReporter, aWarningReporter, false );
}
// Recalculate constants if windows size was changed
if( m_windowSize != m_oldWindowsSize )
{
m_oldWindowsSize = m_windowSize;
aIsMoving = true;
requestRedraw = true;
initializeBlockPositions();
}
// Clear buffers
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClearDepth( 1.0f );
glClearStencil( 0x00 );
glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
// 4-byte pixel alignment
glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
glDisable( GL_STENCIL_TEST );
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
glDisable( GL_DEPTH_TEST );
glDisable( GL_TEXTURE_2D );
glDisable( GL_BLEND );
glDisable( GL_MULTISAMPLE );
const bool was_camera_changed = m_camera.ParametersChanged();
if( requestRedraw || aIsMoving || was_camera_changed )
m_renderState = RT_RENDER_STATE_MAX; // Set to an invalid state,
// so it will restart again latter
// This will only render if need, otherwise it will redraw the PBO on the screen again
if( aIsMoving || was_camera_changed )
{
// Set head light (camera view light) with the opposite direction of the camera
if( m_cameraLight )
m_cameraLight->SetDirection( -m_camera.GetDir() );
OglDrawBackground( premultiplyAlpha( m_boardAdapter.m_BgColorTop ),
premultiplyAlpha( m_boardAdapter.m_BgColorBot ) );
// Bind PBO
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
// Get the PBO pixel pointer to write the data
GLubyte* ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB,
GL_WRITE_ONLY_ARB );
if( ptrPBO )
{
renderPreview( ptrPBO );
// release pointer to mapping buffer, this initialize the coping to PBO
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
}
glWindowPos2i( m_xoffset, m_yoffset );
}
else
{
// Bind PBO
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
if( m_renderState != RT_RENDER_STATE_FINISH )
{
// Get the PBO pixel pointer to write the data
GLubyte* ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB,
GL_WRITE_ONLY_ARB );
if( ptrPBO )
{
render( ptrPBO, aStatusReporter );
if( m_renderState != RT_RENDER_STATE_FINISH )
requestRedraw = true;
// release pointer to mapping buffer, this initialize the coping to PBO
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
}
}
if( m_renderState == RT_RENDER_STATE_FINISH )
{
glClear( GL_COLOR_BUFFER_BIT );
}
glWindowPos2i( m_xoffset, m_yoffset );
}
// This way it will blend the progress rendering with the last buffer. eg:
// if it was called after a openGL.
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_ALPHA_TEST );
glDrawPixels( m_realBufferSize.x, m_realBufferSize.y, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );
return requestRedraw;
}
void RENDER_3D_RAYTRACE::render( GLubyte* ptrPBO, REPORTER* aStatusReporter )
void RENDER_3D_RAYTRACE_BASE::render( GLubyte* ptrPBO, REPORTER* aStatusReporter )
{
if( ( m_renderState == RT_RENDER_STATE_FINISH ) || ( m_renderState >= RT_RENDER_STATE_MAX ) )
{
@ -342,14 +174,14 @@ void RENDER_3D_RAYTRACE::render( GLubyte* ptrPBO, REPORTER* aStatusReporter )
if( aStatusReporter && ( m_renderState == RT_RENDER_STATE_FINISH ) )
{
// Calculation time in seconds
const double elapsed_time = (double)( GetRunningMicroSecs() - m_renderStartTime ) / 1e6;
const double elapsed_time = (double) ( GetRunningMicroSecs() - m_renderStartTime ) / 1e6;
aStatusReporter->Report( wxString::Format( _( "Rendering time %.3f s" ), elapsed_time ) );
}
}
void RENDER_3D_RAYTRACE::renderTracing( GLubyte* ptrPBO, REPORTER* aStatusReporter )
void RENDER_3D_RAYTRACE_BASE::renderTracing( GLubyte* ptrPBO, REPORTER* aStatusReporter )
{
m_isPreview = false;
@ -364,6 +196,8 @@ void RENDER_3D_RAYTRACE::renderTracing( GLubyte* ptrPBO, REPORTER* aStatusReport
std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
m_blockPositions.size() );
const int timeLimit = m_blockPositions.size() > 40000 ? 500 : 200;
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread( [&]()
@ -380,8 +214,10 @@ void RENDER_3D_RAYTRACE::renderTracing( GLubyte* ptrPBO, REPORTER* aStatusReport
// Check if it spend already some time render and request to exit
// to display the progress
if( std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - startTime ).count() > 150 )
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - startTime );
if( diff.count() > timeLimit )
breakLoop = true;
}
}
@ -459,7 +295,7 @@ SFVEC4F ConvertSRGBAToLinear( const SFVEC4F& aSRGBAcolor )
#endif
void RENDER_3D_RAYTRACE::renderFinalColor( GLubyte* ptrPBO, const SFVEC4F& rgbColor,
void RENDER_3D_RAYTRACE_BASE::renderFinalColor( GLubyte* ptrPBO, const SFVEC4F& rgbColor,
bool applyColorSpaceConversion )
{
SFVEC4F color = rgbColor;
@ -494,7 +330,7 @@ static void HITINFO_PACKET_init( HITINFO_PACKET* aHitPacket )
}
void RENDER_3D_RAYTRACE::renderRayPackets( const SFVEC4F* bgColorY, const RAY* aRayPkt,
void RENDER_3D_RAYTRACE_BASE::renderRayPackets( const SFVEC4F* bgColorY, const RAY* aRayPkt,
HITINFO_PACKET* aHitPacket, bool is_testShadow,
SFVEC4F* aOutHitColor )
{
@ -516,7 +352,7 @@ void RENDER_3D_RAYTRACE::renderRayPackets( const SFVEC4F* bgColorY, const RAY* a
}
void RENDER_3D_RAYTRACE::renderAntiAliasPackets( const SFVEC4F* aBgColorY,
void RENDER_3D_RAYTRACE_BASE::renderAntiAliasPackets( const SFVEC4F* aBgColorY,
const HITINFO_PACKET* aHitPck_X0Y0,
const HITINFO_PACKET* aHitPck_AA_X1Y1,
const RAY* aRayPck, SFVEC4F* aOutHitColor )
@ -640,7 +476,7 @@ void RENDER_3D_RAYTRACE::renderAntiAliasPackets( const SFVEC4F* aBgColorY,
#define DISP_FACTOR 0.075f
void RENDER_3D_RAYTRACE::renderBlockTracing( GLubyte* ptrPBO, signed int iBlock )
void RENDER_3D_RAYTRACE_BASE::renderBlockTracing( GLubyte* ptrPBO, signed int iBlock )
{
// Initialize ray packets
const SFVEC2UI& blockPos = m_blockPositions[iBlock];
@ -851,7 +687,7 @@ void RENDER_3D_RAYTRACE::renderBlockTracing( GLubyte* ptrPBO, signed int iBlock
}
void RENDER_3D_RAYTRACE::postProcessShading( GLubyte* /* ptrPBO */, REPORTER* aStatusReporter )
void RENDER_3D_RAYTRACE_BASE::postProcessShading( GLubyte* /* ptrPBO */, REPORTER* aStatusReporter )
{
if( m_boardAdapter.m_Cfg->m_Render.raytrace_post_processing )
{
@ -903,7 +739,7 @@ void RENDER_3D_RAYTRACE::postProcessShading( GLubyte* /* ptrPBO */, REPORTER* aS
}
void RENDER_3D_RAYTRACE::postProcessBlurFinish( GLubyte* ptrPBO, REPORTER* /* aStatusReporter */ )
void RENDER_3D_RAYTRACE_BASE::postProcessBlurFinish( GLubyte* ptrPBO, REPORTER* /* aStatusReporter */ )
{
if( m_boardAdapter.m_Cfg->m_Render.raytrace_post_processing )
{
@ -960,7 +796,7 @@ void RENDER_3D_RAYTRACE::postProcessBlurFinish( GLubyte* ptrPBO, REPORTER* /* aS
}
void RENDER_3D_RAYTRACE::renderPreview( GLubyte* ptrPBO )
void RENDER_3D_RAYTRACE_BASE::renderPreview( GLubyte* ptrPBO )
{
m_isPreview = true;
@ -1558,7 +1394,7 @@ void RENDER_3D_RAYTRACE::renderPreview( GLubyte* ptrPBO )
#define USE_EXPERIMENTAL_SOFT_SHADOWS 1
SFVEC4F RENDER_3D_RAYTRACE::shadeHit( const SFVEC4F& aBgColor, const RAY& aRay, HITINFO& aHitInfo,
SFVEC4F RENDER_3D_RAYTRACE_BASE::shadeHit( const SFVEC4F& aBgColor, const RAY& aRay, HITINFO& aHitInfo,
bool aIsInsideObject, unsigned int aRecursiveLevel,
bool is_testShadow ) const
{
@ -1734,6 +1570,10 @@ SFVEC4F RENDER_3D_RAYTRACE::shadeHit( const SFVEC4F& aBgColor, const RAY& aRay,
sum_color += add;
}
else
{
sum_color += aBgColor;
}
}
outColor += (sum_color / SFVEC4F( (float)reflection_number_of_samples) );
@ -1825,49 +1665,6 @@ SFVEC4F RENDER_3D_RAYTRACE::shadeHit( const SFVEC4F& aBgColor, const RAY& aRay,
}
void RENDER_3D_RAYTRACE::initializeNewWindowSize()
{
initPbo();
}
void RENDER_3D_RAYTRACE::initPbo()
{
if( GLEW_ARB_pixel_buffer_object )
{
m_openglSupportsVertexBufferObjects = true;
// Try to delete vbo if it was already initialized
deletePbo();
// Learn about Pixel buffer objects at:
// http://www.songho.ca/opengl/gl_pbo.html
// http://web.eecs.umich.edu/~sugih/courses/eecs487/lectures/25-PBO+Mipmapping.pdf
// "create 2 pixel buffer objects, you need to delete them when program exits.
// glBufferDataARB with NULL pointer reserves only memory space."
// This sets the number of RGBA pixels
m_pboDataSize = m_realBufferSize.x * m_realBufferSize.y * 4;
glGenBuffersARB( 1, &m_pboId );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboDataSize, 0, GL_STREAM_DRAW_ARB );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );
wxLogTrace( m_logTrace,
wxT( "RENDER_3D_RAYTRACE:: GLEW_ARB_pixel_buffer_object is supported" ) );
}
}
bool RENDER_3D_RAYTRACE::initializeOpenGL()
{
m_is_opengl_initialized = true;
return true;
}
static float distance( const SFVEC2UI& a, const SFVEC2UI& b )
{
const float dx = (float) a.x - (float) b.x;
@ -1876,7 +1673,7 @@ static float distance( const SFVEC2UI& a, const SFVEC2UI& b )
}
void RENDER_3D_RAYTRACE::initializeBlockPositions()
void RENDER_3D_RAYTRACE_BASE::initializeBlockPositions()
{
m_realBufferSize = SFVEC2UI( 0 );
@ -1959,7 +1756,7 @@ void RENDER_3D_RAYTRACE::initializeBlockPositions()
}
BOARD_ITEM* RENDER_3D_RAYTRACE::IntersectBoardItem( const RAY& aRay )
BOARD_ITEM* RENDER_3D_RAYTRACE_BASE::IntersectBoardItem( const RAY& aRay )
{
HITINFO hitInfo;
hitInfo.m_tHit = std::numeric_limits<float>::infinity();

View File

@ -22,10 +22,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef RENDER_3D_RAYTRACE_H
#define RENDER_3D_RAYTRACE_H
#ifndef RENDER_3D_RAYTRACE_BASE_H
#define RENDER_3D_RAYTRACE_BASE_H
#include "../../common_ogl/openGL_includes.h"
#include "accelerators/container_3d.h"
#include "accelerators/accelerator_3d.h"
#include "../render_3d_base.h"
@ -52,19 +51,16 @@ typedef enum
} RT_RENDER_STATE;
class RENDER_3D_RAYTRACE : public RENDER_3D_BASE
class RENDER_3D_RAYTRACE_BASE : public RENDER_3D_BASE
{
public:
// TODO: Take into account board thickness so that the camera won't move inside of the board
// when facing it perpendicularly.
static constexpr float MIN_DISTANCE_IU = 4 * PCB_IU_PER_MM;
explicit RENDER_3D_RAYTRACE( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdapter, CAMERA& aCamera );
explicit RENDER_3D_RAYTRACE_BASE( BOARD_ADAPTER& aAdapter, CAMERA& aCamera );
~RENDER_3D_RAYTRACE();
void SetCurWindowSize( const wxSize& aSize ) override;
bool Redraw( bool aIsMoving, REPORTER* aStatusReporter, REPORTER* aWarningReporter ) override;
~RENDER_3D_RAYTRACE_BASE();
int GetWaitForEditingTimeOut() override;
@ -73,11 +69,9 @@ public:
BOARD_ITEM *IntersectBoardItem( const RAY& aRay );
private:
bool initializeOpenGL();
void initializeNewWindowSize();
void initPbo();
void deletePbo();
protected:
virtual void initPbo() = 0;
virtual void deletePbo() = 0;
void createItemsFromContainer( const BVH_CONTAINER_2D* aContainer2d, PCB_LAYER_ID aLayer_id,
const MATERIAL* aMaterialLayer, const SFVEC3F& aLayerColor,
float aLayerZOffset );
@ -128,6 +122,8 @@ private:
void render( GLubyte* ptrPBO, REPORTER* aStatusReporter );
void renderPreview( GLubyte* ptrPBO );
static SFVEC4F premultiplyAlpha( const SFVEC4F& aInput );
struct
{
BLINN_PHONG_MATERIAL m_Paste;
@ -148,6 +144,7 @@ private:
BRUSHED_METAL_NORMAL m_brushedMetalMaterial;
SILK_SCREEN_NORMAL m_silkScreenMaterial;
bool m_is_canvas_initialized;
bool m_isPreview;
/// State used on quality render
@ -165,10 +162,8 @@ private:
DIRECTIONAL_LIGHT* m_cameraLight;
bool m_openglSupportsVertexBufferObjects;
GLuint m_pboId;
GLuint m_pboDataSize;
/*GLuint m_pboId;
GLuint m_pboDataSize;*/
CONTAINER_3D m_objectContainer;
@ -224,4 +219,4 @@ extern SFVEC4F ConvertSRGBAToLinear( const SFVEC4F& aSRGBAcolor );
#define ConvertSRGBAToLinear( v ) ( v )
#endif
#endif // RENDER_3D_RAYTRACE_H
#endif // RENDER_3D_RAYTRACE_BASE_H

View File

@ -0,0 +1,243 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 2024 Alex Shvartzkop <dudesuchamazing@gmail.com>
* Copyright (C) 2015-2024 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
*/
#include <gal/opengl/kiglew.h> // Must be included first
#include <algorithm>
#include <atomic>
#include <chrono>
#include <thread>
#include "render_3d_raytrace_gl.h"
#include "../common_ogl/ogl_utils.h"
#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
#include <wx/log.h>
RENDER_3D_RAYTRACE_GL::RENDER_3D_RAYTRACE_GL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdapter, CAMERA& aCamera ) :
RENDER_3D_RAYTRACE_BASE( aAdapter, aCamera )
{
wxLogTrace( m_logTrace, wxT( "RENDER_3D_RAYTRACE_GL::RENDER_3D_RAYTRACE_GL" ) );
m_openglSupportsVertexBufferObjects = false;
m_pboId = GL_NONE;
m_pboDataSize = 0;
}
RENDER_3D_RAYTRACE_GL::~RENDER_3D_RAYTRACE_GL()
{
deletePbo();
}
void RENDER_3D_RAYTRACE_GL::deletePbo()
{
// Delete PBO if it was created
if( m_openglSupportsVertexBufferObjects )
{
if( glIsBufferARB( m_pboId ) )
glDeleteBuffers( 1, &m_pboId );
m_pboId = GL_NONE;
}
}
void RENDER_3D_RAYTRACE_GL::SetCurWindowSize( const wxSize& aSize )
{
if( m_windowSize != aSize )
{
m_windowSize = aSize;
glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
initPbo();
}
}
bool RENDER_3D_RAYTRACE_GL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
REPORTER* aWarningReporter )
{
bool requestRedraw = false;
// Initialize openGL if need
if( !m_canvasInitialized )
{
m_canvasInitialized = true;
//aIsMoving = true;
requestRedraw = true;
// It will assign the first time the windows size, so it will now
// revert to preview mode the first time the Redraw is called
m_oldWindowsSize = m_windowSize;
initializeBlockPositions();
}
std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
// Reload board if it was requested
if( m_reloadRequested )
{
if( aStatusReporter )
aStatusReporter->Report( _( "Loading..." ) );
//aIsMoving = true;
requestRedraw = true;
Reload( aStatusReporter, aWarningReporter, false );
}
// Recalculate constants if windows size was changed
if( m_windowSize != m_oldWindowsSize )
{
m_oldWindowsSize = m_windowSize;
aIsMoving = true;
requestRedraw = true;
initializeBlockPositions();
}
// Clear buffers
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClearDepth( 1.0f );
glClearStencil( 0x00 );
glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
// 4-byte pixel alignment
glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
glDisable( GL_STENCIL_TEST );
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
glDisable( GL_DEPTH_TEST );
glDisable( GL_TEXTURE_2D );
glDisable( GL_BLEND );
glDisable( GL_MULTISAMPLE );
const bool was_camera_changed = m_camera.ParametersChanged();
if( requestRedraw || aIsMoving || was_camera_changed )
m_renderState = RT_RENDER_STATE_MAX; // Set to an invalid state,
// so it will restart again latter
// This will only render if need, otherwise it will redraw the PBO on the screen again
if( aIsMoving || was_camera_changed )
{
// Set head light (camera view light) with the opposite direction of the camera
if( m_cameraLight )
m_cameraLight->SetDirection( -m_camera.GetDir() );
OglDrawBackground( premultiplyAlpha( m_boardAdapter.m_BgColorTop ),
premultiplyAlpha( m_boardAdapter.m_BgColorBot ) );
// Bind PBO
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
// Get the PBO pixel pointer to write the data
GLubyte* ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB,
GL_WRITE_ONLY_ARB );
if( ptrPBO )
{
renderPreview( ptrPBO );
// release pointer to mapping buffer, this initialize the coping to PBO
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
}
glWindowPos2i( m_xoffset, m_yoffset );
}
else
{
// Bind PBO
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
if( m_renderState != RT_RENDER_STATE_FINISH )
{
// Get the PBO pixel pointer to write the data
GLubyte* ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB,
GL_WRITE_ONLY_ARB );
if( ptrPBO )
{
render( ptrPBO, aStatusReporter );
if( m_renderState != RT_RENDER_STATE_FINISH )
requestRedraw = true;
// release pointer to mapping buffer, this initialize the coping to PBO
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
}
}
if( m_renderState == RT_RENDER_STATE_FINISH )
{
glClear( GL_COLOR_BUFFER_BIT );
}
glWindowPos2i( m_xoffset, m_yoffset );
}
// This way it will blend the progress rendering with the last buffer. eg:
// if it was called after a openGL.
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_ALPHA_TEST );
glDrawPixels( m_realBufferSize.x, m_realBufferSize.y, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );
return requestRedraw;
}
void RENDER_3D_RAYTRACE_GL::initPbo()
{
if( GLEW_ARB_pixel_buffer_object )
{
m_openglSupportsVertexBufferObjects = true;
// Try to delete vbo if it was already initialized
deletePbo();
// Learn about Pixel buffer objects at:
// http://www.songho.ca/opengl/gl_pbo.html
// http://web.eecs.umich.edu/~sugih/courses/eecs487/lectures/25-PBO+Mipmapping.pdf
// "create 2 pixel buffer objects, you need to delete them when program exits.
// glBufferDataARB with NULL pointer reserves only memory space."
// This sets the number of RGBA pixels
m_pboDataSize = m_realBufferSize.x * m_realBufferSize.y * 4;
glGenBuffersARB( 1, &m_pboId );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboDataSize, 0, GL_STREAM_DRAW_ARB );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );
wxLogTrace( m_logTrace,
wxT( "RENDER_3D_RAYTRACE_GL:: GLEW_ARB_pixel_buffer_object is supported" ) );
}
}

View File

@ -0,0 +1,54 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 2024 Alex Shvartzkop <dudesuchamazing@gmail.com>
* Copyright (C) 2015-2024 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
*/
#ifndef RENDER_3D_RAYTRACE_GL_H
#define RENDER_3D_RAYTRACE_GL_H
#include "../../common_ogl/openGL_includes.h"
#include "render_3d_raytrace_base.h"
class RENDER_3D_RAYTRACE_GL : public RENDER_3D_RAYTRACE_BASE
{
public:
explicit RENDER_3D_RAYTRACE_GL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdapter,
CAMERA& aCamera );
~RENDER_3D_RAYTRACE_GL();
void SetCurWindowSize( const wxSize& aSize ) override;
bool Redraw( bool aIsMoving, REPORTER* aStatusReporter, REPORTER* aWarningReporter ) override;
protected:
void initPbo() override;
void deletePbo() override;
bool m_openglSupportsVertexBufferObjects;
GLuint m_pboId;
GLuint m_pboDataSize;
};
#endif // RENDER_3D_RAYTRACE_GL_H

View File

@ -0,0 +1,159 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 2024 Alex Shvartzkop <dudesuchamazing@gmail.com>
* Copyright (C) 2015-2024 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
*/
#include "render_3d_raytrace_ram.h"
#include <wx/log.h>
RENDER_3D_RAYTRACE_RAM::RENDER_3D_RAYTRACE_RAM( BOARD_ADAPTER& aAdapter, CAMERA& aCamera ) :
RENDER_3D_RAYTRACE_BASE( aAdapter, aCamera ),
m_outputBuffer( nullptr ),
m_pboDataSize( 0 )
{
}
RENDER_3D_RAYTRACE_RAM::~RENDER_3D_RAYTRACE_RAM()
{
deletePbo();
}
GLubyte* RENDER_3D_RAYTRACE_RAM::GetBuffer()
{
return m_outputBuffer;
}
wxSize RENDER_3D_RAYTRACE_RAM::GetRealBufferSize()
{
return wxSize( m_realBufferSize.x, m_realBufferSize.y );
}
void RENDER_3D_RAYTRACE_RAM::deletePbo()
{
delete[] m_outputBuffer;
m_outputBuffer = nullptr;
}
void RENDER_3D_RAYTRACE_RAM::SetCurWindowSize( const wxSize& aSize )
{
if( m_windowSize != aSize )
{
m_windowSize = aSize;
initPbo();
}
}
bool RENDER_3D_RAYTRACE_RAM::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
REPORTER* aWarningReporter )
{
bool requestRedraw = false;
// Initialize openGL if need
if( !m_canvasInitialized )
{
m_canvasInitialized = true;
//aIsMoving = true;
requestRedraw = true;
// It will assign the first time the windows size, so it will now
// revert to preview mode the first time the Redraw is called
m_oldWindowsSize = m_windowSize;
initializeBlockPositions();
}
std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
// Reload board if it was requested
if( m_reloadRequested )
{
if( aStatusReporter )
aStatusReporter->Report( _( "Loading..." ) );
//aIsMoving = true;
requestRedraw = true;
Reload( aStatusReporter, aWarningReporter, false );
}
// Recalculate constants if windows size was changed
if( m_windowSize != m_oldWindowsSize )
{
m_oldWindowsSize = m_windowSize;
aIsMoving = true;
requestRedraw = true;
initializeBlockPositions();
}
const bool was_camera_changed = m_camera.ParametersChanged();
if( requestRedraw || aIsMoving || was_camera_changed )
m_renderState = RT_RENDER_STATE_MAX; // Set to an invalid state,
// so it will restart again latter
// This will only render if need, otherwise it will redraw the PBO on the screen again
if( aIsMoving || was_camera_changed )
{
// Set head light (camera view light) with the opposite direction of the camera
if( m_cameraLight )
m_cameraLight->SetDirection( -m_camera.GetDir() );
if( m_outputBuffer )
{
renderPreview( m_outputBuffer );
}
}
else
{
if( m_renderState != RT_RENDER_STATE_FINISH )
{
if( m_outputBuffer )
{
render( m_outputBuffer, aStatusReporter );
if( m_renderState != RT_RENDER_STATE_FINISH )
requestRedraw = true;
}
}
}
return requestRedraw;
}
void RENDER_3D_RAYTRACE_RAM::initPbo()
{
deletePbo();
m_pboDataSize = m_realBufferSize.x * m_realBufferSize.y * 4;
m_outputBuffer = new GLubyte[m_pboDataSize]();
}

View File

@ -0,0 +1,58 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 2024 Alex Shvartzkop <dudesuchamazing@gmail.com>
* Copyright (C) 2015-2024 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
*/
#ifndef RENDER_3D_RAYTRACE_RAM_H
#define RENDER_3D_RAYTRACE_RAM_H
#include "render_3d_raytrace_base.h"
class RENDER_3D_RAYTRACE_RAM : public RENDER_3D_RAYTRACE_BASE
{
public:
// TODO: Take into account board thickness so that the camera won't move inside of the board
// when facing it perpendicularly.
static constexpr float MIN_DISTANCE_IU = 4 * PCB_IU_PER_MM;
explicit RENDER_3D_RAYTRACE_RAM( BOARD_ADAPTER& aAdapter, CAMERA& aCamera );
~RENDER_3D_RAYTRACE_RAM();
GLubyte* GetBuffer();
wxSize GetRealBufferSize();
void SetCurWindowSize( const wxSize& aSize ) override;
bool Redraw( bool aIsMoving, REPORTER* aStatusReporter, REPORTER* aWarningReporter ) override;
private:
void initPbo() override;
void deletePbo() override;
GLubyte* m_outputBuffer;
GLuint m_pboDataSize;
};
#endif // RENDER_3D_RAYTRACE_RAM_H

View File

@ -2,7 +2,7 @@
* 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.
* Copyright (C) 1992-2024 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
@ -44,13 +44,12 @@
const wxChar* RENDER_3D_BASE::m_logTrace = wxT( "KI_TRACE_3D_RENDER" );
RENDER_3D_BASE::RENDER_3D_BASE( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera ) :
RENDER_3D_BASE::RENDER_3D_BASE( BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera ) :
m_boardAdapter( aBoardAdapter ),
m_camera( aCamera )
{
wxLogTrace( m_logTrace, wxT( "RENDER_3D_BASE::RENDER_3D_BASE" ) );
m_canvas = aCanvas;
m_is_opengl_initialized = false;
m_canvasInitialized = false;
m_windowSize = wxSize( -1, -1 );
m_reloadRequested = true;
}

View File

@ -2,7 +2,7 @@
* 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.
* Copyright (C) 1992-2024 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
@ -42,7 +42,7 @@
class RENDER_3D_BASE
{
public:
explicit RENDER_3D_BASE( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera );
explicit RENDER_3D_BASE( BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera );
virtual ~RENDER_3D_BASE() = 0;
@ -98,16 +98,13 @@ protected:
*/
std::unique_ptr<BUSY_INDICATOR> CreateBusyIndicator() const;
///< the canvas to display the scene
EDA_3D_CANVAS* m_canvas;
///< Settings reference in use for this render.
BOARD_ADAPTER& m_boardAdapter;
CAMERA& m_camera;
///< Flag if the opengl specific for this render was already initialized.
bool m_is_opengl_initialized;
///< Flag if the canvas specific for this render was already initialized.
bool m_canvasInitialized;
///< @todo This must be reviewed in order to flag change types.
bool m_reloadRequested;

View File

@ -2,7 +2,7 @@
* 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) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2015-2024 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
@ -38,11 +38,24 @@
// stdlib
#include <algorithm>
TRACK_BALL::TRACK_BALL( float aInitialDistance ) :
CAMERA( aInitialDistance )
TRACK_BALL::TRACK_BALL( float aInitialDistance ) : CAMERA( aInitialDistance )
{
wxLogTrace( m_logTrace, wxT( "TRACK_BALL::TRACK_BALL" ) );
initQuat();
}
TRACK_BALL::TRACK_BALL( SFVEC3F aInitPos, SFVEC3F aLookat, PROJECTION_TYPE aProjectionType ) :
CAMERA( aInitPos, aLookat, aProjectionType )
{
wxLogTrace( m_logTrace, wxT( "TRACK_BALL::TRACK_BALL" ) );
initQuat();
}
void TRACK_BALL::initQuat()
{
memset( m_quat_t0, 0, sizeof( m_quat_t0 ) );
memset( m_quat_t1, 0, sizeof( m_quat_t1 ) );
@ -50,6 +63,7 @@ TRACK_BALL::TRACK_BALL( float aInitialDistance ) :
trackball( m_quat_t1, 0.0, 0.0, 0.0, 0.0 );
}
void TRACK_BALL::Drag( const wxPoint& aNewMousePosition )
{
m_parametersChanged = true;

View File

@ -2,7 +2,7 @@
* 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) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2015-2024 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
@ -37,6 +37,7 @@ class TRACK_BALL : public CAMERA
{
public:
explicit TRACK_BALL( float aInitialDistance );
explicit TRACK_BALL( SFVEC3F aInitPos, SFVEC3F aLookat, PROJECTION_TYPE aProjectionType );
virtual ~TRACK_BALL()
{
@ -57,6 +58,8 @@ public:
void Interpolate( float t ) override;
private:
void initQuat();
/**
* interpolate quaternions of the trackball
*/

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 CERN
* Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2020-2024 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
@ -23,7 +23,6 @@
#define EDA_3D_VIEWER_SETTINGS_H_
#include <3d_enums.h>
#include <common_ogl/ogl_attr_list.h>
#include <plugins/3dapi/xv3d_types.h>
#include <settings/app_settings.h>
#include <settings/parameters.h>
@ -34,6 +33,8 @@
#define FOLLOW_PLOT_SETTINGS wxT( "follow_plot_settings" )
#define LEGACY_PRESET_FLAG wxT( "legacy_preset_flag" )
enum class ANTIALIASING_MODE;
struct LAYER_PRESET_3D
{

View File

@ -45,7 +45,9 @@ set(3D-VIEWER_SRCS
${DIR_RAY_ACC}/container_2d.cpp
${DIR_RAY}/PerlinNoise.cpp
${DIR_RAY}/create_scene.cpp
${DIR_RAY}/render_3d_raytrace.cpp
${DIR_RAY}/render_3d_raytrace_base.cpp
${DIR_RAY}/render_3d_raytrace_gl.cpp
${DIR_RAY}/render_3d_raytrace_ram.cpp
${DIR_RAY}/frustum.cpp
${DIR_RAY}/material.cpp
${DIR_RAY}/mortoncodes.cpp

View File

@ -88,6 +88,7 @@ set( KICOMMON_SRCS
jobs/job_export_sch_pythonbom.cpp
jobs/job_fp_export_svg.cpp
jobs/job_fp_upgrade.cpp
jobs/job_pcb_render.cpp
jobs/job_pcb_drc.cpp
jobs/job_sch_erc.cpp
jobs/job_sym_export_svg.cpp

View File

@ -2,7 +2,7 @@
* 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) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2015-2024 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
@ -29,6 +29,7 @@
#include <gal/3d/camera.h>
#include <wx/log.h>
#include <algorithm>
#include <3d_enums.h>
// A helper function to normalize aAngle between -2PI and +2PI
inline void normalise2PI( float& aAngle )
@ -50,15 +51,22 @@ const float CAMERA::DEFAULT_MIN_ZOOM = 0.020f;
const float CAMERA::DEFAULT_MAX_ZOOM = 2.0f;
CAMERA::CAMERA( float aInitialDistance )
CAMERA::CAMERA( float aInitialDistance ) :
CAMERA( SFVEC3F( 0.0f, 0.0f, -aInitialDistance ), SFVEC3F( 0.0f ),
PROJECTION_TYPE::PERSPECTIVE )
{
}
CAMERA::CAMERA( SFVEC3F aInitPos, SFVEC3F aLookat, PROJECTION_TYPE aProjectionType )
{
wxLogTrace( m_logTrace, wxT( "CAMERA::CAMERA" ) );
m_camera_pos_init = SFVEC3F( 0.0f, 0.0f, -aInitialDistance );
m_board_lookat_pos_init = SFVEC3F( 0.0f );
m_windowSize = SFVEC2I( 0, 0 );
m_projectionType = PROJECTION_TYPE::PERSPECTIVE;
m_interpolation_mode = CAMERA_INTERPOLATION::BEZIER;
m_camera_pos_init = aInitPos;
m_board_lookat_pos_init = aLookat;
m_windowSize = SFVEC2I( 0, 0 );
m_projectionType = aProjectionType;
m_interpolation_mode = CAMERA_INTERPOLATION::BEZIER;
m_minZoom = DEFAULT_MIN_ZOOM;
m_maxZoom = DEFAULT_MAX_ZOOM;
@ -99,6 +107,57 @@ void CAMERA::Reset()
}
bool CAMERA::ViewCommand_T1( VIEW3D_TYPE aRequestedView )
{
switch( aRequestedView )
{
case VIEW3D_TYPE::VIEW3D_RIGHT:
SetT0_and_T1_current_T();
Reset_T1();
RotateZ_T1( glm::radians( -90.0f ) );
RotateX_T1( glm::radians( -90.0f ) );
return true;
case VIEW3D_TYPE::VIEW3D_LEFT:
Reset_T1();
RotateZ_T1( glm::radians( 90.0f ) );
RotateX_T1( glm::radians( -90.0f ) );
return true;
case VIEW3D_TYPE::VIEW3D_FRONT:
Reset_T1();
RotateX_T1( glm::radians( -90.0f ) );
return true;
case VIEW3D_TYPE::VIEW3D_BACK:
Reset_T1();
RotateX_T1( glm::radians( -90.0f ) );
// The rotation angle should be 180.
// We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
// using 180 deg if the previous rotated position was already 180 deg
RotateZ_T1( glm::radians( 179.999f ) );
return true;
case VIEW3D_TYPE::VIEW3D_TOP:
Reset_T1();
return true;
case VIEW3D_TYPE::VIEW3D_BOTTOM:
Reset_T1();
RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
return true;
case VIEW3D_TYPE::VIEW3D_FLIP:
RotateY_T1( glm::radians( 179.999f ) );
return true;
default:
return false;
}
}
void CAMERA::Reset_T1()
{
m_camera_pos_t1 = m_camera_pos_init;

View File

@ -0,0 +1,28 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 2024 Alex Shvartzkop <dudesuchamazing@gmail.com>
* Copyright (C) 2023-2024 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <jobs/job_pcb_render.h>
JOB_PCB_RENDER::JOB_PCB_RENDER( bool aIsCli ) :
JOB( "render", aIsCli ), m_filename(), m_outputFile()
{
}

View File

@ -0,0 +1,84 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 2024 Alex Shvartzkop <dudesuchamazing@gmail.com>
* Copyright (C) 1992-2024 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef JOB_PCB_RENDER_H
#define JOB_PCB_RENDER_H
#include <kicommon.h>
#include <wx/string.h>
#include "job.h"
#include <optional>
#include <math/vector3.h>
class KICOMMON_API JOB_PCB_RENDER : public JOB
{
public:
JOB_PCB_RENDER( bool aIsCli );
wxString m_filename;
wxString m_outputFile;
enum class FORMAT
{
PNG,
JPEG
};
enum class QUALITY
{
BASIC,
HIGH,
USER
};
enum class BG_STYLE
{
DEFAULT,
TRANSPARENT,
OPAQUE
};
enum class SIDE
{
TOP,
BOTTOM,
LEFT,
RIGHT,
FRONT,
BACK
};
FORMAT m_format = FORMAT::PNG;
QUALITY m_quality = QUALITY::BASIC;
BG_STYLE m_bgStyle = BG_STYLE::DEFAULT;
int m_width = 0;
int m_height = 0;
std::string m_colorPreset;
SIDE m_side = SIDE::TOP;
double m_zoom = 1.0;
bool m_perspective = false;
VECTOR3D m_rotation;
VECTOR3D m_pan;
VECTOR3D m_pivot;
bool m_floor = false;
};
#endif

View File

@ -2,7 +2,7 @@
* 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) 2015-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2015-2024 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
@ -34,6 +34,7 @@
#include <plugins/3dapi/xv3d_types.h>
#include <wx/gdicmn.h> // for wxSize
#include <vector>
#include <3d_enums.h>
enum class PROJECTION_TYPE
{
@ -110,6 +111,7 @@ public:
* @param aInitialDistance Initial Z-distance to the board
*/
explicit CAMERA( float aInitialDistance );
explicit CAMERA( SFVEC3F aInitPos, SFVEC3F aLookat, PROJECTION_TYPE aProjectionType );
virtual ~CAMERA()
{
@ -232,6 +234,8 @@ public:
zoomChanged();
}
bool ViewCommand_T1( VIEW3D_TYPE aRequestedView );
void RotateX( float aAngleInRadians );
void RotateY( float aAngleInRadians );
void RotateZ( float aAngleInRadians );

View File

@ -42,6 +42,7 @@ set( KICAD_CLI_SRCS
cli/command.cpp
cli/command_pcb_export_base.cpp
cli/command_pcb_drc.cpp
cli/command_pcb_render.cpp
cli/command_pcb_export_3d.cpp
cli/command_pcb_export_drill.cpp
cli/command_pcb_export_dxf.cpp

View File

@ -0,0 +1,302 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 2024 Alex Shvartzkop <dudesuchamazing@gmail.com>
* Copyright (C) 1992-2024 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "command_pcb_render.h"
#include <cli/exit_codes.h>
#include "jobs/job_pcb_render.h"
#include <kiface_base.h>
#include <layer_ids.h>
#include <string_utils.h>
#include <wx/crt.h>
#include <magic_enum.hpp>
#include <macros.h>
#include <wx/tokenzr.h>
#include "../../3d-viewer/3d_viewer/eda_3d_viewer_settings.h"
#include <math/vector3.h>
#define ARG_BACKGROUND "--background"
#define ARG_QUALITY "--quality"
#define ARG_WIDTH "--width"
#define ARG_WIDTH_SHORT "-w"
#define ARG_HEIGHT "--height"
#define ARG_HEIGHT_SHORT "-h"
#define ARG_SIDE "--side"
#define ARG_PRESET "--preset"
#define ARG_PAN "--pan"
#define ARG_PIVOT "--pivot"
#define ARG_ROTATE "--rotate"
#define ARG_ZOOM "--zoom"
#define ARG_PERSPECTIVE "--perspective"
#define ARG_FLOOR "--floor"
template <typename T>
static wxString enumString()
{
wxString str;
auto names = magic_enum::enum_names<T>();
for( size_t i = 0; i < names.size(); i++ )
{
std::string name = { names[i].begin(), names[i].end() };
if( i > 0 )
str << ", ";
std::transform( name.begin(), name.end(), name.begin(),
[]( unsigned char c )
{
return std::tolower( c );
} );
str << name;
}
return str;
}
template <typename T>
static std::vector<std::string> enumChoices()
{
std::vector<std::string> out;
for( auto& strView : magic_enum::enum_names<T>() )
{
std::string name = { strView.begin(), strView.end() };
std::transform( name.begin(), name.end(), name.begin(),
[]( unsigned char c )
{
return std::tolower( c );
} );
out.emplace_back( name );
}
return out;
}
template <typename T>
static std::optional<T> strToEnum( std::string& aInput )
{
return magic_enum::enum_cast<T>( aInput, magic_enum::case_insensitive );
}
template <typename T>
static bool getToEnum( const std::string& aInput, T& aOutput )
{
// If not specified, leave at default
if( aInput.empty() )
return true;
if( auto opt = magic_enum::enum_cast<T>( aInput, magic_enum::case_insensitive ) )
{
aOutput = *opt;
return true;
}
return false;
}
static bool getToVector3( const std::string& aInput, VECTOR3D& aOutput )
{
// If not specified, leave at default
if( aInput.empty() )
return true;
// Remove potential quotes
wxString wxStr = From_UTF8( aInput );
if( wxStr[0] == '\'' )
wxStr = wxStr.AfterFirst( '\'' );
if( wxStr[wxStr.length() - 1] == '\'' )
wxStr = wxStr.BeforeLast( '\'' );
wxArrayString arr = wxSplit( wxStr, ',', 0 );
if( arr.size() != 3 )
return false;
VECTOR3D vec;
bool success = true;
success &= arr[0].Trim().ToCDouble( &vec.x );
success &= arr[1].Trim().ToCDouble( &vec.y );
success &= arr[2].Trim().ToCDouble( &vec.z );
if( !success )
return false;
aOutput = vec;
return true;
}
CLI::PCB_RENDER_COMMAND::PCB_RENDER_COMMAND() : COMMAND( "render" )
{
addCommonArgs( true, true, false, false );
addDefineArg();
m_argParser.add_description(
UTF8STDSTR( _( "Renders the PCB in 3D view to PNG or JPEG image" ) ) );
m_argParser.add_argument( ARG_WIDTH, ARG_WIDTH_SHORT )
.default_value( 1600 )
.scan<'i', int>()
.metavar( "WIDTH" )
.help( UTF8STDSTR( _( "Image width" ) ) );
m_argParser.add_argument( ARG_HEIGHT, ARG_HEIGHT_SHORT )
.default_value( 900 )
.scan<'i', int>()
.metavar( "HEIGHT" )
.help( UTF8STDSTR( _( "Image height" ) ) );
m_argParser.add_argument( ARG_SIDE )
.default_value( std::string( "top" ) )
.add_choices( enumChoices<JOB_PCB_RENDER::SIDE>() )
.metavar( "SIDE" )
.help( UTF8STDSTR( wxString::Format( _( "Render from side. Options: %s" ),
enumString<JOB_PCB_RENDER::SIDE>() ) ) );
m_argParser.add_argument( ARG_BACKGROUND )
.default_value( std::string( "" ) )
.help( UTF8STDSTR( _( "Image background. Options: transparent, opaque. Default: "
"transparent for PNG, opaque for JPEG" ) ) )
.metavar( "BG" );
m_argParser.add_argument( ARG_QUALITY )
.default_value( std::string( "basic" ) )
.add_choices( enumChoices<JOB_PCB_RENDER::QUALITY>() )
.metavar( "QUALITY" )
.help( UTF8STDSTR( wxString::Format( _( "Render quality. Options: %s" ),
enumString<JOB_PCB_RENDER::QUALITY>() ) ) );
m_argParser.add_argument( ARG_PRESET )
.default_value( std::string( wxString( FOLLOW_PLOT_SETTINGS ) ) )
.metavar( "PRESET" )
.help( UTF8STDSTR( wxString::Format( _( "Color preset. Options: %s, %s, %s, ..." ),
FOLLOW_PCB, FOLLOW_PLOT_SETTINGS,
LEGACY_PRESET_FLAG ) ) );
m_argParser.add_argument( ARG_FLOOR )
.flag()
.help( UTF8STDSTR( _( "Enables floor, shadows and post-processing, even if disabled in "
"quality preset" ) ) );
m_argParser.add_argument( ARG_PERSPECTIVE )
.flag()
.help( UTF8STDSTR( _( "Use perspective projection instead of orthogonal" ) ) );
m_argParser.add_argument( ARG_ZOOM )
.default_value( 1.0 )
.scan<'g', double>()
.metavar( "ZOOM" )
.help( UTF8STDSTR( _( "Camera zoom" ) ) );
m_argParser.add_argument( ARG_PAN )
.default_value( std::string( "" ) )
.metavar( "VECTOR" )
.help( UTF8STDSTR( _( "Pan camera, format 'X,Y,Z' e.g.: '3,0,0'" ) ) );
m_argParser.add_argument( ARG_PIVOT )
.default_value( std::string( "" ) )
.metavar( "PIVOT" )
.help( UTF8STDSTR( _( "Set pivot point relative to the board center in centimeters, format 'X,Y,Z' "
"e.g.: '-10,2,0'" ) ) );
m_argParser.add_argument( ARG_ROTATE )
.default_value( std::string( "" ) )
.metavar( "ANGLES" )
.help( UTF8STDSTR(
_( "Rotate board, format 'X,Y,Z' e.g.: '-45,0,45' for isometric view" ) ) );
}
int CLI::PCB_RENDER_COMMAND::doPerform( KIWAY& aKiway )
{
std::unique_ptr<JOB_PCB_RENDER> renderJob( new JOB_PCB_RENDER( true ) );
renderJob->m_outputFile = m_argOutput;
renderJob->m_filename = m_argInput;
renderJob->SetVarOverrides( m_argDefineVars );
renderJob->m_colorPreset = m_argParser.get<std::string>( ARG_PRESET );
renderJob->m_width = m_argParser.get<int>( ARG_WIDTH );
renderJob->m_height = m_argParser.get<int>( ARG_HEIGHT );
renderJob->m_zoom = m_argParser.get<double>( ARG_ZOOM );
renderJob->m_perspective = m_argParser.get<bool>( ARG_PERSPECTIVE );
renderJob->m_floor = m_argParser.get<bool>( ARG_FLOOR );
getToEnum( m_argParser.get<std::string>( ARG_QUALITY ), renderJob->m_quality );
getToEnum( m_argParser.get<std::string>( ARG_SIDE ), renderJob->m_side );
if( !getToEnum( m_argParser.get<std::string>( ARG_BACKGROUND ), renderJob->m_bgStyle ) )
{
wxFprintf( stderr, _( "Invalid background\n" ) );
return EXIT_CODES::ERR_ARGS;
}
if( !getToVector3( m_argParser.get<std::string>( ARG_ROTATE ), renderJob->m_rotation ) )
{
wxFprintf( stderr, _( "Invalid rotation format\n" ) );
return EXIT_CODES::ERR_ARGS;
}
if( !getToVector3( m_argParser.get<std::string>( ARG_PAN ), renderJob->m_pan ) )
{
wxFprintf( stderr, _( "Invalid pan format\n" ) );
return EXIT_CODES::ERR_ARGS;
}
if( !getToVector3( m_argParser.get<std::string>( ARG_PIVOT ), renderJob->m_pivot ) )
{
wxFprintf( stderr, _( "Invalid pivot format\n" ) );
return EXIT_CODES::ERR_ARGS;
}
if( m_argOutput.Lower().EndsWith( wxS( ".png" ) ) )
{
renderJob->m_format = JOB_PCB_RENDER::FORMAT::PNG;
}
else if( m_argOutput.Lower().EndsWith( wxS( ".jpg" ) )
|| m_argOutput.Lower().EndsWith( wxS( ".jpeg" ) ) )
{
renderJob->m_format = JOB_PCB_RENDER::FORMAT::JPEG;
}
else
{
wxFprintf( stderr, _( "Invalid image format\n" ) );
return EXIT_CODES::ERR_ARGS;
}
int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, renderJob.get() );
return exitCode;
}

View File

@ -0,0 +1,38 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 1992-2024 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMAND_EXPORT_PCB_RENDER_H
#define COMMAND_EXPORT_PCB_RENDER_H
#include "command.h"
namespace CLI
{
class PCB_RENDER_COMMAND : public COMMAND
{
public:
PCB_RENDER_COMMAND();
protected:
int doPerform( KIWAY& aKiway ) override;
};
} // namespace CLI
#endif

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2004-2024 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
@ -49,6 +49,7 @@
#include "cli/command_pcb.h"
#include "cli/command_pcb_export.h"
#include "cli/command_pcb_drc.h"
#include "cli/command_pcb_render.h"
#include "cli/command_pcb_export_3d.h"
#include "cli/command_pcb_export_drill.h"
#include "cli/command_pcb_export_dxf.h"
@ -128,6 +129,7 @@ struct COMMAND_ENTRY
static CLI::PCB_COMMAND pcbCmd{};
static CLI::PCB_DRC_COMMAND pcbDrcCmd{};
static CLI::PCB_RENDER_COMMAND pcbRenderCmd{};
static CLI::PCB_EXPORT_DRILL_COMMAND exportPcbDrillCmd{};
static CLI::PCB_EXPORT_DXF_COMMAND exportPcbDxfCmd{};
static CLI::PCB_EXPORT_3D_COMMAND exportPcbGlbCmd{ "glb", UTF8STDSTR( _( "Export GLB (binary GLTF)" ) ), JOB_EXPORT_PCB_3D::FORMAT::GLB };
@ -183,6 +185,9 @@ static std::vector<COMMAND_ENTRY> commandStack = {
{
&pcbDrcCmd
},
{
&pcbRenderCmd
},
{
&exportPcbCmd,
{

View File

@ -37,6 +37,7 @@
#include <jobs/job_export_pcb_pos.h>
#include <jobs/job_export_pcb_svg.h>
#include <jobs/job_export_pcb_3d.h>
#include <jobs/job_pcb_render.h>
#include <jobs/job_pcb_drc.h>
#include <cli/exit_codes.h>
#include <exporters/place_file_exporter.h>
@ -62,6 +63,9 @@
#include <pcbnew_settings.h>
#include <pcbplot.h>
#include <pgm_base.h>
#include <3d_rendering/raytracing/render_3d_raytrace_ram.h>
#include <3d_rendering/track_ball.h>
#include <project_pcb.h>
#include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h>
#include <reporter.h>
#include <string_utf8_map.h>
@ -73,10 +77,18 @@
#include "pcbnew_scripting_helpers.h"
#ifdef _WIN32
#ifdef TRANSPARENT
#undef TRANSPARENT
#endif
#endif
PCBNEW_JOBS_HANDLER::PCBNEW_JOBS_HANDLER( KIWAY* aKiway ) :
JOB_DISPATCHER( aKiway )
{
Register( "3d", std::bind( &PCBNEW_JOBS_HANDLER::JobExportStep, this, std::placeholders::_1 ) );
Register( "render", std::bind( &PCBNEW_JOBS_HANDLER::JobExportRender, this, std::placeholders::_1 ) );
Register( "svg", std::bind( &PCBNEW_JOBS_HANDLER::JobExportSvg, this, std::placeholders::_1 ) );
Register( "dxf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportDxf, this, std::placeholders::_1 ) );
Register( "pdf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPdf, this, std::placeholders::_1 ) );
@ -212,6 +224,186 @@ int PCBNEW_JOBS_HANDLER::JobExportStep( JOB* aJob )
}
int PCBNEW_JOBS_HANDLER::JobExportRender( JOB* aJob )
{
JOB_PCB_RENDER* aRenderJob = dynamic_cast<JOB_PCB_RENDER*>( aJob );
if( aRenderJob == nullptr )
return CLI::EXIT_CODES::ERR_UNKNOWN;
if( aJob->IsCli() )
m_reporter->Report( _( "Loading board\n" ), RPT_SEVERITY_INFO );
BOARD* brd = LoadBoard( aRenderJob->m_filename, true );
brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
BOARD_ADAPTER m_boardAdapter;
m_boardAdapter.SetBoard( brd );
m_boardAdapter.m_IsBoardView = false;
m_boardAdapter.m_IsPreviewer =
true; // Force display 3D models, regardless the 3D viewer options
EDA_3D_VIEWER_SETTINGS* cfg =
Pgm().GetSettingsManager().GetAppSettings<EDA_3D_VIEWER_SETTINGS>();
if( aRenderJob->m_bgStyle == JOB_PCB_RENDER::BG_STYLE::TRANSPARENT
|| aRenderJob->m_bgStyle == JOB_PCB_RENDER::BG_STYLE::DEFAULT
&& aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG )
{
BOARD_ADAPTER::g_DefaultBackgroundTop = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
BOARD_ADAPTER::g_DefaultBackgroundBot = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
}
if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::BASIC )
{
// Silkscreen is pixelated without antialiasing
cfg->m_Render.raytrace_anti_aliasing = true;
cfg->m_Render.raytrace_backfloor = false;
cfg->m_Render.raytrace_post_processing = false;
cfg->m_Render.raytrace_procedural_textures = false;
cfg->m_Render.raytrace_reflections = false;
cfg->m_Render.raytrace_shadows = false;
// Better colors
cfg->m_Render.differentiate_plated_copper = true;
// Tracks below soldermask are not visible without refractions
cfg->m_Render.raytrace_refractions = true;
cfg->m_Render.raytrace_recursivelevel_refractions = 1;
}
else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::HIGH )
{
cfg->m_Render.raytrace_anti_aliasing = true;
cfg->m_Render.raytrace_backfloor = true;
cfg->m_Render.raytrace_post_processing = true;
cfg->m_Render.raytrace_procedural_textures = true;
cfg->m_Render.raytrace_reflections = true;
cfg->m_Render.raytrace_shadows = true;
cfg->m_Render.raytrace_refractions = true;
cfg->m_Render.differentiate_plated_copper = true;
}
if( aRenderJob->m_floor )
{
cfg->m_Render.raytrace_backfloor = true;
cfg->m_Render.raytrace_shadows = true;
cfg->m_Render.raytrace_post_processing = true;
}
cfg->m_CurrentPreset = aRenderJob->m_colorPreset;
m_boardAdapter.m_Cfg = cfg;
m_boardAdapter.Set3dCacheManager( PROJECT_PCB::Get3DCacheManager( brd->GetProject() ) );
static std::map<JOB_PCB_RENDER::SIDE, VIEW3D_TYPE> s_viewCmdMap = {
{ JOB_PCB_RENDER::SIDE::TOP, VIEW3D_TYPE::VIEW3D_TOP },
{ JOB_PCB_RENDER::SIDE::BOTTOM, VIEW3D_TYPE::VIEW3D_BOTTOM },
{ JOB_PCB_RENDER::SIDE::LEFT, VIEW3D_TYPE::VIEW3D_LEFT },
{ JOB_PCB_RENDER::SIDE::RIGHT, VIEW3D_TYPE::VIEW3D_RIGHT },
{ JOB_PCB_RENDER::SIDE::FRONT, VIEW3D_TYPE::VIEW3D_FRONT },
{ JOB_PCB_RENDER::SIDE::BACK, VIEW3D_TYPE::VIEW3D_BACK },
};
PROJECTION_TYPE projection =
aRenderJob->m_perspective ? PROJECTION_TYPE::PERSPECTIVE : PROJECTION_TYPE::ORTHO;
wxSize windowSize( aRenderJob->m_width, aRenderJob->m_height );
TRACK_BALL camera( 2 * RANGE_SCALE_3D );
camera.SetProjection( projection );
camera.SetCurWindowSize( windowSize );
RENDER_3D_RAYTRACE_RAM raytrace( m_boardAdapter, camera );
raytrace.SetCurWindowSize( windowSize );
for( bool first = true; raytrace.Redraw( false, m_reporter, m_reporter ); first = false )
{
if( first )
{
const float cmTo3D = m_boardAdapter.BiuTo3dUnits() * pcbIUScale.mmToIU( 10.0 );
// First redraw resets lookat point to the board center, so set up the camera here
camera.ViewCommand_T1( s_viewCmdMap[aRenderJob->m_side] );
camera.SetLookAtPos_T1(
camera.GetLookAtPos_T1()
+ SFVEC3F( aRenderJob->m_pivot.x, aRenderJob->m_pivot.y, aRenderJob->m_pivot.z )
* cmTo3D );
camera.Pan_T1(
SFVEC3F( aRenderJob->m_pan.x, aRenderJob->m_pan.y, aRenderJob->m_pan.z ) );
camera.Zoom_T1( aRenderJob->m_zoom );
camera.RotateX_T1( DEG2RAD( aRenderJob->m_rotation.x ) );
camera.RotateY_T1( DEG2RAD( aRenderJob->m_rotation.y ) );
camera.RotateZ_T1( DEG2RAD( aRenderJob->m_rotation.z ) );
camera.Interpolate( 1.0f );
camera.SetT0_and_T1_current_T();
camera.ParametersChanged();
}
}
GLubyte* rgbaBuffer = raytrace.GetBuffer();
wxSize realSize = raytrace.GetRealBufferSize();
bool success = !!rgbaBuffer;
if( rgbaBuffer )
{
const unsigned int wxh = realSize.x * realSize.y;
unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
unsigned char* rgbaPtr = rgbaBuffer;
unsigned char* rgbPtr = rgbBuffer;
unsigned char* alphaPtr = alphaBuffer;
for( int y = 0; y < realSize.y; y++ )
{
for( int x = 0; x < realSize.x; x++ )
{
rgbPtr[0] = rgbaPtr[0];
rgbPtr[1] = rgbaPtr[1];
rgbPtr[2] = rgbaPtr[2];
alphaPtr[0] = rgbaPtr[3];
rgbaPtr += 4;
rgbPtr += 3;
alphaPtr += 1;
}
}
wxImage image( realSize );
image.SetData( rgbBuffer );
image.SetAlpha( alphaBuffer );
image = image.Mirror( false );
image.SetOption( wxIMAGE_OPTION_QUALITY, 90 );
image.SaveFile( aRenderJob->m_outputFile,
aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ? wxBITMAP_TYPE_PNG
: wxBITMAP_TYPE_JPEG );
}
m_reporter->Report( wxString::Format( _( "Actual image size: %dx%d" ), realSize.x, realSize.y )
+ wxS( "\n" ),
RPT_SEVERITY_INFO );
if( success )
m_reporter->Report( _( "Successfully created 3D render image" ) + wxS( "\n" ),
RPT_SEVERITY_INFO );
else
m_reporter->Report( _( "Error creating 3D render image" ) + wxS( "\n" ),
RPT_SEVERITY_ERROR );
return CLI::EXIT_CODES::OK;
}
int PCBNEW_JOBS_HANDLER::JobExportSvg( JOB* aJob )
{
JOB_EXPORT_PCB_SVG* aSvgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( aJob );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2024 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
@ -36,6 +36,7 @@ class PCBNEW_JOBS_HANDLER : public JOB_DISPATCHER
public:
PCBNEW_JOBS_HANDLER( KIWAY* aKiway );
int JobExportStep( JOB* aJob );
int JobExportRender( JOB* aJob );
int JobExportSvg( JOB* aJob );
int JobExportDxf( JOB* aJob );
int JobExportPdf( JOB* aJob );