/* This file is part of Warzone 2100. Copyright (C) 2007-2008 Warzone Resurrection Project Warzone 2100 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 2 of the License, or (at your option) any later version. Warzone 2100 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 Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "wzmviewer.h" #include #include #include #if (defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) # define strcasecmp _stricmp # define strncasecmp _strnicmp # define inline __inline # define alloca _alloca # define fileno _fileno # define isfinite _finite #endif // The WZM format is a proposed successor to the PIE format used by Warzone. // For an explanation of the WZM format, see http://wiki.wz2100.net/WZM_format // To compile: gcc -o wzmviewer wzmviewer.c wzmutils.c -Wall -g -O0 -Wshadow -lpng `sdl-config --libs --cflags` -lGL -lGLU static char *input = ""; static char *texPath = ""; static SDL_Surface *screen; static uint32_t now = 0; static void parse_args(int argc, char **argv) { unsigned int i = 1; if (argc < 2 + i) { fprintf(stderr, "Syntax: wzmviewer filename.wzm path_to_textures\n"); exit(1); } input = argv[i++]; texPath = argv[i++]; } static void resizeWindow(int width, int height) { glViewport(0, 0, width, height); } // Adjust the vector in vec1 with the vector to be in vec2 by fractional value in // fraction which indicates how far we've come toward vec2. The result is put into // the result vector. static void interpolateVectors(Vector3f vec1, Vector3f vec2, Vector3f *result, double fraction) { result->x = vec1.x * (1.0 - fraction) + vec2.x * fraction; result->y = vec1.y * (1.0 - fraction) + vec2.y * fraction; result->z = vec1.z * (1.0 - fraction) + vec2.z * fraction; } static void drawModel(MODEL *psModel, int x, int y) { int i; glColor3f(1.0f, 1.0f, 1.0f); for (i = 0; i < psModel->meshes; i++) { MESH *psMesh = &psModel->mesh[i]; if (psMesh->frameArray) { FRAME *psFrame = &psMesh->frameArray[psMesh->currentFrame]; FRAME *nextFrame = psFrame; double fraction = 1.0f / (psFrame->timeSlice * 1000) * (now - psMesh->lastChange); // until next frame Vector3f vec; assert(psMesh->currentFrame < psMesh->frames); if (psMesh->currentFrame == psMesh->frames - 1) { nextFrame = &psMesh->frameArray[0]; // wrap around } else { nextFrame = &psMesh->frameArray[psMesh->currentFrame + 1]; } // Try to avoid a crash from crap drivers assert(isfinite(psFrame->translation.x) && isfinite(psFrame->translation.y) && isfinite(psFrame->translation.z)); assert(psFrame->translation.x >= 0.0f && psFrame->translation.y >= 0.0f && psFrame->translation.z >= 0.0f); assert(psFrame->rotation.x >= 0.0f && psFrame->rotation.y >= 0.0f && psFrame->rotation.z >= 0.0f); assert(psFrame->rotation.x <= 360.0f && psFrame->rotation.y <= 360.0f && psFrame->rotation.z <= 360.0f); // Translate interpolateVectors(psFrame->translation, nextFrame->translation, &vec, fraction); glTranslatef(vec.x, vec.y, vec.z); // Rotate interpolateVectors(psFrame->rotation, nextFrame->rotation, &vec, fraction); glRotatef(vec.x, 1, 0, 0); glRotatef(vec.y, 0, 1, 0); glRotatef(vec.z, 0, 0, 1); } glTexCoordPointer(2, GL_FLOAT, 0, psMesh->textureArray[psMesh->currentFrame]); glVertexPointer(3, GL_FLOAT, 0, psMesh->vertexArray); glDrawElements(GL_TRIANGLES, psMesh->faces * 3, GL_UNSIGNED_INT, psMesh->indexArray); } } static void prepareModel(MODEL *psModel) { glGenTextures(1, &psModel->texture); glBindTexture(GL_TEXTURE_2D, psModel->texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, psModel->pixmap->w, psModel->pixmap->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, psModel->pixmap->pixels); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } int main(int argc, char **argv) { MODEL *psModel; const int width = 640, height = 480; SDL_Event event; GLfloat angle = 0.0f; const float aspect = (float)width / (float)height; bool quit = false; float dimension = 0.0f; int i; parse_args(argc, argv); /* Initialize SDL */ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } atexit(SDL_Quit); psModel = readModel(input, texPath); psModel->pixmap = readPixmap(psModel->texPath); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); /* Initialize the display */ screen = SDL_SetVideoMode(width, height, 0, SDL_OPENGL|SDL_ANYFORMAT); if (screen == NULL) { fprintf(stderr, "Couldn't initialize display: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } printf("OpenGL version: %s\n", glGetString(GL_VERSION)); printf("OpenGL renderer: %s\n", glGetString(GL_RENDERER)); printf("OpenGL vendor: %s\n", glGetString(GL_VENDOR)); resizeWindow(width, height); glEnable(GL_TEXTURE_2D); glDisable(GL_FOG); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glClearDepth(1.0f); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, aspect, 0.1f, 500.0f); glMatrixMode(GL_MODELVIEW); prepareModel(psModel); for (i = 0; i < psModel->meshes; i++) { int j; MESH *psMesh = &psModel->mesh[i]; for (j = 0; j < psMesh->vertices * 3; j++) { dimension = MAX(fabs(psMesh->vertexArray[j]), dimension); } } /* Find model size */ while (!quit) { now = SDL_GetTicks(); while (SDL_PollEvent(&event)) { SDL_keysym *keysym = &event.key.keysym; switch (event.type) { case SDL_VIDEORESIZE: resizeWindow(event.resize.w, event.resize.h); break; case SDL_QUIT: quit = true; break; case SDL_KEYDOWN: switch (keysym->sym) { case SDLK_F1: glEnable(GL_CULL_FACE); printf("Culling enabled.\n"); break; case SDLK_F2: glDisable(GL_CULL_FACE); printf("Culling disabled.\n"); break; case SDLK_F3: glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); printf("Wireframe mode.\n"); break; case SDLK_F4: glEnable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); printf("Texturing mode.\n"); break; case SDLK_ESCAPE: quit = true; break; case SDLK_KP_PLUS: case SDLK_PLUS: for (i = 0; i < psModel->meshes; i++) { MESH *psMesh = &psModel->mesh[i]; if (psMesh->currentFrame < psMesh->frames - 1) { psMesh->currentFrame++; } else { psMesh->currentFrame = 0; } } break; default: break; } break; } } // Animation support! :-) for (i = 0; i < psModel->meshes; i++) { MESH *psMesh = &psModel->mesh[i]; FRAME *psFrame; if (!psMesh->frameArray) { continue; } psFrame = &psMesh->frameArray[psMesh->currentFrame]; assert(psMesh->currentFrame < psMesh->frames && psMesh->currentFrame >= 0); if (psFrame->timeSlice != 0 && psFrame->timeSlice * 1000 + psMesh->lastChange < now) { psMesh->lastChange = now; psMesh->currentFrame++; if (psMesh->currentFrame >= psMesh->frames) { psMesh->currentFrame = 0; // loop } } } glLoadIdentity(); glTranslatef(0.0f, -30.0f, -50.0f + -(dimension * 2.0f));; glRotatef(angle, 0, 1, 0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); drawModel(psModel, 0, 0); SDL_GL_SwapBuffers(); SDL_Delay(10); angle += 0.1; if (angle > 360.0f) { angle = 0.0f; } } glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); freeModel(psModel); return 0; }