org.shaman.terrain.ArbaroToJmeExporter.java Source code

Java tutorial

Introduction

Here is the source code for org.shaman.terrain.ArbaroToJmeExporter.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.shaman.terrain;

import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer;
import com.jme3.texture.Texture;
import com.jme3.util.BufferUtils;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.arbaro.export.*;
import net.sourceforge.arbaro.mesh.*;
import net.sourceforge.arbaro.params.FloatFormat;
import net.sourceforge.arbaro.transformation.Vector;
import net.sourceforge.arbaro.tree.DefaultTreeTraversal;
import net.sourceforge.arbaro.tree.Leaf;
import net.sourceforge.arbaro.tree.Tree;
import org.apache.commons.lang3.ArrayUtils;

/**
 *
 * @author Sebastian Weiss
 */
public class ArbaroToJmeExporter extends MeshExporter {
    private static final Logger LOG = Logger.getLogger(ArbaroToJmeExporter.class.getName());
    private static final float BARK_UV_SCALING = 1;

    long vertexProgressCount = 0;
    long faceProgressCount = 0;
    Mesh mesh;
    LeafMesh leafMesh;
    Tree tree;
    long smoothingGroup;
    int vertexOffset;
    int uvVertexOffset;

    private final AssetManager assetManager;
    private ArrayList<Vector3f> stemVertices;
    private ArrayList<Vector3f> stemNormals;
    private ArrayList<Vector2f> stemUVs;
    private ArrayList<Integer> stemIndices;
    private ArrayList<Vector3f> leafVertices;
    private ArrayList<Vector3f> leafNormals;
    private ArrayList<Vector2f> leafUVs;
    private ArrayList<Vector2f> leafTmpUVs;
    private ArrayList<Integer> leafIndices;
    private com.jme3.scene.Node spatial;
    private String barkTex;
    private String leafTex;
    private ColorRGBA barkCol;
    private boolean writeLeaves = true;
    private float leafRotation = 0;

    public ArbaroToJmeExporter(AssetManager assetManager, Tree tree, MeshGenerator meshGenerator) {
        super(meshGenerator);
        this.assetManager = assetManager;
        this.tree = tree;
        LOG.info("exporter created");
        progress = new Progress();
    }

    public void setBarkTexture(String tex) {
        LOG.log(Level.INFO, "use bark texture {0}", tex);
        barkTex = tex;
    }

    public void setBarkColor(ColorRGBA col) {
        barkCol = col;
    }

    public void setLeafTexture(String tex) {
        LOG.log(Level.INFO, "use leaf texture {0}", tex);
        leafTex = tex;
    }

    public void writeLeaves(boolean leaves) {
        LOG.log(Level.INFO, "write leaves: {0}", leaves);
        writeLeaves = leaves;
    }

    public void setLeafRotation(float rotDegree) {
        LOG.log(Level.INFO, "use leaf rotation of {0}", rotDegree);
        leafRotation = FastMath.DEG_TO_RAD * rotDegree;
    }

    public com.jme3.scene.Spatial getSpatial() {
        return spatial;
    }

    @Override
    public void doWrite() {
        LOG.info("doWrite");

        //create buffers
        stemVertices = new ArrayList<>();
        stemNormals = new ArrayList<>();
        stemUVs = new ArrayList<>();
        stemIndices = new ArrayList<>();
        leafVertices = new ArrayList<>();
        leafNormals = new ArrayList<>();
        leafUVs = new ArrayList<>();
        leafTmpUVs = new ArrayList<>();
        leafIndices = new ArrayList<>();

        smoothingGroup = 1;

        mesh = meshGenerator.createStemMeshByLevel(tree, progress);
        leafMesh = meshGenerator.createLeafMesh(tree, meshGenerator.getUseQuads());

        //stem
        writeStemVertices();
        writeStemFaces();

        //leaves
        if (writeLeaves) {
            writeLeafVertices();
            writeLeafFaces();
            duplicateLeaves();
        }

        //create normals
        createNormals(stemVertices, stemNormals, stemIndices);
        createNormals(leafVertices, leafNormals, leafIndices);

        //create spatial
        spatial = new com.jme3.scene.Node("tree");

        com.jme3.scene.Mesh stemMesh = new com.jme3.scene.Mesh();
        stemMesh.setBuffer(VertexBuffer.Type.Position, 3,
                BufferUtils.createFloatBuffer(stemVertices.toArray(new Vector3f[stemVertices.size()])));
        stemMesh.setBuffer(VertexBuffer.Type.Normal, 3,
                BufferUtils.createFloatBuffer(stemNormals.toArray(new Vector3f[stemNormals.size()])));
        stemMesh.setBuffer(VertexBuffer.Type.TexCoord, 2,
                BufferUtils.createFloatBuffer(stemUVs.toArray(new Vector2f[stemUVs.size()])));
        stemMesh.setBuffer(VertexBuffer.Type.Index, 3,
                ArrayUtils.toPrimitive(stemIndices.toArray(new Integer[stemIndices.size()])));
        stemMesh.updateCounts();
        stemMesh.updateBound();
        Geometry geom = new Geometry("Stem", stemMesh);
        //      Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        Material mat = new Material(assetManager, "org/shaman/terrain/shader/Tree.j3md");
        mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        geom.setQueueBucket(RenderQueue.Bucket.Transparent);
        if (barkTex != null) {
            Texture tex = assetManager.loadTexture(barkTex);
            tex.setWrap(Texture.WrapMode.Repeat);
            mat.setTexture("DiffuseMap", tex);
        } else {
            mat.setColor("Diffuse", barkCol != null ? barkCol : ColorRGBA.Brown);
            mat.setBoolean("UseMaterialColors", true);
        }
        geom.setMaterial(mat);
        spatial.attachChild(geom);

        if (writeLeaves) {
            com.jme3.scene.Mesh leafMesh = new com.jme3.scene.Mesh();
            leafMesh.setBuffer(VertexBuffer.Type.Position, 3,
                    BufferUtils.createFloatBuffer(leafVertices.toArray(new Vector3f[leafVertices.size()])));
            leafMesh.setBuffer(VertexBuffer.Type.Normal, 3,
                    BufferUtils.createFloatBuffer(leafNormals.toArray(new Vector3f[leafNormals.size()])));
            leafMesh.setBuffer(VertexBuffer.Type.TexCoord, 2,
                    BufferUtils.createFloatBuffer(leafUVs.toArray(new Vector2f[leafUVs.size()])));
            leafMesh.setBuffer(VertexBuffer.Type.Index, 3,
                    ArrayUtils.toPrimitive(leafIndices.toArray(new Integer[leafIndices.size()])));
            leafMesh.updateCounts();
            leafMesh.updateBound();
            geom = new Geometry("Leaves", leafMesh);
            //         mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
            mat = new Material(assetManager, "org/shaman/terrain/shader/Tree.j3md");
            if (leafTex != null) {
                mat.setTexture("DiffuseMap", assetManager.loadTexture(leafTex));
                mat.getAdditionalRenderState().setAlphaTest(true);
                mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
                mat.setFloat("AlphaDiscardThreshold", 0.5f);
                geom.setQueueBucket(RenderQueue.Bucket.Transparent);
            } else {
                mat.setColor("Diffuse", ColorRGBA.Green);
                mat.setBoolean("UseMaterialColors", true);
            }
            geom.setMaterial(mat);
            spatial.attachChild(geom);
        }

    }

    private void createNormals(List<Vector3f> positions, List<Vector3f> normals, List<Integer> indices) {
        normals.clear();
        for (int i = 0; i < positions.size(); ++i) {
            normals.add(new Vector3f());
        }
        for (int i = 0; i < indices.size() / 3; ++i) {
            int a = indices.get(3 * i);
            int b = indices.get(3 * i + 1);
            int c = indices.get(3 * i + 2);
            Vector3f A = positions.get(a);
            Vector3f B = positions.get(b);
            Vector3f C = positions.get(c);
            Vector3f N = (B.subtract(A)).cross((C.subtract(A)));
            //N.normalizeLocal();
            normals.get(a).addLocal(N);
            normals.get(b).addLocal(N);
            normals.get(c).addLocal(N);
        }
        for (Vector3f n : normals) {
            n.normalizeLocal();
        }
    }

    private void writeStemVertices() {

        for (Enumeration vertices = mesh.allVertices(false); vertices.hasMoreElements();) {
            Vertex vertex = (Vertex) vertices.nextElement();
            stemVertices.add(new Vector3f((float) vertex.point.getX(), (float) vertex.point.getY(),
                    (float) vertex.point.getZ()));
            stemUVs.add(new Vector2f((float) vertex.point.u * BARK_UV_SCALING,
                    (float) vertex.point.v * BARK_UV_SCALING));
        }
        //      for (Enumeration vertices = mesh.allVertices(true);
        //         vertices.hasMoreElements();) {
        //            UVVector vertex = (UVVector)vertices.nextElement();
        //            stemUVs.add(new Vector2f((float) vertex.u * BARK_UV_SCALING, (float) vertex.v * 256));
        //      }
    }

    private void writeStemFaces() {
        // output mesh triangles
        vertexOffset = 0;
        //boolean separate_trunk = false;
        for (int stemLevel = 0; stemLevel < tree.getLevels(); stemLevel++) {

            for (Enumeration parts = mesh.allParts(stemLevel); parts.hasMoreElements();) {

                MeshPart mp = (MeshPart) parts.nextElement();
                uvVertexOffset = 1 + mesh.firstUVIndex(mp.getStem().getLevel());

                Enumeration faces = mp.allFaces(mesh, vertexOffset, false);
                Enumeration uvFaces = mp.allFaces(mesh, uvVertexOffset, true);

                while (faces.hasMoreElements()) {
                    Face face = (Face) faces.nextElement();
                    Face uvFace = (Face) uvFaces.nextElement();
                    //face can be a triangle or a quad
                    for (int i = 2; i < face.points.length; ++i) {
                        stemIndices.add((int) face.points[0]);
                        stemIndices.add((int) face.points[i]);
                        stemIndices.add((int) face.points[i - 1]);
                    }
                }

                vertexOffset += mp.vertexCount();

                uvVertexOffset += mp.uvCount();

                incProgressCount(AbstractExporter.MESH_PROGRESS_STEP);
            }
        }
    }

    private void writeLeafVertices() {
        //Write positions
        LeafVertexWriter vertexExporter = new LeafVertexWriter(tree, this, leafMesh, vertexOffset, "v");
        tree.traverseTree(vertexExporter);
        vertexOffset = vertexExporter.leafVertexOffset;
        //write uvs
        if (leafMesh.isFlat()) {
            for (int i = 0; i < leafMesh.getShapeVertexCount(); i++) {
                UVVector v = leafMesh.shapeUVAt(i);
                float x = (float) v.u;
                float y = (float) v.v;
                //            leafUVs.add(new Vector2f((float) v.u, (float) v.v));
                if (leafRotation != 0) {
                    //transform it
                    x -= 0.5f;
                    y -= 0.5f;
                    float x2 = FastMath.cos(leafRotation) * x - FastMath.sin(leafRotation) * y;
                    float y2 = FastMath.sin(leafRotation) * x + FastMath.cos(leafRotation) * y;
                    x = x2 + 0.5f;
                    y = y2 + 0.5f;
                }
                leafTmpUVs.add(new Vector2f(x, y));
            }
        }

        LOG.info("leave counts: " + leafVertices.size() + "," + leafUVs.size());
    }

    private void writeLeafFaces() {
        LeafFaceWriter faceExporter = new LeafFaceWriter(tree, this, leafMesh, vertexOffset, uvVertexOffset,
                smoothingGroup);
        tree.traverseTree(faceExporter);
        LOG.info("leave triangle count: " + leafIndices.size() / 3);
    }

    private void duplicateLeaves() {
        ArrayList<Vector3f> tmp = leafVertices;
        int count = tmp.size();
        leafVertices = new ArrayList<>(leafVertices.size() * 2);
        for (Vector3f v : tmp)
            leafVertices.add(v.clone());
        for (Vector3f v : tmp)
            leafVertices.add(v.clone());

        tmp = leafNormals;
        leafNormals = new ArrayList<>(leafNormals.size() * 2);
        for (Vector3f v : tmp)
            leafNormals.add(v.clone());
        for (Vector3f v : tmp)
            leafNormals.add(v.clone());

        ArrayList<Vector2f> tmp2 = leafUVs;
        leafUVs = new ArrayList<>(leafUVs.size() * 2);
        for (Vector2f v : tmp2)
            leafUVs.add(v.clone());
        for (Vector2f v : tmp2)
            leafUVs.add(v.clone());

        ArrayList<Integer> index = leafIndices;
        leafIndices = new ArrayList<>(index.size() * 2);
        for (int i = 0; i < index.size(); i += 3) {
            int a = index.get(i);
            int b = index.get(i + 1);
            int c = index.get(i + 2);
            leafIndices.add(a);
            leafIndices.add(b);
            leafIndices.add(c);
            leafIndices.add(a + count);
            leafIndices.add(c + count);
            leafIndices.add(b + count);
        }
    }

    private class LeafWriterBase extends DefaultTreeTraversal {
        Progress progress;
        LeafMesh leafMesh;
        AbstractExporter exporter;
        public int leafVertexOffset;
        PrintWriter w;
        long leavesProgressCount = 0;
        Tree tree;

        final NumberFormat fmt = FloatFormat.getInstance();

        /**
         * 
         */
        public LeafWriterBase(Tree tree, LeafMesh leafMesh, AbstractExporter exporter, int leafVertexOffset) {
            super();
            this.exporter = exporter;
            this.w = exporter.getWriter();
            this.leafMesh = leafMesh;
            this.leafVertexOffset = leafVertexOffset;
        }

        public boolean enterTree(Tree tree) {
            this.tree = tree;
            return true;
        }
    }

    private class LeafVertexWriter extends LeafWriterBase {
        String type;
        long vertexProgressCount = 0;

        /**
         * @param pw
         * @param leafMesh
         * @param leafVertexOffset
         */
        public LeafVertexWriter(Tree tree, AbstractExporter exporter, LeafMesh leafMesh, int leafVertexOffset,
                String type) {
            super(tree, leafMesh, exporter, leafVertexOffset);
            this.type = type;
        }

        public boolean visitLeaf(Leaf l) {
            for (int i = 0; i < leafMesh.getShapeVertexCount(); i++) {

                if ("v".equals(type)) {
                    Vector v = l.getTransformation().apply(leafMesh.shapeVertexAt(i).point);
                    leafVertices.add(new Vector3f((float) v.getX(), (float) v.getY(), (float) v.getZ()));
                    leafUVs.add(new Vector2f());
                } else {
                    Vector v = l.getTransformation().apply(leafMesh.shapeVertexAt(i).normal);
                    leafNormals.add(new Vector3f((float) v.getX(), (float) v.getY(), (float) v.getZ()));
                }
            }

            return true;
        }

        //      private void writeVertex(Vector v, String type) {
        //         w.println(type+" "
        //               +fmt.format(v.getX())+" "
        //               +fmt.format(v.getZ())+" "
        //               +fmt.format(v.getY()));
        //      }

    }

    private class LeafFaceWriter extends LeafWriterBase {
        long faceProgressCount = 0;

        long smoothingGroup;
        boolean outputLeafUVs = true;
        int offset = 0;

        /**
         * @param pw
         * @param leafMesh
         * @param leafVertexOffset
         */
        public LeafFaceWriter(Tree tree, AbstractExporter exporter, LeafMesh leafMesh, int leafVertexOffset,
                int uvVertexOffset, long smoothingGroup) {
            super(tree, leafMesh, exporter, leafVertexOffset);

            this.outputLeafUVs = true;
        }

        public boolean visitLeaf(Leaf l) {

            for (int i = 0; i < leafMesh.getShapeFaceCount(); i++) {
                Face face = leafMesh.shapeFaceAt(i);
                writeFace(face);
            }
            offset += leafMesh.getShapeVertexCount();

            return true;
        }

        private void writeFace(Face f) {
            for (int i = 2; i < f.points.length; ++i) {
                leafIndices.add((int) f.points[0] + offset);
                leafIndices.add((int) f.points[i] + offset);
                leafIndices.add((int) f.points[i - 1] + offset);
            }
            if (!leafTmpUVs.isEmpty()) {
                for (int i = 0; i < f.points.length; ++i) {
                    leafUVs.get((int) f.points[i] + offset).set(leafTmpUVs.get((int) f.points[i]));
                }
            }
        }
    }
}