/* =============== CL_AddParticles =============== */ void CL_AddParticles (void) { cparticle_t *p, *next; float alpha; float time, time2; vec3_t org; int color; cparticle_t *active, *tail; int contents; float grav; active = NULL; tail = NULL; if (!cl_drawParticles->integer) return; // allow gravity tweaks by the server grav = Cvar_VariableValue("sv_gravity"); if (!grav) grav = 1; else grav /= 800; for (p = active_particles; p; p = next) { next = p->next; // set alpha // PMM - added INSTANT_PARTICLE handling if (p->alphavel != INSTANT_PARTICLE) { time = (cl.time - p->time) * 0.001; alpha = p->alpha + time * p->alphavel; if (alpha <= 0) { // faded out CL_FreeParticle (p); continue; } else if (alpha <= 0.3f && p->color == 240) // this is HACK central... { // do blood decals if (rand() & 4) { trace_t tr; time2 = time * time; org[0] = p->org[0] + p->vel[0] * time + p->accel[0] * time2; org[1] = p->org[1] + p->vel[1] * time + p->accel[1] * time2; org[2] = p->org[2] + p->vel[2] * time + p->accel[2] * time2; tr = CL_Trace(p->org, org, 0, MASK_SOLID); if (tr.fraction != 1.0f) { if (!VectorCompare(tr.plane.normal, vec3_origin) && !(CM_PointContents(p->org, 0) & MASK_WATER)) // no blood splatters underwater... { vec4_t color; Vector4Set(color, 1.0, 0.0, 0.0, 1.0f); RE_AddDecal(tr.endpos, tr.plane.normal, color, 16 + ((rand() % 21 * 0.05f) - 0.5f), DECAL_BLOOD + (rand() & 4), 0, frand() * 360); } } } } } else { alpha = p->alpha; } if (alpha > 1.0) alpha = 1; // set color color = p->color; // backup old position VectorCopy (p->org, p->oldOrg); // calculate new position time2 = time * time; org[0] = p->org[0] + p->vel[0] * time + p->accel[0] * time2; org[1] = p->org[1] + p->vel[1] * time + p->accel[1] * time2; org[2] = p->org[2] + p->vel[2] * time + p->accel[2] * time2; // gravity modulation //if (!p->ignoreGrav) // org[2] += time2 * -PARTICLE_GRAVITY; // collision test if (cl_particleCollision->integer) { if (p->bounceFactor) { trace_t trace; vec3_t vel; int hitTime; float time; trace = CL_Trace(p->oldOrg, org, 0, CONTENTS_SOLID); if (trace.fraction > 0 && trace.fraction < 1) { // reflect the velocity on the trace plane hitTime = cl.time - cls.rframetime + cls.rframetime * trace.fraction; time = ((float)hitTime - p->time) * 0.001; Vector3Set (vel, p->vel[0], p->vel[1], p->vel[2] + p->accel[2] * time * grav); VectorReflect (vel, trace.plane.normal, p->vel); VectorScale (p->vel, p->bounceFactor, p->vel); // check for stop, making sure that even on low FPS systems it doesn't bobble if (trace.allsolid || (trace.plane.normal[2] > 0 && (p->vel[2] < 40 || p->vel[2] < -cls.rframetime * p->vel[2]))) { VectorClear(p->vel); VectorClear(p->accel); p->bounceFactor = 0.0f; } VectorCopy (trace.endpos, org); // reset particle p->time = cl.time; VectorCopy (org, p->org); } } } // kill all particles in solid contents = CM_PointContents (org, 0); if (contents & MASK_SOLID) { CL_FreeParticle (p); continue; } // have this always below CL_FreeParticle(p); ! p->next = NULL; if (!tail) { active = tail = p; } else { tail->next = p; tail = p; } // add to scene V_AddParticle (org, color, alpha); // PMM if (p->alphavel == INSTANT_PARTICLE) { p->alphavel = 0.0; p->alpha = 0.0; } } active_particles = active; }
/* * R_DrawPortalSurface * * Renders the portal view and captures the results from framebuffer if * we need to do a $portalmap stage. Note that for $portalmaps we must * use a different viewport. */ static void R_DrawPortalSurface( portalSurface_t *portalSurface ) { unsigned int i; int x, y, w, h; float dist, d, best_d; vec3_t viewerOrigin; vec3_t origin; mat3_t axis; entity_t *ent, *best; const entity_t *portal_ent = portalSurface->entity; cplane_t *portal_plane = &portalSurface->plane, *untransformed_plane = &portalSurface->untransformed_plane; const shader_t *shader = portalSurface->shader; vec_t *portal_mins = portalSurface->mins, *portal_maxs = portalSurface->maxs; vec_t *portal_centre = portalSurface->centre; qboolean mirror, refraction = qfalse; image_t *captureTexture; int captureTextureId = -1; int prevRenderFlags = 0; qboolean doReflection, doRefraction; image_t *portalTexures[2] = { NULL, NULL }; doReflection = doRefraction = qtrue; if( shader->flags & SHADER_PORTAL_CAPTURE ) { shaderpass_t *pass; captureTexture = NULL; captureTextureId = 0; for( i = 0, pass = shader->passes; i < shader->numpasses; i++, pass++ ) { if( pass->program_type == GLSL_PROGRAM_TYPE_DISTORTION ) { if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 1 ) ) doRefraction = qfalse; else if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 0 ) ) doReflection = qfalse; break; } } } else { captureTexture = NULL; captureTextureId = -1; } x = y = 0; w = rn.refdef.width; h = rn.refdef.height; dist = PlaneDiff( rn.viewOrigin, portal_plane ); if( dist <= BACKFACE_EPSILON || !doReflection ) { if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) || !doRefraction ) return; // even if we're behind the portal, we still need to capture // the second portal image for refraction refraction = qtrue; captureTexture = NULL; captureTextureId = 1; if( dist < 0 ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist; } } if( !(rn.renderFlags & RF_NOVIS) && !R_ScissorForEntity( portal_ent, portal_mins, portal_maxs, &x, &y, &w, &h ) ) return; mirror = qtrue; // default to mirror view // it is stupid IMO that mirrors require a RT_PORTALSURFACE entity best = NULL; best_d = 100000000; for( i = 1; i < rsc.numEntities; i++ ) { ent = R_NUM2ENT(i); if( ent->rtype != RT_PORTALSURFACE ) continue; d = PlaneDiff( ent->origin, untransformed_plane ); if( ( d >= -64 ) && ( d <= 64 ) ) { d = Distance( ent->origin, portal_centre ); if( d < best_d ) { best = ent; best_d = d; } } } if( best == NULL ) { if( captureTextureId < 0 ) return; } else { if( !VectorCompare( best->origin, best->origin2 ) ) // portal mirror = qfalse; best->rtype = NUM_RTYPES; } prevRenderFlags = rn.renderFlags; if( !R_PushRefInst() ) { return; } VectorCopy( rn.viewOrigin, viewerOrigin ); setup_and_render: if( refraction ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist - 1; CategorizePlane( portal_plane ); VectorCopy( rn.viewOrigin, origin ); Matrix3_Copy( rn.refdef.viewaxis, axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags = RF_PORTALVIEW; if( !mirror ) rn.renderFlags |= RF_PVSCULL; } else if( mirror ) { VectorReflect( rn.viewOrigin, portal_plane->normal, portal_plane->dist, origin ); VectorReflect( &rn.viewAxis[AXIS_FORWARD], portal_plane->normal, 0, &axis[AXIS_FORWARD] ); VectorReflect( &rn.viewAxis[AXIS_RIGHT], portal_plane->normal, 0, &axis[AXIS_RIGHT] ); VectorReflect( &rn.viewAxis[AXIS_UP], portal_plane->normal, 0, &axis[AXIS_UP] ); Matrix3_Normalize( axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags = RF_MIRRORVIEW|RF_FLIPFRONTFACE; } else { vec3_t tvec; mat3_t A, B, C, rot; // build world-to-portal rotation matrix VectorNegate( portal_plane->normal, tvec ); NormalVectorToAxis( tvec, A ); // build portal_dest-to-world rotation matrix ByteToDir( best->frame, tvec ); NormalVectorToAxis( tvec, B ); Matrix3_Transpose( B, C ); // multiply to get world-to-world rotation matrix Matrix3_Multiply( C, A, rot ); // translate view origin VectorSubtract( rn.viewOrigin, best->origin, tvec ); Matrix3_TransformVector( rot, tvec, origin ); VectorAdd( origin, best->origin2, origin ); Matrix3_Transpose( A, B ); Matrix3_Multiply( rn.viewAxis, B, rot ); Matrix3_Multiply( best->axis, rot, B ); Matrix3_Transpose( C, A ); Matrix3_Multiply( B, A, axis ); // set up portal_plane VectorCopy( &axis[AXIS_FORWARD], portal_plane->normal ); portal_plane->dist = DotProduct( best->origin2, portal_plane->normal ); CategorizePlane( portal_plane ); // for portals, vis data is taken from portal origin, not // view origin, because the view point moves around and // might fly into (or behind) a wall rn.renderFlags = RF_PORTALVIEW|RF_PVSCULL; VectorCopy( best->origin2, rn.pvsOrigin ); VectorCopy( best->origin2, rn.lodOrigin ); // ignore entities, if asked politely if( best->renderfx & RF_NOPORTALENTS ) rn.renderFlags |= RF_NOENTS; } rn.renderFlags |= (prevRenderFlags & RF_SOFT_PARTICLES); rn.refdef.rdflags &= ~( RDF_UNDERWATER|RDF_CROSSINGWATER ); rn.shadowGroup = NULL; rn.meshlist = &r_portallist; rn.renderFlags |= RF_CLIPPLANE; rn.clipPlane = *portal_plane; rn.farClip = R_DefaultFarClip(); rn.clipFlags |= ( 1<<5 ); rn.frustum[5] = *portal_plane; CategorizePlane( &rn.frustum[5] ); // if we want to render to a texture, initialize texture // but do not try to render to it more than once if( captureTextureId >= 0 ) { int texFlags = shader->flags & SHADER_NO_TEX_FILTERING ? IT_NOFILTERING : 0; captureTexture = R_GetPortalTexture( rsc.refdef.width, rsc.refdef.height, texFlags, rsc.frameCount ); portalTexures[captureTextureId] = captureTexture; if( !captureTexture ) { // couldn't register a slot for this plane goto done; } x = y = 0; w = captureTexture->upload_width; h = captureTexture->upload_height; rn.refdef.width = w; rn.refdef.height = h; rn.refdef.x = 0; rn.refdef.y = 0; rn.fbColorAttachment = captureTexture; // no point in capturing the depth buffer due to oblique frustum messing up // the far plane and depth values rn.fbDepthAttachment = NULL; Vector4Set( rn.viewport, rn.refdef.x + x, rn.refdef.y + y, w, h ); Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); } else { // no point in capturing the depth buffer due to oblique frustum messing up // the far plane and depth values rn.fbDepthAttachment = NULL; Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); } VectorCopy( origin, rn.refdef.vieworg ); Matrix3_Copy( axis, rn.refdef.viewaxis ); R_RenderView( &rn.refdef ); if( doRefraction && !refraction && ( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { refraction = qtrue; captureTexture = NULL; captureTextureId = 1; goto setup_and_render; } done: portalSurface->texures[0] = portalTexures[0]; portalSurface->texures[1] = portalTexures[1]; R_PopRefInst( rn.fbDepthAttachment != NULL ? 0 : GL_DEPTH_BUFFER_BIT ); }
/* * R_DrawPortalSurface * * Renders the portal view and captures the results from framebuffer if * we need to do a $portalmap stage. Note that for $portalmaps we must * use a different viewport. */ static void R_DrawPortalSurface( portalSurface_t *portalSurface ) { unsigned int i; int x, y, w, h; float dist, d, best_d; vec3_t viewerOrigin; vec3_t origin; mat3_t axis; entity_t *ent, *best; cplane_t *portal_plane = &portalSurface->plane, *untransformed_plane = &portalSurface->untransformed_plane; const shader_t *shader = portalSurface->shader; vec_t *portal_centre = portalSurface->centre; bool mirror, refraction = false; image_t *captureTexture; int captureTextureId = -1; int prevRenderFlags = 0; bool prevFlipped; bool doReflection, doRefraction; image_t *portalTexures[2] = { NULL, NULL }; doReflection = doRefraction = true; if( shader->flags & SHADER_PORTAL_CAPTURE ) { shaderpass_t *pass; captureTexture = NULL; captureTextureId = 0; for( i = 0, pass = shader->passes; i < shader->numpasses; i++, pass++ ) { if( pass->program_type == GLSL_PROGRAM_TYPE_DISTORTION ) { if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 1 ) ) { doRefraction = false; } else if( ( pass->alphagen.type == ALPHA_GEN_CONST && pass->alphagen.args[0] == 0 ) ) { doReflection = false; } break; } } } else { captureTexture = NULL; captureTextureId = -1; } x = y = 0; w = rn.refdef.width; h = rn.refdef.height; dist = PlaneDiff( rn.viewOrigin, portal_plane ); if( dist <= BACKFACE_EPSILON || !doReflection ) { if( !( shader->flags & SHADER_PORTAL_CAPTURE2 ) || !doRefraction ) { return; } // even if we're behind the portal, we still need to capture // the second portal image for refraction refraction = true; captureTexture = NULL; captureTextureId = 1; if( dist < 0 ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist; } } mirror = true; // default to mirror view // it is stupid IMO that mirrors require a RT_PORTALSURFACE entity best = NULL; best_d = 100000000; for( i = 0; i < rn.numEntities; i++ ) { ent = R_NUM2ENT( rn.entities[i] ); if( ent->rtype != RT_PORTALSURFACE ) { continue; } d = PlaneDiff( ent->origin, untransformed_plane ); if( ( d >= -64 ) && ( d <= 64 ) ) { d = Distance( ent->origin, portal_centre ); if( d < best_d ) { best = ent; best_d = d; } } } if( best == NULL ) { if( captureTextureId < 0 ) { // still do a push&pop because to ensure the clean state if( R_PushRefInst() ) { R_PopRefInst(); } return; } } else { if( !VectorCompare( best->origin, best->origin2 ) ) { // portal mirror = false; } best->rtype = NUM_RTYPES; } prevRenderFlags = rn.renderFlags; prevFlipped = ( rn.refdef.rdflags & RDF_FLIPPED ) != 0; if( !R_PushRefInst() ) { return; } VectorCopy( rn.viewOrigin, viewerOrigin ); if( prevFlipped ) { VectorInverse( &rn.viewAxis[AXIS_RIGHT] ); } setup_and_render: if( refraction ) { VectorInverse( portal_plane->normal ); portal_plane->dist = -portal_plane->dist; CategorizePlane( portal_plane ); VectorCopy( rn.viewOrigin, origin ); Matrix3_Copy( rn.refdef.viewaxis, axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags |= RF_PORTALVIEW; if( prevFlipped ) { rn.renderFlags |= RF_FLIPFRONTFACE; } } else if( mirror ) { VectorReflect( rn.viewOrigin, portal_plane->normal, portal_plane->dist, origin ); VectorReflect( &rn.viewAxis[AXIS_FORWARD], portal_plane->normal, 0, &axis[AXIS_FORWARD] ); VectorReflect( &rn.viewAxis[AXIS_RIGHT], portal_plane->normal, 0, &axis[AXIS_RIGHT] ); VectorReflect( &rn.viewAxis[AXIS_UP], portal_plane->normal, 0, &axis[AXIS_UP] ); Matrix3_Normalize( axis ); VectorCopy( viewerOrigin, rn.pvsOrigin ); rn.renderFlags = ( prevRenderFlags ^ RF_FLIPFRONTFACE ) | RF_MIRRORVIEW; } else { vec3_t tvec; mat3_t A, B, C, rot; // build world-to-portal rotation matrix VectorNegate( portal_plane->normal, tvec ); NormalVectorToAxis( tvec, A ); // build portal_dest-to-world rotation matrix ByteToDir( best->frame, tvec ); NormalVectorToAxis( tvec, B ); Matrix3_Transpose( B, C ); // multiply to get world-to-world rotation matrix Matrix3_Multiply( C, A, rot ); // translate view origin VectorSubtract( rn.viewOrigin, best->origin, tvec ); Matrix3_TransformVector( rot, tvec, origin ); VectorAdd( origin, best->origin2, origin ); Matrix3_Transpose( A, B ); Matrix3_Multiply( rn.viewAxis, B, rot ); Matrix3_Multiply( best->axis, rot, B ); Matrix3_Transpose( C, A ); Matrix3_Multiply( B, A, axis ); // set up portal_plane VectorCopy( &axis[AXIS_FORWARD], portal_plane->normal ); portal_plane->dist = DotProduct( best->origin2, portal_plane->normal ); CategorizePlane( portal_plane ); // for portals, vis data is taken from portal origin, not // view origin, because the view point moves around and // might fly into (or behind) a wall VectorCopy( best->origin2, rn.pvsOrigin ); VectorCopy( best->origin2, rn.lodOrigin ); rn.renderFlags |= RF_PORTALVIEW; // ignore entities, if asked politely if( best->renderfx & RF_NOPORTALENTS ) { rn.renderFlags |= RF_ENVVIEW; } if( prevFlipped ) { rn.renderFlags |= RF_FLIPFRONTFACE; } } rn.refdef.rdflags &= ~( RDF_UNDERWATER | RDF_CROSSINGWATER | RDF_FLIPPED ); rn.meshlist = &r_portallist; rn.portalmasklist = NULL; rn.renderFlags |= RF_CLIPPLANE; rn.renderFlags &= ~RF_SOFT_PARTICLES; rn.clipPlane = *portal_plane; rn.nearClip = Z_NEAR; rn.farClip = R_DefaultFarClip(); rn.polygonFactor = POLYOFFSET_FACTOR; rn.polygonUnits = POLYOFFSET_UNITS; rn.clipFlags |= 16; rn.frustum[4] = *portal_plane; // nearclip CategorizePlane( &rn.frustum[4] ); // if we want to render to a texture, initialize texture // but do not try to render to it more than once if( captureTextureId >= 0 ) { int texFlags = shader->flags & SHADER_NO_TEX_FILTERING ? IT_NOFILTERING : 0; captureTexture = R_GetPortalTexture( rsc.refdef.width, rsc.refdef.height, texFlags, rsc.frameCount ); portalTexures[captureTextureId] = captureTexture; if( !captureTexture ) { // couldn't register a slot for this plane goto done; } x = y = 0; w = captureTexture->upload_width; h = captureTexture->upload_height; rn.refdef.width = w; rn.refdef.height = h; rn.refdef.x = 0; rn.refdef.y = 0; rn.renderTarget = captureTexture->fbo; rn.renderFlags |= RF_PORTAL_CAPTURE; Vector4Set( rn.viewport, rn.refdef.x + x, rn.refdef.y + y, w, h ); Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); } else { rn.renderFlags &= ~RF_PORTAL_CAPTURE; } VectorCopy( origin, rn.refdef.vieworg ); Matrix3_Copy( axis, rn.refdef.viewaxis ); R_SetupViewMatrices( &rn.refdef ); R_SetupFrustum( &rn.refdef, rn.nearClip, rn.farClip, rn.frustum, rn.frustumCorners ); R_SetupPVS( &rn.refdef ); R_RenderView( &rn.refdef ); if( doRefraction && !refraction && ( shader->flags & SHADER_PORTAL_CAPTURE2 ) ) { rn.renderFlags = prevRenderFlags; refraction = true; captureTexture = NULL; captureTextureId = 1; goto setup_and_render; } done: portalSurface->texures[0] = portalTexures[0]; portalSurface->texures[1] = portalTexures[1]; R_PopRefInst(); }