Create a Polygon Matrix? [SOLVED]



  • On 30/08/2014 at 11:49, xxxxxxxx wrote:

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

    ---------
    Hi,

    I'm trying to build a matrix from a polygon. And these are the requirements:
    - The Y axis needs to point out outwards (away from the polygon)
    - The X&Z axis need to be squared with the polygon
    - This has to work if the object the polygon is on is not at world center 0,0,0
    - This has to work if the object the polygon is on is rotated
    - This has to work if the object the polygon is on is either a parent or a child object

    As an example. C4D already does this when we go into polygon mode and select a polygon.
    But it's Z Axis is pointing outwards instead of the Y axis.
    I need to know how they wrote that code so that I can write one that points in the Y direction.

    I am 99% there. But I just can't get the axis to meet all of the above conditions.
    I've manged to get the Y axis to point outwards. But the other two axis I can't figure out how to make squared to the polygon. They keep fighting me.
    And it doesn't work if the object is rotated.

    My code:

    Bool SimplePlugin::Execute(BaseDocument *doc)  
    {  
      PolygonObject *obj = (PolygonObject* ) doc->GetActiveObject();  
      if(!obj)return FALSE;  
      Vector *points = obj->GetPointW();  
      Matrix objMtx = obj->GetMg();  
      
      //The first polygon in the object  
      CPolygon poly = obj->GetPolygonW()[0];  
      
      //The global positions of each point in the polygon  
      Vector a = obj->GetPointW()[poly.a] * objMtx;  
      Vector b = obj->GetPointW()[poly.b] * objMtx;  
      Vector c = obj->GetPointW()[poly.c] * objMtx;  
      Vector d = obj->GetPointW()[poly.d] * objMtx;  
      
      Vector midpoint;  
      if(c == d) midpoint = (a+b+c) / 3;     //The global position to the "midpoint" variable  
      else  midpoint = (a+b+c+d) / 4;        //The global position to the "midpoint" variable      
      
      //The normals direction the polygon is facing  
      Vector faceNormal = CalcFaceNormal(points, poly);  
      
      //Create a matrix for the polygon  
      Matrix polyMtx;  
      polyMtx.off = midpoint * objMtx;  
      polyMtx.v1 = !((a - midpoint)%faceNormal);  
      polyMtx.v2 = !(faceNormal);                   //<---These matrix values are wrong!!  
      polyMtx.v3 = !(polyMtx.v1 % faceNormal);      //How do I make the three Axis squared(alined) with the polygon?  
                                                    //With the Y axis pointing outwards from the polygon?  
      BaseObject *null = BaseObject::Alloc(Onull);  
      null->SetMg(polyMtx);  
      doc->InsertObject(null, NULL, NULL);  
      null->Message(MSG_UPDATE);  
        
      EventAdd();  
      return TRUE;  
    }
    

    Can anyone tell me how to tame these matrix axis handles please?

    -ScottA



  • On 30/08/2014 at 14:01, xxxxxxxx wrote:

    You've already put Vectors a,b,c,d into object space so the midpoint will be in object space.  Why are you multiplying it by the object matrix again?

    Here is code from my Greebler plugin that puts a polygon at the WCS origin with the polygon normal pointing in the +Y WCS axis.  Not exactly what you are trying to do but it may provide a means to get there.

    Vector vup = Vector(0.0,1.0,0.0);
    Vector vdn = Vector(0.0,-1.0,0.0);
    Vector vpx = Vector(1.0,0.0,0.0);
    Vector vnx = Vector(-1.0,0.0,0.0);
    // GMPSupport.PrepareQuadrangle
    // - Make polygon centered at world with normal along +Y axis
    //*---------------------------------------------------------------------------*
    void GMPSupport::PrepareQuadrangle(GSettings* settings)
    //*---------------------------------------------------------------------------*
    {
    	// tv0-tv3 are the polygons vertices
    	Vector tv0 = settings->tv0;
    	Vector tv1 = settings->tv1;
    	Vector tv2 = settings->tv2;
    	Vector tv3 = settings->tv3;
    	// Polygon Area
    	settings->area = Sqrt(QuadrangleArea(tv3, tv2, tv1, tv0));
    	// Center of polygon
    	Vector	center =	(tv0+tv1+tv2+tv3) * 0.25;
    	// Move polygon to world 0,0,0
    	tv0 -= center;
    	tv1 -= center;
    	tv2 -= center;
    	tv3 -= center;
    	settings->pos = center;
    	// Rotate polygon so that normal made coincident with +Y axis and polygon system is horizontal on X-Z plane
    	Vector n = !((tv1-tv3)%(tv2-tv0));
    	Vector u;
    	if (VectorEqual(n, vup)) u =	!(vpx%n);
    	else if (VectorEqual(n, vdn)) u = !(vnx%n);
    	else u =	!(vup%n);
    	Vector v = !(u%n);
    	Matrix m;
    	m.v1 = Vector(u.x, n.x, v.x);
    	m.v2 = Vector(u.y, n.y, v.y);
    	m.v3 = Vector(u.z, n.z, v.z);
    	// - Invert matrix for putting greebles and such on polygon surface
    	settings->tmat = !m;
    	settings->tv0 = tv0 * m;
    	settings->tv1 = tv1 * m;
    	settings->tv2 = tv2 * m;
    	settings->tv3 = tv3 * m;
    }
    


  • On 30/08/2014 at 15:33, xxxxxxxx wrote:

    Thanks Robert.

    My code is chopped down version of a bigger project. Where I'm painting cloned objects on the surface of other objects with the LMB. And those inserted objects are children of another object. So I needed to transform them into the parent's space.
    That why the multiplication is there.

    In my larger project. Rather than using the polygon midpoint. I'm using a ray collider to get the mouse cursor position on the polygon. Then setting the inserted object's m.v1 axis to: pa - mousePos;
    This results in the X being pointed where the mouse cursor is. And causes the object to be slightly rotated along X when it's created. That's what I can't figure out how to fix.
    So I'm starting over from scratch on a simpler example to try and learn how to make a polygon matrix pointing in +Y that also has the other two axis squared with the polygon.
    I think I can solve any of the m.off problems that might come up.
    But setting up the axis directions is proving to be painfully difficult. Because as the polygon rotates the normal vector changes.

    This is what's confusing me the most:
    If a polygon's normal is facing in +X: One version of code is needed to point the axis how I want
    If a polygon's normal is facing in -X:  Another version of code is needed to point the axis
    If a polygon's normal is facing in +Y: Another version of code is needed to point the axis
    If a polygon's normal is facing in -Y:  Another version of code is needed to point the axis
    If a polygon's normal is facing in +Z: Another version of code is needed to point the axis
    If a polygon's normal is facing in -Z:  Another version of code is needed to point the axis

    It's simple the make an axis point in a direction if that direction is constant.
    But when the direction is not constant because the polygon gets rotated. I'm lost how to deal with that.
    And even if I do get Y always pointing outwards properly. I have no idea how to square the other two axis according to the polygon's points?

    -ScottA



  • On 30/08/2014 at 17:16, xxxxxxxx wrote:

    Well, in the code that I presented, you see that I check the polygon's normal vector against some base axial system vectors (vup is +Y axis and so on).  This allows for the math to be within a reasonable angle for vector maths to work (see the conditionals using VectorEqual()).  The result is used in a cross-product to get the resulting normal between the vectors u and n to point in the +Y axis.  You then don't have to parse out each axial direction (+X, -X, +Y,-Y, etc) as you are thinking.  The vector math resolves this for you.  My Greebler plugin randomly places geometry over a polygon of a polygonal object without fail.  The math works.

    Get the midpoint for the transformation but also transform the collision point so that the polygon's vertices, midpoint, and collision point are all in the same coordinate system.  As you can see in the matrix construction in my code, all three axial bases are made orthonormal so that the origin is (0,0,0), +Y aligns to the normal of the polygon and X/Z axes are mutually perpendicular to that normal.



  • On 30/08/2014 at 18:41, xxxxxxxx wrote:

    Ok thanks. Will try but it's confusing stuff.
    I don't really understand what your remainders are doing. 😵
    They're scary.

        //Determine if n == +Y  
      if(VectorEqual(n, vup)) u = !(vpx % n);      //If n is equal to +Y. Make vector u ?  
      else if(VectorEqual(n, vdn)) u = !(vnx % n); //Otherwise. Make vector u ?
    

    -ScottA



  • On 31/08/2014 at 06:19, xxxxxxxx wrote:

    if n (the polygon normal) is pointing up (+Y), then get the normal of +X and +Y (+Z)
    else if n is pointing down (-Y), then get the normal of -X and -Y (-Z iirc)
    otherwise get the normal of +Y and n

    The first two conditionals could probably be hardcoded but the math accounts for any difference in equality (as there is some play in that code with the epsilon value).  They avoid a possible gimbal lock if n is very close to +Y/-Y wherein the new normal would be indeterminate if simply using u = !(vup%n).



  • On 31/08/2014 at 08:01, xxxxxxxx wrote:

    Ok thanks.
    The math is bit frightening to me.

    I tried your solution. But it always points the Y axis in the Z world direction.
    That's not what I wanted. But it might be useful in the future.
    What I'm trying to achieve is to have the Y axis pointing along the polygon's normal. And the other two axis to be squared with the polygon's shape. Even if the polygon is rotated.
    Like if you have an arched doorway made up of polygons that are slightly rotated to form an arch.
    I need to know how to make the X&Z axis squared with the currently selected polygon so that when I use the new matrix to place my little decoration mesh on them. They follow the same arched pattern as the polygons.

    I can get the Y axis part working.
    I can handle the positioning stuff.
    I can handle the ray casting and insertion of the meshes onto the surface of the polygons.
    But I can't figure out how to square the darned X&Z axis with the polygon.

    -ScottA



  • On 22/09/2014 at 23:16, xxxxxxxx wrote:

    Hi Scott,

    Here is some code I was working on a few weeks ago.

    # place a null on the midpoint of the first edge of every polygon in op.
    # the x axis of the null will be parallel to the first edge of the polygon.
    # the y axis will be perpendicular to the first two edges of the polygon.
      
      
    import c4d
      
      
    def main() :
        doc = c4d.documents.GetActiveDocument()
        op = doc.GetActiveObject()
      
        if not op:
            print "no object selected."
            return
        if op.GetType() != c4d.Opolygon:
            print "selected object is not Opolygon"
            return
      
        if op.GetNgonCount() > 0:
            print "object contains ngons"
            return
      
        vadr = op.GetAllPolygons()
        padr = op.GetAllPoints()
      
        doc.StartUndo()
        for i in xrange(op.GetPolygonCount()) :
            # get vectors for 2 sides of polygon.
            # make sure vectors are end to end
            vec_side_0 = padr[vadr[i].b] - padr[vadr[i].a]
            vec_side_1 = padr[vadr[i].b] - padr[vadr[i].c]
            # note: pick different consecutive edges to rotate the null
            # around the y axis
            # cross product arguments must be normalized.
            # build the x axis.
            v1 = vec_side_0.GetNormalized()
            # the x axis of the coordinate system wil now be
            # parallel to side_0 of the polygon.
            # now build the y normal (side_0 normalized x side_1 normalized)
            # using the cross product.
            v2 = vec_side_1.GetNormalized().Cross(v1).GetNormalized()
            # v2 is now perpendicular to side_0 and side_1 ( the normal if poly is planar ).
            # now we have our x any y axis.
            # the cross product of x and y will produce a vector
            # that is perpendicular to both.
            # build the z axis
            v3 = v1.Cross(v2)
            # place center of coordinate system on side_0,
            # centering along the length of the edge.
            off = ((padr[vadr[i].b] - padr[vadr[i].a]) / 2) + padr[vadr[i].a]
            m = c4d.Matrix(off, v1, v2, v3) # note: this is local to op
            null = c4d.BaseObject(c4d.Onull)
            bc = null.GetDataInstance()
            bc.SetInt32(c4d.NULLOBJECT_DISPLAY, c4d.NULLOBJECT_DISPLAY_PYRAMID)
            bc.SetInt32(c4d.NULLOBJECT_ORIENTATION, c4d.NULLOBJECT_ORIENTATION_XZ)
            bc.SetFloat(c4d.NULLOBJECT_RADIUS, 5)
            null.SetMg(m)
            doc.InsertObject(null)
            doc.AddUndo(c4d.UNDOTYPE_NEW, null)
      
        doc.EndUndo()
        c4d.EventAdd()
      
    if __name__ == "__main__":
        main()
    

    This script works fine on geometry that was generated by primitives, but if the geometry was modeled by hand, the order of the edges relative to surrounding polygons probably will not be the same.

    You might still need to determine the first edge to get the proper rotation about the y axis.

    If you are working with strip of polygons(that is what I was working on), this can be done with the neighbor class.

    If you know what direction your decorations need to be oriented, you could find the highest edge and go from there(Like painting on a sphere).

    I have no intelligent suggestions for painting on a cube... Maybe paint in perspective, put the geometry in camera space and then work out the most x, y or z edge?

    Joe Buck



  • On 23/09/2014 at 08:06, xxxxxxxx wrote:

    Thanks Joe.
    I'll take a look at it and see if it helps me out.

    -ScottA


Log in to reply