static void _LaserImpact( trace_t *trace, vec3_t dir ) { if( !trace || trace->ent < 0 ) return; if( cg_particles->integer && laserOwner ) { #define TRAILTIME ( (int)( 1000.0f / 20.0f ) ) // density as quantity per second if( laserOwner->localEffects[LOCALEFFECT_LASERBEAM_SMOKE_TRAIL] + TRAILTIME < cg.time ) { laserOwner->localEffects[LOCALEFFECT_LASERBEAM_SMOKE_TRAIL] = cg.time; CG_ImpactSmokePuff( trace->endpos, trace->plane.normal, 3, 1.0f, 8, 12 ); } #undef TRAILTIME } // it's a brush model if( trace->ent == 0 || !( cg_entities[trace->ent].current.effects & EF_TAKEDAMAGE ) ) { CG_AddLightToScene( trace->endpos, 100, 0.75f, 0.75f, 0.375f, NULL ); // TODO: add impact model return; } // it's a player // TODO: add player-impact model }
/* * CG_AddWeaponOnTag * * Add weapon model(s) positioned at the tag */ void CG_AddWeaponOnTag( entity_t *ent, orientation_t *tag, int weaponid, int effects, orientation_t *projectionSource, unsigned int flash_time, unsigned int barrel_time ) { entity_t weapon; weaponinfo_t *weaponInfo; float intensity; //don't try without base model if( !ent->model ) return; //don't try without a tag if( !tag ) return; weaponInfo = CG_GetWeaponInfo( weaponid ); if( !weaponInfo ) return; //if( ent->renderfx & RF_WEAPONMODEL ) // effects &= ~EF_RACEGHOST; //weapon memset( &weapon, 0, sizeof( weapon ) ); Vector4Set( weapon.shaderRGBA, 255, 255, 255, 255 ); weapon.scale = ent->scale; weapon.renderfx = ent->renderfx; weapon.frame = 0; weapon.oldframe = 0; weapon.model = weaponInfo->model[WEAPON]; CG_PlaceModelOnTag( &weapon, ent, tag ); CG_AddColoredOutLineEffect( &weapon, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &weapon ); if( !weapon.model ) return; CG_AddShellEffects( &weapon, effects ); // update projection source if( projectionSource != NULL ) { VectorCopy( vec3_origin, projectionSource->origin ); Matrix3_Copy( axis_identity, projectionSource->axis ); CG_MoveToTag( projectionSource->origin, projectionSource->axis, weapon.origin, weapon.axis, weaponInfo->tag_projectionsource.origin, weaponInfo->tag_projectionsource.axis ); } //expansion if( ( effects & EF_STRONG_WEAPON ) && weaponInfo->model[EXPANSION] ) { if( CG_GrabTag( tag, &weapon, "tag_expansion" ) ) { entity_t expansion; memset( &expansion, 0, sizeof( expansion ) ); Vector4Set( expansion.shaderRGBA, 255, 255, 255, 255 ); expansion.model = weaponInfo->model[EXPANSION]; expansion.scale = ent->scale; expansion.renderfx = ent->renderfx; expansion.frame = 0; expansion.oldframe = 0; CG_PlaceModelOnTag( &expansion, &weapon, tag ); CG_AddColoredOutLineEffect( &expansion, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &expansion ); // skelmod CG_AddShellEffects( &expansion, effects ); } } // barrel if( weaponInfo->model[BARREL] ) { if( CG_GrabTag( tag, &weapon, "tag_barrel" ) ) { orientation_t barrel_recoiled; vec3_t rotangles = { 0, 0, 0 }; entity_t barrel; memset( &barrel, 0, sizeof( barrel ) ); Vector4Set( barrel.shaderRGBA, 255, 255, 255, 255 ); barrel.model = weaponInfo->model[BARREL]; barrel.scale = ent->scale; barrel.renderfx = ent->renderfx; barrel.frame = 0; barrel.oldframe = 0; // rotation if( barrel_time > cg.time ) { intensity = (float)( barrel_time - cg.time ) / (float)weaponInfo->barrelTime; rotangles[2] = ( 360.0f * weaponInfo->barrelSpeed * intensity * intensity ); while( rotangles[2] > 360 ) rotangles[2] -= 360; // Check for tag_recoil if( CG_GrabTag( &barrel_recoiled, &weapon, "tag_recoil" ) ) VectorLerp( tag->origin, intensity, barrel_recoiled.origin, tag->origin ); } AnglesToAxis( rotangles, barrel.axis ); // barrel requires special tagging CG_PlaceRotatedModelOnTag( &barrel, &weapon, tag ); CG_AddColoredOutLineEffect( &barrel, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &barrel ); // skelmod CG_AddShellEffects( &barrel, effects ); } } if( flash_time < cg.time ) return; // flash if( !CG_GrabTag( tag, &weapon, "tag_flash" ) ) return; if( weaponInfo->model[FLASH] ) { entity_t flash; qbyte c; if( weaponInfo->flashFade ) { intensity = (float)( flash_time - cg.time )/(float)weaponInfo->flashTime; c = ( qbyte )( 255 * intensity ); } else { intensity = 1.0f; c = 255; } memset( &flash, 0, sizeof( flash ) ); Vector4Set( flash.shaderRGBA, c, c, c, c ); flash.model = weaponInfo->model[FLASH]; flash.scale = ent->scale; flash.renderfx = ent->renderfx | RF_NOSHADOW; flash.frame = 0; flash.oldframe = 0; CG_PlaceModelOnTag( &flash, &weapon, tag ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &flash ); CG_AddLightToScene( flash.origin, weaponInfo->flashRadius * intensity, weaponInfo->flashColor[0], weaponInfo->flashColor[1], weaponInfo->flashColor[2] ); } }
/* * CG_AddLocalEntities */ void CG_AddLocalEntities( void ) { #define FADEINFRAMES 2 int f; lentity_t *le, *next, *hnode; entity_t *ent; float scale, frac, fade, time, scaleIn, fadeIn; float backlerp; vec3_t angles; time = cg.frameTime; backlerp = 1.0f - cg.lerpfrac; hnode = &cg_localents_headnode; for( le = hnode->next; le != hnode; le = next ) { next = le->next; frac = ( cg.time - le->start ) * 0.01f; f = ( int )floor( frac ); clamp_low( f, 0 ); // it's time to DIE if( f >= le->frames - 1 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); continue; } if( le->frames > 1 ) { scale = 1.0f - frac / ( le->frames - 1 ); scale = bound( 0.0f, scale, 1.0f ); fade = scale * 255.0f; // quick fade in, if time enough if( le->frames > FADEINFRAMES * 2 ) { scaleIn = frac / (float)FADEINFRAMES; clamp( scaleIn, 0.0f, 1.0f ); fadeIn = scaleIn * 255.0f; } else fadeIn = 255.0f; } else { scale = 1.0f; fade = 255.0f; fadeIn = 255.0f; } ent = &le->ent; if( le->light && scale ) CG_AddLightToScene( ent->origin, le->light * scale, le->lightcolor[0], le->lightcolor[1], le->lightcolor[2], NULL ); if( le->type == LE_LASER ) { CG_QuickPolyBeam( ent->origin, ent->origin2, ent->radius, ent->customShader ); // wsw : jalfixme: missing the color (comes inside ent->skinnum) continue; } if( le->type == LE_DASH_SCALE ) { if( f < 1 ) ent->scale = 0.2 * frac; else { VecToAngles( ent->axis[1], angles ); ent->axis[1][1] += 0.005f *sin( DEG2RAD ( angles[YAW] ) ); //length ent->axis[1][0] += 0.005f *cos( DEG2RAD ( angles[YAW] ) ); //length ent->axis[0][1] += 0.008f *cos( DEG2RAD ( angles[YAW] ) ); //width ent->axis[0][0] -= 0.008f *sin( DEG2RAD ( angles[YAW] ) ); //width ent->axis[2][2] -= 0.052f; //height if( ent->axis[2][2] <= 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } if( le->type == LE_DASH_SCALE_2 ) { if( f < 1 ) ent->scale = 0.25 * frac; else { VecToAngles( ent->axis[1], angles ); ent->axis[1][1] += 0.005f *sin( DEG2RAD ( angles[YAW] ) ); //length ent->axis[1][0] += 0.005f *cos( DEG2RAD ( angles[YAW] ) ); //length ent->axis[0][1] += 0.008f *cos( DEG2RAD ( angles[YAW] ) ); //width ent->axis[0][0] -= 0.008f *sin( DEG2RAD ( angles[YAW] ) ); //width ent->axis[2][2] -= 0.018f; //height ent->origin[1] -= 0.6f *sin( DEG2RAD ( angles[YAW] ) ); //velocity ent->origin[0] -= 0.6f *cos( DEG2RAD ( angles[YAW] ) ); //velocity if( ent->axis[2][2] <= 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } if( le->type == LE_PUFF_SCALE ) { if( frac < 1 ) ent->scale = 7.0f*frac; else ent->scale = 7.0f - 4.0f*( frac-1 ); if( ent->scale < 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } if( le->type == LE_PUFF_SCALE_2 ) { if( le->frames - f < 4 ) ent->scale = 1.0f - 1.0f * ( frac - abs( 4-le->frames ) )/4; } if( le->type == LE_PUFF_SHRINK ) { if( frac < 3 ) ent->scale = 1.0f - 0.2f * frac/4; else { ent->scale = 0.8 - 0.8*( frac-3 )/3; VectorScale( le->velocity, 0.85f, le->velocity ); } } if( le->type == LE_EXPLOSION_TRACER ) { if( cg.time - ent->rotation > 10.0f ) { ent->rotation = cg.time; if( ent->radius - 16*frac > 4 ) CG_Explosion_Puff( ent->origin, ent->radius-16*frac, le->frames - f ); } } switch( le->type ) { case LE_NO_FADE: break; case LE_RGB_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[0] = ( qbyte )( fade * le->color[0] ); ent->shaderRGBA[1] = ( qbyte )( fade * le->color[1] ); ent->shaderRGBA[2] = ( qbyte )( fade * le->color[2] ); break; case LE_SCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = 1.0f + 1.0f / scale; ent->scale = min( ent->scale, 5.0f ); ent->shaderRGBA[3] = ( qbyte )( fade * le->color[3] ); break; case LE_INVERSESCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = scale + 0.1f; clamp( ent->scale, 0.1f, 1.0f ); ent->shaderRGBA[3] = ( qbyte )( fade * le->color[3] ); break; case LE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[3] = ( qbyte )( fade * le->color[3] ); break; default: break; } ent->backlerp = backlerp; if( le->bounce ) { trace_t trace; vec3_t next_origin; VectorMA( ent->origin, time, le->velocity, next_origin ); CG_Trace( &trace, ent->origin, debris_mins, debris_maxs, next_origin, 0, MASK_SOLID ); // remove the particle when going out of the map if( ( trace.contents & CONTENTS_NODROP ) || ( trace.surfFlags & SURF_SKY ) ) { le->frames = 0; } else if( trace.fraction != 1.0 ) // found solid { float dot; vec3_t vel; float xzyspeed; // Reflect velocity VectorSubtract( next_origin, ent->origin, vel ); dot = -2 *DotProduct( vel, trace.plane.normal ); VectorMA( vel, dot, trace.plane.normal, le->velocity ); //put new origin in the impact point, but move it out a bit along the normal VectorMA( trace.endpos, 1, trace.plane.normal, ent->origin ); //the entity has not speed enough. Stop checks xzyspeed = sqrt( le->velocity[0]*le->velocity[0] + le->velocity[1]*le->velocity[1] + le->velocity[2]*le->velocity[2] ); if( xzyspeed * time < 1.0f ) { trace_t traceground; vec3_t ground_origin; //see if we have ground VectorCopy( ent->origin, ground_origin ); ground_origin[2] += ( debris_mins[2] - 4 ); CG_Trace( &traceground, ent->origin, debris_mins, debris_maxs, ground_origin, 0, MASK_SOLID ); if( traceground.fraction != 1.0 ) { le->bounce = qfalse; VectorClear( le->velocity ); VectorClear( le->accel ); if( le->type == LE_EXPLOSION_TRACER ) { // blx le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } else VectorScale( le->velocity, le->bounce * time, le->velocity ); } else { VectorCopy( ent->origin, ent->origin2 ); VectorCopy( next_origin, ent->origin ); } } else { VectorCopy( ent->origin, ent->origin2 ); VectorMA( ent->origin, time, le->velocity, ent->origin ); } VectorCopy( ent->origin, ent->lightingOrigin ); VectorMA( le->velocity, time, le->accel, le->velocity ); CG_AddEntityToScene( ent ); } }
/* * CG_AddLocalEntities */ void CG_AddLocalEntities( void ) { #define FADEINFRAMES 2 int f; lentity_t *le, *next, *hnode; entity_t *ent; float scale, frac, fade, time, scaleIn, fadeIn; float backlerp; vec3_t angles; time = cg.frameTime; backlerp = 1.0f - cg.lerpfrac; hnode = &cg_localents_headnode; for( le = hnode->next; le != hnode; le = next ) { next = le->next; frac = ( cg.time - le->start ) * 0.01f; f = ( int )floor( frac ); clamp_low( f, 0 ); // it's time to DIE if( f >= le->frames - 1 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); continue; } if( le->frames > 1 ) { scale = 1.0f - frac / ( le->frames - 1 ); scale = bound( 0.0f, scale, 1.0f ); fade = scale * 255.0f; // quick fade in, if time enough if( le->frames > FADEINFRAMES * 2 ) { scaleIn = frac / (float)FADEINFRAMES; clamp( scaleIn, 0.0f, 1.0f ); fadeIn = scaleIn * 255.0f; } else fadeIn = 255.0f; } else { scale = 1.0f; fade = 255.0f; fadeIn = 255.0f; } ent = &le->ent; if( le->light && scale ) CG_AddLightToScene( ent->origin, le->light * scale, le->lightcolor[0], le->lightcolor[1], le->lightcolor[2] ); if( le->type == LE_LASER ) { CG_QuickPolyBeam( ent->origin, ent->origin2, ent->radius, ent->customShader ); // wsw : jalfixme: missing the color (comes inside ent->skinnum) continue; } if( le->type == LE_DASH_SCALE ) { if( f < 1 ) ent->scale = 0.15 * frac; else { VecToAngles( &ent->axis[AXIS_RIGHT], angles ); ent->axis[1*3+1] += 0.005f *sin( DEG2RAD ( angles[YAW] ) ); //length ent->axis[1*3+0] += 0.005f *cos( DEG2RAD ( angles[YAW] ) ); //length ent->axis[0*3+1] += 0.008f *cos( DEG2RAD ( angles[YAW] ) ); //width ent->axis[0*3+0] -= 0.008f *sin( DEG2RAD ( angles[YAW] ) ); //width ent->axis[2*3+2] -= 0.052f; //height if( ent->axis[AXIS_UP+2] <= 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } if( le->type == LE_PUFF_SCALE ) { if( le->frames - f < 4 ) ent->scale = 1.0f - 1.0f * ( frac - abs( 4-le->frames ) )/4; } if( le->type == LE_PUFF_SHRINK ) { if( frac < 3 ) ent->scale = 1.0f - 0.2f * frac/4; else { ent->scale = 0.8 - 0.8*( frac-3 )/3; VectorScale( le->velocity, 0.85f, le->velocity ); } } if( le->type == LE_EXPLOSION_TRACER ) { if( cg.time - ent->rotation > 10.0f ) { ent->rotation = cg.time; if( ent->radius - 16*frac > 4 ) CG_Explosion_Puff( ent->origin, ent->radius-16*frac, le->frames - f ); } } switch( le->type ) { case LE_NO_FADE: break; case LE_RGB_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[0] = ( uint8_t )( fade * le->color[0] ); ent->shaderRGBA[1] = ( uint8_t )( fade * le->color[1] ); ent->shaderRGBA[2] = ( uint8_t )( fade * le->color[2] ); break; case LE_SCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = 1.0f + 1.0f / scale; ent->scale = min( ent->scale, 5.0f ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; case LE_INVERSESCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = scale + 0.1f; clamp( ent->scale, 0.1f, 1.0f ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; case LE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; default: break; } ent->backlerp = backlerp; if ( le->avelocity[0] || le->avelocity[1] || le->avelocity[2] ) { VectorMA( le->angles, time, le->avelocity, le->angles ); AnglesToAxis( le->angles, le->ent.axis ); } // apply rotational friction if( le->bounce ) { // FIXME? int i; const float adj = 100 * 6 * time; // magic constants here for( i = 0; i < 3; i++ ) { if( le->avelocity[i] > 0.0f ) { le->avelocity[i] -= adj; if( le->avelocity[i] < 0.0f ) { le->avelocity[i] = 0.0f; } } else if ( le->avelocity[i] < 0.0f ) { le->avelocity[i] += adj; if ( le->avelocity[i] > 0.0f ) { le->avelocity[i] = 0.0f; } } } } if( le->bounce ) { trace_t trace; vec3_t next_origin; VectorMA( ent->origin, time, le->velocity, next_origin ); CG_Trace( &trace, ent->origin, debris_mins, debris_maxs, next_origin, 0, MASK_SOLID ); // remove the particle when going out of the map if( ( trace.contents & CONTENTS_NODROP ) || ( trace.surfFlags & SURF_SKY ) ) { le->frames = 0; } else if( trace.fraction != 1.0 ) // found solid { float dot; float xyzspeed, orig_xyzspeed; float bounce; orig_xyzspeed = VectorLength( le->velocity ); // Reflect velocity dot = DotProduct( le->velocity, trace.plane.normal ); VectorMA( le->velocity, -2.0f * dot, trace.plane.normal, le->velocity ); //put new origin in the impact point, but move it out a bit along the normal VectorMA( trace.endpos, 1, trace.plane.normal, ent->origin ); // make sure we don't gain speed from bouncing off bounce = 2.0f * le->bounce * 0.01f; if( bounce < 1.5f ) bounce = 1.5f; xyzspeed = orig_xyzspeed / bounce; VectorNormalize( le->velocity ); VectorScale( le->velocity, xyzspeed, le->velocity ); //the entity has not speed enough. Stop checks if( xyzspeed * time < 1.0f ) { trace_t traceground; vec3_t ground_origin; //see if we have ground VectorCopy( ent->origin, ground_origin ); ground_origin[2] += ( debris_mins[2] - 4 ); CG_Trace( &traceground, ent->origin, debris_mins, debris_maxs, ground_origin, 0, MASK_SOLID ); if( traceground.fraction != 1.0 ) { le->bounce = 0; VectorClear( le->velocity ); VectorClear( le->accel ); VectorClear( le->avelocity ); if( le->type == LE_EXPLOSION_TRACER ) { // blx le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } } else { VectorCopy( ent->origin, ent->origin2 ); VectorCopy( next_origin, ent->origin ); } } else { VectorCopy( ent->origin, ent->origin2 ); VectorMA( ent->origin, time, le->velocity, ent->origin ); } VectorCopy( ent->origin, ent->lightingOrigin ); VectorMA( le->velocity, time, le->accel, le->velocity ); CG_AddEntityToScene( ent ); } }