mirror of
https://github.com/simtactics/niotso.git
synced 2025-03-22 10:52:20 +00:00
Also fixed formatting in recent source files. My next task is implementing IFF chunk reading. I'll also make a tool that can convert the game's behavior.iff file to an HTML page.
703 lines
22 KiB
C++
703 lines
22 KiB
C++
/*
|
|
libvitaboy - Copyright (c) 2012 Fatbag <X-Fi6@phppoll.org>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
Instructions:
|
|
You must have the following files in the same directory as the Renderer binary:
|
|
|
|
Skeleton:
|
|
* skeleton.skel ("adult.skel" in ./avatardata/skeletons/)
|
|
|
|
Meshes:
|
|
* body.mesh (pick one from ./avatardata/bodies/meshes/)
|
|
* head.mesh (pick one from ./avatardata/heads/meshes/)
|
|
* lhand.mesh (pick one from ./avatardata/hands/meshes/)
|
|
* rhand.mesh (pick one from ./avatardata/hands/meshes/)
|
|
|
|
Textures:
|
|
* body.jpg (pick one from ./avatardata/bodies/textures/)
|
|
* head.jpg (pick one from ./avatardata/heads/textures/)
|
|
* hand.jpg (pick one from ./avatardata/hands/textures/)
|
|
|
|
Animation:
|
|
* animation.anim (pick one from ./avatardata/animations/)
|
|
|
|
==== Controls ====
|
|
Arrow keys: Rotate the sim
|
|
i,j,k,l: Translate the sim around the screen
|
|
z,x: Rotate the sim like a clock
|
|
a,s: Zoom in, out
|
|
n: Animate the character
|
|
F11: Enter/leave fullscreen
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <gl\gl.h>
|
|
#include <gl\glu.h>
|
|
#include <gl\glext.h>
|
|
#include <FileHandler.hpp>
|
|
#include "libvitaboy.hpp"
|
|
|
|
HDC hDC=NULL;
|
|
HGLRC hRC=NULL;
|
|
HWND hWnd=NULL;
|
|
HINSTANCE hInstance;
|
|
|
|
bool keys[256] = {0};
|
|
bool active=true;
|
|
bool fullscreen=false;
|
|
|
|
float zoom = -10;
|
|
struct CharacterPlacement_t {
|
|
Vertex_t Translation;
|
|
Vertex_t Rotation;
|
|
};
|
|
CharacterPlacement_t Character = {{0,-3,0}, {0,0,0}};
|
|
|
|
Skeleton_t Skeleton;
|
|
|
|
const unsigned TextureCount = 3;
|
|
unsigned texture[3];
|
|
enum { Texture_Body, Texture_Head, Texture_Hand };
|
|
const char* const TexturePaths[] = {"body.jpg", "head.jpg", "hand.jpg"};
|
|
|
|
const unsigned MeshCount = 4;
|
|
Mesh_t Meshes[4];
|
|
enum { Mesh_Body, Mesh_Head, Mesh_LHand, Mesh_RHand };
|
|
const char* const MeshPaths[] = {"body.mesh", "head.mesh", "lhand.mesh", "rhand.mesh" };
|
|
const unsigned Mesh_UseTexture[] = { Texture_Body, Texture_Head, Texture_Hand, Texture_Hand };
|
|
const char* const MeshActivate[] = {NULL, "HEAD", "L_HAND", "R_HAND"};
|
|
|
|
Animation_t Animation;
|
|
float AnimationTime = 0;
|
|
|
|
bool ShowMesh = true;
|
|
bool ShowSkeleton = true;
|
|
|
|
LARGE_INTEGER ClockFreq, PreviousTime;
|
|
float FramePeriod;
|
|
|
|
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
void DisplayFileError(const char * Filename){
|
|
const char * Message;
|
|
switch(File::Error){
|
|
case FERR_NOT_FOUND:
|
|
Message = "%s does not exist.";
|
|
break;
|
|
case FERR_OPEN:
|
|
Message = "%s could not be opened for reading.";
|
|
break;
|
|
case FERR_BLANK:
|
|
case FERR_UNRECOGNIZED:
|
|
Message = "%s is corrupt or invalid.";
|
|
break;
|
|
case FERR_MEMORY:
|
|
Message = "Memory for %s could not be allocated.";
|
|
break;
|
|
default:
|
|
Message = "%s could not be read.";
|
|
break;
|
|
}
|
|
|
|
char Buffer[1024];
|
|
sprintf(Buffer, Message, Filename);
|
|
MessageBox(hWnd, Buffer, NULL, MB_OK | MB_ICONERROR);
|
|
}
|
|
|
|
bool LoadTextures()
|
|
{
|
|
glGenTextures(3, texture);
|
|
for(int i=0; i<3; i++){
|
|
Image_t * Image = File::ReadImageFile(TexturePaths[i]);
|
|
if(!Image){
|
|
DisplayFileError(TexturePaths[i]);
|
|
return false;
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture[i]);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, Image->Width, Image->Height, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->Data);
|
|
free(Image->Data);
|
|
free(Image);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ResizeScene(GLsizei width, GLsizei height)
|
|
{
|
|
if(height==0) height++;
|
|
|
|
glViewport(0,0,width,height);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
// Calculate The Aspect Ratio Of The Window
|
|
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
}
|
|
|
|
bool InitGL()
|
|
{
|
|
if(!LoadTextures())
|
|
return false;
|
|
|
|
glShadeModel(GL_SMOOTH);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClearDepth(1.0f);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glEnable(GL_CULL_FACE);
|
|
glEnable(GL_RESCALE_NORMAL);
|
|
glDisable(GL_BLEND);
|
|
glDepthFunc(GL_LEQUAL);
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
return true;
|
|
}
|
|
|
|
void TransformVertices(Bone_t& Bone)
|
|
{
|
|
glTranslatef(Bone.Translation.x, Bone.Translation.y, Bone.Translation.z);
|
|
float Matrix[16];
|
|
FindQuaternionMatrix(Matrix, &Bone.Rotation);
|
|
glMultMatrixf(Matrix);
|
|
|
|
unsigned MeshIndex = 0;
|
|
unsigned BoneIndex;
|
|
|
|
for(unsigned i=1; i<MeshCount; i++){
|
|
if(!strcmp(Bone.Name, MeshActivate[i])){
|
|
MeshIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
Mesh_t& Mesh = Meshes[MeshIndex];
|
|
for(BoneIndex=0; BoneIndex<Mesh.BindingCount; BoneIndex++){
|
|
if(!strcmp(Bone.Name, Mesh.BoneNames[Mesh.BoneBindings[BoneIndex].BoneIndex]))
|
|
break;
|
|
}
|
|
|
|
if(BoneIndex < Mesh.BindingCount){
|
|
for(unsigned i=0; i<Mesh.BoneBindings[BoneIndex].FixedVertexCount; i++){
|
|
unsigned VertexIndex = Mesh.BoneBindings[BoneIndex].FirstFixedVertex + i;
|
|
Vertex_t& RelativeVertex = Mesh.VertexData[VertexIndex];
|
|
Vertex_t& AbsoluteVertex = Mesh.TransformedVertexData[VertexIndex];
|
|
|
|
glTranslatef(RelativeVertex.x, RelativeVertex.y, RelativeVertex.z);
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, Matrix);
|
|
AbsoluteVertex.x = Matrix[12];
|
|
AbsoluteVertex.y = Matrix[13];
|
|
AbsoluteVertex.z = Matrix[14];
|
|
glTranslatef(-RelativeVertex.x, -RelativeVertex.y, -RelativeVertex.z);
|
|
}
|
|
for(unsigned i=0; i<Mesh.BoneBindings[BoneIndex].BlendedVertexCount; i++){
|
|
unsigned VertexIndex = Mesh.FixedVertexCount + Mesh.BoneBindings[BoneIndex].FirstBlendedVertex + i;
|
|
Vertex_t& RelativeVertex = Mesh.VertexData[VertexIndex];
|
|
Vertex_t& AbsoluteVertex = Mesh.TransformedVertexData[VertexIndex];
|
|
|
|
glTranslatef(RelativeVertex.x, RelativeVertex.y, RelativeVertex.z);
|
|
glGetFloatv(GL_MODELVIEW_MATRIX, Matrix);
|
|
AbsoluteVertex.x = Matrix[12];
|
|
AbsoluteVertex.y = Matrix[13];
|
|
AbsoluteVertex.z = Matrix[14];
|
|
glTranslatef(-RelativeVertex.x, -RelativeVertex.y, -RelativeVertex.z);
|
|
}
|
|
}
|
|
|
|
if(Bone.ChildrenCount == 1){
|
|
TransformVertices(*Bone.Children[0]);
|
|
}else if(Bone.ChildrenCount > 1){
|
|
for(unsigned i=0; i<Bone.ChildrenCount; i++){
|
|
glPushMatrix();
|
|
TransformVertices(*Bone.Children[i]);
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlendVertices()
|
|
{
|
|
for(unsigned i=0; i<MeshCount; i++){
|
|
Mesh_t& Mesh = Meshes[i];
|
|
for(unsigned i=0; i<Mesh.BlendedVertexCount; i++){
|
|
unsigned Weight = Mesh.BlendData[i].Weight;
|
|
Vertex_t& BlendedVertex = Mesh.TransformedVertexData[Mesh.FixedVertexCount + i];
|
|
Vertex_t& FixedVertex = Mesh.TransformedVertexData[Mesh.FixedVertexCount + i];
|
|
BlendedVertex.x =
|
|
Weight * BlendedVertex.x +
|
|
(1-Weight) * FixedVertex.x;
|
|
BlendedVertex.y =
|
|
Weight * BlendedVertex.y +
|
|
(1-Weight) * FixedVertex.y;
|
|
BlendedVertex.z =
|
|
Weight * BlendedVertex.z +
|
|
(1-Weight) * FixedVertex.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawMeshes()
|
|
{
|
|
glPointSize(2.0);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
TransformVertices(Skeleton.Bones[0]);
|
|
glPopMatrix();
|
|
BlendVertices();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
for(unsigned i=0; i<MeshCount; i++){
|
|
glBindTexture(GL_TEXTURE_2D, texture[Mesh_UseTexture[i]]);
|
|
glVertexPointer(3, GL_FLOAT, offsetof(Vertex_t, y)-offsetof(Vertex_t, x)-sizeof(float),
|
|
Meshes[i].TransformedVertexData);
|
|
glTexCoordPointer(2, GL_FLOAT, offsetof(TextureVertex_t, v)-offsetof(TextureVertex_t, u)-sizeof(float),
|
|
Meshes[i].TransformedTextureData);
|
|
glDrawElements(GL_TRIANGLES, Meshes[i].FaceCount*3, GL_UNSIGNED_INT, Meshes[i].FaceData);
|
|
}
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
void AdvanceFrame(Skeleton_t& Skeleton, Animation_t& Animation, float TimeDelta)
|
|
{
|
|
float Duration = (float)Animation.Motions[0].FrameCount/30;
|
|
AnimationTime += TimeDelta;
|
|
while(AnimationTime >= Duration) AnimationTime -= Duration;
|
|
if(AnimationTime<0) AnimationTime = 0; //Safe-guard against rounding error
|
|
|
|
for(unsigned i=0; i<Animation.MotionsCount; i++){
|
|
unsigned BoneIndex = FindBone(Skeleton, Animation.Motions[i].BoneName, Skeleton.BoneCount);
|
|
if(BoneIndex == (unsigned)-1) continue;
|
|
|
|
Bone_t& Bone = Skeleton.Bones[BoneIndex];
|
|
|
|
unsigned Frame = AnimationTime*30;
|
|
float FractionShown = AnimationTime*30 - Frame;
|
|
unsigned NextFrame = (Frame+1 != Animation.Motions[0].FrameCount) ? Frame+1 : 0;
|
|
|
|
if(Animation.Motions[i].HasTranslation){
|
|
Translation_t& Translation = Animation.Motions[i].Translations[Frame];
|
|
Translation_t& NextTranslation = Animation.Motions[i].Translations[NextFrame];
|
|
Bone.Translation.x = (1-FractionShown)*Translation.x + FractionShown*NextTranslation.x;
|
|
Bone.Translation.y = (1-FractionShown)*Translation.y + FractionShown*NextTranslation.y;
|
|
Bone.Translation.z = (1-FractionShown)*Translation.z + FractionShown*NextTranslation.z;
|
|
}
|
|
if(Animation.Motions[i].HasRotation){
|
|
Rotation_t& Rotation = Animation.Motions[i].Rotations[Frame];
|
|
Rotation_t& NextRotation = Animation.Motions[i].Rotations[NextFrame];
|
|
|
|
//Use Slerp to interpolate
|
|
float w1, w2 = 1;
|
|
float cosTheta = DotProduct(&Rotation, &NextRotation);
|
|
if(cosTheta < 0){
|
|
cosTheta *= -1;
|
|
w2 *= -1;
|
|
}
|
|
float theta = (float) acos(cosTheta);
|
|
float sinTheta = (float) sin(theta);
|
|
|
|
if(sinTheta > 0.001f){
|
|
w1 = (float) sin((1.0f-FractionShown)*theta)/sinTheta;
|
|
w2 *= (float) sin(FractionShown *theta)/sinTheta;
|
|
} else {
|
|
w1 = 1.0f - FractionShown;
|
|
w2 = FractionShown;
|
|
}
|
|
|
|
Bone.Rotation.x = w1*Rotation.x + w2*NextRotation.x;
|
|
Bone.Rotation.y = w1*Rotation.y + w2*NextRotation.y;
|
|
Bone.Rotation.z = w1*Rotation.z + w2*NextRotation.z;
|
|
Bone.Rotation.w = w1*Rotation.w + w2*NextRotation.w;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawBonesSkeleton(Bone_t& Bone)
|
|
{
|
|
glPointSize(5.0);
|
|
glTranslatef(Bone.Translation.x, Bone.Translation.y, Bone.Translation.z);
|
|
float RotationMatrix[16];
|
|
FindQuaternionMatrix(RotationMatrix, &Bone.Rotation);
|
|
glMultMatrixf(RotationMatrix);
|
|
|
|
if(!strcmp(Bone.Name, "ROOT"))
|
|
glColor3f(1.0, 0.0, 0.0);
|
|
else if(!strcmp(Bone.Name, "HEAD"))
|
|
glColor3f(1.0, 1.0, 0.0);
|
|
else
|
|
glColor3f(0.0, 1.0, 0.0);
|
|
glBegin(GL_POINTS); glVertex3f(0, 0, 0); glEnd();
|
|
|
|
if(Bone.ChildrenCount == 1){
|
|
DrawBonesSkeleton(*Bone.Children[0]);
|
|
}else if(Bone.ChildrenCount > 1){
|
|
for(unsigned i=0; i<Bone.ChildrenCount; i++){
|
|
glPushMatrix();
|
|
DrawBonesSkeleton(*Bone.Children[i]);
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawSkeleton()
|
|
{
|
|
glPushMatrix();
|
|
DrawBonesSkeleton(Skeleton.Bones[0]);
|
|
glPopMatrix();
|
|
}
|
|
|
|
int DrawGLScene()
|
|
{
|
|
//Obtain the current time
|
|
LARGE_INTEGER CurrentTime;
|
|
QueryPerformanceCounter(&CurrentTime);
|
|
float TimeDelta = (float)(CurrentTime.QuadPart-PreviousTime.QuadPart)/ClockFreq.QuadPart;
|
|
if(TimeDelta < 0) TimeDelta = 0; //Safe-guard in case of system delay
|
|
PreviousTime = CurrentTime;
|
|
|
|
//Handle user interaction
|
|
if(keys['A']) /*{if(zoom <=-1.0f) zoom+=0.05f; }*/ zoom+=3*TimeDelta;
|
|
if(keys['S']) /*{if(zoom >=-10.0f) zoom-=0.05f; }*/ zoom-=3*TimeDelta;
|
|
if(keys[VK_UP]){ if((Character.Rotation.x-=60*TimeDelta) <=-360) Character.Rotation.x+=360; }
|
|
if(keys[VK_DOWN]){ if((Character.Rotation.x+=60*TimeDelta) >=360) Character.Rotation.x-=360; }
|
|
if(keys[VK_LEFT]){ if((Character.Rotation.y-=60*TimeDelta) <=-360) Character.Rotation.y+=360; }
|
|
if(keys[VK_RIGHT]){ if((Character.Rotation.y+=60*TimeDelta) >=360) Character.Rotation.y-=360; }
|
|
if(keys['X']){ if((Character.Rotation.z-=60*TimeDelta) <=-360) Character.Rotation.z+=360; }
|
|
if(keys['Z']){ if((Character.Rotation.z+=60*TimeDelta) >=360) Character.Rotation.z-=360; }
|
|
if(keys['K']){ Character.Translation.y-=3*TimeDelta; }
|
|
if(keys['I']){ Character.Translation.y+=3*TimeDelta; }
|
|
if(keys['J']){ Character.Translation.x-=3*TimeDelta; }
|
|
if(keys['L']){ Character.Translation.x+=3*TimeDelta; }
|
|
if(keys['N']){ AdvanceFrame(Skeleton, Animation, TimeDelta); }
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clear the screen and the depth buffer
|
|
|
|
glLoadIdentity();
|
|
glTranslatef(Character.Translation.x, Character.Translation.y, zoom + Character.Translation.z);
|
|
glRotatef(Character.Rotation.x,1.0f,0.0f,0.0f);
|
|
glRotatef(Character.Rotation.y,0.0f,1.0f,0.0f);
|
|
glRotatef(Character.Rotation.z,0.0f,0.0f,1.0f);
|
|
|
|
if(ShowMesh){
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
|
|
DrawMeshes();
|
|
}
|
|
|
|
if(ShowSkeleton){
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
DrawSkeleton();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void KillGLWindow()
|
|
{
|
|
if(fullscreen){
|
|
ChangeDisplaySettings(NULL, 0); //Reset to the desktop resolution
|
|
ShowCursor(true);
|
|
}
|
|
|
|
if(hRC){
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(hRC);
|
|
hRC = NULL;
|
|
}
|
|
|
|
if(hDC){
|
|
ReleaseDC(hWnd,hDC);
|
|
hDC = NULL;
|
|
}
|
|
|
|
if(hWnd){
|
|
DestroyWindow(hWnd);
|
|
hWnd = NULL;
|
|
}
|
|
|
|
UnregisterClass("OpenGL", hInstance);
|
|
hInstance = NULL;
|
|
}
|
|
|
|
typedef bool (APIENTRY *PFNWGLSWAPINTERVALFARPROC)(int);
|
|
BOOL CreateGLWindow(const char * title, int width, int height, int bits, bool fullscreenflag)
|
|
{
|
|
fullscreen = fullscreenflag;
|
|
hInstance = GetModuleHandle(NULL);
|
|
|
|
WNDCLASS wc = {
|
|
CS_HREDRAW | CS_VREDRAW | CS_OWNDC, //style
|
|
(WNDPROC) WndProc, //lpfnWndProc
|
|
0, //cbClsExtra
|
|
0, //cbWndExtra
|
|
hInstance, //hInstance
|
|
(HICON) LoadImage(NULL, IDI_WINLOGO, IMAGE_ICON, 0, 0, LR_SHARED), //hIcon
|
|
(HCURSOR) LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED | LR_DEFAULTSIZE), //hCursor
|
|
NULL, //hbrBackground
|
|
NULL, //lpszMenuName
|
|
"OpenGL" //lpszClassName
|
|
};
|
|
|
|
if(!RegisterClass(&wc)){
|
|
MessageBox(NULL, "Failed to registrer the window class.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
DWORD dwStyle, dwExStyle;
|
|
|
|
if(fullscreen){
|
|
DEVMODE dmScreenSettings;
|
|
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmScreenSettings);
|
|
width = dmScreenSettings.dmPelsWidth;
|
|
height = dmScreenSettings.dmPelsHeight;
|
|
bits = dmScreenSettings.dmBitsPerPel;
|
|
|
|
dwExStyle = WS_EX_APPWINDOW | WS_EX_TOPMOST;
|
|
dwStyle = WS_POPUP;
|
|
ShowCursor(false);
|
|
}else{
|
|
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
|
|
dwStyle = WS_OVERLAPPEDWINDOW;
|
|
}
|
|
|
|
RECT WindowRect;
|
|
WindowRect.left = 0;
|
|
WindowRect.right = width;
|
|
WindowRect.top = 0;
|
|
WindowRect.bottom = height;
|
|
AdjustWindowRectEx(&WindowRect, dwStyle, false, dwExStyle);
|
|
|
|
// Create The Window
|
|
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.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
const PIXELFORMATDESCRIPTOR pfd = {
|
|
sizeof(PIXELFORMATDESCRIPTOR), 1, //Size and version
|
|
PFD_DRAW_TO_WINDOW | //dwFlags
|
|
PFD_SUPPORT_OPENGL |
|
|
PFD_DOUBLEBUFFER,
|
|
PFD_TYPE_RGBA, //iPixelType
|
|
bits, //cColorBits
|
|
0, 0, 0, 0, 0, 0, 0, 0, //R,G,B,A bits
|
|
0, 0, 0, 0, 0, //Accumulation buffer bits
|
|
16, //cDepthBits
|
|
0, //cStencilBits
|
|
0, //cAuxBuffers
|
|
PFD_MAIN_PLANE, //iLayerType
|
|
0, //Reserved
|
|
0, 0, 0 //Masks
|
|
};
|
|
|
|
hDC = GetDC(hWnd);
|
|
if(!hDC){
|
|
KillGLWindow();
|
|
MessageBox(NULL, "Failed to create an OpenGL device context.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
unsigned PixelFormat = ChoosePixelFormat(hDC, &pfd);
|
|
if(!PixelFormat){
|
|
KillGLWindow();
|
|
MessageBox(NULL, "Can't find a suitable PixelFormat.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
if(!SetPixelFormat(hDC,PixelFormat, &pfd)){
|
|
KillGLWindow();
|
|
MessageBox(NULL, "Can't set the PixelFormat.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
hRC = wglCreateContext(hDC);
|
|
if(!hRC){
|
|
KillGLWindow();
|
|
MessageBox(NULL, "Failed to create an OpenGL rendering context.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
if(!wglMakeCurrent(hDC, hRC)){
|
|
KillGLWindow();
|
|
MessageBox(NULL, "Failed to activate an OpenGL device context.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
ShowWindow(hWnd,SW_SHOW);
|
|
SetForegroundWindow(hWnd);
|
|
SetFocus(hWnd);
|
|
ResizeScene(width, height);
|
|
|
|
if(!InitGL()){
|
|
KillGLWindow();
|
|
MessageBox(NULL, "Initialization failed.", NULL, MB_OK | MB_ICONERROR);
|
|
return false;
|
|
}
|
|
|
|
PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0;
|
|
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress("wglSwapIntervalEXT");
|
|
if(wglSwapIntervalEXT) wglSwapIntervalEXT(1);
|
|
|
|
QueryPerformanceFrequency(&ClockFreq);
|
|
QueryPerformanceCounter(&PreviousTime);
|
|
|
|
return true;
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg) {
|
|
case WM_KEYDOWN: {
|
|
if(wParam == VK_ESCAPE){
|
|
PostQuitMessage(0);
|
|
}else if(wParam == VK_F11 && !keys[VK_F11]){
|
|
KillGLWindow();
|
|
fullscreen = !fullscreen;
|
|
if(!CreateGLWindow("libvitaboy - Renderer",640,480,16,fullscreen)){
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
|
|
keys[wParam] = true;
|
|
} return 0;
|
|
|
|
case WM_KEYUP: {
|
|
keys[wParam] = false;
|
|
} return 0;
|
|
|
|
case WM_ACTIVATE: {
|
|
// LoWord Can Be WA_INACTIVE, WA_ACTIVE, WA_CLICKACTIVE,
|
|
// The High-Order Word Specifies The Minimized State Of The Window Being Activated Or Deactivated.
|
|
// A NonZero Value Indicates The Window Is Minimized.
|
|
if ((LOWORD(wParam) != WA_INACTIVE) && !((BOOL)HIWORD(wParam)))
|
|
active = true;
|
|
else
|
|
active = false;
|
|
} return 0;
|
|
|
|
case WM_SIZE: {
|
|
ResizeScene(LOWORD(lParam),HIWORD(lParam));
|
|
} return 0;
|
|
|
|
case WM_SYSCOMMAND: {
|
|
switch (wParam) {
|
|
case SC_SCREENSAVE:
|
|
case SC_MONITORPOWER:
|
|
return 0;
|
|
}
|
|
} break;
|
|
|
|
case WM_DEVMODECHANGE: {
|
|
DEVMODE dm;
|
|
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
|
|
FramePeriod = 1.0f/dm.dmDisplayFrequency;
|
|
} return 0;
|
|
|
|
case WM_CLOSE: {
|
|
PostQuitMessage(0);
|
|
} return 0;
|
|
}
|
|
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
bool Read(const char * Filename, uint8_t ** InData){
|
|
*InData = File::ReadFile(Filename);
|
|
if(*InData != NULL){
|
|
VBFile.set(*InData, File::FileSize);
|
|
return true;
|
|
}
|
|
|
|
DisplayFileError(Filename);
|
|
return false;
|
|
}
|
|
|
|
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
|
{
|
|
uint8_t * InData;
|
|
|
|
if(!Read("skeleton.skel", &InData))
|
|
return 0;
|
|
ReadSkeleton(Skeleton);
|
|
free(InData);
|
|
|
|
for(unsigned i=0; i<MeshCount; i++){
|
|
if(!Read(MeshPaths[i], &InData))
|
|
return 0;
|
|
ReadMesh(Meshes[i]);
|
|
free(InData);
|
|
}
|
|
|
|
if(!Read("animation.anim", &InData))
|
|
return 0;
|
|
ReadAnimation(Animation);
|
|
free(InData);
|
|
|
|
AdvanceFrame(Skeleton, Animation, 0);
|
|
|
|
if(!CreateGLWindow("libvitaboy - Renderer",640,480,16,fullscreen)){
|
|
return 0;
|
|
}
|
|
|
|
DEVMODE dm;
|
|
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
|
|
FramePeriod = 1.0f/dm.dmDisplayFrequency;
|
|
|
|
bool quit = false;
|
|
MSG msg;
|
|
while(true){
|
|
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
|
|
if(msg.message == WM_QUIT)
|
|
quit = true;
|
|
}
|
|
if(quit) break;
|
|
|
|
DrawGLScene();
|
|
SwapBuffers(hDC);
|
|
LARGE_INTEGER RenderTime;
|
|
QueryPerformanceCounter(&RenderTime);
|
|
float SleepDuration = (FramePeriod - (float)(RenderTime.QuadPart-PreviousTime.QuadPart)/ClockFreq.QuadPart) * 1000;
|
|
if(SleepDuration > 1) Sleep((unsigned) SleepDuration);
|
|
}
|
|
|
|
//Shutdown
|
|
KillGLWindow();
|
|
return (msg.wParam);
|
|
}
|