diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 087f13cc2..e655bb1b3 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -14,6 +14,7 @@ use Slic3r::GUI::ConfigWizard; use Slic3r::GUI::Controller; use Slic3r::GUI::Controller::ManualControlDialog; use Slic3r::GUI::Controller::PrinterPanel; +use Slic3r::GUI::GLShader; use Slic3r::GUI::MainFrame; use Slic3r::GUI::Notifier; use Slic3r::GUI::Plater; diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 374469a0e..ed4b82fbd 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -720,6 +720,13 @@ sub InitGL { return if $self->init; return unless $self->GetContext; $self->init(1); + + my $shader; +# $shader = $self->{shader} = new Slic3r::GUI::GLShader; + if ($self->{shader}) { + my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); + print $info if $info; + } glClearColor(0, 0, 0, 1); glColor3f(1, 0, 0); @@ -957,6 +964,8 @@ 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) = @_; + $self->{shader}->Enable if (! $fakecolor && $self->{shader}); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1017,15 +1026,33 @@ sub draw_volumes { if ($qverts_begin < $qverts_end) { glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr); - glDrawArrays(GL_QUADS, $qverts_begin / 3, ($qverts_end-$qverts_begin) / 3); + $qverts_begin /= 3; + $qverts_end /= 3; + my $nvertices = $qverts_end-$qverts_begin; + while ($nvertices > 0) { + my $nvertices_this = ($nvertices > 4096) ? 4096 : $nvertices; + glDrawArrays(GL_QUADS, $qverts_begin, $nvertices_this); + $qverts_begin += $nvertices_this; + $nvertices -= $nvertices_this; + } } if ($tverts_begin < $tverts_end) { glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr); - glDrawArrays(GL_TRIANGLES, $tverts_begin / 3, ($tverts_end-$tverts_begin) / 3); + $tverts_begin /= 3; + $tverts_end /= 3; + my $nvertices = $tverts_end-$tverts_begin; + while ($nvertices > 0) { + my $nvertices_this = ($nvertices > 4095) ? 4095 : $nvertices; + glDrawArrays(GL_TRIANGLES, $tverts_begin, $nvertices_this); + $tverts_begin += $nvertices_this; + $nvertices -= $nvertices_this; + } } - + + glVertexPointer_c(3, GL_FLOAT, 0, 0); + glNormalPointer_c(GL_FLOAT, 0, 0); glPopMatrix(); } glDisableClientState(GL_NORMAL_ARRAY); @@ -1036,8 +1063,11 @@ sub draw_volumes { glColor3f(0, 0, 0); glVertexPointer_p(3, $self->cut_lines_vertices); glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); + glVertexPointer_c(3, GL_FLOAT, 0, 0); } glDisableClientState(GL_VERTEX_ARRAY); + + $self->{shader}->Disable if (! $fakecolor && $self->{shader}); } sub _report_opengl_state @@ -1069,6 +1099,86 @@ sub _report_opengl_state } } +sub _vertex_shader { + return <<'VERTEX'; +#version 110 + +varying float object_z; + +void main() +{ + vec3 normal, lightDir, viewVector, halfVector; + vec4 diffuse, ambient, globalAmbient, specular = vec4(0.0); + float NdotL,NdotHV; + + // First transform the normal into eye space and normalize the result. + normal = normalize(gl_NormalMatrix * gl_Normal); + + // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. + // Also since we're talking about a directional light, the position field is actually direction. + lightDir = normalize(vec3(gl_LightSource[0].position)); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + NdotL = max(dot(normal, lightDir), 0.0); + + // Compute the diffuse, ambient and globalAmbient terms. +// diffuse = NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse); +// ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient; + diffuse = NdotL * (gl_Color * gl_LightSource[0].diffuse); + ambient = gl_Color * gl_LightSource[0].ambient; + globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient; + + // compute the specular term if NdotL is larger than zero + if (NdotL > 0.0) { + NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0); + specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess); + } + + // Perform the same lighting calculation for the 2nd light source. + lightDir = normalize(vec3(gl_LightSource[1].position)); + NdotL = max(dot(normal, lightDir), 0.0); +// diffuse += NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse); +// ambient += gl_FrontMaterial.ambient * gl_LightSource[1].ambient; + diffuse += NdotL * (gl_Color * gl_LightSource[1].diffuse); + ambient += gl_Color * gl_LightSource[1].ambient; + + // compute the specular term if NdotL is larger than zero + if (NdotL > 0.0) { + NdotHV = max(dot(normal, normalize(gl_LightSource[1].halfVector.xyz)),0.0); + specular += gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV,gl_FrontMaterial.shininess); + } + + gl_FrontColor = globalAmbient + diffuse + ambient + specular; + gl_FrontColor.a = 1.; + + gl_Position = ftransform(); + object_z = gl_Vertex.z / gl_Vertex.w; +} + +VERTEX +} + +sub _fragment_shader { + return <<'FRAGMENT'; +#version 110 +#define M_PI 3.1415926535897932384626433832795 + +varying float object_z; + +void main() +{ + float layer_height = 0.25; + float layer_height2 = 0.5 * layer_height; + float layer_center = floor(object_z / layer_height) * layer_height + layer_height2; + float intensity = cos(M_PI * 0.7 * (layer_center - object_z) / layer_height); + gl_FragColor = gl_Color * intensity; + gl_FragColor.a = 1.; +} + +FRAGMENT +} + # Container for object geometry and selection status. package Slic3r::GUI::3DScene::Volume; use Moo; diff --git a/lib/Slic3r/GUI/GLShader.pm b/lib/Slic3r/GUI/GLShader.pm new file mode 100644 index 000000000..e5f9c4a62 --- /dev/null +++ b/lib/Slic3r/GUI/GLShader.pm @@ -0,0 +1,174 @@ +############################################################ +# +# OpenGL::Shader::Objects - Copyright 2007 Graphcomp - ALL RIGHTS RESERVED +# Author: Bob "grafman" Free - grafman@graphcomp.com +# +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +############################################################ + +package Slic3r::GUI::GLShader; +use OpenGL(':all'); + +# Shader constructor +sub new +{ + # Check for required OpenGL extensions + return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects')); + return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader')); + return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader')); + return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100')); +# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION); +# my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB ); +# print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n"; + + my $this = shift; + my $class = ref($this) || $this; + my($type) = @_; + my $self = {type => uc($type)}; + bless($self,$class); + + # Get GL_SHADING_LANGUAGE_VERSION_ARB + my $shader_ver = glGetString(0x8B8C); + $shader_ver =~ m|([\d\.]+)|; + $self->{version} = $1 || '0'; + print + return $self; +} + +# Shader destructor +# Must be disabled first +sub DESTROY +{ + my($self) = @_; + + if ($self->{program}) + { + glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id}); + glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id}); + glDeleteProgramsARB_p($self->{program}); + } + + glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id}); + glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id}); +} + + +# Load shader strings +sub Load +{ + my($self,$fragment,$vertex) = @_; + + # Load fragment code + if ($fragment) + { + $self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER); + return undef if (!$self->{fragment_id}); + + glShaderSourceARB_p($self->{fragment_id}, $fragment); + #my $frag = glGetShaderSourceARB_p($self->{fragment_id}); + #print STDERR "Loaded fragment:\n$frag\n"; + + glCompileShaderARB($self->{fragment_id}); + my $stat = glGetInfoLogARB_p($self->{fragment_id}); + return "Fragment shader: $stat" if ($stat); + } + + # Load vertex code + if ($vertex) + { + $self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER); + return undef if (!$self->{vertex_id}); + + glShaderSourceARB_p($self->{vertex_id}, $vertex); + #my $vert = glGetShaderSourceARB_p($self->{vertex_id}); + #print STDERR "Loaded vertex:\n$vert\n"; + + glCompileShaderARB($self->{vertex_id}); + $stat = glGetInfoLogARB_p($self->{vertex_id}); + return "Vertex shader: $stat" if ($stat); + } + + + # Link shaders + my $sp = glCreateProgramObjectARB(); + glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment); + glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex); + glLinkProgramARB($sp); + my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB); + if (!$linked) + { + $stat = glGetInfoLogARB_p($sp); + #print STDERR "Load shader: $stat\n"; + return "Link shader: $stat" if ($stat); + return 'Unable to link shader'; + } + + $self->{program} = $sp; + + return ''; +} + +# Enable shader +sub Enable +{ + my($self) = @_; + glUseProgramObjectARB($self->{program}) if ($self->{program}); +} + +# Disable shader +sub Disable +{ + my($self) = @_; + glUseProgramObjectARB(0) if ($self->{program}); +} + +# Return shader vertex attribute ID +sub MapAttr +{ + my($self,$attr) = @_; + return undef if (!$self->{program}); + my $id = glGetAttribLocationARB_p($self->{program},$attr); + return undef if ($id < 0); + return $id; +} + +# Return shader uniform variable ID +sub Map +{ + my($self,$var) = @_; + return undef if (!$self->{program}); + my $id = glGetUniformLocationARB_p($self->{program},$var); + return undef if ($id < 0); + return $id; +} + +# Set shader vector +sub SetVector +{ + my($self,$var,@values) = @_; + + my $id = $self->Map($var); + return 'Unable to map $var' if (!defined($id)); + + my $count = scalar(@values); + eval('glUniform'.$count.'fARB($id,@values)'); + + return ''; +} + +# Set shader 4x4 matrix +sub SetMatrix +{ + my($self,$var,$oga) = @_; + + my $id = $self->Map($var); + return 'Unable to map $var' if (!defined($id)); + + glUniformMatrix4fvARB_c($id,1,0,$oga->ptr()); + return ''; +} + +1; +__END__