mirror of https://github.com/rusefi/jzy3d-api.git
ObjFile support face format where normal are not defined ("/" separator
not present)
This commit is contained in:
parent
adacd5b6aa
commit
71767f6489
|
@ -80,7 +80,7 @@
|
|||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- <testSourceDirectory>src/tests</testSourceDirectory> -->
|
||||
<testSourceDirectory>src/tests</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
|
|
|
@ -66,7 +66,8 @@ public class OBJFile {
|
|||
};
|
||||
|
||||
public boolean loadModelFromFilename(String file) {
|
||||
//URL fileURL = getClass().getClassLoader().getResource(File.separator + file);
|
||||
// URL fileURL = getClass().getClassLoader().getResource(File.separator
|
||||
// + file);
|
||||
|
||||
URL fileURL = null;
|
||||
try {
|
||||
|
@ -98,102 +99,18 @@ public class OBJFile {
|
|||
boolean hasNormals = false;
|
||||
|
||||
while ((line = input.readLine()) != null) {
|
||||
if(line.isEmpty()){
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
switch (line.charAt(0)) {
|
||||
case '#':
|
||||
break;
|
||||
case 'v':
|
||||
switch (line.charAt(1)) {
|
||||
|
||||
case ' ':
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
// vertex, 3 or 4 components
|
||||
val[0] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[1] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[2] = Float.valueOf(line);
|
||||
positions_.add(val[0]);
|
||||
positions_.add(val[1]);
|
||||
positions_.add(val[2]);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
// normal, 3 components
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[0] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[1] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[2] = Float.valueOf(line);
|
||||
normals_.add(val[0]);
|
||||
normals_.add(val[1]);
|
||||
normals_.add(val[2]);
|
||||
break;
|
||||
}
|
||||
parseObjVertex(line, val);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
// face
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
|
||||
// Remove any additional leading whitespace. The whitespace count
|
||||
// can vary for different programs, i.e., Right Hemisphere produces
|
||||
// obj files with 2 spaces. Meshlab produces single spaced files.
|
||||
while(line.startsWith(" ")){
|
||||
line = line.substring(1);
|
||||
}
|
||||
|
||||
idx[0][0] = Integer.valueOf(line.substring(0, line.indexOf("//"))).intValue();
|
||||
line = line.substring(line.indexOf("//") + 2);
|
||||
idx[0][1] = Integer.valueOf(line.substring(0, line.indexOf(" "))).intValue();
|
||||
|
||||
{
|
||||
// This face has vertex and normal indices
|
||||
|
||||
// in .obj, f v1 .... the vertex index used start
|
||||
// from 1, so -1 here
|
||||
// remap them to the right spot
|
||||
idx[0][0] = (idx[0][0] > 0) ? (idx[0][0] - 1) : (positions_.size() - idx[0][0]);
|
||||
idx[0][1] = (idx[0][1] > 0) ? (idx[0][1] - 1) : (normals_.size() - idx[0][1]);
|
||||
|
||||
// grab the second vertex to prime
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
idx[1][0] = Integer.valueOf(line.substring(0, line.indexOf("//")));
|
||||
line = line.substring(line.indexOf("//") + 2);
|
||||
idx[1][1] = Integer.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
|
||||
// remap them to the right spot
|
||||
idx[1][0] = (idx[1][0] > 0) ? (idx[1][0] - 1) : (positions_.size() - idx[1][0]);
|
||||
idx[1][1] = (idx[1][1] > 0) ? (idx[1][1] - 1) : (normals_.size() - idx[1][1]);
|
||||
|
||||
// grab the third vertex to prime
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
idx[2][0] = Integer.valueOf(line.substring(0, line.indexOf("//")));
|
||||
line = line.substring(line.indexOf("//") + 2);
|
||||
idx[2][1] = Integer.valueOf(line);
|
||||
{
|
||||
// remap them to the right spot
|
||||
idx[2][0] = (idx[2][0] > 0) ? (idx[2][0] - 1) : (positions_.size() - idx[2][0]);
|
||||
idx[2][1] = (idx[2][1] > 0) ? (idx[2][1] - 1) : (normals_.size() - idx[2][1]);
|
||||
|
||||
// add the indices
|
||||
for (int ii = 0; ii < 3; ii++) {
|
||||
pIndex_.add(idx[ii][0]);
|
||||
nIndex_.add(idx[ii][1]);
|
||||
}
|
||||
|
||||
// prepare for the next iteration, the num 0
|
||||
// does not change.
|
||||
idx[1][0] = idx[2][0];
|
||||
idx[1][1] = idx[2][1];
|
||||
}
|
||||
hasNormals = true;
|
||||
}
|
||||
hasNormals = parseObjFace(line, idx, hasNormals);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -223,8 +140,7 @@ public class OBJFile {
|
|||
} catch (IOException closee) {
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
logger.error("URL was null");
|
||||
}
|
||||
|
||||
|
@ -232,16 +148,176 @@ public class OBJFile {
|
|||
}
|
||||
|
||||
/**
|
||||
* This function takes the raw model data in the internal
|
||||
* structures, and attempts to bring it to a format directly
|
||||
* accepted for vertex array style rendering. This means that
|
||||
* a unique compiled vertex will exist for each unique
|
||||
* combination of position, normal, tex coords, etc that are
|
||||
* used in the model. The prim parameter, tells the model
|
||||
* what type of index list to compile. By default it compiles
|
||||
* a simple triangle mesh with no connectivity.
|
||||
*
|
||||
* v 1.0 0.0 0.0
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Vertex/Normal Format
|
||||
*
|
||||
* <pre>
|
||||
* v 1.0 0.0 0.0
|
||||
* vn 0.0 1.0 0.0
|
||||
*
|
||||
* n : normal
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @see https://fr.wikipedia.org/wiki/Objet_3D_%28format_de_fichier%29
|
||||
*/
|
||||
public void parseObjVertex(String line, float[] val) {
|
||||
switch (line.charAt(1)) {
|
||||
case ' ':
|
||||
// logger.info(line);
|
||||
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
// vertex, 3 or 4 components
|
||||
val[0] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[1] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[2] = Float.valueOf(line);
|
||||
positions_.add(val[0]);
|
||||
positions_.add(val[1]);
|
||||
positions_.add(val[2]);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
// normal, 3 components
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[0] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[1] = Float.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
val[2] = Float.valueOf(line);
|
||||
normals_.add(val[0]);
|
||||
normals_.add(val[1]);
|
||||
normals_.add(val[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Face Format
|
||||
*
|
||||
* <pre>
|
||||
* f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3
|
||||
*
|
||||
* t : texture
|
||||
* n : normal
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public boolean parseObjFace(String line, int[][] idx, boolean hasNormals) {
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
|
||||
// Remove any additional leading whitespace. The whitespace count
|
||||
// can vary for different programs, i.e., Right Hemisphere produces
|
||||
// obj files with 2 spaces. Meshlab produces single spaced files.
|
||||
while (line.startsWith(" ")) {
|
||||
line = line.substring(1);
|
||||
}
|
||||
|
||||
// If following pattern "f 9227//9524 8376//8650 8377//8649"
|
||||
if (ObjFaceFormat.vertexNormal(line)) {
|
||||
idx[0][0] = Integer.valueOf(line.substring(0, line.indexOf("//"))).intValue();
|
||||
line = line.substring(line.indexOf("//") + 2);
|
||||
idx[0][1] = Integer.valueOf(line.substring(0, line.indexOf(" "))).intValue();
|
||||
|
||||
{
|
||||
// This face has vertex and normal indices
|
||||
|
||||
// in .obj, f v1 .... the vertex index used start
|
||||
// from 1, so -1 here
|
||||
// remap them to the right spot
|
||||
idx[0][0] = (idx[0][0] > 0) ? (idx[0][0] - 1) : (positions_.size() - idx[0][0]);
|
||||
idx[0][1] = (idx[0][1] > 0) ? (idx[0][1] - 1) : (normals_.size() - idx[0][1]);
|
||||
|
||||
// grab the second vertex to prime
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
idx[1][0] = Integer.valueOf(line.substring(0, line.indexOf("//")));
|
||||
line = line.substring(line.indexOf("//") + 2);
|
||||
idx[1][1] = Integer.valueOf(line.substring(0, line.indexOf(" ")));
|
||||
|
||||
// remap them to the right spot
|
||||
idx[1][0] = (idx[1][0] > 0) ? (idx[1][0] - 1) : (positions_.size() - idx[1][0]);
|
||||
idx[1][1] = (idx[1][1] > 0) ? (idx[1][1] - 1) : (normals_.size() - idx[1][1]);
|
||||
|
||||
// grab the third vertex to prime
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
idx[2][0] = Integer.valueOf(line.substring(0, line.indexOf("//")));
|
||||
line = line.substring(line.indexOf("//") + 2);
|
||||
idx[2][1] = Integer.valueOf(line);
|
||||
{
|
||||
// remap them to the right spot
|
||||
idx[2][0] = (idx[2][0] > 0) ? (idx[2][0] - 1) : (positions_.size() - idx[2][0]);
|
||||
idx[2][1] = (idx[2][1] > 0) ? (idx[2][1] - 1) : (normals_.size() - idx[2][1]);
|
||||
|
||||
// add the indices
|
||||
for (int ii = 0; ii < 3; ii++) {
|
||||
pIndex_.add(idx[ii][0]);
|
||||
nIndex_.add(idx[ii][1]);
|
||||
}
|
||||
|
||||
// prepare for the next iteration, the num 0
|
||||
// does not change.
|
||||
idx[1][0] = idx[2][0];
|
||||
idx[1][1] = idx[2][1];
|
||||
}
|
||||
hasNormals = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Not following pattern "f 9227//9524 8376//8650 8377//8649"
|
||||
else if (ObjFaceFormat.vertex(line)) {
|
||||
String[] nums = line.split(" ");
|
||||
idx[0][0] = Integer.parseInt(nums[0]);
|
||||
|
||||
// in .obj, f v1 .... the vertex index used start
|
||||
// from 1, so -1 here
|
||||
// remap them to the right spot
|
||||
idx[0][0] = (idx[0][0] > 0) ? (idx[0][0] - 1) : (positions_.size() - idx[0][0]);
|
||||
|
||||
idx[1][0] = Integer.parseInt(nums[1]);
|
||||
|
||||
// remap them to the right spot
|
||||
idx[1][0] = (idx[1][0] > 0) ? (idx[1][0] - 1) : (positions_.size() - idx[1][0]);
|
||||
|
||||
idx[2][0] = Integer.parseInt(nums[2]);
|
||||
|
||||
// remap them to the right spot
|
||||
idx[2][0] = (idx[2][0] > 0) ? (idx[2][0] - 1) : (positions_.size() - idx[2][0]);
|
||||
|
||||
|
||||
// add the indices
|
||||
for (int ii = 0; ii < 3; ii++) {
|
||||
pIndex_.add(idx[ii][0]);
|
||||
}
|
||||
|
||||
// prepare for the next iteration, the num 0
|
||||
// does not change.
|
||||
idx[1][0] = idx[2][0];
|
||||
|
||||
// logger.info("FACE - " + line);
|
||||
}
|
||||
return hasNormals;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes the raw model data in the internal structures, and
|
||||
* attempts to bring it to a format directly accepted for vertex array style
|
||||
* rendering. This means that a unique compiled vertex will exist for each
|
||||
* unique combination of position, normal, tex coords, etc that are used in
|
||||
* the model. The prim parameter, tells the model what type of index list to
|
||||
* compile. By default it compiles a simple triangle mesh with no
|
||||
* connectivity.
|
||||
*/
|
||||
public void compileModel() {
|
||||
|
||||
logger.info("expect pIndex.size: " + pIndex_.size() + " Nindex.size:" + nIndex_.size());
|
||||
|
||||
boolean needsTriangles = true;
|
||||
|
||||
HashMap<IdxSet, Integer> pts = new HashMap<IdxSet, Integer>();
|
||||
|
@ -296,7 +372,8 @@ public class OBJFile {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the points defining the axis-aligned bounding box containing the model.
|
||||
* Returns the points defining the axis-aligned bounding box containing the
|
||||
* model.
|
||||
*/
|
||||
public BoundingBox3d computeBoundingBox() {
|
||||
float[] minVal = new float[3];
|
||||
|
@ -398,6 +475,10 @@ public class OBJFile {
|
|||
return openEdges_;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "countVertices =" + getPositionCount() + " indexSize = " + getIndexCount() + " vertexSize = " + getCompiledVertexCount() + " byteOffset = " + getCompiledVertexSize() + " normalOffset = " + getCompiledNormalOffset() + " dimensions = " + getPositionSize();
|
||||
}
|
||||
|
||||
protected List<Float> positions_ = new ArrayList<Float>();
|
||||
protected List<Float> normals_ = new ArrayList<Float>();
|
||||
protected int posSize_;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package org.jzy3d.io.obj;
|
||||
|
||||
public class ObjFaceFormat {
|
||||
/** f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 */
|
||||
public boolean vertexTextureNormal(String line){
|
||||
return false;
|
||||
}
|
||||
|
||||
/** f v1//vn1 v2//vn2 v3//vn3 */
|
||||
public static boolean vertexNormal(String line){
|
||||
return line.indexOf("//")!=-1;
|
||||
}
|
||||
|
||||
/** f v1 v2 v3 */
|
||||
public static boolean vertex(String line){
|
||||
return !line.contains("/");
|
||||
}
|
||||
}
|
|
@ -9,8 +9,8 @@ import org.jzy3d.io.IGLLoader;
|
|||
import org.jzy3d.maths.BoundingBox3d;
|
||||
import org.jzy3d.maths.Coord3d;
|
||||
import org.jzy3d.plot3d.primitives.AbstractDrawable;
|
||||
import org.jzy3d.plot3d.primitives.IGLBindedResource;
|
||||
import org.jzy3d.plot3d.primitives.AbstractGeometry.PolygonMode;
|
||||
import org.jzy3d.plot3d.primitives.IGLBindedResource;
|
||||
import org.jzy3d.plot3d.primitives.vbo.buffers.FloatVBO;
|
||||
import org.jzy3d.plot3d.rendering.canvas.Quality;
|
||||
import org.jzy3d.plot3d.rendering.compat.GLES2CompatUtils;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.jzy3d.plot3d.primitives.vbo.drawable;
|
||||
|
||||
import org.jzy3d.io.IGLLoader;
|
||||
import org.jzy3d.plot3d.primitives.vbo.drawable.DrawableVBO;
|
||||
|
||||
import com.jogamp.opengl.GL2;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.jzy3d.plot3d.primitives.vbo.drawable;
|
||||
|
||||
import org.jzy3d.io.IGLLoader;
|
||||
import org.jzy3d.plot3d.primitives.vbo.drawable.DrawableVBO;
|
||||
|
||||
import com.jogamp.opengl.GL2;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.jzy3d.plot3d.primitives.vbo.drawable;
|
||||
|
||||
import org.jzy3d.io.IGLLoader;
|
||||
import org.jzy3d.plot3d.primitives.vbo.drawable.DrawableVBO;
|
||||
|
||||
import com.jogamp.opengl.GL2;
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.jzy3d.plot3d.rendering.canvas;
|
|||
import org.jzy3d.plot3d.rendering.ordering.AbstractOrderingStrategy;
|
||||
import org.jzy3d.plot3d.rendering.view.View;
|
||||
|
||||
import com.jogamp.nativewindow.ScalableSurface;
|
||||
import com.jogamp.opengl.util.Animator;
|
||||
|
||||
/** Provides a structure for setting the rendering quality, i.e., the tradeoff
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package org.jzy3d.io.obj;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestObjFile {
|
||||
@Test
|
||||
public void loadDragon(){
|
||||
OBJFile objFile = new OBJFile();
|
||||
|
||||
String objFilePath = "data/objfiles/dragon.obj";
|
||||
objFile.loadModelFromFilename("file://" + new File(objFilePath).getAbsolutePath());
|
||||
|
||||
int countVertices = objFile.getPositionCount();
|
||||
int indexSize = objFile.getIndexCount();
|
||||
int vertexSize = objFile.getCompiledVertexCount();
|
||||
int byteOffset = objFile.getCompiledVertexSize();
|
||||
int normalOffset = objFile.getCompiledNormalOffset();
|
||||
int dimensions = objFile.getPositionSize();
|
||||
|
||||
Assert.assertTrue(objFile.hasNormals());
|
||||
|
||||
Assert.assertEquals(437645, countVertices);
|
||||
Assert.assertEquals(2614242, indexSize);
|
||||
Assert.assertEquals(15685452, vertexSize);
|
||||
Assert.assertEquals(0, byteOffset);
|
||||
Assert.assertEquals(-1, normalOffset);
|
||||
Assert.assertEquals(3, dimensions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadBunny(){
|
||||
OBJFile objFile = new OBJFile();
|
||||
|
||||
String objFilePath = "data/objfiles/bunny.obj";
|
||||
objFile.loadModelFromFilename("file://" + new File(objFilePath).getAbsolutePath());
|
||||
|
||||
|
||||
int countVertices = objFile.getPositionCount();
|
||||
int indexSize = objFile.getIndexCount();
|
||||
int vertexSize = objFile.getCompiledVertexCount();
|
||||
int byteOffset = objFile.getCompiledVertexSize();
|
||||
int normalOffset = objFile.getCompiledNormalOffset();
|
||||
int dimensions = objFile.getPositionSize();
|
||||
|
||||
Assert.assertFalse(objFile.hasNormals());
|
||||
Assert.assertEquals(80289, countVertices);
|
||||
Assert.assertEquals(482409, indexSize);
|
||||
Assert.assertEquals(1447227, vertexSize);
|
||||
Assert.assertEquals(0, byteOffset);
|
||||
Assert.assertEquals(-1, normalOffset);
|
||||
Assert.assertEquals(3, dimensions);
|
||||
|
||||
objFile.compileModel();
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue