package javaforce.gl;
import java.io.*;
import javaforce.*;
/**
* JF3D - New format designed for JavaForce
* Chunk based format
*
* Supports:
* - GLModel, GLObject, GLUVMap(s)
* TODO:
* - animation data
*
* struct ChunkHeader {
* int id;
* int len; //size of data excluding ChunkHeader
* }
*
* Everything is Little Endian (Intel based)
*
* @author pquiring
*/
public class GL_JF3D {
private byte data[];
private int datapos;
private int skip;
private static boolean debug = false;
private static final int MAGIC = 0x4433464a; //'JF3D'
private static final int VERSION = 0x100;
private static final int ID_MODEL = 0x010000;
private static final int ID_OBJECT = 0x020000;
private static final int ID_UVMAP = 0x030000;
// future reserved
// private static final int ID_CAMERA = 0x40000;
// private static final int ID_LIGHT = 0x50000;
private GLModel model;
private GLObject obj;
public GLModel load(String filename) {
try {
return loadJF3D(new FileInputStream(filename));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public GLModel load(InputStream is) {
try {
return loadJF3D(is);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private boolean eof() {
return datapos >= data.length;
}
private int readuint16() {
int uint16 = LE.getuint16(data, datapos);
datapos += 2;
skip -= 2;
return uint16;
}
private int readuint32() {
int uint32 = LE.getuint32(data, datapos);
datapos += 4;
skip -= 4;
return uint32;
}
private float readfloat() {
return Float.intBitsToFloat(readuint32());
}
private String readString() {
String ret = "";
char ch;
while (!eof()) {
ch = (char)data[datapos++];
skip--;
if (ch == 0) break;
ret += ch;
}
return ret;
}
private GLModel loadJF3D(InputStream is) throws Exception {
datapos = 0;
data = JF.readAll(is);
int magic = readuint32();
if (magic != MAGIC) {
throw new Exception("GL_JF3D:Not JF3D file");
}
int version = readuint32();
if (version < VERSION) {
throw new Exception("GL_JF3D:Bad version");
}
while (!eof()) {
int head_id = readuint32();
int head_len = readuint32();
skip = head_len;
int head_ver = head_id & 0xffff;
head_id &= 0xffff0000;
switch (head_id) {
case ID_MODEL:
if (model != null) {
throw new Exception("GL_JF3D:Multiple Model chunks found");
}
model = new GLModel();
int fcnt = readuint32();
for(int a=0;a<fcnt;a++) {
String txt = readString();
model.textures.add(txt);
// JFLog.log("Texture=" + txt);
}
if (head_ver > 0) {
//future reserved
}
break;
case ID_OBJECT:
obj = new GLObject();
model.addObject(obj);
obj.name = readString();
obj.type = readuint32();
obj.org.x = readfloat();
obj.org.y = readfloat();
obj.org.z = readfloat();
int vcnt = readuint32(); //vertex count
for(int v=0;v<vcnt;v++) {
float fx = readfloat();
float fy = readfloat();
float fz = readfloat();
obj.addVertex(new float[] {fx, fy, fz});
}
int pcnt = readuint32(); //poly count
switch (obj.type) {
case GL.GL_TRIANGLES:
pcnt *= 3;
break;
case GL.GL_QUADS:
pcnt *= 4;
break;
default:
JFLog.log("GL_JF3D:Error Unknown GL Type:" + obj.type);
return null;
}
for(int p=0;p<pcnt;p++) {
int pt = readuint32();
if (pt >= vcnt) {
JFLog.log("Error:Poly includes invalid vertex !!!");
}
obj.addPoly(new int[] {pt});
}
break;
case ID_UVMAP:
GLUVMap map = obj.createUVMap();
map.name = readString();
map.textureIndex = readuint32();
int uvcnt = readuint32();
if (uvcnt != obj.getVertexCount()) {
JFLog.log("Warning:UVMAP size != vertex count");
}
for(int i=0;i<uvcnt;i++) {
float u = readfloat();
float v = readfloat();
map.addText(new float[] {u, v});
}
break;
default:
break;
}
if (skip > 0) {
datapos += skip;
}
}
return model;
}
public boolean save(GLModel model, String filename) {
try {
FileOutputStream fos = new FileOutputStream(filename);
saveJF3D(model, fos);
fos.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean save(GLModel model, OutputStream os) {
try {
saveJF3D(model, os);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private ByteArrayOutputStream baos;
private byte tmp[];
private void writeString(String str) throws Exception {
baos.write(str.getBytes());
baos.write(0);
}
private void writeuint16(int val) {
LE.setuint16(tmp, 0, val);
baos.write(tmp, 0, 2);
}
private void writeuint32(int val) {
LE.setuint32(tmp, 0, val);
baos.write(tmp, 0, 4);
}
private void writefloat(float f) {
writeuint32(Float.floatToIntBits(f));
}
private void saveJF3D(GLModel model, OutputStream os) throws Exception {
baos = new ByteArrayOutputStream();
tmp = new byte[8];
int size;
writeuint32(MAGIC);
writeuint32(VERSION);
writeuint32(ID_MODEL);
size = 0;
int tcnt = model.textures.size();
for(int a=0;a<tcnt;a++) {
size += model.textures.get(a).length() + 1;
}
writeuint32(size);
writeuint32(tcnt);
for(int a=0;a<tcnt;a++) {
writeString(model.textures.get(a));
}
for(int o=0;o<model.ol.size();o++) {
GLObject obj = model.ol.get(o);
writeuint32(ID_OBJECT);
int vcnt = obj.vpl.size();
int pcnt = obj.vil.size();
size = obj.name.length() + 1 + 4 + (4*3) + (4 + (vcnt * 4)) + (4 + (pcnt * 4));
writeuint32(size);
writeString(obj.name);
writeuint32(obj.type);
writefloat(obj.org.x);
writefloat(obj.org.y);
writefloat(obj.org.z);
writeuint32(vcnt / 3);
float xyz[] = obj.vpl.toArray();
for(int a=0;a<vcnt;a++) {
writefloat(xyz[a]);
}
switch (obj.type) {
case GL.GL_TRIANGLES:
writeuint32(pcnt / 3);
break;
case GL.GL_QUADS:
writeuint32(pcnt / 4);
break;
}
int pts[] = obj.vil.toArray();
for(int a=0;a<pcnt;a++) {
writeuint32(pts[a]);
}
int maps = obj.maps.size();
if (maps == 0) {
JFLog.log("GL_JF3D:Warning:No UVMaps found for object:" + obj.name);
}
for(int m=0;m<maps;m++) {
GLUVMap map = obj.maps.get(m);
writeuint32(ID_UVMAP);
int uvcnt = map.uvl.size();
size = map.name.length() + 1 + 4 + (4 + (uvcnt * 4));
writeuint32(size);
writeString(map.name);
writeuint32(map.textureIndex);
writeuint32(uvcnt/2);
float uv[] = map.uvl.toArray();
for(int a=0;a<uvcnt;a++) {
writefloat(uv[a]);
}
}
}
os.write(baos.toByteArray());
}
}