package org.jzy3d.io.obj;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.log4j.Logger;
import org.jzy3d.maths.BoundingBox3d;
/**
* Translated from C++ Version: nvModel.h - Model support class
*
* The nvModel class implements an interface for a multipurpose model object.
* This class is useful for loading and formatting meshes for use by OpenGL. It
* can compute face normals, tangents, and adjacency information. The class
* supports the obj file format.
*
* Author: Evan Hart Email: sdkfeedback@nvidia.com
*
* Copyright (c) NVIDIA Corporation. All rights reserved.
*/
public class OBJFile {
static Logger logger = Logger.getLogger(OBJFile.class);
/** Enumeration of primitive types */
public enum PrimType {
eptNone(0x0), eptPoints(0x1), eptEdges(0x2), eptTriangles(0x4), eptTrianglesWithAdjacency(0x8), eptAll(0xf);
PrimType(int val) {
m_iVal = val;
}
int m_iVal = 0;
};
public static final int NumPrimTypes = 4;
public OBJFile() {
posSize_ = 0;
pOffset_ = -1;
nOffset_ = -1;
vtxSize_ = 0;
openEdges_ = 0;
}
public class IdxSet {
Integer pIndex = 0;
Integer nIndex = 0;
boolean lessThan(IdxSet rhs) {
if (pIndex < rhs.pIndex)
return true;
else if (pIndex == rhs.pIndex) {
if (nIndex < rhs.nIndex)
return true;
}
return false;
}
};
public boolean loadModelFromFilename(String file) {
//URL fileURL = getClass().getClassLoader().getResource(File.separator + file);
URL fileURL = null;
try {
fileURL = new URL(file);
} catch (MalformedURLException e) {
logger.error(e);
return false;
}
return loadModelFromURL(fileURL);
}
/**
* This function attempts to determine the type of the filename passed as a
* parameter. If it understands that file type, it attempts to parse and
* load the file into its raw data structures. If the file type is
* recognized and successfully parsed, the function returns true, otherwise
* it returns false.
*/
public boolean loadModelFromURL(URL fileURL) {
if (fileURL != null) {
BufferedReader input = null;
try {
input = new BufferedReader(new InputStreamReader(fileURL.openStream()));
String line = null;
float[] val = new float[4];
int[][] idx = new int[3][3];
boolean hasNormals = false;
while ((line = input.readLine()) != null) {
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;
}
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;
}
break;
default:
break;
}
;
}
// post-process data
// free anything that ended up being unused
if (!hasNormals) {
normals_.clear();
nIndex_.clear();
}
posSize_ = 3;
return true;
} catch (FileNotFoundException kFNF) {
logger.error("Unable to find the shader file " + fileURL + " : FileNotFoundException : " + kFNF.getMessage());
} catch (IOException kIO) {
logger.error("Problem reading the shader file " + fileURL + " : IOException : " + kIO.getMessage());
} catch (NumberFormatException kIO) {
logger.error("Problem reading the shader file " + fileURL + " : NumberFormatException : " + kIO.getMessage());
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException closee) {
}
}
}
else{
logger.error("URL was null");
}
return false;
}
/**
* 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() {
boolean needsTriangles = true;
HashMap pts = new HashMap();
vertices_ = FloatBuffer.allocate((pIndex_.size() + nIndex_.size()) * 3);
indices_ = IntBuffer.allocate(pIndex_.size());
for (int i = 0; i < pIndex_.size(); i++) {
IdxSet idx = new IdxSet();
idx.pIndex = pIndex_.get(i);
if (normals_.size() > 0)
idx.nIndex = nIndex_.get(i);
else
idx.nIndex = 0;
if (!pts.containsKey(idx)) {
if (needsTriangles)
indices_.put(pts.size());
pts.put(idx, pts.size());
// position,
vertices_.put(positions_.get(idx.pIndex * posSize_));
vertices_.put(positions_.get(idx.pIndex * posSize_ + 1));
vertices_.put(positions_.get(idx.pIndex * posSize_ + 2));
// normal
if (normals_.size() > 0) {
vertices_.put(normals_.get(idx.nIndex * 3));
vertices_.put(normals_.get(idx.nIndex * 3 + 1));
vertices_.put(normals_.get(idx.nIndex * 3 + 2));
}
} else {
if (needsTriangles)
indices_.put(pts.get(idx));
}
}
// create selected prim
// set the offsets and vertex size
pOffset_ = 0; // always first
vtxSize_ = posSize_;
if (hasNormals()) {
nOffset_ = vtxSize_;
vtxSize_ += 3;
} else {
nOffset_ = -1;
}
vertices_.rewind();
indices_.rewind();
}
/**
* Returns the points defining the axis-aligned bounding box containing the model.
*/
public BoundingBox3d computeBoundingBox() {
float[] minVal = new float[3];
float[] maxVal = new float[3];
if (positions_.isEmpty())
return null;
for (int i = 0; i < 3; i++) {
minVal[i] = 1e10f;
maxVal[i] = -1e10f;
}
for (int i = 0; i < positions_.size(); i += 3) {
float x = positions_.get(i);
float y = positions_.get(i + 1);
float z = positions_.get(i + 2);
minVal[0] = Math.min(minVal[0], x);
minVal[1] = Math.min(minVal[1], y);
minVal[2] = Math.min(minVal[2], z);
maxVal[0] = Math.max(maxVal[0], x);
maxVal[1] = Math.max(maxVal[1], y);
maxVal[2] = Math.max(maxVal[2], z);
}
return new BoundingBox3d(minVal[0], maxVal[0], minVal[1], maxVal[1], minVal[2], maxVal[2]);
}
public void clearNormals() {
normals_.clear();
nIndex_.clear();
}
public boolean hasNormals() {
return normals_.size() > 0;
}
public int getPositionSize() {
return posSize_;
}
public int getNormalSize() {
return 3;
}
public List getPositions() {
return (positions_.size() > 0) ? positions_ : null;
}
public List getNormals() {
return (normals_.size() > 0) ? normals_ : null;
}
public List getPositionIndices() {
return (pIndex_.size() > 0) ? pIndex_ : null;
}
public List getNormalIndices() {
return (nIndex_.size() > 0) ? nIndex_ : null;
}
public int getPositionCount() {
return (posSize_ > 0) ? positions_.size() / posSize_ : 0;
}
public int getNormalCount() {
return normals_.size() / 3;
}
public int getIndexCount() {
return pIndex_.size();
}
public FloatBuffer getCompiledVertices() {
return vertices_;
}
public IntBuffer getCompiledIndices() {
return indices_;
}
public int getCompiledPositionOffset() {
return pOffset_;
}
public int getCompiledNormalOffset() {
return nOffset_;
}
public int getCompiledVertexSize() {
return vtxSize_;
}
public int getCompiledVertexCount() {
return ((pIndex_.size() + nIndex_.size()) * 3);
}
public int getOpenEdgeCount() {
return openEdges_;
}
protected List positions_ = new ArrayList();
protected List normals_ = new ArrayList();
protected int posSize_;
protected List pIndex_ = new ArrayList();
protected List nIndex_ = new ArrayList();
// data structures optimized for rendering, compiled model
protected IntBuffer indices_ = null;
protected FloatBuffer vertices_ = null;
protected int pOffset_;
protected int nOffset_;
protected int vtxSize_ = 0;
protected int openEdges_;
};
Ads help maintain this website.