Paranoia2_original/utils/hlsv/sprite_render.cpp

479 lines
12 KiB
C++

/***
*
* Copyright (c) 1998, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
// updates:
// 1-4-99 fixed file texture load and file read bug
////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gl.h>
#include <GL/glu.h>
#include "SpriteModel.h"
#include "GLWindow.h"
#include "ViewerSettings.h"
#include "stringlib.h"
#include <mx.h>
#include "sprviewer.h"
vec3_t g_lightcolor;
bool bUseWeaponOrigin = false;
extern bool g_bStopPlaying;
void SpriteModel :: centerView( bool reset )
{
vec3_t min, max;
ExtractBbox( min, max );
float dx = max[0] - min[0];
float dy = max[1] - min[1];
float dz = max[2] - min[2];
float d = max( dx, max( dy, dz ));
if( reset )
{
g_viewerSettings.trans[0] = 0;
g_viewerSettings.trans[1] = 0;
g_viewerSettings.trans[2] = 0;
}
else
{
g_viewerSettings.trans[0] = 0;
g_viewerSettings.trans[1] = min[2] + dz / 2.0f;
g_viewerSettings.trans[2] = d * 1.0f;
}
g_viewerSettings.rot[0] = -90.0f;
g_viewerSettings.rot[1] = -90.0f;
g_viewerSettings.rot[2] = 0.0f;
g_viewerSettings.movementScale = max( 1.0f, d * 0.01f );
m_yaw = 90.0;
}
int SpriteModel :: setFrame( int newframe )
{
if( !m_pspritehdr )
return 0;
if( newframe == -1 )
return m_frame;
if( newframe < g_GlWindow->getStartFrame( ))
return m_frame;
if( newframe > g_GlWindow->getEndFrame( ))
return m_frame;
m_frame = newframe;
return newframe;
}
/*
================
R_GetSpriteFrame
assume pModel is valid
================
*/
mspriteframe_t *SpriteModel :: GetSpriteFrame( int frame )
{
msprite_t *psprite = m_pspritehdr;
mspritegroup_t *pspritegroup;
mspriteframe_t *pspriteframe = NULL;
float *pintervals, fullinterval;
int i, numframes;
float targettime;
if( !psprite ) return NULL;
if( frame < 0 )
{
frame = 0;
}
else if( frame >= psprite->numframes )
{
frame = psprite->numframes - 1;
}
if( psprite->frames[frame].type == FRAME_SINGLE )
{
pspriteframe = psprite->frames[frame].frameptr;
}
else if( psprite->frames[frame].type == FRAME_GROUP )
{
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
pintervals = pspritegroup->intervals;
numframes = pspritegroup->numframes;
fullinterval = pintervals[numframes-1];
// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
// are positive, so we don't have to worry about division by zero
targettime = m_flTime - ((int)( m_flTime / fullinterval )) * fullinterval;
for( i = 0; i < (numframes - 1); i++ )
{
if( pintervals[i] > targettime )
break;
}
pspriteframe = pspritegroup->frames[i];
}
else if( psprite->frames[frame].type == FRAME_ANGLED )
{
int angleframe = (int)(Q_rint(( m_yaw - g_viewerSettings.rot[1] + 45.0f ) / 360 * 8) - 4) & 7;
// e.g. doom-style sprite monsters
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
pspriteframe = pspritegroup->frames[angleframe];
}
return pspriteframe;
}
/*
================
GetSpriteFrameInterpolant
NOTE: we using prevblending[0] and [1] for holds interval
between frames where are we lerping
================
*/
float SpriteModel :: GetSpriteFrameInterpolant( int frame, mspriteframe_t **oldframe, mspriteframe_t **curframe )
{
msprite_t *psprite = m_pspritehdr;
mspritegroup_t *pspritegroup;
int i, j, numframes;
float lerpFrac, jtime, jinterval;
float *pintervals, fullinterval, targettime;
int m_fDoInterp = true;
if( !psprite ) return 0.0f;
lerpFrac = 1.0f;
if( frame < 0 )
{
frame = 0;
}
else if( frame >= psprite->numframes )
{
frame = psprite->numframes - 1;
}
if( psprite->frames[frame].type == FRAME_SINGLE )
{
if( m_fDoInterp )
{
if( m_prevblending[0] >= psprite->numframes || psprite->frames[m_prevblending[0]].type != FRAME_SINGLE )
{
// this can be happens when rendering switched between single and angled frames
// or change model on replace delta-entity
m_prevblending[0] = m_prevblending[1] = frame;
m_sequencetime = m_flTime;
lerpFrac = 1.0f;
}
if( m_sequencetime < m_flTime )
{
if( frame != m_prevblending[1] )
{
m_prevblending[0] = m_prevblending[1];
m_prevblending[1] = frame;
m_sequencetime = m_flTime;
lerpFrac = 0.0f;
}
else lerpFrac = (m_flTime - m_sequencetime) * 11.0f;
}
else
{
m_prevblending[0] = m_prevblending[1] = frame;
m_sequencetime = m_flTime;
lerpFrac = 0.0f;
}
}
else
{
m_prevblending[0] = m_prevblending[1] = frame;
lerpFrac = 1.0f;
}
if( m_prevblending[0] >= psprite->numframes )
{
// reset interpolation on change model
m_prevblending[0] = m_prevblending[1] = frame;
m_sequencetime = m_flTime;
lerpFrac = 0.0f;
}
// get the interpolated frames
if( oldframe ) *oldframe = psprite->frames[m_prevblending[0]].frameptr;
if( curframe ) *curframe = psprite->frames[frame].frameptr;
}
else if( psprite->frames[frame].type == FRAME_GROUP )
{
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
pintervals = pspritegroup->intervals;
numframes = pspritegroup->numframes;
fullinterval = pintervals[numframes-1];
jinterval = pintervals[1] - pintervals[0];
jtime = 0.0f;
// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
// are positive, so we don't have to worry about division by zero
targettime = m_flTime - ((int)( m_flTime / fullinterval)) * fullinterval;
// LordHavoc: since I can't measure the time properly when it loops from numframes - 1 to 0,
// i instead measure the time of the first frame, hoping it is consistent
for( i = 0, j = numframes - 1; i < (numframes - 1); i++ )
{
if( pintervals[i] > targettime )
break;
j = i;
jinterval = pintervals[i] - jtime;
jtime = pintervals[i];
}
if( m_fDoInterp )
lerpFrac = (targettime - jtime) / jinterval;
else j = i; // no lerping
// get the interpolated frames
if( oldframe ) *oldframe = pspritegroup->frames[j];
if( curframe ) *curframe = pspritegroup->frames[i];
}
else if( psprite->frames[frame].type == FRAME_ANGLED )
{
// e.g. doom-style sprite monsters
int angleframe = (int)(Q_rint(( g_viewerSettings.rot[1] - m_yaw + 45.0f ) / 360 * 8) - 4) & 7;
if( m_fDoInterp )
{
if( m_prevblending[0] >= psprite->numframes || psprite->frames[m_prevblending[0]].type != FRAME_ANGLED )
{
// this can be happens when rendering switched between single and angled frames
// or change model on replace delta-entity
m_prevblending[0] = m_prevblending[1] = frame;
m_sequencetime = m_flTime;
lerpFrac = 1.0f;
}
if( m_sequencetime < m_flTime )
{
if( frame != m_prevblending[1] )
{
m_prevblending[0] = m_prevblending[1];
m_prevblending[1] = frame;
m_sequencetime = m_flTime;
lerpFrac = 0.0f;
}
else lerpFrac = (m_flTime - m_sequencetime) * 11.0f;
}
else
{
m_prevblending[0] = m_prevblending[1] = frame;
m_sequencetime = m_flTime;
lerpFrac = 0.0f;
}
}
else
{
m_prevblending[0] = m_prevblending[1] = frame;
lerpFrac = 1.0f;
}
pspritegroup = (mspritegroup_t *)psprite->frames[m_prevblending[0]].frameptr;
if( oldframe ) *oldframe = pspritegroup->frames[angleframe];
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
if( curframe ) *curframe = pspritegroup->frames[angleframe];
}
return lerpFrac;
}
/*
=================
DrawSpriteQuad
=================
*/
void SpriteModel :: DrawQuad( mspriteframe_t *frame, const vec3_t &org, const vec3_t &v_right, const vec3_t &v_up, float scale )
{
vec3_t point;
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 1.0f );
point = org + v_up * (frame->down * scale);
point = point + v_right * (frame->left * scale);
glVertex3fv( point );
glTexCoord2f( 0.0f, 0.0f );
point = org + v_up * (frame->up * scale);
point = point + v_right * (frame->left * scale);
glVertex3fv( point );
glTexCoord2f( 1.0f, 0.0f );
point = org + v_up * (frame->up * scale);
point = point + v_right * (frame->right * scale);
glVertex3fv( point );
glTexCoord2f( 1.0f, 1.0f );
point = org + v_up * (frame->down * scale);
point = point + v_right * (frame->right * scale);
glVertex3fv( point );
glEnd();
}
/*
=================
R_SpriteAllowLerping
=================
*/
bool SpriteModel :: AllowLerping( void )
{
if( !m_pspritehdr )
return false;
if( m_pspritehdr->numframes <= 1 )
return false;
if( g_viewerSettings.renderMode != SPR_ADDITIVE )
return false;
return true;
}
void SpriteModel :: SetupTransform( void )
{
vec3_t angles( 0.0f, 0.0f, 0.0f );
vec3_t origin( 0.0f, 0.0f, 0.0f );
m_protationmatrix = matrix4x4( origin, angles, 1.0f );
angles[0] = 270.0f - g_viewerSettings.rot[0];
angles[1] = 270.0f - g_viewerSettings.rot[1];
origin = g_viewerSettings.trans;
m_viewVectors = matrix4x4( origin, angles, 1.0f );
}
/*
================
StudioModel::DrawModel
inputs:
currententity
r_entorigin
================
*/
void SpriteModel :: DrawSprite( void )
{
mspriteframe_t *frame, *oldframe;
float angle, dot, sr, cr;
float lerp = 1.0f, ilerp;
vec3_t v_right, v_up;
vec3_t origin, color;
float blend = 1.0f;
msprite_t *psprite;
int i, type;
if( !m_pspritehdr ) return;
SetupTransform();
psprite = (msprite_t * )m_pspritehdr;
origin = g_vecZero; // set render origin
// all sprites can have color
color = Vector( 1.0f, 1.0f, 1.0f );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
if( g_viewerSettings.renderMode == SPR_ADDITIVE )
blend = 0.5f;
if( g_viewerSettings.renderMode == SPR_ALPHTEST )
{
color[0] = g_viewerSettings.lColor[0];
color[1] = g_viewerSettings.lColor[1];
color[2] = g_viewerSettings.lColor[2];
}
if( AllowLerping( ))
lerp = GetSpriteFrameInterpolant( m_frame, &oldframe, &frame );
else frame = oldframe = GetSpriteFrame( m_frame );
type = psprite->type;
switch( g_viewerSettings.orientType )
{
case SPR_ORIENTED:
v_right = m_protationmatrix[1];
v_up = m_protationmatrix[2];
break;
case SPR_FACING_UPRIGHT:
case SPR_FWD_PARALLEL_UPRIGHT:
dot = m_viewVectors[0][2];
if(( dot > 0.999848f ) || ( dot < -0.999848f )) // cos(1 degree) = 0.999848
return; // invisible
v_up = Vector( 0.0f, 0.0f, 1.0f );
v_right = Vector( -m_viewVectors[0][1], m_viewVectors[0][0], 0.0f ).Normalize();
break;
case SPR_FWD_PARALLEL_ORIENTED:
angle = 0.0f * (M_PI2 / 360.0f);
SinCos( angle, &sr, &cr );
for( i = 0; i < 3; i++ )
{
v_right[i] = (m_viewVectors[1][i] * cr + m_viewVectors[2][i] * sr);
v_up[i] = m_viewVectors[1][i] * -sr + m_viewVectors[2][i] * cr;
}
break;
case SPR_FWD_PARALLEL: // normal sprite
default:
v_right = m_viewVectors[1];
v_up = m_viewVectors[2];
break;
}
if( psprite->facecull == SPR_CULL_NONE )
glCullFace( GL_NONE );
if( oldframe == frame )
{
// draw the single non-lerped frame
glColor4f( color[0], color[1], color[2], blend );
glBindTexture( GL_TEXTURE_2D, frame->gl_texturenum );
DrawQuad( frame, origin, v_right, v_up, 1.0f );
}
else
{
// draw two combined lerped frames
lerp = bound( 0.0f, lerp, 1.0f );
ilerp = 1.0f - lerp;
if( ilerp != 0.0f )
{
glColor4f( color[0], color[1], color[2], blend * ilerp );
glBindTexture( GL_TEXTURE_2D, oldframe->gl_texturenum );
DrawQuad( oldframe, origin, v_right, v_up, 1.0f );
}
if( lerp != 0.0f )
{
glColor4f( color[0], color[1], color[2], blend * lerp );
glBindTexture( GL_TEXTURE_2D, frame->gl_texturenum );
DrawQuad( frame, origin, v_right, v_up, 1.0f );
}
}
if( psprite->facecull == SPR_CULL_NONE )
glCullFace( GL_FRONT );
glDisable( GL_ALPHA_TEST );
glDepthMask( GL_TRUE );
glDisable( GL_BLEND );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
glEnable( GL_DEPTH_TEST );
}