module MD2;


//..............................................................................
function StreamReadString(Stream _ifs, int _l) {
    int i=0;
    String s_tmpname<=new String;
    s_tmpname.alloc(_l);
    int c;
    do {
	c=_ifs.i8;
	s_tmpname[i]=c;
    } while (++i<_l);
    s_tmpname.fixLength();
    return deref s_tmpname;
}

// -----------------------------------------------------------------------------
class CMD2_Triangle {
    int vertexIndices [3];
    int textureIndices[3];
}


// -----------------------------------------------------------------------------
class CMD2_Frame {
    CMD2  md2;

    float    scaling[3];
    float    translation[3];
    String   name;
    int      indexed_vertices[];
    float    vertices[];
    float    frame_scl;

    float    face_normals[];  // md2.numTriangles
    float    point_normals[]; // numTriangles*3

    /* void    */ convertIndexedVertices();
    /*         */ scaleFrame(float _scl);
    /*         */ calcNormals();

    /* CGLMesh */ getGLMesh(Texture _tex);
    /*         */ createGLMesh(CGLMesh _mesh, Texture _tex);
}

//..............................................................................
CMD2_Frame::convertIndexedVertices() {
    int ni=(indexed_vertices.numElements/4);
    vertices.alloc(ni*3); vertices.numElements=vertices.maxElements;
    int i=0;
    int j=0;
    float maxx=0,maxy=0,maxz=0,t;
    compile loop(ni)
	{
	    float x,y,z;
	    t=(indexed_vertices[i++]*scaling[0])+translation[0];
	    if(abs(t)>maxx)
		maxx=abs(t);
	    vertices[j++]=t;
	    t=(indexed_vertices[i++]*scaling[1])+translation[1];
	    if(abs(t)>maxy)
		maxy=abs(t);
	    vertices[j++]=t;
	    t=(indexed_vertices[i++]*scaling[2])+translation[2];
	    if(abs(t)>maxz)
		maxz=abs(t);
	    vertices[j++]=t;
	    
	    i++; // skip lightNormalIndex
	}
    // ---- normalize ----
    maxx=(maxx==0)?1:(1/maxx);
    maxy=(maxy==0)?1:(1/maxy);
    maxz=(maxz==0)?1:(1/maxz);
    frame_scl=(maxx<maxy)?(maxx<maxz)?maxx:maxz:(maxy<maxz)?maxy:maxz;
    //trace "CMD2_Frame::convertIndexedVertices: converted "+indexed_vertices.numElements+" vertices.";
}

CMD2_Frame::scaleFrame {
    int j=0;
    float cx,cy,cz;
    int ni=indexed_vertices.numElements/4;
    compile loop(ni)
	{
	    cx=vertices[j]; cy=vertices[j+1]; cz=vertices[j+2];
	    vertices[j  ]=-cy*_scl;
	    vertices[j+1] =cz*_scl;
	    vertices[j+2]=-cx*_scl;
	    j=j+3;
	}
}

CMD2_Frame::calcNormals() {
}

//..............................................................................
CMD2_Frame::getGLMesh {
    CGLMesh m<=new CGLMesh;
    createGLMesh(m, _tex);
    return deref m;
}

CMD2_Frame::createGLMesh {
    CGLMesh m<=_mesh;
    m.tex<=_tex;
    FloatArray m_vtx<=m.vertices;
    FloatArray m_nrm<=m.normals;
    FloatArray m_uv<=m.uvcoords;
    IntArray m_col<=m.colors;
    int i_totalNumVertices=md2.numTriangles*3;
    m_vtx.alloc(i_totalNumVertices*3); m_vtx.numElements=m_vtx.maxElements;
    m_nrm.alloc(i_totalNumVertices*3); m_nrm.numElements=m_nrm.maxElements;
    m_uv.alloc(i_totalNumVertices*2);  m_uv .numElements=m_uv .maxElements;
    m_col.alloc(i_totalNumVertices);   m_col.numElements=m_col.maxElements;
    int i=0,j=0,k=0,l=0;
    int ti;
    CMD2_Triangle ctri;
    compile loop(md2.numTriangles)
	{
	    ctri<=md2.triangles[j++];

	    ti=ctri.vertexIndices[0]*3;
	    m_vtx[i++]=vertices[ti++];
	    m_vtx[i++]=vertices[ti++];
	    m_vtx[i++]=vertices[ti];
	    //trace "vtx=("+m_vtx[i-3]+"; "+m_vtx[i-2]+"; "+m_vtx[i-1]+")";
	    ti=ctri.textureIndices[0]*2;
	    m_uv[k++]=md2.texcoords[ti++];
	    m_uv[k++]=md2.texcoords[ti];
//  	    trace "uv=("+m_uv[k-2]+"; "+m_uv[k-1]+")";
	    m_col[l++]=#ffffffff;

	    ti=ctri.vertexIndices[1]*3;
	    m_vtx[i++]=vertices[ti++];
	    m_vtx[i++]=vertices[ti++];
	    m_vtx[i++]=vertices[ti];
	    ti=ctri.textureIndices[1]*2;
	    m_uv[k++]=md2.texcoords[ti++];
	    m_uv[k++]=md2.texcoords[ti];
	    m_col[l++]=#ffffffff;

	    ti=ctri.vertexIndices[2]*3;
	    m_vtx[i++]=vertices[ti++];
	    m_vtx[i++]=vertices[ti++];
	    m_vtx[i++]=vertices[ti];
	    ti=ctri.textureIndices[2]*2;
	    m_uv[k++]=md2.texcoords[ti++];
	    m_uv[k++]=md2.texcoords[ti];
	    m_col[l++]=#ffffffff;
	}
}

// -----------------------------------------------------------------------------
class CMD2 {
    int version;
    int skinWidth;
    int skinHeight;
    int frameSize;
    int numSkins;
    int numVertices;
    int numTexCoords;
    int numTriangles;
    int numGLCommands;
    int numFrames;
    int offsetSkins;
    int offsetTexCoords;
    int offsetTriangles;
    int offsetFrames;
    int offsetGLCommands;
    int offsetEnd;

    CMD2_Triangle triangles[];
    CMD2_Frame    frames[];
    float         texcoords[]; /* u/v pairs, normalized */

    /*bool*/ loadMD2(String _name);
    /*bool*/ loadMD2FromStream(Stream _ifs);
    /*    */ parseTriangles(Stream _ifs);
    /*    */ parseFrames(Stream _ifs);
    /*    */ parseTexCoords(Stream _ifs);
    /*CGLShapeAnim*/ getGLShapeAnim(Texture _tex);
}


//..............................................................................
CMD2::loadMD2 {
    PakFile f;
    if(f.open(_name))
	{
	    f.byteOrder=LITTLE_ENDIAN;
	    int r=loadMD2FromStream(f);
	    f.close();
	    return r;
	}
    else
	{
	    trace "[---] error opening MD2 filestream \""+_name+"\".";
	    return 0;
	}
}

//..............................................................................
CMD2::loadMD2FromStream() {
    trace "loadMD2FromStream()";
    if(_ifs.i8=='I')
	if(_ifs.i8=='D')
	    if(_ifs.i8=='P')
		if(_ifs.i8/*=='2'*/)
		    {
			trace "[...] found header";
			version          = _ifs.i32;
			skinWidth        = _ifs.i32;
			skinHeight       = _ifs.i32;
			frameSize        = _ifs.i32;
			numSkins         = _ifs.i32;
			numVertices      = _ifs.i32;
			numTexCoords     = _ifs.i32;
			numTriangles     = _ifs.i32;
			numGLCommands    = _ifs.i32;
			numFrames        = _ifs.i32;
			offsetSkins      = _ifs.i32;
			offsetTexCoords  = _ifs.i32;
			offsetTriangles  = _ifs.i32;
			offsetFrames     = _ifs.i32;
			offsetGLCommands = _ifs.i32;
			offsetEnd        = _ifs.i32;

			trace "\tversion         ="+version;
			trace "\tskinWidth       ="+skinWidth;
			trace "\tskinHeight      ="+skinHeight;
			trace "\tframeSize       ="+frameSize;
			trace "\tnumSkins        ="+numSkins;
			trace "\tnumVertices     ="+numVertices;
			trace "\tnumTriangles    ="+numTriangles;
			trace "\tnumGLCommands   ="+numGLCommands;
			trace "\tnumFrames       ="+numFrames;
			trace "\toffsetSkins     ="+offsetSkins;
			trace "\toffsetTexCoords ="+offsetTexCoords;
			trace "\toffsetTriangles ="+offsetTriangles;
			trace "\toffsetFrames    ="+offsetFrames;
			trace "\toffsetGLCommands="+offsetGLCommands;
			trace "\toffsetEnd       ="+offsetEnd;

  			parseTexCoords(_ifs);
  			parseTriangles(_ifs);
  			parseFrames(_ifs);

			return 1;
		    }
    return 0;
}

//..............................................................................
CMD2::parseTriangles {
    _ifs.offset=offsetTriangles;
    triangles.alloc(numTriangles); 
    CMD2_Triangle ctri;
    loop(numTriangles)
	{
		ctri<=triangles.nextFree;
		IntArray tex<=ctri.textureIndices;
		IntArray vtx<=ctri.vertexIndices;
		tex.numElements=3;
		vtx.numElements=3;
		vtx[0]=_ifs.i16;
		vtx[1]=_ifs.i16;
		vtx[2]=_ifs.i16;
		tex[0]=_ifs.i16;
		tex[1]=_ifs.i16;
		tex[2]=_ifs.i16;
	}
    trace "[...] read "+numTriangles+" triangles.";
}

//..............................................................................
CMD2::parseFrames {
    _ifs.offset=offsetFrames;
    frames.alloc(numFrames);
    CMD2_Frame cf;
    int framenr=0;
    float scl=1;
    loop(numFrames)
	{
	    cf<=frames.nextFree;
	    cf.md2<=this;
	    IntArray vtx<=cf.indexed_vertices;
	    vtx.alloc(numVertices*4); vtx.numElements=vtx.maxElements;
	    FloatArray fa<=cf.scaling;
	    fa[0]=_ifs.f32;
	    fa[1]=_ifs.f32;
	    fa[2]=_ifs.f32;
	    fa<=cf.translation;
	    fa[0]=_ifs.f32;
	    fa[1]=_ifs.f32;
	    fa[2]=_ifs.f32;
	    cf.name=StreamReadString(_ifs, 16);
	    trace "parseFrame["+framenr+++"] name=\""+cf.name+"\".";
	    int i=0;
	    compile loop(numVertices)
		{
		    vtx[i++]=_ifs.i8;
		    vtx[i++]=_ifs.i8;
		    vtx[i++]=_ifs.i8;
		    vtx[i++]=_ifs.i8;
		}
	    cf.convertIndexedVertices();
	    if(cf.frame_scl<scl)
		scl=cf.frame_scl;
	}
    frames.numElements=0;
    loop(numFrames)
	{
	    cf<=frames.nextFree;
	    cf.scaleFrame(scl);
	}
}

//..............................................................................
CMD2::parseTexCoords {
    trace "parseTExCoords:";
    if(numTexCoords&&skinWidth&&skinHeight)
	{
	    texcoords.alloc(2*numTexCoords);
	    _ifs.offset=offsetTexCoords;
	    int i=0;
	    compile loop(numTexCoords)
		{
		    texcoords[i++]=tcfloat(_ifs.i16)/skinWidth;
		    texcoords[i++]=tcfloat(_ifs.i16)/skinHeight;
		    trace "parse texcoord ("+texcoords[i-2]+"; "+texcoords[i-1]+")";
		}
	}
    trace "[...] CMD2::parseTexCoords: parsed "+numTexCoords+" texture coordinates.";
}

//..............................................................................
CMD2::getGLShapeAnim {
    CGLShapeAnim a<=new CGLShapeAnim;
    a.meshes.alloc(numFrames);
    int i=0;
    compile loop(numFrames)
	{
	    CGLMesh m<=a.meshes.nextFree;
	    CMD2_Frame f<=frames[i++];
	    f.createGLMesh(m, _tex);
	}
    a.initAnim(0, numFrames, 0, numFrames);
    a.initVertices();
    return deref a;
}