3D Viewer: Fix zoom limit calculations

Fixes https://gitlab.com/kicad/code/kicad/issues/9407

Fixes https://gitlab.com/kicad/code/kicad/issues/8805
This commit is contained in:
Mikolaj Wielgus 2021-10-24 19:30:40 +02:00 committed by Jon Evans
parent 363214d252
commit cf3a979711
5 changed files with 101 additions and 41 deletions

View File

@ -483,18 +483,25 @@ void EDA_3D_CANVAS::DoRePaint()
{
m_3d_render->SetCurWindowSize( clientSize );
bool reloadRaytracingForIntersectionCalculations = false;
bool reloadRaytracingForCalculations = false;
if( m_boardAdapter.GetRenderEngine() == RENDER_ENGINE::OPENGL
&& m_3d_render_opengl->IsReloadRequestPending() )
{
reloadRaytracingForIntersectionCalculations = true;
reloadRaytracingForCalculations = true;
}
requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
&activityReporter, &warningReporter );
if( reloadRaytracingForIntersectionCalculations )
// Raytracer renderer is responsible for some features also used by the OpenGL
// renderer.
// FIXME: Presumably because raytracing renderer reload is called only after current
// renderer redraw, the old zoom value stays for a single frame. This is ugly, but only
// cosmetic, so I'm not fixing that for now: I don't know how to do this without
// reloading twice (maybe it's not too bad of an idea?) or doing a complicated
// refactor.
if( reloadRaytracingForCalculations )
m_3d_render_raytracing->Reload( nullptr, nullptr, true );
}
catch( std::runtime_error& )
@ -510,7 +517,7 @@ void EDA_3D_CANVAS::DoRePaint()
if( m_render_pivot )
{
const float scale = glm::min( m_camera.ZoomGet(), 1.0f );
const float scale = glm::min( m_camera.GetZoom(), 1.0f );
render_pivot( curtime_delta_s, scale * scale );
}
@ -584,7 +591,7 @@ void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
if( m_camera_is_moving )
return;
float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
if( m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
delta_move *= 0.01f * event.GetWheelRotation();
@ -945,7 +952,7 @@ bool EDA_3D_CANVAS::SetView3D( int aKeycode )
if( m_camera_is_moving )
return false;
const float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
const float arrow_moving_time_speed = 8.0f;
bool handled = false;
@ -987,7 +994,7 @@ bool EDA_3D_CANVAS::SetView3D( int aKeycode )
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.ZoomGet(), 1/1.26f ), 1.26f ) );
request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
return true;
case WXK_END:
@ -1023,7 +1030,7 @@ bool EDA_3D_CANVAS::SetView3D( int aKeycode )
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.ZoomGet(), 0.5f ), 1.125f ) );
request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
return true;
case ID_VIEW3D_RIGHT:
@ -1069,7 +1076,7 @@ bool EDA_3D_CANVAS::SetView3D( int aKeycode )
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.ZoomGet(), 0.5f ), 1.125f ) );
request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
return true;
case ID_VIEW3D_BOTTOM:
@ -1077,7 +1084,7 @@ bool EDA_3D_CANVAS::SetView3D( int aKeycode )
m_camera.SetT0_and_T1_current_T();
m_camera.Reset_T1();
m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
return true;
case ID_VIEW3D_FLIP:

View File

@ -47,8 +47,8 @@ inline void normalise2PI( float& aAngle )
const wxChar *CAMERA::m_logTrace = wxT( "KI_TRACE_CAMERA" );
#define MIN_ZOOM 0.10f
#define MAX_ZOOM 2.00f
#define DEFAULT_MIN_ZOOM 0.10f
#define DEFAULT_MAX_ZOOM 1.20f
CAMERA::CAMERA( float aInitialDistance )
@ -61,6 +61,9 @@ CAMERA::CAMERA( float aInitialDistance )
m_projectionType = PROJECTION_TYPE::PERSPECTIVE;
m_interpolation_mode = CAMERA_INTERPOLATION::BEZIER;
m_minZoom = DEFAULT_MIN_ZOOM;
m_maxZoom = DEFAULT_MAX_ZOOM;
Reset();
}
@ -118,6 +121,21 @@ void CAMERA::Reset_T1()
}
void CAMERA::zoomChanged()
{
if( m_zoom < m_minZoom )
m_zoom = m_minZoom;
if( m_zoom > m_maxZoom )
m_zoom = m_maxZoom;
m_camera_pos.z = m_camera_pos_init.z * m_zoom;
updateViewMatrix();
rebuildProjection();
}
void CAMERA::updateViewMatrix()
{
m_viewMatrix = glm::translate( glm::mat4( 1.0f ), m_camera_pos ) *
@ -159,9 +177,7 @@ void CAMERA::rebuildProjection()
return;
m_frustum.ratio = (float) m_windowSize.x / (float)m_windowSize.y;
// Consider that we can render double the length multiplied by the 2/sqrt(2)
m_frustum.farD = glm::length( m_camera_pos_init ) * 2.0f * ( 2.0f * sqrtf( 2.0f ) );
m_frustum.farD = glm::length( m_camera_pos_init ) * m_maxZoom * 2.0f;
switch( m_projectionType )
{
@ -384,6 +400,12 @@ const glm::mat4& CAMERA::GetProjectionMatrixInv() const
}
float CAMERA::GetCameraMinDimension() const
{
return -m_camera_pos_init.z * m_frustum.tang;
}
void CAMERA::ResetXYpos()
{
m_parametersChanged = true;
@ -459,40 +481,34 @@ void CAMERA::ZoomReset()
bool CAMERA::Zoom( float aFactor )
{
if( ( m_zoom == MIN_ZOOM && aFactor > 1 ) || ( m_zoom == MAX_ZOOM && aFactor < 1 )
|| aFactor == 1 )
if( ( m_zoom == m_minZoom && aFactor > 1 ) || ( m_zoom == m_maxZoom && aFactor < 1 )
|| aFactor == 1 )
{
return false;
}
m_zoom /= aFactor;
if( m_zoom <= MIN_ZOOM )
m_zoom = MIN_ZOOM;
if( m_zoom >= MAX_ZOOM )
m_zoom = MAX_ZOOM;
m_camera_pos.z = m_camera_pos_init.z * m_zoom;
updateViewMatrix();
rebuildProjection();
zoomChanged();
return true;
}
bool CAMERA::Zoom_T1( float aFactor )
{
if( ( m_zoom == MIN_ZOOM && aFactor > 1 ) || ( m_zoom == MAX_ZOOM && aFactor < 1 )
|| aFactor == 1 )
if( ( m_zoom == m_minZoom && aFactor > 1 ) || ( m_zoom == m_maxZoom && aFactor < 1 )
|| aFactor == 1 )
{
return false;
}
m_zoom_t1 = m_zoom / aFactor;
if( m_zoom_t1 < MIN_ZOOM )
m_zoom_t1 = MIN_ZOOM;
if( m_zoom_t1 < m_minZoom )
m_zoom_t1 = m_minZoom;
if( m_zoom_t1 > MAX_ZOOM )
m_zoom_t1 = MAX_ZOOM;
if( m_zoom_t1 > m_maxZoom )
m_zoom_t1 = m_maxZoom;
m_camera_pos_t1.z = m_camera_pos_init.z * m_zoom_t1;
@ -500,12 +516,6 @@ bool CAMERA::Zoom_T1( float aFactor )
}
float CAMERA::ZoomGet() const
{
return m_zoom;
}
void CAMERA::RotateX( float aAngleInRadians )
{
m_rotate_aux.x += aAngleInRadians;

View File

@ -128,6 +128,9 @@ public:
const SFVEC3F& GetLookAtPos_T1() const { return m_lookat_pos_t1; }
const SFVEC3F& GetCameraPos() const { return m_camera_pos; }
const SFVEC3F& GetCameraInitPos() const { return m_camera_pos_init; }
float GetCameraMinDimension() const;
/**
* Calculate a new mouse drag position
@ -171,7 +174,21 @@ public:
bool Zoom_T1( float aFactor );
float ZoomGet() const ;
float GetZoom() const { return m_zoom; }
float GetMinZoom() { return m_minZoom; }
void SetMinZoom( float minZoom )
{
m_minZoom = minZoom;
zoomChanged();
}
float GetMaxZoom() { return m_maxZoom; }
void SetMaxZoom( float maxZoom )
{
m_maxZoom = maxZoom;
zoomChanged();
}
void RotateX( float aAngleInRadians );
void RotateY( float aAngleInRadians );
@ -236,6 +253,7 @@ public:
void MakeRayAtCurrrentMousePosition( SFVEC3F& aOutOrigin, SFVEC3F& aOutDirection ) const;
protected:
void zoomChanged();
void rebuildProjection();
void updateFrustum();
void updateViewMatrix();
@ -249,6 +267,12 @@ protected:
float m_zoom_t0;
float m_zoom_t1;
/**
* Possible 3D zoom range
*/
float m_minZoom;
float m_maxZoom;
/**
* The window size that this camera is working.
*/

View File

@ -917,6 +917,21 @@ void RENDER_3D_RAYTRACE::Reload( REPORTER* aStatusReporter, REPORTER* aWarningRe
}
}
// Set min. and max. zoom range. This doesn't really fit here, but moving this outside of this
// class would require reimplementing bounding box calculation (feel free to do this if you
// have time and patience).
if( m_objectContainer.GetList().size() > 0 )
{
/*float ratio = std::max( 1.0f, m_objectContainer.GetBBox().GetMaxDimension()
/ m_boardAdapter.GetBBox().GetMaxDimension() );*/
float ratio =
std::max( 1.0f, m_objectContainer.GetBBox().GetMaxDimension() / RANGE_SCALE_3D );
m_camera.SetMaxZoom( m_camera.GetMaxZoom() * ratio );
m_camera.SetMinZoom( static_cast<float>( MIN_DISTANCE_IU * m_boardAdapter.BiuTo3dUnits()
/ -m_camera.GetCameraInitPos().z ) );
}
// Create an accelerator
delete m_accelerator;
m_accelerator = new BVH_PBRT( m_objectContainer, 8, SPLITMETHOD::MIDDLE );

View File

@ -55,6 +55,10 @@ typedef enum
class RENDER_3D_RAYTRACE : 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 );
~RENDER_3D_RAYTRACE();