Convert 3D space to 2D Space
-
On 04/07/2018 at 03:36, xxxxxxxx wrote:
Hey Everyone,
Just wanted to know if anyone can point me in the right direction.
Im looking to convert a 3D (world) space, point co-ordinate into a 2D camera space co-ordinate.
Is this possible with the python SDK?
Any info or a nudge in the right direction is much appreciated!
Cheers,
Cerac
-
On 04/07/2018 at 12:22, xxxxxxxx wrote:
Check out the BaseView class
BaseView::WC() or BaseView::WC_V() is what you most likely are looking for. -
On 05/07/2018 at 02:12, xxxxxxxx wrote:
Hi Ceracticus,
As Daniel already pointed you BaseView.WC and BaseView.WC_V is the way to go.
Additionally, I would like to point you to the C++ manual which can give you some valuable information even for python.
Cheers,
Maxime -
On 05/07/2018 at 09:53, xxxxxxxx wrote:
Hey Both,
thanks so much for your suggestions, massively helpful.
Managed to get 90% of the way with looking up other examples on this site, plus your feedback which is really great.
What im essentially doing is rebuilding the interactive render region crop function out of guides (converted from world to screen space) then plugging these values into the active render setting.
Ive got everything working as expected - apart from when a camera has film offset. I know i can access this data which is great.
[c4d.CAMERAOBJECT_FILM_OFFSET_Y]```
[c4d.CAMERAOBJECT_FILM_OFFSET_X]`Does anyone know what the best way of taking this into account whilst running my code?
def main() : rd = doc.GetActiveRenderData() rd_w = rd[c4d.RDATA_XRES_VIRTUAL] rd_h = rd[c4d.RDATA_YRES_VIRTUAL] bd = doc.GetActiveBaseDraw() # Get current view sf = bd.GetSafeFrame() #SafeFrame obj = op.GetObject() # Get Null cam = obj[c4d.ID_USERDATA,2] # Get Camera top = obj[c4d.ID_USERDATA,3] # GetGuideObjects bottom = obj[c4d.ID_USERDATA,7] left = obj[c4d.ID_USERDATA,8] right = obj[c4d.ID_USERDATA,9] switch = obj[c4d.ID_USERDATA,4] # Get Switch cZ = cam[c4d.CAMERAOBJECT_TARGETDISTANCE] #z-distance if switch: # +-------------------------- TOP IRR topPos = top.GetMg().off #top global position top_v = bd.WS(topPos) #World2Screen top_v[0] = (top_v[0] - sf["cl"])/(sf["cr"]-sf["cl"]) top_v[1] = (top_v[1] - sf["ct"])/(sf["cb"]-sf["ct"]) top_v[2] = (cZ-top_v[2])/cZ top_pos = top_v[1] #Gets Y pos from vector # Rangemap the result to IRR space irrTop_min = c4d.utils.RangeMap(top_pos, 0, 1, 0, rd_h, True) irrTop_max = 0 # If its over halfway - reverse it if irrTop_min >= (rd_h/2) : irrTop_max = c4d.utils.RangeMap(irrTop_min, rd_h, rd_h/2, 0, rd_h/2,True) rd[c4d.RDATA_RENDERREGION_TOP] = int(irrTop_max) else: pass
Thanks again!
Cerac
-
On 09/07/2018 at 07:09, xxxxxxxx wrote:
Hi Ceracticus,
Actually, WS already take care of the film offset, so you just have to do something like that.
rd = doc.GetActiveRenderData() rd_w = rd[c4d.RDATA_XRES_VIRTUAL] rd_h = rd[c4d.RDATA_YRES_VIRTUAL] bd = doc.GetActiveBaseDraw() viewX = bd.GetFrame()["cr"] viewY = bd.GetFrame()["cb"] sf = bd.GetSafeFrame() #SafeFrame cam = bd.GetSceneCamera(doc) top = doc.GetFirstObject().GetMg().off bot = doc.GetFirstObject().GetNext().GetMg().off left = doc.GetFirstObject().GetNext().GetNext().GetMg().off right = doc.GetFirstObject().GetNext().GetNext().GetNext().GetMg().off # Get pixels counts between top obj and the frame, then scale this pixel count according to the ratio between viewport size and render size. top_p = max(0, bd.WS(top).y - sf["ct"]) * rd_h / viewY bot_p = max(0, sf["cb"] - bd.WS(bot).y) * rd_h / viewY left_p = max(0, bd.WS(left).x - sf["cl"]) * rd_w / viewX right_p = max(0, sf["cr"] - bd.WS(right).x) * rd_w / viewX rd[c4d.RDATA_RENDERREGION_TOP] = int(top_p) rd[c4d.RDATA_RENDERREGION_BOTTOM] = int(bot_p) rd[c4d.RDATA_RENDERREGION_LEFT] = int(left_p) rd[c4d.RDATA_RENDERREGION_RIGHT] = int(right_p) c4d.EventAdd()
On a side topic, please use [ code] [ /code] to insert code in your post, it increases the readability of them.
If you have any question, please let me know.
Cheers,
Maxime -
On 11/07/2018 at 01:05, xxxxxxxx wrote:
Thanks Maxime,
i got there in the end, my solution was far less elegant than yours though, so thank you for spending the time to look through my code, i can certainly learn lots from your approach.
Apologies also for my scrappy code - there was parts in there that made no sense whatsoever so kudos for managing to read through that.
I'll post my solution below for reference but anyone who references this thread should definitely go with Maximes above suggestion.
def screenCalc(guide, cZ, bd, sf, rd_w, rd_h) : gPos = guide.GetMg().off # get global position of guide convertPos = bd.WS(gPos) # convert world to screen space # Perform safe zone maths convertPos[0] = (convertPos[0] - sf["cl"])/(sf["cr"]-sf["cl"]) convertPos[1] = (convertPos[1] - sf["ct"])/(sf["cb"]-sf["ct"]) convertPos[2] = (cZ - convertPos[2])/cZ gV1 = convertPos[0] # get x from vector gV2 = convertPos[1] # get y from vector # Remaps vector values to screenspace values, aswell as inverts g_Xpos = c4d.utils.RangeMap(gV1, 0, 1, 0, rd_w, True) INVg_Xpos = c4d.utils.RangeMap(gV1, 1, 0, 0, rd_w, True) g_Ypos = c4d.utils.RangeMap(gV2, 0, 1, 0, rd_h, True) INVg_Ypos = c4d.utils.RangeMap(gV2, 1, 0, 0, rd_h, True) # Loads final values into a list and returns it pos = [int(g_Xpos), int(INVg_Xpos), int(g_Ypos), int(INVg_Ypos)] return pos def main() : # +------- GET DOCUMENT DATA --------# bd = doc.GetActiveBaseDraw() # Get current view rd = doc.GetActiveRenderData() # Get Render Setting rd_w = rd[c4d.RDATA_XRES_VIRTUAL] # Get X resolution rd_h = rd[c4d.RDATA_YRES_VIRTUAL] # Get Y resolution sf = bd.GetSafeFrame() # Get SafeFrame dims # +------- GET CUSTOM DATA --------# obj = op.GetObject() # Get Null cam = obj[c4d.ID_USERDATA,2] # Get Camera top = obj[c4d.ID_USERDATA,3] # GetGuideObjects bottom = obj[c4d.ID_USERDATA,7] # -------------- left = obj[c4d.ID_USERDATA,8] # -------------- right = obj[c4d.ID_USERDATA,9] # -------------- switch = obj[c4d.ID_USERDATA,4] # Get Switch cZ = cam[c4d.CAMERAOBJECT_TARGETDISTANCE] # Get z-distance # If tag is activated if switch: # Use function to grab data try: topPos = screenCalc(top, cZ, bd, sf, rd_w, rd_h) bottomPos = screenCalc(bottom, cZ, bd, sf, rd_w, rd_h) leftPos = screenCalc(left, cZ, bd, sf, rd_w, rd_h) rightPos = screenCalc(right, cZ, bd, sf, rd_w, rd_h) # SET RENDER DATA rd[c4d.RDATA_RENDERREGION_TOP] = topPos[2] rd[c4d.RDATA_RENDERREGION_BOTTOM] = bottomPos[3] rd[c4d.RDATA_RENDERREGION_LEFT] = leftPos[0] rd[c4d.RDATA_RENDERREGION_RIGHT] = rightPos[1] doc.SetActiveRenderData(rd) c4d.EventAdd() except ZeroDivisionError: pass else: pass
Thanks again,
Cerac