D:\cygwin\wf_fusion_src_r8\CVSROOT\cgame\cg_view.c

/*
Copyright (C) 2002-2003 Victor Luchits

This program 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.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "cg_local.h"

// cg_view.c -- player rendering positioning

//=============

int         r_numDlights;
dlight_t    r_dlights[MAX_DLIGHTS];

int         r_numEntities;
entity_t    r_entities[MAX_ENTITIES];

int         r_numPolys;
poly_t      r_polys[MAX_POLYS];

/*
====================
CG_ClearScene
====================
*/
void CG_ClearScene (void)
{
    r_numDlights = 0;
    r_numEntities = 0;
    r_numPolys = 0;
}


/*
=====================
CG_AddEntity
=====================
*/
void CG_AddEntity ( entity_t *ent )
{
    if ( r_numEntities < MAX_ENTITIES ) {
        r_entities[r_numEntities++] = *ent;
    }
}

/*
=====================
CG_AddLight
=====================
*/
void CG_AddLight ( vec3_t org, float intensity, float r, float g, float b )
{
    dlight_t    *dl;

    if ( r_numDlights < MAX_DLIGHTS ) {
        dl = &r_dlights[r_numDlights++];
        VectorCopy (org, dl->origin);
        dl->intensity = intensity;
        dl->color[0] = r;
        dl->color[1] = g;
        dl->color[2] = b;
    }
}

/*
=====================
CG_AddPoly
=====================
*/
void CG_AddPoly ( poly_t *poly )
{
    if ( r_numPolys < MAX_POLYS ) {
        r_polys[r_numPolys++] = *poly;
    }
}

/*
================
CG_TestEntities

If cg_testEntities is set, create 32 player models
================
*/
void CG_TestEntities (void)
{
    int         i, j;
    float       f, r;
    entity_t    *ent;

    r_numEntities = 32;
    memset ( r_entities, 0, sizeof(r_entities) );

    ent = r_entities;
    for ( i = 0; i < r_numEntities; i++, ent++ )
    {
        r = 64 * ( (i%4) - 1.5 );
        f = 64 * (i/4) + 128;

        for ( j = 0; j < 3; j++ )
            ent->origin[j] = cg.refdef.vieworg[j] + cg.v_forward[j]*f + cg.v_right[j]*r;

        //splitmodels 
        ent->model = cgs.baseClientInfo.pmodel.pmodelinfo->model[UPPER];
        if (cgs.baseClientInfo.pmodel.customSkin[UPPER])
            ent->customSkin = cgs.baseClientInfo.pmodel.customSkin[UPPER];
        else
            ent->customSkin = NULL;
    }
}

/*
================
CG_TestLights

If cg_testLights is set, create 32 lights models
================
*/
void CG_TestLights (void)
{
    int         i, j;
    float       f, r;
    dlight_t    *dl;

    r_numDlights = 32;
    memset (r_dlights, 0, sizeof(r_dlights));

    dl = r_dlights;
    for ( i = 0; i < r_numDlights; i++, dl++ )
    {
        r = 64 * ((i%4) - 1.5);
        f = 64 * (i/4) + 128;

        for ( j = 0; j < 3; j++ )
            dl->origin[j] = cg.refdef.vieworg[j] + cg.v_forward[j]*f + cg.v_right[j]*r;

        dl->color[0] = ((i%6)+1) & 1;
        dl->color[1] = (((i%6)+1) & 2)>>1;
        dl->color[2] = (((i%6)+1) & 4)>>2;
        dl->intensity = 200;
    }
}

void CG_SpawnBlend (void)
{
    if (cg.isactive)
        return;

    cg.refdef.blend[0] = -1;
    cg.refdef.blend[1] = -1;
    cg.refdef.blend[2] = -1;
    cg.refdef.blend[3] = 1 - cg.blend ;

    cg.blend = cg.blend + .02*cg.normalfps;
    if (cg.blend >= 1)
        cg.isactive = qtrue;

}
/*
================
CG_TestBlend

If cg_testBlend is set, create a debug blend
================
*/
void CG_TestBlend (void)
{
    cg.refdef.blend[0] = 1;
    cg.refdef.blend[1] = 0.5;
    cg.refdef.blend[2] = 0.25;
    cg.refdef.blend[3] = 0.5;
}


vec3_t Gas[9] =
{
    {0.4 ,0.7 ,0.1 },
    {0.2 ,0.1 ,0.75},
    {0.7 ,0.9 ,0.2 },
    {0.2 ,0.3 ,0.4 },
    {0.1 ,0.2 ,0.54},
    {0.7 ,0.3 ,0.68},
    {0.8 ,0.6 ,0.2 },
    {0.4 ,0.6 ,0.3 },
    {0.34,0.7 ,0.94}
};


void CG_GasView (void)
{
    short number;
    int i;

    number=(int)(random()*10)%9;

    if (cg.GasTime < cg.time)
    {
        for (i=0; i<3; i++)
        {
            cg.Gas[i][0]=Gas[i][number];
            cg.GasViewOffset[i][0]=(float)((int)(random()*1000)%400-200)/10;
            cg.GasTime = cg.time + 400 + (random()*100);
        }

        cg.GasFOV[0]=(int)(random()*1000)%100+41;
    }

    for (i=0; i<3; i++)
    {
        if (cg.Gas[i][0] > cg.Gas[i][1]) 
            cg.Gas[i][1] +=.01;
        else if (cg.Gas[i][0] < cg.Gas[i][1])
            cg.Gas[i][1] -=.01;

        if (cg.GasViewOffset[i][0] > cg.GasViewOffset[i][1])
        {
            cg.GasViewOffset[i][1] +=((cg.GasViewOffset[i][0] - cg.GasViewOffset[i][1])/8)*cg.normalfps;
        }
        if (cg.GasViewOffset[i][0] < cg.GasViewOffset[i][1])
            cg.GasViewOffset[i][1] -=((cg.GasViewOffset[i][1]-cg.GasViewOffset[i][0])/8)*cg.normalfps;
    }

    for (i=0; i<3; i++)
    {
        cg.refdef.blend[i] = cg.Gas[i][1];
        cg.refdef.viewangles[i] += cg.GasViewOffset[i][1];
    }

    cg.refdef.blend[3] = .65;
    
    if (cg.GasFOV[0] > cg.GasFOV[1])
        cg.GasFOV[1] +=1*cg.normalfps;
    else
        cg.GasFOV[1] -=1*cg.normalfps;

    if (cg.GasFOV[1]< 1)
        cg.GasFOV[1] =1;
    if (cg.GasFOV[1]>180)
        cg.GasFOV[1]=180;

    cg.refdef.fov_x = cg.GasFOV[1];
}

void CG_ShockView (void)
{
    float p=0;
    if (cg.shocktime > cg.time)
    {
        p = (cg.shocktime-cg.time);
        p /=250;
        cg.refdef.viewangles[PITCH] -=p*crandom();
        cg.refdef.viewangles[YAW] -=p*crandom();
        cg.refdef.viewangles[ROLL] -=p*crandom();
        p *=crandom();
    }
    
    cg.refdef.vieworg[0] += (1.0/16) + p;
    cg.refdef.vieworg[1] += (1.0/16) + p;
    cg.refdef.vieworg[2] += (1.0/16) + p;

    cg.refdef.x = scr_vrect.x;
    cg.refdef.y = scr_vrect.y;
    
//  Com_Printf("Debug: delta %f pitch:%f\n",p,cg.refdef.viewangles[PITCH]);
}

//===================================================================

/*
================
CG_ThirdPerson_CameraUpdate
================
*/
void CG_ThirdPerson_CameraUpdate (void)
{
    float   dist, f, r;
    vec3_t  dest, stop;
    vec3_t  chase_dest;
    trace_t trace;
    static vec3_t mins = { -4, -4, -4 };
    static vec3_t maxs = { 4, 4, 4 };

    // calc exact destination
    VectorCopy ( cg.refdef.vieworg, chase_dest );
    r = DEG2RAD( cg_thirdPersonAngle->value );
    f = -cos( r );
    r = -sin( r );
    VectorMA( chase_dest, cg_thirdPersonRange->value * f, cg.v_forward, chase_dest );
    VectorMA( chase_dest, cg_thirdPersonRange->value * r, cg.v_right, chase_dest );
    chase_dest[2] += 8;

    // find the spot the player is looking at
    VectorMA ( cg.refdef.vieworg, 512, cg.v_forward, dest );
    CG_Trace ( &trace, cg.refdef.vieworg, mins, maxs, dest, cgs.playerNum+1, MASK_SOLID );

    // calculate pitch to look at the same spot from camera
    VectorSubtract ( trace.endpos, cg.refdef.vieworg, stop );
    dist = sqrt ( stop[0] * stop[0] + stop[1] * stop[1] );
    if (dist < 1)
        dist = 1;
    cg.refdef.viewangles[PITCH] = RAD2DEG( -atan2(stop[2], dist) );
    cg.refdef.viewangles[YAW] -= cg_thirdPersonAngle->value;
    AngleVectors ( cg.refdef.viewangles, cg.v_forward, cg.v_right, cg.v_up );

    // move towards destination
    CG_Trace ( &trace, cg.refdef.vieworg, mins, maxs, chase_dest, cgs.playerNum+1, MASK_SOLID );

    if ( trace.fraction != 1.0 ) {
        VectorCopy ( trace.endpos, stop );
        stop[2] += ( 1.0 - trace.fraction ) * 32;
        CG_Trace ( &trace, cg.refdef.vieworg, mins, maxs, stop, cgs.playerNum+1, MASK_SOLID );
        VectorCopy ( trace.endpos, chase_dest );
    }

    VectorCopy ( chase_dest, cg.refdef.vieworg );
}

//============================================================================

/*
==================
CG_RenderFlags
==================
*/
int CG_RenderFlags (void)
{
    int rdflags, contents;

    rdflags = 0;

    contents = CG_PointContents ( cg.refdef.vieworg );
    if ( contents & MASK_WATER )
        rdflags |= RDF_UNDERWATER;
    else
        rdflags &= ~RDF_UNDERWATER;

    return rdflags;
}

//============================================================================

/*
==================
CG_RenderView

==================
*/
#define WAVE_AMPLITUDE  0.015   // [0..1]
#define WAVE_FREQUENCY  0.6     // [0..1]

void CG_RenderView ( float frameTime, int realTime, float stereo_separation, qboolean forceRefresh )
    {
    if ( !cg.frame.valid ) {
        SCR_DrawLoading ();
        return;
    }

    // do 3D refresh drawing, and then update the screen
    SCR_CalcVrect ();

    // clear any dirty part of the background
    SCR_TileClear ();

    // update time
    cg.realTime = realTime;
    cg.frameTime = frameTime;
    cg.frameCount++;
    cg.time += frameTime * 1000;

    // clamp time 
    clamp ( cg.time, cg.frame.serverTime - 100, cg.frame.serverTime );

    if ( cg.frameTime > (1.0 / 5.0) ) {
        cg.frameTime = (1.0 / 5.0);
    }

    // predict all unacknowledged movements
    CG_PredictMovement ();

    CG_FixUpGender ();

    if ( !cg_paused->value || forceRefresh )
    {
        CG_ClearScene ();

        // build a refresh entity list
        // this also calls CG_CalcViewValues which loads
        // v_forward, etc.
        CG_AddEntities ();

        if ( cg_testEntities->value ) {
            CG_TestEntities ();
        }
        if ( cg_testLights->value ) {
            CG_TestLights ();
        }
        if ( cg_testBlend->value ) {
            CG_TestBlend ();
        }

        // offset vieworg appropriately if we're doing stereo separation
        if ( stereo_separation != 0 ) {
            VectorMA( cg.refdef.vieworg, stereo_separation, cg.v_right, cg.refdef.vieworg );
        }

        if ( cg.thirdPerson ) {
            CG_ThirdPerson_CameraUpdate ();
        }

        if (cg.frame.playerState.stats[STAT_GASSED])
            CG_GasView();

        // never let it sit exactly on a node line, because a water plane can
        // dissapear when viewed with the eye exactly on it.
        // the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis

        CG_ShockView();
        
        cg.refdef.width = scr_vrect.width;
        cg.refdef.height = scr_vrect.height;
//      cg.refdef.fov_x = 90;
        cg.refdef.fov_y = CalcFov ( cg.refdef.fov_x, cg.refdef.width, cg.refdef.height );

        cg.refdef.time = cg.time*0.001;

        cg.refdef.areabits = cg.frame.areabits;

        cg.refdef.num_entities = r_numEntities;
        cg.refdef.entities = r_entities;
        cg.refdef.num_dlights = r_numDlights;
        cg.refdef.dlights = r_dlights;
        cg.refdef.num_polys = r_numPolys;
        cg.refdef.polys = r_polys;

        cg.refdef.rdflags = CG_RenderFlags ();

        // warp if underwater
        if ( cg.refdef.rdflags & RDF_UNDERWATER ) {
            float phase = cg.refdef.time * WAVE_FREQUENCY * M_TWOPI;
            float v = WAVE_AMPLITUDE * (sin( phase ) - 1.0) + 1;
            cg.refdef.fov_x *= v;
            cg.refdef.fov_y *= v;
        }
    }

    trap_R_RenderFrame ( &cg.refdef );

    // update audio
    trap_S_Update ( cg.refdef.vieworg, cg.v_forward, cg.v_right, cg.v_up );

    if ( cg_stats->value ) {
        CG_Printf ( "ent:%i  lt:%i  polys:%i\n", r_numEntities, r_numDlights, r_numPolys );
    }

    if ( 0 ) {      // mirror of back view
        cg.refdef.x = scr_vrect.x;
        cg.refdef.y = scr_vrect.y;
        cg.refdef.width = scr_vrect.width / 5;
        cg.refdef.height = scr_vrect.height / 5;
        cg.refdef.y += scr_vrect.height / 2 - cg.refdef.height / 2;
        cg.refdef.viewangles[PITCH] = anglemod ( -cg.refdef.viewangles[PITCH] );
        cg.refdef.viewangles[YAW] = anglemod ( cg.refdef.viewangles[YAW] + 180 );

        trap_R_RenderFrame ( &cg.refdef );
    }

    SCR_Draw2D ();
}