Converting 3D Points to Screen Space X&Y
-
On 28/07/2018 at 16:27, xxxxxxxx wrote:
Hello,
I'm trying to get the X&Y pixel coordinates of my polygon plane's vertices (as well as their depth). The output dimensions are 4096x2160 pixels (attached). These are the plane's 3D vectors followed by the pixel coordinates I'm looking to get:# Top Left Corner: (-200,200,0) -> 1493,505 # Top Right Corner: (200,200,0) -> 2458,471 # Bottom Left Corner: (-200,-200,0) -> 1524,1712 # Bottom Right Corner: (200,-200,0) -> 2438,1509
How can I do this in Python? I've tried to use BaseDraw's WC, WC_V, WS, CS methods, but I'm not sure what to do with their results as they are not in pixels.
WC: Vector(-174.873, 181.224, 1288.966) # Top Left Corner WC then CS: Vector(472.901, 423.506, 1288.966) # Top Left Corner WS: Vector(472.901, 423.506, 1288.966) # Top Left Corner WC_V: Vector(-163.448, 174.118, -151.55) # Top Left Corner CS: Vector(-25959351, -25959394, 0),Vector(25960649, -25959394, 0),Vector(25960649, 25960606, 0),Vector(-25959351, 25960606, 0) # All four vertices
Thank you.
-
On 30/07/2018 at 08:32, xxxxxxxx wrote:
Hi blastframe, welcome to our community and thanks for writing here.
With regard to your question, there are a couple of point missing:
- what's the context? Rendering or Viewport?
- The output dimensions you're reporting is the size of the rendered image or is it the size of the viewport?
As a rule of thumb, in case you're evaluating the viewport in a rendering context it's recommended to use BaseDocument::GetRenderBaseDraw() whilst if in viewport feel free to use BaseDocument::GetActiveBaseDraw().
Aside fro this also note that the origin of the screen space is the top/left corner - where the "Perspective" label is - and ends on the bottom/right - where the "Grid spacing" label is.
A brief test in viewport provides the correct results with this code
def main() : if not doc or not op: return activeBD = doc.GetActiveBaseDraw() if not activeBD: return points = op.GetAllPoints() if not points: return for point in points: print "point: [",point,"] / [", activeBD.WS(point),"]" if __name__=='__main__': main()
Hoping it helps, give best.
-
On 02/08/2018 at 08:41, xxxxxxxx wrote:
Hi Riccardo,
Thank you again for the reply. To answer your questions:- the context is Rendering.
- The output dimensions are the size of the rendered image
I used the code you provided with GetRenderBaseDraw(). It still is giving me numbers that do not equate to the X & Y coordinates in pixels. For example, if the rendered image is 1920x1080 and I have a point at 0,200,-200, the pixel coordinates in the image are 657 x 144. The script's output is:
point: [ Vector(0, 200, -200) ] / [ Vector(443.885, 338.496, 880.435) ]
If I use the Camera to screen conversion method, I get closer, but the Y is a crazy value:
for point in points: print "point: [",point,"] / [", activeBD.CS(point, True),"]"
Output:
point: [ Vector(0, 200, -200) ] / [ Vector(649, -25959394, -500000000) ]
Can you help me get the values that I'm seeking? Thanks again!
-
On 03/08/2018 at 06:23, xxxxxxxx wrote:
Hi Blastframe thanks for following up.
I've prepared the following script hoping it works nicely for you.
def main() : # check for doc being valid if not doc: return # retrieve the BaseDraw of the view set to "Use as Render View" renderBD = doc.GetRenderBaseDraw() if not renderBD: return # retrieve the safe-frame information renderSafeFrame = renderBD.GetSafeFrame() renderSafeFrameRes = (renderSafeFrame['cr'] - renderSafeFrame['cl'], renderSafeFrame['cb'] - renderSafeFrame['ct']) # retrieve active RenderData rData = doc.GetActiveRenderData() if not rData: return # get render data rDataBC = rData.GetDataInstance() if not rDataBC: return # store frame resolution frameRes = (rDataBC.GetInt32(c4d.RDATA_XRES), rDataBC.GetInt32(c4d.RDATA_YRES)) # check there's a valid active object if not op or not op.IsInstanceOf(c4d.Opolygon) : return # get the points or return instance it's not valid points = op.GetAllPoints() if not points: return # loop through points for point in points: # transform point from World to Screen pointToScreen = renderBD.WS(point) # notify about points outside the render frame if pointToScreen.x > renderSafeFrame['cr'] or pointToScreen.x < renderSafeFrame['cl']: print " -- x-coordinate out of frame area --- " if pointToScreen.y > renderSafeFrame['cb'] or pointToScreen.y < renderSafeFrame['ct']: print " -- y-coordinate out of frame area --- " # compensate the x/y offset pointsR_XY = (pointToScreen.x - renderSafeFrame['cl'], pointToScreen.y -renderSafeFrame['ct']) # scale with regard of the final frame size pointsR_XY_2 = (pointsR_XY[0] * frameRes[0] / renderSafeFrameRes[0], pointsR_XY[1] * frameRes[1] / renderSafeFrameRes[1]) # just print print "point: [", point ,"] / [", pointsR_XY_2, "]"
Let me know if something is unclear or wrong.
Cheers, Riccardo -
On 03/08/2018 at 07:33, xxxxxxxx wrote:
WOW! You did it, Riccardo! Thank you very much I'm very impressed and grateful.
-
On 03/08/2018 at 07:57, xxxxxxxx wrote:
My apologies, I do have one follow-up question. I see the line with # notify about points outside the render frame
How could I tell if the point is being obscured by other geometry? Would this be
renderBD.TestPointZ(point)
?
When I use this with the plane highlighted in green, it returns False for two of the points which are visible.
point: [ Vector(0, 200, -200) ] / [ (576.2718396467545, 166.23981331428323) ] Visible: False point: [ Vector(0, -200, -200) ] / [ (589.7969294792699, 878.0441715337967) ] Visible: False point: [ Vector(0, 200, 200) ] / [ (1066.6762020149142, 205.85938605460527) ] Visible: True point: [ Vector(0, -200, 200) ] / [ (1063.7210058999553, 771.3600524945704) ] Visible: True
-
On 06/08/2018 at 00:23, xxxxxxxx wrote:
Hi Blastframe, thanks for following up.
With regard to your last question, consider that the point being passed to the BaseView::TestPointZ() needs to be expressed in camera space and not in world space. Then I warmly suggest to use BaseView::WC() before passing your point to the TestPointZ function.
Best, Riccardo
-
On 06/08/2018 at 12:09, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Hi Blastframe, thanks for following up.
With regard to your last question, consider that the point being passed to the BaseView::TestPointZ()needs to be expressed in camera space and not in world space. Then I warmly suggest to use BaseView::WC() before passing your point to the TestPointZ function.
Best, Riccardo
Hi Riccardo,
Thank you for all of your help with my issue. Per your advice, I tried converting to camera space, but the results for all points were still true, even if they were blocked by self geometry or another object.pointToCS = renderBD.WC(point) print renderBD.TestPointZ(pointToCS)
Maybe this is not what TestPointZ is checking? I am wanting to skip vertices that are not visible. Thank you.
-
On 07/08/2018 at 03:05, xxxxxxxx wrote:
Hi Blastframe, thanks for following up.
With regard to your last post, I need to apologize cause I provided a misleading information being myself confused by the documentation. Actually the BaseView::TestPointZ() verify if a given point is within the camera near/far clipping range and not if the point is occluded by any other object.
In your case instead, you should think about a more complex design where each of your point is tested against any other geometry found in the scene by using a
GeRayCollider()
[URL-REMOVED] and then evaluate the result according to the intersection distance.Unfortunately there's no other turn-key solution to achieve your desired functionality.
Best, Riccardo
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.