SimulationPass.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # Copyright (c) 2017 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from UM.Math.Color import Color
  4. from UM.Math.Vector import Vector
  5. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
  6. from UM.Resources import Resources
  7. from UM.Scene.SceneNode import SceneNode
  8. from UM.Scene.ToolHandle import ToolHandle
  9. from UM.Application import Application
  10. from UM.PluginRegistry import PluginRegistry
  11. from UM.View.RenderPass import RenderPass
  12. from UM.View.RenderBatch import RenderBatch
  13. from UM.View.GL.OpenGL import OpenGL
  14. from cura.Settings.ExtruderManager import ExtruderManager
  15. import os.path
  16. ## RenderPass used to display g-code paths.
  17. from .NozzleNode import NozzleNode
  18. class SimulationPass(RenderPass):
  19. def __init__(self, width, height):
  20. super().__init__("simulationview", width, height)
  21. self._layer_shader = None
  22. self._layer_shadow_shader = None
  23. self._current_shader = None # This shader will be the shadow or the normal depending if the user wants to see the paths or the layers
  24. self._tool_handle_shader = None
  25. self._nozzle_shader = None
  26. self._old_current_layer = 0
  27. self._old_current_path = 0
  28. self._switching_layers = True # It tracks when the user is moving the layers' slider
  29. self._gl = OpenGL.getInstance().getBindingsObject()
  30. self._scene = Application.getInstance().getController().getScene()
  31. self._extruder_manager = ExtruderManager.getInstance()
  32. self._layer_view = None
  33. self._compatibility_mode = None
  34. def setSimulationView(self, layerview):
  35. self._layer_view = layerview
  36. self._compatibility_mode = layerview.getCompatibilityMode()
  37. def _updateLayerShaderValues(self):
  38. # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
  39. self._layer_shader.setUniformValue("u_active_extruder",
  40. float(max(0, self._extruder_manager.activeExtruderIndex)))
  41. if self._layer_view:
  42. self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
  43. self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
  44. self._layer_shader.setUniformValue("u_max_thickness", self._layer_view.getMaxThickness())
  45. self._layer_shader.setUniformValue("u_min_thickness", self._layer_view.getMinThickness())
  46. self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getSimulationViewType())
  47. self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
  48. self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
  49. self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
  50. self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
  51. self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
  52. else:
  53. # defaults
  54. self._layer_shader.setUniformValue("u_max_feedrate", 1)
  55. self._layer_shader.setUniformValue("u_min_feedrate", 0)
  56. self._layer_shader.setUniformValue("u_max_thickness", 1)
  57. self._layer_shader.setUniformValue("u_min_thickness", 0)
  58. self._layer_shader.setUniformValue("u_layer_view_type", 1)
  59. self._layer_shader.setUniformValue("u_extruder_opacity", [1, 1, 1, 1])
  60. self._layer_shader.setUniformValue("u_show_travel_moves", 0)
  61. self._layer_shader.setUniformValue("u_show_helpers", 1)
  62. self._layer_shader.setUniformValue("u_show_skin", 1)
  63. self._layer_shader.setUniformValue("u_show_infill", 1)
  64. def render(self):
  65. if not self._layer_shader:
  66. if self._compatibility_mode:
  67. shader_filename = "layers.shader"
  68. shadow_shader_filename = "layers_shadow.shader"
  69. else:
  70. shader_filename = "layers3d.shader"
  71. shadow_shader_filename = "layers3d_shadow.shader"
  72. self._layer_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shader_filename))
  73. self._layer_shadow_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), shadow_shader_filename))
  74. self._current_shader = self._layer_shader
  75. self._updateLayerShaderValues()
  76. if not self._tool_handle_shader:
  77. self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
  78. if not self._nozzle_shader:
  79. self._nozzle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
  80. self._nozzle_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_nozzle").getRgb()))
  81. self.bind()
  82. tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay, backface_cull = True)
  83. head_position = None # Indicates the current position of the print head
  84. nozzle_node = None
  85. for node in DepthFirstIterator(self._scene.getRoot()):
  86. if isinstance(node, ToolHandle):
  87. tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
  88. elif isinstance(node, NozzleNode):
  89. nozzle_node = node
  90. nozzle_node.setVisible(False)
  91. elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
  92. layer_data = node.callDecoration("getLayerData")
  93. if not layer_data:
  94. continue
  95. # Render all layers below a certain number as line mesh instead of vertices.
  96. if self._layer_view._current_layer_num > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
  97. start = self._layer_view.start_elements_index
  98. end = self._layer_view.end_elements_index
  99. index = self._layer_view._current_path_num
  100. offset = 0
  101. layer = layer_data.getLayer(self._layer_view._current_layer_num)
  102. if layer is None:
  103. continue
  104. for polygon in layer.polygons:
  105. # The size indicates all values in the two-dimension array, and the second dimension is
  106. # always size 3 because we have 3D points.
  107. if index >= polygon.data.size // 3 - offset:
  108. index -= polygon.data.size // 3 - offset
  109. 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
  110. continue
  111. # The head position is calculated and translated
  112. head_position = Vector(polygon.data[index + offset][0], polygon.data[index + offset][1],
  113. polygon.data[index + offset][2]) + node.getWorldPosition()
  114. break
  115. # Calculate the range of paths in the last layer
  116. current_layer_start = end
  117. current_layer_end = end + self._layer_view._current_path_num * 2 # Because each point is used twice
  118. # This uses glDrawRangeElements internally to only draw a certain range of lines.
  119. # All the layers but the current selected layer are rendered first
  120. if self._old_current_path != self._layer_view._current_path_num:
  121. self._current_shader = self._layer_shadow_shader
  122. self._switching_layers = False
  123. if not self._layer_view.isSimulationRunning() and self._old_current_layer != self._layer_view._current_layer_num:
  124. self._current_shader = self._layer_shader
  125. self._switching_layers = True
  126. layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end), backface_cull = True)
  127. layers_batch.addItem(node.getWorldTransformation(), layer_data)
  128. layers_batch.render(self._scene.getActiveCamera())
  129. # Current selected layer is rendered
  130. current_layer_batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (current_layer_start, current_layer_end))
  131. current_layer_batch.addItem(node.getWorldTransformation(), layer_data)
  132. current_layer_batch.render(self._scene.getActiveCamera())
  133. self._old_current_layer = self._layer_view._current_layer_num
  134. self._old_current_path = self._layer_view._current_path_num
  135. # Create a new batch that is not range-limited
  136. batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid)
  137. if self._layer_view.getCurrentLayerMesh():
  138. batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh())
  139. if self._layer_view.getCurrentLayerJumps():
  140. batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps())
  141. if len(batch.items) > 0:
  142. batch.render(self._scene.getActiveCamera())
  143. # The nozzle is drawn when once we know the correct position of the head,
  144. # but the user is not using the layer slider, and the compatibility mode is not enabled
  145. if not self._switching_layers and not self._compatibility_mode and self._layer_view.getActivity() and nozzle_node is not None:
  146. if head_position is not None:
  147. nozzle_node.setVisible(True)
  148. nozzle_node.setPosition(head_position)
  149. nozzle_batch = RenderBatch(self._nozzle_shader, type = RenderBatch.RenderType.Transparent)
  150. nozzle_batch.addItem(nozzle_node.getWorldTransformation(), mesh = nozzle_node.getMeshData())
  151. nozzle_batch.render(self._scene.getActiveCamera())
  152. # Render toolhandles on top of the layerview
  153. if len(tool_handle_batch.items) > 0:
  154. tool_handle_batch.render(self._scene.getActiveCamera())
  155. self.release()