Editor Translate/Scale/Rotate tools



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 30/08/2006 at 15:00, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   9.102 
    Platform:   Windows  ;   Mac OSX  ; 
    Language(s) :     C++  ;

    ---------
    Okay, I'm going to ask a lot here. My tool plugin now handles selection very well. It also has Translate/Scale/Rotate operations for the editor window on the selected object. These are not affecting standard matrices, but sliders - so the standard tools cannot be used (as there is no way to determine if the user has just used these tools - if there is, that information would save me all of this trouble - note that R8.2 sends no messages).

    So, does anyone have tool code that simulates Cinema's TSR tools - including the graphical display controls and their highlighting?

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 12:55, xxxxxxxx wrote:

    No one?

    Could someone (eh hem) at least tell me how to draw 3D 'objects' (ala BaseDraw::Box3D, Circle3D) so that they are always the same size in the viewport? 2D stuff is easy. 3D is not so intuitive to scale the m.v1/v2/v3 so that the draw is always the same size.

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 13:28, xxxxxxxx wrote:

    Maybe build your matrix starting with an identity, then just apply the (inverse) camera rotation and translation values.
    I hadn't specifically looked at how the viewport rendering code works, so if C4D doesn't then apply the current camera/view matrix, you should be able to just multiply your vertices by the above and render...
    If it's going to use the camera/view matrix, then you'll need a different approach for your matrix  - forget the camera/view translation and rotation values... determine the scaling of the camera/view matrix and multiply your vertices (or the matrix you're going to multiply the vertices by) by the inverse.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 13:50, xxxxxxxx wrote:

    I'm using the 'object' matrix to position the 3D Circles - that works. The problem is that BaseDraw::Circle3D() only uses a matrix. How do you set the scaling factor so that the screen display width/height are always the same no matter the camera angle/zoom/position?

    For Spherify.cpp, they use this:

    m.v1*=rad;
    m.v2*=rad;
    m.v3*=rad;

    But 'rad' is a 3D size that varies with the camera (e.g.: as you dolly in, the circles get larger). I need a multiplier that varies with the camera to get the same pixel (screen) dimensions (e.g.: as you dolly in, the circles stay the same size on the display). This is the same behavior as the standard C4D Rotate tool.

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 14:04, xxxxxxxx wrote:

    ...after briefly looking at this... it looks like you're going to need to find out the current camera scaling information (the second case I described above).  There might be something useful in the BaseView SDK section.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 14:06, xxxxxxxx wrote:

    Okay, contemplating the 'dolly in' factor, it appears that the orthogonal Z distance from screen to point should be a determining factor in the scaling. So, I decided to try this:

    Real     rad2 =     rad * 0.0005;  
    Vector     ctr =     bd->WS(m.off);  
    if (ctr.z < 0.0)     return;  
    m.v1 *=               ctr.z*rad2;  
    m.v2 *=               ctr.z*rad2;  
    m.v3 *=               ctr.z*rad2;
    

    When ctr is calculated from World m.off to Screen, the Z distance is placed in ctr.z. This gives me a Z determiner. The very small multiplier compensates for the large values of both ctr.z (C4D units) and rad2 (pixels).

    This is not working so well with the camera zoom. No idea how to bring that into the mix.

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 14:08, xxxxxxxx wrote:

    Oops.. cross-posted :).
    Yeah, once you determine what the current camera/view scaling vector is, you multiply the matrix you're passing in by the inverse of that to keep your object the same size.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 14:46, xxxxxxxx wrote:

    I don't quite follow you here. BaseView has two methods: GetMg() and GetMi() which return the Camera matrix and inverse matrix, respectively. I've tried multiplying the m.v1 (etc.) by Len(c.v1) where c is either the Camera matrix or inverse. That makes it look proper, but it still scales with the camera. Multiplying m by the camera matrix draws a right mess. ;)

    Now, there is GetViewParameter(), but I already get the view pixel dimensions from GetFrame(). I use that to
    determine 'rad' (a pixel value) for Circle2D().

    I should also mention that Circle3D() wants a matrix in World space. So it seems that the projection/camera is considered in the call.

    Still lost.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 15:17, xxxxxxxx wrote:

    Ahh.. I'll try explaining better in an e-mail...



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 02/09/2006 at 20:46, xxxxxxxx wrote:

    This old message:

    _despite Begin2d and End2D
    the display never seems to get updated
    ive gone through just about every way I can think of to get the display to update.

    as I said , the circle is drawn to the Basedraw bitmap and can only be seen by minimising and then maximimising cinama.
    so im at a loss here.

    maybe the EditorWindow class is in some way blocking this update?

    maybe this plugintool class is just not allowed to refresh the screen?

    thanks for any help

    Paul "baffled" Everett
    _

    Followed by the superbly explanatory:

    _
    Subject: working now
    From: paul Everett
    Date: 9/14/2001 3:55 PM GMT

    thanks David :)_

    I'm having this problem as well - only orthographic views - and only some of them. It is so heartwarming to see that the solution was given for posterity (in invisible ink requiring lemon juice LISP code). Tell me the solution, please (this is not directed to you, Keith). Gad - it should be MANDATORY that solutions are posted for everyone either directly or on request. If this continues, I'm moving to Maya...

    Robert



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 07/09/2006 at 23:53, xxxxxxxx wrote:

    Perhaps the old post about camera projections will help clear this up? http://www.plugincafe.com/forum_browse.asp?messageID=2799 Unfortunately I don't have the time right now to look into the math part of it.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 08/09/2006 at 00:09, xxxxxxxx wrote:

    As noted in my other thread, I cannot fathom why a 2D draw (based upon the screen pixels of the view) would be in any way affected by projection (ever!). Sometimes I get the 2D circle, most times not - especially not in axonometric views. OpenGL or Software modes don't make a difference.

    I can see how the 3D draws could be affected by the projection considering that they don't have the same 'z-distance' criteria as perspective projection. In that case, I'll have to experiment with the code in the link (and in the documentation).

    Nonetheless, one would assume that 2D drawing would be ubiquitous despite the active view unless there is some mitigating factor involved. I have been on the edge of providing the tool code in full to support to see if the error is obvious. May I do this after full investigation?

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 08/09/2006 at 00:14, xxxxxxxx wrote:

    (Code is always welcome! As usual, make it as short a snippet as possible but not shorter.)



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 08/09/2006 at 10:51, xxxxxxxx wrote:

    Here's the ToolData::Draw() code and one of the Control draw methods, some stuff removed for clarity, but still whopping (hey, my tool plugin is 2000 lines of code and growing).

    // ToolData.Draw  
    //*---------------------------------------------------------------------------*  
    LONG IPPTool::Draw(BaseDocument* doc, BaseContainer& data, BaseDraw* bd, BaseDrawHelp* bh, BaseThread* bt, LONG flags)  
    //*---------------------------------------------------------------------------*  
    {  
         LONG     drawFlags;  
         // Draw Operation Controls  
         if (selectedBP)  
         {  
              if          (operation == IPTOP_ROTATE)          DrawRotateControl(bd, bh);  
              else if (operation == IPTOP_TWIST)          DrawTwistControl(bd, bh);  
              else if (operation == IPTOP_SCALE)          DrawScaleControl(bd, bh);  
              else if (operation == IPTOP_TRANSLATE)     DrawTranslateControl(bd, bh);  
              drawFlags =     DRAW_HANDLES;  
         }  
         else     drawFlags =     DRAW_AXIS|DRAW_HANDLES;  
         // Only draw into active view  
         if (bd != doc->GetActiveBaseDraw())     return drawFlags;  
         // Go no further if in dragMode for operation  
         if (dragMode)                              return drawFlags;  
         // This allows click-deselection  
         hilitedOP =                                   NULL;  
         hilitedBP =                                   NULL;  
         // From here on in, only draw highlighting (if R9.0+)  
    #ifdef C4D_R9  
         if (!(flags & DRAWFLAGS_HIGHLIGHT))     return drawFlags;  
    #endif  
         // Mouse outside of Editor view  
         if (mouseX < 0.0)                         return drawFlags;  
      
         // Add IPP objects under cursor to selection list  
         AutoAlloc<C4DObjectList>               objList;  
         if (!objList)                              return drawFlags;  
         if (!SelectionListCreate(doc, NULL, bd, mouseX, mouseY, NULL, objList))     return drawFlags;  
         LONG               cnt =                    objList->GetCount();  
         if (!cnt)                                   return drawFlags;  
      
         // The rest of the code involves Polygon/BoundingBox highlighting.  
         // This is working in all views.  
         // Though I do note that when the operation draws (above),  
         // the BoundingBox highlighting is no longer in the highlight color.  
      
         ...  
      
         // So, I've included just a section where a bounding box is highlighted  
         else  
         {  
              hilitedOP =               obj;  
              hilitedBP =               ippTag->GetDataInstance()->GetObjectLink(IPPOBJECT_ROOTBONE, doc);  
              if (!hilitedBP)          return drawFlags;  
    #ifdef C4D_R9  
              bd->LineZOffset(32);  
    #endif  
              bd->SetTransparency(100);  
              Matrix          mg =     hilitedBP->GetMg();  
              // Global position  
              mg.off =               mg*hilitedBP->GetMp();  
              // 'arbitrary cubic form to match bounding box  
              Vector          rad =     hilitedBP->GetRad()*2;  
              mg.v1 *=               rad.x;  
              mg.v2 *=               rad.y;  
              mg.v3 *=               rad.z;  
              // color[0] is from the Vector color[4] array of colors used in polygon highlighting  
              bd->Box3D(mg, 0.5, color[0]);  
    #ifdef C4D_R9  
              bd->LineZOffset(0);  
              return drawFlags|DRAW_HIGHLIGHTS;  
    #else  
              return drawFlags;  
    #endif  
         }  
         return drawFlags;  
    }  
      
    // IPPTool.DrawRotateControl  
    //*---------------------------------------------------------------------------*  
    void IPPTool::DrawRotateControl(BaseDraw* bd, BaseDrawHelp* bh)  
    //*---------------------------------------------------------------------------*  
    {  
         Matrix m =          selectedBP->GetMg();  
         Vector     ctr =     bd->WS(m.off);  
         if (ctr.z <= 0.0)     return;  
      
         // BaseView dimensions  
         LONG left, top, right, bottom, width, height;  
         bd->GetFrame(&left;, &top;, &right;, &bottom;);  
         width =               right - left + 1;  
         height =          bottom - top + 1;  
         Real     rad =     (Real)((width > height)?(height>>2) :(width>>2));  
         // rad2 = 0.5 rad * ctr.z * scaling factor 0.0015  
         Real     rad2 =     ctr.z * (rad * 0.00075);  
      
         // Scale for PolygonObject  
         m.v1 =               !m.v1 * rad2;  
         m.v2 =               !m.v2 * rad2;  
         m.v3 =               !m.v3 * rad2;  
      
         // PolygonObject Color Properties  
         ObjectColorProperties     ocp;  
         ocp.usecolor =     ID_BASEOBJECT_USECOLOR_ALWAYS;  
         ocp.xray =          FALSE;  
      
    #ifdef C4D_R9  
         bd->LineZOffset(32);  
    #endif  
         bd->SetTransparency(255);  
         // 'Sphere'  
         bd->SetPen(GetWorldContainer().GetVector(WPREF_SELECT_AXIS_COL));  
         bd->Circle2D(ctr.x, ctr.y, rad);  
         // 'Rings'  
    ...  
         // Z-Axis  
         ocp.color =          GetWorldContainer().GetVector(WPREF_ZAXIS_COL);  
         rotatObj->SetColorProperties(&ocp;);  
         rotatObj->SetMg(m*MatrixRotX(Rad(-90.0)));  
    #ifdef C4D_R9  
         bd->DrawPolygonObject(bh, rotatObj, 0L);  
         bd->LineZOffset(0);  
    #else  
         bd->PolygonObject(bh, rotatObj, 0L);  
    #endif  
    }
    

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 08/09/2006 at 11:10, xxxxxxxx wrote:

    Eh hem. Well, I found the first mistake - that helps the missing Circle2D partially. I though that I had double-checked this, but too many hours of working can play tricks on the mind. How about 'bd->SetTransparency(0)' instead of (255). Still not showing under every circumstance and most of the axonometric/orthographic views.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 08/09/2006 at 12:19, xxxxxxxx wrote:

    Next solution. Removing this for the controls draws them in any view:

    if (ctr.z <= 0.0) return;

    Seems that this is true for certain situations in axonometric/orthographic views. Now it's just a matter of keeping the control sizes the same relative to the screen space.

    Also, I still haven't received any official information on how to factor in camera zoom/focal length with respect to control size.

    Thanks,



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 09/09/2006 at 14:48, xxxxxxxx wrote:

    Eureka!!!!!!!!!! ;D

    For posterity and for the benefit of others to relieve their suffering in purgatory, I present code which ACTUALLY works in all projections, all views, all cameras for displaying Tool controls (similar to Cinema 4D's) using BaseDraw:

    First off, implement these. ScreenToWorld is useful for drawing, but WorldToScreen may prove useful for picking.

    // IPPTool.WorldToScreen (see BaseView.GetViewParameter)  
    //*---------------------------------------------------------------------------*  
    Vector IPPTool::WorldToScreen(const Vector& pp, BaseDraw* bd, LONG projection)  
    //*---------------------------------------------------------------------------*  
    {  
         Vector     p =                    pp*bd->GetMi();  
         Vector     off, scale, scalez;  
         bd->GetViewParameter(&off;, &scale;, &scalez;);  
      
         if (projection == Pperspective)  
         {  
              Real     nz =     (p.z <= 0.0) ? 20.0 : 1.0/(p.z + 0.05);  
              p.x =               p.x*scale.x*nz+off.x;  
              p.y =               p.y*scale.y*nz+off.y;  
              return p;  
         }  
      
         p.x =     (p.x*scale.x)+off.x;  
         p.y =     (p.y*scale.y)+off.y;  
         switch (projection)  
         {  
              case Pmilitary: case Pfrog: case Pbird: case Pgentleman:  
              p.x +=     p.z*scale.x*scalez.x;  
              p.y -=     p.z*scale.y*scalez.y;  
              break;  
         }  
         return p;  
    }  
    // IPPTool.ScreenToWorld (see BaseView.GetViewParameter)  
    //*---------------------------------------------------------------------------*  
    Vector IPPTool::ScreenToWorld(const Vector& pp, BaseDraw* bd, LONG projection)  
    //*---------------------------------------------------------------------------*  
    {  
         Vector p =                    pp;  
      
         Vector     off, scale, scalez;  
         bd->GetViewParameter(&off;, &scale;, &scalez;);  
         switch (projection)  
         {  
              case Pmilitary: case Pfrog: case Pbird: case Pgentleman:  
              p.x -= p.z*scale.x*scalez.x;  
              p.y += p.z*scale.y*scalez.y;  
              break;  
         }  
      
         p.x = (p.x-off.x)/scale.x;  
         p.y = (p.y-off.y)/scale.y;  
      
         if (projection==Pperspective)  
         {  
              Real nz = p.z + 0.05;  
              p.x *= nz;  
              p.y *= nz;  
         }  
         return p*bd->GetMg();  
    }  
    

    Then do your controls like so:

    // IPPTool.DrawRotateControl  
    //*---------------------------------------------------------------------------*  
    void IPPTool::DrawRotateControl(BaseDraw* bd, BaseDrawHelp* bh)  
    //*---------------------------------------------------------------------------*  
    {  
         Matrix     m =          selectedBP->GetMg();  
         Vector     ctr =     bd->WS(m.off);  
      
         LONG left, top, right, bottom, width, height;  
         bd->GetFrame(&left;, &top;, &right;, &bottom;);  
         width =               right - left + 1;  
         height =          bottom - top + 1;  
         Real     rad =     (Real)((width > height)?(height>>2) :(width>>2));  
      
          // Scale for PolygonObject  
         LONG     proj =     bd->GetProjection();  
         Vector     a =          ScreenToWorld(ctr, bd, proj);  
         Vector     b =          ScreenToWorld(Vector(ctr.x+rad*0.5, ctr.y, ctr.z), bd, proj);  
         Real     rad2 =     2.0 * Len(b-a);  
         if (proj == Pfrog)  
         {  
              m.v1 =               !m.v1 * rad2;  
              m.v2 =               !m.v2 * rad2 * 0.333;  
              m.v3 =               !m.v3 * rad2;  
         }  
         else  
         {  
              m.v1 =               !m.v1 * rad2;  
              m.v2 =               !m.v2 * rad2;  
              m.v3 =               !m.v3 * rad2;  
         }  
      
    #ifdef C4D_R9  
         bd->LineZOffset(32);  
    #endif  
         bd->SetTransparency(0);  
         // 'Sphere'  
         bd->SetPen(GetWorldContainer().GetVector(WPREF_SELECT_AXIS_COL));  
         bd->Circle2D(ctr.x, ctr.y, rad);  
         // 'Rings'  
         // X-Axis  
         ocp.color =          GetWorldContainer().GetVector(WPREF_XAXIS_COL);  
         rotatObj->SetColorProperties(&ocp;);  
         rotatObj->SetMg(m*zrotMat);  
    #ifdef C4D_R9  
         bd->DrawPolygonObject(bh, rotatObj, 0L);  
    #else  
         bd->PolygonObject(bh, rotatObj, 0L);  
    #endif  
         // Y-Axis  
         ocp.color =          GetWorldContainer().GetVector(WPREF_YAXIS_COL);  
         rotatObj->SetColorProperties(&ocp;);  
         rotatObj->SetMg(m);  
    #ifdef C4D_R9  
         bd->DrawPolygonObject(bh, rotatObj, 0L);  
    #else  
         bd->PolygonObject(bh, rotatObj, 0L);  
    #endif  
         // Z-Axis  
         ocp.color =          GetWorldContainer().GetVector(WPREF_ZAXIS_COL);  
         rotatObj->SetColorProperties(&ocp;);  
         rotatObj->SetMg(m*xrotMat);  
    #ifdef C4D_R9  
         bd->DrawPolygonObject(bh, rotatObj, 0L);  
         bd->LineZOffset(0);  
    #else  
         bd->PolygonObject(bh, rotatObj, 0L);  
    #endif  
    }  
    

    For non-radial controls (translate/scale), rad2 = Len(b-a). This considers projection, camera, view, focus, zoom - so simple and you wonder why NOONE (especially the developers) can provide this for .... sake!!

    Thank you, thank me...



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 09/09/2006 at 18:04, xxxxxxxx wrote:

    Slight modification to ScreenToWorld() and WorldToScreen() to support pre-R9.0 versions:

    replace:

         bd->GetViewParameter(&off;, &scale;, &scalez;);
    

    with:

    #ifdef C4D_R9  
         bd->GetViewParameter(&off;, &scale;, &scalez;);  
    #else  
         bd->GetParameter(&off;, &scale;, &scalez;);  
    #endif
    

    More information on the polygon objects used for the control interface. I built the controls in C4D and exported as Wavefront so that the extents were 1x1x1 (the radius is 0.5). The polygon objects are cone (translate), cube (scale) and ring (rotate/twist). These were setup as Vector arrays for the points and LONG arrays for the polygons in a header (could be done with either reading/parsing the Wavefront .obj files or reading some other proprietary format). The objects are created/stored in the constructor and verified in the ToolData::Init() so that they must exist whenever the tool is activated.

    zrotMat is a MatrixRotZ(Rad(90)) matrix
    xrotMat is a MatrixRotX(Rad(-90)) matrix

    These two matrices 'orthogonalize' the polygon objects to the particular local axis.


Log in to reply