PickingPass.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from UM.Application import Application
  4. from UM.Math.Vector import Vector
  5. from UM.Resources import Resources
  6. from UM.View.RenderPass import RenderPass
  7. from UM.View.GL.OpenGL import OpenGL
  8. from UM.View.RenderBatch import RenderBatch
  9. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
  10. ## A RenderPass subclass that renders a the distance of selectable objects from the active camera to a texture.
  11. # The texture is used to map a 2d location (eg the mouse location) to a world space position
  12. #
  13. # Note that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels
  14. class PickingPass(RenderPass):
  15. def __init__(self, width: int, height: int):
  16. super().__init__("picking", width, height)
  17. self._renderer = Application.getInstance().getRenderer()
  18. self._shader = None
  19. self._scene = Application.getInstance().getController().getScene()
  20. def render(self) -> None:
  21. if not self._shader:
  22. self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "camera_distance.shader"))
  23. width, height = self.getSize()
  24. self._gl.glViewport(0, 0, width, height)
  25. self._gl.glClearColor(1.0, 1.0, 1.0, 0.0)
  26. self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
  27. # Create a new batch to be rendered
  28. batch = RenderBatch(self._shader)
  29. # Fill up the batch with objects that can be sliced. `
  30. for node in DepthFirstIterator(self._scene.getRoot()):
  31. if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
  32. batch.addItem(node.getWorldTransformation(), node.getMeshData())
  33. self.bind()
  34. batch.render(self._scene.getActiveCamera())
  35. self.release()
  36. ## Get the distance in mm from the camera to at a certain pixel coordinate.
  37. def getPickedDepth(self, x: int, y: int) -> float:
  38. output = self.getOutput()
  39. window_size = self._renderer.getWindowSize()
  40. px = (0.5 + x / 2.0) * window_size[0]
  41. py = (0.5 + y / 2.0) * window_size[1]
  42. if px < 0 or px > (output.width() - 1) or py < 0 or py > (output.height() - 1):
  43. return -1
  44. distance = output.pixel(px, py) # distance in micron, from in r, g & b channels
  45. distance = (distance & 0x00ffffff) / 1000. # drop the alpha channel and covert to mm
  46. return distance
  47. ## Get the world coordinates of a picked point
  48. def getPickedPosition(self, x: int, y: int) -> Vector:
  49. distance = self.getPickedDepth(x, y)
  50. ray = self._scene.getActiveCamera().getRay(x, y)
  51. return ray.getPointAlongRay(distance)