eqemu-server/utils/deprecated/azone2/GLModelViewer.cpp
2014-08-21 22:43:33 -07:00

549 lines
13 KiB
C++

// Quick and dirty EQ model viewer for Windows, using OpenGL
//
// Put together by Derision from EQEmu forums using basic Windows OpenGL framework from NeHe tutorials @ gamedev.net
// and S3D and EQG file loaders from OpenEQ (modified a bit to fix some bugs and support newer EQG formats).
#ifdef WIN32
#define Polygon Polygon_win32
#include <windows.h>
#undef Polygon
#endif
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>
#include <cstdlib>
#include <math.h>
#include "types.h"
#include "wld.hpp"
#include "archive.hpp"
#include "pfs.hpp"
#include "file_loader.hpp"
#include "zon.hpp"
#include "zonv4.hpp"
#include "ter.hpp"
typedef struct _vertex{
float x;
float y;
float z;
}VERTEX;
void DrawEQModel(FileLoader *fileloader, int modnum);
FileLoader *mfileloader;
bool ProcessZoneFile(const char *shortname);
enum EQFileType { S3D, EQG, UNKNOWN };
HDC hDC=NULL;
HGLRC hRC=NULL;
HWND hWnd=NULL;
HINSTANCE hInstance;
bool keys[256];
char ch;
bool active=true;
GLuint base;
int modelnum = 1; // The Number of the model we are currently displaying.
float angle = 0; // used to rotate the model. Updated by a timer
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc
GLvoid BuildFont(GLvoid)
{
HFONT font;
HFONT oldfont;
base = glGenLists(96);
font = CreateFont(-24, 0, 0, 0, FW_BOLD, false, false, false, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
FF_DONTCARE|DEFAULT_PITCH, "Courier New");
oldfont = (HFONT)SelectObject(hDC, font);
wglUseFontBitmaps(hDC, 32, 96, base);
SelectObject(hDC, oldfont);
DeleteObject(font);
}
GLvoid KillFont(GLvoid)
{
glDeleteLists(base, 96);
}
GLvoid glPrint(const char *fmt, ...)
{
char text[256];
va_list ap;
if (fmt == NULL) return;
va_start(ap, fmt);
vsprintf(text, fmt, ap);
va_end(ap);
glPushAttrib(GL_LIST_BIT);
glListBase(base - 32);
glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);
glPopAttrib();
}
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)
{
if (height==0)height=1;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f,(GLfloat)width/(GLfloat)height,0.1f,12000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int InitGL(GLvoid)
{
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
BuildFont();
return true;
}
int DrawGLScene(char *ZoneFileName)
{
char textBuffer[100];
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
if(mfileloader->model_data.models[modelnum])
DrawEQModel(mfileloader, modelnum);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-1.2f);
glColor3f(100.0f,0.0f,0.0f);
glRasterPos2f(-1.15f,0.56f);
if(mfileloader->model_data.models[modelnum])
if(mfileloader->model_data.models[modelnum]->name)
sprintf(textBuffer," %s: Model Number %4d. Name %s", ZoneFileName, modelnum, mfileloader->model_data.models[modelnum]->name);
else
sprintf(textBuffer," %s: Model Number %4d. Not Viewable (load failed).", ZoneFileName, modelnum);
else
sprintf(textBuffer," %s: Model Number %4d. Not Viewable (probably zone mesh).", ZoneFileName, modelnum);
glPrint(textBuffer);
sprintf(textBuffer," Use the + and - keys to cycle through models.");
glRasterPos2f(-1.15f,0.50f);
glPrint(textBuffer);
return true;
}
GLvoid KillGLWindow(GLvoid) {
if (hRC) {
if (!wglMakeCurrent(NULL,NULL)) {
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC)) {
MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
hRC=NULL;
}
if (hDC && !ReleaseDC(hWnd,hDC)) {
MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL;
}
if (hWnd && !DestroyWindow(hWnd)) {
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL;
}
if (!UnregisterClass("OpenGL",hInstance)) {
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL;
}
KillFont();
}
BOOL CreateGLWindow(char* title, int width, int height, int bits) {
GLuint PixelFormat;
WNDCLASS wc;
DWORD dwExStyle;
DWORD dwStyle;
RECT WindowRect;
WindowRect.left=(long)0;
WindowRect.right=(long)width;
WindowRect.top=(long)0;
WindowRect.bottom=(long)height;
hInstance = GetModuleHandle(NULL);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "OpenGL";
if (!RegisterClass(&wc)) {
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle=WS_OVERLAPPEDWINDOW;
AdjustWindowRectEx(&WindowRect, dwStyle, false, dwExStyle);
if (!(hWnd=CreateWindowEx(dwExStyle, "OpenGL", title, dwStyle|WS_CLIPSIBLINGS|WS_CLIPCHILDREN,
0, 0, WindowRect.right-WindowRect.left, WindowRect.bottom-WindowRect.top,
NULL, NULL, hInstance, NULL))) {
KillGLWindow();
MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
static PIXELFORMATDESCRIPTOR pfd= {
sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA, bits, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, PFD_MAIN_PLANE,
0, 0, 0, 0
};
if (!(hDC=GetDC(hWnd))) {
KillGLWindow();
MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) {
KillGLWindow();
MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) {
KillGLWindow();
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
if (!(hRC=wglCreateContext(hDC))) {
KillGLWindow();
MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
if(!wglMakeCurrent(hDC,hRC)) {
KillGLWindow();
MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
ShowWindow(hWnd,SW_SHOW);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
ReSizeGLScene(width, height);
if (!InitGL()) {
KillGLWindow();
MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
SetTimer(hWnd, 1, 50, (TIMERPROC) NULL);
return true;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_ACTIVATE:
if (!HIWORD(wParam)) active=true;
else
active=false;
return 0;
case WM_SYSCOMMAND:
switch (wParam) {
case SC_SCREENSAVE:
case SC_MONITORPOWER:
return 0;
}
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
keys[wParam] = true;
return 0;
case WM_CHAR:
ch = wParam;
return 0;
case WM_KEYUP:
keys[wParam] = false;
return 0;
case WM_SIZE:
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
return 0;
case WM_TIMER:
angle = angle + 1;
if(angle>359) angle = 0;
return 0;
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
void TranslateVertex(VERTEX &v, float XOffset, float YOffset, float ZOffset) {
v.x = v.x + XOffset;
v.y = v.y + YOffset;
v.z = v.z + ZOffset;
}
void ScaleVertex(VERTEX &v, float XScale, float YScale, float ZScale) {
v.x = v.x * XScale;
v.y = v.y * YScale;
v.z = v.z * ZScale;
}
void DrawEQModel(FileLoader *fileloader, int modnum) {
Polygon *poly;
Vertex *verts[3];
VERTEX v1, v2, v3;
float maxDimension = 0, minx = 999999, miny = 999999, minz = 999999, maxx = -999999, maxy=-999999, maxz=-999999;
Model *model = fileloader->model_data.models[modnum];
for(int i = 0; i < model->poly_count; ++i) {
poly = model->polys[i];
verts[0] = model->verts[poly->v1];
verts[1] = model->verts[poly->v2];
verts[2] = model->verts[poly->v3];
for(int j=0; j<3; j++) {
if(verts[j]->x > maxDimension) maxDimension = verts[j]->x;
if(verts[j]->y > maxDimension) maxDimension = verts[j]->y;
if(verts[j]->z > maxDimension) maxDimension = verts[j]->z;
if(verts[j]->x < minx) minx = verts[j]->x;
if(verts[j]->y < miny) miny = verts[j]->y;
if(verts[j]->z < minz) minz = verts[j]->z;
if(verts[j]->x > maxx) maxx = verts[j]->x;
if(verts[j]->y > maxy) maxy = verts[j]->y;
if(verts[j]->z > maxz) maxz = verts[j]->z;
}
}
maxDimension = 0;
if(maxx-minx>maxDimension) maxDimension = maxx-minx;
if(maxy-miny>maxDimension) maxDimension = maxy-miny;
if(maxz-minz>maxDimension) maxDimension = maxz-minz;
// Hack for very small models (e.g. spoons)
if(maxDimension>1)
glTranslatef(-1.5f,0.0f,-(maxDimension*2));
else
glTranslatef(-1.5f,0.0f,-10);
// angle is updated by a timer every 50ms.
glRotatef(angle, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
glRotatef(angle, 0, 0, 1);
glBegin(GL_TRIANGLES);
for(int i = 0; i < model->poly_count; ++i) {
poly = model->polys[i];
verts[0] = model->verts[poly->v1];
verts[1] = model->verts[poly->v2];
verts[2] = model->verts[poly->v3];
v1.x = verts[0]->x; v1.y = verts[0]->y; v1.z = verts[0]->z;
v2.x = verts[1]->x; v2.y = verts[1]->y; v2.z = verts[1]->z;
v3.x = verts[2]->x; v3.y = verts[2]->y; v3.z = verts[2]->z;
// The aim of this is to centre the model in the window
TranslateVertex(v1, -(minx + (maxx-minx)/2), -(miny + (maxy-miny)/2), -(minz + (maxz-minz)/2));
TranslateVertex(v2, -(minx + (maxx-minx)/2), -(miny + (maxy-miny)/2), -(minz + (maxz-minz)/2));
TranslateVertex(v3, -(minx + (maxx-minx)/2), -(miny + (maxy-miny)/2), -(minz + (maxz-minz)/2));
// For very small models, magnify them
if(maxDimension<=1) {
ScaleVertex(v1, 10, 10, 10);
ScaleVertex(v2, 10, 10, 10);
ScaleVertex(v3, 10, 10, 10);
}
// Assign a kind of random colour to each polygon
//glColor3b((i%50)+50,(i*5)%200,(i*10)%200);
float col = (float)(100 + ((i*10) % 150)) /250 * 1.0f;
glColor3f(col, col, col);
glVertex3f(v1.x, v1.z, v1.y);
glVertex3f(v2.x, v2.z, v2.y);
glVertex3f(v3.x, v3.z, v3.y);
}
glEnd();
}
bool ProcessZoneFile(const char *shortname) {
char bufs[96];
Archive *archive;
Zone_Model *zm;
FILE *fff;
EQFileType FileType = UNKNOWN;
GLuint *textures;
sprintf(bufs, "%s.s3d", shortname);
archive = new PFSLoader();
fff = fopen(bufs, "rb");
if(fff != NULL)
FileType = S3D;
else {
sprintf(bufs, "%s.eqg", shortname);
fff = fopen(bufs, "rb");
if(fff != NULL)
FileType = EQG;
}
if(FileType == UNKNOWN) {
MessageBox(NULL,"Unable to locate specified zone either as EQG or S3D","ERROR",MB_OK|MB_ICONEXCLAMATION);
return(false);
}
if(archive->Open(fff) == 0) {
MessageBox(NULL,"Unable to open container file","ERROR",MB_OK|MB_ICONEXCLAMATION);
return(false);
}
switch(FileType) {
case S3D:
mfileloader = new WLDLoader();
if(mfileloader->Open(NULL, (char *) shortname, archive) == 0) {
MessageBox(NULL,"Error reading WLD from container file","ERROR",MB_OK|MB_ICONEXCLAMATION);
return(false);
}
break;
case EQG:
mfileloader = new ZonLoader();
if(mfileloader->Open(NULL, (char *) shortname, archive) == 0) {
delete mfileloader;
mfileloader = new Zonv4Loader();
if(mfileloader->Open(NULL, (char *) shortname, archive) == 0) {
MessageBox(NULL,"Error reading ZON/TER from container file","ERROR",MB_OK|MB_ICONEXCLAMATION);
return(false);
}
}
break;
case UNKNOWN:
break;
}
if(mfileloader->model_data.model_count==0) {
MessageBox(NULL,"No models found. For S3D, probably could not locate _obj.s3d file","ERROR",MB_OK|MB_ICONEXCLAMATION);
return false;
}
zm = mfileloader->model_data.zone_model;
return(true);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MSG msg;
BOOL done=false;
FILE *fp;
for(int i=0;i<256;i++)
keys[i] = false;
char* buf = (char *) new char[strlen(lpCmdLine) + 1] ;
char *pTmp = buf;
if(!strlen(lpCmdLine)) {
MessageBox(NULL,"Specify the shortname of the zone (without the S3D or EQG extension) on the command line.",
"ERROR",MB_OK | MB_ICONINFORMATION);
return 0;
}
strcpy(buf,lpCmdLine);
if(!ProcessZoneFile(buf)) return 0;
if (!CreateGLWindow("EQ Model Viewer",1280,768,16))
return 0;
while(!done) {
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
if (msg.message==WM_QUIT)
done=true;
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else {
if ((active && !DrawGLScene(buf)) || keys[VK_ESCAPE])
done=true;
else
SwapBuffers(hDC);
// Use + and - to cycle through the models
if (toupper(ch)=='+') {
if(modelnum+1 < mfileloader->model_data.model_count)
modelnum++;
else
modelnum=0;
angle = 0;
}
if (toupper(ch)=='-') {
if(modelnum>0)
modelnum--;
else
modelnum = mfileloader->model_data.model_count - 1;
angle = 0;
}
ch = 0;
}
}
KillGLWindow();
return (msg.wParam);
}