forked from FWGS/Paranoia2
799 lines
22 KiB
C++
799 lines
22 KiB
C++
//
|
|
// Half-Life Model Viewer (c) 1999 by Mete Ciragan
|
|
//
|
|
// file: GlWindow.cpp
|
|
// last modified: May 04 1999, Mete Ciragan
|
|
// copyright: The programs and associated files contained in this
|
|
// distribution were developed by Mete Ciragan. The programs
|
|
// are not in the public domain, but they are freely
|
|
// distributable without licensing fees. These programs are
|
|
// provided without guarantee or warrantee expressed or
|
|
// implied.
|
|
//
|
|
// version: 1.2
|
|
//
|
|
// email: mete@swissquake.ch
|
|
// web: http://www.swissquake.ch/chumbalum-soft/
|
|
//
|
|
#include <mx.h>
|
|
#include <mxMessageBox.h>
|
|
#include <mxTga.h>
|
|
#include <mxPcx.h>
|
|
#include <mxBmp.h>
|
|
#include <gl.h>
|
|
#include <GL/glu.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include "GlWindow.h"
|
|
#include "StudioModel.h"
|
|
#include "ViewerSettings.h"
|
|
#include "ControlPanel.h"
|
|
#include "stringlib.h"
|
|
#include "mdlviewer.h"
|
|
#include "muzzle1.h"
|
|
#include "muzzle2.h"
|
|
#include "muzzle3.h"
|
|
|
|
extern char g_appTitle[];
|
|
extern bool g_bStopPlaying;
|
|
extern bool bUseWeaponOrigin;
|
|
extern bool bUseWeaponLeftHand;
|
|
extern bool bUseParanoiaFOV;
|
|
|
|
GlWindow *g_GlWindow = 0;
|
|
|
|
GlWindow :: GlWindow( mxWindow *parent, int x, int y, int w, int h, const char *label, int style ) : mxGlWindow( parent, x, y, w, h, label, style )
|
|
{
|
|
glDepthFunc( GL_LEQUAL );
|
|
|
|
if( !parent ) setVisible( true );
|
|
else mx :: setIdleWindow ( this );
|
|
|
|
// load muzzle flahses
|
|
loadTextureBuffer( muzzleflash1_bmp, sizeof( muzzleflash1_bmp ), TEXTURE_MUZZLEFLASH1 );
|
|
loadTextureBuffer( muzzleflash2_bmp, sizeof( muzzleflash2_bmp ), TEXTURE_MUZZLEFLASH2 );
|
|
loadTextureBuffer( muzzleflash3_bmp, sizeof( muzzleflash3_bmp ), TEXTURE_MUZZLEFLASH3 );
|
|
}
|
|
|
|
GlWindow :: ~GlWindow( void )
|
|
{
|
|
mx::setIdleWindow( 0 );
|
|
loadTexture( NULL, TEXTURE_GROUND );
|
|
loadTexture( NULL, TEXTURE_BACKGROUND );
|
|
loadTexture( NULL, TEXTURE_MUZZLEFLASH1 );
|
|
loadTexture( NULL, TEXTURE_MUZZLEFLASH2 );
|
|
loadTexture( NULL, TEXTURE_MUZZLEFLASH3 );
|
|
}
|
|
|
|
int GlWindow :: handleEvent( mxEvent *event )
|
|
{
|
|
static float oldrx, oldry, oldtz, oldtx, oldty;
|
|
static float oldlx, oldly;
|
|
static int oldx, oldy;
|
|
static double lastupdate;
|
|
|
|
switch( event->event )
|
|
{
|
|
|
|
case mxEvent::Idle:
|
|
{
|
|
static double prev;
|
|
double curr = (double) mx::getTickCount () / 1000.0;
|
|
double dt = (curr - prev);
|
|
#if 1
|
|
// clamp to 100fps
|
|
if( dt >= 0.0 && dt < 0.01 )
|
|
{
|
|
Sleep( Q_max( 10 - dt * 1000.0, 0 ) );
|
|
return 1;
|
|
}
|
|
#endif
|
|
g_studioModel.updateTimings( curr, dt );
|
|
|
|
rand (); // keep the random time dependent
|
|
|
|
if( !g_bStopPlaying && !g_viewerSettings.pause && prev != 0.0 )
|
|
{
|
|
if( !g_studioModel.AdvanceFrame( dt * g_viewerSettings.speedScale ))
|
|
d_cpl->resetPlayingSequence(); // hit the end of sequence
|
|
}
|
|
|
|
if( !g_viewerSettings.pause )
|
|
redraw();
|
|
|
|
// update counter every 0.2 secs
|
|
if(( curr - lastupdate ) > 0.2 )
|
|
{
|
|
g_ControlPanel->updateDrawnPolys();
|
|
lastupdate = curr;
|
|
}
|
|
|
|
prev = curr;
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case mxEvent::MouseUp:
|
|
{
|
|
g_viewerSettings.pause = false;
|
|
}
|
|
break;
|
|
|
|
case mxEvent::MouseDown:
|
|
{
|
|
oldrx = g_viewerSettings.rot[0];
|
|
oldry = g_viewerSettings.rot[1];
|
|
oldtx = g_viewerSettings.trans[0];
|
|
oldty = g_viewerSettings.trans[1];
|
|
oldtz = g_viewerSettings.trans[2];
|
|
oldlx = g_viewerSettings.gLightVec[0];
|
|
oldly = g_viewerSettings.gLightVec[1];
|
|
oldx = event->x;
|
|
oldy = event->y;
|
|
|
|
// HACKHACK: reset focus to main window to catch hot-keys again
|
|
if( g_MDLViewer ) SetFocus( (HWND) g_MDLViewer->getHandle ());
|
|
g_viewerSettings.pause = true;
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case mxEvent::MouseDrag:
|
|
{
|
|
if( g_viewerSettings.showTexture )
|
|
{
|
|
redraw ();
|
|
return 1;
|
|
}
|
|
if( event->buttons & mxEvent::MouseLeftButton )
|
|
{
|
|
if( event->modifiers & mxEvent::KeyShift )
|
|
{
|
|
g_viewerSettings.trans[0] = oldtx - (float)(event->x - oldx) * g_viewerSettings.movementScale;
|
|
g_viewerSettings.trans[1] = oldty + (float)(event->y - oldy) * g_viewerSettings.movementScale;
|
|
}
|
|
else if( event->modifiers & mxEvent::KeyCtrl )
|
|
{
|
|
float yaw = oldlx + (float)(event->x - oldx);
|
|
float pitch = oldly + (float)(event->y - oldy);
|
|
float sy, cy, sp, cp;
|
|
|
|
pitch = DEG2RAD( anglemod( pitch * 0.6f ));
|
|
yaw = DEG2RAD( anglemod( yaw * 0.6f ));
|
|
SinCos( yaw, &sy, &cy );
|
|
SinCos( pitch, &sp, &cp );
|
|
|
|
g_viewerSettings.gLightVec[0] = (cp*cy);
|
|
g_viewerSettings.gLightVec[1] = (-sy);
|
|
g_viewerSettings.gLightVec[2] = (sp*cy);
|
|
}
|
|
else
|
|
{
|
|
g_viewerSettings.rot[0] = oldrx + (float)(event->y - oldy);
|
|
g_viewerSettings.rot[1] = oldry + (float)(event->x - oldx);
|
|
}
|
|
}
|
|
else if( event->buttons & mxEvent::MouseRightButton )
|
|
{
|
|
g_viewerSettings.trans[2] = oldtz + (float)(event->y - oldy) * g_viewerSettings.movementScale;
|
|
}
|
|
|
|
redraw ();
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
} // switch (event->event)
|
|
|
|
return 1;
|
|
}
|
|
|
|
void GlWindow :: drawFloor( int texture )
|
|
{
|
|
float scale = 5.0f;
|
|
float dist = -100.0f;
|
|
glEnable( GL_MULTISAMPLE );
|
|
|
|
if( texture )
|
|
{
|
|
static Vector tMap( 0, 0, 0 );
|
|
static Vector dxMap( 1, 0, 0 );
|
|
static Vector dyMap( 0, 1, 0 );
|
|
|
|
Vector deltaPos;
|
|
Vector deltaAngles;
|
|
|
|
g_studioModel.GetMovement( g_studioModel.m_prevGroundCycle, deltaPos, deltaAngles );
|
|
|
|
float dpdd = scale / dist;
|
|
|
|
tMap.x = tMap.x + dxMap.x * deltaPos.x * dpdd + dxMap.y * deltaPos.y * dpdd;
|
|
tMap.y = tMap.y + dyMap.x * deltaPos.x * dpdd + dyMap.y * deltaPos.y * dpdd;
|
|
|
|
while (tMap.x < 0.0) tMap.x += 1.0f;
|
|
while (tMap.x > 1.0) tMap.x += -1.0f;
|
|
while (tMap.y < 0.0) tMap.y += 1.0f;
|
|
while (tMap.y > 1.0) tMap.y += -1.0f;
|
|
|
|
dxMap = VectorYawRotate( dxMap, -deltaAngles.y );
|
|
dyMap = VectorYawRotate( dyMap, -deltaAngles.y );
|
|
|
|
glBegin( GL_QUADS );
|
|
glTexCoord2f( tMap.x - (dxMap.x - dyMap.x) * scale, tMap.y - (-dxMap.y + dyMap.y) * scale );
|
|
glVertex3f( -dist, -dist, 0 );
|
|
|
|
glTexCoord2f( tMap.x + (dxMap.x + dyMap.x) * scale, tMap.y + (-dxMap.y - dyMap.y) * scale );
|
|
glVertex3f( dist, -dist, 0 );
|
|
|
|
glTexCoord2f( tMap.x + (dxMap.x - dyMap.x) * scale, tMap.y + (-dxMap.y + dyMap.y) * scale );
|
|
glVertex3f( dist, dist, 0 );
|
|
|
|
glTexCoord2f( tMap.x + (-dxMap.x - dyMap.x) * scale, tMap.y + (dxMap.y + dyMap.y) * scale );
|
|
glVertex3f( -dist, dist, 0 );
|
|
glEnd();
|
|
}
|
|
else
|
|
{
|
|
glBegin( GL_QUADS );
|
|
glTexCoord2f( 0.0f, 1.0f );
|
|
glVertex3f( -dist, -dist, 0 );
|
|
|
|
glTexCoord2f( 1.0f, 1.0f );
|
|
glVertex3f( dist, -dist, 0 );
|
|
|
|
glTexCoord2f( 1.0f, 0.0f );
|
|
glVertex3f( dist, dist, 0 );
|
|
|
|
glTexCoord2f( 0.0f, 0.0f );
|
|
glVertex3f( -dist, dist, 0 );
|
|
glEnd();
|
|
}
|
|
|
|
// restore original modelview
|
|
SetModelviewMatrix( m_modelview );
|
|
glDisable( GL_MULTISAMPLE );
|
|
}
|
|
|
|
void GlWindow :: setupRenderMode( void )
|
|
{
|
|
switch( g_viewerSettings.renderMode )
|
|
{
|
|
case RM_WIREFRAME:
|
|
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
|
|
glDisable( GL_TEXTURE_2D );
|
|
glDisable( GL_CULL_FACE );
|
|
glEnable( GL_DEPTH_TEST );
|
|
break;
|
|
case RM_FLATSHADED:
|
|
case RM_SMOOTHSHADED:
|
|
case RM_BONEWEIGHTS:
|
|
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
|
|
glDisable( GL_TEXTURE_2D );
|
|
glEnable( GL_CULL_FACE );
|
|
glEnable( GL_DEPTH_TEST );
|
|
|
|
if( g_viewerSettings.renderMode == RM_FLATSHADED )
|
|
glShadeModel( GL_FLAT );
|
|
else glShadeModel( GL_SMOOTH );
|
|
break;
|
|
default:
|
|
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
|
|
glEnable( GL_TEXTURE_2D );
|
|
glEnable( GL_CULL_FACE );
|
|
glEnable( GL_DEPTH_TEST );
|
|
glShadeModel( GL_SMOOTH );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GlWindow :: GluPerspective( float fov_y )
|
|
{
|
|
GLdouble xMin, xMax, yMin, yMax, zNear, zFar;
|
|
float aspect = (float)w() / h();
|
|
|
|
zFar = 131072.0f; // don't cull giantic models (e.g. skybox models)
|
|
zNear = bUseWeaponOrigin ? 4.0f : 0.1f; // ammo shell issues
|
|
|
|
if( bUseWeaponOrigin && bUseParanoiaFOV )
|
|
{
|
|
float fovX = 60.0f; // HL2 uses 54 as default
|
|
float fovY = V_CalcFov( fovX, w(), h() );
|
|
|
|
yMax = zNear * tan( fovY * M_PI / 360.0 );
|
|
yMin = -yMax;
|
|
|
|
xMax = zNear * tan( fovX * M_PI / 360.0 );
|
|
xMin = -xMax;
|
|
}
|
|
else
|
|
{
|
|
yMax = zNear * tan( fov_y * M_PI / 360.0 );
|
|
yMin = -yMax;
|
|
|
|
xMin = yMin * aspect;
|
|
xMax = yMax * aspect;
|
|
}
|
|
|
|
m_projection.CreateProjection( xMax, xMin, yMax, yMin, zNear, zFar );
|
|
SetProjectionMatrix( m_projection );
|
|
}
|
|
|
|
void GlWindow :: SetProjectionMatrix( const matrix4x4 source )
|
|
{
|
|
GLfloat dest[16];
|
|
|
|
source.CopyToArray( dest );
|
|
glMatrixMode( GL_PROJECTION );
|
|
glLoadMatrixf( dest );
|
|
}
|
|
|
|
void GlWindow :: SetModelviewMatrix( const matrix4x4 source )
|
|
{
|
|
GLfloat dest[16];
|
|
|
|
source.CopyToArray( dest );
|
|
glMatrixMode( GL_MODELVIEW );
|
|
glLoadMatrixf( dest );
|
|
}
|
|
|
|
void GlWindow :: ResetModelviewMatrix( void )
|
|
{
|
|
glMatrixMode( GL_MODELVIEW );
|
|
glLoadIdentity();
|
|
}
|
|
|
|
void GlWindow :: draw( void )
|
|
{
|
|
glClearColor( g_viewerSettings.bgColor[0], g_viewerSettings.bgColor[1], g_viewerSettings.bgColor[2], 0.0f );
|
|
|
|
if( g_viewerSettings.useStencil )
|
|
glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT );
|
|
else glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
|
|
|
|
glViewport( 0, 0, w2(), h2() );
|
|
glDisable( GL_MULTISAMPLE );
|
|
|
|
// remap textures if changed
|
|
g_studioModel.RemapTextures();
|
|
|
|
//
|
|
// show textures
|
|
//
|
|
if( g_viewerSettings.showTexture )
|
|
{
|
|
m_projection.CreateOrtho( 0.0f, w2(), h2(), 0.0f, 1.0f, -1.0f );
|
|
SetProjectionMatrix( m_projection );
|
|
glDisable( GL_MULTISAMPLE );
|
|
|
|
studiohdr_t *hdr = g_studioModel.getTextureHeader();
|
|
|
|
if( hdr )
|
|
{
|
|
mstudiotexture_t *ptextures = (mstudiotexture_t *)((byte *)hdr + hdr->textureindex);
|
|
float w = (float) ptextures[g_viewerSettings.texture].width * g_viewerSettings.textureScale;
|
|
float h = (float) ptextures[g_viewerSettings.texture].height * g_viewerSettings.textureScale;
|
|
|
|
ResetModelviewMatrix();
|
|
|
|
glDisable( GL_CULL_FACE );
|
|
glDisable( GL_BLEND );
|
|
|
|
if( ptextures[g_viewerSettings.texture].flags & STUDIO_NF_MASKED )
|
|
{
|
|
glEnable( GL_ALPHA_TEST );
|
|
glAlphaFunc( GL_GREATER, 0.25f );
|
|
}
|
|
else glDisable( GL_ALPHA_TEST );
|
|
|
|
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
|
|
float x = ((float)w2 () - w) / 2;
|
|
float y = ((float)h2 () - h) / 2;
|
|
|
|
if(( g_viewerSettings.show_uv_map || g_viewerSettings.pending_export_uvmap ) && !g_viewerSettings.overlay_uv_map )
|
|
{
|
|
glColor4f (0.0f, 0.0f, 0.0f, 1.0f);
|
|
glDisable (GL_TEXTURE_2D);
|
|
}
|
|
else
|
|
{
|
|
glEnable (GL_TEXTURE_2D);
|
|
glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
|
|
glBindTexture (GL_TEXTURE_2D, TEXTURE_COUNT + g_viewerSettings.texture );
|
|
}
|
|
|
|
glBegin( GL_TRIANGLE_STRIP );
|
|
glTexCoord2f( 0, 0 );
|
|
glVertex2f( x, y );
|
|
glTexCoord2f( 1, 0 );
|
|
glVertex2f( x + w, y );
|
|
glTexCoord2f( 0, 1 );
|
|
glVertex2f( x, y + h );
|
|
glTexCoord2f( 1, 1 );
|
|
glVertex2f( x + w, y + h );
|
|
glEnd();
|
|
|
|
if( g_viewerSettings.show_uv_map || g_viewerSettings.pending_export_uvmap || g_viewerSettings.overlay_uv_map )
|
|
{
|
|
if( g_viewerSettings.anti_alias_lines )
|
|
{
|
|
glEnable( GL_LINE_SMOOTH );
|
|
glEnable( GL_POLYGON_SMOOTH );
|
|
glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
|
|
glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
g_studioModel.SetOffset2D( x, y );
|
|
g_studioModel.DrawModelUVMap();
|
|
|
|
if( g_viewerSettings.anti_alias_lines )
|
|
{
|
|
glDisable( GL_LINE_SMOOTH );
|
|
glDisable(GL_POLYGON_SMOOTH);
|
|
}
|
|
}
|
|
|
|
if( g_viewerSettings.pending_export_uvmap && g_viewerSettings.uvmapPath[0] )
|
|
{
|
|
mxImage *image = new mxImage ();
|
|
|
|
if( image->create((int)w, (int)h, 24 ))
|
|
{
|
|
glReadBuffer( GL_BACK );
|
|
glReadPixels((int)x-1, (int)y, (int)w, (int)h, GL_RGB, GL_UNSIGNED_BYTE, image->data );
|
|
|
|
image->flip_vertical();
|
|
|
|
if( !mxBmpWrite( g_viewerSettings.uvmapPath, image ))
|
|
mxMessageBox( this, "Error writing .BMP texture.", g_appTitle, MX_MB_OK|MX_MB_ERROR );
|
|
}
|
|
|
|
// cleanup
|
|
memset( g_viewerSettings.uvmapPath, 0, sizeof( g_viewerSettings.uvmapPath ));
|
|
g_viewerSettings.pending_export_uvmap = false;
|
|
delete image;
|
|
}
|
|
|
|
glClear( GL_DEPTH_BUFFER_BIT );
|
|
glBindTexture( GL_TEXTURE_2D, 0 );
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
// draw background
|
|
//
|
|
if( g_viewerSettings.showBackground && d_textureNames[TEXTURE_BACKGROUND] && !g_viewerSettings.showTexture )
|
|
{
|
|
m_projection.CreateOrtho( 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f );
|
|
SetProjectionMatrix( m_projection );
|
|
|
|
ResetModelviewMatrix();
|
|
|
|
glDisable( GL_CULL_FACE );
|
|
glEnable( GL_TEXTURE_2D );
|
|
|
|
glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
|
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
|
|
|
|
glBindTexture( GL_TEXTURE_2D, d_textureNames[TEXTURE_BACKGROUND] );
|
|
|
|
glBegin( GL_TRIANGLE_STRIP );
|
|
glTexCoord2f( 0, 0 );
|
|
glVertex2f( 0, 0 );
|
|
glTexCoord2f( 1, 0 );
|
|
glVertex2f( 1, 0 );
|
|
glTexCoord2f( 0, 1 );
|
|
glVertex2f( 0, 1 );
|
|
glTexCoord2f( 1, 1 );
|
|
glVertex2f( 1, 1 );
|
|
glEnd();
|
|
|
|
glClear( GL_DEPTH_BUFFER_BIT );
|
|
glBindTexture( GL_TEXTURE_2D, 0 );
|
|
}
|
|
|
|
// calc viewer FOV
|
|
float fov_x = (bUseWeaponOrigin) ? 68.0f : 65.0f; // same as original
|
|
float fov_y = V_CalcFov( fov_x, w(), h() );
|
|
|
|
// NOTE: GluPerspective receives fov_y as input but original code sent fov_x
|
|
// this is completely wrong but i'm leave it for backward compatibility
|
|
GluPerspective( fov_x );
|
|
|
|
if( bUseWeaponOrigin )
|
|
{
|
|
m_modelview.CreateModelview(); // init quake world orientation
|
|
m_modelview.ConcatTranslate( 1.0f, 0.0f, -1.0f ); // shift back like in HL
|
|
vectors = matrix3x3( Vector( 90.0f, 0.0f, 0.0f ));
|
|
}
|
|
else
|
|
{
|
|
m_modelview.Identity();
|
|
m_modelview.ConcatTranslate( -g_viewerSettings.trans[0], -g_viewerSettings.trans[1], -g_viewerSettings.trans[2] );
|
|
m_modelview.ConcatRotate( g_viewerSettings.rot[0], 1, 0, 0 );
|
|
m_modelview.ConcatRotate( g_viewerSettings.rot[1], 0, 0, 1 );
|
|
vectors = matrix3x3( Vector( 180.0f - g_viewerSettings.rot[0], 270.0f - g_viewerSettings.rot[1], 0.0f ));
|
|
}
|
|
|
|
SetModelviewMatrix( m_modelview );
|
|
|
|
// setup stencil buffer
|
|
if( g_viewerSettings.useStencil && !bUseWeaponOrigin )
|
|
{
|
|
// Don't update color or depth.
|
|
glDisable( GL_DEPTH_TEST );
|
|
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
|
|
|
|
// Draw 1 into the stencil buffer.
|
|
glEnable( GL_STENCIL_TEST );
|
|
glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
|
|
glStencilFunc( GL_ALWAYS, 1, 0xffffffff );
|
|
|
|
// Now render floor; floor pixels just get their stencil set to 1.
|
|
drawFloor();
|
|
|
|
// Re-enable update of color and depth.
|
|
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
|
|
glEnable( GL_DEPTH_TEST );
|
|
|
|
// Now, only render where stencil is set to 1.
|
|
glStencilFunc( GL_EQUAL, 1, 0xffffffff ); // draw if == 1
|
|
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
|
|
}
|
|
|
|
if( g_viewerSettings.mirror && !bUseWeaponOrigin )
|
|
{
|
|
const GLdouble flClipPlane[] = { 0.0, 0.0, -1.0, 0.0 };
|
|
|
|
setupRenderMode();
|
|
glCullFace( GL_BACK );
|
|
glEnable( GL_CLIP_PLANE0 );
|
|
glClipPlane( GL_CLIP_PLANE0, flClipPlane );
|
|
g_studioModel.DrawModel( true );
|
|
glDisable( GL_CLIP_PLANE0 );
|
|
}
|
|
|
|
g_viewerSettings.drawn_polys = 0;
|
|
|
|
if( g_viewerSettings.useStencil )
|
|
glDisable( GL_STENCIL_TEST );
|
|
|
|
setupRenderMode();
|
|
|
|
if( bUseWeaponOrigin && bUseWeaponLeftHand )
|
|
glCullFace( GL_BACK );
|
|
else glCullFace( GL_FRONT );
|
|
|
|
g_studioModel.DrawModel();
|
|
|
|
//
|
|
// draw ground
|
|
//
|
|
if( g_viewerSettings.showGround && !bUseWeaponOrigin )
|
|
{
|
|
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
|
|
glEnable( GL_DEPTH_TEST );
|
|
glEnable( GL_CULL_FACE );
|
|
|
|
if( g_viewerSettings.useStencil )
|
|
glFrontFace( GL_CW );
|
|
else glDisable( GL_CULL_FACE );
|
|
|
|
glEnable( GL_BLEND );
|
|
|
|
if( !d_textureNames[TEXTURE_GROUND] )
|
|
{
|
|
glDisable( GL_TEXTURE_2D );
|
|
glColor4f( g_viewerSettings.gColor[0], g_viewerSettings.gColor[1], g_viewerSettings.gColor[2], 0.7f );
|
|
glBindTexture( GL_TEXTURE_2D, 0 );
|
|
}
|
|
else
|
|
{
|
|
glEnable( GL_TEXTURE_2D );
|
|
glColor4f( 1.0f, 1.0f, 1.0f, 0.6f );
|
|
glBindTexture( GL_TEXTURE_2D, d_textureNames[TEXTURE_GROUND] );
|
|
}
|
|
|
|
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
|
drawFloor( d_textureNames[TEXTURE_GROUND] );
|
|
glDisable( GL_BLEND );
|
|
|
|
if( g_viewerSettings.useStencil )
|
|
{
|
|
glCullFace( GL_BACK );
|
|
glColor4f( 0.1f, 0.1f, 0.1f, 1.0f );
|
|
glBindTexture( GL_TEXTURE_2D, 0 );
|
|
drawFloor();
|
|
glFrontFace( GL_CCW );
|
|
}
|
|
else glEnable( GL_CULL_FACE );
|
|
}
|
|
|
|
if( bUseWeaponOrigin )
|
|
{
|
|
float hW = (float)w2() / 2;
|
|
float hH = (float)h2() / 2;
|
|
float flColor = 1.0f;
|
|
int dir = 5;
|
|
|
|
m_projection.CreateOrtho( 0.0f, w2(), h2(), 0.0f, 1.0f, -1.0f );
|
|
SetProjectionMatrix( m_projection );
|
|
|
|
glDisable( GL_MULTISAMPLE );
|
|
glDisable( GL_TEXTURE_2D );
|
|
ResetModelviewMatrix();
|
|
|
|
glColor4f( 1.0f, flColor, flColor, 0.8f );
|
|
|
|
glBegin( GL_LINES );
|
|
glVertex3f( hW, hH - dir, 0.0f );
|
|
glVertex3f( hW, hH - ( CROSS_LENGTH * 1.2f ) - dir, 0.0f );
|
|
glEnd();
|
|
|
|
glBegin( GL_LINES );
|
|
glVertex3f( hW + dir, hH + dir, 0.0f );
|
|
glVertex3f( hW + CROSS_LENGTH + dir, hH + CROSS_LENGTH + dir, 0.0f );
|
|
glEnd();
|
|
|
|
glBegin( GL_LINES );
|
|
glVertex3f( hW - dir, hH + dir, 0.0f );
|
|
glVertex3f( hW - CROSS_LENGTH - dir, hH + CROSS_LENGTH + dir, 0.0f );
|
|
glEnd();
|
|
|
|
glEnable( GL_TEXTURE_2D );
|
|
}
|
|
|
|
g_studioModel.incrementFramecounter();
|
|
}
|
|
|
|
int GlWindow :: loadTextureImage( mxImage *image, int name )
|
|
{
|
|
if( image )
|
|
{
|
|
d_textureNames[name] = name;
|
|
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
|
|
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
|
|
|
|
if( image->bpp == 8 )
|
|
{
|
|
mstudiotexture_t texture;
|
|
texture.width = image->width;
|
|
texture.height = image->height;
|
|
texture.flags = 0;
|
|
|
|
g_studioModel.UploadTexture( &texture, (byte *)image->data, (byte *)image->palette, name );
|
|
}
|
|
else if( image->bpp == 24 )
|
|
{
|
|
glBindTexture( GL_TEXTURE_2D, d_textureNames[name] );
|
|
glHint( GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
|
|
glTexImage2D( GL_TEXTURE_2D, 0, 3, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data );
|
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
|
|
const char *extensions = (const char *)glGetString( GL_EXTENSIONS );
|
|
|
|
// check for anisotropy support
|
|
if( Q_strstr( extensions, "GL_EXT_texture_filter_anisotropic" ))
|
|
{
|
|
float anisotropy = 1.0f;
|
|
glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy );
|
|
}
|
|
}
|
|
else if( image->bpp == 32 )
|
|
{
|
|
glBindTexture( GL_TEXTURE_2D, d_textureNames[name] );
|
|
glHint( GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
|
|
glTexImage2D( GL_TEXTURE_2D, 0, 4, image->width, image->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->data );
|
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
|
|
const char *extensions = (const char *)glGetString( GL_EXTENSIONS );
|
|
|
|
// check for anisotropy support
|
|
if( Q_strstr( extensions, "GL_EXT_texture_filter_anisotropic" ))
|
|
{
|
|
float anisotropy = 1.0f;
|
|
glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy );
|
|
}
|
|
}
|
|
|
|
delete image;
|
|
|
|
return name;
|
|
}
|
|
|
|
return TEXTURE_UNUSED;
|
|
}
|
|
|
|
int GlWindow :: loadTexture( const char *filename, int name )
|
|
{
|
|
if( !filename || !strlen( filename ))
|
|
{
|
|
if( d_textureNames[name] )
|
|
{
|
|
glDeleteTextures( 1, (const GLuint *)&d_textureNames[name] );
|
|
d_textureNames[name] = TEXTURE_UNUSED;
|
|
|
|
if( name == TEXTURE_BACKGROUND )
|
|
strcpy( g_viewerSettings.backgroundTexFile, "" );
|
|
else if( name == TEXTURE_GROUND )
|
|
strcpy( g_viewerSettings.groundTexFile, "" );
|
|
}
|
|
return TEXTURE_UNUSED;
|
|
}
|
|
|
|
mxImage *image = NULL;
|
|
char ext[16];
|
|
|
|
strcpy( ext, mx_getextension( filename ));
|
|
|
|
if( !mx_strcasecmp( ext, ".tga" ))
|
|
image = mxTgaRead( filename );
|
|
else if( !mx_strcasecmp( ext, ".pcx" ))
|
|
image = mxPcxRead( filename );
|
|
else if( !mx_strcasecmp( ext, ".bmp" ))
|
|
image = mxBmpRead( filename );
|
|
|
|
if( image )
|
|
{
|
|
if( name == TEXTURE_BACKGROUND )
|
|
strcpy( g_viewerSettings.backgroundTexFile, filename );
|
|
else if( name == TEXTURE_GROUND )
|
|
strcpy( g_viewerSettings.groundTexFile, filename );
|
|
}
|
|
|
|
return loadTextureImage( image, name );
|
|
}
|
|
|
|
int GlWindow :: loadTextureBuffer( const byte *buffer, size_t size, int name )
|
|
{
|
|
if( !buffer || size <= 0 )
|
|
{
|
|
if( d_textureNames[name] )
|
|
{
|
|
glDeleteTextures( 1, (const GLuint *)&d_textureNames[name] );
|
|
d_textureNames[name] = TEXTURE_UNUSED;
|
|
}
|
|
return TEXTURE_UNUSED;
|
|
}
|
|
|
|
mxImage *image = NULL;
|
|
|
|
image = mxBmpReadBuffer( buffer, size );
|
|
|
|
return loadTextureImage( image, name );
|
|
}
|
|
|
|
void GlWindow :: dumpViewport( const char *filename )
|
|
{
|
|
redraw();
|
|
int w = w2();
|
|
int h = h2();
|
|
|
|
mxImage *image = new mxImage ();
|
|
if( image->create( w, h, 24 ))
|
|
{
|
|
glReadBuffer( GL_FRONT );
|
|
glReadPixels( 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, image->data );
|
|
|
|
image->flip_vertical();
|
|
|
|
if( !mxBmpWrite( filename, image ))
|
|
mxMessageBox( this, "Error writing screenshot.", g_appTitle, MX_MB_OK|MX_MB_ERROR );
|
|
|
|
delete image;
|
|
}
|
|
} |