/* rtfview_v2.cpp
Copyright 2009 Shaun Thompson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.*/
/***************************************************************************
0x01 blocks:
the first one, which refers to a 0x02 block, which in turn refers to 0x10
blocks. The remainder, for example:
0x10|0x13|0x20|0x21|0x40 // vertices
0x20|0x21|0x40 // normals
0x20|0x21|0x40 // texcoords
We must read each offset in the 0x01 and 0x02 blocks until we hit NULL and
we have loaded all of the vertex blocks.
Other blocks known as follows:
0x02 - always points to 0x10 blocks
0x10 - unknown, contains floats, fixed length, 6 QWORDS (most likely material definitions)
0x13 - hit box, fixed length, 8 QWORDS
0x20 - unknown, fixed length, 4 QWORDS
0x21 - unknown, fixed length, 5 QWORDS (second QWORD, first WORD is texture file)
0x40 - vertex block, fixed length header, variable length data,
vertCount-1 is 8th WORD past 0x4000
/***************************************************************************/
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/freeglut.h>
#include <SDL/sdl.h>
#include <SDL/SDL_opengl.h>
#include <math.h>
#include <stdio.h>
#include "common.h"
#include "linked_lists.h"
#include "p6t.h"
SDL_Surface *surface; // This surface will tell us the details of the image
SDL_Surface *InitVideo(int w, int h, int bpp)
{
SDL_Surface *screen = NULL;
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
if ( (screen=SDL_SetVideoMode( w, h, 32, SDL_OPENGL )) == NULL ) {
fprintf(stderr, "Couldn't set GL mode: %s\n", SDL_GetError());
SDL_Quit();
return NULL;
}
glViewport(0,0,w,h); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)w/(GLfloat)h,0.1f,50.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
glFrontFace(GL_CW);
glMatrixMode(GL_PROJECTION);
glPointSize(2.0);
glEnable( GL_TEXTURE_2D );
return screen; // Initialization Went OK
}
GLuint IMD_DrawBlocks(DWORD numSegs)
{
DWORD i = 0;
DWORD j = 0;
DWORD flagTest = 0;
IMD_SEG *curSeg = NULL;
GLuint displayList;
char outstring[255];
FILE *log = NULL;
BYTE dump = 1;
BYTE listFlag = 0;
IMD_VERT firstVert[3];
if(dump)
log = fopen("imd_log.txt", "w");
displayList = glGenLists(1);
glNewList(displayList,GL_COMPILE);
for(i = 0; i < numSegs; i++)
{
curSeg = getSeg();
if(curSeg->texInfo1.textureFN >= 648)
{
//glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, curSeg->texInfo1.texture);
}
if(dump)
{
sprintf(outstring, "Seg: %i\n\n", i);
fputs(outstring, log);
}
j = 0;
while( j <= curSeg->vertHeader->numVerts)
{
if((DWORD)curSeg->verts[j].flag == 0xbf800000 || (DWORD)curSeg->verts[j].flag == 0x3f800000 || (DWORD)curSeg->verts[j].flag == 0x00000001)
{
if((DWORD)curSeg->verts[j].flag == 0x00000001)
{
if(listFlag == 1)
{
glEnd();
}
listFlag = 1;
glBegin(GL_TRIANGLE_STRIP);
if(dump)
fputs("\n\nglBegin:\n\n", log);
}
/*if((DWORD)curSeg->verts[j].flag == 0xbf800000)
glColor3ub(0,0,255);
else if((DWORD)curSeg->verts[j].flag == 0x3f800000)
glColor3ub(0,255,0);
else
glColor3ub(255,0,0);*/
if((DWORD)curSeg->verts[j].flag == 0x00000001 || (DWORD)curSeg->verts[j+1].flag == 0x00000001)
{
if(dump)
{
sprintf(outstring, "\nglVertex3f: %f, %f, %f Flag: %x\n glNormal3f: %f, %f, %f Flag: %x\n",
curSeg->verts[j].x, curSeg->verts[j].y, curSeg->verts[j].z,
(DWORD)curSeg->verts[j].flag, curSeg->verts[j+1].x, curSeg->verts[j+1].y, curSeg->verts[j+1].z,
(DWORD)curSeg->verts[j+1].flag);
fputs(outstring, log);
}
if(curSeg->verts[j].flag == 0 || curSeg->verts[j].flag == 0x43000000 || curSeg->verts[j].flag == 0x17000000)
j+=1;
if(curSeg->texInfo1.texture > 0 && curSeg->verts[j+2].z == 1.0)
{
if(dump)
{
sprintf(outstring, "glTexCoord2f: %f, %f Flag: %x\n", curSeg->verts[j+2].x, curSeg->verts[j].y, (DWORD)curSeg->verts[j].flag);
fputs(outstring, log);
}
glTexCoord2f(curSeg->verts[j+2].x, curSeg->verts[j+2].y);
glNormal3f(curSeg->verts[j+1].x, curSeg->verts[j+1].y, curSeg->verts[j+1].z);
glVertex3f(curSeg->verts[j].x, curSeg->verts[j].y, curSeg->verts[j].z);
j+=3;
}
else
{
glNormal3f(curSeg->verts[j+1].x, curSeg->verts[j+1].y, curSeg->verts[j+1].z);
glVertex3f(curSeg->verts[j].x, curSeg->verts[j].y, curSeg->verts[j].z);
j+=2;
}
}
if(dump)
{
sprintf(outstring, "\nglVertex3f: %f, %f, %f Flag: %x\n glNormal3f: %f, %f, %f Flag: %x\n",
curSeg->verts[j].x, curSeg->verts[j].y, curSeg->verts[j].z,
(DWORD)curSeg->verts[j].flag, curSeg->verts[j+1].x, curSeg->verts[j+1].y, curSeg->verts[j+1].z,
(DWORD)curSeg->verts[j+1].flag);
fputs(outstring, log);
}
if(curSeg->verts[j].flag == 0 || curSeg->verts[j].flag == 0x43000000 || curSeg->verts[j].flag == 0x17000000)
j+=1;
if(curSeg->texInfo1.texture > 0 && curSeg->verts[j+2].z == 1.0)
{
if(dump)
{
sprintf(outstring, "glTexCoord2f: %f, %f Flag: %x\n", curSeg->verts[j+2].x, curSeg->verts[j].y, (DWORD)curSeg->verts[j].flag);
fputs(outstring, log);
}
glTexCoord2f(curSeg->verts[j+2].x, curSeg->verts[j+2].y);
glNormal3f(curSeg->verts[j+1].x, curSeg->verts[j+1].y, curSeg->verts[j+1].z);
glVertex3f(curSeg->verts[j].x, curSeg->verts[j].y, curSeg->verts[j].z);
j+=3;
}
else
{
glNormal3f(curSeg->verts[j+1].x, curSeg->verts[j+1].y, curSeg->verts[j+1].z);
glVertex3f(curSeg->verts[j].x, curSeg->verts[j].y, curSeg->verts[j].z);
j+=2;
}
}
else
{
j+=1;
}
}
j = 0;
listFlag = 0;
//if(curSeg->texInfo1.texture > 0)
//glDisable(GL_TEXTURE_2D);
glEnd();
nextSeg();
}
if(dump)
fclose(log);
glEndList();
return displayList;
}
void IMD_DrawBBox(IMD_VERT *bbox)
{
glColor3ub(0, 255, 0);
glBegin(GL_POINTS);
glVertex3f(bbox[0].x, bbox[0].y, bbox[0].z);
glVertex3f(bbox[1].x, bbox[1].y, bbox[1].z);
glVertex3f(bbox[2].x, bbox[2].y, bbox[2].z);
glVertex3f(bbox[3].x, bbox[3].y, bbox[3].z);
glVertex3f(bbox[4].x, bbox[4].y, bbox[4].z);
glVertex3f(bbox[5].x, bbox[5].y, bbox[5].z);
glVertex3f(bbox[6].x, bbox[6].y, bbox[6].z);
glVertex3f(bbox[7].x, bbox[7].y, bbox[7].z);
glEnd();
}
DWORD IMD_Load(char *fn)
{
IMD_HEAD1 header;
IMD_OFS curDataOfs; // current offset in 0x01 block (offset to 0x40 blocks)
IMD_OFS curSegOfs; // current offset in post hitbox block (offset to 0x02, 0x01 blocks)
IMD_SEG *curSeg; // current 0x01 block
IMD_SEG_ENT tempSeg;
IMD_VERT_HEADER *curVertHeader = (IMD_VERT_HEADER*)malloc(sizeof(IMD_VERT_HEADER));
IMD_VERT *curVerts = NULL;
FILE *infile = NULL;
WORD numSegs = 0;
DWORD curDataTableOfs; // current data table offset; from post hit box
DWORD i = 0;
DWORD test = 0;
WORD numSegEnts = 0;
char texFN[MAX_PATH];
infile = fopen(fn, "rb");
if(infile == NULL)
{
printf("%s not found!\n", fn);
return 1;
}
// get header
fread(&header, sizeof(IMD_HEAD1), 1, infile);
// skip standard hit box
fseek(infile, header.typeOffset+0x10, SEEK_SET);
//fread(&curData->bbox, sizeof(IMD_VERT)*8, 1, infile);
fseek(infile, sizeof(IMD_VERT)*8, SEEK_CUR);
// get numSegEnts
fread(&numSegEnts, sizeof(WORD), 1, infile);
// seek to beginning of Data Offset Table
fseek(infile, 0x1E, SEEK_CUR);
// I'm going to ignore the 0x02 block for now since I dont know what 0x10 blocks are for
fread(&curDataOfs, sizeof(IMD_OFS), 1, infile); // read first data offset (0x02 block)
if(curDataOfs.offset != 0x02000000)
fseek(infile, -(sizeof(IMD_OFS)), SEEK_CUR);
while(curDataOfs.offset!=NULL)
{
curSeg = (IMD_SEG*)malloc(sizeof(IMD_SEG));
fread(&curDataOfs, sizeof(IMD_OFS), 1, infile); // read second offset
curDataTableOfs = ftell(infile); // save position
// seek to seg
fseek(infile, curDataOfs.offset+32, SEEK_SET);
// read current seg up to normalOffset
fread(curSeg, sizeof(IMD_SEG)-(52+(sizeof(IMD_SEG_ENT)*3)+(sizeof(IMD_TEXINFO)*3)), 1, infile);
fread(&curSeg->vertOffset, sizeof(IMD_SEG_ENT), 1, infile);
fread(&curSeg->vertOffset2, sizeof(IMD_SEG_ENT), 1, infile);
// if we have normals check for textures
if(curSeg->vertOffset2.vertOffset != 0)
fread(&curSeg->vertOffset3, sizeof(IMD_SEG_ENT), 1, infile);
// now we handle loading the seg block data
curSeg->vertHeader = (IMD_VERT_HEADER*)malloc(sizeof(IMD_VERT_HEADER));
fseek(infile, curSeg->vertOffset.vertOffset, SEEK_SET);
fread(curSeg->vertHeader, sizeof(IMD_VERT_HEADER), 1, infile);
curSeg->verts = (IMD_VERT*)malloc(sizeof(IMD_VERT)*curSeg->vertHeader->numVerts);
fread(curSeg->verts, sizeof(IMD_VERT), curSeg->vertHeader->numVerts, infile);
fseek(infile, curSeg->vertOffset.texInfo, SEEK_SET);
fread(&curSeg->texInfo1, sizeof(IMD_TEXINFO)-sizeof(GLuint), 1, infile);
if(curSeg->texInfo1.textureFN != 0)
{
sprintf(texFN, "%05i.P6T", curSeg->texInfo1.textureFN);
curSeg->texInfo1.texture = P6T_LoadTexture(texFN, curSeg);
}
i++;
/*if(curSeg->vertOffset2.vertOffset != 0)
{
curSeg->vertHeader2 = (IMD_VERT_HEADER*)malloc(sizeof(IMD_VERT_HEADER));
fseek(infile, curSeg->vertOffset2.vertOffset, SEEK_SET);
fread(curSeg->vertHeader2, sizeof(IMD_VERT_HEADER), 1, infile);
curSeg->normals = (IMD_VERT*)malloc(sizeof(IMD_VERT)*curSeg->vertHeader2->numVerts);
fread(curSeg->verts2, sizeof(IMD_VERT), curSeg->vertHeader2->numVerts, infile);
fseek(infile, curSeg->normalOffset.texInfo, SEEK_SET);
fread(&curSeg->texInfo2, sizeof(IMD_TEXINFO)-sizeof(GLuint), 1, infile);
if(curSeg->texInfo2.textureFN != 0)
{
sprintf(texFN, "%05i.P6T", curSeg->texInfo2.textureFN);
curSeg->texInfo2.texture = P6T_LoadTexture(texFN, curSeg);
}
i++;
if(curSeg->texCoordOffset.vertOffset != 0)
{
curSeg->texCoordHeader = (IMD_VERT_HEADER*)malloc(sizeof(IMD_VERT_HEADER));
fseek(infile, curSeg->texCoordOffset.vertOffset, SEEK_SET);
fread(curSeg->texCoordHeader, sizeof(IMD_VERT_HEADER), 1, infile);
curSeg->texCoords = (IMD_VERT*)malloc(sizeof(IMD_VERT)*curSeg->texCoordHeader->numVerts);
fread(curSeg->texCoords, sizeof(IMD_VERT), curSeg->texCoordHeader->numVerts, infile);
fseek(infile, curSeg->texCoordOffset.texInfo, SEEK_SET);
fread(&curSeg->texInfo3, sizeof(IMD_TEXINFO)-sizeof(GLuint), 1, infile);
if(curSeg->texInfo3.textureFN != 0)
{
sprintf(texFN, "%05i.P6T", curSeg->texInfo3.textureFN);
curSeg->texInfo3.texture = P6T_LoadTexture(texFN, curSeg);
}
i++;
}*/
}
pushSeg(curSeg);
numSegs++;
fseek(infile, curDataTableOfs, SEEK_SET); // restore position
}
fclose(infile);
printf("Total Segs: %i\n", numSegs);
return numSegs;
}
int main(int argc, char *argv[])
{
SDL_Surface *screen = NULL;
SDL_Event event;
FILE *infile = NULL;
FILE *outfile = NULL;
GLfloat scale = 0.5f;
GLfloat xrot, yrot, zrot;
GLfloat xtrans, ytrans, ztrans;
GLuint displayList;
xtrans=ytrans=ztrans=0;
xrot=yrot=zrot=0;
char title[MAX_PATH];
DWORD numSegs = 0;
//system("pause");
if(argc<2)
{
printf("USAGE: rtfview <infile>\n");
return 0;
}
screen = InitVideo(1024, 768, 32);
numSegs = IMD_Load(argv[1]);
sprintf(title, "RTFView: %s", argv[1]);
SDL_WM_SetCaption(title, NULL);
displayList = IMD_DrawBlocks(numSegs);
while(SDL_WaitEvent(&event) >= 0)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f+xtrans,0.0f-0.4f+ytrans,-3.0f+1.5f+ztrans);
glScalef(scale, scale, scale);
glRotatef(180+xrot, 1.0f, 0.0f, 0.0f);
glRotatef(90+yrot, 0.0f, 1.0f, 0.0f);
glCallList(displayList);
glMatrixMode(GL_PROJECTION);
SDL_GL_SwapBuffers();
switch(event.type)
{
case SDL_QUIT:
exit(0);
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_KP_PLUS:
ztrans += 0.25f;
break;
case SDLK_KP_MINUS:
ztrans -= 0.25f;
break;
case SDLK_LEFT:
yrot -= 10.0f;
break;
case SDLK_RIGHT:
yrot += 10.0f;
break;
case SDLK_UP:
xrot -= 10.0f;
break;
case SDLK_DOWN:
xrot += 10.0f;
break;
case SDLK_a:
xtrans -= 0.05f;
break;
case SDLK_d:
xtrans += 0.05f;
break;
case SDLK_w:
ytrans += 0.05f;
break;
case SDLK_s:
ytrans -= 0.05f;
break;
}
}
}
return 0;
}