/* * ================ * CG_AddExplosion * ================ */ static void CG_AddExplosion(localEntity_t *ex) { refEntity_t *ent; ent = &ex->refEntity; // add the entity trap_R_AddRefEntityToScene(ent); // add the dlight if (ex->light) { float light; light = (float)(cg.time - ex->startTime) / (ex->endTime - ex->startTime); if (light < 0.5) { light = 1.0; } else { light = 1.0 - (light - 0.5) * 2; } light = ex->light * light; trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2]); } }
/* * ================ * CG_ReflectVelocity * ================ */ void CG_ReflectVelocity(localEntity_t *le, trace_t *trace) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction; BG_EvaluateTrajectoryDelta(&le->pos, hitTime, velocity); dot = DotProduct(velocity, trace->plane.normal); VectorMA(velocity, -2 * dot, trace->plane.normal, le->pos.trDelta); VectorScale(le->pos.trDelta, le->bounceFactor, le->pos.trDelta); VectorCopy(trace->endpos, le->pos.trBase); le->pos.trTime = cg.time; // check for stop, making sure that even on low FPS systems it doesn't bobble if (trace->allsolid || (trace->plane.normal[2] > 0 && (le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2]))) { le->pos.trType = TR_STATIONARY; } else { } }
/* * ================= * CG_AddFallScaleFade * * This is just an optimized CG_AddMoveScaleFade * For blood mists that drift down, fade out, and are * removed if the view passes through them. * There are often 100+ of these, so it needs to be simple. * ================= */ static void CG_AddFallScaleFade(localEntity_t *le) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; // fade time c = (le->endTime - cg.time) * le->lifeRate; re->shaderRGBA[3] = 0xff * c * le->color[3]; re->origin[2] = le->pos.trBase[2] - (1.0 - c) * le->pos.trDelta[2]; re->radius = le->radius * (1.0 - c) + 16; // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract(re->origin, cg.refdef.vieworg, delta); len = VectorLength(delta); if (len < le->radius) { CG_FreeLocalEntity(le); return; } trap_R_AddRefEntityToScene(re); }
/* * ================ * CG_FragmentBounceSound * ================ */ void CG_FragmentBounceSound(localEntity_t *le, trace_t *trace) { if (le->leBounceSoundType == LEBS_BLOOD) { // half the gibs will make splat sounds if (rand() & 1) { int r = rand() & 3; sfxHandle_t s; if (r == 0) { s = cgs.media.gibBounce1Sound; } else if (r == 1) { s = cgs.media.gibBounce2Sound; } else { s = cgs.media.gibBounce3Sound; } trap_S_StartSound(trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s); } } else if (le->leBounceSoundType == LEBS_BRASS) { } // don't allow a fragment to make multiple bounce sounds, // or it gets too noisy as they settle le->leBounceSoundType = LEBS_NONE; }
/* * ==================================================================================== * * FRAGMENT PROCESSING * * A fragment localentity interacts with the environment in some way (hitting walls), * or generates more localentities along a trail. * * ==================================================================================== */ /* * ================ * CG_BloodTrail * * Leave expanding blood puffs behind gibs * ================ */ void CG_BloodTrail(localEntity_t *le) { int t; int t2; int step; vec3_t newOrigin; localEntity_t *blood; step = 150; t = step * ((cg.time - cg.frametime + step) / step); t2 = step * (cg.time / step); for ( ; t <= t2; t += step) { BG_EvaluateTrajectory(&le->pos, t, newOrigin); blood = CG_SmokePuff(newOrigin, vec3_origin, 20, // radius 1, 1, 1, 1, // color 2000, // trailTime t, // startTime 0, // fadeInTime 0, // flags cgs.media.bloodTrailShader); // use the optimized version blood->leType = LE_FALL_SCALE_FADE; // drop a total of 40 units over its lifetime blood->pos.trDelta[2] = 40; } }
localEntity_t *cg_freeLocalEntities; // single linked list /* * =================== * CG_InitLocalEntities * * This is called at startup and for tournement restarts * =================== */ void CG_InitLocalEntities(void) { int i; memset(cg_localEntities, 0, sizeof(cg_localEntities)); cg_activeLocalEntities.next = &cg_activeLocalEntities; cg_activeLocalEntities.prev = &cg_activeLocalEntities; cg_freeLocalEntities = cg_localEntities; for (i = 0; i < MAX_LOCAL_ENTITIES - 1; i++) { cg_localEntities[i].next = &cg_localEntities[i + 1]; } }
/* * ================== * CG_FreeLocalEntity * ================== */ void CG_FreeLocalEntity(localEntity_t *le) { if (!le->prev) { CG_Error("CG_FreeLocalEntity: not active"); } // remove from the doubly linked active list le->prev->next = le->next; le->next->prev = le->prev; // the free list is only singly linked le->next = cg_freeLocalEntities; cg_freeLocalEntities = le; }
/* * ===================================================================== * * TRIVIAL LOCAL ENTITIES * * These only do simple scaling or modulation before passing to the renderer * ===================================================================== */ /* * ==================== * CG_AddFadeRGB * ==================== */ void CG_AddFadeRGB(localEntity_t *le) { refEntity_t *re; float c; re = &le->refEntity; c = (le->endTime - cg.time) * le->lifeRate; c *= 0xff; re->shaderRGBA[0] = le->color[0] * c; re->shaderRGBA[1] = le->color[1] * c; re->shaderRGBA[2] = le->color[2] * c; re->shaderRGBA[3] = le->color[3] * c; trap_R_AddRefEntityToScene(re); }
/* * ================ * CG_AddSpriteExplosion * ================ */ static void CG_AddSpriteExplosion(localEntity_t *le) { refEntity_t re; float c; re = le->refEntity; c = (le->endTime - cg.time) / ( float )(le->endTime - le->startTime); if (c > 1) { c = 1.0; // can happen during connection problems } re.shaderRGBA[0] = 0xff; re.shaderRGBA[1] = 0xff; re.shaderRGBA[2] = 0xff; re.shaderRGBA[3] = 0xff * c * 0.33; re.reType = RT_SPRITE; re.radius = 42 * (1.0 - c) + 30; trap_R_AddRefEntityToScene(&re); // add the dlight if (le->light) { float light; light = (float)(cg.time - le->startTime) / (le->endTime - le->startTime); if (light < 0.5) { light = 1.0; } else { light = 1.0 - (light - 0.5) * 2; } light = le->light * light; trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2]); } }
/* * ================== * CG_AddMoveScaleFade * ================== */ static void CG_AddMoveScaleFade(localEntity_t *le) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; if (le->fadeInTime > le->startTime && cg.time < le->fadeInTime) { // fade / grow time c = 1.0 - (float)(le->fadeInTime - cg.time) / (le->fadeInTime - le->startTime); } else { // fade / grow time c = (le->endTime - cg.time) * le->lifeRate; } re->shaderRGBA[3] = 0xff * c * le->color[3]; if (!(le->leFlags & LEF_PUFF_DONT_SCALE)) { re->radius = le->radius * (1.0 - c) + 8; } BG_EvaluateTrajectory(&le->pos, cg.time, re->origin); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract(re->origin, cg.refdef.vieworg, delta); len = VectorLength(delta); if (len < le->radius) { CG_FreeLocalEntity(le); return; } trap_R_AddRefEntityToScene(re); }
/* * ================ * CG_FragmentBounceMark * ================ */ void CG_FragmentBounceMark(localEntity_t *le, trace_t *trace) { int radius; if (le->leMarkType == LEMT_BLOOD) { radius = 16 + (rand() & 31); CG_ImpactMark(cgs.media.bloodMarkShader, trace->endpos, trace->plane.normal, random() * 360, 1, 1, 1, 1, true, radius, false); } else if (le->leMarkType == LEMT_BURN) { radius = 8 + (rand() & 15); CG_ImpactMark(cgs.media.burnMarkShader, trace->endpos, trace->plane.normal, random() * 360, 1, 1, 1, 1, true, radius, false); } // don't allow a fragment to make multiple marks, or they // pile up while settling le->leMarkType = LEMT_NONE; }
/* * =================== * CG_AllocLocalEntity * * Will allways succeed, even if it requires freeing an old active entity * =================== */ localEntity_t *CG_AllocLocalEntity(void) { localEntity_t *le; if (!cg_freeLocalEntities) { // no free entities, so free the one at the end of the chain // remove the oldest active entity CG_FreeLocalEntity(cg_activeLocalEntities.prev); } le = cg_freeLocalEntities; cg_freeLocalEntities = cg_freeLocalEntities->next; memset(le, 0, sizeof(*le)); // link into the active list le->next = cg_activeLocalEntities.next; le->prev = &cg_activeLocalEntities; cg_activeLocalEntities.next->prev = le; cg_activeLocalEntities.next = le; return(le); }
/* * =================== * CG_AddScorePlum * =================== */ #define NUMBER_SIZE 8 void CG_AddScorePlum(localEntity_t *le) { refEntity_t *re; vec3_t origin, delta, dir, vec, up = { 0, 0, 1 }; float c, len; int i, score, digits[10], numdigits, negative;
/* * ================ * CG_AddFragment * ================ */ void CG_AddFragment(localEntity_t *le) { vec3_t newOrigin; trace_t trace; if (le->pos.trType == TR_STATIONARY) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if (t < SINK_TIME) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy(le->refEntity.origin, le->refEntity.lightingOrigin); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * (1.0 - (float)t / SINK_TIME); trap_R_AddRefEntityToScene(&le->refEntity); le->refEntity.origin[2] = oldZ; } else { trap_R_AddRefEntityToScene(&le->refEntity); } return; } // calculate new position BG_EvaluateTrajectory(&le->pos, cg.time, newOrigin); // trace a line from previous position to new position CG_Trace(&trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID); if (trace.fraction == 1.0) { // still in free fall VectorCopy(newOrigin, le->refEntity.origin); if (le->leFlags & LEF_TUMBLE) { vec3_t angles; BG_EvaluateTrajectory(&le->angles, cg.time, angles); AnglesToAxis(angles, le->refEntity.axis); } trap_R_AddRefEntityToScene(&le->refEntity); // add a blood trail if (le->leBounceSoundType == LEBS_BLOOD) { CG_BloodTrail(le); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if (trap_CM_PointContents(trace.endpos, 0) & CONTENTS_NODROP) { CG_FreeLocalEntity(le); return; } // leave a mark CG_FragmentBounceMark(le, &trace); // do a bouncy sound CG_FragmentBounceSound(le, &trace); // reflect the velocity on the trace plane CG_ReflectVelocity(le, &trace); trap_R_AddRefEntityToScene(&le->refEntity); }