In my effort to learn C++, i have translated my PB's MD2 animation to work with VS2010.
And here is the result:
//+--------------------------------------------------------------------------+
//| |
//| GDImageMD2 |
//| Version 1.00 |
//| |
//| OpenGL project using the GDImage flat API to create on the fly |
//| MD2 object with smooth shading and ambient light. |
//| |
//| The MD2 engine is based on Petr Schreiber's work |
//| psch.thinbasic.com |
//| |
//| The GDImage.dll unregistered version has no limitation, |
//| and all features of the API are fully functional. |
//| |
//| Buying a commercial license removes the copyright nag screen. |
//| |
//+--------------------------------------------------------------------------+
//| |
//| Author Patrice TERRIER |
//| copyright (c) 2010 |
//| |
//| pterrier@zapsolution.com |
//| |
//| www.zapsolution.com |
//| |
//+--------------------------------------------------------------------------+
//| Project started on : 08-23-2010 (MM-DD-YYYY) |
//| Last revised : 08-26-2010 (MM-DD-YYYY) |
//+--------------------------------------------------------------------------+
#include <windows.h> // Search along the path.
#include <windowsx.h> // Search along the path.
//#include <stdlib.h> // Search along the path.
//#include <tchar.h> // Search along the path.
//#include <stdio.h> // Search along the path.
#include <gl\GL.h>
#include <gl\GLU.h>
#pragma comment( lib, "opengl32.lib" ) // Search For OpenGL32.lib While Linking.
#pragma comment( lib, "glu32.lib" ) // Search For GLu32.lib While Linking.
#include <iostream> // Search along the path.
#include <string.h> // Search along the path.
using namespace std;
#include "zTrace.h" // Search in the same directory of the current file.
#include "WinLIFT.h" // Search in the same directory of the current file.
#include "GDImage.h"
#include "MD2ex.h"
#define _SCL_SECURE_NO_WARNINGS // No warning message when using sprintf, strcopy, etc.
#define NM_FIRST 0
#define NM_AUDIOCOMPLETION NM_FIRST - 80
#define NM_MOVIEPLAYING NM_FIRST - 81
#define TBS_AUTOTICKS 0x0001
#define TBS_HORZ 0x0000
#define TBS_VERT 0x0002
#define TBS_BOTTOM 0x0000
#define TBM_GETPOS WM_USER
#define TBM_SETPOS WM_USER + 5
#define TBM_SETRANGE WM_USER + 6
#define TBM_SETTICFREQ WM_USER + 20
#define TBM_SETPAGESIZE WM_USER + 21
#define TBM_SETLINESIZE WM_USER + 23
#define MyWindowWidth 668
#define MyWindowHeight 532
#define IDC_GLCTRL -100
#define IDC_ZOOM -101
#define IDC_LABEL -102
#define IDC_CLOCK -103
#define IDC_LOGO -104
#define IDC_TIMER -105
typedef struct {
float radius;
float barsize;
float xoff;
float yoff;
float zoff;
float xrot;
float yrot;
float xcpy;
float ycpy;
int move;
int xdown;
int ydown;
int mouseDown;
int listIndex;
} THISOBJECT;
// Global variables
HINSTANCE g_hInst; // Current instance
HWND g_hGLcontrol, g_hMain;
WNDPROC g_hGLproc;
THISOBJECT glObj;
GLUquadric* g_quadObj;
ZGLTEXTURE_EX mt[10];
ZGLFONT g_UseFont;
float g_fUseFrame;
int g_MaxFrame, g_mtCount;
string g_sResource;
// Declaration of the functions being used in this module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK glWndProc(HWND, UINT, WPARAM, LPARAM);
string FullExeName();
string ExePath();
void ComboSelectPlus (HWND hCombo, int nSelected);
int GetStaticValue(string sUseString);
void SetStaticValue(string sUseString, int nValue);
void UseThisZoom(int nPos);
void UseThisPhoto(string sUsePhoto, int bReset);
void InitializeGL();
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
char szWindowClass[] = "ZMD2DISPLAY";
//HANDLE hMutex = CreateMutex(NULL, 0, szWindowClass);
//if (hMutex) {
// if (GetLastError() == ERROR_ALREADY_EXISTS) {
// }
//}
// Load WinLIFT.dll and GDImage.dll.
if (LoadDLL_WinLIFT()) {
if (LoadDLL_GDImage()) {
// We must initialize GDImage using the private key
ZI_LoadDLL(""); }
else {
//Error("Unable to start GDImage")
return GetLastError();
}
LoadDLL_MD2ex(); }
else {
//Error("Unable to start WinLIFT")
return GetLastError();
}
// Declaration of local variables
MSG msg;
int K = 0;
int nRet = 0;
int IsInitialized = 0;
WNDCLASSEXA wcx;
POINT p;
RECT lpr;
//TCHAR szWindowClass[] = _T("ZHUDPLUS");
//TCHAR szTitle[] = _T("HUD window");
string sTitle = "GDImage control "; sTitle += ZI_Version(); sTitle += " - MD2 demo";
char szTitle[260] = ""; strncpy_s(szTitle, sTitle.c_str(), sTitle.size());
// Register the main window class
wcx.cbSize = sizeof(wcx);
IsInitialized = GetClassInfoExA(hInstance, szWindowClass, &wcx);
if (!IsInitialized)
{
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcx.lpszMenuName = NULL;
wcx.lpszClassName = szWindowClass;
wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (RegisterClassExA(&wcx))
{
IsInitialized = -1;
}
}
if (IsInitialized) {
g_hInst = hInstance; // Stocke le handle d'instance dans la variable globale
DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
SetRect(&lpr, 0, 0, MyWindowWidth, MyWindowHeight);
AdjustWindowRectEx(&lpr, dwStyle, FALSE, dwExStyle);
int w = lpr.right - lpr.left;
int h = lpr.bottom - lpr.top;
int x = max((GetSystemMetrics(SM_CXSCREEN) - w) / 2, 0);
int y = max((GetSystemMetrics(SM_CYSCREEN) - h) / 2, 0);
g_hMain = CreateWindowExA(
dwExStyle, // Optional window styles.
szWindowClass, // Window class
szTitle, // Window text
dwStyle, // Window style
// Size and position
x, y, w, h,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (g_hMain) {
g_hGLcontrol = CreateWindowExA(0, GLImageClassName, NULL,
WS_CHILD | WS_VISIBLE,
10, 10, 512, 512,
g_hMain, (HMENU) IDC_GLCTRL,
hInstance, NULL);
// Subclass the OpenGL control
if (g_hGLcontrol) {
g_hGLproc = (WNDPROC) SetWindowLongPtr(g_hGLcontrol, GWL_WNDPROC, (LONG) glWndProc);
}
ZI_SetAnchorMode(g_hGLcontrol, ANCHOR_HEIGHT_WIDTH); // Anchor the control (make it a resizable)
HWND hCtrl = CreateWindowExA(0, "MSCTLS_TRACKBAR32", NULL,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_HORZ | TBS_BOTTOM | TBS_AUTOTICKS,
537, 457, 120, 16,
g_hMain, (HMENU) IDC_ZOOM,
hInstance, NULL);
ZI_SetAnchorMode(hCtrl, ANCHOR_BOTTOM_RIGHT);
// Setup the trackbar parameters.
SendMessage(hCtrl, TBM_SETPAGESIZE, 0, 1);
SendMessage(hCtrl, TBM_SETLINESIZE, 0, 1);
SendMessage(hCtrl, TBM_SETTICFREQ, 0, 1);
SendMessage(hCtrl, TBM_SETRANGE, 1, MAKELONG(1, 100)); // Set the zoom range
SendMessage(hCtrl, TBM_SETPOS, 1, 50);
string sExePath = ExePath();
g_sResource = sExePath; g_sResource += "Resource\\";
g_mtCount = 2; // sizeof(mt) / sizeof(mt[0]);
string str;
str = g_sResource; str += "Background.jpg"; strcpy_s(mt[0].szFullName, str.c_str()); mt[0].ID = 1; mt[0].nSquare = 0;
str = g_sResource; str += "Dragon.png"; strcpy_s(mt[1].szFullName, str.c_str()); mt[1].ID = 2; mt[1].nSquare = 0;
MD2_MakeMultipleTexture(mt, g_mtCount);
for (K = 1; K < g_mtCount; K++) { str = mt[K].szFullName; str = str.substr(0, str.rfind(".")); str += ".md2"; strcpy_s(mt[K].szFullName, str.c_str()); }
InitializeGL();
strcpy_s(g_UseFont.fontName, "Pristina");
g_UseFont.fontHeight = 14;
g_UseFont.fontWeight = FW_BOLD;
g_UseFont.fontHandle = 0;
ZI_BuildGLfont(ZI_GetGLDC(g_hGLcontrol), g_UseFont); // Build OpenGL font for our OpenGL window
string SkinTheme = ExePath(); SkinTheme += "Sony.sks";
if (skInitEngine (SkinTheme, "")) {
skSkinWindow(g_hMain, "Dock|Undock|Minimize|Maximize|Restore|Close");
// Add a skinned border around the GDImage control.
GetWindowRect(g_hGLcontrol, &lpr);
p.x = lpr.left; p.y = lpr.top;
ScreenToClient(g_hMain, &p);
skBorder(g_hGLcontrol, p.x - 1, p.y - 1, lpr.right - lpr.left + 2, lpr.bottom - lpr.top + 2, 0, 1);
// Add a clock control.
string sResource = g_sResource; sResource += "Clock_09_92.png";
hCtrl = skClockCtrl(g_hMain, sResource, p.x + lpr.right - lpr.left + 28, p.y, 0, 0, IDC_CLOCK, 0x7FCB0000, 0x7F8F8F8F, 0);
skSetAnchorCtrl(hCtrl, ANCHOR_RIGHT); // Anchor control
skCreateToolTip(hCtrl, "WinLIFT clock widget"); // Add tooltip.
// Add a static control to display the help text.
GetClientRect(g_hMain, &lpr);
char szMessage[] = "Mouse buttons\n\nLeft: rotation, and view angle.\n\nRight: new X,Y location.";
hCtrl = CreateWindowExA(0, "STATIC", szMessage, WS_CHILD | WS_VISIBLE | SS_CENTER | WS_BORDER,
lpr.right - (129 + 8), 10 + (22 + 5) * 5 + 5, 129, 135, g_hMain, (HMENU) IDC_LABEL, hInstance, NULL);
// Skin the static control on the fly.
skSkinChildCtrl(hCtrl, 0);
ZI_SetAnchorMode(hCtrl, ANCHOR_RIGHT);
// Use a custom font with the static control.
skSetLabelFont(hCtrl, "TREBUCHET MS", 15, skARGB(200, 255,255,255), FontStyleBold);
// Diplay the GDImage logo.
sResource = g_sResource; sResource += "GDImage.png";
hCtrl = skStaticImage(g_hMain, sResource, lpr.right - (129 + 6), 350, 124, 45, IDC_LOGO);
ZI_SetAnchorMode(hCtrl, ANCHOR_BOTTOM_RIGHT);
ShowWindow(g_hMain, nCmdShow);
SetForegroundWindow(g_hMain);
SetFocus(g_hMain);
// Main message loop
SetTimer(g_hMain, IDC_TIMER, 0, 0);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
KillTimer(g_hMain, IDC_TIMER);
nRet = msg.wParam;
}
// Free OpenGL resources.
if (g_quadObj) { gluDeleteQuadric(g_quadObj); } // OpenGL quadric onject
if (g_mtCount) {
for (K = 1; K < g_mtCount; K++) { MD2_Delete(K); }
MD2_DestroyTexture (mt, g_mtCount);
}
}
}
return nRet;
}
void InitObject() {
glObj.xoff = 0.0; // Initial x location.
glObj.yoff = 0; // Initial y location.
glObj.zoff = -4; // Initial zoom factor.
glObj.xrot = 45.0;
glObj.yrot = -30.0;
glObj.radius = 2.0;
}
void InitializeGL() {
InitObject();
glEnable(GL_TEXTURE_2D);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
g_quadObj = gluNewQuadric();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
gluQuadricNormals(g_quadObj, GLU_SMOOTH);
GLfloat LightDiffuse[] = { 1.0f, 1.0f, -0.8f, 1.0f };
GLfloat LightAmbient[] = { 0.3f, 0.3f, -1.3f, 1.0f };
GLfloat LightPosition[] = { -50.0f, 150.0f, -100.0f, 1.0f };
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); // Setup the Diffuse Light
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // Setup the Ambient Light
glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); // Position the Light
glMaterialfv(GL_FRONT, GL_SPECULAR, LightDiffuse);
GLfloat red_emission[] = { 50.0f, -0.0f, 0.0f, 0.0f }; glMaterialfv(GL_FRONT, GL_EMISSION, red_emission);
GLfloat low_shininess[] = { 5.0f }; glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
glEnable(GL_LIGHT1); // Enable Light ONE
glEnable(GL_LIGHTING); // Enable Lighting
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL); // Enable Coloring Of Material
MD2_Load(1, mt[1].szFullName, mt[1].nTexture, mt[1].nWidth, mt[1].nHeight);
g_MaxFrame = MD2_GetFrameCount(1);
g_fUseFrame = 1.0f;
}
void DrawBackground() {
glBindTexture(GL_TEXTURE_2D, mt[0].nTexture);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(-80,-40,-80.0); // Bottom Left Of The Texture And Quad
glTexCoord2f(1.0, 0.0); glVertex3f( 80,-40,-80.0); // Bottom Right Of The Texture And Quad
glTexCoord2f(1.0, 1.0); glVertex3f( 80, 40,-80.0); // Top Right Of The Texture And Quad
glTexCoord2f(0.0, 1.0); glVertex3f(-80, 40,-80.0); // Top Left Of The Texture And Quad
glEnd();
}
void SetZoom(int Zoom) {
ZI_SetGLzoom(g_hGLcontrol, Zoom);
ZI_ResizeGLWindow(g_hGLcontrol);
}
void RenderOpenGL() {
int K, ZoomIs;
static int TextColor;
// Save current zoom, then switch to a fixed zoom for the background.
ZoomIs = ZI_GetGLzoom(g_hGLcontrol);
SetZoom(50);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Draw fixed background image
DrawBackground();
// Restore the previously saved zoom.
SetZoom(ZoomIs);
glTranslatef(glObj.xoff, glObj.yoff, glObj.zoff);
glRotatef(glObj.xrot, 1.0f, 0.0f, 0.0f);
glRotatef(glObj.yrot, 0.0f, 1.0f, 0.0f);
glPushMatrix();
glScalef(0.025f, 0.025f, 0.025f);
for (K = 1; K < g_mtCount; K++) {
MD2_Draw(K, g_fUseFrame);
}
glPopMatrix();
glTranslatef(2.0f * glObj.radius, 0.0f, 0.0f);
glTranslatef(-2.0f * glObj.radius, 0.0f, 2.0f * glObj.radius + 0.5f);
// Display the x,y location, and the current zoom value
if (TextColor == 0) { TextColor = ZD_ColorARGB(255, RGB(247, 225, 125)); }
char zString[64];
sprintf_s(zString,"Horizontal %.2f", glObj.xoff);
ZI_DrawGLText(g_hGLcontrol, g_UseFont, 7, 10, zString, TextColor);
sprintf_s(zString, "Vertical %.2f", glObj.yoff + 12);
ZI_DrawGLText(g_hGLcontrol, g_UseFont, 23, 10 + g_UseFont.fontHeight, zString, TextColor);
sprintf_s(zString, "Zoom %d", 100 - ZoomIs);
ZI_DrawGLText(g_hGLcontrol, g_UseFont, 41, 10 + g_UseFont.fontHeight * 2, zString, TextColor);
sprintf_s(zString, "Frame %.0f / %d", g_fUseFrame, g_MaxFrame);
ZI_DrawGLText(g_hGLcontrol, g_UseFont, 33, 10 + g_UseFont.fontHeight * 3, zString, TextColor);
// Update the display
ZI_UpdateGLWindow(g_hGLcontrol);
}
void PaintMain(HWND hWin) {
PAINTSTRUCT ps;
BeginPaint(hWin, &ps);
EndPaint(hWin, &ps);
RenderOpenGL();
}
// Main winproc
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
int nRet;
switch (uMsg) {
case WM_SIZE:
ZI_ResizeGLWindow(g_hGLcontrol);
RenderOpenGL();
break;
case WM_HSCROLL:
nRet = GetDlgCtrlID((HWND) lParam);
if (nRet == IDC_ZOOM) {
SetZoom(101 - SendMessageA((HWND) lParam, TBM_GETPOS, 0, 0)); // Do not use nPosition, because it could be NULL.
RenderOpenGL();
}
break;
case WM_TIMER:
static DWORD nTempo;
if (nTempo < GetTickCount()) {
g_fUseFrame += 0.25; if (g_fUseFrame > g_MaxFrame) { g_fUseFrame = 1; }
nTempo = GetTickCount() + 30; // 33 FPS ~= 30 ticks
RenderOpenGL();
}
return 0;
case WM_PAINT:
PaintMain(hWnd);
return 0;
case WM_DESTROY:
ZI_DeleteGLFont(g_UseFont);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
string FullExeName() {
string sResult;
char exepath[MAX_PATH];
if (GetModuleFileNameA(0, exepath, sizeof(exepath))) {
sResult = exepath;
}
return sResult;
}
string ExePath() {
string sResult;
char exepath[MAX_PATH];
if (GetModuleFileNameA(0, exepath, sizeof(exepath))) {
sResult = exepath;
sResult = sResult.substr( 0, sResult.rfind("\\"));
sResult += "\\";
}
return sResult;
}
LRESULT CALLBACK glWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
RECT lpr;
int x, y, dx, dy;
static int LeftMousing, RightMousing, wasLX, wasLY, wasRX, wasRY;
switch (uMsg) {
case WM_PAINT:
RenderOpenGL();
break;
case WM_LBUTTONDOWN:
if (!LeftMousing) {
x = GET_X_LPARAM(lParam); y = GET_Y_LPARAM(lParam);
glObj.xcpy = glObj.xrot;
glObj.ycpy = glObj.yrot;
glObj.xdown = x;
glObj.ydown = y;
LeftMousing = TRUE;
}
if (GetFocus() != hWnd) { SetFocus(hWnd); }
break;
case WM_LBUTTONUP:
LeftMousing = FALSE;
break;
case WM_RBUTTONDOWN:
if (!RightMousing) {
x = GET_X_LPARAM(lParam); y = GET_Y_LPARAM(lParam);
glObj.xcpy = glObj.xoff;
glObj.ycpy = glObj.yoff;
glObj.xdown = x;
glObj.ydown = y;
RightMousing = TRUE;
}
if (GetFocus() != hWnd) { SetFocus(hWnd); }
break;
case WM_RBUTTONUP:
RightMousing = FALSE;
break;
case WM_MOUSEMOVE:
x = GET_X_LPARAM(lParam); y = GET_Y_LPARAM(lParam);
if (!ZI_IsLButtonDown()) {
LeftMousing = FALSE; }
else {
if ((wasLX != x) | (wasLY != y)) {
if (LeftMousing) {
dx = x - glObj.xdown; glObj.yrot = glObj.ycpy + dx / 2.0f;
dy = y - glObj.ydown; glObj.xrot = glObj.xcpy + dy / 2.0f;
}
RenderOpenGL();
}
wasLX = x; wasLY = y;
}
if (!ZI_IsRButtonDown()) {
RightMousing = FALSE; }
else {
if ((wasLX != x) | (wasLY != y)) {
if (RightMousing) {
GetClientRect(g_hGLcontrol, &lpr);
dx = x - glObj.xdown; glObj.xoff = glObj.xcpy - (-45 * dx) / (12.0f * lpr.right); // Use 12.0 instead of 2.0 to reduce sensitivity
dy = y - glObj.ydown; glObj.yoff = glObj.ycpy + (-45 * dy) / (12.0f * lpr.bottom); // Use 12.0 instead of 2.0 to reduce sensitivity
}
RenderOpenGL();
}
wasRX = x; wasRY = y;
}
break;
}
return CallWindowProc(g_hGLproc, hWnd, uMsg, wParam, lParam);
}
As you can see on the attached screen shot, the C++ version looks exactly the same than the PB's.
C++, is a real challenge for me, i did from my best with my current knowledge of the language, but if you think that my code could be optimized, please let me know (as i am still learning) thank you!
...
Here is another MD2 demo running 3 animations altogether.
This one is provided with the latest header files for MD2ex, WinLIFT, and GDImage.
It does use the WinLIFT's theme "Gray.sks".
The complete VS2010 project is provided with the ZIP file attached to this post.
...
All the attachements have been updated, to fix the ZIP file corruption caused by the "Server Collapse".
...