def __init__(self, node, hull, parent = None): super().__init__(parent) self.setCalculateBoundingBox(False) self._shader = None self._original_parent = parent self._inherit_orientation = False self._inherit_scale = False self._color = Color(35, 35, 35, 128) self._node = node self._node.transformationChanged.connect(self._onNodePositionChanged) self._node.parentChanged.connect(self._onNodeParentChanged) self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._onNodeDecoratorsChanged(self._node) self._convex_hull_head_mesh = None self._hull = hull hull_points = self._hull.getPoints() hull_mesh = self.createHullMesh(self._hull.getPoints()) if hull_mesh: self.setMeshData(hull_mesh) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
def __init__(self, node, hull, parent = None): super().__init__(parent) self.setCalculateBoundingBox(False) self._shader = None self._original_parent = parent self._inherit_orientation = False self._inherit_scale = False self._color = Color(35, 35, 35, 128) self._mesh_height = 0.1 #The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._node = node self._node.transformationChanged.connect(self._onNodePositionChanged) self._node.parentChanged.connect(self._onNodeParentChanged) self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._onNodeDecoratorsChanged(self._node) self._convex_hull_head_mesh = None self._hull = hull hull_points = self._hull.getPoints() # TODO: @UnusedVariable hull_mesh = self.createHullMesh(self._hull.getPoints()) if hull_mesh: self.setMeshData(hull_mesh) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
def _onNodeDecoratorsChanged(self, node): self._color = Color(35, 35, 35, 0.5) if not node: return if node.hasDecoration("getProfile"): self._color.setR(0.75) if node.hasDecoration("getSetting"): self._color.setG(0.75)
def getIdAtCoordinate(self, x, y): if not self._selection_image: return None px = (0.5 + x / 2.0) * self._viewport_width py = (0.5 + y / 2.0) * self._viewport_height if px < 0 or px > (self._selection_image.width() - 1) or py < 0 or py > (self._selection_image.height() - 1): return None pixel = self._selection_image.pixel(px, py) return self._selection_map.get(Color.fromARGB(pixel), None)
def getIdAtPosition(self, x, y): output = self.getOutput() window_size = self._renderer.getWindowSize() px = (0.5 + x / 2.0) * window_size[0] py = (0.5 + y / 2.0) * window_size[1] if px < 0 or px > (output.width() - 1) or py < 0 or py > (output.height() - 1): return None pixel = output.pixel(px, py) return self._selection_map.get(Color.fromARGB(pixel), None)
def test_getAndSet(data): color = Color(*data["data_to_set"]) assert color == Color(*data["expected"]) assert color.r == data["expected"][0] assert color.g == data["expected"][1] assert color.b == data["expected"][2] assert color.a == data["expected"][3] # And flip the data around to set the values one by one reversed_data = data["data_to_set"][::-1] color.setR(reversed_data[0]) color.setG(reversed_data[1]) color.setB(reversed_data[2]) color.setA(reversed_data[3]) assert color.r == data["expected"][3] assert color.g == data["expected"][2] assert color.b == data["expected"][1] assert color.a == data["expected"][0]
def getSelectionColorAtCoorindateRadius(self,x,y,radius): if not self._selection_image: return None px = (0.5 + x / 2.0) * self._viewport_width py = (0.5 + y / 2.0) * self._viewport_height squared_radius = radius * radius samples = [] for sx in range(-radius, radius): squared_sx = sx*sx if px + sx < 0 or px + sx > (self._selection_image.width() - 1): continue for sy in range(-radius, radius): squared_sy = sy * sy if py + sy < 0 or py + sy > (self._selection_image.height() - 1): continue if squared_sx + squared_sy < squared_radius: pixel = self._selection_image.pixel(px + sx, py + sy) samples.append(Color.fromARGB(pixel)) return samples
def getSelectionColorAtCoordinate(self,x,y): if not self._selection_image: return None px = (0.5 + x / 2.0) * self._viewport_width py = (0.5 + y / 2.0) * self._viewport_height return Color.fromARGB(self._selection_image.pixel(px,py))
def test_fromHexString(data): color = Color.fromHexString(data["data_to_set"]) expected_color = Color(*data["expected"]) assert color == expected_color
def _dropAlpha(self, color): return Color(color.r, color.g, color.b, 0.0)
def render(self): if not self._layer_shader: if self._compatibility_mode: shader_filename = "layers.shader" shadow_shader_filename = "layers_shadow.shader" else: shader_filename = "layers3d.shader" shadow_shader_filename = "layers3d_shadow.shader" self._layer_shader = OpenGL.getInstance().createShaderProgram( os.path.join( PluginRegistry.getInstance().getPluginPath( "SimulationView"), shader_filename)) self._layer_shadow_shader = OpenGL.getInstance( ).createShaderProgram( os.path.join( PluginRegistry.getInstance().getPluginPath( "SimulationView"), shadow_shader_filename)) self._current_shader = self._layer_shader # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers) self._layer_shader.setUniformValue( "u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex))) if self._layer_view: self._layer_shader.setUniformValue( "u_max_feedrate", self._layer_view.getMaxFeedrate()) self._layer_shader.setUniformValue( "u_min_feedrate", self._layer_view.getMinFeedrate()) self._layer_shader.setUniformValue( "u_max_thickness", self._layer_view.getMaxThickness()) self._layer_shader.setUniformValue( "u_min_thickness", self._layer_view.getMinThickness()) self._layer_shader.setUniformValue( "u_layer_view_type", self._layer_view.getSimulationViewType()) self._layer_shader.setUniformValue( "u_extruder_opacity", self._layer_view.getExtruderOpacities()) self._layer_shader.setUniformValue( "u_show_travel_moves", self._layer_view.getShowTravelMoves()) self._layer_shader.setUniformValue( "u_show_helpers", self._layer_view.getShowHelpers()) self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin()) self._layer_shader.setUniformValue( "u_show_infill", self._layer_view.getShowInfill()) else: #defaults self._layer_shader.setUniformValue("u_max_feedrate", 1) self._layer_shader.setUniformValue("u_min_feedrate", 0) self._layer_shader.setUniformValue("u_max_thickness", 1) self._layer_shader.setUniformValue("u_min_thickness", 0) self._layer_shader.setUniformValue("u_layer_view_type", 1) self._layer_shader.setUniformValue("u_extruder_opacity", [1, 1, 1, 1]) self._layer_shader.setUniformValue("u_show_travel_moves", 0) self._layer_shader.setUniformValue("u_show_helpers", 1) self._layer_shader.setUniformValue("u_show_skin", 1) self._layer_shader.setUniformValue("u_show_infill", 1) if not self._tool_handle_shader: self._tool_handle_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "toolhandle.shader")) if not self._nozzle_shader: self._nozzle_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "color.shader")) self._nozzle_shader.setUniformValue( "u_color", Color(*Application.getInstance().getTheme().getColor( "layerview_nozzle").getRgb())) self.bind() tool_handle_batch = RenderBatch(self._tool_handle_shader, type=RenderBatch.RenderType.Overlay, backface_cull=True) active_build_plate = Application.getInstance().getBuildPlateModel( ).activeBuildPlate head_position = None # Indicates the current position of the print head nozzle_node = None for node in DepthFirstIterator(self._scene.getRoot()): if isinstance(node, ToolHandle): tool_handle_batch.addItem(node.getWorldTransformation(), mesh=node.getSolidMesh()) elif isinstance(node, NozzleNode): nozzle_node = node nozzle_node.setVisible(False) elif issubclass(type(node), SceneNode) and (node.getMeshData( ) or node.callDecoration("isBlockSlicing")) 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._layer_view._current_layer_num > -1 and ( (not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())): start = 0 end = 0 element_counts = layer_data.getElementCounts() for layer in sorted(element_counts.keys()): # In the current layer, we show just the indicated paths if layer == self._layer_view._current_layer_num: # We look for the position of the head, searching the point of the current path index = self._layer_view._current_path_num offset = 0 for polygon in layer_data.getLayer(layer).polygons: # The size indicates all values in the two-dimension array, and the second dimension is # always size 3 because we have 3D points. if index >= polygon.data.size // 3 - offset: index -= polygon.data.size // 3 - offset offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon continue # The head position is calculated and translated head_position = Vector( polygon.data[index + offset][0], polygon.data[index + offset][1], polygon.data[index + offset] [2]) + node.getWorldPosition() break break if self._layer_view._minimum_layer_num > layer: start += element_counts[layer] end += element_counts[layer] # Calculate the range of paths in the last layer current_layer_start = end current_layer_end = end + self._layer_view._current_path_num * 2 # Because each point is used twice # This uses glDrawRangeElements internally to only draw a certain range of lines. # All the layers but the current selected layer are rendered first if self._old_current_path != self._layer_view._current_path_num: self._current_shader = self._layer_shadow_shader self._switching_layers = False if not self._layer_view.isSimulationRunning( ) and self._old_current_layer != self._layer_view._current_layer_num: self._current_shader = self._layer_shader self._switching_layers = True layers_batch = RenderBatch( self._current_shader, type=RenderBatch.RenderType.Solid, mode=RenderBatch.RenderMode.Lines, range=(start, end), backface_cull=True) layers_batch.addItem(node.getWorldTransformation(), layer_data) layers_batch.render(self._scene.getActiveCamera()) # Current selected layer is rendered current_layer_batch = RenderBatch( self._layer_shader, type=RenderBatch.RenderType.Solid, mode=RenderBatch.RenderMode.Lines, range=(current_layer_start, current_layer_end)) current_layer_batch.addItem(node.getWorldTransformation(), layer_data) current_layer_batch.render(self._scene.getActiveCamera()) self._old_current_layer = self._layer_view._current_layer_num self._old_current_path = self._layer_view._current_path_num # Create a new batch that is not range-limited batch = RenderBatch(self._layer_shader, type=RenderBatch.RenderType.Solid) if self._layer_view.getCurrentLayerMesh(): batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh()) if self._layer_view.getCurrentLayerJumps(): batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps()) if len(batch.items) > 0: batch.render(self._scene.getActiveCamera()) # The nozzle is drawn when once we know the correct position of the head, # but the user is not using the layer slider, and the compatibility mode is not enabled if not self._switching_layers and not self._compatibility_mode and self._layer_view.getActivity( ) and nozzle_node is not None: if head_position is not None: nozzle_node.setVisible(True) nozzle_node.setPosition(head_position) nozzle_batch = RenderBatch( self._nozzle_shader, type=RenderBatch.RenderType.Transparent) nozzle_batch.addItem(nozzle_node.getWorldTransformation(), mesh=nozzle_node.getMeshData()) nozzle_batch.render(self._scene.getActiveCamera()) # Render toolhandles on top of the layerview if len(tool_handle_batch.items) > 0: tool_handle_batch.render(self._scene.getActiveCamera()) self.release()
def event(self, event) -> bool: modifiers = QApplication.keyboardModifiers() ctrl_is_active = modifiers & Qt.ControlModifier shift_is_active = modifiers & Qt.ShiftModifier if event.type == Event.KeyPressEvent and ctrl_is_active: amount = 10 if shift_is_active else 1 if event.key == KeyEvent.UpKey: self.setLayer(self._current_layer_num + amount) return True if event.key == KeyEvent.DownKey: self.setLayer(self._current_layer_num - amount) return True if event.type == Event.ViewActivateEvent: # Start listening to changes. Application.getInstance().getPreferences( ).preferenceChanged.connect(self._onPreferencesChanged) self._controller.getScene().getRoot().childrenChanged.connect( self._onSceneChanged) self.calculateMaxLayers() self.calculateMaxPathsOnLayer(self._current_layer_num) # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching. # This can happen when you do the following steps: # 1. Start Cura # 2. Load a model # 3. Switch to Custom mode # 4. Select the model and click on the per-object tool icon # 5. Switch view to Layer view or X-Ray # 6. Cura will very likely crash # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why. # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL # context is None. if Platform.isOSX(): if QOpenGLContext.currentContext() is None: Logger.log( "d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later" ) CuraApplication.getInstance().callLater( lambda e=event: self.event(e)) return False # Make sure the SimulationPass is created layer_pass = self.getSimulationPass() renderer = self.getRenderer() if renderer is None: return False renderer.addRenderPass(layer_pass) # Make sure the NozzleNode is add to the root nozzle = self.getNozzleNode() nozzle.setParent(self.getController().getScene().getRoot()) nozzle.setVisible(False) Application.getInstance().globalContainerStackChanged.connect( self._onGlobalStackChanged) self._onGlobalStackChanged() if not self._simulationview_composite_shader: plugin_path = cast( str, PluginRegistry.getInstance().getPluginPath( "SimulationView")) self._simulationview_composite_shader = OpenGL.getInstance( ).createShaderProgram( os.path.join(plugin_path, "simulationview_composite.shader")) theme = CuraApplication.getInstance().getTheme() if theme is not None: self._simulationview_composite_shader.setUniformValue( "u_background_color", Color(*theme.getColor("viewport_background").getRgb())) self._simulationview_composite_shader.setUniformValue( "u_outline_color", Color(*theme.getColor( "model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = cast( CompositePass, renderer.getRenderPass("composite")) self._old_layer_bindings = self._composite_pass.getLayerBindings( )[:] # make a copy so we can restore to it later self._composite_pass.getLayerBindings().append("simulationview") self._old_composite_shader = self._composite_pass.getCompositeShader( ) self._composite_pass.setCompositeShader( self._simulationview_composite_shader) self._updateSliceWarningVisibility() elif event.type == Event.ViewDeactivateEvent: self._controller.getScene().getRoot().childrenChanged.disconnect( self._onSceneChanged) Application.getInstance().getPreferences( ).preferenceChanged.disconnect(self._onPreferencesChanged) self._wireprint_warning_message.hide() self._slice_first_warning_message.hide() Application.getInstance().globalContainerStackChanged.disconnect( self._onGlobalStackChanged) if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect( self._onPropertyChanged) if self._nozzle_node: self._nozzle_node.setParent(None) renderer = self.getRenderer() if renderer is None: return False if self._layer_pass is not None: renderer.removeRenderPass(self._layer_pass) if self._composite_pass: self._composite_pass.setLayerBindings( cast(List[str], self._old_layer_bindings)) self._composite_pass.setCompositeShader( cast(ShaderProgram, self._old_composite_shader)) return False
def rebuild(self): if not self._width or not self._height or not self._depth: return min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 max_h = self._height min_d = -self._depth / 2 max_d = self._depth / 2 mb = MeshBuilder() mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) self.setMeshData(mb.build()) mb = MeshBuilder() mb.addQuad( Vector(min_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, max_d), Vector(min_w, min_h - 0.2, max_d) ) for n in range(0, 6): v = mb.getVertex(n) mb.setVertexUVCoordinates(n, v[0], v[2]) self._grid_mesh = mb.build() disallowed_area_height = 0.1 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color = color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds. # This is a very nasty hack. This pretty much only works for UM machines. # This disallowed area_size needs a -lot- of rework at some point in the future: TODO if numpy.min(points[:, 1]) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area. size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) else: size = 0 disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.build() else: self._disallowed_area_mesh = None self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d)) skirt_size = 0.0 container_stack = Application.getInstance().getGlobalContainerStack() if container_stack: skirt_size = self._getSkirtSize(container_stack) # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1), maximum = Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
class BuildVolume(SceneNode): VolumeOutlineColor = Color(12, 169, 227, 255) def __init__(self, parent = None): super().__init__(parent) self._width = 0 self._height = 0 self._depth = 0 self._shader = None self._grid_mesh = None self._grid_shader = None self._disallowed_areas = [] self._disallowed_area_mesh = None self.setCalculateBoundingBox(False) self._volume_aabb = None self._active_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) self._onGlobalContainerStackChanged() def setWidth(self, width): if width: self._width = width def setHeight(self, height): if height: self._height = height def setDepth(self, depth): if depth: self._depth = depth def getDisallowedAreas(self): return self._disallowed_areas def setDisallowedAreas(self, areas): self._disallowed_areas = areas def render(self, renderer): if not self.getMeshData(): return True if not self._shader: self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader")) renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines) renderer.queueNode(self, mesh = self._grid_mesh, shader = self._grid_shader, backface_cull = True) if self._disallowed_area_mesh: renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9) return True ## Recalculates the build volume & disallowed areas. def rebuild(self): if not self._width or not self._height or not self._depth: return min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 max_h = self._height min_d = -self._depth / 2 max_d = self._depth / 2 mb = MeshBuilder() mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) self.setMeshData(mb.build()) mb = MeshBuilder() mb.addQuad( Vector(min_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, max_d), Vector(min_w, min_h - 0.2, max_d) ) for n in range(0, 6): v = mb.getVertex(n) mb.setVertexUVCoordinates(n, v[0], v[2]) self._grid_mesh = mb.build() disallowed_area_height = 0.1 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color = color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds. # This is a very nasty hack. This pretty much only works for UM machines. # This disallowed area_size needs a -lot- of rework at some point in the future: TODO if numpy.min(points[:, 1]) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area. size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) else: size = 0 disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.build() else: self._disallowed_area_mesh = None self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d)) skirt_size = 0.0 container_stack = Application.getInstance().getGlobalContainerStack() if container_stack: skirt_size = self._getSkirtSize(container_stack) # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( minimum = Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1), maximum = Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1) ) Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds def getBoundingBox(self): return self._volume_aabb def _onGlobalContainerStackChanged(self): if self._active_container_stack: self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) self._active_container_stack = Application.getInstance().getGlobalContainerStack() if self._active_container_stack: self._active_container_stack.propertyChanged.connect(self._onSettingPropertyChanged) self._width = self._active_container_stack.getProperty("machine_width", "value") if self._active_container_stack.getProperty("print_sequence", "value") == "one_at_a_time": self._height = self._active_container_stack.getProperty("gantry_height", "value") else: self._height = self._active_container_stack.getProperty("machine_height", "value") self._depth = self._active_container_stack.getProperty("machine_depth", "value") self._updateDisallowedAreas() self.rebuild() def _onSettingPropertyChanged(self, setting_key, property_name): if property_name != "value": return if setting_key == "print_sequence": if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time": self._height = self._active_container_stack.getProperty("gantry_height", "value") else: self._height = self._active_container_stack.getProperty("machine_height", "value") self.rebuild() if setting_key in self._skirt_settings: self._updateDisallowedAreas() self.rebuild() def _updateDisallowedAreas(self): if not self._active_container_stack: return disallowed_areas = self._active_container_stack.getProperty("machine_disallowed_areas", "value") areas = [] skirt_size = self._getSkirtSize(self._active_container_stack) if disallowed_areas: # Extend every area already in the disallowed_areas with the skirt size. for area in disallowed_areas: poly = Polygon(numpy.array(area, numpy.float32)) poly = poly.getMinkowskiHull(Polygon(numpy.array([ [-skirt_size, 0], [-skirt_size * 0.707, skirt_size * 0.707], [0, skirt_size], [skirt_size * 0.707, skirt_size * 0.707], [skirt_size, 0], [skirt_size * 0.707, -skirt_size * 0.707], [0, -skirt_size], [-skirt_size * 0.707, -skirt_size * 0.707] ], numpy.float32))) areas.append(poly) # Add the skirt areas around the borders of the build plate. if skirt_size > 0: half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2 half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2 areas.append(Polygon(numpy.array([ [-half_machine_width, -half_machine_depth], [-half_machine_width, half_machine_depth], [-half_machine_width + skirt_size, half_machine_depth - skirt_size], [-half_machine_width + skirt_size, -half_machine_depth + skirt_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [half_machine_width, half_machine_depth], [half_machine_width, -half_machine_depth], [half_machine_width - skirt_size, -half_machine_depth + skirt_size], [half_machine_width - skirt_size, half_machine_depth - skirt_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [-half_machine_width, half_machine_depth], [half_machine_width, half_machine_depth], [half_machine_width - skirt_size, half_machine_depth - skirt_size], [-half_machine_width + skirt_size, half_machine_depth - skirt_size] ], numpy.float32))) areas.append(Polygon(numpy.array([ [half_machine_width, -half_machine_depth], [-half_machine_width, -half_machine_depth], [-half_machine_width + skirt_size, -half_machine_depth + skirt_size], [half_machine_width - skirt_size, -half_machine_depth + skirt_size] ], numpy.float32))) self._disallowed_areas = areas ## Convenience function to calculate the size of the bed adhesion. def _getSkirtSize(self, container_stack): skirt_size = 0.0 adhesion_type = container_stack.getProperty("adhesion_type", "value") if adhesion_type == "skirt": skirt_distance = container_stack.getProperty("skirt_gap", "value") skirt_line_count = container_stack.getProperty("skirt_line_count", "value") skirt_size = skirt_distance + (skirt_line_count * container_stack.getProperty("skirt_line_width", "value")) elif adhesion_type == "brim": skirt_size = container_stack.getProperty("brim_line_count", "value") * container_stack.getProperty("skirt_line_width", "value") elif adhesion_type == "raft": skirt_size = container_stack.getProperty("raft_margin", "value") if container_stack.getProperty("draft_shield_enabled", "value"): skirt_size += container_stack.getProperty("draft_shield_dist", "value") if container_stack.getProperty("xy_offset", "value"): skirt_size += container_stack.getProperty("xy_offset", "value") return skirt_size def _clamp(self, value, min_value, max_value): return max(min(value, max_value), min_value) _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"]
def _checkSetup(self): if not self._extruders_model: self._extruders_model = Application.getInstance( ).getExtrudersModel() if not self._theme: self._theme = Application.getInstance().getTheme() if not self._enabled_shader: self._enabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "overhang.shader")) self._enabled_shader.setUniformValue( "u_overhangColor", Color(*self._theme.getColor("model_overhang").getRgb())) self._enabled_shader.setUniformValue("u_renderError", 0.0) if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._disabled_shader.setUniformValue( "u_diffuseColor1", Color(*self._theme.getColor("model_unslicable").getRgb())) self._disabled_shader.setUniformValue( "u_diffuseColor2", Color(*self._theme.getColor("model_unslicable_alt").getRgb())) self._disabled_shader.setUniformValue("u_width", 50.0) if not self._non_printing_shader: self._non_printing_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "transparent_object.shader")) self._non_printing_shader.setUniformValue( "u_diffuseColor", Color(*self._theme.getColor("model_non_printing").getRgb())) self._non_printing_shader.setUniformValue("u_opacity", 0.1) if not self._support_mesh_shader: self._support_mesh_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._support_mesh_shader.setUniformValue("u_vertical_stripes", True) self._support_mesh_shader.setUniformValue("u_width", 5.0) if not Application.getInstance().getPreferences().getValue( self._show_xray_warning_preference): self._xray_shader = None self._xray_composite_shader = None if self._composite_pass and 'xray' in self._composite_pass.getLayerBindings( ): self._composite_pass.setLayerBindings(self._old_layer_bindings) self._composite_pass.setCompositeShader( self._old_composite_shader) self._old_layer_bindings = None self._old_composite_shader = None self._enabled_shader.setUniformValue( "u_renderError", 0.0) # We don't want any error markers!. self._xray_warning_message.hide() else: if not self._xray_shader: self._xray_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "xray.shader")) if not self._xray_composite_shader: self._xray_composite_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "xray_composite.shader")) theme = Application.getInstance().getTheme() self._xray_composite_shader.setUniformValue( "u_background_color", Color(*theme.getColor("viewport_background").getRgb())) self._xray_composite_shader.setUniformValue( "u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) self._xray_composite_shader.setUniformValue( "u_flat_error_color_mix", 0.) # Don't show flat error color in solid-view. renderer = self.getRenderer() if not self._composite_pass or not 'xray' in self._composite_pass.getLayerBindings( ): # Currently the RenderPass constructor requires a size > 0 # This should be fixed in RenderPass's constructor. self._xray_pass = XRayPass.XRayPass(1, 1) self._enabled_shader.setUniformValue( "u_renderError", 1.0) # We don't want any error markers!. renderer.addRenderPass(self._xray_pass) if not self._composite_pass: self._composite_pass = self.getRenderer().getRenderPass( "composite") self._old_layer_bindings = self._composite_pass.getLayerBindings( ) self._composite_pass.setLayerBindings( ["default", "selection", "xray"]) self._old_composite_shader = self._composite_pass.getCompositeShader( ) self._composite_pass.setCompositeShader( self._xray_composite_shader)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() if not self._enabled_material: if Preferences.getInstance().getValue("view/show_overhang"): self._enabled_material = renderer.createMaterial( Resources.getPath(Resources.Shaders, "default.vert"), Resources.getPath(Resources.Shaders, "overhang.frag")) else: self._enabled_material = renderer.createMaterial( Resources.getPath(Resources.Shaders, "default.vert"), Resources.getPath(Resources.Shaders, "default.frag")) self._enabled_material.setUniformValue("u_ambientColor", Color(0.3, 0.3, 0.3, 1.0)) self._enabled_material.setUniformValue("u_diffuseColor", self.EnabledColor) self._enabled_material.setUniformValue("u_specularColor", Color(0.4, 0.4, 0.4, 1.0)) self._enabled_material.setUniformValue("u_overhangColor", Color(1.0, 0.0, 0.0, 1.0)) self._enabled_material.setUniformValue("u_shininess", 20.) if not self._disabled_material: self._disabled_material = renderer.createMaterial( Resources.getPath(Resources.Shaders, "default.vert"), Resources.getPath(Resources.Shaders, "default.frag")) self._disabled_material.setUniformValue("u_ambientColor", Color(0.3, 0.3, 0.3, 1.0)) self._disabled_material.setUniformValue("u_diffuseColor", self.DisabledColor) self._disabled_material.setUniformValue("u_specularColor", Color(0.4, 0.4, 0.4, 1.0)) self._disabled_material.setUniformValue("u_overhangColor", Color(1.0, 0.0, 0.0, 1.0)) self._disabled_material.setUniformValue("u_shininess", 20.) if Application.getInstance().getMachineManager().getActiveProfile(): profile = Application.getInstance().getMachineManager( ).getActiveProfile() if profile.getSettingValue("support_enable"): angle = profile.getSettingValue("support_angle") if angle != None: self._enabled_material.setUniformValue( "u_overhangAngle", math.cos(math.radians(90 - angle))) else: self._enabled_material.setUniformValue( "u_overhangAngle", math.cos(math.radians(0))) for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): # TODO: Find a better way to handle this #if node.getBoundingBoxMesh(): # renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines) if hasattr(node, "_outside_buildarea"): if node._outside_buildarea: renderer.queueNode( node, material=self._disabled_material) else: renderer.queueNode(node, material=self._enabled_material) else: renderer.queueNode(node, material=self._enabled_material) if node.callDecoration("isGroup"): renderer.queueNode(scene.getRoot(), mesh=node.getBoundingBoxMesh(), mode=Renderer.RenderLines)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() if not self._extruders_model: self._extruders_model = Application.getInstance( ).getExtrudersModel() if not self._theme: self._theme = Application.getInstance().getTheme() if not self._enabled_shader: self._enabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "overhang.shader")) self._enabled_shader.setUniformValue( "u_overhangColor", Color(*self._theme.getColor("model_overhang").getRgb())) if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._disabled_shader.setUniformValue( "u_diffuseColor1", Color(*self._theme.getColor("model_unslicable").getRgb())) self._disabled_shader.setUniformValue( "u_diffuseColor2", Color(*self._theme.getColor("model_unslicable_alt").getRgb())) self._disabled_shader.setUniformValue("u_width", 50.0) if not self._non_printing_shader: self._non_printing_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "transparent_object.shader")) self._non_printing_shader.setUniformValue( "u_diffuseColor", Color(*self._theme.getColor("model_non_printing").getRgb())) self._non_printing_shader.setUniformValue("u_opacity", 0.6) if not self._support_mesh_shader: self._support_mesh_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._support_mesh_shader.setUniformValue("u_vertical_stripes", True) self._support_mesh_shader.setUniformValue("u_width", 5.0) global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault( "support_extruder_nr") support_angle_stack = Application.getInstance().getExtruderManager( ).getExtruderStack(support_extruder_nr) if support_angle_stack is not None and Application.getInstance( ).getPreferences().getValue("view/show_overhang"): angle = support_angle_stack.getProperty( "support_angle", "value") # Make sure the overhang angle is valid before passing it to the shader if angle is not None and angle >= 0 and angle <= 90: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(90 - angle))) else: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(0)) ) #Overhang angle of 0 causes no area at all to be marked as overhang. else: self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible( ) and not node.callDecoration("getLayerData"): uniforms = {} shade_factor = 1.0 per_mesh_stack = node.callDecoration("getStack") extruder_index = node.callDecoration( "getActiveExtruderPosition") if extruder_index is None: extruder_index = "0" extruder_index = int(extruder_index) # Use the support extruder instead of the active extruder if this is a support_mesh if per_mesh_stack: if per_mesh_stack.getProperty("support_mesh", "value"): extruder_index = int( global_container_stack. getExtruderPositionValueWithDefault( "support_extruder_nr")) try: material_color = self._extruders_model.getItem( extruder_index)["color"] except KeyError: material_color = self._extruders_model.defaultColors[0] if extruder_index != ExtruderManager.getInstance( ).activeExtruderIndex: # Shade objects that are printed with the non-active extruder 25% darker shade_factor = 0.6 try: # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0]) uniforms["diffuse_color"] = [ shade_factor * int(material_color[1:3], 16) / 255, shade_factor * int(material_color[3:5], 16) / 255, shade_factor * int(material_color[5:7], 16) / 255, 1.0 ] except ValueError: pass if node.callDecoration("isNonPrintingMesh"): if per_mesh_stack and (per_mesh_stack.getProperty( "infill_mesh", "value") or per_mesh_stack.getProperty( "cutting_mesh", "value")): renderer.queueNode( node, shader=self._non_printing_shader, uniforms=uniforms, transparent=True) else: renderer.queueNode( node, shader=self._non_printing_shader, transparent=True) elif getattr(node, "_outside_buildarea", False): renderer.queueNode(node, shader=self._disabled_shader) elif per_mesh_stack and per_mesh_stack.getProperty( "support_mesh", "value"): # Render support meshes with a vertical stripe that is darker shade_factor = 0.6 uniforms["diffuse_color_2"] = [ uniforms["diffuse_color"][0] * shade_factor, uniforms["diffuse_color"][1] * shade_factor, uniforms["diffuse_color"][2] * shade_factor, 1.0 ] renderer.queueNode(node, shader=self._support_mesh_shader, uniforms=uniforms) else: renderer.queueNode(node, shader=self._enabled_shader, uniforms=uniforms) if node.callDecoration("isGroup") and Selection.isSelected( node): renderer.queueNode(scene.getRoot(), mesh=node.getBoundingBoxMesh(), mode=RenderBatch.RenderMode.LineLoop)
class ConvexHullNode(SceneNode): def __init__(self, node, hull, parent = None): super().__init__(parent) self.setCalculateBoundingBox(False) self._shader = None self._original_parent = parent self._inherit_orientation = False self._inherit_scale = False self._color = Color(35, 35, 35, 128) self._node = node self._node.transformationChanged.connect(self._onNodePositionChanged) self._node.parentChanged.connect(self._onNodeParentChanged) self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._onNodeDecoratorsChanged(self._node) self._convex_hull_head_mesh = None self._hull = hull hull_points = self._hull.getPoints() hull_mesh = self.createHullMesh(self._hull.getPoints()) if hull_mesh: self.setMeshData(hull_mesh) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints()) def createHullMesh(self, hull_points): mesh = MeshData() if len(hull_points) > 3: center = (hull_points.min(0) + hull_points.max(0)) / 2.0 mesh.addVertex(center[0], -0.1, center[1]) else: return None for point in hull_points: mesh.addVertex(point[0], -0.1, point[1]) indices = [] for i in range(len(hull_points) - 1): indices.append([0, i + 1, i + 2]) indices.append([0, mesh.getVertexCount() - 1, 1]) mesh.addIndices(numpy.array(indices, numpy.int32)) return mesh def getWatchedNode(self): return self._node def render(self, renderer): if not self._shader: self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) self._shader.setUniformValue("u_color", self._color) if self.getParent(): renderer.queueNode(self, transparent = True, shader = self._shader, backface_cull = True, sort = -8) if self._convex_hull_head_mesh: renderer.queueNode(self, shader = self._shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8) return True def _onNodePositionChanged(self, node): if node.callDecoration("getConvexHull"): node.callDecoration("setConvexHull", None) node.callDecoration("setConvexHullNode", None) self.setParent(None) def _onNodeParentChanged(self, node): if node.getParent(): self.setParent(self._original_parent) else: self.setParent(None) def _onNodeDecoratorsChanged(self, node): self._color = Color(35, 35, 35, 0.5) if not node: return if node.hasDecoration("getProfile"): self._color.setR(0.75) if node.hasDecoration("getSetting"): self._color.setG(0.75)
class ConvexHullNode(SceneNode): def __init__(self, node, hull, parent = None): super().__init__(parent) self.setCalculateBoundingBox(False) self._shader = None self._original_parent = parent self._inherit_orientation = False self._inherit_scale = False self._color = Color(35, 35, 35, 128) self._mesh_height = 0.1 #The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._node = node self._node.transformationChanged.connect(self._onNodePositionChanged) self._node.parentChanged.connect(self._onNodeParentChanged) self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged) self._onNodeDecoratorsChanged(self._node) self._convex_hull_head_mesh = None self._hull = hull hull_points = self._hull.getPoints() hull_mesh = self.createHullMesh(self._hull.getPoints()) if hull_mesh: self.setMeshData(hull_mesh) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints()) def createHullMesh(self, hull_points): #Input checking. if len(hull_points) < 3: return None mesh_builder = MeshBuilder() point_first = Vector(hull_points[0][0], self._mesh_height, hull_points[0][1]) point_previous = Vector(hull_points[1][0], self._mesh_height, hull_points[1][1]) for point in hull_points[2:]: #Add the faces in the order of a triangle fan. point_new = Vector(point[0], self._mesh_height, point[1]) mesh_builder.addFace(point_first, point_previous, point_new, color = self._color) point_previous = point_new #Prepare point_previous for the next triangle. return mesh_builder.getData() def getWatchedNode(self): return self._node def render(self, renderer): if not self._shader: self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) self._shader.setUniformValue("u_color", self._color) if self.getParent(): renderer.queueNode(self, transparent = True, shader = self._shader, backface_cull = True, sort = -8) if self._convex_hull_head_mesh: renderer.queueNode(self, shader = self._shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8) return True def _onNodePositionChanged(self, node): if node.callDecoration("getConvexHull"): node.callDecoration("setConvexHull", None) node.callDecoration("setConvexHullNode", None) self.setParent(None) def _onNodeParentChanged(self, node): if node.getParent(): self.setParent(self._original_parent) else: self.setParent(None) def _onNodeDecoratorsChanged(self, node): self._color = Color(35, 35, 35, 0.5) if not node: return if node.hasDecoration("getProfile"): self._color.setR(0.75) if node.hasDecoration("getSetting"): self._color.setG(0.75)
def event(self, event): if event.type == Event.ViewActivateEvent: # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching. # This can happen when you do the following steps: # 1. Start Cura # 2. Load a model # 3. Switch to Custom mode # 4. Select the model and click on the per-object tool icon # 5. Switch view to Layer view or X-Ray # 6. Cura will very likely crash # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why. # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL # context is None. if Platform.isOSX(): if QOpenGLContext.currentContext() is None: Logger.log( "d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later" ) CuraApplication.getInstance().callLater( lambda e=event: self.event(e)) return if not self._xray_pass: # Currently the RenderPass constructor requires a size > 0 # This should be fixed in RenderPass's constructor. self._xray_pass = XRayPass.XRayPass(1, 1) self.getRenderer().addRenderPass(self._xray_pass) if not self._xray_composite_shader: self._xray_composite_shader = OpenGL.getInstance( ).createShaderProgram( os.path.join( PluginRegistry.getInstance().getPluginPath("XRayView"), "xray_composite.shader")) theme = Application.getInstance().getTheme() self._xray_composite_shader.setUniformValue( "u_background_color", Color(*theme.getColor("viewport_background").getRgb())) self._xray_composite_shader.setUniformValue( "u_error_color", Color(*theme.getColor("xray_error").getRgb())) self._xray_composite_shader.setUniformValue( "u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = self.getRenderer().getRenderPass( "composite") self._old_layer_bindings = self._composite_pass.getLayerBindings() self._composite_pass.setLayerBindings( ["default", "selection", "xray"]) self._old_composite_shader = self._composite_pass.getCompositeShader( ) self._composite_pass.setCompositeShader( self._xray_composite_shader) if event.type == Event.ViewDeactivateEvent: self.getRenderer().removeRenderPass(self._xray_pass) self._composite_pass.setLayerBindings(self._old_layer_bindings) self._composite_pass.setCompositeShader(self._old_composite_shader)