st/code/ui/ui_anim.c
2008-04-04 00:00:00 +00:00

2049 lines
44 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2007 HermitWorks Entertainment Corporation
This file is part of the Space Trader source code.
The Space Trader source code 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.
The Space Trader source code 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 the Space Trader source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "ui_local.h"
#include "ui_anim.h"
/*
These are the curve types.
CT_FREE indicates an empty curve slot and always evaluates to <0, done>.
A curve of a given type is created by calling curve_create_<type's-base-name>.
*/
typedef enum curveType_t
{
CT_NOT_FREE = -1,
CT_FREE = 0,
CT_FUNC,
CT_CONSTANT,
CT_LERP,
} curveType_t;
/*
A func_t is a function that takes a [0, 1] value
that represents the function's full "natural" period
and returns it's "natural" value at that point.
For example, a func_t implementing sin would return
sin( t * M_PI * 2 ).
*/
typedef float (*func_t)( float t );
/*
The props for the sprite and the effect. Note that the first N props must
both sprites and effects have in common must be in the same order.
*/
typedef enum commonProp_t
{
CP_POSITION_X,
CP_POSITION_Y,
CP_SCALE_X,
CP_SCALE_Y,
CP_ROTATION,
} commonProp_t;
typedef enum spriteProp_t
{
SP_POSITION_X,
SP_POSITION_Y,
SP_SCALE_X,
SP_SCALE_Y,
SP_ROTATION,
SP_COLOR_R,
SP_COLOR_G,
SP_COLOR_B,
SP_COLOR_A,
SP_NUM_PROPS,
} spriteProp_t;
typedef enum {
EP_POSITION_X,
EP_POSITION_Y,
EP_SCALE_X,
EP_SCALE_Y,
EP_ROTATION,
EP_NUM_PROPS,
} effectProp_t;
#define CURVE_DONE 0x00000001
#define CURVE_DELETEME 0x00000002
typedef struct
{
curveType_t type;
/*
This union holds all of the parameters to run
the curve. Data members shall be named the same
as the curveType_t element they correspond to,
all lowercase and without the CT_ prefix.
Data should be strucutred such that zero-initialization
makes sense.
*/
union
{
struct
{
func_t fcb;
float base_val;
float amplitude;
float phase;
float frequency;
} func;
struct
{
float val;
float duration;
} constant;
struct
{
float start;
float end;
float duration;
qboolean hold;
} lerp;
} data;
/*
Evaluation time = (toSecs(current_time - timeBase) * timeScale) % timeMod.
The toSecs conversion is premultiplied into the timeScale
factor upon curve creation.
Note that timeMod is only applied if timeMod > 0.
The timeBase subtraction is done in unsigned integer milliseconds in order
to avoid cancellation errors on large time values. Everything beyond that
is done in floats as we should (ideally) be in the 0-1 neighborhood.
*/
uint timeBase;
float timeScale;
float timeMod;
float value;
int flags;
/*
This is a counter to give us a way to track lifetimes.
It gets incremented upon acurve expiring.
Zero is reserved as invalid - if seqNum is zero, it is
incremented upon the curve's creation as well.
*/
short seqNum;
} animCurve_t;
#define SPRITE_DELETEME 0x000000001
typedef struct sprite_t
{
float baseVals[SP_NUM_PROPS]; //a set of implicit non-expiring constant curves that are always in effect
qhandle_t bindings[SP_NUM_PROPS][8];
float propVals[SP_NUM_PROPS]; //the final computed values
float w,h;
vec4_t uv_coord;
qhandle_t shader;
int flags;
short seqNum;
} sprite_t;
typedef struct
{
const char * pc;
int i;
} stackFrame_t;
typedef struct effect_t
{
effectDef_t * def;
rectDef_t rect;
char text[ EFFECT_TEXT_LENGTH ];
qhandle_t shader;
qhandle_t props[EP_NUM_PROPS][8];
float baseVals[EP_NUM_PROPS];
float propVals[EP_NUM_PROPS];
int thread;
qhandle_t curves[EFFECT_TEXT_LENGTH * 4];
int curveCount;
qhandle_t sprites[EFFECT_TEXT_LENGTH * 4];
int spriteCount;
int flags; // status flags from outside
int touch; // counter that decrements (stopping at zero) on each draw
int waitfor; // bitfield of stuff to wait for
int sleeping;
itemDef_t * item;
// a small computer
stackFrame_t stack[ 8 ];
int sp;
short seqNum;
} effect_t;
static struct
{
sprite_t sprites [ 384 ];
sprite_t * freelist[ 384 ];
int count;
} sprites;
static struct
{
animCurve_t curves [ 512 ];
animCurve_t * freelist[ 512 ];
int count;
} curves;
static struct
{
effect_t effects [ 64 ];
effect_t * freelist[ 64 ];
int count;
} effects;
/*
Animation curves
**************************************************************************/
static ID_INLINE animCurve_t * curve_get( qhandle_t h ) {
if ( h )
{
int index = (h&0xFFFF)-1;
int seqNum = (h>>16)&0xFFFF;
animCurve_t * c = curves.curves + index;
if ( c->seqNum == seqNum )
return c;
}
return 0;
}
static ID_INLINE qhandle_t curve_getId( animCurve_t * c ) {
return ((c->seqNum&0xFFFF)<<16) | ((c-curves.curves+1)&0xFFFF);
}
static ID_INLINE animCurve_t * curve_alloc( void )
{
short seqNum;
animCurve_t *cv;
if( curves.count >= lengthof( curves.freelist ) )
return 0;
cv = curves.freelist[curves.count++];
seqNum = cv->seqNum;
Com_Memset( cv, 0, sizeof( *cv ) );
cv->seqNum = seqNum;
return cv;
}
/*
The following functions initialize new curves.
*/
static ID_INLINE animCurve_t * curve_create_base( uint timeBase, float timeScale, float timeMod, curveType_t type )
{
animCurve_t * c = curve_alloc();
if( !c )
return 0;
c->type = type;
c->timeBase = timeBase;
c->timeScale = timeScale;
c->timeMod = timeMod;
c->flags = 0;
return c;
}
/*
This is pretty much the cannonical curve create function.
It calls curve_create_base to set up the common curve parameters,
checks for an allocation failure, and then sets up the curve-type
specific parameters. Finally it calls curve_eval to set up the initial
curve value. One of these should exist for each curve type.
*/
qhandle_t curve_create_func( int timeBase, float timeScale, float timeMod,
func_t func, float freq, float phase, float amp, float base )
{
animCurve_t *c;
if( !func )
return 0;
c = curve_create_base( timeBase, timeScale, timeMod, CT_FUNC );
if( !c )
return 0;
c->data.func.fcb = func;
c->data.func.frequency = freq;
c->data.func.phase = phase;
c->data.func.amplitude = amp;
c->data.func.base_val = base;
return curve_getId( c );
}
qhandle_t curve_create_constant( int timeBase, float val, float duration )
{
animCurve_t *c;
c = curve_create_base( timeBase, 1, 0, CT_CONSTANT );
if( !c )
return 0;
c->data.constant.val = val;
c->data.constant.duration = duration;
return curve_getId( c );
}
static qhandle_t curve_create_lerp( int timeBase, float start, float end, float duration, qboolean hold )
{
animCurve_t *c;
c = curve_create_base( timeBase, 1.0F, 0.0F, CT_LERP );
if( !c )
return 0;
c->data.lerp.start = start;
c->data.lerp.end = end;
c->data.lerp.duration = duration;
c->data.lerp.hold = hold;
return curve_getId( c );
}
/*
Curve destruction.
*/
void curve_destroy( qhandle_t h )
{
animCurve_t * c = curve_get( h );
if ( c )
c->flags |= CURVE_DELETEME;
}
/*
Curve evaluation. Fun stuff.
*/
float fracf( float f ) { return f - floorf( f ); }
static void curve_eval( animCurve_t *cv, uint eval_time )
{
float t;
if( !cv )
return;
if( eval_time >= cv->timeBase )
eval_time -= cv->timeBase;
else
eval_time = 0;
t = (float)eval_time * cv->timeScale;
if( cv->timeMod > 0 )
t = fmodf( t, cv->timeMod );
switch( cv->type )
{
case CT_FUNC:
if( t > 1 )
{
t = 1;
cv->flags |= CURVE_DONE;
}
else
cv->flags &= ~CURVE_DONE;
t = fracf( t * cv->data.func.frequency + cv->data.func.phase );
cv->value = cv->data.func.fcb( t ) * cv->data.func.amplitude + cv->data.func.base_val;
break;
case CT_CONSTANT:
cv->value = cv->data.constant.val;
break;
case CT_LERP:
t = t / cv->data.lerp.duration;
if( t > 1.0f )
{
t = 1.0f;
cv->flags |= CURVE_DONE;
}
else
cv->flags &= ~CURVE_DONE;
cv->value = cv->data.lerp.start * (1.0F - t) + cv->data.lerp.end * t;
break;
default:
cv->flags |= CURVE_DONE;
cv->value = 0;
break;
}
}
void curves_update()
{
int i;
for ( i=0; i<curves.count; i++ )
{
animCurve_t * c = curves.freelist[ i ];
if ( c->flags & CURVE_DELETEME )
{
curves.freelist[ i-- ] = curves.freelist[ --curves.count ];
curves.freelist[ curves.count ] = c;
c->seqNum++;
}
}
}
float curve_get_value( qhandle_t h )
{
animCurve_t * c = curve_get( h );
if ( c )
return c->value;
return 0.0f;
}
qboolean curve_is_done( qhandle_t h )
{
animCurve_t * c = curve_get( h );
return (c)?c->flags&CURVE_DONE:1;
}
/*
Sprites
**************************************************************************/
static ID_INLINE sprite_t * sprite_get( qhandle_t h ) {
if ( h )
{
int index = (h&0xFFFF)-1;
int seqNum = (h>>16)&0xFFFF;
sprite_t * s = sprites.sprites + index;
if ( s->seqNum == seqNum )
return s;
}
return 0;
}
static ID_INLINE qhandle_t sprite_getId( sprite_t * s ) {
return ((s->seqNum&0xFFFF)<<16) | ((s-sprites.sprites+1)&0xFFFF);
}
static ID_INLINE sprite_t * sprite_alloc( void ) {
if ( sprites.count >= lengthof( sprites.freelist ) )
return 0;
return sprites.freelist[ sprites.count++ ];
}
static void sprite_eval( sprite_t *spr )
{
int i;
for ( i=0; i<lengthof( spr->propVals ); i++ )
{
int j;
spr->propVals[i] = spr->baseVals[i];
for ( j=0; j<lengthof( spr->bindings[i] ); j++ )
{
animCurve_t *cv = curve_get( spr->bindings[i][j] );
if( !cv )
continue;
switch ( i )
{
case SP_POSITION_X:
case SP_POSITION_Y:
case SP_ROTATION:
spr->propVals[i] += cv->value;
break;
case SP_SCALE_X:
case SP_SCALE_Y:
case SP_COLOR_R:
case SP_COLOR_G:
case SP_COLOR_B:
case SP_COLOR_A:
spr->propVals[i] *= cv->value;
break;
}
}
}
}
void sprite_bake( qhandle_t h, spriteProp_t prop )
{
sprite_t * s = sprite_get( h );
if( !s )
return;
s->baseVals[ prop ] = s->propVals[ prop ];
}
void sprites_update( void )
{
int i;
for ( i=0; i<sprites.count; i++ )
{
sprite_t * s = sprites.freelist[ i ];
if ( s->flags & SPRITE_DELETEME )
{
sprites.freelist[ i-- ] = sprites.freelist[ --sprites.count ];
sprites.freelist[ sprites.count ] = s;
s->seqNum++;
}
}
}
void sprite_set_base_prop( qhandle_t h, spriteProp_t prop, float val )
{
sprite_t *spr = sprite_get( h );
if( !spr )
return;
spr->baseVals[prop] = val;
}
void sprite_set_base_props( qhandle_t h, float vals[SP_NUM_PROPS] )
{
sprite_t *spr = sprite_get( h );
if( !spr )
return;
Com_Memcpy( spr->baseVals, vals, sizeof( spr->baseVals ) );
}
void sprite_attach_curve( qhandle_t h, spriteProp_t prop, qhandle_t curve )
{
uint i;
sprite_t *spr = sprite_get( h );
if( !spr )
return;
for( i=0; i<lengthof( spr->bindings[prop] ); i++ )
{
if( !curve_get( spr->bindings[ prop ][ i ] ) )
{
spr->bindings[prop][i] = curve;
break;
}
}
}
const float* sprite_get_props( qhandle_t h )
{
sprite_t *spr = sprite_get( h );
if( !spr )
return NULL;
return spr->propVals;
}
qhandle_t sprite_create_rect( rectDef_t * r, qhandle_t shader, vec4_t color, vec4_t uv_coord )
{
sprite_t *ret;
ret = sprite_alloc();
if( !ret )
return 0;
ret->flags = 0;
ret->baseVals[ SP_POSITION_X ] = r->x + r->w*0.5f;
ret->baseVals[ SP_POSITION_Y ] = r->y + r->h*0.5f;
ret->baseVals[ SP_SCALE_X ] = 1.0f;
ret->baseVals[ SP_SCALE_Y ] = 1.0f;
ret->baseVals[ SP_COLOR_R ] = color[ 0 ];
ret->baseVals[ SP_COLOR_G ] = color[ 1 ];
ret->baseVals[ SP_COLOR_B ] = color[ 2 ];
ret->baseVals[ SP_COLOR_A ] = color[ 3 ];
ret->w = r->w;
ret->h = r->h;
ret->uv_coord[ 0 ] = uv_coord[ 0 ];
ret->uv_coord[ 1 ] = uv_coord[ 1 ];
ret->uv_coord[ 2 ] = uv_coord[ 2 ];
ret->uv_coord[ 3 ] = uv_coord[ 3 ];
ret->shader = shader;
return sprite_getId( ret );
}
void sprite_destroy( qhandle_t h )
{
//do any sprite-specific data cleanup
sprite_t * s = sprite_get( h );
if ( s )
s->flags |= SPRITE_DELETEME;
}
void sprite_draw( sprite_t * s, vec3_t m[2] )
{
float w = s->w * s->propVals[SP_SCALE_X];
float h = s->h * s->propVals[SP_SCALE_Y];
float Tx = s->propVals[SP_POSITION_X];
float Ty = s->propVals[SP_POSITION_Y];
float R = DEG2RAD( s->propVals[SP_ROTATION] );
float Rc = cosf( R );
float Rs = sinf( R );
float Sx = DC->glconfig.xscale;
float Sy = DC->glconfig.yscale;
float Bx = DC->glconfig.xbias;
float By = 0; //y bias, if we ever get one
float mat[2][3];
/*
Set the matrix equal to S * M * T * R where:
S is a matrix that adjusts for screen resolution.
M is the incoming effect matrix.
T is the sprite's translation matrix.
R is the sprite's rotation matrix.
Note that the sprite comes in pre-scaled.
*/
mat[0][0] = Sx * (Rc * m[0][0] + Rs * m[0][1]);
mat[0][1] = Sx * (Rc * m[0][1] - Rs * m[0][0]);
mat[0][2] = Sx * (Tx * m[0][0] + Ty * m[0][1] + m[0][2]) + Bx;
mat[1][0] = Sy * (Rc * m[1][0] + Rs * m[1][1]);
mat[1][1] = Sy * (Rc * m[1][1] - Rs * m[1][0]);
mat[1][2] = Sy * (Tx * m[1][0] + Ty * m[1][1] + m[1][2]) + By;
DC->setColor( &s->propVals[ SP_COLOR_R ] );
trap_R_DrawSprite( -w * 0.5F, -h * 0.5F, w, h, mat, s->uv_coord, s->shader );
}
static qhandle_t effect_getId( effect_t * e ) {
return ((e->seqNum&0xFFFF)<<16) | ((e-effects.effects+1)&0xFFFF);
}
static effect_t * effect_get( qhandle_t h ) {
if ( h!=0 && h!=-1 )
{
int index = (h&0xFFFF)-1;
int seqNum = (h>>16)&0xFFFF;
effect_t * e = effects.effects + index;
if ( e->seqNum == seqNum )
return e;
}
return 0;
}
void UI_Effect_Init()
{
int i;
for ( i=0; i<lengthof( curves.freelist ); i++ )
curves.freelist[ i ] = curves.curves + i;
for ( i=0; i<lengthof( sprites.freelist ); i++ )
sprites.freelist[ i ] = sprites.sprites + i;
for ( i=0; i<lengthof( effects.freelist ); i++ )
effects.freelist[ i ] = effects.effects + i;
}
static effect_t * effect_alloc()
{
if ( effects.count >= lengthof( effects.freelist ) )
return 0;
return effects.freelist[ effects.count++ ];
}
void UI_Effect_SetFlag( qhandle_t h, int flags )
{
effect_t *e = effect_get( h );
if ( e )
e->flags |= flags;
}
void UI_Effect_ClearFlag( qhandle_t h, int flags )
{
effect_t * e = effect_get( h );
if( e )
e->flags &= ~flags;
}
int UI_Effect_GetFlags( qhandle_t h )
{
effect_t *e = effect_get( h );
return e ? e->flags : 0;
}
int UI_Effect_GetTouchCount( qhandle_t h )
{
effect_t *e = effect_get( h );
if( !e )
return 0;
return e->touch;
}
int UI_Effect_SetTouchCount( qhandle_t h, int newTouch )
{
int ret;
effect_t *e = effect_get( h );
if( !e )
return 0;
ret = e->touch;
e->touch = newTouch;
return ret;
}
effectDef_t * UI_Effect_Find( const char * name )
{
int lower = 0;
int upper = DC->Assets.effectCount;
int i;
if ( DC->Assets.effectCount == 0 )
return 0;
for ( i=(lower+upper)>>1; ( lower<=upper ); )
{
int c = Q_stricmp( DC->Assets.effects[ i ]->name, name );
if ( c == 0 )
break;
else if ( c > 0 )
upper = i-1;
else
lower = i+1;
i = (lower+upper)>>1;
}
if ( lower <= upper )
return DC->Assets.effects[ i ];
return 0;
}
static void UI_Effect_PrintLine( effect_t * e, const lineInfo_t * line, fontInfo_t * font, float italic,
float x, float y, float fontScale )
{
int i;
float rgba[4];
x += line->ox;
italic = (italic * fontScale) / (float)font->glyphs[ 'A' ].height;
Vec4_Cpy( rgba, line->startColor );
for( i = 0; i < line->count; i++ )
{
if( Q_IsColorString( line->text + i ) )
{
i++;
if( line->text[i] == '-' )
Vec4_Cpy( rgba, line->defaultColor );
else
Vec4_Cpy( rgba, g_color_table[ColorIndex( line->text[i] )] );
continue;
}
else
{
float step = 0.0f;
if( line->text[i] == ' ' )
step = (font->glyphs[' '].xSkip * fontScale) + line->sa;
else
{
glyphInfo_t * glyph = font->glyphs + line->text[i];
rectDef_t r;
vec4_t uv;
if( e->spriteCount >= lengthof( e->sprites ) )
break;
r.x = x;
r.y = y - (float)glyph->top * fontScale;
r.w = glyph->imageWidth * fontScale;
r.h = glyph->imageHeight * fontScale;
uv[ 0 ] = glyph->s;
uv[ 1 ] = glyph->t;
uv[ 2 ] = glyph->s2;
uv[ 3 ] = glyph->t2;
e->sprites[ e->spriteCount++ ] = sprite_create_rect( &r, glyph->glyph, rgba, uv );
step = glyph->xSkip * fontScale;
}
x += step;
}
}
}
static void UI_Effect_Print2( effect_t * e, fontInfo_t * font, const char * text, float x, float y )
{
int i;
float fontScale = e->def->textscale * font->glyphScale;
float oy = 0.0f;
float h;
float lineHeight = font->glyphs[ 'A' ].height * fontScale;
float lineSpace;
float italic;
lineInfo_t lines[ 16 ];
int lineCount;
int lim = e->def->maxChars ? e->def->maxChars : -1;
lineCount = trap_R_FlowText( font, fontScale, e->def->forecolor, e->def->forecolor, text, lim, e->def->textalignx, e->rect.w - x, lines, lengthof(lines) );
lineSpace = lineHeight * 0.4f;
h = lineCount * lineHeight + ((lineCount-1)*lineSpace);
// vertically justify text
switch ( e->def->textaligny )
{
case TEXT_ALIGN_CENTER: oy = (e->rect.h - h)*0.5f; break; // middle
case TEXT_ALIGN_RIGHT: oy = (e->rect.h - h); break; // bottom
case TEXT_ALIGN_JUSTIFY: lineHeight += (e->rect.h - h) / (float)lineCount; break;
}
italic = (e->def->textStyle&ITEM_TEXTSTYLE_ITALIC)?4.0f:0.0f;
for( i = 0; i < lineCount; i++ )
{
float ly = y + oy + (lineHeight * (i+1)) + (lineSpace * i);
UI_Effect_PrintLine( e, lines + i, font, italic, x, ly, fontScale );
}
}
static void UI_Effect_Print( effect_t * e, const char * text, float x, float y ) {
fontInfo_t f;
trap_R_GetFont( e->def->font, e->def->textscale, &f );
UI_Effect_Print2( e, &f, text, x, y );
}
static void AttachCurveToSprite( effect_t * e, int sprite, spriteProp_t prop, qhandle_t curve )
{
if ( sprite == -1 )
{
int i;
for ( i=0; i<e->spriteCount; i++ )
sprite_attach_curve( e->sprites[ i ], prop, curve );
} else
sprite_attach_curve( e->sprites[ sprite ], prop, curve );
}
static void SetSpriteProp( effect_t * e, int sprite, spriteProp_t prop, float value )
{
if ( sprite == -1 )
{
int i;
for ( i=0; i<e->spriteCount; i++ )
sprite_set_base_prop( e->sprites[ i ], prop, value );
} else
sprite_set_base_prop( e->sprites[ sprite ], prop, value );
}
static void BakeSprite( effect_t * e, int sprite, spriteProp_t prop )
{
if ( sprite == -1 )
{
int i;
for ( i=0; i<e->spriteCount; i++ )
sprite_bake( e->sprites[ i ], prop );
} else
sprite_bake( e->sprites[ sprite ], prop );
}
static void effect_eval( effect_t *e )
{
int i;
for( i = 0; i < EP_NUM_PROPS; i++ )
{
int j;
e->propVals[i] = e->baseVals[i];
for( j = 0; j < lengthof( e->props[i] ); j++ )
{
animCurve_t *cv = curve_get( e->props[i][j] );
if( !cv )
continue;
switch( i )
{
case EP_SCALE_X:
case EP_SCALE_Y:
e->propVals[i] *= curve_get_value( e->props[i][j] );
break;
default:
e->propVals[i] += curve_get_value( e->props[i][j] );
break;
}
}
}
}
static void effect_attach_curve( effect_t *e, effectProp_t prop, qhandle_t curve )
{
int i;
for( i = 0; i < lengthof( e->props[prop] ); i++ )
{
if( !curve_get( e->props[prop][i] ) )
{
e->props[prop][i] = curve;
break;
}
}
}
typedef struct duration_t
{
float time;
qboolean hold;
} duration_t;
static duration_t ParseDuration( const char **s )
{
duration_t ret;
const char *tmp;
ret.time = fatof( COM_ParseExt( s, qfalse ) );
tmp = *s;
if( Q_stricmp( COM_ParseExt( &tmp, qfalse ), "hold" ) == 0 )
{
ret.hold = qtrue;
*s = tmp;
}
else
ret.hold = qfalse;
return ret;
}
static float ParseAngle( const char **s )
{
char *tok = COM_ParseExt( s, qfalse );
if( Q_stricmp( tok, "rand" ) == 0 )
{
return Q_random() * M_PI * 2;
}
return DEG2RAD( fatof( tok ) );
}
/*
Isct_RayLineComp
Find the intersection between a ray originating at o,
with a vector v, and a perpendicular line that crosses l.
*/
qboolean Isct_RayLineComp( float o, float d, float l, float *isctT )
{
float t;
if( fabsf( d ) < 0.001F )
{
if( o == l )
{
if( isctT ) *isctT = 0;
return qtrue;
}
return qfalse;
}
t = (l - o) / d;
if( isctT ) *isctT = t;
return t >= 0 ? qtrue : qfalse;
}
qboolean Isct_RayLeavingBox( const vec2_t ro, const vec2_t rd, const rectDef_t *rc, float *isctT )
{
float tmin = 0;
//ASSUME: that the ray originates inside the rectangle.
//ASSUME: that rd != < 0, 0 >
if( fabsf( rd[0] ) > 0.001F )
{
if( rd[0] < 0 )
{
//only check the left side
if( !Isct_RayLineComp( ro[0], rd[0], rc->x, &tmin ) )
return qfalse;
}
else
{
//only the right
if( !Isct_RayLineComp( ro[0], rd[0], rc->x + rc->w, &tmin ) )
return qfalse;
}
}
if( fabsf( rd[1] ) > 0.001F )
{
float t;
if( rd[1] < 0 )
{
//only check the top side
if( !Isct_RayLineComp( ro[1], rd[1], rc->y, &t ) )
return qfalse;
}
else
{
//only the right
if( !Isct_RayLineComp( ro[1], rd[1], rc->y + rc->h, &t ) )
return qfalse;
}
if( t < tmin ) tmin = t;
}
if( isctT ) *isctT = tmin;
return qtrue;
}
qboolean Rect_PlaceOutsideOfRect( rectDef_t *rc, const rectDef_t *ro, const vec2_t v, float *isctT )
{
float t;
vec2_t o;
//first find the spot on rc that's opposite the center along -v
o[0] = rc->x + rc->w * 0.5F;
o[1] = rc->y + rc->h * 0.5F;
//use v instead of -v (just negate t after), works since ray origin = center of rect
if( !Isct_RayLeavingBox( o, v, rc, &t ) )
return qfalse;
o[0] += v[0] * -t;
o[1] += v[1] * -t;
//now find out how far along we have to go to get this point outside the outer rect
if( !Isct_RayLeavingBox( o, v, ro, &t ) )
{
//the rect is already outside
if( isctT ) *isctT = 0;
return qtrue;
}
if( isctT ) *isctT = t;
//move the rectangle
rc->x += v[0] * t;
rc->y += v[1] * t;
return qtrue;
}
static void AttachCurveToSpriteCB( effect_t *e, stackFrame_t *sf, int prop, qhandle_t curve )
{
AttachCurveToSprite( e, sf->i, prop, curve );
}
static void AttachCurveToEffectCB( effect_t *e, stackFrame_t *sf, int prop, qhandle_t curve )
{
REF_PARAM( sf );
effect_attach_curve( e, prop, curve );
}
#define EnsureCurveCount( newCount, stmt ) \
if( e->curveCount + (newCount) >= lengthof( e->curves ) ) \
stmt; \
else \
(void)0
#define EnsureCurveCount_return( newCount ) EnsureCurveCount( newCount, return )
#define EnsureCurveCount_break( newCount ) EnsureCurveCount( newCount, break )
typedef void (*AttachCurves_t)( effect_t *e, stackFrame_t *sf, int prop, qhandle_t curve );
static void AnimEffect_SlideDirected( AttachCurves_t attach, effect_t *e, stackFrame_t *sf, qboolean in )
{
vec2_t v;
rectDef_t ro, ri;
qhandle_t c;
float angle = ParseAngle( &sf->pc );
duration_t dur = ParseDuration( &sf->pc );
EnsureCurveCount_return( 2 );
ro.x = -(DC->glconfig.xbias / DC->glconfig.xscale);
ro.w = 640 + (DC->glconfig.xbias / DC->glconfig.xscale) * 2.0F;
ro.y = 0;
ro.h = 480;
v[0] = cosf( angle );
v[1] = -sinf( angle );
ri = e->rect;
if( !Rect_PlaceOutsideOfRect( &ri, &ro, v, NULL ) )
//degenerate rectangle...
return;
if( in )
{
c = curve_create_lerp( DC->realTime, ri.x - e->rect.x, 0.0F, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
attach( e, sf, CP_POSITION_X, c );
}
c = curve_create_lerp( DC->realTime, ri.y - e->rect.y, 0.0F, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
attach( e, sf, CP_POSITION_Y, c );
}
}
else
{
c = curve_create_lerp( DC->realTime, 0.0F, ri.x - e->rect.x, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
attach( e, sf, CP_POSITION_X, c );
}
c = curve_create_lerp( DC->realTime, 0.0F, ri.y - e->rect.y, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
attach( e, sf, CP_POSITION_Y, c );
}
}
}
static void effect_exec( effect_t * e )
{
stackFrame_t * sf;
if( e->def->flags & ED_DIEWITHMENU )
{
if ( e->item && !(((menuDef_t*)e->item->parent)->window.flags & WINDOW_VISIBLE) )
{
e->flags |= EF_DELETEME;
return;
}
}
if ( e->flags & EF_NORECT )
return;
if ( e->sleeping > 0 )
{
e->sleeping -= DC->frameTime;
if ( e->sleeping > 0 )
return;
}
if ( e->waitfor )
{
e->waitfor &= ~e->flags;
if ( e->waitfor )
return;
}
sf = &e->stack[ e->sp - 1 ];
for ( ;; )
{
char * t = COM_ParseExt( &sf->pc, qtrue );
if ( t[ 0 ] == '\0' )
break;
switch( SWITCHSTRING( t ) )
{
// for
case CS('f','o','r',0):
{
const char * pc;
int start = 0;
// eat '<'
pc = COM_ParseExt( &sf->pc, qfalse );
if ( pc[0] != '<' )
{
start = atoi( pc );
COM_ParseExt( &sf->pc, qfalse );
}
pc = sf->pc;
sf++;
sf->i = start;
sf->pc = pc;
}
break;
// > - end of loop
case CS('>',0,0,0):
{
sf->i++;
if ( sf->i < e->spriteCount )
sf->pc = sf[ -1 ].pc;
else
{
const char * pc = sf->pc;
sf--;
sf->pc = pc;
}
}
break;
// wait
case CS('w','a','i','t'):
{
char * t = COM_ParseExt( &sf->pc, qfalse );
switch ( SWITCHSTRING( t ) )
{
//curves
case CS('c','u','r','v'):
e->waitfor |= EF_NOANIM;
break;
//nofocus
case CS('n','o','f','o'):
e->waitfor |= EF_NOFOCUS;
break;
//forever
case CS('f','o','r','e'):
e->waitfor |= EF_FOREVER;
break;
default:
e->sleeping = atoi( t );
}
e->sp = sf - e->stack + 1;
return;
}
break;
// die_if_nofocus
case CS('d','i','e','_'):
{
if ( e->flags & EF_NOFOCUS )
e->flags |= EF_DELETEME;
}
break;
// offset_from_cursor
case CS('o','f','f','s'):
{
float xb = DC->glconfig.xbias / DC->glconfig.xscale;
e->rect.x = e->def->rect.x + (float)DC->cursorx;
e->rect.y = e->def->rect.y + (float)DC->cursory;
e->rect.w = e->def->rect.w;
e->rect.h = e->def->rect.h;
// don't let rect go off screen
if ( e->rect.x < -xb ) e->rect.x = -xb;
if ( e->rect.y < 0.0f ) e->rect.y = 0.0f;
if ( e->rect.x + e->rect.w > 640.0f + xb ) e->rect.x = 640.0f + xb - e->rect.w;
if ( e->rect.y + e->rect.h > 480.0f ) e->rect.y = 480.0f - e->rect.h;
} break;
// sprite
case CS('s','p','r','i'):
{
vec4_t uv = { 0.0f, 0.0f, 1.0f, 1.0f };
rectDef_t r;
if( e->spriteCount >= lengthof( e->sprites ) )
break;
r.x = 0.0f;
r.y = 0.0f;
r.w = e->rect.w;
r.h = e->rect.h;
e->sprites[ e->spriteCount++ ] = sprite_create_rect( &r, (e->def->shader)?e->def->shader:e->shader, e->def->backcolor, uv );
}
break;
case CS('d','r','a','w'):
{
vec4_t uv = { 0.0f, 0.0f, 1.0f, 1.0f };
rectDef_t r;
qhandle_t s = (e->def->shader)?e->def->shader:e->shader;
r.x = fatof( COM_ParseExt( &sf->pc, qfalse ) );
r.y = fatof( COM_ParseExt( &sf->pc, qfalse ) );
r.w = fatof( COM_ParseExt( &sf->pc, qfalse ) );
r.h = fatof( COM_ParseExt( &sf->pc, qfalse ) );
if( e->spriteCount >= lengthof( e->sprites ) )
break;
if ( s )
e->sprites[ e->spriteCount++ ] = sprite_create_rect( &r, s, e->def->backcolor, uv );
} break;
// print
case CS('p','r','i','n'):
{
float x = fatof( COM_ParseExt( &sf->pc, qfalse ) );
float y = fatof( COM_ParseExt( &sf->pc, qfalse ) );
UI_Effect_Print( e, e->text, x,y );
}
break;
case CS('s','e','t',0):
{
switch( SWITCHSTRING( COM_ParseExt( &sf->pc, qfalse ) ) )
{
case CS('a',0,0,0):
SetSpriteProp( e, sf->i, SP_COLOR_A, fatof( COM_ParseExt( &sf->pc, qfalse ) ) );
break;
case CS( 'r', 'o', 't', 0 ):
SetSpriteProp( e, sf->i, SP_ROTATION, fatof( COM_ParseExt( &sf->pc, qfalse ) ) );
break;
}
}
break;
case CS('b','a','k','e'):
{
switch( SWITCHSTRING( COM_ParseExt( &sf->pc, qfalse ) ) )
{
case CS('s','c','a','l'):
BakeSprite( e, sf->i, SP_SCALE_X );
BakeSprite( e, sf->i, SP_SCALE_Y );
break;
}
}
break;
/*
Animation launchers.
The commands that actuall attach curves and kick off animations go here:
*/
// move
case CS('m','o','v','e'):
{
qhandle_t c;
float x = fatof( COM_ParseExt( &sf->pc, qfalse ) );
float y = fatof( COM_ParseExt( &sf->pc, qfalse ) );
EnsureCurveCount_break( 2 );
c = curve_create_constant( DC->realTime, x, 0.0f );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_POSITION_X, c );
}
c = curve_create_constant( DC->realTime, y, 0.0f );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_POSITION_Y, c );
}
}
break;
// fade
case CS('f','a','d','e'):
{
switch( SWITCHSTRING( COM_ParseExt( &sf->pc, qfalse ) ) )
{
// out
case CS('o','u','t',0):
{
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, 1.0f, 0.0f, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_COLOR_A, c );
}
}
break;
// in
case CS('i','n',0,0):
{
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, 0.0f, 1.0f, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_COLOR_A, c );
}
}
break;
}
}
break;
// slide
case CS('s','l','i','d'):
{
switch( SWITCHSTRING( COM_ParseExt( &sf->pc, qfalse ) ) )
{
// in_from_right
case CS('i','n','_','f'):
{
float x = 640.0f + (DC->glconfig.xbias / DC->glconfig.xscale)*2.0f;
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, x - e->rect.x, 0.0f, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_POSITION_X, c );
}
}
break;
// up
case CS('u','p',0,0):
{
float distance = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, 0.0f, -distance, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_POSITION_Y, c );
}
}
break;
//efx_slide down
case CS('d','o','w','n'):
{
float distance = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, 0.0f, distance, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_POSITION_Y, c );
}
}
break;
case CS( 'f', 'r', 'o', 'm' ):
AnimEffect_SlideDirected( AttachCurveToSpriteCB, e, sf, qtrue );
break;
case CS( 't', 'o', 0, 0 ):
AnimEffect_SlideDirected( AttachCurveToSpriteCB, e, sf, qfalse );
break;
}
}
break;
// scale
case CS('s','c','a','l'):
{
int cmd = SWITCHSTRING( COM_ParseExt( &sf->pc, qfalse ) );
float start = fatof( COM_ParseExt( &sf->pc, qfalse ) );
float end = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, start, end, dur.time, dur.hold );
if( !c )
break;
e->curves[ e->curveCount++ ] = c;
switch( cmd )
{
case CS('x',0,0,0):
AttachCurveToSprite( e, sf->i, SP_SCALE_X, c );
break;
case CS('y',0,0,0):
AttachCurveToSprite( e, sf->i, SP_SCALE_Y, c );
break;
case CS('x','y',0,0):
AttachCurveToSprite( e, sf->i, SP_SCALE_X, c );
AttachCurveToSprite( e, sf->i, SP_SCALE_Y, c );
break;
}
}
break;
case CS( 'r', 'o', 't', 'a' ):
{
float start = fatof( COM_ParseExt( &sf->pc, qfalse ) );
float end = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, start, end, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
AttachCurveToSprite( e, sf->i, SP_ROTATION, c );
}
}
break;
/*
Effect animation launchers.
Same as above only with the efx_ prefix. These apply to the entire effect.
*/
case CS( 'e', 'f', 'x', '_' ):
switch( SWITCHSTRING( t + 4 ) )
{
//efx_move
case CS('m','o','v','e'):
{
qhandle_t c;
float x = fatof( COM_ParseExt( &sf->pc, qfalse ) );
float y = fatof( COM_ParseExt( &sf->pc, qfalse ) );
EnsureCurveCount_break( 2 );
c = curve_create_constant( DC->realTime, x, 0.0f );
if( c )
{
e->curves[e->curveCount++] = c;
effect_attach_curve( e, EP_POSITION_X, c );
}
c = curve_create_constant( DC->realTime, y, 0.0f );
if( c )
{
e->curves[e->curveCount++] = c;
effect_attach_curve( e, EP_POSITION_Y, c );
}
}
break;
//efx_slide
case CS('s','l','i','d'):
{
switch( SWITCHSTRING( COM_ParseExt( &sf->pc, qfalse ) ) )
{
//efx_slide in_from_right
case CS('i','n','_','f'):
{
float x = 640.0f + (DC->glconfig.xbias / DC->glconfig.xscale) * 2.0F;
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, x - e->rect.x, 0.0F, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
effect_attach_curve( e, EP_POSITION_X, c );
}
}
break;
//efx_slide up
case CS('u','p',0,0):
{
float distance = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, 0.0f, -distance, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
effect_attach_curve( e, EP_POSITION_Y, c );
}
}
break;
//efx_slide down
case CS('d','o','w','n'):
{
float distance = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, 0.0f, distance, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
effect_attach_curve( e, EP_POSITION_Y, c );
}
}
break;
case CS( 'f', 'r', 'o', 'm' ):
AnimEffect_SlideDirected( AttachCurveToEffectCB, e, sf, qtrue );
break;
case CS( 't', 'o', 0, 0 ):
AnimEffect_SlideDirected( AttachCurveToEffectCB, e, sf, qfalse );
break;
}
} break;
//efx_scale
case CS('s','c','a','l'):
{
int cmd = SWITCHSTRING( COM_ParseExt( &sf->pc, qfalse ) );
float start = fatof( COM_ParseExt( &sf->pc, qfalse ) );
float end = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, start, end, dur.time, dur.hold );
if( !c )
break;
e->curves[ e->curveCount++ ] = c;
switch( cmd )
{
case CS('x',0,0,0):
effect_attach_curve( e, EP_SCALE_X, c );
break;
case CS('y',0,0,0):
effect_attach_curve( e, EP_SCALE_Y, c );
break;
case CS('x','y',0,0):
effect_attach_curve( e, EP_SCALE_X, c );
effect_attach_curve( e, EP_SCALE_Y, c );
break;
}
}
case CS( 'r', 'o', 't', 'a' ):
{
float start = fatof( COM_ParseExt( &sf->pc, qfalse ) );
float end = fatof( COM_ParseExt( &sf->pc, qfalse ) );
duration_t dur = ParseDuration( &sf->pc );
qhandle_t c;
EnsureCurveCount_break( 1 );
c = curve_create_lerp( DC->realTime, start, end, dur.time, dur.hold );
if( c )
{
e->curves[ e->curveCount++ ] = c;
effect_attach_curve( e, EP_ROTATION, c );
}
}
break;
}
break;
}
}
sf--;
e->sp = sf - e->stack + 1;
}
static void effect_setrect( effect_t * e, rectDef_t *r )
{
e->rect = *r;
}
static void effect_buildmatrix( effect_t * e, vec3_t m[2] )
{
float Sx, Sy, R, Tx, Ty;
float Rs, Rc;
float Cx, Cy;
/*
Scale, rotate, translate.
Build a matrix that:
- scales by e->props[EP_SCALE_X/Y] from the center of e->rect
- rotates by e->props[EP_ROTATION] degress around the center of e->rect
- translates by e->props[EP_POSITION_X/Y]
*/
Sx = e->propVals[EP_SCALE_X];
Sy = e->propVals[EP_SCALE_Y];
R = DEG2RAD( e->propVals[EP_ROTATION] );
Rs = sinf( R );
Rc = cosf( R );
Tx = e->propVals[EP_POSITION_X] + e->rect.x;
Ty = e->propVals[EP_POSITION_Y] + e->rect.y;
Cx = -e->rect.w * 0.5F;
Cy = -e->rect.h * 0.5F;
/*
This nasty beast is the matrix M = T * Tc-1 * R * S * Tc where:
T is the required translation
Tc is a translation that centers e->rect on the origin
R is the required rotation
S is the required scale
*/
m[0][0] = Rc * Sx; m[0][1] = -Rs * Sy; m[0][2] = Rc * Sx * Cx - Rs * Sy * Cy + Tx - Cx;
m[1][0] = Rs * Sx; m[1][1] = Rc * Sy; m[1][2] = Rs * Sx * Cx + Rc * Cy * Sy + Ty - Cy;
}
void UI_Effect_SetJustRect( qhandle_t h, rectDef_t * r )
{
effect_t * e = effect_get( h );
if ( e )
{
effect_setrect( e, r );
e->flags &= ~EF_NORECT;
}
}
void UI_Effect_SetRect( qhandle_t h, rectDef_t * r, itemDef_t * item )
{
effect_t * e = effect_get( h );
if ( e )
{
effect_setrect( e, r );
e->item = item;
e->flags &= ~EF_NORECT;
}
}
void UI_Effect_SetItem( qhandle_t h, itemDef_t * item )
{
effect_t * e = effect_get( h );
if ( e )
e->item = item;
}
static effect_t* effect_create( effectDef_t *effect )
{
effect_t * e = effect_alloc();
short seqNum;
if( !e )
return NULL;
seqNum = e->seqNum;
Com_Memset( e, 0, sizeof(*e) );
e->seqNum = seqNum;
e->def = effect;
e->baseVals[EP_SCALE_X] = 1;
e->baseVals[EP_SCALE_Y] = 1;
return e;
}
qhandle_t UI_Effect_SpawnText( rectDef_t * r, effectDef_t * effect, const char * text, qhandle_t shader )
{
if ( effect )
{
effect_t * e = effect_create( effect );
if( !e )
return 0;
if ( r )
effect_setrect( e, r );
else
e->flags |= EF_NORECT;
if ( text )
Q_strncpyz( e->text, text, sizeof(e->text) );
e->shader = shader;
e->stack[ 0 ].pc = (char*)e->def->action;
e->stack[ 0 ].i = -1;
e->sp = 1;
effect_exec( e );
return effect_getId( e );
}
return 0;
}
qboolean UI_Effect_IsAlive( qhandle_t h )
{
return effect_get(h) != 0;
}
effectDef_t* UI_Effect_GetEffect( qhandle_t h )
{
effect_t *e = effect_get( h );
if( !e )
return NULL;
return e->def;
}
void UI_Effects_Update()
{
curves_update();
sprites_update();
}
extern int trap_Key_GetCatcher( void );
void UI_Effects_Draw( menuDef_t * menu )
{
int i,j;
int ui_active = trap_Key_GetCatcher() & KEYCATCH_UI;
for ( i=0; i<effects.count; i++ )
{
vec3_t m[ 2 ];
effect_t * e = effects.freelist[ i ];
if( e->def->flags & ED_DIEWITHMENU )
{
if( e->item && !(((menuDef_t*)e->item->parent)->window.flags & WINDOW_VISIBLE) )
e->flags |= EF_DELETEME;
}
// delete effect
if( e->sp == 0 || e->flags & EF_DELETEME )
{
for ( j=0; j<e->curveCount; j++ )
curve_destroy( e->curves[ j ] );
for ( j=0; j<e->spriteCount; j++ )
sprite_destroy( e->sprites[ j ] );
effects.freelist[ i-- ] = effects.freelist[ --effects.count ];
effects.freelist[ effects.count ] = e;
e->seqNum++;
continue;
}
if ( (e->def->flags & ED_NODRAW_INUI) && ui_active )
continue;
if( menu )
{
//only want items attached to menu, excluding ALWAYSONTOP
if( !e->item || e->item->parent != menu || e->def->flags & ED_ALWAYSONTOP )
continue; // do not draw if not attached to the current menu, or always on top
}
else
{
//want unattached items except where always on top
if( e->item ) {
if ( !(e->def->flags & ED_ALWAYSONTOP) )
continue; // do not draw if attached to a menu and not on top
if ( !(((menuDef_t*)e->item->parent)->window.flags & WINDOW_VISIBLE) )
continue; // do not draw if associated menu is not drawing
}
}
if ( e->sp > 0 )
effect_exec( e );
if( e->sp == 0 || e->flags & EF_DELETEME )
//effect just died, skip (will get eaten on next frame)
continue;
e->flags |= EF_NOANIM; // flag as nothing animating
for ( j=0; j<e->curveCount; j++ )
{
animCurve_t * c = curve_get( e->curves[ j ] );
if ( !c )
continue;
if ( !c->flags&CURVE_DONE )
{
if ( c->type != CT_CONSTANT )
e->flags &= ~EF_NOANIM; // something is animating
curve_eval( c, DC->realTime );
continue;
}
// don't delete curves set to hold
if ( c->type == CT_LERP && c->data.lerp.hold )
continue;
c->flags |= CURVE_DELETEME;
e->curves[ j-- ] = e->curves[ --e->curveCount ];
}
effect_eval( e );
effect_buildmatrix( e, m );
for ( j=0; j<e->spriteCount; j++ )
{
if ( !(e->flags & EF_NORECT) ) {
sprite_t * s = sprite_get( e->sprites[ j ] );
if ( s ) {
sprite_eval( s );
sprite_draw( s, m );
}
}
}
switch( e->flags & EF_ONEFRAME_BITS )
{
case EF_ONEFRAME_RECT:
e->flags |= EF_NORECT;
break;
case EF_ONEFRAME_NOFOCUS:
e->flags |= EF_NOFOCUS;
break;
}
if( e->touch )
e->touch--;
}
}