/* =============== CG_ApplyJitters =============== */ static void CG_ApplyJitters( trailBeam_t *tb ) { trailBeamNode_t *i = nullptr; int j; baseTrailBeam_t *btb; trailSystem_t *ts; trailBeamNode_t *start; trailBeamNode_t *end; if ( !tb || !tb->nodes ) { return; } btb = tb->class_; ts = tb->parent; for ( j = 0; j < btb->numJitters; j++ ) { if ( tb->nextJitterTimes[ j ] <= cg.time ) { for ( i = tb->nodes; i; i = i->next ) { i->jitters[ j ][ 0 ] = ( crandom() * btb->jitters[ j ].magnitude ); i->jitters[ j ][ 1 ] = ( crandom() * btb->jitters[ j ].magnitude ); } tb->nextJitterTimes[ j ] = cg.time + btb->jitters[ j ].period; } } start = tb->nodes; end = CG_FindLastBeamNode( tb ); if ( !btb->jitterAttachments ) { if ( CG_Attached( &ts->frontAttachment ) && start->next ) { start = start->next; } if ( CG_Attached( &ts->backAttachment ) && end->prev ) { end = end->prev; } } for ( i = start; i; i = i->next ) { vec3_t forward, right, up; trailBeamNode_t *prev; trailBeamNode_t *next; float upJitter = 0.0f, rightJitter = 0.0f; prev = i->prev; next = i->next; if ( prev && next ) { //this node has two neighbours GetPerpendicularViewVector( cg.refdef.vieworg, next->position, prev->position, up ); VectorSubtract( next->position, prev->position, forward ); } else if ( !prev && next ) { //this is the front GetPerpendicularViewVector( cg.refdef.vieworg, next->position, i->position, up ); VectorSubtract( next->position, i->position, forward ); } else if ( prev && !next ) { //this is the back GetPerpendicularViewVector( cg.refdef.vieworg, i->position, prev->position, up ); VectorSubtract( i->position, prev->position, forward ); } VectorNormalize( forward ); CrossProduct( forward, up, right ); VectorNormalize( right ); for ( j = 0; j < btb->numJitters; j++ ) { upJitter += i->jitters[ j ][ 0 ]; rightJitter += i->jitters[ j ][ 1 ]; } VectorMA( i->position, upJitter, up, i->position ); VectorMA( i->position, rightJitter, right, i->position ); if ( i == end ) { break; } } }
void CG_AddTrailToScene(trailJunc_t *trail, int iteration, int numJuncs) { #define MAX_TRAIL_VERTS 2048 polyVert_t verts[MAX_TRAIL_VERTS]; polyVert_t outVerts[MAX_TRAIL_VERTS * 3]; int k, i, n, l, numOutVerts; polyVert_t mid; float mod[4]; float sInc = 0.0f, s = 0.0f; // TTimo: init trailJunc_t *j, *jNext; vec3_t fwd, up, p, v; (void)fwd; // Ignore compiler warning -- Justasic // clipping vars #define TRAIL_FADE_CLOSE_DIST 64.0 #define TRAIL_FADE_FAR_SCALE 4.0 vec3_t viewProj; float viewDist, fadeAlpha; // add spark shader at head position if(trail->flags & TJFL_SPARKHEADFLARE) { j = trail; VectorCopy(j->pos, p); VectorMA(p, -j->width * 2, vup, p); VectorMA(p, -j->width * 2, vright, p); VectorCopy(p, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = (unsigned char)(j->alpha * 255.0); VectorCopy(j->pos, p); VectorMA(p, -j->width * 2, vup, p); VectorMA(p, j->width * 2, vright, p); VectorCopy(p, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = (unsigned char)(j->alpha * 255.0); VectorCopy(j->pos, p); VectorMA(p, j->width * 2, vup, p); VectorMA(p, j->width * 2, vright, p); VectorCopy(p, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = (unsigned char)(j->alpha * 255.0); VectorCopy(j->pos, p); VectorMA(p, j->width * 2, vup, p); VectorMA(p, -j->width * 2, vright, p); VectorCopy(p, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = (unsigned char)(j->alpha * 255.0); trap_R_AddPolyToScene(cgs.media.sparkFlareShader, 4, verts); } // if (trail->flags & TJFL_CROSSOVER && iteration < 1) { // iteration = 1; // } if(!numJuncs) { // first count the number of juncs in the trail j = trail; numJuncs = 0; sInc = 0; while(j) { numJuncs++; // check for a dead next junc if(!j->inuse && j->nextJunc && !j->nextJunc->inuse) { CG_KillTrail(j); } else if(j->nextJunc && j->nextJunc->freed) { // not sure how this can happen, but it does, and causes infinite loops j->nextJunc = NULL; } if(j->nextJunc) { sInc += VectorDistance(j->nextJunc->pos, j->pos); } j = j->nextJunc; } } if(numJuncs < 2) { return; } if(trail->sType == STYPE_STRETCH) { //sInc = ((1.0 - 0.1) / (float)(numJuncs)); // hack, the end of funnel shows a bit of the start (looping) s = 0.05; //s = 0.05; } else if(trail->sType == STYPE_REPEAT) { s = trail->sTex; } // now traverse the list j = trail; jNext = j->nextJunc; i = 0; while(jNext) { // first get the directional vectors to the next junc VectorSubtract(jNext->pos, j->pos, fwd); GetPerpendicularViewVector(cg.refdef.vieworg, j->pos, jNext->pos, up); // if it's a crossover, draw it twice if(j->flags & TJFL_CROSSOVER) { if(iteration > 0) { ProjectPointOntoVector(cg.refdef.vieworg, j->pos, jNext->pos, viewProj); VectorSubtract(cg.refdef.vieworg, viewProj, v); VectorNormalize(v); if(iteration == 1) { VectorMA(up, 0.3, v, up); } else { VectorMA(up, -0.3, v, up); } VectorNormalize(up); } } // do fading when moving towards the projection point onto the trail segment vector else if(!(j->flags & TJFL_NOCULL) && (j->widthEnd > 4 || jNext->widthEnd > 4)) { ProjectPointOntoVector(cg.refdef.vieworg, j->pos, jNext->pos, viewProj); viewDist = Distance(viewProj, cg.refdef.vieworg); if(viewDist < (TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE)) { if(viewDist < TRAIL_FADE_CLOSE_DIST) { fadeAlpha = 0.0; } else { fadeAlpha = (viewDist - TRAIL_FADE_CLOSE_DIST) / (TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE); } if(fadeAlpha < j->alpha) { j->alpha = fadeAlpha; } if(fadeAlpha < jNext->alpha) { jNext->alpha = fadeAlpha; } } } // now output the QUAD for this segment // 1 ---- VectorMA(j->pos, 0.5 * j->width, up, p); VectorCopy(p, verts[i].xyz); verts[i].st[0] = s; verts[i].st[1] = 1.0; for(k = 0; k < 3; k++) verts[i].modulate[k] = (unsigned char)(j->color[k] * 255.0); verts[i].modulate[3] = (unsigned char)(j->alpha * 255.0); // blend this with the previous junc if(j != trail) { VectorAdd(verts[i].xyz, verts[i - 1].xyz, verts[i].xyz); VectorScale(verts[i].xyz, 0.5, verts[i].xyz); VectorCopy(verts[i].xyz, verts[i - 1].xyz); } else if(j->flags & TJFL_FADEIN) { verts[i].modulate[3] = 0; // fade in } i++; // 2 ---- VectorMA(p, -1 * j->width, up, p); VectorCopy(p, verts[i].xyz); verts[i].st[0] = s; verts[i].st[1] = 0.0; for(k = 0; k < 3; k++) verts[i].modulate[k] = (unsigned char)(j->color[k] * 255.0); verts[i].modulate[3] = (unsigned char)(j->alpha * 255.0); // blend this with the previous junc if(j != trail) { VectorAdd(verts[i].xyz, verts[i - 3].xyz, verts[i].xyz); VectorScale(verts[i].xyz, 0.5, verts[i].xyz); VectorCopy(verts[i].xyz, verts[i - 3].xyz); } else if(j->flags & TJFL_FADEIN) { verts[i].modulate[3] = 0; // fade in } i++; if(trail->sType == STYPE_REPEAT) { s = jNext->sTex; } else { //s += sInc; s += VectorDistance(j->pos, jNext->pos) / sInc; if(s > 1.0) { s = 1.0; } } // 3 ---- VectorMA(jNext->pos, -0.5 * jNext->width, up, p); VectorCopy(p, verts[i].xyz); verts[i].st[0] = s; verts[i].st[1] = 0.0; for(k = 0; k < 3; k++) verts[i].modulate[k] = (unsigned char)(jNext->color[k] * 255.0); verts[i].modulate[3] = (unsigned char)(jNext->alpha * 255.0); i++; // 4 ---- VectorMA(p, jNext->width, up, p); VectorCopy(p, verts[i].xyz); verts[i].st[0] = s; verts[i].st[1] = 1.0; for(k = 0; k < 3; k++) verts[i].modulate[k] = (unsigned char)(jNext->color[k] * 255.0); verts[i].modulate[3] = (unsigned char)(jNext->alpha * 255.0); i++; if(i + 4 > MAX_TRAIL_VERTS) { break; } j = jNext; jNext = j->nextJunc; } if(trail->flags & TJFL_FIXDISTORT) { // build the list of outVerts, by dividing up the QUAD's into 4 Tri's each, so as to allow // any shaped (convex) Quad without bilinear distortion for(k = 0, numOutVerts = 0; k < i; k += 4) { VectorCopy(verts[k].xyz, mid.xyz); mid.st[0] = verts[k].st[0]; mid.st[1] = verts[k].st[1]; for(l = 0; l < 4; l++) { mod[l] = (float)verts[k].modulate[l]; } for(n = 1; n < 4; n++) { VectorAdd(verts[k + n].xyz, mid.xyz, mid.xyz); mid.st[0] += verts[k + n].st[0]; mid.st[1] += verts[k + n].st[1]; for(l = 0; l < 4; l++) { mod[l] += (float)verts[k + n].modulate[l]; } } VectorScale(mid.xyz, 0.25, mid.xyz); mid.st[0] *= 0.25; mid.st[1] *= 0.25; for(l = 0; l < 4; l++) { mid.modulate[l] = (unsigned char)(mod[l] / 4.0); } // now output the tri's for(n = 0; n < 4; n++) { outVerts[numOutVerts++] = verts[k + n]; outVerts[numOutVerts++] = mid; if(n < 3) { outVerts[numOutVerts++] = verts[k + n + 1]; } else { outVerts[numOutVerts++] = verts[k]; } } } if(!(trail->flags & TJFL_NOPOLYMERGE)) { trap_R_AddPolysToScene(trail->shader, 3, &outVerts[0], numOutVerts / 3); } else { int k; for(k = 0; k < numOutVerts / 3; k++) { trap_R_AddPolyToScene(trail->shader, 3, &outVerts[k * 3]); } } } else { // send the polygons // FIXME: is it possible to send a GL_STRIP here? We are actually sending 2x the verts we really need to if(!(trail->flags & TJFL_NOPOLYMERGE)) { trap_R_AddPolysToScene(trail->shader, 4, &verts[0], i / 4); } else { int k; for(k = 0; k < i / 4; k++) { trap_R_AddPolyToScene(trail->shader, 4, &verts[k * 4]); } } } // do we need to make another pass? if(trail->flags & TJFL_CROSSOVER) { if(iteration < 2) { CG_AddTrailToScene(trail, iteration + 1, numJuncs); } } }
/* =============== CG_RenderBeam Renders a beam =============== */ static void CG_RenderBeam( trailBeam_t *tb ) { trailBeamNode_t *i = nullptr; trailBeamNode_t *prev = nullptr; trailBeamNode_t *next = nullptr; vec3_t up; polyVert_t verts[( MAX_TRAIL_BEAM_NODES - 1 ) * 4 ]; int numVerts = 0; baseTrailBeam_t *btb; trailSystem_t *ts; baseTrailSystem_t *bts; if ( !tb || !tb->nodes ) { return; } btb = tb->class_; ts = tb->parent; bts = ts->class_; if ( bts->thirdPersonOnly && ( CG_AttachmentCentNum( &ts->frontAttachment ) == cg.snap->ps.clientNum || CG_AttachmentCentNum( &ts->backAttachment ) == cg.snap->ps.clientNum ) && !cg.renderingThirdPerson ) { return; } CG_CalculateBeamNodeProperties( tb ); i = tb->nodes; do { prev = i->prev; next = i->next; if ( prev && next ) { //this node has two neighbours GetPerpendicularViewVector( cg.refdef.vieworg, next->position, prev->position, up ); } else if ( !prev && next ) { //this is the front GetPerpendicularViewVector( cg.refdef.vieworg, next->position, i->position, up ); } else if ( prev && !next ) { //this is the back GetPerpendicularViewVector( cg.refdef.vieworg, i->position, prev->position, up ); } else { break; } if ( prev ) { VectorMA( i->position, i->halfWidth, up, verts[ numVerts ].xyz ); verts[ numVerts ].st[ 0 ] = i->textureCoord; verts[ numVerts ].st[ 1 ] = 1.0f; if ( btb->realLight ) { CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); } else { VectorCopy( i->color, verts[ numVerts ].modulate ); verts[ numVerts ].modulate[ 3 ] = i->alpha; } numVerts++; VectorMA( i->position, -i->halfWidth, up, verts[ numVerts ].xyz ); verts[ numVerts ].st[ 0 ] = i->textureCoord; verts[ numVerts ].st[ 1 ] = 0.0f; if ( btb->realLight ) { CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); } else { VectorCopy( i->color, verts[ numVerts ].modulate ); verts[ numVerts ].modulate[ 3 ] = i->alpha; } numVerts++; } if ( next ) { VectorMA( i->position, -i->halfWidth, up, verts[ numVerts ].xyz ); verts[ numVerts ].st[ 0 ] = i->textureCoord; verts[ numVerts ].st[ 1 ] = 0.0f; if ( btb->realLight ) { CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); } else { VectorCopy( i->color, verts[ numVerts ].modulate ); verts[ numVerts ].modulate[ 3 ] = i->alpha; } numVerts++; VectorMA( i->position, i->halfWidth, up, verts[ numVerts ].xyz ); verts[ numVerts ].st[ 0 ] = i->textureCoord; verts[ numVerts ].st[ 1 ] = 1.0f; if ( btb->realLight ) { CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); } else { VectorCopy( i->color, verts[ numVerts ].modulate ); verts[ numVerts ].modulate[ 3 ] = i->alpha; } numVerts++; } if( btb->dynamicLight ) { trap_R_AddLightToScene( i->position, btb->dLightRadius, 3, ( float ) btb->dLightColor[ 0 ] / ( float ) 0xFF, ( float ) btb->dLightColor[ 1 ] / ( float ) 0xFF, ( float ) btb->dLightColor[ 2 ] / ( float ) 0xFF, 0, 0 ); } i = i->next; } while ( i ); trap_R_AddPolysToScene( tb->class_->shader, 4, &verts[ 0 ], numVerts / 4 ); }