package javaforce.gl;
import java.io.*;
import java.util.*;
import javaforce.*;
/**
* Autodesk .3DS reader
*
* Supports:
* - Mesh, UVMap, animation data
*
*
* @author pquiring
*/
public class GL_3DS {
//Loading//
private static final int _3DS_FLG_TENSION = 0x01;
private static final int _3DS_FLG_CONTINUITY = 0x02;
private static final int _3DS_FLG_BIAS = 0x04;
private static final int _3DS_FLG_EASE_TO = 0x08;
private static final int _3DS_FLG_EASE_FROM = 0x10;
//*.3DS loading (loades meshes, textures and animation - lights are not supported)
//You should now use GLSCene.load3DS() instead
public byte data[];
public int datapos;
public static boolean debug = false;
public GLModel load(String filename) {
try {
return load3ds(new FileInputStream(filename));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public GLModel load(InputStream is) {
try {
return load3ds(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;
return uint16;
}
private int readuint32() {
int uint32 = LE.getuint32(data, datapos);
datapos += 4;
return uint32;
}
private float readfloat() {
return Float.intBitsToFloat(readuint32());
}
private GLModel load3ds(InputStream is) throws Exception {
GLObject obj = null;
int off = 0;
int head_id;
int head_len;
int _siz;
float _float[];
int _pts[];
boolean done_vertex = false;
boolean done_pts = false;
int vertexidx = -1;
int vertexcnt = -1;
String name;
ArrayList<material> matlist; //materials (objects refer to material name)
ArrayList<String> objlist; //object names (keyframe data refers to object name)
String objname = "";
int objidx = -1;
material mat;
String matname = "", fn;
int a, b, keys, u32;
boolean ok;
int u16;
GLTranslate trans;
GLRotate rot;
GLScale scale;
GLModel mod;
GLUVMap map;
int mapidx = -1;
int skip=0;
int parent;
int frameno;
datapos = 0;
data = JF.readAll(is);
matlist = new ArrayList<material>();
objlist = new ArrayList<String>();
mod = new GLModel();
while (!eof()) {
head_id = readuint16();
head_len = readuint32();
if (head_len == -1) break; //this does happen in some files
if (head_len < 6) throw new Exception("head_len < 6 (" + head_len + ")"); //bad file
head_len -= 6;
//JFLog.log("id="+Integer.toString(head_id,16));
switch (head_id) {
case 0x4d4d: //main chunk
break;
case 0x3d3d: //mesh chunk
break;
case 0xafff: //material chunk
matname = "";
break;
case 0xa000: //material chunk name
matname = readname(head_len);
break;
case 0xa200: //texture details
break;
case 0xa300: //texture filename
mat = new material();
fn = readname(head_len);
//JFLog.log("texture filename" + fn);
mat.name = matname;
mat.filename = fn;
matlist.add(mat);
break;
case 0x4000: //object chunk
objname = readname(-1); //don't skip the whole chunk
if (debug) {
JFLog.log("obj=" + objname);
}
done_vertex = false;
done_pts = false;
break;
case 0x4100: //triangular object chunk
break;
case 0x4110: //vertex list of a polygon
skip = head_len;
if (done_vertex) {JFLog.log("Warning : 2 vertex lists found for 1 object?");break;}
obj = new GLObject();
obj.type = GL.GL_TRIANGLES; //3ds only supports triangles
obj.name = objname;
mapidx = -1;
mod.ol.add(obj);
objlist.add(objname);
_siz = readuint16();
skip-=2;
vertexidx = obj.vpl.size();
vertexcnt = _siz;
if (_siz == 0) break;
_float = new float[_siz * 3];
for(a=0;a<_siz;a++) {
for(b=0;b<3;b++) {
_float[a*3+b] = readfloat();
skip-=4;
}
if (debug) {
JFLog.log(String.format("v=%3.3f,%3.3f,%3.3f", _float[a*3+0] , _float[a*3+1] , _float[a*3+2]));
}
}
obj.addVertex(_float);
_float = null;
done_vertex = true;
break;
case 0x4120: //Points list
_siz = readuint16();
skip = _siz * 2 * 4;
if (!done_vertex) {JFLog.log("Warning : pts list before vertex list?");break;}
if (done_pts) {JFLog.log("Warning : 2 pts lists found for 1 object?");break;}
if (_siz == 0) break;
_pts = new int[3]; //p1,p2,p3,flgs per triangle
for(a=0;a<_siz;a++) {
for(b=0;b<3;b++) {
_pts[b] = (short)readuint16();
skip -= 2;
}
readuint16(); //skip flgs
skip -= 2;
obj.addPoly(_pts);
if (debug) {
JFLog.log("p=" + _pts[0] + "," + _pts[1] + "," + _pts[2]);
}
}
_pts = null;
done_pts = true;
break;
case 0x4130: //object material name
name = readname(head_len);
mapidx++;
map = obj.createUVMap();
map.name = "uvmap" + mapidx;
if (obj != null) {
//find name in matlist
ok = false;
for(a=0;a<matlist.size();a++) {
if (matlist.get(a).name.equals(name)) {
int idx = mod.addTexture(matlist.get(a).filename);
map.textureIndex = idx;
ok = true;
break;
}
}
// if (!ok) throw new Exception("0x4130 : object material name not found in list : " + name);
}
if (debug) {
JFLog.log("mat=" + map.textureIndex);
}
break;
case 0x4140: //texture vertex list (UV)
_siz = readuint16();
skip = _siz * 2 * 4;
if (!done_vertex) {JFLog.log("Warning:Texture coords (UV) list before vertex list");break;}
if (_siz != vertexcnt) {JFLog.log("Warning:texture list siz != vertex list siz");break;}
if (_siz == 0) break;
_float = new float[2];
for(a=0;a<_siz;a++) {
_float[0] = readfloat(); //U is okay
skip-=4;
_float[1] = 1.0f - readfloat(); //V must be inverted
skip-=4;
obj.addText(_float, mapidx);
if (debug) {
JFLog.log(String.format("t=%3.3f,%3.3f", _float[0] , _float[1]));
}
}
_float = null;
break;
case 0x4160: //obj matrix
//read in 3x3 matrix and show for now
int s = 0; //padding to convert to 4x4 matrix
for(a=0;a<3*3;a++) {
u32 = readuint32();
if ((a > 0) && (a % 3 == 0)) s++;
//not sure what this matrix is for??? But I don't seem to need it
// obj.m.m[a+s] = readfloat();
// if (debug) JFLog.log("m=" + obj.m.m[a+s]);
}
obj.org.x = readfloat();
obj.org.y = readfloat();
obj.org.z = readfloat();
if (debug) JFLog.log("pos=" + obj.org.x + "," + obj.org.y + "," + obj.org.z);
break;
case 0xb000: //keyframe header
break;
case 0xb002: //object node chunk
objidx = -1;
break;
case 0xb010: //keyframe object name
name = readname(-1);
readuint16(); //f1
readuint16(); //f2
parent = readuint16(); //parent
//find name in objlist
objidx = 0;
ok = false;
for(a=0;a<objlist.size();a++) {
if (objlist.get(a).equals(name)) {
ok = true;
break;
}
objidx++;
}
if (!ok) {
objidx = -1;
} else {
obj = mod.ol.get(objidx);
if (parent != 65535) obj.parent = parent;
obj = null;
}
//JFLog.log("0xb010 : name=" + name + ":objidx=" + objidx + ":parent=" + parent);
break;
case 0xb020: //keyframe pos
skip = head_len;
if (objidx == -1) break;
obj = mod.ol.get(objidx);
u16 = readuint16(); //flgs
skip -= 2;
u32 = readuint32(); //r1
skip -= 4;
u32 = readuint32(); //r2
skip -= 4;
keys = readuint32(); //keys
skip -= 4;
_float = new float[3];
for(a=0;a<keys;a++) {
frameno = readuint32(); //frame #
skip -= 4;
u16 = readuint16(); //flgs
skip -= 2;
u32 = 0;
if ((u16 & _3DS_FLG_TENSION) != 0) u32++;
if ((u16 & _3DS_FLG_CONTINUITY) != 0) u32++;
if ((u16 & _3DS_FLG_BIAS) != 0) u32++;
if ((u16 & _3DS_FLG_EASE_TO) != 0) u32++;
if ((u16 & _3DS_FLG_EASE_FROM) != 0) u32++;
if (u32 > 0) {
datapos += u32 * 4; //all ignored
skip -= u32 * 4;
}
trans = new GLTranslate();
for(b=0;b<3;b++) {
_float[b] = readfloat();
skip -= 4;
}
trans.x = _float[0];
trans.y = _float[1];
trans.z = _float[2];
//JFLog.log("pos["+frameno+"]:"+pos.x+","+pos.y+","+pos.z+":flgs="+u16);
obj.tl.put(frameno, trans);
if (obj.maxframeCount < frameno) obj.maxframeCount = frameno;
}
_float = null;
obj = null;
break;
case 0xb021: //keyframe rotate
skip = head_len;
if (objidx == -1) break;
obj = mod.ol.get(objidx);
u16 = readuint16(); //flgs
skip -= 2;
u32 = readuint32(); //r1
skip -= 4;
u32 = readuint32(); //r2
skip -= 4;
keys = readuint32(); //keys
skip -= 4;
_float = new float[4];
for(a=0;a<keys;a++) {
frameno = readuint32(); //frame #
skip -= 4;
u16 = readuint16(); //flgs
skip -= 2;
u32 = 0;
if ((u16 & _3DS_FLG_TENSION) != 0) u32++;
if ((u16 & _3DS_FLG_CONTINUITY) != 0) u32++;
if ((u16 & _3DS_FLG_BIAS) != 0) u32++;
if ((u16 & _3DS_FLG_EASE_TO) != 0) u32++;
if ((u16 & _3DS_FLG_EASE_FROM) != 0) u32++;
if (u32 > 0) {
datapos += u32 * 4; //all ignored
skip -= u32 * 4;
}
rot = new GLRotate();
for(b=0;b<4;b++) {
_float[b] = readfloat();
skip -= 4;
}
rot.angle = _float[0] * 57.2957795f; //convert to degrees
rot.x = _float[1];
rot.y = _float[2];
rot.z = _float[3];
//JFLog.log("rot["+frameno+"]:"+rot.angle+","+rot.x+","+rot.y+","+rot.z+":flgs="+u16);
obj.rl.put(frameno, rot);
if (obj.maxframeCount < frameno) obj.maxframeCount = frameno;
}
_float = null;
obj = null;
break;
case 0xb022: //keyframe scale
skip = head_len;
if (objidx == -1) break;
obj = mod.ol.get(objidx);
u16 = readuint16(); //flgs
skip -= 2;
u32 = readuint32(); //r1
skip -= 4;
u32 = readuint32(); //r2
skip -= 4;
keys = readuint32(); //keys
skip -= 4;
_float = new float[3];
for(a=0;a<keys;a++) {
frameno = readuint32(); //frame #
skip -= 4;
u16 = readuint16(); //flgs
skip -= 2;
u32 = 0;
if ((u16 & _3DS_FLG_TENSION) != 0) u32++;
if ((u16 & _3DS_FLG_CONTINUITY) != 0) u32++;
if ((u16 & _3DS_FLG_BIAS) != 0) u32++;
if ((u16 & _3DS_FLG_EASE_TO) != 0) u32++;
if ((u16 & _3DS_FLG_EASE_FROM) != 0) u32++;
if (u32 > 0) {
datapos += u32 * 4; //all ignored
skip -= u32 * 4;
}
scale = new GLScale();
for(b=0;b<3;b++) {
_float[b] = readfloat();
skip -= 4;
}
scale.x = _float[0];
scale.y = _float[1];
scale.z = _float[2];
//JFLog.log("scale["+frameno+"]:"+scale.x+","+scale.y+","+scale.z+":flgs="+u16);
obj.sl.put(frameno, scale);
if (obj.maxframeCount < frameno) obj.maxframeCount = frameno;
}
_float = null;
obj = null;
break;
default:
skip = head_len;
break;
}
if (skip > 0) {
datapos += skip;
skip = 0;
}
}
//setup any lights
_siz = mod.ol.size();
for(a=0;a<_siz;a++) {
obj = mod.ol.get(a);
}
//delete temp lists
matlist.clear();
objlist.clear();
return mod;
}
//for load3DS()
private String readname(int maxread) {
String ret = "";
char ch;
while (!eof()) {
ch = (char)data[datapos++];
if (maxread != -1) {
maxread--;
if (maxread == 0) break;
}
if (ch == 0) break;
ret += ch;
}
if (maxread > 0) {datapos+=maxread;}
return ret;
}
//private class for load3DS()
private static class material {
String name;
String filename;
}
}