Is it possible to check if a point is occluded as seen from the camera?
Currently I’m thinking of extending lines from points towards the camera, and if they intersect with geometry then consider them occluded. This doesn’t seem like an efficient method though.
@Alasdair, the visibility may also depend on the current display mode.
In case of a Point3d you can first look if it is visible in the current view, get the direction vector from the camera location and then shoot a ray to all objects visible in that view. Then measure the reflections and compare distances to find about occlusion.
There is a rs.ShootRay() method which works for untrimmed surfaces and polysurface only, so if you want this to work properly with all object types i guess could work with Intersection.MeshRay using rendermeshes of shadeable objects.
_
c.
import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc
def visibleRenderMesh():
all = rs.AllObjects()
all = rs.ObjectsByType(8 | 16 | 32)
objects = []
for item in all:
if rs.IsVisibleInView(item):
objects.append(rs.coercerhinoobject(item))
objRef = Rhino.DocObjects.RhinoObject.GetRenderMeshes(objects, False, True)
meshes = []
for item in objRef:
mesh = item.Mesh()
meshes.append(mesh)
#sc.doc.Objects.AddMesh(mesh)
return meshes
def isVisible(point, object, renderMeshes):
projection = rs.ViewProjection()
if projection == 1: #Parallel
viewCPlane = rs.ViewCPlane()
bounds = rs.BoundingBox(object, viewCPlane)
rs.AddLine(bounds[0], bounds[4])
depth = rs.Distance(bounds[0], bounds[4])
direction = rs.VectorScale(viewCPlane[3], depth*1.2)
viewpoint = rs.PointAdd(point, direction)
ray = Rhino.Geometry.Ray3d(point, direction)
elif projection == 2: #Perspective
viewpoint = rs.ViewCameraPlane()[0]
direction = -rs.VectorCreate(viewpoint, point)
ray = Rhino.Geometry.Ray3d(viewpoint, direction)
else: return False
occluded = False
for mesh in renderMeshes:
intersection = Rhino.Geometry.Intersect.Intersection.MeshRay(mesh, ray)
if 0.99 >= intersection > 0.01:
occluded = True
print "Vertex is Occluded"
break
if occluded == False:
return point
def visibleEditPoints():
object = rs.GetObject(preselect=True, select=True)
if not object: return
brep_obj = rs.coercebrep(object)
vertices = brep_obj.Vertices
rs.EnableRedraw(False)
meshes = visibleRenderMesh()
for i in range (0,vertices.Count):
point = isVisible(vertices[i].Location, object, meshes)
if point: rs.AddPoint(point)
rs.EnableRedraw(True)
visibleEditPoints()
I’m trying to make it work in orthographic views too, which might need a different approach, since meshray returns points that are obscured by geometry in the same plane. I could try a regular intersection.
Hi @Alasdair, note that this returns a parameter along the ray:
To get the point where the ray hit something, you could use:
if intersection > 0.0: hit_pt = ray.PointAt(intersection)
If you know the distance (A) from the camera location to your brep vertex, compare this with the distance (B) from the hit_pt to the camera location. (for the perspective case). You might need to add a small tolerance to this comparison. If B < A, the vertex is likely occluded.
In a parallel view you might get the first distance (A) by measuring the vertex point to a closest point on a plane. The plane could be made from the camera direction (normal) the origin of this plane is the camera location. Then compare with a distance (B) from the hit_pt to that same plane…
_
c.
Hi @Alasdair, great. You might speed things up a litle by getting the projection type once, outside the function as this does not change during the script run. I’ve found that measuring distances in rhinoscript syntax like this can be very slow:
distance = rs.Distance(viewpoint, point)
because it makes some kind of conversion of the inputs every time. It can be done like this without:
True, same goes for many other vector-based operations. If you are going for speed, you are better off doing the math in your own funcitons and not call rs. for this. So for example rs.vectorcreate could be instead calculated like this: FastVectorCreate = array(arrEnd(0) - arrStart(0), arrEnd(1) - arrStart(1), arrEnd(2) - arrStart(2))
(this is my vbscipt example, but it should give you an idea for more speedup. Same goes to all other vector and point operations)
HI @Alasdaire, @clement,
I am trying to put together a Python script to select all existing points which are visible in my current view and see you have this more sophisticated version written but I’m getting a bit confused by your logic surrounding objects and renderMeshes. Any chance one of you could cut down your existing script to help me out?
Best wishes from Nantes,
Graham
Hi @Dancergraham, i guess you would then have for every point, iterate over all geometry and find out if it is obscuring the point by shooting rays. Depending on the amount of points and objects, this can take a while. I am wondering if there is a better (faster) way to do this, eg. using the ZBuffer…
Hi,
I would usually be looking at several hundred points and up to a few thousand surfaces or mesh faces - I would want it to run within a few seconds.
… looks up zbuffer …
err
I ended up hacking together another method using tools I already know, adding small spheres around my points, checking their visibility with the Rhino command _SelVisible and deleting the spheres again. It seems to work well enough for me for now, but the default sphere diameter of 0.1 will probably need some tweaking.
I got a bit scared by the ray tracing method
import rhinoscriptsyntax as rs
def annotate_visible():
"""Annotate all visible points with a text dot"""
rs.EnableRedraw(False)
previous_selection = rs.SelectedObjects()
rs.UnselectAllObjects()
spheres = {}
for pt in rs.ObjectsByType(1):
spheres[pt] = rs.AddSphere(rs.PointCoordinates(pt), 0.1)
rs.Redraw()
rs.Command("_SelVisible _Enter")
visible = set(rs.SelectedObjects())
for pt, sphere in spheres.iteritems():
if sphere in visible:
nom = rs.ObjectName(pt)
col = rs.ObjectColor(pt)
dot = rs.AddTextDot(nom, rs.PointCoordinates(pt))
rs.ObjectColor(dot, col)
rs.ObjectName(dot,nom)
rs.DeleteObject(sphere)
rs.UnselectAllObjects()
rs.SelectObjects(previous_selection)
rs.EnableRedraw(True)
if __name__ == "__main__":
annotate_visible()