3d-viewer: Navigation gizmo for 3D viewer
ADDED: Blender-like navigational gizmo - new feature Fixes https://gitlab.com/kicad/code/kicad/-/issues/16956
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
m_spheres_gizmo->resetSelectedGizmoSphere();
|
||||
}
|
||||
|
||||
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 );
|
||||
SPHERES_GIZMO::GizmoSphereSelection RENDER_3D_OPENGL::getSelectedGizmoSphere() const
|
||||
{
|
||||
return m_spheres_gizmo->getSelectedGizmoSphere();
|
||||
}
|
||||
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadIdentity();
|
||||
gluPerspective( 45.0f, 1.0f, 0.001f, RANGE_SCALE_3D );
|
||||
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
void RENDER_3D_OPENGL::setGizmoViewport( int x, int y, int width, int height )
|
||||
{
|
||||
m_spheres_gizmo->setViewport( x, y, width, height );
|
||||
}
|
||||
|
||||
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();
|
||||
std::tuple<int, int, int, int> RENDER_3D_OPENGL::getGizmoViewport() const
|
||||
{
|
||||
return m_spheres_gizmo->getViewport();
|
||||
}
|
||||
|
||||
glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
|
||||
|
||||
setArrowMaterial();
|
||||
|
||||
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 );
|
||||
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ) );
|
||||
|
||||
@@ -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" );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ) );
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" ) ),
|
||||
};
|
||||
|
||||
+1
-1
@@ -622,7 +622,7 @@
|
||||
欠陥電気
|
||||
木 王
|
||||
郝弘毅
|
||||
|
||||
Damjan Prerad
|
||||
|
||||
See git repo on GitLab for contributors at
|
||||
https://gitlab.com/kicad/code/kicad/-/graphs/master
|
||||
+1
-1
@@ -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,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user