Browse Source

3d-viewer: Navigation gizmo for 3D viewer

ADDED: Blender-like navigational gizmo - new feature

Fixes https://gitlab.com/kicad/code/kicad/-/issues/16956
pull/18/head
Damjan 6 months ago
committed by Seth Hillbrand
parent
commit
2c92f95a1c
  1. 8
      3d-viewer/3d_canvas/board_adapter.cpp
  2. 29
      3d-viewer/3d_canvas/eda_3d_canvas.cpp
  3. 368
      3d-viewer/3d_rendering/opengl/3d_spheres_gizmo.cpp
  4. 133
      3d-viewer/3d_rendering/opengl/3d_spheres_gizmo.h
  5. 57
      3d-viewer/3d_rendering/opengl/render_3d_opengl.cpp
  6. 13
      3d-viewer/3d_rendering/opengl/render_3d_opengl.h
  7. 7
      3d-viewer/3d_viewer/eda_3d_viewer_frame.cpp
  8. 10
      3d-viewer/3d_viewer/eda_3d_viewer_settings.cpp
  9. 2
      3d-viewer/3d_viewer/eda_3d_viewer_settings.h
  10. 18
      3d-viewer/3d_viewer/tools/eda_3d_actions.cpp
  11. 2
      3d-viewer/3d_viewer/tools/eda_3d_actions.h
  12. 4
      3d-viewer/3d_viewer/tools/eda_3d_controller.cpp
  13. 1
      3d-viewer/CMakeLists.txt
  14. 2
      3d-viewer/dialogs/appearance_controls_3D.cpp
  15. 2
      AUTHORS.txt
  16. 2
      include/layer_ids.h

8
3d-viewer/3d_canvas/board_adapter.cpp

@ -791,7 +791,7 @@ void BOARD_ADAPTER::SetVisibleLayers( const std::bitset<LAYER_3D_END>& aLayers )
m_Cfg->m_Render.show_model_bbox = aLayers.test( LAYER_3D_BOUNDING_BOXES );
m_Cfg->m_Render.show_off_board_silk = aLayers.test( LAYER_3D_OFF_BOARD_SILK );
m_Cfg->m_Render.show_axis = aLayers.test( LAYER_3D_AXES );
m_Cfg->m_Render.show_navigator = aLayers.test( LAYER_3D_NAVIGATOR );
}
@ -834,7 +834,7 @@ std::bitset<LAYER_3D_END> BOARD_ADAPTER::GetVisibleLayers() const
ret.set( LAYER_3D_BOUNDING_BOXES, m_Cfg->m_Render.show_model_bbox );
ret.set( LAYER_3D_OFF_BOARD_SILK, m_Cfg->m_Render.show_off_board_silk );
ret.set( LAYER_3D_AXES, m_Cfg->m_Render.show_axis );
ret.set( LAYER_3D_NAVIGATOR, m_Cfg->m_Render.show_navigator );
return ret;
}
@ -868,7 +868,7 @@ std::bitset<LAYER_3D_END> BOARD_ADAPTER::GetVisibleLayers() const
ret.set( LAYER_3D_BOUNDING_BOXES, m_Cfg->m_Render.show_model_bbox );
ret.set( LAYER_3D_OFF_BOARD_SILK, m_Cfg->m_Render.show_off_board_silk );
ret.set( LAYER_3D_AXES, m_Cfg->m_Render.show_axis );
ret.set( LAYER_3D_NAVIGATOR, m_Cfg->m_Render.show_navigator );
if( m_Cfg->m_CurrentPreset == FOLLOW_PCB )
{
@ -966,7 +966,7 @@ std::bitset<LAYER_3D_END> BOARD_ADAPTER::GetDefaultVisibleLayers() const
ret.set( LAYER_3D_BOUNDING_BOXES, false );
ret.set( LAYER_3D_OFF_BOARD_SILK, false );
ret.set( LAYER_3D_AXES, true );
ret.set( LAYER_3D_NAVIGATOR, true );
return ret;
}

29
3d-viewer/3d_canvas/eda_3d_canvas.cpp

@ -583,6 +583,19 @@ void EDA_3D_CANVAS::DoRePaint()
Request_refresh( false );
}
static constexpr std::array<VIEW3D_TYPE, static_cast<int>( SPHERES_GIZMO::GizmoSphereSelection::Count )>
viewTable = { VIEW3D_TYPE::VIEW3D_RIGHT, VIEW3D_TYPE::VIEW3D_LEFT, VIEW3D_TYPE::VIEW3D_BACK,
VIEW3D_TYPE::VIEW3D_FRONT, VIEW3D_TYPE::VIEW3D_TOP, VIEW3D_TYPE::VIEW3D_BOTTOM };
SPHERES_GIZMO::GizmoSphereSelection selectedGizmoSphere = m_3d_render_opengl->getSelectedGizmoSphere();
int index = static_cast<int>( selectedGizmoSphere );
if( index >= 0 && index < static_cast<int>( viewTable.size() ) )
{
SetView3D( viewTable[index] );
}
m_3d_render_opengl->resetSelectedGizmoSphere();
m_is_currently_painting.clear();
}
@ -859,6 +872,22 @@ void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
m_mouse_is_moving = false;
restart_editingTimeOut_Timer();
}
wxSize logicalSize = GetClientSize();
int logicalW = logicalSize.GetWidth();
int logicalH = logicalSize.GetHeight();
int gizmo_x, gizmo_y, gizmo_width, gizmo_height;
std::tie( gizmo_x, gizmo_y, gizmo_width, gizmo_height ) = m_3d_render_opengl->getGizmoViewport();
float scaleX = static_cast<float>( gizmo_width ) / logicalW;
float scaleY = static_cast<float>( gizmo_height ) / logicalH;
int scaledMouseX = static_cast<int>( event.GetX() * scaleX );
int scaledMouseY = static_cast<int>( ( logicalH - event.GetY() ) * scaleY );
m_3d_render_opengl->handleGizmoMouseInput( scaledMouseX, scaledMouseY );
Refresh();
}

368
3d-viewer/3d_rendering/opengl/3d_spheres_gizmo.cpp

@ -0,0 +1,368 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2025, Damjan Prerad <damjanovmail@gmail.com>
* Copyright (C) The 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 <glm/geometric.hpp>
#include "3d_spheres_gizmo.h"
SPHERES_GIZMO::~SPHERES_GIZMO()
{
if( m_quadric )
{
gluDeleteQuadric( m_quadric );
m_quadric = nullptr;
}
}
SPHERES_GIZMO::SPHERES_GIZMO( int aGizmoPosX, int aGizmoPosY )
{
m_gizmoPosX = aGizmoPosX;
m_gizmoPosY = aGizmoPosY;
m_quadric = gluNewQuadric();
gluQuadricNormals( m_quadric, GLU_SMOOTH );
}
void SPHERES_GIZMO::setViewport( int ax, int ay, int aWidth, int aHeight )
{
m_viewportX = ax;
m_viewportY = ay;
m_viewportW = aWidth;
m_viewportH = aHeight;
}
std::tuple<int, int, int, int> SPHERES_GIZMO::getViewport() const
{
return std::make_tuple( m_viewportX, m_viewportY, m_viewportW, m_viewportH );
}
void SPHERES_GIZMO::setGizmoPosition( int ax, int ay )
{
m_gizmoPosX = ax;
m_gizmoPosY = ay;
}
void SPHERES_GIZMO::setGizmoMaterial()
{
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
}
void SPHERES_GIZMO::handleMouseInput( int aMouseX, int aMouseY )
{
int smallViewportW = m_viewportH / 8;
int smallViewportH = m_viewportH / 8;
bool inside = ( aMouseX >= m_gizmoPosX && aMouseX <= m_gizmoPosX + smallViewportW && aMouseY >= m_gizmoPosY
&& aMouseY <= m_gizmoPosY + smallViewportH );
if( inside )
{
m_ndcX = 2.0f * static_cast<float>( aMouseX - m_gizmoPosX ) / smallViewportW - 1.0f;
m_ndcY = 2.0f * static_cast<float>( aMouseY - m_gizmoPosY ) / smallViewportH - 1.0f;
}
else
{
m_ndcX = -1.0f;
m_ndcY = -1.0f;
}
}
void SPHERES_GIZMO::render3dSpheresGizmo( glm::mat4 aCameraRotationMatrix )
{
float fov = 60.0f;
glDisable( GL_CULL_FACE );
// Set up a square viewport (Y x Y)
glViewport( m_gizmoPosX, m_gizmoPosY, m_viewportH / 8, m_viewportH / 8 );
glClear( GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( fov, 1.0f, 0.001f, 2.0f * RANGE_SCALE_3D );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glm::mat4 TranslationMatrix = glm::translate( glm::mat4( 1.0f ), SFVEC3F( 0.0f, 0.0f, -( m_arrowSize * 2.75f ) ) );
glm::mat4 ViewMatrix = TranslationMatrix * aCameraRotationMatrix;
glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
setGizmoMaterial();
// Intersection test
glm::mat4 proj = glm::perspective( glm::radians( fov ), 1.0f, 0.001f, 2.0f * RANGE_SCALE_3D );
glm::mat4 invVP = glm::inverse( proj * ViewMatrix );
glm::vec4 rayStartNDC( m_ndcX, m_ndcY, -1.0f, 1.0f );
glm::vec4 rayEndNDC( m_ndcX, m_ndcY, 1.0f, 1.0f );
glm::vec4 rayStartWorld = invVP * rayStartNDC;
rayStartWorld /= rayStartWorld.w;
glm::vec4 rayEndWorld = invVP * rayEndNDC;
rayEndWorld /= rayEndWorld.w;
glm::vec3 rayOrigin = glm::vec3( rayStartWorld );
glm::vec3 rayDirection = glm::normalize( glm::vec3( rayEndWorld - rayStartWorld ) );
auto intersects =
[]( const glm::vec3& aRayOrigin, const glm::vec3& aRayDir, const glm::vec3& aSphereCenter, float aRadius )
{
glm::vec3 L = aSphereCenter - aRayOrigin;
float tca = glm::dot( L, aRayDir );
float d2 = glm::dot( L, L ) - tca * tca;
return d2 <= aRadius * aRadius;
};
int clickedIndex = -1;
m_selectedGizmoSphere = GizmoSphereSelection::None;
for( size_t i = 0; i < m_spheres.size(); ++i )
{
const auto& sphere = m_spheres[i];
if( intersects( rayOrigin, rayDirection, sphere.m_position, sphere.m_radius ) )
{
clickedIndex = static_cast<int>( i );
m_selectedGizmoSphere = static_cast<GizmoSphereSelection>( i );
break; // only pick the first intersected sphere
}
}
// Update colors
for( size_t i = 0; i < m_spheres.size(); ++i )
{
if( static_cast<int>( i ) == clickedIndex )
{
m_spheres[i].m_color = { 1.0f, 1.0f, 1.0f }; // White
}
else
{
m_spheres[i].m_color = m_spheres[i].m_originalColor; // Restore default
}
}
// Intersection test done
auto drawBillboardCircle = []( const glm::vec3& aCenter, float aRadius, const glm::vec3& aColor,
const glm::vec3& aCamRight, const glm::vec3& aCamUp, int aSegments = 64 )
{
float thickness = aRadius * 0.4f;
glColor3f( aColor.r, aColor.g, aColor.b );
glBegin( GL_TRIANGLE_STRIP );
for( int i = 0; i <= aSegments; ++i )
{
float angle = 2.0f * glm::pi<float>() * i / aSegments;
glm::vec3 dir = cos( angle ) * aCamRight + sin( angle ) * aCamUp;
glm::vec3 outer = aCenter + dir * ( aRadius + thickness * 0.5f );
glm::vec3 inner = aCenter + dir * ( aRadius - thickness * 0.5f );
glVertex3f( outer.x, outer.y, outer.z );
glVertex3f( inner.x, inner.y, inner.z );
}
glEnd();
};
glm::vec3 camRight( aCameraRotationMatrix[0][0], aCameraRotationMatrix[1][0], aCameraRotationMatrix[2][0] );
glm::vec3 camUp( aCameraRotationMatrix[0][1], aCameraRotationMatrix[1][1], aCameraRotationMatrix[2][1] );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
for( const auto& sphere : m_spheres )
{
glColor4f( sphere.m_color.r, sphere.m_color.g, sphere.m_color.b, 0.3f );
glPushMatrix();
glTranslatef( sphere.m_position.x, sphere.m_position.y, sphere.m_position.z );
if( m_quadric )
{
gluSphere( m_quadric, sphere.m_radius, 32, 32 );
}
glPopMatrix();
drawBillboardCircle( sphere.m_position, sphere.m_radius, sphere.m_color, camRight, camUp );
}
// Draw sphere labels
glDisable( GL_DEPTH_TEST );
glDisable( GL_LIGHTING );
std::array<std::string, 6> labels = { "X", "", "Y", "", "Z", "" };
// View direction (camera looks along negative Z in view space)
// So we offset a little toward the camera to avoid z-fighting
glm::vec3 offset = glm::normalize( -rayDirection ) * 0.02f;
glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
auto drawX = []( const glm::vec3& aPos, float aSize, const glm::vec3& aColor, const glm::vec3& aCamRight,
const glm::vec3& aCamUp )
{
glColor3f( aColor.r, aColor.g, aColor.b );
glLineWidth( 3.0f );
float h = aSize * 0.5f;
// Define two diagonal line directions in camera-facing plane
glm::vec3 dir1 = ( -aCamRight + aCamUp ) * h; // one diagonal
glm::vec3 dir2 = ( -aCamRight - aCamUp ) * h; // other diagonal
glBegin( GL_LINES );
glVertex3f( ( aPos - dir1 ).x, ( aPos - dir1 ).y, ( aPos - dir1 ).z );
glVertex3f( ( aPos + dir1 ).x, ( aPos + dir1 ).y, ( aPos + dir1 ).z );
glVertex3f( ( aPos - dir2 ).x, ( aPos - dir2 ).y, ( aPos - dir2 ).z );
glVertex3f( ( aPos + dir2 ).x, ( aPos + dir2 ).y, ( aPos + dir2 ).z );
glEnd();
};
auto drawY = []( const glm::vec3& aPos, float aSize, const glm::vec3& aColor, const glm::vec3& aCamRight,
const glm::vec3& aCamUp )
{
glColor3f( aColor.r, aColor.g, aColor.b );
glLineWidth( 3.0f );
float h = aSize * 0.5f;
// Top-left and top-right in screen plane
glm::vec3 topLeft = aPos + aCamUp * h - aCamRight * h;
glm::vec3 topRight = aPos + aCamUp * h + aCamRight * h;
glm::vec3 bottom = aPos - aCamUp * h;
glBegin( GL_LINES );
glVertex3f( topLeft.x, topLeft.y, topLeft.z );
glVertex3f( aPos.x, aPos.y, aPos.z );
glVertex3f( topRight.x, topRight.y, topRight.z );
glVertex3f( aPos.x, aPos.y, aPos.z );
glVertex3f( aPos.x, aPos.y, aPos.z );
glVertex3f( bottom.x, bottom.y, bottom.z );
glEnd();
};
auto drawZ = []( const glm::vec3& aPos, float aSize, const glm::vec3& aColor, const glm::vec3& aCamRight,
const glm::vec3& aCamUp )
{
glColor3f( aColor.r, aColor.g, aColor.b );
glLineWidth( 3.0f );
float h = aSize * 0.5f;
// Define corners in screen plane relative to camera
glm::vec3 topLeft = aPos + aCamUp * h - aCamRight * h;
glm::vec3 topRight = aPos + aCamUp * h + aCamRight * h;
glm::vec3 bottomLeft = aPos - aCamUp * h - aCamRight * h;
glm::vec3 bottomRight = aPos - aCamUp * h + aCamRight * h;
glBegin( GL_LINE_STRIP );
glVertex3f( topLeft.x, topLeft.y, topLeft.z );
glVertex3f( topRight.x, topRight.y, topRight.z );
glVertex3f( bottomLeft.x, bottomLeft.y, bottomLeft.z );
glVertex3f( bottomRight.x, bottomRight.y, bottomRight.z );
glEnd();
};
for( size_t i = 0; i < m_spheres.size(); ++i )
{
if( labels[i].empty() )
continue;
glm::vec3 textPos = m_spheres[i].m_position + offset;
const std::string& label = labels[i];
if( label == "X" )
{
drawX( textPos, 0.30f, glm::vec3( 0.0f ), camRight, camUp );
}
else if( label == "Y" )
{
drawY( textPos, 0.30f, glm::vec3( 0.0f ), camRight, camUp );
}
else if( label == "Z" )
{
drawZ( textPos, 0.30f, glm::vec3( 0.0f ), camRight, camUp );
}
}
glEnable( GL_LIGHTING );
glEnable( GL_DEPTH_TEST );
// Draw lines only to the positive axis spheres
glLineWidth( 2.0f );
glBegin( GL_LINES );
glColor3f( 0.9f, 0.0f, 0.0f ); // X+
glVertex3f( 0.0f, 0.0f, 0.0f );
glVertex3f( m_arrowSize, 0.0f, 0.0f );
glColor3f( 0.0f, 0.9f, 0.0f ); // Y+
glVertex3f( 0.0f, 0.0f, 0.0f );
glVertex3f( 0.0f, m_arrowSize, 0.0f );
glColor3f( 0.0f, 0.0f, 0.9f ); // Z+
glVertex3f( 0.0f, 0.0f, 0.0f );
glVertex3f( 0.0f, 0.0f, m_arrowSize );
glEnd();
glEnable( GL_CULL_FACE );
}
SPHERES_GIZMO::GizmoSphereSelection SPHERES_GIZMO::getSelectedGizmoSphere() const
{
return m_selectedGizmoSphere;
}
void SPHERES_GIZMO::resetSelectedGizmoSphere()
{
m_selectedGizmoSphere = GizmoSphereSelection::None;
m_ndcX = -1.0f;
m_ndcY = -1.0f;
}

133
3d-viewer/3d_rendering/opengl/3d_spheres_gizmo.h

@ -0,0 +1,133 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2025, Damjan Prerad <damjanovmail@gmail.com>
* Copyright (C) The 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 _3D_SPHERES_GIZMO_H_
#define _3D_SPHERES_GIZMO_H_
#include "../render_3d_base.h"
/**
* @class SPHERES_GIZMO
* @brief Renders a set of colored spheres in 3D space that act as a directional orientation gizmo.
*/
class SPHERES_GIZMO
{
public:
SPHERES_GIZMO( int aGizmoPosX, int aGizmoPosY );
~SPHERES_GIZMO();
void setViewport( int ax, int ay, int aWidth, int aHeight );
std::tuple<int, int, int, int> getViewport() const;
void setGizmoPosition( int ax, int ay );
void handleMouseInput( int aMouseX, int aMouseY );
/**
* @enum GizmoSphereSelection
* @brief Enum to indicate which sphere (direction) is selected.
*/
enum class GizmoSphereSelection
{
None = -1, ///< No sphere selected.
Right = 0, ///< +X direction.
Left, ///< -X direction.
Back, ///< +Y direction.
Front, ///< -Y direction.
Top, ///< +Z direction.
Bottom, ///< -Z direction.
Count ///< Number of selectable spheres.
};
GizmoSphereSelection getSelectedGizmoSphere() const;
void resetSelectedGizmoSphere();
void render3dSpheresGizmo( glm::mat4 aCameraRotationMatrix );
private:
void setGizmoMaterial();
GLUquadric* m_quadric = nullptr;
int m_gizmoPosX = 0;
int m_gizmoPosY = 0;
int m_viewportX = 0;
int m_viewportY = 0;
int m_viewportW = 0;
int m_viewportH = 0;
float m_ndcX = -1.0f;
float m_ndcY = -1.0f;
struct GizmoSphere
{
glm::vec3 m_position;
float m_radius;
glm::vec3 m_labelPosition;
glm::vec3 m_color;
glm::vec3 m_originalColor;
};
// Define sphere positions
const float m_arrowSize = RANGE_SCALE_3D * 0.20f;
const float m_sphereRadius = 0.05f * RANGE_SCALE_3D;
/**
* @brief List of all directional gizmo spheres.
* The order follows the GizmoSphereSelection enum.
*/
std::array<GizmoSphere, 6> m_spheres = { { { { m_arrowSize, 0.0f, 0.0f },
m_sphereRadius,
{ m_arrowSize + 0.02f, 0.0f, 0.0f },
{ 0.9f, 0.0f, 0.0f },
{ 0.9f, 0.0f, 0.0f } },
{ { -m_arrowSize, 0.0f, 0.0f },
m_sphereRadius,
{ -m_arrowSize - 0.02f, 0.0f, 0.0f },
{ 0.4f, 0.0f, 0.0f },
{ 0.4f, 0.0f, 0.0f } },
{ { 0.0f, m_arrowSize, 0.0f },
m_sphereRadius,
{ 0.0f, m_arrowSize + 0.02f, 0.0f },
{ 0.0f, 0.9f, 0.0f },
{ 0.0f, 0.9f, 0.0f } },
{ { 0.0f, -m_arrowSize, 0.0f },
m_sphereRadius,
{ 0.0f, -m_arrowSize - 0.02f, 0.0f },
{ 0.0f, 0.4f, 0.0f },
{ 0.0f, 0.4f, 0.0f } },
{ { 0.0f, 0.0f, m_arrowSize },
m_sphereRadius,
{ 0.0f, 0.0f, m_arrowSize + 0.02f },
{ 0.0f, 0.0f, 0.9f },
{ 0.0f, 0.0f, 0.9f } },
{ { 0.0f, 0.0f, -m_arrowSize },
m_sphereRadius,
{ 0.0f, 0.0f, -m_arrowSize - 0.02f },
{ 0.0f, 0.0f, 0.4f },
{ 0.0f, 0.0f, 0.4f } } } };
GizmoSphereSelection m_selectedGizmoSphere = GizmoSphereSelection::None;
};
#endif // _3D_SPHERES_GIZMO_H_

57
3d-viewer/3d_rendering/opengl/render_3d_opengl.cpp

@ -81,6 +81,8 @@ RENDER_3D_OPENGL::RENDER_3D_OPENGL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdap
m_boardWithHoles = nullptr;
m_3dModelMap.clear();
m_spheres_gizmo = new SPHERES_GIZMO( 4, 4 );
}
@ -104,9 +106,13 @@ void RENDER_3D_OPENGL::SetCurWindowSize( const wxSize& aSize )
{
if( m_windowSize != aSize )
{
int viewport[4];
int fbWidth, fbHeight;
glGetIntegerv( GL_VIEWPORT, viewport );
m_windowSize = aSize;
glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
setGizmoViewport( 0, 0, m_windowSize.x, m_windowSize.y );
// Initialize here any screen dependent data here
}
}
@ -139,42 +145,33 @@ void RENDER_3D_OPENGL::setLightBottom( bool enabled )
}
void RENDER_3D_OPENGL::render3dArrows()
void RENDER_3D_OPENGL::resetSelectedGizmoSphere()
{
const float arrow_size = RANGE_SCALE_3D * 0.30f;
glDisable( GL_CULL_FACE );
// YxY squared view port, this is on propose
glViewport( 4, 4, m_windowSize.y / 8 , m_windowSize.y / 8 );
glClear( GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 45.0f, 1.0f, 0.001f, RANGE_SCALE_3D );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
m_spheres_gizmo->resetSelectedGizmoSphere();
}
const glm::mat4 TranslationMatrix =
glm::translate( glm::mat4( 1.0f ), SFVEC3F( 0.0f, 0.0f, -( arrow_size * 2.75f ) ) );
const glm::mat4 ViewMatrix = TranslationMatrix * m_camera.GetRotationMatrix();
SPHERES_GIZMO::GizmoSphereSelection RENDER_3D_OPENGL::getSelectedGizmoSphere() const
{
return m_spheres_gizmo->getSelectedGizmoSphere();
}
glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
setArrowMaterial();
void RENDER_3D_OPENGL::setGizmoViewport( int x, int y, int width, int height )
{
m_spheres_gizmo->setViewport( x, y, width, height );
}
glColor3f( 0.9f, 0.0f, 0.0f );
DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( arrow_size, 0.0f, 0.0f ), 0.275f );
glColor3f( 0.0f, 0.9f, 0.0f );
DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, arrow_size, 0.0f ), 0.275f );
std::tuple<int, int, int, int> RENDER_3D_OPENGL::getGizmoViewport() const
{
return m_spheres_gizmo->getViewport();
}
glColor3f( 0.0f, 0.0f, 0.9f );
DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, arrow_size ), 0.275f );
glEnable( GL_CULL_FACE );
void RENDER_3D_OPENGL::handleGizmoMouseInput( int mouseX, int mouseY )
{
m_spheres_gizmo->handleMouseInput( mouseX, mouseY );
}
@ -826,8 +823,8 @@ bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
}
// Render 3D arrows
if( cfg.show_axis )
render3dArrows();
if( cfg.show_navigator )
m_spheres_gizmo->render3dSpheresGizmo( m_camera.GetRotationMatrix() );
// Return back to the original viewport (this is important if we want
// to take a screenshot after the render)

13
3d-viewer/3d_rendering/opengl/render_3d_opengl.h

@ -27,6 +27,7 @@
#include "../render_3d_base.h"
#include "layer_triangles.h"
#include "3d_spheres_gizmo.h"
#include "../raytracing/shapes2D/polygon_2d.h"
#include "../raytracing/shapes2D/triangle_2d.h"
@ -70,6 +71,11 @@ public:
* Load footprint models if they are not already loaded, i.e. if m_3dModelMap is empty
*/
void Load3dModelsIfNeeded();
void handleGizmoMouseInput( int mouseX, int mouseY );
void setGizmoViewport( int x, int y, int width, int height );
std::tuple<int, int, int, int> getGizmoViewport() const;
SPHERES_GIZMO::GizmoSphereSelection getSelectedGizmoSphere() const;
void resetSelectedGizmoSphere();
private:
OPENGL_RENDER_LIST* generateHoles( const LIST_OBJECT2D& aListHolesObject2d,
@ -168,8 +174,6 @@ private:
void setLightTop( bool enabled );
void setLightBottom( bool enabled );
void render3dArrows();
/**
* Create a 3D grid to an OpenGL display list.
*
@ -239,8 +243,9 @@ private:
BOARD_ITEM* m_currentRollOverItem;
SHAPE_POLY_SET m_antiBoardPolys; ///< The negative polygon representation of the board
///< outline.
SHAPE_POLY_SET m_antiBoardPolys; ///< The negative polygon representation of the board
///< outline.
SPHERES_GIZMO* m_spheres_gizmo;
};
#endif // RENDER_3D_OPENGL_H

7
3d-viewer/3d_viewer/eda_3d_viewer_frame.cpp

@ -257,10 +257,9 @@ void EDA_3D_VIEWER_FRAME::setupUIConditions()
{
return m_boardAdapter.m_Cfg->m_Render.show_model_bbox;
};
auto showAxes =
[this]( const SELECTION& aSel )
auto showNavig = [this]( const SELECTION& aSel )
{
return m_boardAdapter.m_Cfg->m_Render.show_axis;
return m_boardAdapter.m_Cfg->m_Render.show_navigator;
};
auto ortho =
[this]( const SELECTION& )
@ -284,7 +283,7 @@ void EDA_3D_VIEWER_FRAME::setupUIConditions()
mgr->SetConditions( EDA_3D_ACTIONS::showDNP, ACTION_CONDITIONS().Check( show_DNP ) );
mgr->SetConditions( EDA_3D_ACTIONS::showBBoxes, ACTION_CONDITIONS().Check( showBBoxes ) );
mgr->SetConditions( EDA_3D_ACTIONS::showAxis, ACTION_CONDITIONS().Check( showAxes ) );
mgr->SetConditions( EDA_3D_ACTIONS::showNavigator, ACTION_CONDITIONS().Check( showNavig ) );
mgr->SetConditions( EDA_3D_ACTIONS::noGrid, GridSizeCheck( GRID3D_TYPE::NONE ) );
mgr->SetConditions( EDA_3D_ACTIONS::show10mmGrid, GridSizeCheck( GRID3D_TYPE::GRID_10MM ) );

10
3d-viewer/3d_viewer/eda_3d_viewer_settings.cpp

@ -103,7 +103,7 @@ PARAM_LAYER_PRESET_3D::PARAM_LAYER_PRESET_3D( const std::string& aPath,
LAYER( "user_drawings", LAYER_3D_USER_DRAWINGS );
LAYER( "user_eco1", LAYER_3D_USER_ECO1 );
LAYER( "user_eco2", LAYER_3D_USER_ECO2 );
LAYER( "3d_axes", LAYER_3D_AXES );
LAYER( "3d_navigator", LAYER_3D_NAVIGATOR );
LAYER( "th_models", LAYER_3D_TH_MODELS );
LAYER( "smd_models", LAYER_3D_SMD_MODELS );
LAYER( "virtual_models", LAYER_3D_VIRTUAL_MODELS );
@ -342,8 +342,8 @@ EDA_3D_VIEWER_SETTINGS::EDA_3D_VIEWER_SETTINGS() :
m_params.emplace_back( new PARAM<bool>( "render.show_adhesive",
&m_Render.show_adhesive, true ) );
m_params.emplace_back( new PARAM<bool>( "render.show_axis",
&m_Render.show_axis, true ) );
m_params.emplace_back( new PARAM<bool>( "render.show_navigator",
&m_Render.show_navigator, true ) );
m_params.emplace_back( new PARAM<bool>( "render.show_board_body",
&m_Render.show_board_body, true ) );
m_params.emplace_back( new PARAM<bool>( "render.show_comments",
@ -489,7 +489,7 @@ EDA_3D_VIEWER_SETTINGS::EDA_3D_VIEWER_SETTINGS() :
legacyColorMap[483] = "virtual_models";
legacyColorMap[484] = "non_pos_file_models";
legacyColorMap[485] = "dnp_models";
legacyColorMap[486] = "3d_axes";
legacyColorMap[486] = "3d_navigator";
legacyColorMap[487] = "bounding_boxes";
legacyColorMap[488] = "off_board_silk";
@ -579,7 +579,7 @@ bool EDA_3D_VIEWER_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg )
ret &= fromLegacy<bool>( aCfg, "Render_RAY_Shadows", "render.raytrace_shadows" );
ret &= fromLegacy<bool>( aCfg, "ShowRealisticMode", "render.realistic" );
ret &= fromLegacy<bool>( aCfg, "ShowAdhesiveLayers", "render.show_adhesive" );
ret &= fromLegacy<bool>( aCfg, "ShowAxis", "render.show_axis" );
ret &= fromLegacy<bool>( aCfg, "ShowNavigator", "render.show_navigator" );
ret &= fromLegacy<bool>( aCfg, "ShowBoardBody", "render.show_board_body" );
ret &= fromLegacy<bool>( aCfg, "ShowCommentsLayers", "render.show_comments" );
ret &= fromLegacy<bool>( aCfg, "ShowEcoLayers", "render.show_eco" );

2
3d-viewer/3d_viewer/eda_3d_viewer_settings.h

@ -126,7 +126,7 @@ public:
std::vector<int> raytrace_lightAzimuth; // 0 .. 359
bool show_adhesive;
bool show_axis;
bool show_navigator;
bool show_board_body;
bool show_comments;
bool show_drawings;

18
3d-viewer/3d_viewer/tools/eda_3d_actions.cpp

@ -363,16 +363,16 @@ TOOL_ACTION EDA_3D_ACTIONS::showDNP( TOOL_ACTION_ARGS()
TOOL_ACTION EDA_3D_ACTIONS::showBBoxes( TOOL_ACTION_ARGS()
.Name( "3DViewer.Control.showBoundingBoxes" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Show Model Bounding Boxes" ) )
.Tooltip( _( "Show 3D model bounding boxes in realtime renderer" ) )
.Icon( BITMAPS::ortho )
.Flags( AF_NONE ) );
TOOL_ACTION EDA_3D_ACTIONS::showAxis( TOOL_ACTION_ARGS()
.Name( "3DViewer.Control.showAxis" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Show 3D Axis" ) )
.FriendlyName( _( "Show Model Bounding Boxes" ) )
.Tooltip( _( "Show 3D model bounding boxes in realtime renderer" ) )
.Icon( BITMAPS::ortho )
.Flags( AF_NONE ) );
TOOL_ACTION EDA_3D_ACTIONS::showNavigator( TOOL_ACTION_ARGS()
.Name( "3DViewer.Control.showNavigator" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Show 3D Navigator" ) )
.Icon( BITMAPS::axis3d_front )
.Flags( AF_NONE ) );

2
3d-viewer/3d_viewer/tools/eda_3d_actions.h

@ -87,7 +87,7 @@ public:
static TOOL_ACTION showNotInPosFile;
static TOOL_ACTION showDNP;
static TOOL_ACTION showBBoxes;
static TOOL_ACTION showAxis;
static TOOL_ACTION showNavigator;
static TOOL_ACTION showLayersManager;
};

4
3d-viewer/3d_viewer/tools/eda_3d_controller.cpp

@ -256,8 +256,8 @@ int EDA_3D_CONTROLLER::ToggleVisibility( const TOOL_EVENT& aEvent )
flipLayer( LAYER_3D_MODELS_NOT_IN_POS );
else if( aEvent.IsAction( &EDA_3D_ACTIONS::showDNP ) )
flipLayer( LAYER_3D_MODELS_MARKED_DNP );
else if( aEvent.IsAction( &EDA_3D_ACTIONS::showAxis ) )
flipLayer( LAYER_3D_AXES );
else if( aEvent.IsAction( &EDA_3D_ACTIONS::showNavigator ) )
flipLayer( LAYER_3D_NAVIGATOR );
else if( aEvent.IsAction( &EDA_3D_ACTIONS::showBBoxes ) )
flipLayer( LAYER_3D_BOUNDING_BOXES );
}

1
3d-viewer/CMakeLists.txt

@ -38,6 +38,7 @@ set(3D-VIEWER_SRCS
3d_rendering/opengl/create_scene.cpp
3d_rendering/opengl/render_3d_opengl.cpp
3d_rendering/opengl/layer_triangles.cpp
3d_rendering/opengl/3d_spheres_gizmo.cpp
${DIR_RAY_ACC}/accelerator_3d.cpp
${DIR_RAY_ACC}/bvh_packet_traversal.cpp
${DIR_RAY_ACC}/bvh_pbrt.cpp

2
3d-viewer/dialogs/appearance_controls_3D.cpp

@ -122,7 +122,7 @@ const APPEARANCE_CONTROLS_3D::APPEARANCE_SETTING_3D APPEARANCE_CONTROLS_3D::s_la
RR( _HKI( "Footprint Text" ), LAYER_FP_TEXT, _HKI( "Show all footprint text" ) ),
RR( _HKI( "Off-board Silkscreen" ), LAYER_3D_OFF_BOARD_SILK, _HKI( "Do not clip silk layers to board outline" ) ),
RR(),
RR( _HKI( "3D Axis" ), LAYER_3D_AXES, EDA_3D_ACTIONS::showAxis ),
RR( _HKI( "3D Navigator" ), LAYER_3D_NAVIGATOR, EDA_3D_ACTIONS::showNavigator ),
RR( _HKI( "Background Start" ), LAYER_3D_BACKGROUND_TOP, _HKI( "Background gradient start color" ) ),
RR( _HKI( "Background End" ), LAYER_3D_BACKGROUND_BOTTOM, _HKI( "Background gradient end color" ) ),
};

2
AUTHORS.txt

@ -622,7 +622,7 @@
欠陥電気
木 王
郝弘毅
Damjan Prerad
See git repo on GitLab for contributors at
https://gitlab.com/kicad/code/kicad/-/graphs/master

2
include/layer_ids.h

@ -601,7 +601,7 @@ enum LAYER_3D_ID : int
LAYER_3D_VIRTUAL_MODELS,
LAYER_3D_MODELS_NOT_IN_POS,
LAYER_3D_MODELS_MARKED_DNP,
LAYER_3D_AXES,
LAYER_3D_NAVIGATOR,
LAYER_3D_BOUNDING_BOXES,
LAYER_3D_OFF_BOARD_SILK,

Loading…
Cancel
Save