Ok, so a week or two ago I decided to start learning lwjgl (without libraries such as slick2d, jpct, etc) and my simple digital world is populated with props which are generated using vector math - so I have absolutely no experience with any type of real model handling…
But I figured it would be nice touch to load at least a handful of real models to the game using .obj format models that I bought from turbosquid.com, and some that I made myself.
After some searching around online I managed to find a standalone .obj model loader which I was able to implement with ease, but now I’m stuck wondering how to manipulate the objects position in 3d world space because it renders at 0,0,0 and I have no experience with this.
Here’s what I mean:
If somebody could just explain to me how I could could go about translating the model to my own desired coordinates it would be very much appreciated as I’m new to lwjgl and have only really played with vector math, texturing, and other basic-basics so far.
In my game I am using this to initialize the model:
this.stall = new Loader("./res/stall.obj");
and this to render it in my existing scene:
stall.DrawModel();
The problem is that I just don’t understand how to set a forced position such as 32, 1, 32… instead of having it draw at 0,0,0 which I assume is the default location with the Loader I am using
package threed.obj;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import org.lwjgl.opengl.GL11;
import com.sun.prism.impl.BufferUtil;
public class Loader {
private String OBJModelPath; // The path to the Model File
private ArrayList vData = new ArrayList(); // List of Vertex Coordinates
private ArrayList vtData = new ArrayList(); // List of Texture Coordinates
private ArrayList vnData = new ArrayList(); // List of Normal Coordinates
private ArrayList<int[]> fv = new ArrayList<int[]>(); // Face Vertex Indices
private ArrayList<int[]> ft = new ArrayList<int[]>(); // Face Texture Indices
private ArrayList<int[]> fn = new ArrayList<int[]>(); // Face Normal Indices
private FloatBuffer modeldata; // Buffer which will contain vertice data
private int FaceFormat; // Format of the Faces Triangles or Quads
private int FaceMultiplier; // Number of possible coordinates per face
private int PolyCount = 0; // The Models Polygon Count
private boolean init = true;
public Loader(String Modelpath) {
OBJModelPath = Modelpath;
LoadOBJModel(OBJModelPath);
SetFaceRenderType();
}
private void LoadOBJModel(String ModelPath) {
try {
// Open a file handle and read the models data
BufferedReader br = new BufferedReader(new FileReader(ModelPath));
String line = null;
while ((line = br.readLine()) != null) {
if (line.startsWith("#")) { // Read Any Descriptor Data in the File
System.out.println("Descriptor: "+line); //Uncomment to print out file descriptor data
} else if (line.equals("")) {
// Ignore whitespace data
} else if (line.startsWith("v ")) { // Read in Vertex Data
vData.add(ProcessData(line));
} else if (line.startsWith("vt ")) { // Read Texture Coordinates
vtData.add(ProcessData(line));
} else if (line.startsWith("vn ")) { // Read Normal Coordinates
vnData.add(ProcessData(line));
} else if (line.startsWith("f ")) { // Read Face Data
ProcessfData(line);
}
}
br.close();
} catch (IOException e) {
System.out.println("Failed to find or read OBJ: " + ModelPath);
System.err.println(e);
}
}
private float[] ProcessData(String read) {
final String s[] = read.split("\\s+");
return (ProcessFloatData(s)); // returns an array of processed float data
}
private float[] ProcessFloatData(String sdata[]) {
float data[] = new float[sdata.length - 1];
for (int loop = 0; loop < data.length; loop++) {
data[loop] = Float.parseFloat(sdata[loop + 1]);
}
return data; // return an array of floats
}
private void ProcessfData(String fread) {
PolyCount++;
String s[] = fread.split("\\s+");
if (fread.contains("//")) { // Pattern is present if obj has only v and vn in face data
for (int loop = 1; loop < s.length; loop++) {
s[loop] = s[loop].replaceAll("//", "/0/"); // insert a zero for missing vt data
}
}
ProcessfIntData(s); // Pass in face data
}
private void ProcessfIntData(String sdata[]) {
int vdata[] = new int[sdata.length - 1];
int vtdata[] = new int[sdata.length - 1];
int vndata[] = new int[sdata.length - 1];
for (int loop = 1; loop < sdata.length; loop++) {
String s = sdata[loop];
String[] temp = s.split("/");
vdata[loop - 1] = Integer.valueOf(temp[0]); // always add vertex indices
if (temp.length > 1) { // we have v and vt data
vtdata[loop - 1] = Integer.valueOf(temp[1]); // add in vt indices
} else {
vtdata[loop - 1] = 0; // if no vt data is present fill in zeros
}
if (temp.length > 2) { // we have v, vt, and vn data
vndata[loop - 1] = Integer.valueOf(temp[2]); // add in vn indices
} else {
vndata[loop - 1] = 0; // if no vn data is present fill in zeros
}
}
fv.add(vdata);
ft.add(vtdata);
fn.add(vndata);
}
private void SetFaceRenderType() {
final int temp[] = fv.get(0);
if (temp.length == 3) {
FaceFormat = GL11.GL_TRIANGLES; // The faces come in sets of 3 so we have triangular faces
FaceMultiplier = 3;
} else if (temp.length == 4) {
FaceFormat = GL11.GL_QUADS; // The faces come in sets of 4 so we have quadrilateral faces
FaceMultiplier = 4;
} else {
FaceFormat = GL11.GL_POLYGON; // Fall back to render as free form polygons
}
}
private void ConstructInterleavedArray() {
final int tv[] = fv.get(0);
final int tt[] = ft.get(0);
final int tn[] = fn.get(0);
// If a value of zero is found that it tells us we don't have that type of data
if ((tv[0] != 0) && (tt[0] != 0) && (tn[0] != 0)) {
ConstructTNV(); // We have Vertex, 2D Texture, and Normal Data
GL11.glInterleavedArrays(GL11.GL_T2F_N3F_V3F, 0, modeldata);
} else if ((tv[0] != 0) && (tt[0] != 0) && (tn[0] == 0)) {
ConstructTV(); // We have just vertex and 2D texture Data
GL11.glInterleavedArrays(GL11.GL_T2F_V3F, 0, modeldata);
} else if ((tv[0] != 0) && (tt[0] == 0) && (tn[0] != 0)) {
ConstructNV(); // We have just vertex and normal Data
GL11.glInterleavedArrays(GL11.GL_N3F_V3F, 0, modeldata);
} else if ((tv[0] != 0) && (tt[0] == 0) && (tn[0] == 0)) {
ConstructV();
GL11.glInterleavedArrays(GL11.GL_V3F, 0, modeldata);
}
}
private void ConstructTNV() {
int[] v, t, n;
float tcoords[] = new float[2]; // Only T2F is supported in
// InterLeavedArrays!!
float coords[] = new float[3];
int fbSize = PolyCount * (FaceMultiplier * 8); // 3v Per Poly, 2vt Per Poly, 3vn Per Poly
modeldata = BufferUtil.newFloatBuffer(fbSize);
modeldata.position(0);
for (int oloop = 0; oloop < fv.size(); oloop++) {
v = (fv.get(oloop));
t = (ft.get(oloop));
n = (fn.get(oloop));
for (int iloop = 0; iloop < v.length; iloop++) {
// Fill in the texture coordinate data
for (int tloop = 0; tloop < tcoords.length; tloop++)
// Only T2F is supported in InterLeavedArrays!!
tcoords[tloop] = ((float[]) vtData.get(t[iloop] - 1))[tloop];
modeldata.put(tcoords);
// Fill in the normal coordinate data
for (int vnloop = 0; vnloop < coords.length; vnloop++)
coords[vnloop] = ((float[]) vnData.get(n[iloop] - 1))[vnloop];
modeldata.put(coords);
// Fill in the vertex coordinate data
for (int vloop = 0; vloop < coords.length; vloop++)
coords[vloop] = ((float[]) vData.get(v[iloop] - 1))[vloop];
modeldata.put(coords);
}
}
modeldata.position(0);
}
private void ConstructTV() {
int[] v, t;
float tcoords[] = new float[2]; // Only T2F is supported in
// InterLeavedArrays!!
float coords[] = new float[3];
int fbSize = PolyCount * (FaceMultiplier * 5); // 3v Per Poly, 2vt Per Poly
modeldata = BufferUtil.newFloatBuffer(fbSize);
modeldata.position(0);
for (int oloop = 0; oloop < fv.size(); oloop++) {
v = (fv.get(oloop));
t = (ft.get(oloop));
for (int iloop = 0; iloop < v.length; iloop++) {
// Fill in the texture coordinate data
for (int tloop = 0; tloop < tcoords.length; tloop++)
// Only T2F is supported in InterLeavedArrays!!
tcoords[tloop] = ((float[]) vtData.get(t[iloop] - 1))[tloop];
modeldata.put(tcoords);
// Fill in the vertex coordinate data
for (int vloop = 0; vloop < coords.length; vloop++)
coords[vloop] = ((float[]) vData.get(v[iloop] - 1))[vloop];
modeldata.put(coords);
}
}
modeldata.position(0);
}
private void ConstructNV() {
int[] v, n;
float coords[] = new float[3];
int fbSize = PolyCount * (FaceMultiplier * 6); // 3v Per Poly, 3vn Per Poly
modeldata = BufferUtil.newFloatBuffer(fbSize);
modeldata.position(0);
for (int oloop = 0; oloop < fv.size(); oloop++) {
v = (fv.get(oloop));
n = (fn.get(oloop));
for (int iloop = 0; iloop < v.length; iloop++) {
// Fill in the normal coordinate data
for (int vnloop = 0; vnloop < coords.length; vnloop++)
coords[vnloop] = ((float[]) vnData.get(n[iloop] - 1))[vnloop];
modeldata.put(coords);
// Fill in the vertex coordinate data
for (int vloop = 0; vloop < coords.length; vloop++)
coords[vloop] = ((float[]) vData.get(v[iloop] - 1))[vloop];
modeldata.put(coords);
}
}
modeldata.position(0);
}
private void ConstructV() {
int[] v;
float coords[] = new float[3];
int fbSize = PolyCount * (FaceMultiplier * 3); // 3v Per Poly
modeldata = BufferUtil.newFloatBuffer(fbSize);
modeldata.position(0);
for (int oloop = 0; oloop < fv.size(); oloop++) {
v = (fv.get(oloop));
for (int iloop = 0; iloop < v.length; iloop++) {
// Fill in the vertex coordinate data
for (int vloop = 0; vloop < coords.length; vloop++)
coords[vloop] = ((float[]) vData.get(v[iloop] - 1))[vloop];
modeldata.put(coords);
}
}
modeldata.position(0);
}
public void DrawModel() {
if (init) {
ConstructInterleavedArray();
cleanup();
init = false;
}
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glCullFace(GL11.GL_BACK);
// GL11.glPolygonMode(GL11.GL_FRONT,GL11.GL_LINE);
GL11.glDrawArrays(FaceFormat, 0, PolyCount * FaceMultiplier);
GL11.glDisable(GL11.GL_CULL_FACE);
}
public String Polycount() {
String pc = Integer.toString(PolyCount);
return pc;
}
private void cleanup() {
vData.clear();
vtData.clear();
vnData.clear();
fv.clear();
ft.clear();
fn.clear();
modeldata.clear();
System.gc();
}
}