Пример #1
0
    ///////////////////////////////////////////////////////////////////////////
    #region [Merge}

    /**
     * Add all vertices/edges/faces from another mesh, and fix attributes if
     * needed.
     * Overriding attributes: vertex's id (of the first mesh only)
     */
    public static void Merge(BMesh mesh, BMesh other)
    {
        var newVerts = new Vertex[other.vertices.Count];
        int i        = 0;

        foreach (Vertex v in other.vertices)
        {
            newVerts[i] = mesh.AddVertex(v.point);
            AttributeLerp(mesh, newVerts[i], v, v, 1); // copy all attributes
            v.id = i;
            ++i;
        }
        foreach (Edge e in other.edges)
        {
            mesh.AddEdge(newVerts[e.vert1.id], newVerts[e.vert2.id]);
        }
        foreach (Face f in other.faces)
        {
            var neighbors    = f.NeighborVertices();
            var newNeighbors = new Vertex[neighbors.Count];
            int j            = 0;
            foreach (var v in neighbors)
            {
                newNeighbors[j] = newVerts[v.id];
                ++j;
            }
            mesh.AddFace(newNeighbors);
        }
    }
Пример #2
0
    static bool TestSubdivideQuad()
    {
        var mesh = new BMesh();

        Vertex v0 = mesh.AddVertex(new Vector3(-1, 0, -1));
        Vertex v1 = mesh.AddVertex(new Vector3(-1, 0, 1));
        Vertex v2 = mesh.AddVertex(new Vector3(1, 0, 1));
        Vertex v3 = mesh.AddVertex(new Vector3(1, 0, -1));

        mesh.AddFace(v0, v1, v2, v3);

        BMeshOperators.Subdivide(mesh);

        Debug.Assert(mesh.vertices.Count == 9, "vertex count");
        Debug.Assert(mesh.edges.Count == 12, "edge count");
        Debug.Assert(mesh.loops.Count == 16, "loop count");
        Debug.Assert(mesh.faces.Count == 4, "face count");

        foreach (Face f in mesh.faces)
        {
            Debug.Assert(f.vertcount == 4, "faces are quads");
        }

        Debug.Log("TestBMeshOperators TestSubdivideQuad passed.");
        return(true);
    }
Пример #3
0
    static bool TestAttributeLerp()
    {
        var mesh = new BMesh();

        mesh.AddVertexAttribute(new AttributeDefinition("uv", AttributeBaseType.Float, 2));
        mesh.AddVertexAttribute(new AttributeDefinition("mat", AttributeBaseType.Int, 1));

        Vertex v0 = mesh.AddVertex(new Vector3(0, 0, 0));
        Vertex v1 = mesh.AddVertex(new Vector3(0, 0, 0));
        Vertex v2 = mesh.AddVertex(new Vector3(0, 0, 0));

        v0.attributes["uv"]  = new FloatAttributeValue(0.12f, 0.0f);
        v2.attributes["uv"]  = new FloatAttributeValue(0.33f, 1.0f);
        v0.attributes["mat"] = new IntAttributeValue(0);
        v2.attributes["mat"] = new IntAttributeValue(1);
        BMeshOperators.AttributeLerp(mesh, v1, v0, v2, 0.4f);
        var uv1  = v1.attributes["uv"] as FloatAttributeValue;
        var mat1 = v1.attributes["mat"] as IntAttributeValue;

        Debug.Assert(uv1.data.Length == 2 && uv1.data[0] == Mathf.Lerp(0.12f, 0.33f, 0.4f) && uv1.data[1] == 0.4f, "interpolate uv");
        Debug.Assert(mat1.data.Length == 1 && mat1.data[0] == 0, "interpolate mat");

        Debug.Log("TestBMeshOperators TestAttributeLerp passed.");
        return(true);
    }
Пример #4
0
    static bool Test3()
    {
        var mesh = new BMesh();

        Vertex v0 = mesh.AddVertex(new Vector3(-1, 0, -1));
        Vertex v1 = mesh.AddVertex(new Vector3(-1, 0, 1));
        Vertex v2 = mesh.AddVertex(new Vector3(1, 0, 1));
        Vertex v3 = mesh.AddVertex(new Vector3(1, 0, -1));
        Face   f0 = mesh.AddFace(v0, v1, v2);
        Face   f1 = mesh.AddFace(v2, v1, v3);

        Debug.Assert(mesh.vertices.Count == 4, "vert count");
        Debug.Assert(mesh.loops.Count == 6, "loop count");
        Debug.Assert(mesh.edges.Count == 5, "edge count");
        Debug.Assert(mesh.faces.Count == 2, "face count");

        Debug.Assert(v0.NeighborFaces().Count == 1, "v0 has one neighbor face (found count: " + v0.NeighborFaces().Count + ")");
        Debug.Assert(v1.NeighborFaces().Count == 2, "v1 has two neighbor face (found count: " + v1.NeighborFaces().Count + ")");

        foreach (Loop l in mesh.loops)
        {
            Debug.Assert(l.next != null, "loop has a next loop");
            Debug.Assert(l.prev != null, "loop has a next loop");
        }

        Debug.Assert(f0.Loop(v0) != null, "loop with vertex v0 does not exist in face f0");
        Debug.Assert(f0.Loop(v0).vert == v0, "loop with vertex v0 has v0 as corner");
        Debug.Assert(f0.Loop(v1) != null, "loop with vertex v1 does not exist in face f0");
        Debug.Assert(f0.Loop(v1).vert == v1, "loop with vertex v1 has v1 as corner");
        Debug.Assert(f0.Loop(v3) == null, "loop with vertex v3 does not exist in face f0");

        Edge e0 = null;

        foreach (Edge e in mesh.edges)
        {
            if ((e.vert1 == v1 && e.vert2 == v2) || (e.vert1 == v2 && e.vert2 == v1))
            {
                e0 = e;
                break;
            }
        }
        Debug.Assert(e0 != null, "found edge between v1 and v2");
        mesh.RemoveEdge(e0);
        Debug.Assert(mesh.vertices.Count == 4, "vert count after removing edge");
        Debug.Assert(mesh.loops.Count == 0, "loop count after removing edge");
        Debug.Assert(mesh.edges.Count == 4, "edge count after removing edge");
        Debug.Assert(mesh.faces.Count == 0, "face count after removing edge");

        foreach (Loop l in mesh.loops)
        {
            Debug.Assert(l.next != null, "loop still has a next loop");
            Debug.Assert(l.prev != null, "loop still has a next loop");
        }

        Debug.Log("TestBMesh #3 passed.");

        return(true);
    }
Пример #5
0
    ///////////////////////////////////////////////////////////////////////////
    #region [AttributeLerp]

    /**
     * Set all attributes in destination vertex to attr[v1] * (1 - t) + attr[v2] * t
     * Overriding attributes: all in vertex 'destination', none in others.
     */
    public static void AttributeLerp(BMesh mesh, Vertex destination, Vertex v1, Vertex v2, float t)
    {
        foreach (var attr in mesh.vertexAttributes)
        {
            if (!v1.attributes.ContainsKey(attr.name) || !v2.attributes.ContainsKey(attr.name))
            {
                continue;
            }
            switch (attr.type.baseType)
            {
            case AttributeBaseType.Float:
            {
                var val1 = v1.attributes[attr.name] as FloatAttributeValue;
                var val2 = v2.attributes[attr.name] as FloatAttributeValue;
                int n    = val1.data.Length;
                Debug.Assert(val2.data.Length == n);
                var val = new FloatAttributeValue {
                    data = new float[n]
                };
                for (int i = 0; i < n; ++i)
                {
                    val.data[i] = Mathf.Lerp(val1.data[i], val2.data[i], t);
                }
                destination.attributes[attr.name] = val;
                break;
            }

            case AttributeBaseType.Int:
            {
                var val1 = v1.attributes[attr.name] as IntAttributeValue;
                var val2 = v2.attributes[attr.name] as IntAttributeValue;
                int n    = val1.data.Length;
                Debug.Assert(val2.data.Length == n);
                var val = new IntAttributeValue {
                    data = new int[n]
                };
                for (int i = 0; i < n; ++i)
                {
                    val.data[i] = (int)Mathf.Round(Mathf.Lerp(val1.data[i], val2.data[i], t));
                }
                destination.attributes[attr.name] = val;
                break;
            }

            default:
                Debug.Assert(false);
                break;
            }
        }
    }
Пример #6
0
    ///////////////////////////////////////////////////////////////////////////
    #region [Subdivide]

    /**
     * Subdivide a mesh, without smoothing it, trying to interpolate all
     * available attributes as much as possible. After subdivision, all faces
     * are quads.
     * Overriding attributes: edge's id
     */
    public static void Subdivide(BMesh mesh)
    {
        int i             = 0;
        var edgeCenters   = new Vertex[mesh.edges.Count];
        var originalEdges = new Edge[mesh.edges.Count];

        foreach (Edge e in mesh.edges)
        {
            edgeCenters[i] = mesh.AddVertex(e.Center());
            AttributeLerp(mesh, edgeCenters[i], e.vert1, e.vert2, 0.5f);
            originalEdges[i] = e;
            e.id             = i++;
        }

        var originalFaces = new List <Face>(mesh.faces); // copy because mesh.faces changes during iterations

        foreach (Face f in originalFaces)
        {
            Vertex faceCenter = mesh.AddVertex(f.Center());
            float  w          = 0;

            // Create one quad per loop in the original face
            Loop it = f.loop;
            do
            {
                w += 1;
                AttributeLerp(mesh, faceCenter, faceCenter, it.vert, 1 / w);

                var quad = new Vertex[] {
                    it.vert,
                    edgeCenters[it.edge.id],
                    faceCenter,
                    edgeCenters[it.prev.edge.id]
                };
                mesh.AddFace(quad);
                it = it.next;
            } while (it != f.loop);

            // then get rid of the original face
            mesh.RemoveFace(f);
        }

        // Remove old edges
        foreach (Edge e in originalEdges)
        {
            mesh.RemoveEdge(e);
        }
    }
Пример #7
0
    ///////////////////////////////////////////////////////////////////////////
    #region [DrawGizmos]

    /**
     * Draw details about the BMesh structure un the viewport.
     * To be used inside of OnDrawGizmozs() in a MonoBehavior script.
     * You'll most likely need to add beforehand:
     *     Gizmos.matrix = transform.localToWorldMatrix
     */
    public static void DrawGizmos(BMesh mesh)
    {
        Gizmos.color = Color.yellow;
        foreach (var e in mesh.edges)
        {
            Gizmos.DrawLine(e.vert1.point, e.vert2.point);
        }
        Gizmos.color = Color.red;
        foreach (var l in mesh.loops)
        {
            Vertex vert  = l.vert;
            Vertex other = l.edge.OtherVertex(vert);
            Gizmos.DrawRay(vert.point, (other.point - vert.point) * 0.1f);

            Loop    nl     = l.next;
            Vertex  nother = nl.edge.ContainsVertex(vert) ? nl.edge.OtherVertex(vert) : nl.edge.OtherVertex(other);
            Vector3 no     = vert.point + (other.point - vert.point) * 0.1f;
            Gizmos.DrawRay(no, (nother.point - no) * 0.1f);
        }
        Gizmos.color = Color.green;
        int i = 0;

        foreach (var f in mesh.faces)
        {
            Vector3 c = f.Center();
            Gizmos.DrawLine(c, f.loop.vert.point);
            Gizmos.DrawRay(c, (f.loop.next.vert.point - c) * 0.2f);
#if UNITY_EDITOR
            //Handles.Label(c, "f" + i);
            ++i;
#endif // UNITY_EDITOR
        }

        i = 0;
        foreach (Vertex v in mesh.vertices)
        {
#if UNITY_EDITOR
            //var uv = v.attributes["uv"] as BMesh.FloatAttributeValue;
            //Handles.Label(v.point, "" + i);
            ++i;
#endif // UNITY_EDITOR
        }
    }
Пример #8
0
    ///////////////////////////////////////////////////////////////////////////
    #region [Merge]

    /**
     * Merge a Unity Mesh into a BMesh. Can be used with an empty BMesh to
     * create a BMesh from a Unity Mesh
     * TODO: Add support for uvs etc.
     */
    public static void Merge(BMesh mesh, Mesh unityMesh, bool flipFaces = false)
    {
        Vector3[] unityVertices  = unityMesh.vertices;
        int[]     unityTriangles = unityMesh.triangles;
        var       verts          = new Vertex[unityVertices.Length];

        for (int i = 0; i < unityVertices.Length; ++i)
        {
            Vector3 p = unityVertices[i];
            verts[i] = mesh.AddVertex(p);
        }
        for (int i = 0; i < unityTriangles.Length / 3; ++i)
        {
            mesh.AddFace(
                verts[unityTriangles[3 * i + (flipFaces ? 1 : 0)]],
                verts[unityTriangles[3 * i + (flipFaces ? 0 : 1)]],
                verts[unityTriangles[3 * i + 2]]
                );
        }
    }
Пример #9
0
    ///////////////////////////////////////////////////////////////////////////
    #region [Nearpoint]

    /**
     * Find the point for which attribute namde 'attrName' is closest to 'value'.
     * NB: If this was going to be repeated a lot of times, consider building
     * a KDTree (not included in BMeshOperators yet).
     */
    public static Vertex Nearpoint(BMesh mesh, AttributeValue value, string attrName)
    {
        if (!mesh.HasVertexAttribute(attrName))
        {
            return(null);
        }
        Vertex argmin = null;
        float  min    = 0;

        foreach (Vertex v in mesh.vertices)
        {
            float d = AttributeValue.Distance(v.attributes[attrName], value);
            if (argmin == null || d < min)
            {
                argmin = v;
                min    = d;
            }
        }
        return(argmin);
    }
Пример #10
0
    static float AverageRadiusLength(BMesh mesh)
    {
        float lengthsum = 0;
        float weightsum = 0;

        foreach (Face f in mesh.faces)
        {
            Vector3       c     = f.Center();
            List <Vertex> verts = f.NeighborVertices();
            if (verts.Count != 4)
            {
                continue;
            }
            // (r for "radius")
            Vector3 r0 = verts[0].point - c;
            Vector3 r1 = verts[1].point - c;
            Vector3 r2 = verts[2].point - c;
            Vector3 r3 = verts[3].point - c;

            var localToGlobal = ComputeLocalAxis(r0, r1, r2, r3);
            var globalToLocal = localToGlobal.transpose;

            // in local coordinates (l for "local")
            Vector3 l0 = globalToLocal * r0;
            Vector3 l1 = globalToLocal * r1;
            Vector3 l2 = globalToLocal * r2;
            Vector3 l3 = globalToLocal * r3;

            // Rotate vectors (rl for "rotated local")
            Vector3 rl0 = l0;
            Vector3 rl1 = new Vector3(l1.y, -l1.x, l1.z);
            Vector3 rl2 = new Vector3(-l2.x, -l2.y, l2.z);
            Vector3 rl3 = new Vector3(-l3.y, l3.x, l3.z);

            Vector3 average = (rl0 + rl1 + rl2 + rl3) / 4;

            lengthsum += average.magnitude;
            weightsum += 1;
        }
        return(lengthsum / weightsum);
    }
Пример #11
0
    static bool TestTwoVertexFaces()
    {
        var mesh = new BMesh();

        Vertex v0 = mesh.AddVertex(new Vector3(-1, 0, -1));
        Vertex v1 = mesh.AddVertex(new Vector3(-1, 0, 1));
        Vertex v2 = mesh.AddVertex(new Vector3(1, 0, 1));
        Face   f0 = mesh.AddFace(v0, v1);
        Face   f1 = mesh.AddFace(v1, v2);

        Debug.Assert(mesh.vertices.Count == 3, "vert count");
        Debug.Assert(mesh.loops.Count == 4, "loop count");
        Debug.Assert(mesh.edges.Count == 2, "edge count");
        Debug.Assert(mesh.faces.Count == 2, "face count");

        Debug.Assert(v0.NeighborFaces().Count == 1, "v0 has one neighbor face (found count: " + v0.NeighborFaces().Count + ")");
        Debug.Assert(v1.NeighborFaces().Count == 2, "v1 has two neighbor face (found count: " + v1.NeighborFaces().Count + ")");

        Debug.Log("TestBMesh TestTwoVertexFaces passed.");

        return(true);
    }
Пример #12
0
    static bool TestSubdivideTris()
    {
        var mesh = new BMesh();

        mesh.AddVertexAttribute(new AttributeDefinition("uv", AttributeBaseType.Float, 2));

        Vertex v0 = mesh.AddVertex(new Vector3(-1, 0, -1));
        Vertex v1 = mesh.AddVertex(new Vector3(-1, 0, 1));
        Vertex v2 = mesh.AddVertex(new Vector3(1, 0, 1));
        Vertex v3 = mesh.AddVertex(new Vector3(1, 0, -1));
        Face   f0 = mesh.AddFace(v0, v1, v2);
        Face   f1 = mesh.AddFace(v2, v1, v3);

        foreach (var v in mesh.vertices)
        {
            v.attributes["uv"] = new FloatAttributeValue(v.point.x, v.point.z);
        }

        BMeshOperators.Subdivide(mesh);

        Debug.Assert(mesh.vertices.Count == 11, "vertex count");
        Debug.Assert(mesh.edges.Count == 16, "edge count");
        Debug.Assert(mesh.loops.Count == 24, "loop count");
        Debug.Assert(mesh.faces.Count == 6, "face count");

        foreach (Face f in mesh.faces)
        {
            Debug.Assert(f.vertcount == 4, "faces are quads");
        }

        foreach (var v in mesh.vertices)
        {
            var uv = v.attributes["uv"] as FloatAttributeValue;
            Debug.Assert(Mathf.Abs(uv.data[0] - v.point.x) < epsilon && Mathf.Abs(uv.data[1] - v.point.z) < epsilon, "attribute interpolation: " + uv.data[0] + " == " + v.point.x + " && " + uv.data[1] + " == " + v.point.z);
        }

        Debug.Log("TestBMeshOperators TestSubdivideTris passed.");
        return(true);
    }
Пример #13
0
    static bool Test1()
    {
        var mesh = new BMesh();

        Vertex v0 = mesh.AddVertex(new Vector3(-0.5f, 0.0f, -Mathf.Sqrt(3) / 6));
        Vertex v1 = mesh.AddVertex(new Vector3(0.5f, 0.0f, -Mathf.Sqrt(3) / 6));
        Vertex v2 = mesh.AddVertex(new Vector3(0, 0.0f, Mathf.Sqrt(3) / 3));
        Face   f  = mesh.AddFace(v0, v1, v2);

        Debug.Assert(mesh.vertices.Count == 3, "vert count");
        Debug.Assert(mesh.loops.Count == 3, "loop count");
        Debug.Assert(mesh.edges.Count == 3, "edge count");
        Debug.Assert(mesh.faces.Count == 1, "face count");

        Loop l = mesh.loops[0];

        for (int i = 0; i < 3; ++i)
        {
            var v = mesh.vertices[i];
            Debug.Assert(mesh.loops[i].face == f, "loop has face");
            Debug.Assert(mesh.loops[i].edge != null, "loop has edge");
            Debug.Assert(mesh.edges[i].loop != null, "edge has loop");
            Debug.Assert(v.edge != null, "vertex has edge");
            Debug.Assert(v.edge.vert1 == v || v.edge.vert2 == v, "vertex is in vertex edge");
            Debug.Assert(l.next != l, "loop has next");
            Debug.Assert(l.next.prev == l, "loop has consistent next");
            Debug.Assert(l.radial_next.radial_prev == l, "loop has consistent radial next");
            l = l.next;
        }
        Debug.Assert(l == mesh.loops[0], "loop loops");

        Debug.Assert(mesh.FindEdge(v0, v1) != null, "edge between v0 and v1");
        Debug.Assert(mesh.FindEdge(v0, v2) != null, "edge between v0 and v2");
        Debug.Assert(mesh.FindEdge(v2, v1) != null, "edge between v2 and v1");

        Debug.Log("TestBMesh #1 passed.");

        return(true);
    }
Пример #14
0
    static bool Test3()
    {
        var mesh = new BMesh();

        Vertex v0 = mesh.AddVertex(new Vector3(-1, 0, -1));
        Vertex v1 = mesh.AddVertex(new Vector3(-1, 0, 1));
        Vertex v2 = mesh.AddVertex(new Vector3(1, 0, 1));
        Vertex v3 = mesh.AddVertex(new Vector3(1, 0, -1));
        Face   f0 = mesh.AddFace(v0, v1, v2);
        Face   f1 = mesh.AddFace(v2, v1, v3);

        Debug.Assert(mesh.vertices.Count == 4, "vert count");
        Debug.Assert(mesh.loops.Count == 6, "loop count");
        Debug.Assert(mesh.edges.Count == 5, "edge count");
        Debug.Assert(mesh.faces.Count == 2, "face count");

        Edge e0 = null;

        foreach (Edge e in mesh.edges)
        {
            if ((e.vert1 == v1 && e.vert2 == v2) || (e.vert1 == v2 && e.vert2 == v1))
            {
                e0 = e;
                break;
            }
        }
        Debug.Assert(e0 != null, "found edge between v1 and v2");
        mesh.RemoveEdge(e0);
        Debug.Assert(mesh.vertices.Count == 4, "vert count after removing edge");
        Debug.Assert(mesh.loops.Count == 0, "loop count after removing edge");
        Debug.Assert(mesh.edges.Count == 4, "edge count after removing edge");
        Debug.Assert(mesh.faces.Count == 0, "face count after removing edge");

        Debug.Log("TestBMesh #3 passed.");

        return(true);
    }
Пример #15
0
    /**
     * Try to make quads as square as possible (may be called iteratively).
     * This is not a very common operation but was developped so I keep it here.
     * This assumes that the mesh is only made of quads.
     * Overriding attributes: vertex's id
     * Optionnaly read vertexattributes:
     *   - restpos: a Float3 telling which position attracts the vertex
     *   - weight: a Float telling to which extent the restpos must be
     *             considered.
     *
     * @param rate speed at which faces are squarified. A higher rate goes
     *        faster but there is a risk for overshooting.
     * @param uniformLength whether the size of the quads must be uniformized.
     */
    public static void SquarifyQuads(BMesh mesh, float rate = 1.0f, bool uniformLength = false)
    {
        float avg = 0;

        if (uniformLength)
        {
            avg = AverageRadiusLength(mesh);
        }

        var pointUpdates = new Vector3[mesh.vertices.Count];
        var weights      = new float[mesh.vertices.Count];

        int i = 0;

        foreach (Vertex v in mesh.vertices)
        {
            if (mesh.HasVertexAttribute("restpos"))
            {
                if (mesh.HasVertexAttribute("weight"))
                {
                    weights[i] = (v.attributes["weight"] as FloatAttributeValue).data[0];
                }
                else
                {
                    weights[i] = 1;
                }
                var restpos = (v.attributes["restpos"] as FloatAttributeValue).AsVector3();
                pointUpdates[i] = (restpos - v.point) * weights[i];
            }
            else
            {
                pointUpdates[i] = Vector3.zero;
                weights[i]      = 0;
            }
            v.id = i++;
        }

        // Accumulate updates
        foreach (Face f in mesh.faces)
        {
            Vector3       c     = f.Center();
            List <Vertex> verts = f.NeighborVertices();
            if (verts.Count != 4)
            {
                continue;
            }
            // (r for "radius")
            Vector3 r0 = verts[0].point - c;
            Vector3 r1 = verts[1].point - c;
            Vector3 r2 = verts[2].point - c;
            Vector3 r3 = verts[3].point - c;

            var localToGlobal = ComputeLocalAxis(r0, r1, r2, r3);
            var globalToLocal = localToGlobal.transpose;

            // in local coordinates (l for "local")
            Vector3 l0 = globalToLocal * r0;
            Vector3 l1 = globalToLocal * r1;
            Vector3 l2 = globalToLocal * r2;
            Vector3 l3 = globalToLocal * r3;

            bool switch03 = false;
            if (l1.normalized.y < l3.normalized.y)
            {
                switch03 = true;
                var tmp = l3;
                l3 = l1;
                l1 = tmp;
            }
            // now 0->1->2->3 is in direct trigonometric order

            // Rotate vectors (rl for "rotated local")
            Vector3 rl0 = l0;
            Vector3 rl1 = new Vector3(l1.y, -l1.x, l1.z);
            Vector3 rl2 = new Vector3(-l2.x, -l2.y, l2.z);
            Vector3 rl3 = new Vector3(-l3.y, l3.x, l3.z);

            Vector3 average = (rl0 + rl1 + rl2 + rl3) / 4;
            if (uniformLength)
            {
                average = average.normalized * avg;
            }

            // Rotate back (lt for "local target")
            Vector3 lt0 = average;
            Vector3 lt1 = new Vector3(-average.y, average.x, average.z);
            Vector3 lt2 = new Vector3(-average.x, -average.y, average.z);
            Vector3 lt3 = new Vector3(average.y, -average.x, average.z);

            // Switch back
            if (switch03)
            {
                var tmp = lt3;
                lt3 = lt1;
                lt1 = tmp;
            }

            // Back to global (t for "target")
            Vector3 t0 = localToGlobal * lt0;
            Vector3 t1 = localToGlobal * lt1;
            Vector3 t2 = localToGlobal * lt2;
            Vector3 t3 = localToGlobal * lt3;

            // Accumulate
            pointUpdates[verts[0].id] += t0 - r0;
            pointUpdates[verts[1].id] += t1 - r1;
            pointUpdates[verts[2].id] += t2 - r2;
            pointUpdates[verts[3].id] += t3 - r3;
            weights[verts[0].id]      += 1;
            weights[verts[1].id]      += 1;
            weights[verts[2].id]      += 1;
            weights[verts[3].id]      += 1;
        }

        // Apply updates
        i = 0;
        foreach (Vertex v in mesh.vertices)
        {
            if (weights[i] > 0)
            {
                v.point += pointUpdates[i] * (rate / weights[i]);
            }
            ++i;
        }
    }
Пример #16
0
    ///////////////////////////////////////////////////////////////////////////
    #region [SetInMeshFilter]

    /**
     * Convert a BMesh into a Unity Mesh and set it in the provided MeshFilter
     * WARNING: Only works with tri or quad meshes!
     * read attributes uv, uv2 from vertices and materialId from faces.
     *
     * NB: UVs are read from vertices, because in a Unity mesh when two face
     * corners have different UVs, they are different vertices. If you worked
     * with UVs as Loop attributes, you must first split points and migrate UVs
     * to vertex attributes.
     */
    public static void SetInMeshFilter(BMesh mesh, MeshFilter mf)
    {
        // Points
        Vector2[] uvs     = null;
        Vector2[] uvs2    = null;
        Vector3[] normals = null;
        Color[]   colors  = null;
        Vector3[] points  = new Vector3[mesh.vertices.Count];
        if (mesh.HasVertexAttribute("uv"))
        {
            uvs = new Vector2[mesh.vertices.Count];
        }
        if (mesh.HasVertexAttribute("uv2"))
        {
            uvs2 = new Vector2[mesh.vertices.Count];
        }
        if (mesh.HasVertexAttribute("normal"))
        {
            normals = new Vector3[mesh.vertices.Count];
        }
        if (mesh.HasVertexAttribute("color"))
        {
            colors = new Color[mesh.vertices.Count];
        }
        int i = 0;

        foreach (var vert in mesh.vertices)
        {
            vert.id   = i;
            points[i] = vert.point;
            if (uvs != null)
            {
                var uv = vert.attributes["uv"] as FloatAttributeValue;
                uvs[i] = new Vector2(uv.data[0], uv.data[1]);
            }
            if (uvs2 != null)
            {
                var uv2 = vert.attributes["uv2"] as FloatAttributeValue;
                uvs2[i] = new Vector2(uv2.data[0], uv2.data[1]);
            }
            if (normals != null)
            {
                var normal = vert.attributes["normal"] as FloatAttributeValue;
                normals[i] = normal.AsVector3();
            }
            if (colors != null)
            {
                var color = vert.attributes["color"] as FloatAttributeValue;
                colors[i] = color.AsColor();
            }
            ++i;
        }

        // Triangles
        int  maxMaterialId   = 0;
        bool hasMaterialAttr = mesh.HasFaceAttribute("materialId");

        if (hasMaterialAttr)
        {
            foreach (var f in mesh.faces)
            {
                maxMaterialId = Mathf.Max(maxMaterialId, f.attributes["materialId"].asInt().data[0]);
            }
        }

        int[] tricounts = new int[maxMaterialId + 1];
        foreach (var f in mesh.faces)
        {
            Debug.Assert(f.vertcount == 3 || f.vertcount == 4, "Only meshes with triangles/quads can be converted to a unity mesh");
            int mat = hasMaterialAttr ? f.attributes["materialId"].asInt().data[0] : 0;
            tricounts[mat] += f.vertcount - 2;
        }
        int[][] triangles = new int[maxMaterialId + 1][];
        for (int mat = 0; mat < triangles.Length; ++mat)
        {
            triangles[mat] = new int[3 * tricounts[mat]];
            tricounts[mat] = 0;
        }
        // from now on tricounts[i] is the index of the next triangle to fill in the i-th triangle list
        foreach (var f in mesh.faces)
        {
            int mat = hasMaterialAttr ? f.attributes["materialId"].asInt().data[0] : 0;
            Debug.Assert(f.vertcount == 3 || f.vertcount == 4);
            {
                var l = f.loop;
                triangles[mat][3 * tricounts[mat] + 0] = l.vert.id; l = l.next;
                triangles[mat][3 * tricounts[mat] + 2] = l.vert.id; l = l.next;
                triangles[mat][3 * tricounts[mat] + 1] = l.vert.id; l = l.next;
                ++tricounts[mat];
            }
            if (f.vertcount == 4)
            {
                var l = f.loop.next.next;
                triangles[mat][3 * tricounts[mat] + 0] = l.vert.id; l = l.next;
                triangles[mat][3 * tricounts[mat] + 2] = l.vert.id; l = l.next;
                triangles[mat][3 * tricounts[mat] + 1] = l.vert.id; l = l.next;
                ++tricounts[mat];
            }
        }

        // Apply mesh
        Mesh unityMesh = new Mesh();

        mf.mesh            = unityMesh;
        unityMesh.vertices = points;
        if (uvs != null)
        {
            unityMesh.uv = uvs;
        }
        if (uvs2 != null)
        {
            unityMesh.uv2 = uvs2;
        }
        if (normals != null)
        {
            unityMesh.normals = normals;
        }
        if (colors != null)
        {
            unityMesh.colors = colors;
        }
        unityMesh.subMeshCount = triangles.Length;

        // Fix an issue when renderer has more materials than there are submeshes
        var renderer = mf.GetComponent <MeshRenderer>();

        if (renderer)
        {
            unityMesh.subMeshCount = Mathf.Max(unityMesh.subMeshCount, renderer.materials.Length);
        }

        for (int mat = 0; mat < triangles.Length; ++mat)
        {
            unityMesh.SetTriangles(triangles[mat], mat);
        }

        if (normals == null)
        {
            unityMesh.RecalculateNormals();
        }
    }
Пример #17
0
    ///////////////////////////////////////////////////////////////////////////
    #region [Merge]

    /**
     * Merge a Unity Mesh into a BMesh. Can be used with an empty BMesh to
     * create a BMesh from a Unity Mesh
     * TODO: Add support for uvs etc.
     */
    public static void Merge(BMesh mesh, Mesh unityMesh, bool flipFaces = false)
    {
        Vector3[] unityVertices = unityMesh.vertices;
        Vector2[] unityUvs      = unityMesh.uv;
        Vector2[] unityUvs2     = unityMesh.uv2;
        Vector3[] unityNormals  = unityMesh.normals;
        Color[]   unityColors   = unityMesh.colors;
        bool      hasUvs        = unityUvs != null && unityUvs.Length > 0;
        bool      hasUvs2       = unityUvs2 != null && unityUvs2.Length > 0;
        bool      hasNormals    = unityNormals != null && unityNormals.Length > 0;
        bool      hasColors     = unityColors != null && unityColors.Length > 0;

        int[] unityTriangles = unityMesh.triangles;
        var   verts          = new Vertex[unityVertices.Length];

        if (hasUvs)
        {
            mesh.AddVertexAttribute(new AttributeDefinition("uv", AttributeBaseType.Float, 2));
        }
        if (hasUvs2)
        {
            mesh.AddVertexAttribute(new AttributeDefinition("uv2", AttributeBaseType.Float, 2));
        }
        if (hasNormals)
        {
            mesh.AddVertexAttribute(new AttributeDefinition("normal", AttributeBaseType.Float, 3));
        }
        if (hasColors)
        {
            mesh.AddVertexAttribute(new AttributeDefinition("color", AttributeBaseType.Float, 4));
        }
        for (int i = 0; i < unityVertices.Length; ++i)
        {
            Vector3 p = unityVertices[i];
            verts[i] = mesh.AddVertex(p);
            if (hasUvs)
            {
                verts[i].attributes["uv"].asFloat().FromVector2(unityUvs[i]);
            }
            if (hasUvs2)
            {
                verts[i].attributes["uv2"].asFloat().FromVector2(unityUvs2[i]);
            }
            if (hasNormals)
            {
                verts[i].attributes["normal"].asFloat().FromVector3(unityNormals[i]);
            }
            if (hasColors)
            {
                verts[i].attributes["color"].asFloat().FromColor(unityColors[i]);
            }
        }
        for (int i = 0; i < unityTriangles.Length / 3; ++i)
        {
            mesh.AddFace(
                verts[unityTriangles[3 * i + (flipFaces ? 1 : 0)]],
                verts[unityTriangles[3 * i + (flipFaces ? 0 : 1)]],
                verts[unityTriangles[3 * i + 2]]
                );
        }
    }
Пример #18
0
    static bool TestAttributes()
    {
        var mesh = new BMesh();

        mesh.AddVertexAttribute(new AttributeDefinition("test", AttributeBaseType.Float, 3));
        mesh.AddEdgeAttribute("edgetest", AttributeBaseType.Float, 2);
        mesh.AddFaceAttribute("facetest", AttributeBaseType.Int, 1);

        Vertex v0 = mesh.AddVertex(new Vector3(-1, 0, -1));
        Vertex v1 = mesh.AddVertex(new Vector3(-1, 0, 1));
        Vertex v2 = mesh.AddVertex(new Vector3(1, 0, 1));
        Vertex v3 = mesh.AddVertex(new Vector3(1, 0, -1));
        Face   f0 = mesh.AddFace(v0, v1, v2);
        Face   f1 = mesh.AddFace(v2, v1, v3);

        var otherAttr = new AttributeDefinition("other", AttributeBaseType.Int, 1);
        var def       = otherAttr.defaultValue as IntAttributeValue;

        def.data[0] = 42;
        mesh.AddVertexAttribute(otherAttr);
        foreach (var v in mesh.vertices)
        {
            Debug.Assert(v.attributes.ContainsKey("test"), "vertex has test attribute");
            var test = v.attributes["test"] as FloatAttributeValue;
            Debug.Assert(test != null, "vertex test attribute has float value");
            var testAsInt = v.attributes["test"] as IntAttributeValue;
            Debug.Assert(testAsInt == null, "vertex test attribute has no int value");
            Debug.Assert(test.data.Length == 3, "vertex test attribute has 3 dimensions");
            Debug.Assert(test.data[0] == 0 && test.data[1] == 0 && test.data[2] == 0, "vertex test attribute has value (0, 0, 0)");

            Debug.Assert(v.attributes.ContainsKey("other"), "vertex has other attribute");
            var other = v.attributes["other"] as IntAttributeValue;
            Debug.Assert(other.data.Length == 1, "vertex other attribute has 1 dimension");
            Debug.Assert(other.data[0] == 42, "vertex other attribute has value 42");
        }

        foreach (var e in mesh.edges)
        {
            Debug.Assert(e.attributes.ContainsKey("edgetest"), "edge has test attribute");
            var edgetest = e.attributes["edgetest"] as FloatAttributeValue;
            Debug.Assert(edgetest != null, "edge test attribute has float value");
            var edgetestAsInt = e.attributes["edgetest"] as IntAttributeValue;
            Debug.Assert(edgetestAsInt == null, "edge test attribute has no int value");
            Debug.Assert(edgetest.data.Length == 2, "edge test attribute has 2 dimensions");
            Debug.Assert(edgetest.data[0] == 0 && edgetest.data[1] == 0, "edge test attribute has value (0, 0)");
        }

        foreach (var f in mesh.faces)
        {
            Debug.Assert(f.attributes.ContainsKey("facetest"), "face has test attribute");
            var facetest = f.attributes["facetest"] as IntAttributeValue;
            Debug.Assert(facetest != null, "face test attribute has int value");
            var facetestAsFloat = f.attributes["facetest"] as FloatAttributeValue;
            Debug.Assert(facetestAsFloat == null, "face test attribute has no float value");
            Debug.Assert(facetest.data.Length == 1, "face test attribute has 1 dimensions");
            Debug.Assert(facetest.data[0] == 0, "face test attribute has value (0)");
        }

        {
            var other1 = v1.attributes["other"] as IntAttributeValue;
            var other2 = v2.attributes["other"] as IntAttributeValue;
            other1.data[0] = 43;
            Debug.Assert(other2.data[0] == 42, "default vertex attribute values are independent");
        }

        {
            var v4 = new Vertex(new Vector3(0, 0, 0))
            {
                attributes = new Dictionary <string, AttributeValue>
                {
                    ["other"] = new FloatAttributeValue {
                        data = new float[] { 1, 2, 3 }
                    }
                }
            };
            Debug.Log("Test expects warning:");
            Debug.Assert(mesh.AddVertex(v4) == v4, "add point is conservative");
            Debug.Assert(v4.attributes.ContainsKey("test"), "new vertex has test attribute");
            var test = v4.attributes["test"] as FloatAttributeValue;
            Debug.Assert(test.data[0] == 0 && test.data[1] == 0 && test.data[2] == 0, "new vertex test attribute has value (0, 0, 0)");
            var other = v4.attributes["other"] as IntAttributeValue;
            Debug.Assert(other != null, "new vertex other attribute is an int");
            Debug.Assert(other.data[0] == 42, "new vertex other attribute ignored non int provided value");
        }

        Debug.Log("TestBMesh TestAttributes passed.");

        return(true);
    }
Пример #19
0
    static bool Test2()
    {
        var mesh = new BMesh();

        Vertex v0 = mesh.AddVertex(new Vector3(-1, 0, -1));
        Vertex v1 = mesh.AddVertex(new Vector3(-1, 0, 1));
        Vertex v2 = mesh.AddVertex(new Vector3(1, 0, 1));
        Vertex v3 = mesh.AddVertex(new Vector3(1, 0, -1));
        Face   f  = mesh.AddFace(v0, v1, v2, v3);

        Debug.Assert(mesh.vertices.Count == 4, "vert count");
        Debug.Assert(mesh.loops.Count == 4, "loop count");
        Debug.Assert(mesh.edges.Count == 4, "edge count");
        Debug.Assert(mesh.faces.Count == 1, "face count");

        // Edges
        Edge e0 = mesh.FindEdge(v0, v1);
        Edge e1 = mesh.FindEdge(v1, v2);
        Edge e2 = mesh.FindEdge(v2, v3);
        Edge e3 = mesh.FindEdge(v3, v0);

        Debug.Assert(e0 != null, "found edge v0->v1");
        Debug.Assert(e1 != null, "found edge v1->v2");
        Debug.Assert(e2 != null, "found edge v2->v3");
        Debug.Assert(e3 != null, "found edge v3->v0");

        Vector3 expected;

        expected = new Vector3(-1, 0, 0);
        Debug.Assert(Vector3.Distance(expected, e0.Center()) < epsilon, "edge 0 center");
        expected = new Vector3(0, 0, 1);
        Debug.Assert(Vector3.Distance(expected, e1.Center()) < epsilon, "edge 1 center");
        expected = new Vector3(1, 0, 0);
        Debug.Assert(Vector3.Distance(expected, e2.Center()) < epsilon, "edge 2 center");
        expected = new Vector3(0, 0, -1);
        Debug.Assert(Vector3.Distance(expected, e3.Center()) < epsilon, "edge 3 center");

        // face
        expected = new Vector3(0, 0, 0);
        Debug.Assert(Vector3.Distance(expected, f.Center()) < epsilon, "face center");

        // Loop consistency
        v0.id = 0; v1.id = 1; v2.id = 2; v3.id = 3;
        Loop l       = v0.edge.loop;
        Loop it      = l;
        int  prevId  = it.prev.vert.id;
        int  forward = (prevId + 1) % 4 == it.vert.id ? 1 : 0;

        do
        {
            Debug.Assert((forward == 1 && (prevId + 1) % 4 == it.vert.id) || (it.vert.id + 1) % 4 == prevId, "valid quad loop order");
            prevId = it.vert.id;
            it     = it.next;
        } while (it != l);

        for (int i = 0; i < 4; ++i)
        {
            var v = mesh.vertices[i];
            Debug.Assert(mesh.loops[i].face == f);
            Debug.Assert(v.edge != null);
            Debug.Assert(v.edge.vert1 == v || v.edge.vert2 == v);
        }

        Debug.Assert(mesh.FindEdge(v0, v1) != null, "edge between v0 and v1");

        mesh.RemoveEdge(mesh.edges[0]);
        Debug.Assert(mesh.vertices.Count == 4, "vert count after removing edge");
        Debug.Assert(mesh.loops.Count == 0, "loop count after removing edge");
        Debug.Assert(mesh.edges.Count == 3, "edge count after removing edge");
        Debug.Assert(mesh.faces.Count == 0, "face count after removing edge");

        Debug.Log("TestBMesh #2 passed.");

        return(true);
    }