Ok, so here is a basic MD2-Loader. It's not exactly the one that's used in Loader, but behaves bascially the same. The one in Loader isn't helpful because it uses some shortcuts into Object3D that are not public. Anyway, here you go:
package naroth.util.tools;
import java.io.*;
import com.threed.jpct.*;
/**
* An alternative MD2Loader for demonstration purpose only.
*/
public class MD2Loader {
public static Object3D loadMD2(InputStream is, float newScale) throws Exception {
int cnt=0;
byte[] buf=new byte[1000];
ByteArrayOutputStream bos=new ByteArrayOutputStream();
do {
cnt=is.read(buf);
if (cnt!=-1) {
bos.write(buf, 0, cnt);
}
} while (cnt!=-1);
buf=bos.toByteArray();
int magicNum=getInt(buf, 0);
if (magicNum!=844121161) {
Logger.log("Not a valid MD2-file!", Logger.ERROR);
}
int version=getInt(buf, 4);
int skinWidth=getInt(buf, 8);
int skinHeight=getInt(buf, 12);
int frameSize=getInt(buf, 16);
int numSkins=getInt(buf, 20);
int numVertices=getInt(buf, 24);
int numTexCoords=getInt(buf, 28);
int numTriangles=getInt(buf, 32);
int numGlCommands=getInt(buf, 36);
int numFrames=getInt(buf, 40);
int offsetTexCoords=getInt(buf, 48);
int offsetTriangles=getInt(buf, 52);
int offsetFrames=getInt(buf, 56);
Logger.log("Magic number: "+magicNum, Logger.MESSAGE);
Logger.log("Version: "+version, Logger.MESSAGE);
Logger.log("Skin width: "+skinWidth, Logger.MESSAGE);
Logger.log("Skin height: "+skinHeight, Logger.MESSAGE);
Logger.log("Frame size: "+frameSize, Logger.MESSAGE);
Logger.log("Number of skins: "+numSkins, Logger.MESSAGE);
Logger.log("Number of Vertices: "+numVertices, Logger.MESSAGE);
Logger.log("Number of Texture coordinates: "+numTexCoords, Logger.MESSAGE);
Logger.log("Number of triangles: "+numTriangles, Logger.MESSAGE);
Logger.log("Number of GL-commands: "+numGlCommands, Logger.MESSAGE);
Logger.log("Number of Frames: "+numFrames, Logger.MESSAGE);
int[][] texCoords=new int[numTexCoords][2];
int[][] triVertex=new int[numTriangles][3];
int[][] triTexture=new int[numTriangles][3];
Logger.log("Reading Texture coordinates...", Logger.MESSAGE);
int oF=offsetTexCoords;
for (int i=0; i<numTexCoords; i++) {
int u=getShortInt(buf, oF+(i*4));
int v=getShortInt(buf, oF+(i*4)+2);
texCoords[i][0]=u;
texCoords[i][1]=v;
}
Logger.log("Done!", Logger.MESSAGE);
Logger.log("Reading polygonal data...", Logger.MESSAGE);
oF=offsetTriangles;
for (int i=0; i<numTriangles; i++) {
int iMul=oF+i*12;
int p1=getShortInt(buf, iMul);
int p2=getShortInt(buf, iMul+2);
int p3=getShortInt(buf, iMul+4);
int t1=getShortInt(buf, iMul+6);
int t2=getShortInt(buf, iMul+8);
int t3=getShortInt(buf, iMul+10);
triVertex[i][0]=p1;
triVertex[i][1]=p2;
triVertex[i][2]=p3;
triTexture[i][0]=t1;
triTexture[i][1]=t2;
triTexture[i][2]=t3;
}
Logger.log("Done!", Logger.MESSAGE);
float scales[][]=new float[numFrames][3];
float trans[][]=new float[numFrames][3];
String names[]=new String[numFrames];
int[][][] vertices=new int[numFrames][numVertices][3];
Logger.log("Reading keyframes...", Logger.MESSAGE);
for (int i=0; i<numFrames; i++) {
oF=(i*frameSize)+offsetFrames;
float scaleX=Float.intBitsToFloat(getInt(buf, oF));
float scaleY=Float.intBitsToFloat(getInt(buf, oF+4));
float scaleZ=Float.intBitsToFloat(getInt(buf, oF+8));
float transX=Float.intBitsToFloat(getInt(buf, oF+12));
float transY=Float.intBitsToFloat(getInt(buf, oF+16));
float transZ=Float.intBitsToFloat(getInt(buf, oF+20));
String name=new String(buf, oF+24, 16);
scales[i][0]=scaleX;
scales[i][1]=scaleY;
scales[i][2]=scaleZ;
trans[i][0]=transX;
trans[i][1]=transY;
trans[i][2]=transZ;
names[i]=name;
oF+=40;
for (int p=0; p<numVertices; p++) {
int iMul=oF+(p*4);
int v1=getUnsignedByte(buf, iMul);
int v2=getUnsignedByte(buf, iMul+1);
int v3=getUnsignedByte(buf, iMul+2);
vertices[i][p][0]=v1;
vertices[i][p][1]=v2;
vertices[i][p][2]=v3;
}
}
Logger.log("Done!", Logger.MESSAGE);
Logger.log("Coverting MD2-format into jPCT-format...", Logger.MESSAGE);
Object3D obj=new Object3D(numTriangles+1);
Object3D tmp=new Object3D(numTriangles+1);
// If the triangles in this model are not sharing vertices (i.e. the model isn't closed), this will be needed.
// This simple loader doesn't detect this rare case.
//obj.disableVertexSharing();
//tmp.disableVertexSharing();
Animation anim=new Animation(numFrames);
String lastSeq="dummy";
SimpleVector[] vs=new SimpleVector[3];
for (int i=0; i<3; i++) {
vs[i]=new SimpleVector();
}
for (int p=0; p<numFrames; p++) {
tmp.clearObject();
for (int i=0; i<numTriangles; i++) {
for (int h=0; h<3; h++) {
vs[h].x=((float) vertices[p][triVertex[i][h]][0])*scales[p][0]+trans[p][0];
vs[h].y=((float) vertices[p][triVertex[i][h]][1])*scales[p][1]+trans[p][1];
vs[h].z=((float) vertices[p][triVertex[i][h]][2])*scales[p][2]+trans[p][2];
vs[h].scalarMul(newScale);
}
int t1p=triTexture[i][0];
int t2p=triTexture[i][2];
int t3p=triTexture[i][1];
float t1u=texCoords[t1p][0]/(float) skinWidth;
float t1v=texCoords[t1p][1]/(float) skinHeight;
float t2u=texCoords[t2p][0]/(float) skinWidth;
float t2v=texCoords[t2p][1]/(float) skinHeight;
float t3u=texCoords[t3p][0]/(float) skinWidth;
float t3v=texCoords[t3p][1]/(float) skinHeight;
if (p==0) {
obj.addTriangle(vs[0], t1u, t1v, vs[1], t2u, t2v, vs[2], t3u, t3v);
}
tmp.addTriangle(vs[0], t1u, t1v, vs[1], t2u, t2v, vs[2], t3u, t3v);
}
tmp.calcBoundingBox();
tmp.calcNormals();
String tmpS=getSequenceName(names[p]);
if (!tmpS.equals(lastSeq)) {
Logger.log("Processing: "+tmpS+"...", Logger.MESSAGE);
lastSeq=tmpS;
anim.createSubSequence(tmpS);
}
anim.addKeyFrame(tmp.getMesh().cloneMesh(Mesh.COMPRESS));
}
obj.calcBoundingBox();
obj.setAnimationSequence(anim);
Logger.log("Done!", Logger.MESSAGE);
return obj;
}
private static int getInt(byte[] b, int offset) {
if (offset+3<b.length) {
int a=unsignedByteToInt(b[offset]);
int d=unsignedByteToInt(b[offset+1]);
int e=unsignedByteToInt(b[offset+2]);
int f=unsignedByteToInt(b[offset+3]);
return (int) (a+(d<<8)+(e<<16)+(f<<24));
}
return-1;
}
private static int getShortInt(byte[] b, int offset) {
if (offset+1<b.length) {
int a=unsignedByteToInt(b[offset]);
int d=unsignedByteToInt(b[offset+1]);
return (int) (a+(d<<8));
}
return-1;
}
private static int getUnsignedByte(byte[] b, int offset) {
if (offset<b.length) {
return unsignedByteToInt(b[offset]);
}
return-1;
}
private static int unsignedByteToInt(byte b) {
return (int) b&0xFF;
}
private static String getSequenceName(String a) {
char c=' ';
StringBuffer res=new StringBuffer(16);
a=a.toLowerCase();
for (int i=0; i<a.length(); i++) {
c=a.charAt(i);
if (c>='a'&&c<='z') {
res.append(c);
}
}
return res.toString();
}
}