From 29804187060529c3fbc55ea737e728fd36be9826 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 26 Aug 2021 12:37:55 +0200 Subject: [PATCH] ENABLE_SEAMS_USING_INSTANCED_MODELS -> WIP: Render models using glDrawElementsInstanced --- resources/shaders/gouraud_light_instanced.fs | 12 +++ resources/shaders/gouraud_light_instanced.vs | 57 ++++++++++++++ src/libslic3r/Technologies.hpp | 4 - src/slic3r/GUI/GCodeViewer.cpp | 63 ++++++++++++---- src/slic3r/GUI/GCodeViewer.hpp | 28 ++++--- src/slic3r/GUI/GLModel.cpp | 79 ++++++++++++++++++++ src/slic3r/GUI/GLModel.hpp | 3 + src/slic3r/GUI/GLShadersManager.cpp | 14 +++- 8 files changed, 229 insertions(+), 31 deletions(-) create mode 100644 resources/shaders/gouraud_light_instanced.fs create mode 100644 resources/shaders/gouraud_light_instanced.vs diff --git a/resources/shaders/gouraud_light_instanced.fs b/resources/shaders/gouraud_light_instanced.fs new file mode 100644 index 000000000..970185a00 --- /dev/null +++ b/resources/shaders/gouraud_light_instanced.fs @@ -0,0 +1,12 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/gouraud_light_instanced.vs b/resources/shaders/gouraud_light_instanced.vs new file mode 100644 index 000000000..5d6a05a6f --- /dev/null +++ b/resources/shaders/gouraud_light_instanced.vs @@ -0,0 +1,57 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// vertex attributes +in vec3 v_position; +in vec3 v_normal; +// instance attributes +in vec3 i_offset; +in vec2 i_scales; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +// x = tainted, y = specular; +varying vec2 intensity; + +void main() +{ + // First transform the normal into camera space and normalize the result. +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + vec3 eye_normal = normalize(gl_NormalMatrix * v_normal); +// vec3 eye_normal = normalize(gl_NormalMatrix * gl_Normal); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + // 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. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + vec4 world_position = vec4(v_position * vec3(vec2(i_scales.x), i_scales.y) + i_offset, 1.0); + vec3 eye_position = (gl_ModelViewMatrix * world_position).xyz; +// vec3 eye_position = (gl_ModelViewMatrix * gl_Vertex).xyz; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + gl_Position = gl_ProjectionMatrix * vec4(eye_position, 1.0); +// gl_Position = ftransform(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +} diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 79976d4a0..0a6b47397 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -50,10 +50,6 @@ // Enable rendering seams (and other options) in preview using models #define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA0) // Enable rendering seams (and other options) in preview using instanced models -// references: -// https://ogldev.org/www/tutorial33/tutorial33.html -// https://docs.gl/gl3/glDrawElementsInstanced -// https://www.khronos.org/opengl/wiki/Vertex_Rendering#Instancing #define ENABLE_SEAMS_USING_INSTANCED_MODELS (1 && ENABLE_SEAMS_USING_MODELS) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index b8388f82f..f045cd72a 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -102,7 +102,8 @@ void GCodeViewer::InstanceVBuffer::reset() if (vbo > 0) glsafe(::glDeleteBuffers(1, &vbo)); s_ids.clear(); - render_range = { 0, 0 }; + buffer.clear(); + render_ranges.clear(); } #endif // ENABLE_SEAMS_USING_INSTANCED_MODELS @@ -803,7 +804,11 @@ void GCodeViewer::render() #if ENABLE_SEAMS_USING_MODELS if (wxGetApp().is_gl_version_greater_or_equal_to(3, 1)) { buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model; +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + buffer.shader = "gouraud_light_instanced"; +#else buffer.shader = "gouraud_light"; +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS buffer.model.model.init_from(diamond(16)); buffer.model.color = option_color(type); } @@ -1764,7 +1769,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.total_instances_gpu_size += static_cast(size_bytes); - m_statistics.max_instance_vbuffer_gpu_size = std::max(m_statistics.max_instance_vbuffer_gpu_size, static_cast(size_bytes)); #endif // ENABLE_GCODE_VIEWER_STATISTICS GLuint id = 0; @@ -1774,6 +1778,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); t_buffer.model.instances2.vbo = id; + t_buffer.model.instances2.buffer = inst_buffer; t_buffer.model.instances2.s_ids = instances_ids[i]; } } @@ -2467,21 +2472,36 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Model) continue; - buffer.model.instances2.render_range = { 0, 0 }; + buffer.model.instances2.render_ranges.clear(); if (!buffer.visible) continue; + buffer.model.instances2.render_ranges.push_back({ 0, 0, buffer.model.color }); + bool has_second_range = top_layer_only && m_sequential_view.current.last != m_sequential_view.global.last; + if (has_second_range) + buffer.model.instances2.render_ranges.push_back({ 0, 0, Neutral_Color }); + if (m_sequential_view.current.first <= buffer.model.instances2.s_ids.back() && buffer.model.instances2.s_ids.front() <= m_sequential_view.current.last) { for (size_t id : buffer.model.instances2.s_ids) { - if (id <= m_sequential_view.current.first) { - buffer.model.instances2.render_range.offset += buffer.model.instances2.instance_size_bytes(); - buffer.model.instances2.render_range.count = 0; + if (has_second_range) { + if (id <= m_sequential_view.endpoints.first) { + buffer.model.instances2.render_ranges.front().offset += buffer.model.instances2.instance_size_bytes(); + ++buffer.model.instances2.render_ranges.back().count; + } + else if (id <= m_sequential_view.current.last) + ++buffer.model.instances2.render_ranges.front().count; + else + break; + } + else { + if (id <= m_sequential_view.current.first) + buffer.model.instances2.render_ranges.front().offset += buffer.model.instances2.instance_size_bytes(); + else if (id <= m_sequential_view.current.last) + ++buffer.model.instances2.render_ranges.front().count; + else + break; } - else if (id <= m_sequential_view.current.last) - ++buffer.model.instances2.render_range.count; - else - break; } } } @@ -2616,7 +2636,9 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } #if ENABLE_SEAMS_USING_MODELS #if ENABLE_SEAMS_USING_INSTANCED_MODELS + statistics->models_instances_size += SLIC3R_STDVEC_MEMSIZE(buffer.model.instances2.buffer, float); statistics->models_instances_size += SLIC3R_STDVEC_MEMSIZE(buffer.model.instances2.s_ids, size_t); + statistics->models_instances_size += SLIC3R_STDVEC_MEMSIZE(buffer.model.instances2.render_ranges, InstanceVBuffer::RenderRange); #else statistics->models_instances_size += SLIC3R_STDVEC_MEMSIZE(buffer.model.instances, TBuffer::Model::Instance); #endif // ENABLE_SEAMS_USING_INSTANCED_MODELS @@ -2711,7 +2733,18 @@ void GCodeViewer::render_toolpaths() #if ENABLE_SEAMS_USING_MODELS auto render_as_instanced_model = [this] - (TBuffer & buffer, GLShaderProgram & shader) { + (TBuffer& buffer, GLShaderProgram & shader) { +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + if (buffer.model.instances2.vbo > 0) { + for (const InstanceVBuffer::RenderRange& range : buffer.model.instances2.render_ranges) { + buffer.model.model.set_color(-1, range.color); + buffer.model.model.render_instanced(buffer.model.instances2.vbo, range.count); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_instanced_models_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } + } +#else for (const TBuffer::Model::Instance& inst : buffer.model.instances) { bool top_layer_only = get_app_config()->get("seq_top_layer_only") == "1"; bool visible = top_layer_only ? @@ -2732,6 +2765,7 @@ void GCodeViewer::render_toolpaths() #endif // ENABLE_GCODE_VIEWER_STATISTICS } } +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS }; #endif // ENABLE_SEAMS_USING_MODELS @@ -3800,7 +3834,11 @@ void GCodeViewer::render_statistics() add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count); add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count); #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + add_counter(std::string("Instanced models:"), m_statistics.gl_instanced_models_calls_count); +#else add_counter(std::string("Models:"), m_statistics.gl_models_calls_count); +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS #endif // ENABLE_SEAMS_USING_MODELS } @@ -3824,9 +3862,6 @@ void GCodeViewer::render_statistics() ImGui::Separator(); add_memory(std::string("Max VBuffer:"), m_statistics.max_vbuffer_gpu_size); add_memory(std::string("Max IBuffer:"), m_statistics.max_ibuffer_gpu_size); -#if ENABLE_SEAMS_USING_INSTANCED_MODELS - add_memory(std::string("Max instance VBuffer:"), m_statistics.max_instance_vbuffer_gpu_size); -#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS } if (ImGui::CollapsingHeader("Other")) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 7bf341a68..bda0491de 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -105,8 +105,9 @@ class GCodeViewer }; #if ENABLE_SEAMS_USING_INSTANCED_MODELS - // vbo buffer containing instances data used to render a toolpaths using instanced models - // record format: 5 floats -> position.x|position.y|position.z|width|height + // buffer containing instances data used to render a toolpaths using instanced models + // instance record format: 5 floats -> position.x|position.y|position.z|width|height + // which is sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced() struct InstanceVBuffer { struct RenderRange @@ -115,13 +116,18 @@ class GCodeViewer unsigned int offset; // count of instances to render unsigned int count; + // Color to apply to the instances + Color color; }; // vbo id unsigned int vbo{ 0 }; - // move ids + // cpu-side buffer containing all instances data + InstanceBuffer buffer; + // indices of the moves for all instances std::vector s_ids; - RenderRange render_range; + // ranges used to render only subparts of the intances + std::vector render_ranges; size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); } @@ -518,7 +524,11 @@ class GCodeViewer int64_t gl_multi_triangles_calls_count{ 0 }; int64_t gl_triangles_calls_count{ 0 }; #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + int64_t gl_instanced_models_calls_count{ 0 }; +#else int64_t gl_models_calls_count{ 0 }; +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS #endif // ENABLE_SEAMS_USING_MODELS // memory int64_t results_size{ 0 }; @@ -529,9 +539,6 @@ class GCodeViewer #endif // ENABLE_SEAMS_USING_INSTANCED_MODELS int64_t max_vbuffer_gpu_size{ 0 }; int64_t max_ibuffer_gpu_size{ 0 }; -#if ENABLE_SEAMS_USING_INSTANCED_MODELS - int64_t max_instance_vbuffer_gpu_size{ 0 }; -#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS int64_t paths_size{ 0 }; int64_t render_paths_size{ 0 }; #if ENABLE_SEAMS_USING_MODELS @@ -570,7 +577,11 @@ class GCodeViewer gl_multi_triangles_calls_count = 0; gl_triangles_calls_count = 0; #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + gl_instanced_models_calls_count = 0; +#else gl_models_calls_count = 0; +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS #endif // ENABLE_SEAMS_USING_MODELS } @@ -583,9 +594,6 @@ class GCodeViewer #endif // ENABLE_SEAMS_USING_INSTANCED_MODELS max_vbuffer_gpu_size = 0; max_ibuffer_gpu_size = 0; -#if ENABLE_SEAMS_USING_INSTANCED_MODELS - max_instance_vbuffer_gpu_size = 0; -#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS paths_size = 0; render_paths_size = 0; #if ENABLE_SEAMS_USING_MODELS diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index c4437a87f..58dfcb68b 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -207,6 +207,85 @@ void GLModel::render() const } } +#if ENABLE_SEAMS_USING_INSTANCED_MODELS +void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const +{ + if (instances_vbo == 0) + return; + + GLShaderProgram* shader = wxGetApp().get_current_shader(); + assert(shader == nullptr || boost::algorithm::iends_with(shader->get_name(), "_instanced")); + + // vertex attributes + GLint position_id = (shader != nullptr) ? shader->get_attrib_location("v_position") : -1; + GLint normal_id = (shader != nullptr) ? shader->get_attrib_location("v_normal") : -1; + assert(position_id != -1 && normal_id != -1); + + // instance attributes + GLint offset_id = (shader != nullptr) ? shader->get_attrib_location("i_offset") : -1; + GLint scales_id = (shader != nullptr) ? shader->get_attrib_location("i_scales") : -1; + assert(offset_id != -1 && scales_id != -1); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo)); + if (offset_id != -1) { + glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)0)); + glsafe(::glEnableVertexAttribArray(offset_id)); + glsafe(::glVertexAttribDivisor(offset_id, 1)); + } + if (scales_id != -1) { + glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float)))); + glsafe(::glEnableVertexAttribArray(scales_id)); + glsafe(::glVertexAttribDivisor(scales_id, 1)); + } + + for (const RenderData& data : m_render_data) { + if (data.vbo_id == 0 || data.ibo_id == 0) + continue; + + GLenum mode; + switch (data.type) + { + default: + case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } + case PrimitiveType::Lines: { mode = GL_LINES; break; } + case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } + case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; } + } + + if (shader != nullptr) + shader->set_uniform("uniform_color", data.color); + else + glsafe(::glColor4fv(data.color.data())); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); + if (position_id != -1) { + glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0)); + glsafe(::glEnableVertexAttribArray(position_id)); + } + if (normal_id != -1) { + glsafe(::glVertexAttribPointer(normal_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(float)))); + glsafe(::glEnableVertexAttribArray(normal_id)); + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); + glsafe(::glDrawElementsInstanced(mode, static_cast(data.indices_count), GL_UNSIGNED_INT, (const void*)0, instances_count)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (normal_id != -1) + glsafe(::glDisableVertexAttribArray(normal_id)); + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); + } + + if (scales_id != -1) + glsafe(::glDisableVertexAttribArray(scales_id)); + if (offset_id != -1) + glsafe(::glDisableVertexAttribArray(offset_id)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS + void GLModel::send_to_gpu(RenderData& data, const std::vector& vertices, const std::vector& indices) { assert(data.vbo_id == 0); diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 99c4bf442..73fb90827 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -72,6 +72,9 @@ namespace GUI { void reset(); void render() const; +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const; +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS bool is_initialized() const { return !m_render_data.empty(); } diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 788fe90c0..7bb0a50a6 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -38,9 +38,17 @@ std::pair GLShadersManager::init() // used to render printbed valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); // used to render options in gcode preview - valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); - if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) - valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 1)) + valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" }); + else { +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS + valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" }); + if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20)) + valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" }); +#if ENABLE_SEAMS_USING_INSTANCED_MODELS + } +#endif // ENABLE_SEAMS_USING_INSTANCED_MODELS // used to render extrusion and travel paths as lines in gcode preview valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); // used to render objects in 3d editor