Package graphics.mesh

Source Code of graphics.mesh.ObjMesh$Face

package graphics.mesh;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import org.lwjgl.BufferUtils;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import static org.lwjgl.opengl.GL15.*;

/**
* ObjMesh is a representation of an object in 3d-space.
* (Vertices, texture coordinates, normals)
*
* It is loaded from .obj
*
* Obj-file must have vertices, texture-coordinates, and normals (v/vt/vn)
* If the file has a material library specified, it will be automatically loaded and set to defaultMaterial.
* The mesh is automatically converted to triangles.
*
* @author simokr
*/
public class ObjMesh extends Mesh{
  /**
   * Loads the mesh from .obj of given path and does all the necessary things to have it renderable.
   *
   * @param path path to the .obj
   * @param saveVBO if set to true, VBO data (in RAM) won't be cleared afterwards.
   * @return success
   */
  public boolean create(String path, boolean saveVBO){
    if(this.isReady())
      this.free();
   
    if(path == null || path.isEmpty()){
      System.err.println("Empty Mesh path");
      return false;
    }
 
    path = path.trim();
   
    /* Get file contents */
    ArrayList<String> file = readFile(path);
                if(file.isEmpty()){
                        System.err.println("Failed to load " + path);
                        return false;
                }
   
    StringBuffer feedback = new StringBuffer();
 
    /* Parse file and generate VBO contents */
    FloatBuffer data = this.parseObj(file, feedback, true);
    FloatBuffer finalData;
    if(data == null){
      System.err.println(path+": "+feedback);
      return false;
    }
   
    data.rewind();
    int indices = data.remaining()/9;
    int keyframes = 1;
   
    String filePath = path.substring(0, path.lastIndexOf("."));
    String fileExt = path.substring(path.lastIndexOf("."));
    ArrayList<FloatBuffer> kfBuffers = new ArrayList<>();
   
    while(!(file = readFile(filePath+"_kf_"+keyframes+fileExt)).isEmpty()){
      FloatBuffer kfData = this.parseObj(file, feedback, false);
      if(kfData == null){
        System.err.println(path+": "+feedback);
        break;
      }
      kfBuffers.add(kfData);
      keyframes++;
    }
   
    if(keyframes > 1){
      float[] temp = new float[3];
      int size = indices*3+indices*6*keyframes;
      finalData = BufferUtils.createFloatBuffer(size);

      // XYZ
      data.position(0);
      for(int i=0;i<indices;i++){
        data.get(temp, 0, 3);
        finalData.put(temp);
      }
     
      // KF XYZ
      for(FloatBuffer kf:kfBuffers){
        kf.position(0);
        for(int i=0;i<indices;i++){
          kf.get(temp, 0, 3);
          finalData.put(temp);
        }
      }
     
      // UV
      data.position(indices*3);
      for(int i=0;i<indices;i++){
        data.get(temp, 0, 2);
        finalData.put(temp, 0, 2);
      }
     
      // NORMAL
      data.position(indices*5);
      for(int i=0;i<indices;i++){
        data.get(temp, 0, 3);
        finalData.put(temp);
      }
     
      // KF NORMAL
      for(FloatBuffer kf:kfBuffers){
        kf.position(indices*5);
        for(int i=0;i<indices;i++){
          kf.get(temp, 0, 3);
          finalData.put(temp);
        }
      }
     
      // ID
      data.position(indices*8);
      for(int i=0;i<indices;i++){
        data.get(temp, 0, 1);
        finalData.put(temp[0]);
      }
     
      finalData.flip();
      System.out.println("Loaded "+path+" with "+keyframes+" keyframes, size in memory: "+finalData.remaining()*4/1024+"kB");
    }
    else
      finalData = data;

    /* Create a renderable Mesh */
    if(!super.create(finalData, GL_STATIC_DRAW, true, keyframes, indices)){
      System.err.println("Failed to load OBJ: " + path);
    }
   
    return true;
  }
 
  /**
   * Parses the Obj data to drawable format
   *
   * @param data Obj file split to Vector by newlines
   * @param feedback Details of error if return value is false
   * @return success
   */
  private FloatBuffer parseObj(ArrayList<String> data, StringBuffer feedback, boolean createDefaultMaterial){
    String[] str;
    String materialLib = new String();
    int strCount = 0,lineNum = 0, currentMtl=0;
    ArrayList<Vector3f> objVertices = new ArrayList<>();
    ArrayList<Vector2f> objTexcoords = new ArrayList<>();
    ArrayList<Vector3f> objNormals = new ArrayList<>();
    ArrayList<ArrayList<Face>> objFaces = new ArrayList<>();
    ArrayList<String> materials = new ArrayList<>();
    /* Populate Vectors */
    for(String line : data){
      lineNum++;
     
      line = line.trim();
      if(line.isEmpty()){
        continue;
      }
     
      str = line.split("\\s+");
      strCount = str.length - 1;
     
      if(line.startsWith("v ")){
        /* Vertex */
        if(strCount < 3){
          feedback.append("Too few floats in vertex. ").append(strCount).append("(3) line: ").append(lineNum);
          return null;
        }
       
        objVertices.add(this.parseToVec3f(str));
      }
      else if(line.startsWith("vt ")){
        /* Texture coordinate */
        if(strCount < 2){
          feedback.append("Too few floats in texture coordinate. ").append(strCount).append("(2) line: ").append(lineNum);
          return null;
        }
        objTexcoords.add(this.parseToVec2f(str));
      }
      else if(line.startsWith("vn ")){
        /* Normal */
        if(strCount < 3){
          feedback.append("Too few floats in normal. ").append(strCount).append("(3) line: ").append(lineNum);
          return null;
        }
       
        objNormals.add(this.parseToVec3f(str));
      }
      else if(line.startsWith("f ")){
        /* Face */
        if(strCount < 3){
          feedback.append("Too few indice in face. ").append(strCount).append("(>=3) line: ").append(lineNum);
          return null;
        }
       
        ArrayList<Face> faceVec = new ArrayList<>();
       
        for (int i = 1; i < strCount+1; i++) {
          String index[] = str[i].split("/");
          if(index.length < 3){
            feedback.append("Face is missing data. Needs v/vt/vn. ").append(index.length).append("(3) line: ").append(lineNum);
            return null;
          }
          float material = currentMtl;
          Face tempFace = this.parseToFace(index, material);
          if(tempFace != null)
            faceVec.add(tempFace);
        }
        /* Skip face if it has less than 3 vertices */
        if(faceVec.size() > 2)
          objFaces.add(faceVec);
      }
      else if(line.startsWith("usemtl ")){
        if(!materials.contains(str[1])){
          materials.add(str[1]);
        }
        currentMtl = materials.lastIndexOf(str[1]);
      }
      else if(line.startsWith("mtllib ")){
        if(materialLib.isEmpty())
          materialLib = str[1].trim();
      }
    }
   
    int vertices = objVertices.size();
    int textureCoordinates = objTexcoords.size();
    int normals = objNormals.size();
    int faceCount = 0;
    int vertexCount = 0;
   
    /* Check face index validity */
    for (ArrayList<Face> vector : objFaces) {
      vertexCount += (vector.size()-2)*3;
      for (Face face : vector) {
        faceCount++;
        face.v = this.getCorrectIndex(face.v, vertices);
        face.vt = this.getCorrectIndex(face.vt, textureCoordinates);
        face.vn = this.getCorrectIndex(face.vn, normals);
       
        if(face.v >= vertices || face.v < 0){
          feedback.append("Face ").append(faceCount).append(": vertex index doesn't exist ").append(face.v).append("(>=").append(vertices).append(" || < 0).");
          return null;
        }
        else if(face.vt >= textureCoordinates || face.vt < 0){
          feedback.append("Face ").append(faceCount).append(": texture coordinate index doesn't exist ").append(face.vt).append("(>=").append(textureCoordinates).append(" || < 0).");
          return null;
        }
        else if(face.vn >= normals || face.vn < 0){
          feedback.append("Face ").append(faceCount).append(": normal index doesn't exist ").append(face.vn).append("(>=").append(normals).append(" || < 0).");
          return null;
        }
       
      }
     
    }
   
    FloatBuffer buffer = this.generateVBO(objVertices,objTexcoords,objNormals,objFaces,vertexCount);
 
    if(createDefaultMaterial){
      this.createDefaultMaterial(materials,materialLib);
    }
   
    return buffer;
  }
 
  /**
   * Generates VBO content from the parsed OBJ
   *
   * @param objV vertices
   * @param objVT texture coordinates
   * @param objVN normals
   * @param objF faces
   * @param vertexCount number of indices in the mesh
   * @return true
   */
  private FloatBuffer generateVBO(ArrayList<Vector3f> objV, ArrayList<Vector2f> objVT, ArrayList<Vector3f> objVN, ArrayList<ArrayList<Face>> objF, int vertexCount){
    /* v(3)+vt(2)+vn(3)+texid(1) */
    int floatsPerVertex = 3+2+3+1;
   
    FloatBuffer temporaryBuffer = BufferUtils.createFloatBuffer(vertexCount*floatsPerVertex);
    FloatBuffer posBuffer = BufferUtils.createFloatBuffer(vertexCount*3);
    FloatBuffer texcBuffer = BufferUtils.createFloatBuffer(vertexCount*2);
    FloatBuffer norBuffer = BufferUtils.createFloatBuffer(vertexCount*3);
    FloatBuffer idBuffer = BufferUtils.createFloatBuffer(vertexCount*1);
   
    for (ArrayList<Face> vector : objF) {
      /* Break polygons to triangles */
      for (int i = 0; i < vector.size()-2; i++) {
        for(int vertex = 0; vertex<3; vertex++) {
          int index = (vertex == 0)?0:i+vertex;
         
          Face face = vector.get(index);
         
          objV.get(face.v).store(posBuffer);
          objVT.get(face.vt).store(texcBuffer);
          objVN.get(face.vn).store(norBuffer);
          idBuffer.put(face.m);
        }
      }
    }
    posBuffer.flip();
    texcBuffer.flip();
    norBuffer.flip();
    idBuffer.flip();
   
    temporaryBuffer.put(posBuffer);
    temporaryBuffer.put(texcBuffer);
    temporaryBuffer.put(norBuffer);
    temporaryBuffer.put(idBuffer);
   
    temporaryBuffer.flip();
   
    return temporaryBuffer;
  }
 
  private void createDefaultMaterial(ArrayList<String> materials, String materialLib){
    /* If there are no materials in the .obj or loading the .mtl fails, set the default material to use grey textures */
    if(materialLib.isEmpty() || !this.defaultMaterial.loadFromFile(materials,"res/mesh/"+materialLib)){
      for(int i=0;i<materials.size();i++){
        this.defaultMaterial.setTexture(i, "empty");
      }
    }
  }
 
  private int getCorrectIndex(int index, int indexSize){
    return (index > 0)?--index:indexSize+index;
  }
 
  private Vector2f parseToVec2f(String str[]){
    Vector2f vec = new Vector2f();
    vec.set(Float.parseFloat(str[1]), 1.0f-Float.parseFloat(str[2]));
    return vec;
  }
 
  private Vector3f parseToVec3f(String str[]){
    Vector3f vec = new Vector3f();
    vec.set(Float.parseFloat(str[1]), Float.parseFloat(str[2]), Float.parseFloat(str[3]));
    return vec;
  }
 
  private Face parseToFace(String str[], float material){
    for (int i = 0; i < 3; i++) {
      if(str[i].equals(""))
        return null;
    }
   
    Face face = new Face();
   
    face.set(Integer.parseInt(str[0]), Integer.parseInt(str[1]), Integer.parseInt(str[2]), material);
    return face;
  }
 
  public static ArrayList<String> readFile(String path) {
                ArrayList<String> file = new ArrayList<>();
               
                try {
                        BufferedReader reader = new BufferedReader(new FileReader(path));
                       
                        // Temporary string to store a line from file
                        String string;
                        while((string = reader.readLine()) != null) {
                                file.add(string);
                        }
                       
                        reader.close();
                       
                } catch(IOException e) {
                        // TODO error handling
                }
               
               
                return file;
        }
 
  private class Face {
    public int v,vt,vn;
    public float m;
    public Face() {
      v = 0;
      vt = 0;
      vn = 0;
      m = 0;
    }
    public Face(int v, int vt, int vn, float m){
      this.v = v;
      this.vt = vt;
      this.vn = vn;
      this.m = m;
    }
    public void set(int v, int vt, int vn, float m){
      this.v = v;
      this.vt = vt;
      this.vn = vn;
      this.m = m;
    }
  }
}
TOP

Related Classes of graphics.mesh.ObjMesh$Face

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.