Пример #1
0
/**
 * @brief Generate rain particle
 * @details Attempt to 'spot' a raindrop somewhere below a sky texture.
 */
static qboolean CG_RainParticleGenerate(cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight)
{
    float angle = random() * 2 * M_PI, distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random();
    float groundHeight, skyHeight;

    particle->pos[0] = cg.refdef_current->vieworg[0] + sin(angle) * distance;
    particle->pos[1] = cg.refdef_current->vieworg[1] + cos(angle) * distance;

    // choose a spawn point randomly between sky and ground
    skyHeight = BG_GetSkyHeightAtPoint(particle->pos);
    if (skyHeight >= MAX_ATMOSPHERIC_HEIGHT)
    {
        return qfalse;
    }
    groundHeight = BG_GetSkyGroundHeightAtPoint(particle->pos);
    if (groundHeight + particle->height + ATMOSPHERIC_PARTICLE_OFFSET >= skyHeight)
    {
        return qfalse;
    }
    particle->pos[2] = groundHeight + random() * (skyHeight - groundHeight);

    // make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling')
    if (cg_atmFx.baseHeightOffset > 0)
    {
        if (particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset)
        {
            particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset;

            if (particle->pos[2] < groundHeight)
            {
                return qfalse;
            }
        }
    }

    // rain goes in bursts - allow max raindrops every 10 seconds
    if (cg_atmFx.oldDropsActive > (0.50 * cg_atmFx.numDrops + 0.001 * cg_atmFx.numDrops * (10000 - (cg.time % 10000))))
    {
        return qfalse;
    }

    CG_SetParticleActive(particle, ACT_FALLING);
    particle->colour[0] = 0.6 + 0.2 * random() * 0xFF;
    particle->colour[1] = 0.6 + 0.2 * random() * 0xFF;
    particle->colour[2] = 0.6 + 0.2 * random() * 0xFF;
    VectorCopy(currvec, particle->delta);
    particle->delta[2] += crandom() * 100;
    VectorCopy(particle->delta, particle->deltaNormalized);
    VectorNormalizeFast(particle->deltaNormalized);
    particle->height       = ATMOSPHERIC_RAIN_HEIGHT + crandom() * 100;
    particle->weight       = currweight;
    particle->effectshader = &cg_atmFx.effectshaders[0];
    //particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ];

    return qtrue;
}
Пример #2
0
/*
* CG_BladeImpact
*/
void CG_BladeImpact( vec3_t pos, vec3_t dir )
{
	lentity_t *le;
	vec3_t angles;
	vec3_t end;
	trace_t	trace;

	//find what are we hitting
	VectorNormalizeFast( dir );
	VectorMA( pos, -1.0, dir, end );
	CG_Trace( &trace, pos, vec3_origin, vec3_origin, end, cg.view.POVent, MASK_SHOT );
	if( trace.fraction == 1.0 )
		return;

	VecToAngles( dir, angles );

	if( trace.surfFlags & SURF_FLESH ||
		( trace.ent > 0 && cg_entities[trace.ent].current.type == ET_PLAYER )
		|| ( trace.ent > 0 && cg_entities[trace.ent].current.type == ET_CORPSE ) )
	{
		le = CG_AllocModel( LE_ALPHA_FADE, pos, angles, 3, //3 frames for weak
			1, 1, 1, 1, //full white no inducted alpha
			0, 0, 0, 0, //dlight
			CG_MediaModel( cgs.media.modBladeWallHit ), NULL );
		le->ent.rotation = rand() % 360;
		le->ent.scale = 1.0f;

		trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxBladeFleshHit[(int)( random()*3 )] ), pos, CHAN_AUTO,
			cg_volume_effects->value, ATTN_NORM );
	}
	else if( trace.surfFlags & SURF_DUST )
	{
		// throw particles on dust
		CG_ParticleEffect( trace.endpos, trace.plane.normal, 0.30f, 0.30f, 0.25f, 30 );

		//fixme? would need a dust sound
		trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxBladeWallHit[(int)( random()*2 )] ), pos, CHAN_AUTO,
			cg_volume_effects->value, ATTN_NORM );
	}
	else
	{
		le = CG_AllocModel( LE_ALPHA_FADE, pos, angles, 3, //3 frames for weak
			1, 1, 1, 1, //full white no inducted alpha
			0, 0, 0, 0, //dlight
			CG_MediaModel( cgs.media.modBladeWallHit ), NULL );
		le->ent.rotation = rand() % 360;
		le->ent.scale = 1.0f;

		CG_ParticleEffect( trace.endpos, trace.plane.normal, 0.30f, 0.30f, 0.25f, 15 );

		trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxBladeWallHit[(int)( random()*2 )] ), pos, CHAN_AUTO,
			cg_volume_effects->value, ATTN_NORM );
		if( !( trace.surfFlags & SURF_NOMARKS ) )
			CG_SpawnDecal( pos, dir, random()*360, 8, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderBulletMark ) );
	}
}
Пример #3
0
qboolean AI_infront2D( vec3_t lookDir, vec3_t origin, vec3_t point, float accuracy )
{
	vec3_t vec;
	float dot;
	vec3_t origin2D, point2D, lookDir2D;

	VectorSet( origin2D, origin[0], origin[1], 0 );
	VectorSet( point2D, point[0], point[1], 0 );
	VectorSet( lookDir2D, lookDir[0], lookDir[1], 0 );
	VectorNormalizeFast( lookDir2D );

	VectorSubtract( point2D, origin2D, vec );
	VectorNormalizeFast( vec );
	dot = DotProduct( vec, lookDir2D );

	clamp( accuracy, -1, 1 );

	return ( dot > accuracy );
}
Пример #4
0
static int vector_NormalizeFast(lua_State * L)
{
	vec_t          *a;

	a = lua_getvector(L, 1);

	VectorNormalizeFast(a);

	return 1;
}
Пример #5
0
/**
 * @brief Generates a single texture coordinate for the specified stage and vertex.
 */
static void R_StageTexCoord (const materialStage_t *stage, const vec3_t v, const vec2_t in, vec2_t out)
{
	if (stage->flags & STAGE_ENVMAP) {  /* generate texcoords */
		vec3_t tmp;
		VectorSubtract(v, refdef.viewOrigin, tmp);
		VectorNormalizeFast(tmp);
		Vector2Copy(tmp, out);
	} else {  /* or use the ones we were given */
		Vector2Copy(in, out);
	}
}
Пример #6
0
static qboolean CG_SnowParticleGenerate( cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight ) {
	// Attempt to 'spot' a snowflake somewhere below a sky texture.

	float angle, distance;
	float groundHeight, skyHeight;
//	int msec = trap_Milliseconds();

//	n_generatetime++;

	angle = random() * 2 * M_PI;
	distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random();

	particle->pos[0] = cg.refdef.vieworg[0] + sin( angle ) * distance;
	particle->pos[1] = cg.refdef.vieworg[1] + cos( angle ) * distance;

	// ydnar: choose a spawn point randomly between sky and ground
	skyHeight = BG_GetSkyHeightAtPoint( particle->pos );
	if ( skyHeight == MAX_ATMOSPHERIC_HEIGHT ) {
		return qfalse;
	}
	groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos );
	if ( groundHeight >= skyHeight ) {
		return qfalse;
	}
	particle->pos[2] = groundHeight + random() * ( skyHeight - groundHeight );

	// make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling')
	if ( cg_atmFx.baseHeightOffset > 0 ) {
		if ( particle->pos[2] - cg.refdef.vieworg[2] > cg_atmFx.baseHeightOffset ) {
			particle->pos[2] = cg.refdef.vieworg[2] + cg_atmFx.baseHeightOffset;
			if ( particle->pos[2] < groundHeight ) {
				return qfalse;
			}
		}
	}

	CG_SetParticleActive( particle, ACT_FALLING );
	VectorCopy( currvec, particle->delta );
	particle->delta[2] += crandom() * 25;
	VectorCopy( particle->delta, particle->deltaNormalized );
	VectorNormalizeFast( particle->deltaNormalized );
	particle->height = ATMOSPHERIC_SNOW_HEIGHT + random() * 2;
	particle->weight = particle->height * 0.5f;

	if (cg_atmFx.numEffectShaders > 1) {
		particle->effectshader = &cg_atmFx.effectshaders[ rand()%cg_atmFx.numEffectShaders ];
	} else {
		particle->effectshader = &cg_atmFx.effectshaders[0];
	}

//	generatetime += trap_Milliseconds() - msec;
	return( qtrue );
}
Пример #7
0
void PerpendicularVector(vec3_t dst, const vec3_t src) {
	if (!src[0]) {
		VectorSet(dst, 1, 0, 0);
	} else if (!src[1]) {
		VectorSet(dst, 0, 1, 0);
	} else if (!src[2]) {
		VectorSet(dst, 0, 0, 1);
	} else {
		VectorSet(dst, -src[1], src[0], 0);
		VectorNormalizeFast(dst);
	}
}
Пример #8
0
/**
 * @brief Generate a snowflake
 * @details Attempt to 'spot' a snowflake somewhere below a sky texture.
 */
static qboolean CG_SnowParticleGenerate(cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight)
{
    float angle = random() * 2 * M_PI, distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random();
    float groundHeight, skyHeight;

    particle->pos[0] = cg.refdef_current->vieworg[0] + sin(angle) * distance;
    particle->pos[1] = cg.refdef_current->vieworg[1] + cos(angle) * distance;

    // choose a spawn point randomly between sky and ground
    skyHeight = BG_GetSkyHeightAtPoint(particle->pos);

    if (skyHeight >= MAX_ATMOSPHERIC_HEIGHT)
    {
        return qfalse;
    }

    groundHeight = BG_GetSkyGroundHeightAtPoint(particle->pos);

    if (groundHeight + particle->height + ATMOSPHERIC_PARTICLE_OFFSET >= skyHeight)
    {
        return qfalse;
    }

    particle->pos[2] = groundHeight + random() * (skyHeight - groundHeight);

    // make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling')
    if (cg_atmFx.baseHeightOffset > 0)
    {
        if (particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset)
        {
            particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset;
            if (particle->pos[2] < groundHeight)
            {
                return qfalse;
            }
        }
    }

    CG_SetParticleActive(particle, ACT_FALLING);

    VectorCopy(currvec, particle->delta);
    particle->delta[2] += crandom() * 25;

    VectorCopy(particle->delta, particle->deltaNormalized);
    VectorNormalizeFast(particle->deltaNormalized);
    particle->height       = ATMOSPHERIC_SNOW_HEIGHT + random() * 2;
    particle->weight       = particle->height * 0.5f;
    particle->effectshader = &cg_atmFx.effectshaders[0];
    //particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ];

    return qtrue;
}
Пример #9
0
static void VelocityForDamage( int damage, vec3_t v )
{
	v[0] = 10.0 * crandom();
	v[1] = 10.0 * crandom();
	v[2] = 20.0 + 10.0 * random();

	VectorNormalizeFast( v );

	if( damage < 50 )
		VectorScale( v, 0.7, v );
	else
		VectorScale( v, 1.2, v );
}
Пример #10
0
/*
* CG_Event_Jump
*/
void CG_Event_Jump( entity_state_t *state, int parm )
{
#define MOVEDIREPSILON 0.25f
	centity_t *cent;
	int xyspeedcheck;

	cent = &cg_entities[state->number];
	xyspeedcheck = SQRTFAST( cent->animVelocity[0]*cent->animVelocity[0] + cent->animVelocity[1]*cent->animVelocity[1] );
	if( xyspeedcheck < 100 )
	{                      // the player is jumping on the same place, not running
		CG_PModel_AddAnimation( state->number, LEGS_JUMP_NEUTRAL, 0, 0, EVENT_CHANNEL );
		CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value );
	}
	else
	{
		vec3_t movedir, viewaxis[3];
		movedir[0] = cent->animVelocity[0];
		movedir[1] = cent->animVelocity[1];
		movedir[2] = 0;
		VectorNormalizeFast( movedir );
		AngleVectors( tv( 0, cent->current.angles[YAW], 0 ), viewaxis[FORWARD], viewaxis[RIGHT], viewaxis[UP] );
		// see what's his relative movement direction
		if( DotProduct( movedir, viewaxis[FORWARD] ) > MOVEDIREPSILON )
		{
			cent->jumpedLeft = !cent->jumpedLeft;
			if( !cent->jumpedLeft )
			{
				CG_PModel_AddAnimation( state->number, LEGS_JUMP_LEG2, 0, 0, EVENT_CHANNEL );
				CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value );
			}
			else
			{
				CG_PModel_AddAnimation( state->number, LEGS_JUMP_LEG1, 0, 0, EVENT_CHANNEL );
				CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value );
			}
		}
		else
		{
			CG_PModel_AddAnimation( state->number, LEGS_JUMP_NEUTRAL, 0, 0, EVENT_CHANNEL );
			CG_SexedSound( state->number, CHAN_BODY, va( S_PLAYER_JUMP_1_to_2, ( rand()&1 )+1 ), cg_volume_players->value );
		}
	}
#undef MOVEDIREPSILON

  //racesow - lm: filter out other players
  if( ISVIEWERENTITY( state->number ))
    CG_AddJumpspeed();
  //!racesow
}
Пример #11
0
qboolean AI_infront( edict_t *self, edict_t *other )
{
	vec3_t vec;
	float dot;
	vec3_t forward;

	AngleVectors( self->s.angles, forward, NULL, NULL );
	VectorSubtract( other->s.origin, self->s.origin, vec );
	VectorNormalizeFast( vec );
	dot = DotProduct( vec, forward );

	if( dot > 0.3 )
		return qtrue;
	return qfalse;
}
Пример #12
0
//-----------------------------------------------------------------------------
// compute the decal basis based on surface normal, and preferred saxis
//-----------------------------------------------------------------------------
void R_DecalComputeBasis( msurface_t *surf, vec3_t pSAxis, vec3_t textureSpaceBasis[3] )
{
	vec3_t	surfaceNormal;

	// setup normal
	if( surf->flags & SURF_PLANEBACK )
		VectorNegate( surf->plane->normal, surfaceNormal );
	else VectorCopy( surf->plane->normal, surfaceNormal );

	VectorCopy( surfaceNormal, textureSpaceBasis[2] );

	if( pSAxis )
	{
		// T = S cross N
		CrossProduct( pSAxis, textureSpaceBasis[2], textureSpaceBasis[1] );

		// Name sure they aren't parallel or antiparallel
		// In that case, fall back to the normal algorithm.
		if( DotProduct( textureSpaceBasis[1], textureSpaceBasis[1] ) > 1e-6 )
		{
			// S = N cross T
			CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );

			VectorNormalizeFast( textureSpaceBasis[0] );
			VectorNormalizeFast( textureSpaceBasis[1] );
			return;
		}
		// Fall through to the standard algorithm for parallel or antiparallel
	}

	// original Half-Life algorithm: get textureBasis from linked surface
	VectorCopy( surf->texinfo->vecs[0], textureSpaceBasis[0] );
	VectorCopy( surf->texinfo->vecs[1], textureSpaceBasis[1] );
	VectorNormalizeFast( textureSpaceBasis[0] );
	VectorNormalizeFast( textureSpaceBasis[1] );
}
Пример #13
0
/*
====================
StudioGetAttachment
====================
*/
void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *origin, float *angles )
{
	mstudioattachment_t		*pAtt;
	vec3_t			angles2;
	model_t			*mod;

	mod = Mod_Handle( e->v.modelindex );
	mod_studiohdr = (studiohdr_t *)Mod_Extradata( mod );
	if( !mod_studiohdr ) return;

	if( mod_studiohdr->numattachments <= 0 )
		return;

	ASSERT( pBlendAPI != NULL );

	if( mod_studiohdr->numattachments > MAXSTUDIOATTACHMENTS )
	{
		mod_studiohdr->numattachments = MAXSTUDIOATTACHMENTS; // reduce it
		MsgDev( D_WARN, "SV_StudioGetAttahment: too many attachments on %s\n", mod_studiohdr->name );
	}

	iAttachment = bound( 0, iAttachment, mod_studiohdr->numattachments );

	// calculate attachment origin and angles
	pAtt = (mstudioattachment_t *)((byte *)mod_studiohdr + mod_studiohdr->attachmentindex);

	VectorCopy( e->v.angles, angles2 );

	if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG ))
		angles2[PITCH] = -angles2[PITCH];

	pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, angles2, e->v.origin,
		e->v.controller, e->v.blending, pAtt[iAttachment].bone, e );

	// compute pos and angles
	if( origin != NULL )
		Matrix3x4_VectorTransform( studio_bones[pAtt[iAttachment].bone], pAtt[iAttachment].org, origin );

	if( sv_allow_studio_attachment_angles->integer && origin != NULL && angles != NULL )
	{
		vec3_t	forward, bonepos;

		Matrix3x4_OriginFromMatrix( studio_bones[pAtt[iAttachment].bone], bonepos );
		VectorSubtract( origin, bonepos, forward ); // make forward
		VectorNormalizeFast( forward );
		VectorAngles( forward, angles );
	}
}
Пример #14
0
/**
 * @brief Checks whether a point is visible from a given position
 * @param[in] origin Origin to test from
 * @param[in] dir Direction to test into
 * @param[in] point This is the point we want to check the visibility for
 */
qboolean FrustumVis (const vec3_t origin, int dir, const vec3_t point)
{
	/* view frustum check */
	vec3_t delta;
	byte dv;

	delta[0] = point[0] - origin[0];
	delta[1] = point[1] - origin[1];
	delta[2] = 0;
	VectorNormalizeFast(delta);
	dv = dir & (DIRECTIONS - 1);

	/* test 120 frustum (cos 60 = 0.5) */
	if ((delta[0] * dvecsn[dv][0] + delta[1] * dvecsn[dv][1]) < 0.5)
		return qfalse;
	else
		return qtrue;
}
Пример #15
0
/*
* CG_Event_WeaponBeam
*/
static void CG_Event_WeaponBeam( vec3_t origin, vec3_t dir, int ownerNum, int weapon, int firemode )
{
	gs_weapon_definition_t *weapondef;
	int range;
	vec3_t end;
	trace_t trace;

	switch( weapon )
	{
	case WEAP_ELECTROBOLT:
		weapondef = GS_GetWeaponDef( WEAP_ELECTROBOLT );
		range = ELECTROBOLT_RANGE;
		break;

	case WEAP_INSTAGUN:
		weapondef = GS_GetWeaponDef( WEAP_INSTAGUN );
		range = weapondef->firedef.timeout;
		break;

	default:
		return;
	}

	VectorNormalizeFast( dir );

	VectorMA( origin, range, dir, end );

	// retrace to spawn wall impact
	CG_Trace( &trace, origin, vec3_origin, vec3_origin, end, cg.view.POVent, MASK_SOLID );
	if( trace.ent != -1 && !(trace.surfFlags & (SURF_SKY|SURF_NOMARKS|SURF_NOIMPACT)) )
	{
		if( weapondef->weapon_id == WEAP_ELECTROBOLT )
			CG_BoltExplosionMode( trace.endpos, trace.plane.normal, FIRE_MODE_STRONG );
		else if( weapondef->weapon_id == WEAP_INSTAGUN )
			CG_InstaExplosionMode( trace.endpos, trace.plane.normal, FIRE_MODE_STRONG );
	}

	// when it's predicted we have to delay the drawing until the view weapon is calculated
	cg_entities[ownerNum].localEffects[LOCALEFFECT_EV_WEAPONBEAM] = weapon;
	VectorCopy( origin, cg_entities[ownerNum].laserOrigin );
	VectorCopy( trace.endpos, cg_entities[ownerNum].laserPoint );
}
Пример #16
0
/*
* G_ClientAddDamageIndicatorImpact
*/
void G_ClientAddDamageIndicatorImpact( gclient_t *client, int damage, const vec3_t basedir )
{
	edict_t *ent;
	vec3_t dir;
	float frac;

	if( damage < 1 )
		return;

	if( !client || client - game.clients < 0 || client - game.clients >= gs.maxclients )
		return;

	ent = &game.edicts[ ( client - game.clients ) + 1 ];

	if( !basedir )
	{
		VectorCopy( vec3_origin, dir );
	}
	else
	{
		VectorNormalize2( basedir, dir );

		//#define ACCENT_SCALE 2.0f
#ifdef ACCENT_SCALE
		// accent the vertical or horizontal aspect of the direction
		if( VectorLengthFast( tv( dir[0], dir[1], 0 ) ) > dir[2] )
		{
			dir[0] *= ACCENT_SCALE;
			dir[1] *= ACCENT_SCALE;
		}
		else
			dir[2] *= ACCENT_SCALE;

		VectorNormalizeFast( dir );
#endif
#undef ACCENT_SCALE
	}

	frac = damage / ( damage + client->resp.snap.damageTaken );
	VectorLerp( client->resp.snap.damageTakenDir, frac, dir, client->resp.snap.damageTakenDir );
	client->resp.snap.damageTaken += damage;
}
Пример #17
0
void RB_CalcPushVertexes( deformStage_t *ds ) {
	int			i;
	float		*xyz;
	//float		*table;
	float		scale;
	vec3_t		offset;
	vec3_t		delta;

	//table = TableForFunc( ds->deformationWave.func );

	scale = ds->bulgeHeight;

	xyz = ( float * ) tess.xyz;
	for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) {
		VectorSubtract( xyz, ds->moveVector, delta );
		VectorNormalizeFast( delta );
		VectorScale( delta,scale,offset );
		VectorAdd( xyz, offset, xyz );
	}
}
Пример #18
0
// Wiggle the normals for wavy environment mapping
void RB_CalcDeformNormals( deformStage_t *ds ) {
	int i;
	float	scale;
	vector3	*xyz = &tess.xyz[0], *normal = &tess.normal[0];

	for ( i = 0; i < tess.numVertexes; i++, xyz++, normal++ ) {
		scale = 0.98f;
		scale = R_NoiseGet4f( 000 + xyz->x * scale, xyz->y * scale, xyz->z * scale, tess.shaderTime * ds->deformationWave.frequency );
		normal->x += ds->deformationWave.amplitude * scale;

		scale = 0.98f;
		scale = R_NoiseGet4f( 100 + xyz->x * scale, xyz->y * scale, xyz->z * scale, tess.shaderTime * ds->deformationWave.frequency );
		normal->y += ds->deformationWave.amplitude * scale;

		scale = 0.98f;
		scale = R_NoiseGet4f( 200 + xyz->z * scale, xyz->y * scale, xyz->z * scale, tess.shaderTime * ds->deformationWave.frequency );
		normal->z += ds->deformationWave.amplitude * scale;

		VectorNormalizeFast( normal );
	}
}
Пример #19
0
void WolfRevivePushEnt( gentity_t *self, gentity_t *other ) {
	vec3_t dir, push;

	VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, dir );
	dir[2] = 0;
	VectorNormalizeFast( dir );

	VectorScale( dir, WR_PUSHAMOUNT, push );

	if ( self->client ) {
		VectorAdd( self->s.pos.trDelta, push, self->s.pos.trDelta );
		VectorAdd( self->client->ps.velocity, push, self->client->ps.velocity );
	}

	VectorScale( dir, -WR_PUSHAMOUNT, push );
	push[2] = WR_PUSHAMOUNT / 2;

	VectorAdd( other->s.pos.trDelta, push, other->s.pos.trDelta );
	//VectorAdd( other->client->ps.velocity, push, other->client->ps.velocity );
	if ( other->client ) {
		VectorAdd( other->client->ps.velocity, push, other->client->ps.velocity );
	}
}
Пример #20
0
/**
 * @brief Finds a vector perpendicular to the source vector
 * @param[in] src The source vector
 * @param[out] dst A vector perpendicular to @c src
 * @note @c dst is a perpendicular vector to @c src such that it is the closest
 * to one of the three axis: {1,0,0}, {0,1,0} and {0,0,1} (chosen in that order
 * in case of equality)
 * @pre non-NULL pointers and @c src is normalized
 * @sa ProjectPointOnPlane
 */
void PerpendicularVector (vec3_t dst, const vec3_t src)
{
	int pos;
	int i;
	float minelem = 1.0F;
	vec3_t tempvec;

	/* find the smallest magnitude axially aligned vector */
	for (pos = 0, i = 0; i < 3; i++) {
		const float a = fabs(src[i]);
		if (a < minelem) {
			pos = i;
			minelem = a;
		}
	}
	tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
	tempvec[pos] = 1.0F;

	/* project the point onto the plane defined by src */
	ProjectPointOnPlane(dst, tempvec, src);

	/* normalize the result */
	VectorNormalizeFast(dst);
}
Пример #21
0
/*
==================
RB_AddFlare

This is called at surface tesselation time
==================
*/
void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) {
	int				i;
	flare_t			*f, *oldest;
	vec3_t			local;
	float			d = 1;
	vec4_t			eye, clip, normalized, window;

	backEnd.pc.c_flareAdds++;

	if(normal && (normal[0] || normal[1] || normal[2]))
	{
		VectorSubtract( backEnd.viewParms.or.origin, point, local );
		VectorNormalizeFast(local);
		d = DotProduct(local, normal);

		// If the viewer is behind the flare don't add it.
		if(d < 0)
			return;
	}

	// if the point is off the screen, don't bother adding it
	// calculate screen coordinates and depth
	R_TransformModelToClip( point, backEnd.or.modelMatrix, 
		backEnd.viewParms.projectionMatrix, eye, clip );

	// check to see if the point is completely off screen
	for ( i = 0 ; i < 3 ; i++ ) {
		if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) {
			return;
		}
	}

	R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window );

	if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth
		|| window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) {
		return;	// shouldn't happen, since we check the clip[] above, except for FP rounding
	}

	// see if a flare with a matching surface, scene, and view exists
	oldest = r_flareStructs;
	for ( f = r_activeFlares ; f ; f = f->next ) {
		if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum
			&& f->inPortal == backEnd.viewParms.isPortal ) {
			break;
		}
	}

	// allocate a new one
	if (!f ) {
		if ( !r_inactiveFlares ) {
			// the list is completely full
			return;
		}
		f = r_inactiveFlares;
		r_inactiveFlares = r_inactiveFlares->next;
		f->next = r_activeFlares;
		r_activeFlares = f;

		f->surface = surface;
		f->frameSceneNum = backEnd.viewParms.frameSceneNum;
		f->inPortal = backEnd.viewParms.isPortal;
		f->addedFrame = -1;
	}

	if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) {
		f->visible = qfalse;
		f->fadeTime = backEnd.refdef.time - 2000;
	}

	f->addedFrame = backEnd.viewParms.frameCount;
	f->fogNum = fogNum;

	VectorCopy(point, f->origin);
	VectorCopy( color, f->color );

	// fade the intensity of the flare down as the
	// light surface turns away from the viewer
	VectorScale( f->color, d, f->color ); 

	// save info needed to test
	f->windowX = backEnd.viewParms.viewportX + window[0];
	f->windowY = backEnd.viewParms.viewportY + window[1];

	f->eyeZ = eye[2];
}
Пример #22
0
/*
==================
RB_AddFlare

This is called at surface tesselation time
==================
*/
void RB_AddFlare(void *surface, int fogNum, vec3_t point, vec3_t color, float scale, vec3_t normal, int id, qboolean cgvisible) // added scale. added id.  added visible
{
	int     i;
	flare_t *f;
	vec3_t  local;
	vec4_t  eye, clip, normalized, window;

	backEnd.pc.c_flareAdds++;

	// if the point is off the screen, don't bother adding it
	// calculate screen coordinates and depth
	R_TransformModelToClip(point, backEnd.orientation.modelMatrix,
	                       backEnd.viewParms.projectionMatrix, eye, clip);

	//Ren_Print("src:  %f  %f  %f  \n", point[0], point[1], point[2]);
	//Ren_Print("eye:  %f  %f  %f  %f\n", eye[0], eye[1], eye[2], eye[3]);

	// check to see if the point is completely off screen
	for (i = 0 ; i < 3 ; i++)
	{
		if (clip[i] >= clip[3] || clip[i] <= -clip[3])
		{
			return;
		}
	}

	R_TransformClipToWindow(clip, &backEnd.viewParms, normalized, window);

	//Ren_Print("window:  %f  %f  %f  \n", window[0], window[1], window[2]);

	if (window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth
	    || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight)
	{
		return; // shouldn't happen, since we check the clip[] above, except for FP rounding
	}

	// see if a flare with a matching surface, scene, and view exists
	for (f = r_activeFlares ; f ; f = f->next)
	{
		// added back in more checks for different scenes
		if (f->id == id && f->frameSceneNum == backEnd.viewParms.frameSceneNum && f->inPortal == backEnd.viewParms.isPortal)
		{
			break;
		}
	}

	// allocate a new one
	if (!f)
	{
		if (!r_inactiveFlares)
		{
			// the list is completely full
			return;
		}
		f                = r_inactiveFlares;
		r_inactiveFlares = r_inactiveFlares->next;
		f->next          = r_activeFlares;
		r_activeFlares   = f;

		f->surface       = surface;
		f->frameSceneNum = backEnd.viewParms.frameSceneNum;
		f->inPortal      = backEnd.viewParms.isPortal;
		f->addedFrame    = -1;
		f->id            = id;
	}

	f->cgvisible = cgvisible;

	if (f->addedFrame != backEnd.viewParms.frameCount - 1)
	{
		f->visible  = qfalse;
		f->fadeTime = backEnd.refdef.time - 2000;
	}

	f->addedFrame = backEnd.viewParms.frameCount;
	f->fogNum     = fogNum;

	VectorCopy(color, f->color);

	f->scale = scale;

	// fade the intensity of the flare down as the
	// light surface turns away from the viewer
	if (normal)
	{
		float d;

		VectorSubtract(backEnd.viewParms.orientation.origin, point, local);
		VectorNormalizeFast(local);
		d = DotProduct(local, normal);
		VectorScale(f->color, d, f->color);
	}

	// save info needed to test
	f->windowX = backEnd.viewParms.viewportX + window[0];
	f->windowY = backEnd.viewParms.viewportY + window[1];

	f->eyeZ = eye[2];
}
Пример #23
0
void HealthComponent::HandleDamage(float amount, gentity_t* source, Util::optional<Vec3> location,
Util::optional<Vec3> direction, int flags, meansOfDeath_t meansOfDeath) {
	if (health <= 0.0f) return;
	if (amount <= 0.0f) return;

	gclient_t *client = entity.oldEnt->client;

	// Check for immunity.
	if (entity.oldEnt->flags & FL_GODMODE) return;
	if (client) {
		if (client->noclip) return;
		if (client->sess.spectatorState != SPECTATOR_NOT) return;
	}

	// Set source to world if missing.
	if (!source) source = &g_entities[ENTITYNUM_WORLD];

	// Don't handle ET_MOVER w/o die or pain function.
	// TODO: Handle mover special casing in a dedicated component.
	if (entity.oldEnt->s.eType == entityType_t::ET_MOVER && !(entity.oldEnt->die || entity.oldEnt->pain)) {
		// Special case for ET_MOVER with act function in initial position.
		if ((entity.oldEnt->moverState == MOVER_POS1 || entity.oldEnt->moverState == ROTATOR_POS1)
		    && entity.oldEnt->act) {
			entity.oldEnt->act(entity.oldEnt, source, source);
		}

		return;
	}

	// Check for protection.
	if (!(flags & DAMAGE_NO_PROTECTION)) {
		// Check for protection from friendly damage.
		if (entity.oldEnt != source && G_OnSameTeam(entity.oldEnt, source)) {
			// Check if friendly fire has been disabled.
			if (!g_friendlyFire.integer) return;

			// Never do friendly damage on movement attacks.
			switch (meansOfDeath) {
				case MOD_LEVEL3_POUNCE:
				case MOD_LEVEL4_TRAMPLE:
					return;

				default:
					break;
			}

			// If dretchpunt is enabled and this is a dretch, do dretchpunt instead of damage.
			// TODO: Add a message for pushing.
			if (g_dretchPunt.integer && client && client->ps.stats[STAT_CLASS] == PCL_ALIEN_LEVEL0)
			{
				vec3_t dir, push;

				VectorSubtract(entity.oldEnt->r.currentOrigin, source->r.currentOrigin, dir);
				VectorNormalizeFast(dir);
				VectorScale(dir, (amount * 10.0f), push);
				push[ 2 ] = 64.0f;

				VectorAdd( client->ps.velocity, push, client->ps.velocity );

				return;
			}
		}

		// Check for protection from friendly buildable damage. Never protect from building actions.
		// TODO: Use DAMAGE_NO_PROTECTION flag instead of listing means of death here.
		if (entity.oldEnt->s.eType == entityType_t::ET_BUILDABLE && source->client &&
		    meansOfDeath != MOD_DECONSTRUCT && meansOfDeath != MOD_SUICIDE &&
		    meansOfDeath != MOD_REPLACE) {
			if (G_OnSameTeam(entity.oldEnt, source) && !g_friendlyBuildableFire.integer) {
				return;
			}
		}
	}

	float take = amount;

	// Apply damage modifiers.
	if (!(flags & DAMAGE_PURE)) {
		entity.ApplyDamageModifier(take, location, direction, flags, meansOfDeath);
	}

	// Update combat timers.
	// TODO: Add a message to update combat timers.
	if (client && source->client && entity.oldEnt != source) {
		client->lastCombatTime = entity.oldEnt->client->lastCombatTime = level.time;
	}

	if (client) {
		// Save damage w/o armor modifier.
		client->damage_received += (int)(amount + 0.5f);

		// Save damage direction.
		if (direction) {
			VectorCopy(direction.value().Data(), client->damage_from);
			client->damage_fromWorld = false;
		} else {
			VectorCopy(entity.oldEnt->r.currentOrigin, client->damage_from);
			client->damage_fromWorld = true;
		}

		// Drain jetpack fuel.
		// TODO: Have another component handle jetpack fuel drain.
		client->ps.stats[STAT_FUEL] = std::max(0, client->ps.stats[STAT_FUEL] -
		                                       (int)(amount + 0.5f) * JETPACK_FUEL_PER_DMG);

		// If boosted poison every attack.
		// TODO: Add a poison message and a PoisonableComponent.
		if (source->client && (source->client->ps.stats[STAT_STATE] & SS_BOOSTED) &&
		    client->pers.team == TEAM_HUMANS && client->poisonImmunityTime < level.time) {
			switch (meansOfDeath) {
				case MOD_POISON:
				case MOD_LEVEL2_ZAP:
					break;

				default:
					client->ps.stats[STAT_STATE] |= SS_POISONED;
					client->lastPoisonTime   = level.time;
					client->lastPoisonClient = source;
					break;
			}
		}
	}

	healthLogger.Notice("Taking damage: %3.1f (%3.1f → %3.1f)", take, health, health - take);

	// Do the damage.
	health -= take;

	// Update team overlay info.
	if (client) client->pers.infoChangeTime = level.time;

	// TODO: Move lastDamageTime to HealthComponent.
	entity.oldEnt->lastDamageTime = level.time;

	// HACK: gentity_t.nextRegenTime only affects alien clients.
	// TODO: Catch damage message in a new RegenerationComponent.
	entity.oldEnt->nextRegenTime = level.time + ALIEN_CLIENT_REGEN_WAIT;

	// Handle non-self damage.
	if (entity.oldEnt != source) {
		float loss = take;

		if (health < 0.0f) loss += health;

		// TODO: Use ClientComponent.
		if (source->client) {
			// Add to the attacker's account on the target.
			// TODO: Move damage account array to HealthComponent.
			entity.oldEnt->credits[source->client->ps.clientNum].value += loss;
			entity.oldEnt->credits[source->client->ps.clientNum].time = level.time;
			entity.oldEnt->credits[source->client->ps.clientNum].team = (team_t)source->client->pers.team;
		}
	}

	// Handle death.
	// TODO: Send a Die/Pain message and handle details where appropriate.
	if (health <= 0) {
		healthLogger.Notice("Dying with %.1f health.", health);

		// Disable knockback.
		if (client) entity.oldEnt->flags |= FL_NO_KNOCKBACK;

		// Call legacy die function.
		if (entity.oldEnt->die) entity.oldEnt->die(entity.oldEnt, source, source, meansOfDeath);

		// Send die message.
		entity.Die(source, meansOfDeath);

		// Trigger ON_DIE event.
		if(!client) G_EventFireEntity(entity.oldEnt, source, ON_DIE);
	} else if (entity.oldEnt->pain) {
		entity.oldEnt->pain(entity.oldEnt, source, (int)std::ceil(take));
	}

	if (entity.oldEnt != source && source->client) {
		bool lethal = (health <= 0);
		CombatFeedback::HitNotify(source, entity.oldEnt, location, take, meansOfDeath, lethal);
	}
}
Пример #24
0
/*
=================
R_MarkFragments

=================
*/
int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
				   int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
	int				numsurfaces, numPlanes;
	int				i, j, k, m, n;
	surfaceType_t	*surfaces[64];
	vec3_t			mins, maxs;
	int				returnedFragments;
	int				returnedPoints;
	vec3_t			normals[MAX_VERTS_ON_POLY+2];
	float			dists[MAX_VERTS_ON_POLY+2];
	vec3_t			clipPoints[2][MAX_VERTS_ON_POLY];
	int				numClipPoints;
	float			*v;
	srfSurfaceFace_t *surf;
	srfGridMesh_t	*cv;
	drawVert_t		*dv;
	vec3_t			normal;
	vec3_t			projectionDir;
	vec3_t			v1, v2;
	int				*indexes;

	//increment view count for double check prevention
	tr.viewCount++;

	//
	VectorNormalize2( projection, projectionDir );
	// find all the brushes that are to be considered
	ClearBounds( mins, maxs );
	for ( i = 0 ; i < numPoints ; i++ ) {
		vec3_t	temp;

		AddPointToBounds( points[i], mins, maxs );
		VectorAdd( points[i], projection, temp );
		AddPointToBounds( temp, mins, maxs );
		// make sure we get all the leafs (also the one(s) in front of the hit surface)
		VectorMA( points[i], -20, projectionDir, temp );
		AddPointToBounds( temp, mins, maxs );
	}

	if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY;
	// create the bounding planes for the to be projected polygon
	for ( i = 0 ; i < numPoints ; i++ ) {
		VectorSubtract(points[(i+1)%numPoints], points[i], v1);
		VectorAdd(points[i], projection, v2);
		VectorSubtract(points[i], v2, v2);
		CrossProduct(v1, v2, normals[i]);
		VectorNormalizeFast(normals[i]);
		dists[i] = DotProduct(normals[i], points[i]);
	}
	// add near and far clipping planes for projection
	VectorCopy(projectionDir, normals[numPoints]);
	dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32;
	VectorCopy(projectionDir, normals[numPoints+1]);
	VectorInverse(normals[numPoints+1]);
	dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20;
	numPlanes = numPoints + 2;

	numsurfaces = 0;
	R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir);
	//assert(numsurfaces <= 64);
	//assert(numsurfaces != 64);

	returnedPoints = 0;
	returnedFragments = 0;

	for ( i = 0 ; i < numsurfaces ; i++ ) {

		if (*surfaces[i] == SF_GRID) {

			cv = (srfGridMesh_t *) surfaces[i];
			for ( m = 0 ; m < cv->height - 1 ; m++ ) {
				for ( n = 0 ; n < cv->width - 1 ; n++ ) {
					// We triangulate the grid and chop all triangles within
					// the bounding planes of the to be projected polygon.
					// LOD is not taken into account, not such a big deal though.
					//
					// It's probably much nicer to chop the grid itself and deal
					// with this grid as a normal SF_GRID surface so LOD will
					// be applied. However the LOD of that chopped grid must
					// be synced with the LOD of the original curve.
					// One way to do this; the chopped grid shares vertices with
					// the original curve. When LOD is applied to the original
					// curve the unused vertices are flagged. Now the chopped curve
					// should skip the flagged vertices. This still leaves the
					// problems with the vertices at the chopped grid edges.
					//
					// To avoid issues when LOD applied to "hollow curves" (like
					// the ones around many jump pads) we now just add a 2 unit
					// offset to the triangle vertices.
					// The offset is added in the vertex normal vector direction
					// so all triangles will still fit together.
					// The 2 unit offset should avoid pretty much all LOD problems.

					numClipPoints = 3;

					dv = cv->verts + m * cv->width + n;

					VectorCopy(dv[0].xyz, clipPoints[0][0]);
					VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]);
					VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
					VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
					VectorCopy(dv[1].xyz, clipPoints[0][2]);
					VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]);
					// check the normal of this triangle
					VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
					VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
					CrossProduct(v1, v2, normal);
					VectorNormalizeFast(normal);
					if (DotProduct(normal, projectionDir) < -0.1) {
						// add the fragments of this triangle
						R_AddMarkFragments(numClipPoints, clipPoints,
										   numPlanes, normals, dists,
										   maxPoints, pointBuffer,
										   maxFragments, fragmentBuffer,
										   &returnedPoints, &returnedFragments, mins, maxs);

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;	// not enough space for more fragments
						}
					}

					VectorCopy(dv[1].xyz, clipPoints[0][0]);
					VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]);
					VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
					VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
					VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]);
					VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]);
					// check the normal of this triangle
					VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
					VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
					CrossProduct(v1, v2, normal);
					VectorNormalizeFast(normal);
					if (DotProduct(normal, projectionDir) < -0.05) {
						// add the fragments of this triangle
						R_AddMarkFragments(numClipPoints, clipPoints,
										   numPlanes, normals, dists,
										   maxPoints, pointBuffer,
										   maxFragments, fragmentBuffer,
										   &returnedPoints, &returnedFragments, mins, maxs);

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;	// not enough space for more fragments
						}
					}
				}
			}
		}
		else if (*surfaces[i] == SF_FACE) {

			surf = ( srfSurfaceFace_t * ) surfaces[i];
			// check the normal of this face
			if (DotProduct(surf->plane.normal, projectionDir) > -0.5) {
				continue;
			}

			/*
			VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
			VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
			CrossProduct(v1, v2, normal);
			VectorNormalize(normal);
			if (DotProduct(normal, projectionDir) > -0.5) continue;
			*/
			indexes = (int *)( (byte *)surf + surf->ofsIndices );
			for ( k = 0 ; k < surf->numIndices ; k += 3 ) {
				for ( j = 0 ; j < 3 ; j++ ) {
					v = surf->points[0] + VERTEXSIZE * indexes[k+j];;
					VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] );
				}
				// add the fragments of this face
				R_AddMarkFragments( 3 , clipPoints,
								   numPlanes, normals, dists,
								   maxPoints, pointBuffer,
								   maxFragments, fragmentBuffer,
								   &returnedPoints, &returnedFragments, mins, maxs);
				if ( returnedFragments == maxFragments ) {
					return returnedFragments;	// not enough space for more fragments
				}
			}
			continue;
		}
		else {
			// ignore all other world surfaces
			// might be cool to also project polygons on a triangle soup
			// however this will probably create huge amounts of extra polys
			// even more than the projection onto curves
			continue;
		}
	}
	return returnedFragments;
}
Пример #25
0
/*
* R_LightForOrigin
*/
void R_LightForOrigin( const vec3_t origin, vec3_t dir, vec4_t ambient, vec4_t diffuse, float radius )
{
	int i, j;
	int k, s;
	int vi[3], elem[4];
	float dot, t[8], scale;
	vec3_t vf, vf2, tdir;
	vec3_t ambientLocal, diffuseLocal;
	vec_t *gridSize, *gridMins;
	int *gridBounds;
	static mgridlight_t lightarray[8];
	lightstyle_t *lightStyles = rsc.lightStyles;

	VectorSet( ambientLocal, 0, 0, 0 );
	VectorSet( diffuseLocal, 0, 0, 0 );

	if( !rsh.worldModel /* || (rn.refdef.rdflags & RDF_NOWORLDMODEL)*/ ||
		!rsh.worldBrushModel->lightgrid || !rsh.worldBrushModel->numlightgridelems )
	{
		VectorSet( dir, 0.1f, 0.2f, 0.7f );
		goto dynamic;
	}

	gridSize = rsh.worldBrushModel->gridSize;
	gridMins = rsh.worldBrushModel->gridMins;
	gridBounds = rsh.worldBrushModel->gridBounds;

	for( i = 0; i < 3; i++ )
	{
		vf[i] = ( origin[i] - gridMins[i] ) / gridSize[i];
		vi[i] = (int)vf[i];
		vf[i] = vf[i] - floor( vf[i] );
		vf2[i] = 1.0f - vf[i];
	}

	elem[0] = vi[2] * gridBounds[3] + vi[1] * gridBounds[0] + vi[0];
	elem[1] = elem[0] + gridBounds[0];
	elem[2] = elem[0] + gridBounds[3];
	elem[3] = elem[2] + gridBounds[0];

	for( i = 0; i < 4; i++ )
	{
		lightarray[i*2+0] = *rsh.worldBrushModel->lightarray[bound( 0, elem[i]+0, (int)rsh.worldBrushModel->numlightarrayelems-1)];
		lightarray[i*2+1] = *rsh.worldBrushModel->lightarray[bound( 1, elem[i]+1, (int)rsh.worldBrushModel->numlightarrayelems-1)];
	}

	t[0] = vf2[0] * vf2[1] * vf2[2];
	t[1] = vf[0] * vf2[1] * vf2[2];
	t[2] = vf2[0] * vf[1] * vf2[2];
	t[3] = vf[0] * vf[1] * vf2[2];
	t[4] = vf2[0] * vf2[1] * vf[2];
	t[5] = vf[0] * vf2[1] * vf[2];
	t[6] = vf2[0] * vf[1] * vf[2];
	t[7] = vf[0] * vf[1] * vf[2];

	VectorClear( dir );

	for( i = 0; i < 4; i++ )
	{
		R_LatLongToNorm( lightarray[i*2].direction, tdir );
		VectorScale( tdir, t[i*2], tdir );
		for( k = 0; k < MAX_LIGHTMAPS && ( s = lightarray[i*2].styles[k] ) != 255; k++ )
		{
			dir[0] += lightStyles[s].rgb[0] * tdir[0];
			dir[1] += lightStyles[s].rgb[1] * tdir[1];
			dir[2] += lightStyles[s].rgb[2] * tdir[2];
		}

		R_LatLongToNorm( lightarray[i*2+1].direction, tdir );
		VectorScale( tdir, t[i*2+1], tdir );
		for( k = 0; k < MAX_LIGHTMAPS && ( s = lightarray[i*2+1].styles[k] ) != 255; k++ )
		{
			dir[0] += lightStyles[s].rgb[0] * tdir[0];
			dir[1] += lightStyles[s].rgb[1] * tdir[1];
			dir[2] += lightStyles[s].rgb[2] * tdir[2];
		}
	}

	for( j = 0; j < 3; j++ )
	{
		if( ambient )
		{
			for( i = 0; i < 4; i++ )
			{
				for( k = 0; k < MAX_LIGHTMAPS; k++ )
				{
					if( ( s = lightarray[i*2].styles[k] ) != 255 )
						ambientLocal[j] += t[i*2] * lightarray[i*2].ambient[k][j] * lightStyles[s].rgb[j];
					if( ( s = lightarray[i*2+1].styles[k] ) != 255 )
						ambientLocal[j] += t[i*2+1] * lightarray[i*2+1].ambient[k][j] * lightStyles[s].rgb[j];
				}
			}
		}
		if( diffuse || radius )
		{
			for( i = 0; i < 4; i++ )
			{
				for( k = 0; k < MAX_LIGHTMAPS; k++ )
				{
					if( ( s = lightarray[i*2].styles[k] ) != 255 )
						diffuseLocal[j] += t[i*2] * lightarray[i*2].diffuse[k][j] * lightStyles[s].rgb[j];
					if( ( s = lightarray[i*2+1].styles[k] ) != 255 )
						diffuseLocal[j] += t[i*2+1] * lightarray[i*2+1].diffuse[k][j] * lightStyles[s].rgb[j];
				}
			}
		}
	}

	// convert to grayscale
	if( r_lighting_grayscale->integer ) {
		vec_t grey;

		if( ambient ) {
			grey = ColorGrayscale( ambientLocal );
			ambientLocal[0] = ambientLocal[1] = ambientLocal[2] = bound( 0, grey, 255 );
		}

		if( diffuse || radius ) {
			grey = ColorGrayscale( diffuseLocal );
			diffuseLocal[0] = diffuseLocal[1] = diffuseLocal[2] = bound( 0, grey, 255 );
		}
	}

dynamic:
	// add dynamic lights
	if( radius && r_dynamiclight->integer )
	{
		unsigned int lnum;
		dlight_t *dl;
		float dist, dist2, add;
		vec3_t direction;
		qboolean anyDlights = qfalse;

		for( lnum = 0; lnum < rsc.numDlights; lnum++ )
		{
			dl = rsc.dlights + lnum;
			if( Distance( dl->origin, origin ) > dl->intensity + radius )
				continue;

			VectorSubtract( dl->origin, origin, direction );
			dist = VectorLength( direction );

			if( !dist || dist > dl->intensity + radius )
				continue;

			if( !anyDlights )
			{
				VectorNormalizeFast( dir );
				anyDlights = qtrue;
			}

			add = 1.0 - (dist / (dl->intensity + radius));
			dist2 = add * 0.5 / dist;
			for( i = 0; i < 3; i++ )
			{
				dot = dl->color[i] * add;
				diffuseLocal[i] += dot;
				ambientLocal[i] += dot * 0.05;
				dir[i] += direction[i] * dist2;
			}
		}
	}

	VectorNormalizeFast( dir );

	scale = mapConfig.mapLightColorScale / 255.0f;

	if( ambient )
	{
		float scale2 = bound( 0.0f, r_lighting_ambientscale->value, 1.0f ) * scale;

		for( i = 0; i < 3; i++ )
			ambient[i] = ambientLocal[i] * scale2;

		ambient[3] = 1.0f;
	}

	if( diffuse )
	{
		float scale2 = bound( 0.0f, r_lighting_directedscale->value, 1.0f ) * scale;

		for( i = 0; i < 3; i++ )
			diffuse[i] = diffuseLocal[i] * scale2;

		diffuse[3] = 1.0f;
	}
}
Пример #26
0
/*
=================
R_MarkFragments

=================
*/
int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
				   int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) {
	int				numsurfaces, numPlanes;
	int				i, j, k, m, n;
	surfaceType_t	*surfaces[64];
	int				surfacesBmodel[64];
	int				lastBmodel;
	vec3_t			mins, maxs;
	int				returnedFragments;
	int				returnedPoints;
	vec3_t			normals[MAX_VERTS_ON_POLY+2], localNormals[MAX_VERTS_ON_POLY+2];
	float			dists[MAX_VERTS_ON_POLY+2], localDists[MAX_VERTS_ON_POLY+2];
	vec3_t			clipPoints[2][MAX_VERTS_ON_POLY];
	int				numClipPoints;
	float			*v;
	srfBspSurface_t	*cv;
	glIndex_t		*tri;
	srfVert_t		*dv;
	vec3_t			normal;
	vec3_t			projectionDir, localProjectionDir;
	vec3_t			v1, v2;

	if (numPoints <= 0) {
		return 0;
	}

	//increment view count for double check prevention
	tr.viewCount++;

	//
	VectorNormalize2( projection, projectionDir );
	// find all the brushes that are to be considered
	ClearBounds( mins, maxs );
	for ( i = 0 ; i < numPoints ; i++ ) {
		vec3_t	temp;

		AddPointToBounds( points[i], mins, maxs );
		VectorAdd( points[i], projection, temp );
		AddPointToBounds( temp, mins, maxs );
		// make sure we get all the leafs (also the one(s) in front of the hit surface)
		VectorMA( points[i], -20, projectionDir, temp );
		AddPointToBounds( temp, mins, maxs );
	}

	if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY;
	// create the bounding planes for the to be projected polygon
	for ( i = 0 ; i < numPoints ; i++ ) {
		VectorSubtract(points[(i+1)%numPoints], points[i], v1);
		VectorAdd(points[i], projection, v2);
		VectorSubtract(points[i], v2, v2);
		CrossProduct(v1, v2, normals[i]);
		VectorNormalizeFast(normals[i]);
		dists[i] = DotProduct(normals[i], points[i]);
	}
	// add near and far clipping planes for projection
	VectorCopy(projectionDir, normals[numPoints]);
	dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32;
	VectorCopy(projectionDir, normals[numPoints+1]);
	VectorInverse(normals[numPoints+1]);
	dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20;
	numPlanes = numPoints + 2;

	numsurfaces = 0;
	R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, surfacesBmodel, 64, &numsurfaces, projectionDir);
	//assert(numsurfaces <= 64);
	//assert(numsurfaces != 64);

	// add bmodel surfaces
	for ( j = 1; j < tr.world->numBModels; j++ ) {
		vec3_t localProjection, bmodelOrigin, bmodelAxis[3];

		R_TransformMarkProjection( j, projection, localProjection, 0, NULL, NULL, NULL, NULL );
		R_GetBmodelInfo( j, NULL, bmodelOrigin, bmodelAxis );

		VectorNormalize2( localProjection, localProjectionDir );
		// find all the brushes that are to be considered
		ClearBounds( mins, maxs );
		for ( i = 0 ; i < numPoints ; i++ ) {
			vec3_t	temp;
			vec3_t	delta;
			vec3_t	localPoint;

			// convert point to bmodel local space
			VectorSubtract( points[i], bmodelOrigin, delta );
			localPoint[0] = DotProduct( delta, bmodelAxis[0] );
			localPoint[1] = DotProduct( delta, bmodelAxis[1] );
			localPoint[2] = DotProduct( delta, bmodelAxis[2] );

			AddPointToBounds( localPoint, mins, maxs );
			VectorAdd( localPoint, localProjection, temp );
			AddPointToBounds( temp, mins, maxs );
			// make sure we get all the leafs (also the one(s) in front of the hit surface)
			VectorMA( localPoint, -20, localProjectionDir, temp );
			AddPointToBounds( temp, mins, maxs );
		}

		R_BmodelSurfaces( j, mins, maxs, surfaces, surfacesBmodel, 64, &numsurfaces, localProjectionDir);
	}

	returnedPoints = 0;
	returnedFragments = 0;
	lastBmodel = -1;

	for ( i = 0 ; i < numsurfaces ; i++ ) {
		if (i == 0 || surfacesBmodel[i] != lastBmodel) {
			R_TransformMarkProjection( surfacesBmodel[i], projectionDir, localProjectionDir, numPlanes, normals, dists, localNormals, localDists );
			lastBmodel = surfacesBmodel[i];

			// don't use projectionDir, normals, or dists beyond this point !!!
			// mins and maxs are not setup, so they are not valid !!!
		}

		if (*surfaces[i] == SF_GRID) {

			cv = (srfBspSurface_t *) surfaces[i];
			for ( m = 0 ; m < cv->height - 1 ; m++ ) {
				for ( n = 0 ; n < cv->width - 1 ; n++ ) {
					// We triangulate the grid and chop all triangles within
					// the bounding planes of the to be projected polygon.
					// LOD is not taken into account, not such a big deal though.
					//
					// It's probably much nicer to chop the grid itself and deal
					// with this grid as a normal SF_GRID surface so LOD will
					// be applied. However the LOD of that chopped grid must
					// be synced with the LOD of the original curve.
					// One way to do this; the chopped grid shares vertices with
					// the original curve. When LOD is applied to the original
					// curve the unused vertices are flagged. Now the chopped curve
					// should skip the flagged vertices. This still leaves the
					// problems with the vertices at the chopped grid edges.
					//
					// To avoid issues when LOD applied to "hollow curves" (like
					// the ones around many jump pads) we now just add a 2 unit
					// offset to the triangle vertices.
					// The offset is added in the vertex normal vector direction
					// so all triangles will still fit together.
					// The 2 unit offset should avoid pretty much all LOD problems.

					numClipPoints = 3;

					dv = cv->verts + m * cv->width + n;

					VectorCopy(dv[0].xyz, clipPoints[0][0]);
					VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]);
					VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
					VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
					VectorCopy(dv[1].xyz, clipPoints[0][2]);
					VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]);
					// check the normal of this triangle
					VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
					VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
					CrossProduct(v1, v2, normal);
					VectorNormalizeFast(normal);
					if (DotProduct(normal, localProjectionDir) < -0.1) {
						// add the fragments of this triangle
						R_AddMarkFragments(numClipPoints, clipPoints,
										   numPlanes, localNormals, localDists,
										   maxPoints, pointBuffer,
										   maxFragments, fragmentBuffer,
										   &returnedPoints, &returnedFragments, mins, maxs, lastBmodel, localProjectionDir);

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;	// not enough space for more fragments
						}
					}

					VectorCopy(dv[1].xyz, clipPoints[0][0]);
					VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]);
					VectorCopy(dv[cv->width].xyz, clipPoints[0][1]);
					VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]);
					VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]);
					VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]);
					// check the normal of this triangle
					VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1);
					VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2);
					CrossProduct(v1, v2, normal);
					VectorNormalizeFast(normal);
					if (DotProduct(normal, localProjectionDir) < -0.05) {
						// add the fragments of this triangle
						R_AddMarkFragments(numClipPoints, clipPoints,
										   numPlanes, localNormals, localDists,
										   maxPoints, pointBuffer,
										   maxFragments, fragmentBuffer,
										   &returnedPoints, &returnedFragments, mins, maxs, lastBmodel, localProjectionDir);

						if ( returnedFragments == maxFragments ) {
							return returnedFragments;	// not enough space for more fragments
						}
					}
				}
			}
		}
		else if (*surfaces[i] == SF_FACE) {

			srfBspSurface_t *surf = ( srfBspSurface_t * ) surfaces[i];

			// check the normal of this face
			if (DotProduct(surf->cullPlane.normal, localProjectionDir) > -0.5) {
				continue;
			}

			for(k = 0, tri = surf->indexes; k < surf->numIndexes; k += 3, tri += 3)
			{
				for(j = 0; j < 3; j++)
				{
					v = surf->verts[tri[j]].xyz;
					VectorMA(v, MARKER_OFFSET, surf->cullPlane.normal, clipPoints[0][j]);
				}

				// add the fragments of this face
				R_AddMarkFragments( 3 , clipPoints,
								   numPlanes, localNormals, localDists,
								   maxPoints, pointBuffer,
								   maxFragments, fragmentBuffer,
								   &returnedPoints, &returnedFragments, mins, maxs, lastBmodel, localProjectionDir);
				if ( returnedFragments == maxFragments ) {
					return returnedFragments;	// not enough space for more fragments
				}
			}
		}
		else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) {

			srfBspSurface_t *surf = (srfBspSurface_t *) surfaces[i];

			for(k = 0, tri = surf->indexes; k < surf->numIndexes; k += 3, tri += 3)
			{
				for(j = 0; j < 3; j++)
				{
					v = surf->verts[tri[j]].xyz;
					VectorMA(v, MARKER_OFFSET, surf->verts[tri[j]].normal, clipPoints[0][j]);
				}

				// add the fragments of this face
				R_AddMarkFragments(3, clipPoints,
								   numPlanes, localNormals, localDists,
								   maxPoints, pointBuffer,
								   maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs, lastBmodel, localProjectionDir);
				if(returnedFragments == maxFragments)
				{
					return returnedFragments;	// not enough space for more fragments
				}
			}
		}
	}
	return returnedFragments;
}
Пример #27
0
void CCam::mortarCam(camInfo_t *ci)
{
	if (eth32.cg.snap->ps.ammo == 0)
		return;

// Set mortar trajectory from current view
	vec3_t angles, forward;
	VectorCopy(eth32.cg.refdef->viewaxis[ROLL], forward);
	VectorCopy(eth32.cg.snap->ps.viewangles, angles);
	angles[PITCH] -= 60.f;
	AngleVectors(angles, forward, NULL, NULL);
	forward[0] *= 3000 * 1.1f;
	forward[1] *= 3000 * 1.1f;
	forward[2] *= 1500 * 1.1f;

	trajectory_t mortarTrajectory;
	mortarTrajectory.trType = TR_GRAVITY;
	mortarTrajectory.trTime = eth32.cg.time;
	VectorCopy(eth32.cg.muzzle, mortarTrajectory.trBase);
	VectorCopy(forward, mortarTrajectory.trDelta);

	// Calculate mortar impact
	int timeOffset = 0;
	trace_t mortarTrace;
	vec3_t mortarImpact;
	VectorCopy(mortarTrajectory.trBase, mortarImpact);
	#define TIME_STEP 20
	while (timeOffset < 10000) {
		vec3_t nextPos;
		timeOffset += TIME_STEP;
		BG_EvaluateTrajectory(&mortarTrajectory, eth32.cg.time + timeOffset, nextPos, qfalse, 0);
		orig_CG_Trace(&mortarTrace, mortarImpact, 0, 0, nextPos, eth32.cg.snap->ps.clientNum, MASK_MISSILESHOT);
		if ((mortarTrace.fraction != 1)
				// Stop if we hit sky
				&& !((mortarTrace.surfaceFlags & SURF_NODRAW) || (mortarTrace.surfaceFlags & SURF_NOIMPACT))
				&& (mortarTrace.contents != 0)) {
			break;
		}
		VectorCopy(nextPos, mortarImpact);
	}

	memcpy(&camRefDef, &eth32.cg.refdef, sizeof(refdef_t));

	// kobject: add some angles
	vec3_t	dpos;
	vec3_t	camOrg;

	dpos[0] = eth32.cg.refdef->vieworg[0]-mortarImpact[0];
	dpos[1] = eth32.cg.refdef->vieworg[1]-mortarImpact[1];
	dpos[2] = 0.0f;
	VectorNormalizeFast( dpos );
	VectorCopy( mortarImpact, camOrg );
	VectorMA( camOrg, ci->distance * sinf(ci->angle * M_PI/180.0), zAxis, camOrg );
	VectorMA( camOrg, ci->distance * cosf(ci->angle * M_PI/180.0), dpos, camOrg );

	int w = ci->x2 - ci->x1;
	int h = ci->y2 - ci->y1;

	camRefDef.fov_x = (w>h) ? ci->fov : ci->fov * w / h;
	camRefDef.fov_y = (h>w) ? ci->fov : ci->fov * h / w;

	VectorCopy(camOrg, camRefDef.vieworg);

	vec3_t camAngle;
	VectorCopy(eth32.cg.refdefViewAngles, camAngle);
	camAngle[PITCH] = ci->angle;

	AnglesToAxis(camAngle, camRefDef.viewaxis);

	drawCam(ci->x1, ci->y1, w, h, &camRefDef, qtrue);

	// Draw impact time
	sprintf(this->str, "^7Impact Time: ^b%.1f ^7seconds", (float)timeOffset / 1000.0f);
	Draw.Text(ci->x1 + (w / 2) - (TEXTWIDTH(this->str) / 2), ci->y1 + h - 22 , 0.24f, str, GUI_FONTCOLOR1, qfalse, qtrue, &eth32.cg.media.fontArial, true);

}
Пример #28
0
void R_DrawSprite (void) {
    int i;
    msprite_t *psprite;
    vec3_t tvec;
    float dot, angle, sr, cr;

    VectorCopy (currententity->origin, r_entorigin);
    VectorSubtract (r_origin, r_entorigin, modelorg);

    psprite = currententity->model->extradata;

    r_spritedesc.pspriteframe = R_GetSpriteframe (psprite);

    sprite_width = r_spritedesc.pspriteframe->width;
    sprite_height = r_spritedesc.pspriteframe->height;

    // TODO: make this caller-selectable
    if (psprite->type == SPR_FACING_UPRIGHT) {
        // generate the sprite's axes, with vup straight up in worldspace, and
        // r_spritedesc.vright perpendicular to modelorg.
        // This will not work if the view direction is very close to straight up or
        // down, because the cross product will be between two nearly parallel
        // vectors and starts to approach an undefined state, so we don't draw if
        // the two vectors are less than 1 degree apart
        VectorNegate(modelorg, tvec);
        VectorNormalizeFast (tvec);
        dot = tvec[2];	// same as DotProduct (tvec, r_spritedesc.vup) because r_spritedesc.vup is 0, 0, 1
        if (dot > 0.999848 || dot < -0.999848)	// cos(1 degree) = 0.999848
            return;
        VectorSet(r_spritedesc.vup, 0, 0, 1);
        // CrossProduct(r_spritedesc.vup, -modelorg, r_spritedesc.vright)
        VectorSet(r_spritedesc.vright, tvec[1], -tvec[0], 0);
        VectorNormalizeFast (r_spritedesc.vright);
        // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, r_spritedesc.vpn)
        VectorSet(r_spritedesc.vpn, -r_spritedesc.vright[1], r_spritedesc.vright[0], 0);
    } else if (psprite->type == SPR_VP_PARALLEL) {
        // generate the sprite's axes, completely parallel to the viewplane. There
        // are no problem situations, because the sprite is always in the same
        // position relative to the viewer
        for (i = 0; i < 3; i++) {
            r_spritedesc.vup[i] = vup[i];
            r_spritedesc.vright[i] = vright[i];
            r_spritedesc.vpn[i] = vpn[i];
        }
    } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) {
        // generate the sprite's axes, with vup straight up in worldspace, and
        // r_spritedesc.vright parallel to the viewplane.
        // This will not work if the view direction is very close to straight up or
        // down, because the cross product will be between two nearly parallel
        // vectors and starts to approach an undefined state, so we don't draw if
        // the two vectors are less than 1 degree apart
        dot = vpn[2];	// same as DotProduct (vpn, r_spritedesc.vup) because r_spritedesc.vup is 0, 0, 1
        if ((dot > 0.999848) || (dot < -0.999848))	// cos(1 degree) = 0.999848
            return;
        VectorSet(r_spritedesc.vup, 0, 0, 1);
        //CrossProduct (r_spritedesc.vup, vpn, r_spritedesc.vright);
        VectorSet(r_spritedesc.vright, vpn[1], -vpn[0], 0);
        VectorNormalizeFast (r_spritedesc.vright);
        // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, r_spritedesc.vpn)
        VectorSet(r_spritedesc.vpn, -r_spritedesc.vright[1], r_spritedesc.vright[0], 0);
    } else if (psprite->type == SPR_ORIENTED) {
        // generate the sprite's axes, according to the sprite's world orientation
        AngleVectors (currententity->angles, r_spritedesc.vpn, r_spritedesc.vright, r_spritedesc.vup);
    } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) {
        // generate the sprite's axes, parallel to the viewplane, but rotated in
        // that plane around the center according to the sprite entity's roll
        // angle. So vpn stays the same, but vright and vup rotate
        angle = DEG2RAD(currententity->angles[ROLL]);
        sr = sin(angle);
        cr = cos(angle);

        for (i = 0; i < 3; i++) {
            r_spritedesc.vpn[i] = vpn[i];
            r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
            r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
        }
    } else {
        Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type);
    }

    R_RotateSprite (psprite->beamlength);

    R_SetupAndDrawSprite ();
}
Пример #29
0
// TODO: Clean this mess further (split into helper functions)
void G_Damage( gentity_t *target, gentity_t *inflictor, gentity_t *attacker,
               vec3_t dir, vec3_t point, int damage, int damageFlags, int mod )
{
	gclient_t *client;
	int       take, loss;
	int       knockback;
	float     modifier;

	if ( !target || !target->takedamage || target->health <= 0 || level.intermissionQueued )
	{
		return;
	}

	client = target->client;

	// don't handle noclip clients
	if ( client && client->noclip )
	{
		return;
	}

	// set inflictor to world if missing
	if ( !inflictor )
	{
		inflictor = &g_entities[ ENTITYNUM_WORLD ];
	}

	// set attacker to world if missing
	if ( !attacker )
	{
		attacker = &g_entities[ ENTITYNUM_WORLD ];
	}

	// don't handle ET_MOVER w/o die or pain function
	if ( target->s.eType == ET_MOVER && !( target->die || target->pain ) )
	{
		// special case for ET_MOVER with act function in initial position
		if ( ( target->moverState == MOVER_POS1 || target->moverState == ROTATOR_POS1 ) &&
		     target->act )
		{
			target->act( target, inflictor, attacker );
		}

		return;
	}

	// do knockback against clients
	if ( client && !( damageFlags & DAMAGE_NO_KNOCKBACK ) && dir )
	{
		// scale knockback by weapon
		if ( inflictor->s.weapon != WP_NONE )
		{
			knockback = ( int )( ( float )damage * BG_Weapon( inflictor->s.weapon )->knockbackScale );
		}
		else
		{
			knockback = damage;
		}

		// apply generic damage to knockback modifier
		knockback *= DAMAGE_TO_KNOCKBACK;

		// HACK: Too much knockback from falling makes you bounce and looks silly
		if ( mod == MOD_FALLING )
		{
			knockback = MIN( knockback, MAX_FALLDMG_KNOCKBACK );
		}

		G_KnockbackByDir( target, dir, knockback, qfalse );
	}
	else
	{
		// damage knockback gets saved, so initialize it here
		knockback = 0;
	}

	// godmode prevents damage
	if ( target->flags & FL_GODMODE )
	{
		return;
	}

	// check for protection
	if ( !( damageFlags & DAMAGE_NO_PROTECTION ) )
	{
		// check for protection from friendly damage
		if ( target != attacker && G_OnSameTeam( target, attacker ) )
		{
			// check if friendly fire has been disabled
			if ( !g_friendlyFire.integer )
			{
				return;
			}

			// don't do friendly damage on movement attacks
			switch ( mod )
			{
				case MOD_LEVEL3_POUNCE:
				case MOD_LEVEL4_TRAMPLE:
					return;

				default:
					break;
			}

			// if dretchpunt is enabled and this is a dretch, do dretchpunt instead of damage
			if ( g_dretchPunt.integer && target->client &&
			     ( target->client->ps.stats[ STAT_CLASS ] == PCL_ALIEN_LEVEL0 ||
			       target->client->ps.stats[ STAT_CLASS ] == PCL_ALIEN_LEVEL0_UPG ) )
			{
				vec3_t dir, push;

				VectorSubtract( target->r.currentOrigin, attacker->r.currentOrigin, dir );
				VectorNormalizeFast( dir );
				VectorScale( dir, ( damage * 10.0f ), push );
				push[ 2 ] = 64.0f;

				VectorAdd( target->client->ps.velocity, push, target->client->ps.velocity );

				return;
			}
		}

		// for buildables, never protect from damage dealt by building actions
		if ( target->s.eType == ET_BUILDABLE && attacker->client &&
		     mod != MOD_DECONSTRUCT && mod != MOD_SUICIDE &&
		     mod != MOD_REPLACE     && mod != MOD_NOCREEP )
		{
			// check for protection from friendly buildable damage
			if ( G_OnSameTeam( target, attacker ) && !g_friendlyBuildableFire.integer )
			{
				return;
			}
		}
	}

	// update combat timers
	if ( target->client && attacker->client && target != attacker )
	{
		target->client->lastCombatTime   = level.time;
		attacker->client->lastCombatTime = level.time;
	}

	if ( client )
	{
		// save damage (w/o armor modifier), knockback
		client->damage_received  += damage;
		client->damage_knockback += knockback;

		// save damage direction
		if ( dir )
		{
			VectorCopy( dir, client->damage_from );
			client->damage_fromWorld = qfalse;
		}
		else
		{
			VectorCopy( target->r.currentOrigin, client->damage_from );
			client->damage_fromWorld = qtrue;
		}

		// drain jetpack fuel
		client->ps.stats[ STAT_FUEL ] -= damage * JETPACK_FUEL_PER_DMG;
		if ( client->ps.stats[ STAT_FUEL ] < 0 )
		{
			client->ps.stats[ STAT_FUEL ] = 0;
		}

		// apply damage modifier
		modifier = CalcDamageModifier( point, target, (class_t) client->ps.stats[ STAT_CLASS ], damageFlags );
		take = ( int )( ( float )damage * modifier + 0.5f );

		// if boosted poison every attack
		if ( attacker->client &&
		     ( attacker->client->ps.stats[ STAT_STATE ] & SS_BOOSTED ) &&
		     target->client->pers.team == TEAM_HUMANS &&
		     target->client->poisonImmunityTime < level.time )
		{
			switch ( mod )
			{
				case MOD_POISON:
				case MOD_LEVEL1_PCLOUD:
				case MOD_LEVEL2_ZAP:
					break;

				default:
					target->client->ps.stats[ STAT_STATE ] |= SS_POISONED;
					target->client->lastPoisonTime   = level.time;
					target->client->lastPoisonClient = attacker;
			}
		}
	}
	else
	{
		take = damage;
	}

	// make sure damage is done
	if ( take < 1 )
	{
		take = 1;
	}

	if ( g_debugDamage.integer > 0 )
	{
		G_Printf( "G_Damage: %3i (%3i → %3i)\n",
		          take, target->health, target->health - take );
	}

	// do the damage
	target->health = target->health - take;

	if ( target->client )
	{
		target->client->ps.stats[ STAT_HEALTH ] = target->health;
		target->client->pers.infoChangeTime = level.time; // ?
	}

	target->lastDamageTime = level.time;

	// TODO: gentity_t->nextRegenTime only affects alien clients, remove it and use lastDamageTime
	// Optionally (if needed for some reason), move into client struct and add "Alien" to name
	target->nextRegenTime = level.time + ALIEN_CLIENT_REGEN_WAIT;

	// handle non-self damage
	if ( attacker != target )
	{
		if ( target->health < 0 )
		{
			loss = ( take + target->health );
		}
		else
		{
			loss = take;
		}

		if ( attacker->client )
		{
			// add to the attacker's account on the target
			target->credits[ attacker->client->ps.clientNum ] += ( float )loss;

			// notify the attacker of a hit
			NotifyClientOfHit( attacker );
		}

		// update buildable stats
		if ( attacker->s.eType == ET_BUILDABLE && attacker->health > 0 )
		{
			attacker->buildableStatsTotal += loss;
		}
	}

	// handle dying target
	if ( target->health <= 0 )
	{
		// set no knockback flag for clients
		if ( client )
		{
			target->flags |= FL_NO_KNOCKBACK;
		}

		// cap negative health
		if ( target->health < -999 )
		{
			target->health = -999;
		}

		// call die function
		if ( target->die )
		{
			target->die( target, inflictor, attacker, mod );
		}

		// update buildable stats
		if ( attacker->s.eType == ET_BUILDABLE && attacker->health > 0 )
		{
			attacker->buildableStatsCount++;
		}

		// for non-client victims, fire ON_DIE event
		if( !target->client )
		{
			G_EventFireEntity( target, attacker, ON_DIE );
		}

		return;
	}
	else if ( target->pain )
	{
		target->pain( target, attacker, take );
	}
}
Пример #30
0
/**
 * @brief Calculates normals and tangents for all frames and does vertex merging based on smoothness
 * @param mesh The mesh to calculate normals for
 * @param nFrames How many frames the mesh has
 * @param smoothness How aggressively should normals be smoothed; value is compared with dotproduct of vectors to decide if they should be merged
 * @sa R_ModCalcNormalsAndTangents
 */
void R_ModCalcUniqueNormalsAndTangents (mAliasMesh_t *mesh, int nFrames, float smoothness)
{
	int i, j;
	vec3_t triangleNormals[MAX_ALIAS_TRIS];
	vec3_t triangleTangents[MAX_ALIAS_TRIS];
	vec3_t triangleBitangents[MAX_ALIAS_TRIS];
	const mAliasVertex_t *vertexes = mesh->vertexes;
	mAliasCoord_t *stcoords = mesh->stcoords;
	mAliasVertex_t *newVertexes;
	mAliasComplexVertex_t tmpVertexes[MAX_ALIAS_VERTS];
	vec3_t tmpBitangents[MAX_ALIAS_VERTS];
	mAliasCoord_t *newStcoords;
	const int numIndexes = mesh->num_tris * 3;
	const int32_t *indexArray = mesh->indexes;
	int32_t *newIndexArray;
	int indRemap[MAX_ALIAS_VERTS];
	int sharedTris[MAX_ALIAS_VERTS];
	int numVerts = 0;

	newIndexArray = (int32_t *)Mem_PoolAlloc(sizeof(int32_t) * numIndexes, vid_modelPool, 0);

	/* calculate per-triangle surface normals */
	for (i = 0, j = 0; i < numIndexes; i += 3, j++) {
		vec3_t dir1, dir2;
		vec2_t dir1uv, dir2uv;

		/* calculate two mostly perpendicular edge directions */
		VectorSubtract(vertexes[indexArray[i + 0]].point, vertexes[indexArray[i + 1]].point, dir1);
		VectorSubtract(vertexes[indexArray[i + 2]].point, vertexes[indexArray[i + 1]].point, dir2);
		Vector2Subtract(stcoords[indexArray[i + 0]], stcoords[indexArray[i + 1]], dir1uv);
		Vector2Subtract(stcoords[indexArray[i + 2]], stcoords[indexArray[i + 1]], dir2uv);

		/* we have two edge directions, we can calculate a third vector from
		 * them, which is the direction of the surface normal */
		CrossProduct(dir1, dir2, triangleNormals[j]);

		/* then we use the texture coordinates to calculate a tangent space */
		if ((dir1uv[1] * dir2uv[0] - dir1uv[0] * dir2uv[1]) != 0.0) {
			const float frac = 1.0 / (dir1uv[1] * dir2uv[0] - dir1uv[0] * dir2uv[1]);
			vec3_t tmp1, tmp2;

			/* calculate tangent */
			VectorMul(-1.0 * dir2uv[1] * frac, dir1, tmp1);
			VectorMul(dir1uv[1] * frac, dir2, tmp2);
			VectorAdd(tmp1, tmp2, triangleTangents[j]);

			/* calculate bitangent */
			VectorMul(-1.0 * dir2uv[0] * frac, dir1, tmp1);
			VectorMul(dir1uv[0] * frac, dir2, tmp2);
			VectorAdd(tmp1, tmp2, triangleBitangents[j]);
		} else {
			const float frac = 1.0 / (0.00001);
			vec3_t tmp1, tmp2;

			/* calculate tangent */
			VectorMul(-1.0 * dir2uv[1] * frac, dir1, tmp1);
			VectorMul(dir1uv[1] * frac, dir2, tmp2);
			VectorAdd(tmp1, tmp2, triangleTangents[j]);

			/* calculate bitangent */
			VectorMul(-1.0 * dir2uv[0] * frac, dir1, tmp1);
			VectorMul(dir1uv[0] * frac, dir2, tmp2);
			VectorAdd(tmp1, tmp2, triangleBitangents[j]);
		}

		/* normalize */
		VectorNormalizeFast(triangleNormals[j]);
		VectorNormalizeFast(triangleTangents[j]);
		VectorNormalizeFast(triangleBitangents[j]);

		Orthogonalize(triangleTangents[j], triangleBitangents[j]);
	}

	/* do smoothing */
	for (i = 0; i < numIndexes; i++) {
		const int idx = (i - i % 3) / 3;
		VectorCopy(triangleNormals[idx], tmpVertexes[i].normal);
		VectorCopy(triangleTangents[idx], tmpVertexes[i].tangent);
		VectorCopy(triangleBitangents[idx], tmpBitangents[i]);

		for (j = 0; j < numIndexes; j++) {
			const int idx2 = (j - j % 3) / 3;
			/* don't add a vertex with itself */
			if (j == i)
				continue;

			/* only average normals if vertices have the same position
			 * and the normals aren't too far apart to start with */
			if (VectorEqual(vertexes[indexArray[i]].point, vertexes[indexArray[j]].point)
					&& DotProduct(triangleNormals[idx], triangleNormals[idx2]) > smoothness) {
				/* average the normals */
				VectorAdd(tmpVertexes[i].normal, triangleNormals[idx2], tmpVertexes[i].normal);

				/* if the tangents match as well, average them too.
				 * Note that having matching normals without matching tangents happens
				 * when the order of vertices in two triangles sharing the vertex
				 * in question is different.  This happens quite frequently if the
				 * modeler does not go out of their way to avoid it. */

				if (Vector2Equal(stcoords[indexArray[i]], stcoords[indexArray[j]])
						&& DotProduct(triangleTangents[idx], triangleTangents[idx2]) > smoothness
						&& DotProduct(triangleBitangents[idx], triangleBitangents[idx2]) > smoothness) {
					/* average the tangents */
					VectorAdd(tmpVertexes[i].tangent, triangleTangents[idx2], tmpVertexes[i].tangent);
					VectorAdd(tmpBitangents[i], triangleBitangents[idx2], tmpBitangents[i]);
				}
			}
		}

		VectorNormalizeFast(tmpVertexes[i].normal);
		VectorNormalizeFast(tmpVertexes[i].tangent);
		VectorNormalizeFast(tmpBitangents[i]);
	}

	/* assume all vertices are unique until proven otherwise */
	for (i = 0; i < numIndexes; i++)
		indRemap[i] = -1;

	/* merge vertices that have become identical */
	for (i = 0; i < numIndexes; i++) {
		vec3_t n, b, t, v;
		if (indRemap[i] != -1)
			continue;

		for (j = i + 1; j < numIndexes; j++) {
			if (Vector2Equal(stcoords[indexArray[i]], stcoords[indexArray[j]])
					&& VectorEqual(vertexes[indexArray[i]].point, vertexes[indexArray[j]].point)
					&& (DotProduct(tmpVertexes[i].normal, tmpVertexes[j].normal) > smoothness)
					&& (DotProduct(tmpVertexes[i].tangent, tmpVertexes[j].tangent) > smoothness)) {
				indRemap[j] = i;
				newIndexArray[j] = numVerts;
			}
		}

		VectorCopy(tmpVertexes[i].normal, n);
		VectorCopy(tmpVertexes[i].tangent, t);
		VectorCopy(tmpBitangents[i], b);

		/* normalization here does shared-vertex smoothing */
		VectorNormalizeFast(n);
		VectorNormalizeFast(t);
		VectorNormalizeFast(b);

		/* Grahm-Schmidt orthogonalization */
		VectorMul(DotProduct(t, n), n, v);
		VectorSubtract(t, v, t);
		VectorNormalizeFast(t);

		/* calculate handedness */
		CrossProduct(n, t, v);
		tmpVertexes[i].tangent[3] = (DotProduct(v, b) < 0.0) ? -1.0 : 1.0;
		VectorCopy(n, tmpVertexes[i].normal);
		VectorCopy(t, tmpVertexes[i].tangent);

		newIndexArray[i] = numVerts++;
		indRemap[i] = i;
	}

	for (i = 0; i < numVerts; i++)
		sharedTris[i] = 0;

	for (i = 0; i < numIndexes; i++)
		sharedTris[newIndexArray[i]]++;

	/* set up reverse-index that maps Vertex objects to a list of triangle verts */
	mesh->revIndexes = (mIndexList_t *)Mem_PoolAlloc(sizeof(mIndexList_t) * numVerts, vid_modelPool, 0);
	for (i = 0; i < numVerts; i++) {
		mesh->revIndexes[i].length = 0;
		mesh->revIndexes[i].list = (int32_t *)Mem_PoolAlloc(sizeof(int32_t) * sharedTris[i], vid_modelPool, 0);
	}

	/* merge identical vertexes, storing only unique ones */
	newVertexes = (mAliasVertex_t *)Mem_PoolAlloc(sizeof(mAliasVertex_t) * numVerts * nFrames, vid_modelPool, 0);
	newStcoords = (mAliasCoord_t *)Mem_PoolAlloc(sizeof(mAliasCoord_t) * numVerts, vid_modelPool, 0);
	for (i = 0; i < numIndexes; i++) {
		const int idx = indexArray[indRemap[i]];
		const int idx2 = newIndexArray[i];

		/* add vertex to new vertex array */
		VectorCopy(vertexes[idx].point, newVertexes[idx2].point);
		Vector2Copy(stcoords[idx], newStcoords[idx2]);
		mesh->revIndexes[idx2].list[mesh->revIndexes[idx2].length++] = i;
	}

	/* copy over the points from successive frames */
	for (i = 1; i < nFrames; i++) {
		for (j = 0; j < numIndexes; j++) {
			const int idx = indexArray[indRemap[j]] + (mesh->num_verts * i);
			const int idx2 = newIndexArray[j] + (numVerts * i);

			VectorCopy(vertexes[idx].point, newVertexes[idx2].point);
		}
	}

	/* copy new arrays back into original mesh */
	Mem_Free(mesh->stcoords);
	Mem_Free(mesh->indexes);
	Mem_Free(mesh->vertexes);

	mesh->num_verts = numVerts;
	mesh->vertexes = newVertexes;
	mesh->stcoords = newStcoords;
	mesh->indexes = newIndexArray;
}