Documented.
Fixed rough Z buffer quantization issues with ortographic camera. Initial implementation of a perspective camera.
This commit is contained in:
parent
17d9c8c9dd
commit
55218c8c4d
1 changed files with 93 additions and 17 deletions
|
@ -1,3 +1,19 @@
|
||||||
|
# Implements pure perl packages
|
||||||
|
#
|
||||||
|
# Slic3r::GUI::3DScene::Base;
|
||||||
|
# Slic3r::GUI::3DScene::Volume;
|
||||||
|
# Slic3r::GUI::3DScene;
|
||||||
|
#
|
||||||
|
# It uses static methods of a C++ class Slic3r::GUI::_3DScene::GLVertexArray
|
||||||
|
# for efficient building of vertex arrays for OpenGL rendering.
|
||||||
|
#
|
||||||
|
# Slic3r::GUI::Plater::3D derives from Slic3r::GUI::3DScene,
|
||||||
|
# Slic3r::GUI::Plater::3DPreview, Slic3r::GUI::Plater::ObjectCutDialog and Slic3r::GUI::Plater::ObjectPartsPanel
|
||||||
|
# own $self->{canvas} of the Slic3r::GUI::3DScene type.
|
||||||
|
#
|
||||||
|
# Therefore the 3DScene supports renderng of STLs, extrusions and cutting planes,
|
||||||
|
# and camera manipulation.
|
||||||
|
|
||||||
package Slic3r::GUI::3DScene::Base;
|
package Slic3r::GUI::3DScene::Base;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
@ -6,12 +22,16 @@ use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL
|
||||||
# must load OpenGL *before* Wx::GLCanvas
|
# must load OpenGL *before* Wx::GLCanvas
|
||||||
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
|
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
|
||||||
use base qw(Wx::GLCanvas Class::Accessor);
|
use base qw(Wx::GLCanvas Class::Accessor);
|
||||||
use Math::Trig qw(asin);
|
use Math::Trig qw(asin tan);
|
||||||
use List::Util qw(reduce min max first);
|
use List::Util qw(reduce min max first);
|
||||||
use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale scaled_epsilon);
|
use Slic3r::Geometry qw(X Y Z MIN MAX triangle_normal normalize deg2rad tan scale unscale scaled_epsilon);
|
||||||
use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl);
|
use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl);
|
||||||
use Wx::GLCanvas qw(:all);
|
use Wx::GLCanvas qw(:all);
|
||||||
|
use Slic3r::Geometry qw(PI);
|
||||||
|
|
||||||
|
# _dirty: boolean flag indicating, that the screen has to be redrawn on EVT_IDLE.
|
||||||
|
# volumes: reference to vector of Slic3r::GUI::3DScene::Volume.
|
||||||
|
# _camera_type: 'perspective' or 'ortho'
|
||||||
__PACKAGE__->mk_accessors( qw(_quat _dirty init
|
__PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
enable_picking
|
enable_picking
|
||||||
enable_moving
|
enable_moving
|
||||||
|
@ -36,15 +56,20 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
_drag_start_pos
|
_drag_start_pos
|
||||||
_drag_start_xy
|
_drag_start_xy
|
||||||
_dragged
|
_dragged
|
||||||
|
_camera_type
|
||||||
_camera_target
|
_camera_target
|
||||||
|
_camera_distance
|
||||||
_zoom
|
_zoom
|
||||||
) );
|
) );
|
||||||
|
|
||||||
use constant TRACKBALLSIZE => 0.8;
|
use constant TRACKBALLSIZE => 0.8;
|
||||||
use constant TURNTABLE_MODE => 1;
|
use constant TURNTABLE_MODE => 1;
|
||||||
use constant GROUND_Z => -0.02;
|
use constant GROUND_Z => -0.02;
|
||||||
|
# For mesh selection: Not selected - bright yellow.
|
||||||
use constant DEFAULT_COLOR => [1,1,0];
|
use constant DEFAULT_COLOR => [1,1,0];
|
||||||
|
# For mesh selection: Selected - bright green.
|
||||||
use constant SELECTED_COLOR => [0,1,0,1];
|
use constant SELECTED_COLOR => [0,1,0,1];
|
||||||
|
# For mesh selection: Mouse hovers over the object, but object not selected yet - dark green.
|
||||||
use constant HOVER_COLOR => [0.4,0.9,0,1];
|
use constant HOVER_COLOR => [0.4,0.9,0,1];
|
||||||
|
|
||||||
# make OpenGL::Array thread-safe
|
# make OpenGL::Array thread-safe
|
||||||
|
@ -88,7 +113,10 @@ sub new {
|
||||||
$self->_zoom(1);
|
$self->_zoom(1);
|
||||||
|
|
||||||
# 3D point in model space
|
# 3D point in model space
|
||||||
|
$self->_camera_type('ortho');
|
||||||
|
# $self->_camera_type('perspective');
|
||||||
$self->_camera_target(Slic3r::Pointf3->new(0,0,0));
|
$self->_camera_target(Slic3r::Pointf3->new(0,0,0));
|
||||||
|
$self->_camera_distance(0.);
|
||||||
|
|
||||||
$self->reset_objects;
|
$self->reset_objects;
|
||||||
|
|
||||||
|
@ -272,6 +300,7 @@ sub mouse_event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Reset selection.
|
||||||
sub reset_objects {
|
sub reset_objects {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
@ -279,6 +308,7 @@ sub reset_objects {
|
||||||
$self->_dirty(1);
|
$self->_dirty(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Setup camera to view all objects.
|
||||||
sub set_viewport_from_scene {
|
sub set_viewport_from_scene {
|
||||||
my ($self, $scene) = @_;
|
my ($self, $scene) = @_;
|
||||||
|
|
||||||
|
@ -603,15 +633,31 @@ sub Resize {
|
||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
my $depth = 10 * max(@{ $self->max_bounding_box->size });
|
if ($self->_camera_type eq 'ortho') {
|
||||||
glOrtho(
|
my $depth = 1.05 * $self->max_bounding_box->radius();
|
||||||
-$x/2, $x/2, -$y/2, $y/2,
|
glOrtho(
|
||||||
-$depth, 2*$depth,
|
-$x/2, $x/2, -$y/2, $y/2,
|
||||||
);
|
-$depth, $depth,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
die "Invalid camera type: ", $self->_camera_type, "\n" if ($self->_camera_type ne 'perspective');
|
||||||
|
my $bbox_r = $self->max_bounding_box->radius();
|
||||||
|
my $fov = PI * 45. / 180.;
|
||||||
|
my $fov_tan = tan(0.5 * $fov);
|
||||||
|
my $cam_distance = 0.5 * $bbox_r / $fov_tan;
|
||||||
|
$self->_camera_distance($cam_distance);
|
||||||
|
my $nr = $cam_distance - $bbox_r * 1.1;
|
||||||
|
my $fr = $cam_distance + $bbox_r * 1.1;
|
||||||
|
$nr = 1 if ($nr < 1);
|
||||||
|
$fr = $nr + 1 if ($fr < $nr + 1);
|
||||||
|
my $h2 = $fov_tan * $nr;
|
||||||
|
my $w2 = $h2 * $x / $y;
|
||||||
|
glFrustum(-$w2, $w2, -$h2, $h2, $nr, $fr);
|
||||||
|
}
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub InitGL {
|
sub InitGL {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
@ -632,6 +678,7 @@ sub InitGL {
|
||||||
glDisable(GL_LINE_SMOOTH);
|
glDisable(GL_LINE_SMOOTH);
|
||||||
glDisable(GL_POLYGON_SMOOTH);
|
glDisable(GL_POLYGON_SMOOTH);
|
||||||
glEnable(GL_MULTISAMPLE);
|
glEnable(GL_MULTISAMPLE);
|
||||||
|
glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
|
||||||
|
|
||||||
# ambient lighting
|
# ambient lighting
|
||||||
glLightModelfv_p(GL_LIGHT_MODEL_AMBIENT, 0.3, 0.3, 0.3, 1);
|
glLightModelfv_p(GL_LIGHT_MODEL_AMBIENT, 0.3, 0.3, 0.3, 1);
|
||||||
|
@ -675,6 +722,12 @@ sub Render {
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
|
||||||
|
{
|
||||||
|
# Shift the perspective camera.
|
||||||
|
my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance);
|
||||||
|
glTranslatef(@$camera_pos);
|
||||||
|
}
|
||||||
|
|
||||||
if (TURNTABLE_MODE) {
|
if (TURNTABLE_MODE) {
|
||||||
glRotatef(-$self->_stheta, 1, 0, 0); # pitch
|
glRotatef(-$self->_stheta, 1, 0, 0); # pitch
|
||||||
|
@ -691,6 +744,9 @@ sub Render {
|
||||||
glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
|
glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
|
||||||
|
|
||||||
if ($self->enable_picking) {
|
if ($self->enable_picking) {
|
||||||
|
# Render the object for picking.
|
||||||
|
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
|
||||||
|
# Better to use software ray-casting on a bounding-box hierarchy.
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
$self->draw_volumes(1);
|
$self->draw_volumes(1);
|
||||||
glFlush();
|
glFlush();
|
||||||
|
@ -729,15 +785,18 @@ sub Render {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
|
||||||
|
# Draws a bluish bottom to top gradient over the complete screen.
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
glBegin(GL_QUADS);
|
glBegin(GL_QUADS);
|
||||||
glColor3f(0.0,0.0,0.0);
|
glColor3f(0.0,0.0,0.0);
|
||||||
glVertex2f(-1.0,-1.0);
|
glVertex3f(-1.0,-1.0, 1.0);
|
||||||
glVertex2f(1,-1.0);
|
glVertex3f( 1.0,-1.0, 1.0);
|
||||||
glColor3f(10/255,98/255,144/255);
|
glColor3f(10/255,98/255,144/255);
|
||||||
glVertex2f(1, 1);
|
glVertex3f( 1.0, 1.0, 1.0);
|
||||||
glVertex2f(-1.0, 1);
|
glVertex3f(-1.0, 1.0, 1.0);
|
||||||
glEnd();
|
glEnd();
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
@ -839,6 +898,7 @@ sub Render {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub draw_volumes {
|
sub draw_volumes {
|
||||||
|
# $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
|
||||||
my ($self, $fakecolor) = @_;
|
my ($self, $fakecolor) = @_;
|
||||||
|
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
|
@ -853,6 +913,7 @@ sub draw_volumes {
|
||||||
glTranslatef(@{$volume->origin});
|
glTranslatef(@{$volume->origin});
|
||||||
|
|
||||||
if ($fakecolor) {
|
if ($fakecolor) {
|
||||||
|
# Object picking mode. Render the object with a color encoding the object index.
|
||||||
my $r = ($volume_idx & 0x000000FF) >> 0;
|
my $r = ($volume_idx & 0x000000FF) >> 0;
|
||||||
my $g = ($volume_idx & 0x0000FF00) >> 8;
|
my $g = ($volume_idx & 0x0000FF00) >> 8;
|
||||||
my $b = ($volume_idx & 0x00FF0000) >> 16;
|
my $b = ($volume_idx & 0x00FF0000) >> 16;
|
||||||
|
@ -922,23 +983,34 @@ sub draw_volumes {
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Container for object geometry and selection status.
|
||||||
package Slic3r::GUI::3DScene::Volume;
|
package Slic3r::GUI::3DScene::Volume;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
has 'bounding_box' => (is => 'ro', required => 1);
|
has 'bounding_box' => (is => 'ro', required => 1);
|
||||||
has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) });
|
has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) });
|
||||||
has 'color' => (is => 'ro', required => 1);
|
has 'color' => (is => 'ro', required => 1);
|
||||||
|
# An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance.
|
||||||
has 'select_group_id' => (is => 'rw', default => sub { -1 });
|
has 'select_group_id' => (is => 'rw', default => sub { -1 });
|
||||||
|
# An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance.
|
||||||
has 'drag_group_id' => (is => 'rw', default => sub { -1 });
|
has 'drag_group_id' => (is => 'rw', default => sub { -1 });
|
||||||
|
# Boolean: Is this object selected?
|
||||||
has 'selected' => (is => 'rw', default => sub { 0 });
|
has 'selected' => (is => 'rw', default => sub { 0 });
|
||||||
|
# Boolean: Is mouse over this object?
|
||||||
has 'hover' => (is => 'rw', default => sub { 0 });
|
has 'hover' => (is => 'rw', default => sub { 0 });
|
||||||
|
# Vector of two values: a span in the Z axis. Meaningful for a display of layers.
|
||||||
has 'range' => (is => 'rw');
|
has 'range' => (is => 'rw');
|
||||||
|
|
||||||
# geometric data
|
# Geometric data.
|
||||||
has 'qverts' => (is => 'rw'); # GLVertexArray object
|
# Quads: GLVertexArray object: C++ class maintaining an std::vector<float> for coords and normals.
|
||||||
has 'tverts' => (is => 'rw'); # GLVertexArray object
|
has 'qverts' => (is => 'rw');
|
||||||
has 'mesh' => (is => 'rw'); # only required for cut contours
|
# Triangles: GLVertexArray object
|
||||||
has 'offsets' => (is => 'rw'); # [ z => [ qverts_idx, tverts_idx ] ]
|
has 'tverts' => (is => 'rw');
|
||||||
|
# If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts
|
||||||
|
# of the extrusions per layer.
|
||||||
|
# [ z => [ qverts_idx, tverts_idx ] ]
|
||||||
|
has 'offsets' => (is => 'rw');
|
||||||
|
|
||||||
sub transformed_bounding_box {
|
sub transformed_bounding_box {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
@ -948,6 +1020,8 @@ sub transformed_bounding_box {
|
||||||
return $bb;
|
return $bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# The 3D canvas to display objects and tool paths.
|
||||||
package Slic3r::GUI::3DScene;
|
package Slic3r::GUI::3DScene;
|
||||||
use base qw(Slic3r::GUI::3DScene::Base);
|
use base qw(Slic3r::GUI::3DScene::Base);
|
||||||
|
|
||||||
|
@ -956,6 +1030,7 @@ use List::Util qw(first min max);
|
||||||
use Slic3r::Geometry qw(scale unscale epsilon);
|
use Slic3r::Geometry qw(scale unscale epsilon);
|
||||||
use Slic3r::Print::State ':steps';
|
use Slic3r::Print::State ':steps';
|
||||||
|
|
||||||
|
# Perimeter: yellow, Infill: redish, Suport: greenish, last: blueish,
|
||||||
use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ];
|
use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ];
|
||||||
|
|
||||||
__PACKAGE__->mk_accessors(qw(
|
__PACKAGE__->mk_accessors(qw(
|
||||||
|
@ -1303,6 +1378,7 @@ sub _extrusionentity_to_verts {
|
||||||
push @$heights, map $path->height, 0..$#$path_lines;
|
push @$heights, map $path->height, 0..$#$path_lines;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# Calling the C++ implementation Slic3r::_3DScene::_extrusionentity_to_verts_do()
|
||||||
Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights,
|
Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights,
|
||||||
$closed, $top_z, $copy, $qverts, $tverts);
|
$closed, $top_z, $copy, $qverts, $tverts);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue