diff --git a/Plugins/src/mini/scene/plugins/OBJLoader.java b/Plugins/src/mini/scene/plugins/OBJLoader.java index 588bed6..2b1cd50 100644 --- a/Plugins/src/mini/scene/plugins/OBJLoader.java +++ b/Plugins/src/mini/scene/plugins/OBJLoader.java @@ -38,9 +38,9 @@ */ public final class OBJLoader implements AssetLoader { - protected final List verts = new ArrayList<>(); + protected final List vertices = new ArrayList<>(); protected final List texCoords = new ArrayList<>(); - protected final List norms = new ArrayList<>(); + protected final List normals = new ArrayList<>(); protected final List faces = new ArrayList<>(); protected final Map> matFaces = new HashMap<>(); @@ -50,9 +50,9 @@ public final class OBJLoader implements AssetLoader { protected final Map vertIndexMap = new HashMap<>(100); protected final Map indexVertMap = new HashMap<>(100); - protected int curIndex = 0; + protected int currentIndex = 0; protected int objectIndex = 0; - protected int geomIndex = 0; + protected int geometryIndex = 0; protected Scanner scan; protected ModelKey key; @@ -60,11 +60,11 @@ public final class OBJLoader implements AssetLoader { protected String objName; protected Node objNode; - private List vertList = new ArrayList<>(); + private List vertexList = new ArrayList<>(); private AssetManager assetManager; protected static class Face { - Vertex[] verticies; + Vertex[] vertices; } public Object load(AssetInfo info) throws IOException { @@ -81,8 +81,7 @@ public Object load(AssetInfo info) throws IOException { String folderName = key.getFolder(); String ext = key.getExtension(); objName = objName.substring(0, objName.length() - ext.length() - 1); - if (folderName != null && folderName.length() > 0 && objName - .startsWith(folderName)) { + if (folderName != null && folderName.length() > 0 && objName.startsWith(folderName)) { objName = objName.substring(folderName.length()); } @@ -105,19 +104,19 @@ public Object load(AssetInfo info) throws IOException { } } } else if (faces.size() > 0) { - // generate final geometry + // Generate final geometry. Geometry geom = createGeometry(faces, null); objNode.attachChild(geom); } - // only 1 geometry, so no need to send node + // Only 1 geometry, so no need to send node. return objNode.getQuantity() == 1 ? objNode.getChild(0) : objNode; } public void reset() { - verts.clear(); + vertices.clear(); texCoords.clear(); - norms.clear(); + normals.clear(); faces.clear(); matFaces.clear(); @@ -126,58 +125,46 @@ public void reset() { currentMatName = null; matList = null; - curIndex = 0; - geomIndex = 0; + currentIndex = 0; + geometryIndex = 0; scan = null; } - private void findVertexIndex(Vertex vert) { - Integer index = vertIndexMap.get(vert); + private void findVertexIndex(Vertex vertex) { + Integer index = vertIndexMap.get(vertex); + if (index != null) { - vert.index = index; - } else { - vert.index = curIndex++; - vertIndexMap.put(vert, vert.index); - indexVertMap.put(vert.index, vert); + vertex.index = index; + return; } + + vertex.index = currentIndex++; + vertIndexMap.put(vertex, vertex.index); + indexVertMap.put(vertex.index, vertex); } - private Face[] quadToTriangle(Face f) { - assert f.verticies.length == 4; + private Face[] quadToTriangles(Face f) { + assert f.vertices.length == 4; - Face[] t = new Face[]{new Face(), new Face()}; - t[0].verticies = new Vertex[3]; - t[1].verticies = new Vertex[3]; + Face[] t = new Face[] { new Face(), new Face() }; - Vertex v0 = f.verticies[0]; - Vertex v1 = f.verticies[1]; - Vertex v2 = f.verticies[2]; - Vertex v3 = f.verticies[3]; + Vertex v0 = f.vertices[0]; + Vertex v1 = f.vertices[1]; + Vertex v2 = f.vertices[2]; + Vertex v3 = f.vertices[3]; - // find the pair of verticies that is closest to each over - // v0 and v2 - // OR - // v1 and v3 + // Find the pair of verticies that is closest to each other. + // v0 and v2 OR v1 and v3. float d1 = v0.v.distanceSquared(v2.v); float d2 = v1.v.distanceSquared(v3.v); if (d1 < d2) { - // put an edge in v0, v2 - t[0].verticies[0] = v0; - t[0].verticies[1] = v1; - t[0].verticies[2] = v3; - - t[1].verticies[0] = v1; - t[1].verticies[1] = v2; - t[1].verticies[2] = v3; + // Put an edge in v0, v2. + t[0].vertices = new Vertex[] { v0, v1, v3 }; + t[1].vertices = new Vertex[] { v1, v2, v3 }; } else { - // put an edge in v1, v3 - t[0].verticies[0] = v0; - t[0].verticies[1] = v1; - t[0].verticies[2] = v2; - - t[1].verticies[0] = v0; - t[1].verticies[1] = v2; - t[1].verticies[2] = v3; + // Put an edge in v1, v3. + t[0].vertices = new Vertex[] { v0, v1, v2 }; + t[1].vertices = new Vertex[] { v0, v2, v3 }; } return t; @@ -185,7 +172,7 @@ private Face[] quadToTriangle(Face f) { private void readFace() { Face f = new Face(); - vertList.clear(); + vertexList.clear(); String line = scan.nextLine().trim(); String[] verticies = line.split("\\s+"); @@ -196,79 +183,79 @@ private void readFace() { String[] split = vertex.split("/"); if (split.length == 1) { - v = Integer.parseInt(split[0].trim()); + v = readVertex(split[0]); } else if (split.length == 2) { - v = Integer.parseInt(split[0].trim()); - vt = Integer.parseInt(split[1].trim()); + v = readVertex(split[0]); + vt = readVertex(split[1]); } else if (split.length == 3 && !split[1].equals("")) { - v = Integer.parseInt(split[0].trim()); - vt = Integer.parseInt(split[1].trim()); - vn = Integer.parseInt(split[2].trim()); + v = readVertex(split[0]); + vt = readVertex(split[1]); + vn = readVertex(split[2]); } else if (split.length == 3) { - v = Integer.parseInt(split[0].trim()); - vn = Integer.parseInt(split[2].trim()); + v = readVertex(split[0]); + vn = readVertex(split[2]); } if (v < 0) { - v = verts.size() + v + 1; + v = vertices.size() + v + 1; } if (vt < 0) { vt = texCoords.size() + vt + 1; } if (vn < 0) { - vn = norms.size() + vn + 1; + vn = normals.size() + vn + 1; } Vertex vx = new Vertex(); - vx.v = verts.get(v - 1); + vx.v = vertices.get(v - 1); if (vt > 0) { vx.vt = texCoords.get(vt - 1); } if (vn > 0) { - vx.vn = norms.get(vn - 1); + vx.vn = normals.get(vn - 1); } - vertList.add(vx); + vertexList.add(vx); } - if (vertList.size() > 4 || vertList.size() <= 2) { + if (vertexList.size() > 4 || vertexList.size() <= 2) { System.err.println("Edge or polygon detected in OBJ. Ignored."); return; } - f.verticies = new Vertex[vertList.size()]; - for (int i = 0; i < vertList.size(); i++) { - f.verticies[i] = vertList.get(i); + f.vertices = new Vertex[vertexList.size()]; + for (int i = 0; i < vertexList.size(); i++) { + f.vertices[i] = vertexList.get(i); } if (matList != null && matFaces.containsKey(currentMatName)) { matFaces.get(currentMatName).add(f); - } else { - faces.add(f); // faces that belong to the default material + return; } + + faces.add(f); // Faces that belong to the default material. } - private Vector3f readVector3() { - Vector3f v = new Vector3f(); + private Integer readVertex(String vertex) { + return Integer.parseInt(vertex.trim()); + } - v.set(Float.parseFloat(scan.next()), - Float.parseFloat(scan.next()), - Float.parseFloat(scan.next())); + private Vector3f readVector3() { + Float x = Float.parseFloat(scan.next()); + Float y = Float.parseFloat(scan.next()); + Float z = Float.parseFloat(scan.next()); - return v; + return new Vector3f(x, y, z); } private Vector2f readVector2() { - Vector2f v = new Vector2f(); + String[] split = scan.nextLine().trim().split("\\s+"); + Float x = Float.parseFloat(split[0].trim()); + Float y = Float.parseFloat(split[1].trim()); - String line = scan.nextLine().trim(); - String[] split = line.split("\\s+"); - v.setX(Float.parseFloat(split[0].trim())); - v.setY(Float.parseFloat(split[1].trim())); - - return v; + return new Vector2f(x, y); } private void loadMtlLib(String name) throws IOException { @@ -286,7 +273,7 @@ private void loadMtlLib(String name) throws IOException { } if (matList != null) { - // create face lists for every material + // Create face lists for every material. for (String matName : matList.keySet()) { matFaces.put(matName, new ArrayList<>()); } @@ -310,35 +297,34 @@ private boolean readLine() throws IOException { String cmd = scan.next(); if (cmd.startsWith("#")) { - // skip entire comment until next line + // Skip entire comment until next line. return nextStatement(); } else if (cmd.equals("v")) { - // vertex position - verts.add(readVector3()); + // Vertex position. + vertices.add(readVector3()); } else if (cmd.equals("vn")) { - // vertex normal - norms.add(readVector3()); + // Vertex normal. + normals.add(readVector3()); } else if (cmd.equals("vt")) { - // texture coordinate + // Texture coordinates. texCoords.add(readVector2()); } else if (cmd.equals("f")) { - // face, can be triangle, quad, or polygon (unsupported) + // Face, can be triangle, quad, or polygon (unsupported). readFace(); } else if (cmd.equals("usemtl")) { - // use material from MTL lib for the following faces + // Use material from MTL lib for the following faces. currentMatName = scan.next(); if (!matList.containsKey(currentMatName)) { throw new IOException("Cannot locate material " + currentMatName + " in MTL file!"); } - } else if (cmd.equals("mtllib")) { - // specify MTL lib to use for this OBJ file + // Specify MTL lib to use for this OBJ file. String mtllib = scan.nextLine().trim(); loadMtlLib(mtllib); } else if (cmd.equals("s") || cmd.equals("g")) { return nextStatement(); } else { - // skip entire command until next line + // Skip entire command until next line. System.err.println("Unknown statement in OBJ! " + cmd); return nextStatement(); } @@ -351,58 +337,57 @@ private Geometry createGeometry(List faceList, String matName) throws IOEx throw new IOException("No geometry data to generate mesh"); } - // Create mesh from the faces + // Create mesh from the faces. Mesh mesh = constructMesh(faceList); - - Geometry geom = new Geometry(objName + "-geom-" + (geomIndex++), mesh); - + Geometry geometry = new Geometry(objName + "-geom-" + (geometryIndex++), mesh); Material material = null; + if (matName != null && matList != null) { - // Get material from material list + // Get material from material list. material = matList.get(matName); } + if (material == null) { - // create default material + // Create default material. material = new Material(assetManager, "MatDefs/Light/Lighting.minid"); material.setFloat("Shininess", 64); } - geom.setMaterial(material); -// if (material.isTransparent()) -// geom.setQueueBucket(Bucket.Transparent); -// else - geom.setQueueBucket(RenderQueue.Bucket.Opaque); + + geometry.setMaterial(material); + geometry.setQueueBucket(RenderQueue.Bucket.Opaque); if (material.getMaterialDef().getName().contains("Lighting") - && mesh.getFloatBuffer(VertexBuffer.Type.Normal) == null) { - System.err.println("OBJ mesh " + geom.getName() + " doesn't contain normals! " - + "It might not display correctly"); + && mesh.getFloatBuffer(VertexBuffer.Type.Normal) == null) { + System.err.println( + "OBJ mesh " + geometry.getName() + " doesn't contain normals! " + "It might not display correctly"); } - return geom; + return geometry; } protected Mesh constructMesh(List faceList) { - Mesh m = new Mesh(); - m.setMode(Mesh.Mode.Triangles); + Mesh mesh = new Mesh(); + mesh.setMode(Mesh.Mode.Triangles); boolean hasTexCoord = false; boolean hasNormals = false; List newFaces = new ArrayList<>(faceList.size()); for (Face f : faceList) { - for (Vertex v : f.verticies) { + for (Vertex v : f.vertices) { findVertexIndex(v); if (!hasTexCoord && v.vt != null) { hasTexCoord = true; } + if (!hasNormals && v.vn != null) { hasNormals = true; } } - if (f.verticies.length == 4) { - Face[] t = quadToTriangle(f); + if (f.vertices.length == 4) { + Face[] t = quadToTriangles(f); newFaces.add(t[0]); newFaces.add(t[1]); } else { @@ -410,92 +395,99 @@ protected Mesh constructMesh(List faceList) { } } - FloatBuffer posBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3); - FloatBuffer normBuf = null; - FloatBuffer tcBuf = null; - - if (hasNormals) { - normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3); - m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf); - } - if (hasTexCoord) { - tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2); - m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf); - } - - IndexBuffer indexBuf; - if (vertIndexMap.size() >= 65536) { - // too many verticies: use intbuffer instead of shortbuffer - IntBuffer ib = BufferUtils.createIntBuffer(newFaces.size() * 3); - m.setBuffer(VertexBuffer.Type.Index, 3, ib); - indexBuf = new IndexIntBuffer(ib); - } else { - ShortBuffer sb = BufferUtils.createShortBuffer(newFaces.size() * 3); - m.setBuffer(VertexBuffer.Type.Index, 3, sb); - indexBuf = new IndexShortBuffer(sb); - } + FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3); + FloatBuffer normalBuffer = hasNormals ? createNormalBuffer(mesh) : null; + FloatBuffer texCoordBuffer = hasTexCoord ? createTextureCoordinateBuffer(mesh) : null; + IndexBuffer indexBuffer = createIndexBuffer(mesh, newFaces); int numFaces = newFaces.size(); for (int i = 0; i < numFaces; i++) { Face f = newFaces.get(i); - if (f.verticies.length != 3) { + + if (f.vertices.length != 3) { continue; } - Vertex v0 = f.verticies[0]; - Vertex v1 = f.verticies[1]; - Vertex v2 = f.verticies[2]; + Vertex v0 = f.vertices[0]; + Vertex v1 = f.vertices[1]; + Vertex v2 = f.vertices[2]; - posBuf.position(v0.index * 3); - posBuf.put(v0.v.x).put(v0.v.y).put(v0.v.z); - posBuf.position(v1.index * 3); - posBuf.put(v1.v.x).put(v1.v.y).put(v1.v.z); - posBuf.position(v2.index * 3); - posBuf.put(v2.v.x).put(v2.v.y).put(v2.v.z); + positionBuffer.position(v0.index * 3); + positionBuffer.put(v0.v.x).put(v0.v.y).put(v0.v.z); + positionBuffer.position(v1.index * 3); + positionBuffer.put(v1.v.x).put(v1.v.y).put(v1.v.z); + positionBuffer.position(v2.index * 3); + positionBuffer.put(v2.v.x).put(v2.v.y).put(v2.v.z); - if (normBuf != null) { + if (normalBuffer != null) { if (v0.vn != null) { - normBuf.position(v0.index * 3); - normBuf.put(v0.vn.x).put(v0.vn.y).put(v0.vn.z); - normBuf.position(v1.index * 3); - normBuf.put(v1.vn.x).put(v1.vn.y).put(v1.vn.z); - normBuf.position(v2.index * 3); - normBuf.put(v2.vn.x).put(v2.vn.y).put(v2.vn.z); + normalBuffer.position(v0.index * 3); + normalBuffer.put(v0.vn.x).put(v0.vn.y).put(v0.vn.z); + normalBuffer.position(v1.index * 3); + normalBuffer.put(v1.vn.x).put(v1.vn.y).put(v1.vn.z); + normalBuffer.position(v2.index * 3); + normalBuffer.put(v2.vn.x).put(v2.vn.y).put(v2.vn.z); } } - if (tcBuf != null) { + if (texCoordBuffer != null) { if (v0.vt != null) { - tcBuf.position(v0.index * 2); - tcBuf.put(v0.vt.x).put(v0.vt.y); - tcBuf.position(v1.index * 2); - tcBuf.put(v1.vt.x).put(v1.vt.y); - tcBuf.position(v2.index * 2); - tcBuf.put(v2.vt.x).put(v2.vt.y); + texCoordBuffer.position(v0.index * 2); + texCoordBuffer.put(v0.vt.x).put(v0.vt.y); + texCoordBuffer.position(v1.index * 2); + texCoordBuffer.put(v1.vt.x).put(v1.vt.y); + texCoordBuffer.position(v2.index * 2); + texCoordBuffer.put(v2.vt.x).put(v2.vt.y); } } - int index = i * 3; // current face * 3 = current index - indexBuf.put(index, v0.index); - indexBuf.put(index + 1, v1.index); - indexBuf.put(index + 2, v2.index); + int index = i * 3; // Current face * 3 = current index. + indexBuffer.put(index, v0.index); + indexBuffer.put(index + 1, v1.index); + indexBuffer.put(index + 2, v2.index); } - m.setBuffer(VertexBuffer.Type.Position, 3, posBuf); - // index buffer and others were set on creation + mesh.setBuffer(VertexBuffer.Type.Position, 3, positionBuffer); + // Index buffer and others were set on creation. - m.setStatic(); - m.updateBound(); - m.updateCounts(); - //m.setInterleaved(); + mesh.setStatic(); + mesh.updateBound(); + mesh.updateCounts(); - // clear data generated face statements - // to prepare for next mesh + // Clear data generated face statements + // to prepare for next mesh. vertIndexMap.clear(); indexVertMap.clear(); - curIndex = 0; + currentIndex = 0; + + return mesh; + } + + private FloatBuffer createNormalBuffer(Mesh mesh) { + FloatBuffer normalBuffer = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3); + mesh.setBuffer(VertexBuffer.Type.Normal, 3, normalBuffer); + return normalBuffer; + } - return m; + private FloatBuffer createTextureCoordinateBuffer(Mesh mesh) { + FloatBuffer texCoordBuffer = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2); + mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, texCoordBuffer); + return texCoordBuffer; + } + + private IndexBuffer createIndexBuffer(Mesh mesh, List faces) { + IndexBuffer indexBuffer; + // Use shortbuffer if number of vertices is small enough. + if (vertIndexMap.size() <= 65535) { + ShortBuffer shortBuffer = BufferUtils.createShortBuffer(faces.size() * 3); + mesh.setBuffer(VertexBuffer.Type.Index, 3, shortBuffer); + indexBuffer = new IndexShortBuffer(shortBuffer); + } else { + IntBuffer intBuffer = BufferUtils.createIntBuffer(faces.size() * 3); + mesh.setBuffer(VertexBuffer.Type.Index, 3, intBuffer); + indexBuffer = new IndexIntBuffer(intBuffer); + } + return indexBuffer; } protected static class Vertex { @@ -510,13 +502,18 @@ public boolean equals(Object obj) { if (obj == null) { return false; } + if (getClass() != obj.getClass()) { return false; } + final Vertex other = (Vertex) obj; - return this.v == other.v || (this.v != null && this.v.equals(other.v)) && ( - this.vt == other.vt || (this.vt != null && this.vt.equals(other.vt)) && ( - this.vn == other.vn || (this.vn != null && this.vn.equals(other.vn)))); + + boolean verticesAreEqual = this.v == other.v || (this.v != null && this.v.equals(other.v)); + boolean tangentsAreEqual = this.vt == other.vt || (this.vt != null && this.vt.equals(other.vt)); + boolean normalsAreEqual = this.vn == other.vn || (this.vn != null && this.vn.equals(other.vn)); + + return verticesAreEqual && (tangentsAreEqual && normalsAreEqual); } @Override @@ -542,20 +539,6 @@ public Spatial createGeometry() { if (objectName == null) { groupNode.setName("Model"); } -// if (matFaces.size() > 0){ -// for (Entry> entry : matFaces.entrySet()){ -// ArrayList materialFaces = entry.getValue(); -// if (materialFaces.size() > 0){ -// Geometry geom = createGeometry(materialFaces, entry.getKey()); -// objNode.attachChild(geom); -// } -// } -// }else if (faces.size() > 0){ -// // generate final geometry -// Geometry geom = createGeometry(faces, null); -// objNode.attachChild(geom); -// } - return groupNode; } }