diff --git a/3d-viewer/3d_canvas/board_adapter.h b/3d-viewer/3d_canvas/board_adapter.h index ceb9350c81..9816096b85 100644 --- a/3d-viewer/3d_canvas/board_adapter.h +++ b/3d-viewer/3d_canvas/board_adapter.h @@ -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" diff --git a/3d-viewer/3d_canvas/eda_3d_canvas.cpp b/3d-viewer/3d_canvas/eda_3d_canvas.cpp index cbd252c4f0..69793878d0 100644 --- a/3d-viewer/3d_canvas/eda_3d_canvas.cpp +++ b/3d-viewer/3d_canvas/eda_3d_canvas.cpp @@ -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; } diff --git a/3d-viewer/3d_canvas/eda_3d_canvas.h b/3d-viewer/3d_canvas/eda_3d_canvas.h index ed6fd21efe..6eef97c641 100644 --- a/3d-viewer/3d_canvas/eda_3d_canvas.h +++ b/3d-viewer/3d_canvas/eda_3d_canvas.h @@ -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; diff --git a/3d-viewer/3d_rendering/opengl/3d_model.h b/3d-viewer/3d_rendering/opengl/3d_model.h index 1663e35ab5..275a0ec8cb 100644 --- a/3d-viewer/3d_rendering/opengl/3d_model.h +++ b/3d-viewer/3d_rendering/opengl/3d_model.h @@ -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> diff --git a/3d-viewer/3d_rendering/opengl/render_3d_opengl.cpp b/3d-viewer/3d_rendering/opengl/render_3d_opengl.cpp index b05bbd34bb..a34574eb81 100644 --- a/3d-viewer/3d_rendering/opengl/render_3d_opengl.cpp +++ b/3d-viewer/3d_rendering/opengl/render_3d_opengl.cpp @@ -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 ); } diff --git a/3d-viewer/3d_rendering/opengl/render_3d_opengl.h b/3d-viewer/3d_rendering/opengl/render_3d_opengl.h index c109d30e72..13ad861590 100644 --- a/3d-viewer/3d_rendering/opengl/render_3d_opengl.h +++ b/3d-viewer/3d_rendering/opengl/render_3d_opengl.h @@ -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; diff --git a/3d-viewer/3d_rendering/raytracing/create_scene.cpp b/3d-viewer/3d_rendering/raytracing/create_scene.cpp index 9a7a6e3c1f..a2cbc78c56 100644 --- a/3d-viewer/3d_rendering/raytracing/create_scene.cpp +++ b/3d-viewer/3d_rendering/raytracing/create_scene.cpp @@ -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 ) { diff --git a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace.cpp b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.cpp similarity index 89% rename from 3d-viewer/3d_rendering/raytracing/render_3d_raytrace.cpp rename to 3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.cpp index 8e516d323e..497bae8e15 100644 --- a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace.cpp +++ b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.cpp @@ -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(); diff --git a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace.h b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.h similarity index 91% rename from 3d-viewer/3d_rendering/raytracing/render_3d_raytrace.h rename to 3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.h index 92eeac2309..7c4a424ac9 100644 --- a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace.h +++ b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.h @@ -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 diff --git a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_gl.cpp b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_gl.cpp new file mode 100644 index 0000000000..883f6d0551 --- /dev/null +++ b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_gl.cpp @@ -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" ) ); + } +} diff --git a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_gl.h b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_gl.h new file mode 100644 index 0000000000..90f59c8047 --- /dev/null +++ b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_gl.h @@ -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 diff --git a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_ram.cpp b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_ram.cpp new file mode 100644 index 0000000000..80ef2a3cac --- /dev/null +++ b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_ram.cpp @@ -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](); +} diff --git a/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_ram.h b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_ram.h new file mode 100644 index 0000000000..6283f24c14 --- /dev/null +++ b/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_ram.h @@ -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 diff --git a/3d-viewer/3d_rendering/render_3d_base.cpp b/3d-viewer/3d_rendering/render_3d_base.cpp index 4215321adc..6e9dc81a75 100644 --- a/3d-viewer/3d_rendering/render_3d_base.cpp +++ b/3d-viewer/3d_rendering/render_3d_base.cpp @@ -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; } diff --git a/3d-viewer/3d_rendering/render_3d_base.h b/3d-viewer/3d_rendering/render_3d_base.h index c088e536f4..fa0c6ede60 100644 --- a/3d-viewer/3d_rendering/render_3d_base.h +++ b/3d-viewer/3d_rendering/render_3d_base.h @@ -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; diff --git a/3d-viewer/3d_rendering/track_ball.cpp b/3d-viewer/3d_rendering/track_ball.cpp index 8895fb7cb4..f1ede58d3b 100644 --- a/3d-viewer/3d_rendering/track_ball.cpp +++ b/3d-viewer/3d_rendering/track_ball.cpp @@ -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; diff --git a/3d-viewer/3d_rendering/track_ball.h b/3d-viewer/3d_rendering/track_ball.h index ed3b262c1d..a98586596b 100644 --- a/3d-viewer/3d_rendering/track_ball.h +++ b/3d-viewer/3d_rendering/track_ball.h @@ -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 */ diff --git a/3d-viewer/3d_viewer/eda_3d_viewer_settings.h b/3d-viewer/3d_viewer/eda_3d_viewer_settings.h index c21dd2f5bb..a466144c8d 100644 --- a/3d-viewer/3d_viewer/eda_3d_viewer_settings.h +++ b/3d-viewer/3d_viewer/eda_3d_viewer_settings.h @@ -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 { diff --git a/3d-viewer/CMakeLists.txt b/3d-viewer/CMakeLists.txt index ccbda1c591..73db487ad9 100644 --- a/3d-viewer/CMakeLists.txt +++ b/3d-viewer/CMakeLists.txt @@ -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 diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 5505b187f5..16642e1c9f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -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 diff --git a/common/gal/3d/camera.cpp b/common/gal/3d/camera.cpp index 7886ff6913..7035af2322 100644 --- a/common/gal/3d/camera.cpp +++ b/common/gal/3d/camera.cpp @@ -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; diff --git a/common/jobs/job_pcb_render.cpp b/common/jobs/job_pcb_render.cpp new file mode 100644 index 0000000000..376ab2201d --- /dev/null +++ b/common/jobs/job_pcb_render.cpp @@ -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() +{ +} \ No newline at end of file diff --git a/common/jobs/job_pcb_render.h b/common/jobs/job_pcb_render.h new file mode 100644 index 0000000000..7cf52b85bd --- /dev/null +++ b/common/jobs/job_pcb_render.h @@ -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 \ No newline at end of file diff --git a/3d-viewer/3d_enums.h b/include/3d_enums.h similarity index 100% rename from 3d-viewer/3d_enums.h rename to include/3d_enums.h diff --git a/include/gal/3d/camera.h b/include/gal/3d/camera.h index 5595e4d197..7e2060089e 100644 --- a/include/gal/3d/camera.h +++ b/include/gal/3d/camera.h @@ -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 ); diff --git a/kicad/CMakeLists.txt b/kicad/CMakeLists.txt index b64f2138e1..7480c7dae4 100644 --- a/kicad/CMakeLists.txt +++ b/kicad/CMakeLists.txt @@ -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 diff --git a/kicad/cli/command_pcb_render.cpp b/kicad/cli/command_pcb_render.cpp new file mode 100644 index 0000000000..5a227e3fcd --- /dev/null +++ b/kicad/cli/command_pcb_render.cpp @@ -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; +} diff --git a/kicad/cli/command_pcb_render.h b/kicad/cli/command_pcb_render.h new file mode 100644 index 0000000000..5b3161cd39 --- /dev/null +++ b/kicad/cli/command_pcb_render.h @@ -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 \ No newline at end of file diff --git a/kicad/kicad_cli.cpp b/kicad/kicad_cli.cpp index 6e99bf6bc0..fde643f717 100644 --- a/kicad/kicad_cli.cpp +++ b/kicad/kicad_cli.cpp @@ -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, { diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index 0ad66cf616..83f84fa942 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/pcbnew/pcbnew_jobs_handler.cpp @@ -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 ); diff --git a/pcbnew/pcbnew_jobs_handler.h b/pcbnew/pcbnew_jobs_handler.h index 33f7019099..39e50eda71 100644 --- a/pcbnew/pcbnew_jobs_handler.h +++ b/pcbnew/pcbnew_jobs_handler.h @@ -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 );