diff --git a/plugins/LayerView/LayerPass.py b/plugins/LayerView/LayerPass.py new file mode 100644 index 0000000000..83ce60ffd1 --- /dev/null +++ b/plugins/LayerView/LayerPass.py @@ -0,0 +1,86 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator +from UM.Resources import Resources +from UM.Scene.SceneNode import SceneNode +from UM.Scene.ToolHandle import ToolHandle +from UM.Application import Application + +from UM.View.RenderPass import RenderPass +from UM.View.RenderBatch import RenderBatch +from UM.View.GL.OpenGL import OpenGL + +## RenderPass used to display g-code paths. +class LayerPass(RenderPass): + def __init__(self, width, height): + super().__init__("layerview", width, height) + + self._shader = None + self._tool_handle_shader = None + self._gl = OpenGL.getInstance().getBindingsObject() + self._scene = Application.getInstance().getController().getScene() + + self._layerview = None + + def setLayerView(self, layerview): + self._layerview = layerview + + def render(self): + if not self._shader: + self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) + if not self._tool_handle_shader: + self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader")) + + self.bind() + self._gl.glDisable(self._gl.GL_DEPTH_TEST) + + tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay) + tool_handle_has_items = False + + for node in DepthFirstIterator(self._scene.getRoot()): + if isinstance(node, ToolHandle): + tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh()) + tool_handle_has_items = True + + if type(node) is SceneNode and node.getMeshData() and node.isVisible(): + layer_data = node.callDecoration("getLayerData") + if not layer_data: + continue + + # Render all layers below a certain number as line mesh instead of vertices. + if self._layerview._current_layer_num - self._layerview._solid_layers > -1 and not self._layerview._only_show_top_layers: + start = 0 + end = 0 + element_counts = layer_data.getElementCounts() + for layer, counts in element_counts.items(): + if layer + self._layerview._solid_layers > self._layerview._current_layer_num: + break + end += counts + + # This uses glDrawRangeElements internally to only draw a certain range of lines. + batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, mode = RenderBatch.RenderMode.Lines, range = (start, end)) + batch.addItem(node.getWorldTransformation(), layer_data) + batch.render(self._scene.getActiveCamera()) + + # Create a new batch that is not range-limited + batch = RenderBatch(self._shader, type = RenderBatch.RenderType.NoType, mode = RenderBatch.RenderMode.Lines) + batch_has_items = False + + if self._layerview._current_layer_mesh: + batch_has_items = True + batch.addItem(node.getWorldTransformation(), self._layerview._current_layer_mesh) + + if self._layerview._current_layer_jumps: + batch_has_items = True + batch.addItem(node.getWorldTransformation(), self._layerview._current_layer_jumps) + + if batch_has_items: + batch.render(self._scene.getActiveCamera()) + + # Render toolhandles on top of the layerview + if tool_handle_has_items: + tool_handle_batch.render(self._scene.getActiveCamera()) + + self._gl.glEnable(self._gl.GL_DEPTH_TEST) + self.release() diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 7956e35d2a..c7354e52be 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -1,6 +1,7 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +from UM.PluginRegistry import PluginRegistry from UM.View.View import View from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Resources import Resources @@ -26,14 +27,16 @@ from . import LayerViewProxy from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") +from . import LayerPass + import numpy +import os.path ## View used to display g-code paths. class LayerView(View): def __init__(self): super().__init__() self._shader = None - self._ghost_shader = None self._num_layers = 0 self._layer_percentage = 0 # what percentage of layers need to be shown (Slider gives value between 0 - 100) self._proxy = LayerViewProxy.LayerViewProxy() @@ -46,6 +49,13 @@ class LayerView(View): self._activity = False self._old_max_layers = 0 + self._ghost_shader = None + self._ghost_pass = None + self._composite_pass = None + self._old_layer_bindings = None + self._layerview_composite_shader = None + self._old_composite_shader = None + Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().addPreference("view/only_show_top_layers", False) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) @@ -87,6 +97,7 @@ class LayerView(View): if not self._ghost_shader: self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader")) self._ghost_shader.setUniformValue("u_color", Color(32, 32, 32, 96)) + for node in DepthFirstIterator(scene.getRoot()): # We do not want to render ConvexHullNode as it conflicts with the bottom layers. # However, it is somewhat relevant when the node is selected, so do render it then. @@ -96,28 +107,6 @@ class LayerView(View): if not node.render(renderer): if node.getMeshData() and node.isVisible(): renderer.queueNode(node, transparent = True, shader = self._ghost_shader) - layer_data = node.callDecoration("getLayerData") - if not layer_data: - continue - - # Render all layers below a certain number as line mesh instead of vertices. - if self._current_layer_num - self._solid_layers > -1 and not self._only_show_top_layers: - start = 0 - end = 0 - element_counts = layer_data.getElementCounts() - for layer, counts in element_counts.items(): - if layer + self._solid_layers > self._current_layer_num: - break - end += counts - - # This uses glDrawRangeElements internally to only draw a certain range of lines. - renderer.queueNode(node, mesh = layer_data, mode = RenderBatch.RenderMode.Lines, overlay = True, range = (start, end)) - - if self._current_layer_mesh: - renderer.queueNode(node, mesh = self._current_layer_mesh, overlay = True) - - if self._current_layer_jumps: - renderer.queueNode(node, mesh = self._current_layer_jumps, overlay = True) def setLayer(self, value): if self._current_layer_num != value: @@ -182,6 +171,31 @@ class LayerView(View): self.setLayer(self._current_layer_num - 1) return True + if event.type == Event.ViewActivateEvent: + if not self._ghost_pass: + # Currently the RenderPass constructor requires a size > 0 + # This should be fixed in RenderPass's constructor. + self._ghost_pass = LayerPass.LayerPass(1, 1) + self._ghost_pass.setLayerView(self) + self.getRenderer().addRenderPass(self._ghost_pass) + + if not self._layerview_composite_shader: + self._layerview_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("LayerView"), "layerview_composite.shader")) + + if not self._composite_pass: + self._composite_pass = self.getRenderer().getRenderPass("composite") + + self._old_layer_bindings = self._composite_pass.getLayerBindings() + layer_bindings = self._old_layer_bindings[:] # make a copy + layer_bindings.append("layerview") + self._composite_pass.setLayerBindings(layer_bindings) + self._old_composite_shader = self._composite_pass.getCompositeShader() + self._composite_pass.setCompositeShader(self._layerview_composite_shader) + + if event.type == Event.ViewDeactivateEvent: + self._composite_pass.setLayerBindings(self._old_layer_bindings) + self._composite_pass.setCompositeShader(self._old_composite_shader) + def _startUpdateTopLayers(self): if self._top_layers_job: self._top_layers_job.finished.disconnect(self._updateCurrentLayerMesh) diff --git a/plugins/LayerView/layerview_composite.shader b/plugins/LayerView/layerview_composite.shader new file mode 100644 index 0000000000..aad251f852 --- /dev/null +++ b/plugins/LayerView/layerview_composite.shader @@ -0,0 +1,80 @@ +[shaders] +vertex = + uniform highp mat4 u_modelViewProjectionMatrix; + attribute highp vec4 a_vertex; + attribute highp vec2 a_uvs; + + varying highp vec2 v_uvs; + + void main() + { + gl_Position = u_modelViewProjectionMatrix * a_vertex; + v_uvs = a_uvs; + } + +fragment = + uniform sampler2D u_layer0; + uniform sampler2D u_layer1; + uniform sampler2D u_layer2; + uniform sampler2D u_layer3; + + uniform float u_imageWidth; + uniform float u_imageHeight; + + uniform vec2 u_offset[9]; + + uniform float u_outline_strength; + uniform vec4 u_outline_color; + + varying vec2 v_uvs; + + float kernel[9]; + + const vec3 x_axis = vec3(1.0, 0.0, 0.0); + const vec3 y_axis = vec3(0.0, 1.0, 0.0); + const vec3 z_axis = vec3(0.0, 0.0, 1.0); + + void main() + { + kernel[0] = 0.0; kernel[1] = 1.0; kernel[2] = 0.0; + kernel[3] = 1.0; kernel[4] = -4.0; kernel[5] = 1.0; + kernel[6] = 0.0; kernel[7] = 1.0; kernel[8] = 0.0; + + vec4 result = vec4(0.965, 0.965, 0.965, 1.0); + vec4 layer0 = texture2D(u_layer0, v_uvs); + vec4 layer2 = texture2D(u_layer2, v_uvs); + + result = layer0 * layer0.a + result * (1.0 - layer0.a); + result = layer2 * layer2.a + result * (1.0 - layer2.a); + + vec4 sum = vec4(0.0); + for (int i = 0; i < 9; i++) + { + vec4 color = vec4(texture2D(u_layer1, v_uvs.xy + u_offset[i]).a); + sum += color * (kernel[i] / u_outline_strength); + } + + vec4 layer1 = texture2D(u_layer1, v_uvs); + if((layer1.rgb == x_axis || layer1.rgb == y_axis || layer1.rgb == z_axis)) + { + gl_FragColor = result; + } + else + { + gl_FragColor = mix(result, u_outline_color, abs(sum.a)); + } + } + +[defaults] +u_layer0 = 0 +u_layer1 = 1 +u_layer2 = 2 +u_layer3 = 3 +u_outline_strength = 1.0 +u_outline_color = [0.05, 0.66, 0.89, 1.0] + +[bindings] + +[attributes] +a_vertex = vertex +a_uvs = uv