Search code examples
c#algorithmxnageometrybezier

Automatic interior edges in bezier surfaces


Normally, Bezier surfaces are used as bicubic patches which have 16 control points. However, in 3dsMax, interior edges can be hidden from editing and calculated automatically (it's their default state). That leaves only 12 control points, which makes editing simpler.

Code from Primitives3D sample project for XNA (simplified):

void CreatePatchVertices(Vector3[] patch, int tessellation)
{
    Debug.Assert(patch.Length == 16);

    for (int i = 0; i <= tessellation; i++)
    {
        float ti = (float)i / tessellation;

        for (int j = 0; j <= tessellation; j++)
        {
            float tj = (float)j / tessellation;

            // Perform four horizontal bezier interpolations
            // between the control points of this patch.
            Vector3 p1 = Bezier(patch[0], patch[1], patch[2], patch[3], ti);
            Vector3 p2 = Bezier(patch[4], patch[5], patch[6], patch[7], ti);
            Vector3 p3 = Bezier(patch[8], patch[9], patch[10], patch[11], ti);
            Vector3 p4 = Bezier(patch[12], patch[13], patch[14], patch[15], ti);

            // Perform a vertical interpolation between the results of the
            // previous horizontal interpolations, to compute the position.
            Vector3 position = Bezier(p1, p2, p3, p4, tj);

            // Perform another four bezier interpolations between the control
            // points, but this time vertically rather than horizontally.
            Vector3 q1 = Bezier(patch[0], patch[4], patch[8], patch[12], tj);
            Vector3 q2 = Bezier(patch[1], patch[5], patch[9], patch[13], tj);
            Vector3 q3 = Bezier(patch[2], patch[6], patch[10], patch[14], tj);
            Vector3 q4 = Bezier(patch[3], patch[7], patch[11], patch[15], tj);

            // Compute vertical and horizontal tangent vectors.
            Vector3 tangentA = BezierTangent(p1, p2, p3, p4, tj);
            Vector3 tangentB = BezierTangent(q1, q2, q3, q4, ti);

            // Cross the two tangent vectors to compute the normal.
            Vector3 normal = Vector3.Cross(tangentA, tangentB);
            normal.Normalize();

            // Create the vertex.
            AddVertex(position, normal);
        }
    }
}

In this sample, how to calculate vectors 5, 6, 9 and 10 (patch[5] etc.) automatically, like in 3dsMax?


Solution

  • Looks like 3ds Max constructs a parallelogram to get interior points.

    patch[5] = patch[0] + (patch[1] - patch[0]) + (patch[4] - patch[0]);
    patch[5] = patch[1] + patch[4] - patch[0];
    

    And the whole function:

    void CreatePatchVertices(Vector3[] s, int tessellation, bool isMirrored)
    {
        Debug.Assert(s.Length == 16);
    
        for (int i = 0; i <= tessellation; i++)
        {
            float ti = (float)i / tessellation;
    
            for (int j = 0; j <= tessellation; j++)
            {
                float tj = (float)j / tessellation;
    
                // Compute automatic interior edges.
                s[5] = s[1] + s[4] - s[0];
                s[6] = s[2] + s[7] - s[3];
                s[9] = s[8] + s[13] - s[12];
                s[10] = s[11] + s[14] - s[15];
    
                // Perform four horizontal bezier interpolations
                // between the control points of this patch.
                Vector3 p1 = Bezier(s[0], s[1], s[2], s[3], ti);
                Vector3 p2 = Bezier(s[4], s[5], s[6], s[7], ti);
                Vector3 p3 = Bezier(s[8], s[9], s[10], s[11], ti);
                Vector3 p4 = Bezier(s[12], s[13], s[14], s[15], ti);
    
                // Perform a vertical interpolation between the results of the
                // previous horizontal interpolations, to compute the position.
                Vector3 position = Bezier(p1, p2, p3, p4, tj);
    
                // Perform another four bezier interpolations between the control
                // points, but this time vertically rather than horizontally.
                Vector3 q1 = Bezier(s[0], s[4], s[8], s[12], tj);
                Vector3 q2 = Bezier(s[1], s[5], s[9], s[13], tj);
                Vector3 q3 = Bezier(s[2], s[6], s[10], s[14], tj);
                Vector3 q4 = Bezier(s[3], s[7], s[11], s[15], tj);
    
                // Compute vertical and horizontal tangent vectors.
                Vector3 tangentA = BezierTangent(p1, p2, p3, p4, tj);
                Vector3 tangentB = BezierTangent(q1, q2, q3, q4, ti);
    
                // Cross the two tangent vectors to compute the normal.
                Vector3 normal = Vector3.Cross(tangentA, tangentB);
                normal.Normalize();
    
                // Create the vertex.
                AddVertex(position, normal);
            }
        }
    }
    

    This is what a teapot primitive with automatic interior points looks like both in 3ds Max and Primitives3D sample:

    Primitives3D screenshot