Draw circle at cursor on object surface?



  • On 15/06/2013 at 02:07, xxxxxxxx wrote:

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

    ---------
    What is needed is some way to 'project' (in Draw() for a Tool plugin) a circle of a given radius from the screen onto the surface of an object (that results in a polygonal mesh) using the cursor as center.  The circle needs to be oriented on the plane perpendicular to the surface normal at that point.  This is probably very much like UV mapping but with the limitations of BaseDraw, it may require converting UV coordinates to pixels somehow (?).  I will do some research but some direction or code would be helpful in the meantime.

    One possibility might be to shoot rays in the direction of the normal towards the surface in a sparse circular pattern and do line seqments from point to point over the intersections that are visible to the camera.  A bit of work but the results should be perfect.



  • On 15/06/2013 at 02:39, xxxxxxxx wrote:

    I would go for the utils.ViewportSelect plus BaseView.ProjectPointOnLine() and ProjectPointOnPlane().

    Happy rendering,
    Ferdinand

    edit : ok that was maybe a bit too short - what i meant was, construct your circle in camera space
    around your desired center and then project its points on the surface(s). with a z-depth high 
    enough you won't have to worry so much about a matching subdiv, as you will simply draw over
    the geometry.



  • On 15/06/2013 at 02:51, xxxxxxxx wrote:

    That would work if the circle were being directly projected from the screen (camera).  But it going to be projected to the plane perpendicular to the surface normal under the cursor.

    I've also determined that line segments (LineStrip()) may not work if there are discontinuities (surface not seen by camera or the circle point misses the mesh entirely).



  • On 15/06/2013 at 03:12, xxxxxxxx wrote:

    Ah ok,

    converting the coords for ViewportSelect forth and back would be indeed a bit clunky.
    An alternative root would be the new snapping core class. i guess it is easier to use 
    than the GeRayCollider were you have usually to worry about the hit sides and so on,
    but i am not sure about the snapping core performance.

    happy rendering,
    ferdinand



  • On 15/06/2013 at 06:47, xxxxxxxx wrote:

    So far so good except for one thing.  Transforming the calculated circle points from a local Z-plane ( Vector(radius+Cos(angle), radius*Sin(angle), 0.0)) to the local plane representing the negative surface normal (translated away from the surface for ray collision) is resulting in elliptical circles.  This is either a skewing in the matrix or the rotation (axis-angle) isn't correct.  Uncertain which is the cause.  All vectors are in local space for the object.

    Here is my code (and some alternatives which show the same issue) :

    Vector    ray_dir =            !(res.f_normal);  
      
    // - Move the ray system far away from the surface, orient it and put it into object space  
    // Rodrigues method  
    Vector    pnormal =            Vector(0.0,0.0,1.0);  
    // - invert normal to point at surface not away from it  
    ray_dir =                    -ray_dir;  
    Vector axis =                !(pnormal%ray_dir);  
    Matrix    pmat;  
    pmat.v1 =    Vector(0.0, -axis.z, axis.y);  
    pmat.v2 =    Vector(axis.z, 0.0, -axis.x);  
    pmat.v3 =    Vector(-axis.y, axis.x, 0.0);  
    Real    angle =                ACos(pnormal*ray_dir);  
    pmat =        unitMatrix + Sin(angle)*pmat + (1.0-Cos(angle))*pmat*pmat;  
    // Quaternion method  
    /*   
    Quaternion    q;  
    q.SetAxis(!(pnormal%ray_dir), ACos(pnormal*ray_dir));  
    Matrix    pmat =                q.GetMatrix();  
    */  
    // Axis-Angle method  
    //Matrix    pmat =                RotAxisToMatrix(!(pnormal%ray_dir), ACos(pnormal*ray_dir));  
    //pmat.Normalize();  
    pmat.off =                    res.hitpos+(ray_dir*-50000.0);  
    Real    radius =            (Real)data.GetLong(TOOLHDRLS_RADIUS);  
    Real    dangle =            0.1745329251994329;  
    Vector    fp;  
    Bool    drawfp =            FALSE;  
    bd->LineStripBegin();  
    // - First point  
    pnormal =                    pmat * Vector(radius*Cos(0.0), radius*Sin(0.0), 0.0);  
    if (m_pDrawCollider->Intersect(pnormal, ray_dir, 100000.0) && m_pDrawCollider->GetNearestIntersection(&res))  
    {  
      bd->LineStrip(res.hitpos, hicolor, NOCLIP_D|NOCLIP_Z);  
      fp =        res.hitpos;  
      drawfp =    TRUE;  
    }  
    for (LONG i = 1L; i != 36L; ++i, dangle += 0.1745329251994329)  
    {  
      pnormal =        pmat * Vector(radius*Cos(dangle), radius*Sin(dangle), 0.0);  
      if (m_pDrawCollider->Intersect(pnormal, ray_dir, 100000.0) && m_pDrawCollider->GetNearestIntersection(&res))  
          bd->LineStrip(res.hitpos, hicolor, NOCLIP_D|NOCLIP_Z);  
    }  
    // - Back to first point  
    if (drawfp)                    bd->LineStrip(fp, hicolor, NOCLIP_D|NOCLIP_Z);  
    bd->LineStripEnd();  
    

Log in to reply