mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Add depth pass for picking a location
This commit is contained in:
parent
f40e9bffa9
commit
0caea24afc
3 changed files with 160 additions and 10 deletions
60
cura/DepthPass.py
Normal file
60
cura/DepthPass.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from UM.Application import Application
|
||||
from UM.Math.Color import Color
|
||||
from UM.Resources import Resources
|
||||
|
||||
from UM.View.RenderPass import RenderPass
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
from UM.View.RenderBatch import RenderBatch
|
||||
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
|
||||
## A RenderPass subclass that renders a depthmap of selectable objects to a texture.
|
||||
# It uses the active camera by default, but it can be overridden to use a different camera.
|
||||
#
|
||||
# Note that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels
|
||||
class DepthPass(RenderPass):
|
||||
def __init__(self, width: int, height: int):
|
||||
super().__init__("preview", width, height, 0)
|
||||
|
||||
self._renderer = Application.getInstance().getRenderer()
|
||||
|
||||
self._shader = None
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
|
||||
|
||||
def render(self) -> None:
|
||||
if not self._shader:
|
||||
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "camera_distance.shader"))
|
||||
|
||||
self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
|
||||
|
||||
# Create a new batch to be rendered
|
||||
batch = RenderBatch(self._shader)
|
||||
|
||||
# Fill up the batch with objects that can be sliced. `
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
|
||||
batch.addItem(node.getWorldTransformation(), node.getMeshData())
|
||||
|
||||
self.bind()
|
||||
batch.render(self._scene.getActiveCamera())
|
||||
self.release()
|
||||
|
||||
## Get the distance in mm from the camera to at a certain pixel coordinate.
|
||||
def getDepthAtPosition(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
|
||||
|
||||
distance = output.pixel(px, py) # distance in micron, from in r, g & b channels
|
||||
return distance / 1000.
|
|
@ -4,14 +4,16 @@ from UM.Math.Vector import Vector
|
|||
from UM.Tool import Tool
|
||||
from PyQt5.QtCore import Qt, QUrl
|
||||
from UM.Application import Application
|
||||
from UM.Event import Event
|
||||
from UM.Event import Event, MouseEvent
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
from cura.DepthPass import DepthPass
|
||||
|
||||
import os
|
||||
import os.path
|
||||
|
@ -25,15 +27,22 @@ class SupportEraser(Tool):
|
|||
def event(self, event):
|
||||
super().event(event)
|
||||
|
||||
if event.type == Event.ToolActivateEvent:
|
||||
if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
|
||||
active_camera = self._controller.getScene().getActiveCamera()
|
||||
|
||||
# Load the remover mesh:
|
||||
self._createEraserMesh()
|
||||
# Create depth pass for picking
|
||||
render_width, render_height = active_camera.getWindowSize()
|
||||
depth_pass = DepthPass(int(render_width), int(render_height))
|
||||
depth_pass.render()
|
||||
|
||||
# After we load the mesh, deactivate the tool again:
|
||||
self.getController().setActiveTool(None)
|
||||
distance = depth_pass.getDepthAtPosition(event.x, event.y)
|
||||
ray = active_camera.getRay(event.x, event.y)
|
||||
picked_position = ray.getPointAlongRay(distance)
|
||||
|
||||
def _createEraserMesh(self):
|
||||
# Add the anto_overhang_mesh cube:
|
||||
self._createEraserMesh(picked_position)
|
||||
|
||||
def _createEraserMesh(self, position: Vector):
|
||||
node = CuraSceneNode()
|
||||
|
||||
node.setName("Eraser")
|
||||
|
@ -41,9 +50,7 @@ class SupportEraser(Tool):
|
|||
mesh = MeshBuilder()
|
||||
mesh.addCube(10,10,10)
|
||||
node.setMeshData(mesh.build())
|
||||
# Place the cube in the platform. Do it manually so it works if the "automatic drop models" is OFF
|
||||
move_vector = Vector(0, 5, 0)
|
||||
node.setPosition(move_vector)
|
||||
node.setPosition(position)
|
||||
|
||||
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||
|
||||
|
|
83
resources/shaders/camera_distance.shader
Normal file
83
resources/shaders/camera_distance.shader
Normal file
|
@ -0,0 +1,83 @@
|
|||
[shaders]
|
||||
vertex =
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
|
||||
attribute highp vec4 a_vertex;
|
||||
|
||||
varying highp vec3 v_vertex;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 world_space_vert = u_modelMatrix * a_vertex;
|
||||
gl_Position = u_viewProjectionMatrix * world_space_vert;
|
||||
|
||||
v_vertex = world_space_vert.xyz;
|
||||
}
|
||||
|
||||
fragment =
|
||||
uniform highp vec3 u_viewPosition;
|
||||
|
||||
varying highp vec3 v_vertex;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float distance_to_camera = distance(v_vertex, u_viewPosition) * 1000.; // distance in micron
|
||||
|
||||
vec3 encoded; // encode float into 3 8-bit channels; this gives a precision of a micron at a range of up to ~16 meter
|
||||
encoded.b = floor(distance_to_camera / 65536.0);
|
||||
encoded.g = floor((distance_to_camera - encoded.b * 65536.0) / 256.0);
|
||||
encoded.r = floor(distance_to_camera - encoded.b * 65536.0 - encoded.g * 256.0);
|
||||
|
||||
gl_FragColor.rgb = encoded / 255.;
|
||||
gl_FragColor.a = 1.0;
|
||||
}
|
||||
|
||||
vertex41core =
|
||||
#version 410
|
||||
uniform highp mat4 u_modelMatrix;
|
||||
uniform highp mat4 u_viewProjectionMatrix;
|
||||
|
||||
in highp vec4 a_vertex;
|
||||
|
||||
out highp vec3 v_vertex;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 world_space_vert = u_modelMatrix * a_vertex;
|
||||
gl_Position = u_viewProjectionMatrix * world_space_vert;
|
||||
|
||||
v_vertex = world_space_vert.xyz;
|
||||
}
|
||||
|
||||
fragment41core =
|
||||
#version 410
|
||||
uniform highp vec3 u_viewPosition;
|
||||
|
||||
in highp vec3 v_vertex;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float distance_to_camera = distance(v_vertex, u_viewPosition) * 1000.; // distance in micron
|
||||
|
||||
vec3 encoded; // encode float into 3 8-bit channels; this gives a precision of a micron at a range of up to ~16 meter
|
||||
encoded.b = floor(distance_to_camera / 65536.0);
|
||||
encoded.g = floor((distance_to_camera - encoded.b * 65536.0) / 256.0);
|
||||
encoded.r = floor(distance_to_camera - encoded.b * 65536.0 - encoded.g * 256.0);
|
||||
|
||||
frag_color.rgb = encoded / 255.;
|
||||
frag_color.a = 1.0;
|
||||
}
|
||||
|
||||
[defaults]
|
||||
|
||||
[bindings]
|
||||
u_modelMatrix = model_matrix
|
||||
u_viewProjectionMatrix = view_projection_matrix
|
||||
u_normalMatrix = normal_matrix
|
||||
u_viewPosition = view_position
|
||||
|
||||
[attributes]
|
||||
a_vertex = vertex
|
Loading…
Add table
Add a link
Reference in a new issue