Package toxi.newmesh

Source Code of toxi.newmesh.IndexedTriangleMesh

package toxi.newmesh;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import toxi.geom.AABB;
import toxi.geom.IsectData3D;
import toxi.geom.Ray3D;
import toxi.geom.ReadonlyVec3D;
import toxi.geom.Sphere;
import toxi.geom.Triangle3D;
import toxi.geom.TriangleIntersector;
import toxi.geom.Vec2D;
import toxi.geom.Vec3D;
import toxi.geom.mesh.subdiv.NewSubdivStrategy;
import toxi.util.datatypes.ItemIndex;
import toxi.util.datatypes.UniqueItemIndex;

public class IndexedTriangleMesh {

    public static final String ATTR_EDGES = "edges";
    public static final String ATTR_FNORMALS = "fnormals";
    public static final String ATTR_UVCOORDS = "uv";
    public static final String ATTR_VCOLORS = "col";
    public static final String ATTR_VERTICES = "vertices";
    public static final String ATTR_VNORMALS = "vnormals";

    public SpatialIndex vertices = new SpatialIndex(0.001f);

    public ItemIndex<Vec3D> fnormals = new UniqueItemIndex<Vec3D>();

    public final ArrayList<AttributedFace> faces = new ArrayList<AttributedFace>();

    public final HashMap<String, UniqueItemIndex<Object>> attributes = new HashMap<String, UniqueItemIndex<Object>>();

    public IndexedTriangleMesh() {
    }

    public IndexedTriangleMesh addFace(Vec3D a, Vec3D b, Vec3D c,
            HashMap<String, Object[]> attribs) {
        int idA = vertices.index(a);
        int idB = vertices.index(b);
        int idC = vertices.index(c);
        if (idA != idB && idA != idC && idB != idC) {
            AttributedFace f = new AttributedFace(idA, idB, idC,
                    addFaceAttributes(null, attribs));
            faces.add(f);
        }
        return this;
    }

    public IndexedTriangleMesh addFace(Vec3D a, Vec3D b, Vec3D c, Vec2D uva,
            Vec2D uvb, Vec2D uvc) {
        HashMap<String, Object[]> attribs = null;
        if (uva != null && uvb != null && uvc != null) {
            attribs = new HashMap<String, Object[]>();
            attribs.put(ATTR_UVCOORDS, new Object[] {
                    uva, uvb, uvc
            });
        }
        return addFace(a, b, c, attribs);
    }

    public HashMap<String, int[]> addFaceAttribute(AttributedFace f,
            String attrib, Object attA, Object attB, Object attC) {
        if (f != null && attrib != null && attA != null && attB != null
                && attC != null) {
            ItemIndex<Object> idx = getAttributeIndex(attrib);
            f.attribs.put(attrib, new int[] {
                    idx.index(attA), idx.index(attB), idx.index(attC)
            });
            return f.attribs;
        }
        return null;
    }

    public HashMap<String, int[]> addFaceAttributes(AttributedFace f,
            HashMap<String, Object[]> attribs) {
        HashMap<String, int[]> fattribs = null;
        if (attribs != null) {
            fattribs = (f != null) ? f.attribs : new HashMap<String, int[]>(
                    attribs.size(), 1);
            for (String attID : attribs.keySet()) {
                Object[] items = attribs.get(attID);
                if (items.length >= 3) {
                    ItemIndex<Object> idx = getAttributeIndex(attID);
                    int[] ids = new int[] {
                            idx.index(items[0]), idx.index(items[1]),
                            idx.index(items[2])
                    };
                    fattribs.put(attID, ids);
                }
            }
        }
        return fattribs;
    }

    public IndexedTriangleMesh addMesh(IndexedTriangleMesh mesh) {
        Vec3D[] v = null;
        for (AttributedFace f : mesh.faces) {
            v = mesh.getFaceVertices(f, v);
            addFace(v[0], v[1], v[2], null);
        }
        return this;
    }

    public IndexedTriangleMesh addMeshWithAttribs(IndexedTriangleMesh mesh) {
        HashMap<String, Object[]> attribs = new HashMap<String, Object[]>();
        Vec3D[] v = null;
        for (AttributedFace f : mesh.faces) {
            attribs.clear();
            for (String a : f.attribs.keySet()) {
                attribs.put(a, mesh.getFaceAttribValues(f, a));
            }
            v = mesh.getFaceVertices(f, v);
            addFace(v[0], v[1], v[2], attribs);
        }
        return this;
    }

    public IndexedTriangleMesh clear() {
        vertices.clear();
        fnormals.clear();
        attributes.clear();
        faces.clear();
        return this;
    }

    public HashMap<String, float[]> compile() {
        HashSet<String> attribs = new HashSet<String>();
        attribs.add(ATTR_VERTICES);
        attribs.add(ATTR_FNORMALS);
        return compile(attribs, null);
    }

    public HashMap<String, float[]> compile(HashSet<String> attribs,
            HashMap<String, MeshAttributeCompiler> compilers) {
        HashMap<String, MeshAttributeCompiler> mergedComps = new HashMap<String, MeshAttributeCompiler>(
                getDefaultCompilers());
        if (compilers != null) {
            mergedComps.putAll(compilers);
        }
        HashMap<String, float[]> buffers = new HashMap<String, float[]>();
        int numF = faces.size();
        for (String attrib : attribs) {
            MeshAttributeCompiler comp = mergedComps.get(attrib);
            if (comp != null) {
                comp.setMesh(this);
                ItemIndex<?> index = comp.getIndex();
                int faceStride = 3 * comp.getStride();
                float[] buf = new float[numF * faceStride];
                int offset = 0;
                for (AttributedFace f : faces) {
                    comp.compileFace(f, index, buf, offset);
                    offset += faceStride;
                }
                buffers.put(attrib, buf);
            }
        }
        return buffers;
    }

    public List<Object> computeEdges() {
        ItemIndex<Object> edges = getAttributeIndex(ATTR_EDGES);
        edges.clear();
        for (AttributedFace f : faces) {
            if (f.attribs == null) {
                f.attribs = new HashMap<String, int[]>();
            }
            f.attribs.put(ATTR_EDGES, new int[] {
                    indexFaceEdge(f, f.a, f.b), indexFaceEdge(f, f.b, f.c),
                    indexFaceEdge(f, f.c, f.a)
            });
        }
        return edges.getItems();
    }

    public List<Vec3D> computeFaceNormals() {
        fnormals.clear();
        Vec3D[] v = null;
        for (AttributedFace f : faces) {
            v = getFaceVertices(f, v);
            f.normal = fnormals.index(v[0].sub(v[1]).crossSelf(v[0].sub(v[2]))
                    .normalize());
        }
        return fnormals.getItems();
    }

    public List<Object> computeVertexNormals() {
        Vec3D[] vnorms = new Vec3D[vertices.size()];
        for (int i = 0; i < vnorms.length; i++) {
            vnorms[i] = new Vec3D();
        }
        for (AttributedFace f : faces) {
            final Vec3D n = fnormals.forID(f.normal);
            vnorms[f.a].addSelf(n);
            vnorms[f.b].addSelf(n);
            vnorms[f.c].addSelf(n);
        }
        for (int i = 0; i < vnorms.length; i++) {
            vnorms[i].normalize();
        }
        ItemIndex<Object> idx = getAttributeIndex(ATTR_VNORMALS);
        idx.clear();
        for (AttributedFace f : faces) {
            if (f.attribs == null) {
                f.attribs = new HashMap<String, int[]>();
            }
            f.attribs.put(
                    ATTR_VNORMALS,
                    new int[] {
                            idx.index(vnorms[f.a]), idx.index(vnorms[f.b]),
                            idx.index(vnorms[f.c])
                    });
        }
        return idx.getItems();
    }

    public IndexedTriangleMesh extrudeFace(AttributedFace f, Vec3D offset,
            float scale) {
        Vec3D[] v = getFaceVertices(f, null);
        Vec3D[] v2 = new Vec3D[3];
        Vec3D c = v[0].add(v[1]).addSelf(v[2]).scaleSelf(1 / 3f);
        Vec3D n = c.add(offset);
        v2[0] = v[0].sub(c).scaleSelf(scale).addSelf(n);
        v2[1] = v[1].sub(c).scaleSelf(scale).addSelf(n);
        v2[2] = v[2].sub(c).scaleSelf(scale).addSelf(n);
        removeFace(f);
        // extruded copy
        addFace(v2[0], v2[1], v2[2], null);
        // sides
        addFace(v[0], v[1], v2[0], null);
        addFace(v[1], v2[1], v2[0], null);
        addFace(v[1], v[2], v2[1], null);
        addFace(v[2], v2[2], v2[1], null);
        addFace(v[2], v[0], v2[2], null);
        addFace(v[0], v2[0], v2[2], null);
        return this;
    }

    /**
     * Flips the current order of all face vertices and attributes. If face or
     * vertex normals are present their direction will be inverted as well.
     *
     * @return itself
     */
    public IndexedTriangleMesh flipVertexOrder() {
        for (AttributedFace f : faces) {
            int t = f.b;
            f.b = f.c;
            f.c = t;
            if (f.attribs != null) {
                for (int[] att : f.attribs.values()) {
                    t = att[1];
                    att[1] = att[2];
                    att[2] = t;
                }
            }
        }
        for (Vec3D n : fnormals.getItems()) {
            fnormals.reindex(n, n.getInverted());
        }
        ItemIndex<Object> vnormals = attributes.get(ATTR_VNORMALS);
        if (vnormals != null) {
            for (Object n : vnormals.getItems()) {
                vnormals.reindex(n, ((Vec3D) n).getInverted());
            }
        }
        return this;
    }

    public ItemIndex<Object> getAttributeIndex(String attID) {
        UniqueItemIndex<Object> idx = attributes.get(attID);
        if (idx == null) {
            idx = new UniqueItemIndex<Object>();
            attributes.put(attID, idx);
        }
        return idx;
    }

    public Sphere getBoundingSphere() {
        return getBounds().getBoundingSphere();
    }

    public AABB getBounds() {
        return AABB.getBoundingBox(vertices.getItems());
    }

    public Vec3D getCentroid() {
        return new Vec3D(getBounds());
    }

    public Vec3D getClosestVertexToPoint(ReadonlyVec3D p) {
        Vec3D closest = null;
        float minDist = Float.MAX_VALUE;
        for (Vec3D v : vertices.getItems()) {
            float d = v.distanceToSquared(p);
            if (d < minDist) {
                closest = v;
                minDist = d;
            }
        }
        return closest;
    }

    public HashMap<String, MeshAttributeCompiler> getDefaultCompilers() {
        HashMap<String, MeshAttributeCompiler> compilers = new HashMap<String, MeshAttributeCompiler>();
        compilers.put(ATTR_VERTICES, new MeshVertexCompiler());
        compilers.put(ATTR_FNORMALS, new MeshFaceNormalCompiler());
        compilers.put(ATTR_VNORMALS, new MeshVertexNormalCompiler());
        compilers.put(ATTR_UVCOORDS, new MeshUVCompiler());
        compilers.put(ATTR_VCOLORS, new MeshVertexColorCompiler());
        return compilers;
    }

    /**
     * @return the edges
     */
    public List<Object> getEdges() {
        return getAttributeIndex(ATTR_EDGES).getItems();
    }

    public List<AttributedEdge> getEdgesForVertex(Vec3D v) {
        List<AttributedEdge> vedges = null;
        int id = vertices.getID(v);
        if (id != -1) {
            vedges = getEdgesForVertexID(id);
        }
        return vedges;
    }

    public List<AttributedEdge> getEdgesForVertexID(int id) {
        List<AttributedEdge> vedges = new ArrayList<AttributedEdge>(2);
        for (Object o : getEdges()) {
            AttributedEdge e = (AttributedEdge) o;
            if (e.a == id || e.b == id) {
                vedges.add(e);
            }
        }
        return vedges;
    }

    public Triangle3D getFaceAsTriangle(AttributedFace f) {
        Vec3D[] verts = getFaceVertices(f, null);
        return new Triangle3D(verts[0], verts[1], verts[2]);
    }

    public HashMap<String, Object[]> getFaceAttribValues(AttributedFace f,
            String... attribs) {
        HashMap<String, Object[]> values = new HashMap<String, Object[]>(
                attribs.length, 1);
        for (String a : attribs) {
            values.put(a, getFaceAttribValues(f, a));
        }
        return values;
    }

    public Object[] getFaceAttribValues(AttributedFace f, String att) {
        ItemIndex<Object> idx = attributes.get(att);
        int[] fattribs = f.attribs.get(att);
        if (idx == null || fattribs == null) {
            return null;
        }
        return new Object[] {
                idx.forID(fattribs[0]), idx.forID(fattribs[1]),
                idx.forID(fattribs[2])
        };
    }

    /**
     * @return the fnormals
     */
    public List<Vec3D> getFaceNormals() {
        return fnormals.getItems();
    }

    /**
     * @return the faces
     */
    public List<AttributedFace> getFaces() {
        return faces;
    }

    public List<AttributedFace> getFacesForVertex(Vec3D v) {
        List<AttributedFace> vfaces = null;
        int id = vertices.getID(v);
        if (id != -1) {
            vfaces = new ArrayList<AttributedFace>(2);
            for (AttributedFace f : faces) {
                if (f.a == id || f.b == id || f.c == id) {
                    vfaces.add(f);
                }
            }
        }
        return vfaces;
    }

    public final Vec3D[] getFaceVertices(AttributedFace f, Vec3D[] verts) {
        if (verts != null) {
            verts[0] = vertices.forID(f.a);
            verts[1] = vertices.forID(f.b);
            verts[2] = vertices.forID(f.c);
            return verts;
        } else {
            return new Vec3D[] {
                    vertices.forID(f.a), vertices.forID(f.b),
                    vertices.forID(f.c)
            };
        }
    }

    public List<Vec3D> getNeighborsForVertexID(int id, List<Vec3D> neighbors) {
        List<AttributedEdge> vedges = getEdgesForVertexID(id);
        if (vedges.size() > 0) {
            if (neighbors == null) {
                neighbors = new ArrayList<Vec3D>();
            } else {
                neighbors.clear();
            }
            for (AttributedEdge e : vedges) {
                neighbors.add(vertices.forID((e.a == id) ? e.b : e.a));
            }
        } else if (neighbors != null) {
            neighbors.clear();
        }
        return neighbors;
    }

    public final int getNumFaces() {
        return faces.size();
    }

    public final int getNumVertices() {
        return vertices.size();
    }

    public float getVertexDelta() {
        return vertices.getDelta();
    }

    /**
     * @return the vertices
     */
    public List<Vec3D> getVertices() {
        return vertices.getItems();
    }

    public List<Vec3D> getVerticesForIDs(List<Vec3D> verts, int... ids) {
        if (verts == null) {
            verts = new ArrayList<Vec3D>(ids.length);
        }
        for (int id : ids) {
            verts.add(vertices.forID(id));
        }
        return verts;
    }

    protected final int indexFaceEdge(AttributedFace f, int a, int b) {
        final AttributedEdge e1 = new AttributedEdge(a, b);
        final AttributedEdge e2 = new AttributedEdge(b, a);
        ItemIndex<Object> edges = getAttributeIndex(ATTR_EDGES);
        final int id1 = edges.getID(e1);
        final int id2 = edges.getID(e2);
        if (id1 != -1) {
            ((AttributedEdge) edges.forID(id1)).addFace(f);
            return id1;
        } else if (id2 != -1) {
            ((AttributedEdge) edges.forID(id2)).addFace(f);
            return id2;
        } else {
            e1.addFace(f);
            return edges.index(e1);
        }
    }

    public IsectData3D intersectsRay(Ray3D ray) {
        TriangleIntersector intersector = new TriangleIntersector();
        Triangle3D tri = intersector.getTriangle();
        Vec3D[] v = null;
        for (AttributedFace f : faces) {
            v = getFaceVertices(f, v);
            tri.set(v[0], v[1], v[2]);
            if (intersector.intersectsRay(ray)) {
                return intersector.getIntersectionData();
            }
        }
        return null;
    }

    public void rebuildVertexIndex() {
        SpatialIndex newVerts = new SpatialIndex(vertices.getDelta());
        Vec3D[] v = null;
        for (AttributedFace f : faces) {
            v = getFaceVertices(f, v);
            f.a = newVerts.index(v[0]);
            f.b = newVerts.index(v[1]);
            f.c = newVerts.index(v[2]);
        }
        vertices = newVerts;
    }

    public IndexedTriangleMesh removeFace(AttributedFace f) {
        faces.remove(f);
        return this;
    }

    public IndexedTriangleMesh setVertexDelta(float delta) {
        vertices.setDelta(delta);
        return this;
    }

    public IndexedTriangleMesh smooth() {
        HashMap<Integer, Vec3D> lapIndex = new HashMap<Integer, Vec3D>();
        List<Vec3D> neighbors = null;
        for (int i = 0, numV = getNumVertices(); i < numV; i++) {
            neighbors = getNeighborsForVertexID(i, neighbors);
            if (neighbors != null && neighbors.size() > 0) {
                Vec3D l = new Vec3D();
                for (Vec3D n : neighbors) {
                    l.addSelf(n);
                }
                l.scaleSelf(1f / neighbors.size());
                lapIndex.put(i, l);
            }
        }
        SpatialIndex newVerts = new SpatialIndex(vertices.getDelta());
        for (Iterator<AttributedFace> i = faces.iterator(); i.hasNext();) {
            AttributedFace f = i.next();
            f.a = newVerts.index(lapIndex.get(f.a));
            f.b = newVerts.index(lapIndex.get(f.b));
            f.c = newVerts.index(lapIndex.get(f.c));
            if (f.a == f.b || f.a == f.c || f.b == f.c) {
                i.remove();
            }
        }
        vertices = newVerts;
        computeEdges();
        return this;
    }

    public IndexedTriangleMesh subdivide(NewSubdivStrategy strategy) {
        Vec3D[] v = null;
        List<Vec3D[]> splitFaces = new ArrayList<Vec3D[]>();
        for (AttributedFace f : new ArrayList<AttributedFace>(faces)) {
            removeFace(f);
            v = getFaceVertices(f, v);
            for (Vec3D[] fverts : strategy.subdivideTriangle(v[0], v[1], v[2],
                    splitFaces)) {
                addFace(fverts[0], fverts[1], fverts[2], null);
            }
            splitFaces.clear();
        }
        rebuildVertexIndex();
        return this;
    }

    @Override
    public String toString() {
        return String.format("vertices: %d, faces: %d", getNumVertices(),
                getNumFaces());
    }
}
TOP

Related Classes of toxi.newmesh.IndexedTriangleMesh

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.