/* * =================== * S_UpdateAmbientSounds * =================== */ static void S_UpdateAmbientSounds(void) { mleaf_t *leaf; float vol; int ambient_channel; channel_t *chan; if (!snd_ambient) return; /* calc ambient sound levels */ if (!cl.worldmodel) return; leaf = Mod_PointInLeaf(cl.worldmodel, listener_origin); if (!leaf || !ambient_level.value) { for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) channels[ambient_channel].sfx = NULL; return; } for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { chan = &channels[ambient_channel]; chan->sfx = ambient_sfx[ambient_channel]; vol = ambient_level.value * leaf->ambient_sound_level[ambient_channel]; if (vol < 8) vol = 0; /* don't adjust volume too fast */ if (chan->master_vol < vol) { chan->master_vol += host_frametime * ambient_fade.value; if (chan->master_vol > vol) chan->master_vol = vol; } else if (chan->master_vol > vol) { chan->master_vol -= host_frametime * ambient_fade.value; if (chan->master_vol < vol) chan->master_vol = vol; } chan->leftvol = chan->rightvol = chan->master_vol; } }
qboolean R_Shader_IsLightInScopeByPoint( unsigned int lightIndex, const vec3_t renderOrigin ) { const R_ShaderLight *light = GetLightFromIndex( lightIndex ); mleaf_t *leaf; if( !light->active ) { return false; } if( VectorDistance( light->origin, renderOrigin ) > light->maxDistance ) { return false; } leaf = Mod_PointInLeaf( (float*)renderOrigin, cl.worldmodel ); if( leaf == NULL ) { return true; } return R_Shader_IsLightVisibleFromLeaf( light, leaf ); }
/* =============== R_SetupFrame =============== */ void R_SetupFrame (void) { int edgecount; vrect_t vrect; float w, h; // don't allow cheats in multiplayer if (cl.maxclients > 1) { Cvar_Set ("r_draworder", "0"); Cvar_Set ("r_fullbright", "0"); Cvar_Set ("r_ambient", "0"); Cvar_Set ("r_drawflat", "0"); } if (r_numsurfs.value) { if ((surface_p - surfaces) > r_maxsurfsseen) r_maxsurfsseen = surface_p - surfaces; Con_Printf ("Used %d of %d surfs; %d max\n", surface_p - surfaces, surf_max - surfaces, r_maxsurfsseen); } if (r_numedges.value) { edgecount = edge_p - r_edges; if (edgecount > r_maxedgesseen) r_maxedgesseen = edgecount; Con_Printf ("Used %d of %d edges; %d max\n", edgecount, r_numallocatededges, r_maxedgesseen); } r_refdef.ambientlight = r_ambient.value; if (r_refdef.ambientlight < 0) r_refdef.ambientlight = 0; if (!sv.active) r_draworder.value = 0; // don't let cheaters look behind walls R_CheckVariables (); R_AnimateLight (); r_framecount++; numbtofpolys = 0; // debugging #if 0 r_refdef.vieworg[0]= 80; r_refdef.vieworg[1]= 64; r_refdef.vieworg[2]= 40; r_refdef.viewangles[0]= 0; r_refdef.viewangles[1]= 46.763641357; r_refdef.viewangles[2]= 0; #endif // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, modelorg); VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); r_dowarpold = r_dowarp; r_dowarp = r_waterwarp.value && (r_viewleaf->contents <= CONTENTS_WATER); if ((r_dowarp != r_dowarpold) || r_viewchanged || lcd_x.value) { if (r_dowarp) { if ((vid.width <= vid.maxwarpwidth) && (vid.height <= vid.maxwarpheight)) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_ViewChanged (&vrect, sb_lines, vid.aspect); } else { w = vid.width; h = vid.height; if (w > vid.maxwarpwidth) { h *= (float)vid.maxwarpwidth / w; w = vid.maxwarpwidth; } if (h > vid.maxwarpheight) { h = vid.maxwarpheight; w *= (float)vid.maxwarpheight / h; } vrect.x = 0; vrect.y = 0; vrect.width = (int)w; vrect.height = (int)h; R_ViewChanged (&vrect, (int)((float)sb_lines * (h/(float)vid.height)), vid.aspect * (h / w) * ((float)vid.width / (float)vid.height)); } } else { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_ViewChanged (&vrect, sb_lines, vid.aspect); } r_viewchanged = false; } // start off with just the four screen edge clip planes R_TransformFrustum (); // save base values VectorCopy (vpn, base_vpn); VectorCopy (vright, base_vright); VectorCopy (vup, base_vup); VectorCopy (modelorg, base_modelorg); R_SetSkyFrame (); R_SetUpFrustumIndexes (); r_cache_thrash = false; // clear frame counts c_faceclip = 0; d_spanpixcount = 0; r_polycount = 0; r_drawnpolycount = 0; r_wholepolycount = 0; r_amodels_drawn = 0; r_outofsurfaces = 0; r_outofedges = 0; D_SetupFrame (); }
/* =============== R_DrawParticles =============== */ void R_DrawParticles (void) { particle_t *p, *kill; mleaf_t *l; float grav; //float grav2, percent; int i; float time1, time2, time3; float dvel; float frametime; // hexen 2 vec3_t diff; // qboolean in_solid; VectorScale (vright, xscaleshrink, r_pright); VectorScale (vup, yscaleshrink, r_pup); VectorCopy (vpn, r_ppn); if(r_fisheye.value) frametime = fabs(cl.time - cl.oldtime)/(float)r_fviews; //qb: divide by # of fisheye views, or runs too fast. else frametime = fabs(cl.time - cl.oldtime); //DEMO_REWIND - qb: Baker change (no, it is not supposed to be 'ctime') // cl.oldtime = cl.time; //qb: shouldn't this be reset each time it's touched? time3 = frametime * 17; time2 = frametime * 13; // 15; time1 = frametime * 10; grav = frametime * sv_gravity.value * 0.05; dvel = 4*frametime; if (!sv_freezephysics.value || !allowcheats) //qb for ( ;; ) { kill = active_particles; if (kill && kill->die < cl.time) { active_particles = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } for (p=active_particles ; p ; p=p->next) { if (!sv_freezephysics.value || !allowcheats) //qb for ( ;; ) { kill = p->next; if (kill && kill->die < cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } // Manoel Kasimier - transparent particles if (!r_part_transparent.value || p->alpha >= 0.80) D_DrawParticle_C (p); else if (p->alpha <= 0.43) D_DrawParticle_33_C (p); else if (p->alpha <= 0.60) D_DrawParticle_50_C (p); else D_DrawParticle_66_C (p); //D_DrawParticle(p); //qb: disabled because no FOV scaling // renamed to not conflict with the asm version if (!sv_freezephysics.value || !allowcheats) { p->org[0] += p->vel[0] * frametime; p->org[1] += p->vel[1] * frametime; p->org[2] += p->vel[2] * frametime; switch (p->type) { case pt_static: break; case pt_fire: p->ramp += time1; if (p->ramp >= 6) p->die = -1; else p->color = ramp3[(int)p->ramp]; p->vel[2] += grav; break; case pt_explode: p->ramp += time2; // p->splatter = 0; if (p->ramp >=8) p->die = -1; else p->color = ramp1[(int)p->ramp]; for (i=0 ; i<3 ; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_explode2: p->ramp += time3; // p->splatter = 0; if (p->ramp >=8) p->die = -1; else p->color = ramp2[(int)p->ramp]; for (i=0 ; i<3 ; i++) p->vel[i] -= p->vel[i]*frametime; p->vel[2] -= grav; break; case pt_blob: for (i=0 ; i<3 ; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_blob2: for (i=0 ; i<2 ; i++) p->vel[i] -= p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_grav: p->vel[2] -= grav * 4; break; case pt_slowgrav: p->vel[2] -= grav * 2; break; case pt_fastgrav: p->vel[2] -= grav * 8; break; case pt_smoke: p->alpha = 1; p->vel[2] += grav; break; case pt_decel: p->alpha = 1; for (i=0 ; i<3 ; i++) p->vel[i] += p->vel[i]* -dvel; break; case pt_staticfade: p->alpha += frametime*p->alphavel; if (p->alpha <= 0) p->die = -1; break; case pt_staticfadeadd: p->alpha += frametime*p->alphavel; if (p->alpha <= 0) p->die = -1; break; case pt_sticky: //pt_blood in engoo... could be anything sticky VectorScale(p->vel, frametime, diff); VectorAdd(p->org, diff, p->org); // WHERE THE HITTING HAPPENS // if hit solid, go to last position, // no velocity, fade out. l = Mod_PointInLeaf (p->org, cl.worldmodel); if (l->contents == CONTENTS_WATER || l->contents == CONTENTS_SLIME) { p->vel[0] -= p->vel[0]*frametime*0.9; p->vel[1] -= p->vel[1]*frametime*0.9; p->vel[2] = grav; //qb: float up slowly } else if (l->contents != CONTENTS_EMPTY) // || in_solid == true { // still have small prob of snow melting on emitter VectorScale(diff, 0.2, p->vel); i = 6; while (l->contents != CONTENTS_EMPTY) { VectorNormalize(p->vel); p->org[0] -= p->vel[0]*3; p->org[1] -= p->vel[1]*3; p->org[2] -= p->vel[2]*3; i--; //no infinite loops if (!i) { p->die = -1; //should never happen now! break; } l = Mod_PointInLeaf (p->org, cl.worldmodel); } p->vel[0] = p->vel[1] = p->vel[2] = 0; p->ramp = 0; p->type = pt_staticfade; //qb: finally figured this out, was pt_static } else { p->vel[2] -= grav * 2; } break; } } } }
/* =============== R_SetupFrame =============== */ void R_SetupFrame (void) { int i; vrect_t vrect; if (r_fullbright->modified) { r_fullbright->modified = false; D_FlushCaches (); // so all lighting changes } r_framecount++; // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, modelorg); VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf if ( !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) { r_viewleaf = Mod_PointInLeaf (r_origin, r_worldmodel); r_viewcluster = r_viewleaf->cluster; } if (sw_waterwarp->value && (r_newrefdef.rdflags & RDF_UNDERWATER) ) r_dowarp = true; else r_dowarp = false; if (r_dowarp) { // warp into off screen buffer vrect.x = 0; vrect.y = 0; vrect.width = r_newrefdef.width < WARP_WIDTH ? r_newrefdef.width : WARP_WIDTH; vrect.height = r_newrefdef.height < WARP_HEIGHT ? r_newrefdef.height : WARP_HEIGHT; d_viewbuffer = r_warpbuffer; r_screenwidth = WARP_WIDTH; } else { vrect.x = r_newrefdef.x; vrect.y = r_newrefdef.y; vrect.width = r_newrefdef.width; vrect.height = r_newrefdef.height; d_viewbuffer = (void *)vid.buffer; r_screenwidth = vid.rowbytes; } R_ViewChanged (&vrect); // start off with just the four screen edge clip planes R_TransformFrustum (); R_SetUpFrustumIndexes (); // save base values VectorCopy (vpn, base_vpn); VectorCopy (vright, base_vright); VectorCopy (vup, base_vup); // clear frame counts c_faceclip = 0; d_spanpixcount = 0; r_polycount = 0; r_drawnpolycount = 0; r_wholepolycount = 0; r_amodels_drawn = 0; r_outofsurfaces = 0; r_outofedges = 0; // d_setup d_roverwrapped = false; d_initial_rover = sc_rover; d_minmip = sw_mipcap->value; if (d_minmip > 3) d_minmip = 3; else if (d_minmip < 0) d_minmip = 0; for (i=0 ; i<(NUM_MIPS-1) ; i++) d_scalemip[i] = basemip[i] * sw_mipscale->value; d_aflatcolor = 0; }
/* ==================== CreateSystem ==================== */ particle_system_t *CParticleEngine::CreateSystem( char *szPath, vec3_t origin, vec3_t dir, int iId, particle_system_t *parent ) { if(!strlen(szPath)) return NULL; char szFilePath[64]; strcpy(szFilePath, "/scripts/particles/"); strcat(szFilePath, szPath); char *pFile = (char *)gEngfuncs.COM_LoadFile(szFilePath, 5, NULL); if(!pFile) { gEngfuncs.Con_Printf("Could not load particle definitions file: %s!\n", szPath); return NULL; } particle_system_t *pSystem = AllocSystem(); if(!pSystem) { gEngfuncs.Con_Printf("Warning! Exceeded max number of particle systems!\n"); gEngfuncs.COM_FreeFile(pFile); return NULL; } // Fill in default values pSystem->id = iId; pSystem->mainalpha = 1; pSystem->spawntime = gEngfuncs.GetClientTime(); VectorCopy(dir, pSystem->dir); char *pToken = pFile; while(1) { char szField[32]; pToken = gEngfuncs.COM_ParseFile(pToken, szField); if(!pToken) break; char szValue[32]; pToken = gEngfuncs.COM_ParseFile(pToken, szValue); if(!pToken) break; if(!strcmp(szField, "systemshape")) pSystem->shapetype = atoi(szValue); else if(!strcmp(szField, "minvel")) pSystem->minvel = atof(szValue); else if(!strcmp(szField, "maxvel")) pSystem->maxvel = atof(szValue); else if(!strcmp(szField, "maxofs")) pSystem->maxofs = atof(szValue); else if(!strcmp(szField, "fadein")) pSystem->fadeintime = atof(szValue); else if(!strcmp(szField, "fadedelay")) pSystem->fadeoutdelay = atof(szValue); else if(!strcmp(szField, "mainalpha")) pSystem->mainalpha = atof(szValue); else if(!strcmp(szField, "veldamp")) pSystem->velocitydamp = atof(szValue); else if(!strcmp(szField, "veldampdelay")) pSystem->veldampdelay = atof(szValue); else if(!strcmp(szField, "life")) pSystem->maxlife = atof(szValue); else if(!strcmp(szField, "lifevar")) pSystem->maxlifevar = atof(szValue); else if(!strcmp(szField, "pcolr")) pSystem->primarycolor.x = (float)atoi(szValue)/255; else if(!strcmp(szField, "pcolg")) pSystem->primarycolor.y = (float)atoi(szValue)/255; else if(!strcmp(szField, "pcolb")) pSystem->primarycolor.z = (float)atoi(szValue)/255; else if(!strcmp(szField, "scolr")) pSystem->secondarycolor.x = (float)atoi(szValue)/255; else if(!strcmp(szField, "scolg")) pSystem->secondarycolor.y = (float)atoi(szValue)/255; else if(!strcmp(szField, "scolb")) pSystem->secondarycolor.z = (float)atoi(szValue)/255; else if(!strcmp(szField, "ctransd")) pSystem->transitiondelay = atof(szValue); else if(!strcmp(szField, "ctranst")) pSystem->transitiontime = atof(szValue); else if(!strcmp(szField, "ctransv")) pSystem->transitionvar = atof(szValue); else if(!strcmp(szField, "scale")) pSystem->scale = atof(szValue); else if(!strcmp(szField, "scalevar")) pSystem->scalevar = atof(szValue); else if(!strcmp(szField, "scaledampdelay")) pSystem->scaledampdelay = atof(szValue); else if(!strcmp(szField, "scaledampfactor")) pSystem->scaledampfactor = atof(szValue); else if(!strcmp(szField, "gravity")) pSystem->gravity = atof(szValue); else if(!strcmp(szField, "systemsize")) pSystem->systemsize = atoi(szValue); else if(!strcmp(szField, "maxparticles")) pSystem->maxparticles = atoi(szValue); else if(!strcmp(szField, "intensity")) pSystem->particlefreq = atof(szValue); else if(!strcmp(szField, "startparticles")) pSystem->startparticles = atoi(szValue); else if(!strcmp(szField, "maxparticlevar")) pSystem->maxparticlevar = atoi(szValue); else if(!strcmp(szField, "lightmaps")) pSystem->lightcheck = atoi(szValue); else if(!strcmp(szField, "collision")) pSystem->collision = atoi(szValue); else if(!strcmp(szField, "colwater")) pSystem->colwater = atoi(szValue); else if(!strcmp(szField, "rendermode")) pSystem->rendermode = atoi(szValue); else if(!strcmp(szField, "display")) pSystem->displaytype = atoi(szValue); else if(!strcmp(szField, "impactdamp")) pSystem->impactdamp = atof(szValue); else if(!strcmp(szField, "rotationvar")) pSystem->rotationvar = atof(szValue); else if(!strcmp(szField, "rotationvel")) pSystem->rotationvel = atof(szValue); else if(!strcmp(szField, "rotationdamp")) pSystem->rotationdamp = atof(szValue); else if(!strcmp(szField, "rotationdampdelay")) pSystem->rotationdampdelay = atof(szValue); else if(!strcmp(szField, "rotxvar")) pSystem->rotxvar = atof(szValue); else if(!strcmp(szField, "rotxvel")) pSystem->rotxvel = atof(szValue); else if(!strcmp(szField, "rotxdamp")) pSystem->rotxdamp = atof(szValue); else if(!strcmp(szField, "rotxdampdelay")) pSystem->rotxdampdelay = atof(szValue); else if(!strcmp(szField, "rotyvar")) pSystem->rotyvar = atof(szValue); else if(!strcmp(szField, "rotyvel")) pSystem->rotyvel = atof(szValue); else if(!strcmp(szField, "rotydamp")) pSystem->rotydamp = atof(szValue); else if(!strcmp(szField, "rotydampdelay")) pSystem->rotydampdelay = atof(szValue); else if(!strcmp(szField, "randomdir")) pSystem->randomdir = atoi(szValue); else if(!strcmp(szField, "overbright")) pSystem->overbright = atoi(szValue); else if(!strcmp(szField, "create")) strcpy(pSystem->create, szValue); else if(!strcmp(szField, "deathcreate")) strcpy(pSystem->deathcreate, szValue); else if(!strcmp(szField, "watercreate")) strcpy(pSystem->watercreate, szValue); else if(!strcmp(szField, "windx")) pSystem->windx = atof(szValue); else if(!strcmp(szField, "windy")) pSystem->windy = atof(szValue); else if(!strcmp(szField, "windvar")) pSystem->windvar = atof(szValue); else if(!strcmp(szField, "windtype")) pSystem->windtype = atoi(szValue); else if(!strcmp(szField, "windmult")) pSystem->windmult = atof(szValue); else if(!strcmp(szField, "windmultvar")) pSystem->windmultvar = atof(szValue); else if(!strcmp(szField, "stuckdie")) pSystem->stuckdie = atof(szValue); else if(!strcmp(szField, "maxheight")) pSystem->maxheight = atof(szValue); else if(!strcmp(szField, "tracerdist")) pSystem->tracerdist = atof(szValue); else if(!strcmp(szField, "fadedistnear")) pSystem->fadedistnear = atoi(szValue); else if(!strcmp(szField, "fadedistfar")) pSystem->fadedistfar = atoi(szValue); else if(!strcmp(szField, "numframes")) pSystem->numframes = atoi(szValue); else if(!strcmp(szField, "framesizex")) pSystem->framesizex = atoi(szValue); else if(!strcmp(szField, "framesizey")) pSystem->framesizey = atoi(szValue); else if(!strcmp(szField, "framerate")) pSystem->framerate = atoi(szValue); else if(!strcmp(szField, "texture")) { int iOriginalBind; glGetIntegerv(GL_TEXTURE_BINDING_2D, &iOriginalBind); char szTexPath[256]; strcpy(szTexPath, "gfx/textures/particles/"); strcat(szTexPath, szValue); strcat(szTexPath, ".dds"); pSystem->texture = gTextureLoader.LoadTexture(szTexPath); if(!pSystem->texture) { // Remove system m_pSystemHeader = pSystem->next; m_pSystemHeader->prev = NULL; delete [] pSystem; gEngfuncs.COM_FreeFile(pFile); return NULL; } glBindTexture(GL_TEXTURE_2D, pSystem->texture->iIndex); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glBindTexture(GL_TEXTURE_2D, iOriginalBind); } else gEngfuncs.Con_Printf("Warning! Unknown field: %s\n", szField); } gEngfuncs.COM_FreeFile(pFile); if(pSystem->shapetype != SYSTEM_SHAPE_PLANE_ABOVE_PLAYER) { if(!parent) { model_t *pWorld = IEngineStudio.GetModelByIndex(1); VectorCopy(origin, pSystem->origin); if(pWorld) pSystem->leaf = Mod_PointInLeaf(pSystem->origin, pWorld); } else { pSystem->leaf = parent->leaf; } } else { pmtrace_t tr; gEngfuncs.pEventAPI->EV_SetTraceHull(2); gEngfuncs.pEventAPI->EV_PlayerTrace(origin, origin + Vector(0, 0, 8496), PM_WORLD_ONLY, -1, &tr); if(tr.fraction == 1.0 || gEngfuncs.PM_PointContents(tr.endpos, NULL) != CONTENTS_SKY) { // Remove system m_pSystemHeader = pSystem->next; m_pSystemHeader->prev = NULL; delete [] pSystem; return NULL; } pSystem->skyheight = tr.endpos.z; } if(pSystem->collision != PARTICLE_COLLISION_DECAL) { if(pSystem->create[0] != 0) pSystem->createsystem = CreateSystem(pSystem->create, pSystem->origin, pSystem->dir, 0, pSystem); if(!pSystem->createsystem) memset(pSystem->create, 0, sizeof(pSystem->create)); } if(pSystem->watercreate[0] != 0) pSystem->watersystem = CreateSystem(pSystem->watercreate, pSystem->origin, pSystem->dir, 0, pSystem); if(!pSystem->watersystem) memset(pSystem->watercreate, 0, sizeof(pSystem->watercreate)); if(parent) { // Child systems cannot spawn on their own pSystem->parentsystem = parent; pSystem->maxparticles = NULL; pSystem->particlefreq = NULL; } else { if(pSystem->shapetype != SYSTEM_SHAPE_PLANE_ABOVE_PLAYER) { // create all starting particles for(int i = 0; i < pSystem->startparticles; i++) CreateParticle(pSystem); } else { // Create particles at random heights EnvironmentCreateFirst(pSystem); } } return pSystem; }
/* ================ R_DrawMirrors Draw all viewpasess from mirror position Mirror textures will be drawn in normal pass ================ */ void R_DrawMirrors( void ) { ref_instance_t oldRI; mplane_t plane; msurface_t *surf, *surf2; int i, oldframecount; mextrasurf_t *es, *tmp, *mirrorchain; vec3_t forward, right, up; vec3_t origin, angles; matrix4x4 mirrormatrix; cl_entity_t *e; model_t *m; float d; if( !tr.num_mirror_entities ) return; // mo mirrors for this frame oldRI = RI; // make refinst backup oldframecount = tr.framecount; for( i = 0; i < tr.num_mirror_entities; i++ ) { mirrorchain = tr.mirror_entities[i].chain; for( es = mirrorchain; es != NULL; es = es->mirrorchain ) { RI.currententity = e = tr.mirror_entities[i].ent; RI.currentmodel = m = RI.currententity->model; surf = INFO_SURF( es, m ); ASSERT( RI.currententity != NULL ); ASSERT( RI.currentmodel != NULL ); // NOTE: copy mirrortexture and mirrormatrix from another surfaces // from this entity\world that has same planes and reduce number of viewpasses // make sure what we have one pass at least if( es != mirrorchain ) { for( tmp = mirrorchain; tmp != es; tmp = tmp->mirrorchain ) { surf2 = INFO_SURF( tmp, m ); if( !tmp->mirrortexturenum ) continue; // not filled? if( surf->plane->dist != surf2->plane->dist ) continue; if( !VectorCompare( surf->plane->normal, surf2->plane->normal )) continue; // found surface with same plane! break; } if( tmp != es && tmp && tmp->mirrortexturenum ) { // just copy reflection texture from surface with same plane Matrix4x4_Copy( es->mirrormatrix, tmp->mirrormatrix ); es->mirrortexturenum = tmp->mirrortexturenum; continue; // pass skiped } } R_PlaneForMirror( surf, &plane, mirrormatrix ); d = -2.0f * ( DotProduct( RI.vieworg, plane.normal ) - plane.dist ); VectorMA( RI.vieworg, d, plane.normal, origin ); d = -2.0f * DotProduct( RI.vforward, plane.normal ); VectorMA( RI.vforward, d, plane.normal, forward ); VectorNormalize( forward ); d = -2.0f * DotProduct( RI.vright, plane.normal ); VectorMA( RI.vright, d, plane.normal, right ); VectorNormalize( right ); d = -2.0f * DotProduct( RI.vup, plane.normal ); VectorMA( RI.vup, d, plane.normal, up ); VectorNormalize( up ); VectorsAngles( forward, right, up, angles ); angles[ROLL] = -angles[ROLL]; RI.params = RP_MIRRORVIEW|RP_CLIPPLANE|RP_OLDVIEWLEAF; RI.clipPlane = plane; RI.clipFlags |= ( 1<<5 ); RI.frustum[5] = plane; RI.frustum[5].signbits = SignbitsForPlane( RI.frustum[5].normal ); RI.frustum[5].type = PLANE_NONAXIAL; RI.refdef.viewangles[0] = anglemod( angles[0] ); RI.refdef.viewangles[1] = anglemod( angles[1] ); RI.refdef.viewangles[2] = anglemod( angles[2] ); VectorCopy( origin, RI.refdef.vieworg ); VectorCopy( origin, RI.cullorigin ); // put pvsorigin before the mirror plane to avoid get full visibility on world mirrors if( RI.currententity == clgame.entities ) { VectorMA( es->origin, 1.0f, plane.normal, origin ); } else { Matrix4x4_VectorTransform( mirrormatrix, es->origin, origin ); VectorMA( origin, 1.0f, plane.normal, origin ); } VectorCopy( origin, RI.pvsorigin ); // combine two leafs from client and mirror views r_viewleaf = Mod_PointInLeaf( oldRI.pvsorigin, cl.worldmodel->nodes ); r_viewleaf2 = Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes ); if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) { // allow screen size RI.viewport[2] = bound( 96, RI.viewport[2], 1024 ); RI.viewport[3] = bound( 72, RI.viewport[3], 768 ); } else { RI.viewport[2] = NearestPOW( RI.viewport[2], true ); RI.viewport[3] = NearestPOW( RI.viewport[3], true ); RI.viewport[2] = bound( 128, RI.viewport[2], 1024 ); RI.viewport[3] = bound( 64, RI.viewport[3], 512 ); } tr.framecount++; R_RenderScene( &RI.refdef ); r_stats.c_mirror_passes++; es->mirrortexturenum = R_AllocateMirrorTexture(); // create personal projection matrix for mirror if( VectorIsNull( e->origin ) && VectorIsNull( e->angles )) { Matrix4x4_Copy( es->mirrormatrix, RI.worldviewProjectionMatrix ); } else { Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, mirrormatrix ); Matrix4x4_Concat( es->mirrormatrix, RI.projectionMatrix, RI.modelviewMatrix ); } RI = oldRI; // restore ref instance } // clear chain for this entity for( es = mirrorchain; es != NULL; ) { tmp = es->mirrorchain; es->mirrorchain = NULL; es = tmp; } tr.mirror_entities[i].chain = NULL; // done tr.mirror_entities[i].ent = NULL; } r_oldviewleaf = r_viewleaf = NULL; // force markleafs next frame tr.framecount = oldframecount; // restore real framecount tr.num_mirror_entities = 0; tr.num_mirrors_used = 0; }
void R_SetupFrame (void) { int edgecount; vrect_t vrect; float w, h; int dtail = 2; // leilei - screen detail // don't allow cheats in multiplayer if (cl.maxclients > 1) { Cvar_Set (r_draworder, "0"); Cvar_Set (r_fullbright, "0"); Cvar_Set (r_ambient, "0"); Cvar_Set (r_drawflat, "0"); } if (r_numsurfs->value) { if ((surface_p - surfaces) > r_maxsurfsseen) r_maxsurfsseen = surface_p - surfaces; Con_Printf ("Used %d of %d surfs; %d max\n", surface_p - surfaces, surf_max - surfaces, r_maxsurfsseen); } if (r_numedges->value) { edgecount = edge_p - r_edges; if (edgecount > r_maxedgesseen) r_maxedgesseen = edgecount; Con_Printf ("Used %d of %d edges; %d max\n", edgecount, r_numallocatededges, r_maxedgesseen); } r_refdef.ambientlight = r_ambient->value; if (r_refdef.ambientlight < 0) r_refdef.ambientlight = 0; if (!sv.active) r_draworder->value = 0; // don't let cheaters look behind walls R_CheckVariables (); R_AnimateLight (); r_framecount++; numbtofpolys = 0; VectorCopy (r_refdef.vieworg, modelorg); VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); scalefactoid = vid.height / vid.vconheight; // leilei r_dowarpold = r_dowarp; r_dowarp = r_waterwarp->value && (r_viewleaf->contents <= CONTENTS_WATER); inwat = s_underwater->value && (r_viewleaf->contents <= CONTENTS_WATER); if ((r_dowarp != r_dowarpold) || r_viewchanged || r_virtualmode->value || lcd_x->value || v_detail->value) { if (r_docrap == 1) { if ((vid.width <= vid.maxlowwidth) && (vid.height <= vid.maxlowheight)) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_SetVrect (&vrect, &r_refdef.vrect, 0); R_ViewChanged (&vrect, 0, vid.aspect); } else { w = vid.width; h = vid.height; if (w > vid.maxlowwidth) { h *= (float)vid.maxlowwidth / w; w = vid.maxlowwidth; } if (h > vid.maxlowheight) { h = vid.maxlowheight; w *= (float)vid.maxlowheight / h; } vrect.x = 0; vrect.y = 0; vrect.width = (int)w; vrect.height = (int)h; R_SetVrect (&vrect, &r_refdef.vrect, ((float)vid.height)); R_ViewChanged (&vrect, 0, vid.aspect * (h / w) * ((float)vid.width / (float)vid.height)); } } else if(r_docrap > 1) { if ((vid.width <= vid.maxwarpwidth) && (vid.height <= vid.maxwarpheight)) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_SetVrect (&vrect, &r_refdef.vrect, ((float)vid.height)); R_ViewChanged (&vrect, 0, vid.aspect); } else { w = vid.width; h = vid.height; if (w > vid.maxwarpwidth) { h *= (float)vid.maxwarpwidth / w; w = vid.maxwarpwidth; } if (h > vid.maxwarpheight) { h = vid.maxwarpheight; w *= (float)vid.maxwarpheight / h; } vrect.x = 0; vrect.y = 0; vrect.width = (int)w; vrect.height = (int)h; R_SetVrect (&vrect, &r_refdef.vrect, (h/(float)vid.height)); R_ViewChanged (&vrect, (h/(float)vid.height), vid.aspect * (h / w) * ((float)vid.width / (float)vid.height)); } } else { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_ViewChanged (&vrect, 0, vid.aspect); } r_viewchanged = false; } // start off with just the four screen edge clip planes R_TransformFrustum (); // save base values VectorCopy (vpn, base_vpn); VectorCopy (vright, base_vright); VectorCopy (vup, base_vup); VectorCopy (modelorg, base_modelorg); R_SetSkyFrame (); // Only do the sky moving on Quack R_SetUpFrustumIndexes (); r_cache_thrash = false; // clear frame counts c_faceclip = 0; d_spanpixcount = 0; r_polycount = 0; r_drawnpolycount = 0; r_wholepolycount = 0; r_amodels_drawn = 0; r_outofsurfaces = 0; r_outofedges = 0; // WarpPalette(); #ifdef EXPREND D_SetupFrameExperimental (); #else D_SetupFrame (); #endif }
void R_UpdateParticles (void) { particle_t *p, *kill; float grav,grav2,percent,speed; int i,j; qboolean in_solid; float time2, time3, time4; float time1; float dvel; float frametime; float vel0, vel1, vel2; float colindex; vec3_t diff,save_org; if (cls.state == ca_disconnected) return; frametime = cl.time - cl.oldtime; // Con_Printf("%10.5f\n",frametime); time4 = frametime * 20; time3 = frametime * 15; time2 = frametime * 10; time1 = frametime * 5; grav = frametime * sv_gravity.value * 0.05; grav2 = frametime * sv_gravity.value * 0.025; dvel = 4*frametime; percent = (frametime / HX_FRAME_TIME); for ( ;; ) { kill = active_particles; if (kill && kill->die < cl.time) { active_particles = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } for (p=active_particles ; p ; p=p->next) { for ( ;; ) { kill = p->next; if (kill && kill->die < cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } if (p->type==pt_rain) { vel0 = p->vel[0]*.001; vel1 = p->vel[1]*.001; vel2 = p->vel[2]*.001; for(i=0;i<4;i++) { p->org[0] += vel0; p->org[1] += vel1; p->org[2] += vel2; } p->org[0] += p->vel[0]*(frametime-.004); p->org[1] += p->vel[1]*(frametime-.004); p->org[2] += p->vel[2]*(frametime-.004); } else if (p->type==pt_snow) { if(p->vel[0]==0&&p->vel[1]==0&&p->vel[2]==0) {//Stopped moving if(p->color==256+31)//Most translucent white {//Go away p->die=-1; } else {//Count fifty and fade in translucency once each time p->ramp+=1; if(p->ramp>=7) { p->color+=1;//Get more translucent p->ramp=0; } } } else {//FIXME: If flake going fast enough, can go through, do a check in increments ot 10, max? //if not in_bounds Get length of diff, add in increments of 4 & check solid mleaf_t *l; if (Cvar_VariableValue("snow_flurry")==1) if(rand()&31) {//Add flurry movement float snow_speed; vec3_t save_vel; snow_speed = p->vel[0] * p->vel[0] + p->vel[1] * p->vel[1] + p->vel[2]*p->vel[2]; snow_speed = sqrt(snow_speed); VectorCopy(p->vel,save_vel); save_vel[0] += ( (rand()*(2.0/RAND_MAX)) - 1)*30; save_vel[1] += ( (rand()*(2.0/RAND_MAX)) - 1)*30; if((rand()&7)||p->vel[2]>10) save_vel[2] += ( (rand()*(2.0/RAND_MAX)) - 1)*30; VectorNormalize(save_vel); VectorScale(save_vel,snow_speed,p->vel);//retain speed but use new dir } /* VectorScale(p->vel,frametime,diff); speed = VectorNormalize(diff); in_solid=false; if(!(p->flags&SFL_IN_BOUNDS)) {//Not cut off by bounds if(speed>=8) {//Moving more than 8 pixels this turn for(i=4;i<speed;i+=4) {//Check for solid in increments of 4 VectorScale(diff,i,save_org); VectorAdd(p->org,save_org,save_org); // if(SV_PointContents(save_org)!=CONTENTS_EMPTY) l = Mod_PointInLeaf (save_org, cl.worldmodel); if (l->contents!=CONTENTS_EMPTY) { in_solid=true; VectorCopy(save_org,p->org); break; } } } } */ // if(!in_solid) { VectorScale(p->vel,frametime,diff); VectorAdd(p->org,diff,p->org); } if(p->flags&SFL_IN_BOUNDS) {//Always stay inside the boundry! if(p->org[0]<p->min_org[0]||p->org[0]>p->max_org[0]|| p->org[1]<p->min_org[1]||p->org[1]>p->max_org[1]|| p->org[2]<p->min_org[2]||p->org[2]>p->max_org[2]) { p->die=-1; } } else { //IF hit solid, go to last position, no velocity, fade out. l = Mod_PointInLeaf (p->org, cl.worldmodel); if(l->contents!=CONTENTS_EMPTY) //||in_solid==true { if(p->flags&SFL_NO_MELT) {//Don't melt, just die p->die=-1; } else {//still have small prob of snow melting on emitter VectorScale(diff,0.2,p->vel); i=6; while(l->contents!=CONTENTS_EMPTY ) { p->org[0] -= p->vel[0]; p->org[1] -= p->vel[1]; p->org[2] -= p->vel[2]; i--;//no infinite loops if (!i) { p->die=-1; //should never happen now! break; } l = Mod_PointInLeaf (p->org, cl.worldmodel); } p->vel[0]=p->vel[1]=p->vel[2]=0; p->ramp=0; } } } } } else { p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; } switch (p->type) { case pt_static: break; case pt_fire: p->ramp += time1; if ((int)p->ramp >= 6) { p->die = -1; } else { p->color = ramp3[(int)p->ramp]; } p->vel[2] += grav; break; case pt_explode: p->ramp += time2; if ((int)p->ramp >=8) { p->die = -1; } else { p->color = ramp1[(int)p->ramp]; } for (i=0 ; i<3 ; i++) { p->vel[i] += p->vel[i]*dvel; } p->vel[2] -= grav; break; case pt_explode2: p->ramp += time3; if ((int)p->ramp >=8) { p->die = -1; } else { p->color = ramp2[(int)p->ramp]; } for (i=0 ; i<3 ; i++) { p->vel[i] -= p->vel[i]*frametime; } p->vel[2] -= grav; break; case pt_c_explode: p->ramp += time2; if ((int)p->ramp >=8) { p->die = -1; } else if (time2) { p->color--; } for (i=0 ; i<3 ; i++) { p->vel[i] += p->vel[i]*dvel; } p->vel[2] -= grav; break; case pt_c_explode2: p->ramp += time3; if ((int)p->ramp >=8) { p->die = -1; } else if (time3) { p->color -= 2; } for (i=0 ; i<3 ; i++) { p->vel[i] -= p->vel[i]*frametime; } p->vel[2] -= grav; break; /* //jfm:not used case pt_blob: for (i=0 ; i<3 ; i++) { p->vel[i] += p->vel[i]*dvel; } p->vel[2] -= grav; break; case pt_blob2: for (i=0 ; i<2 ; i++) { p->vel[i] -= p->vel[i]*dvel; } p->vel[2] -= grav; break; */ case pt_grav: #ifdef QUAKE2 p->vel[2] -= grav * 20; break; #endif case pt_slowgrav: p->vel[2] -= grav; break; case pt_fastgrav: p->vel[2] -= grav*4; break; case pt_rain: break; case pt_snow: break; case pt_fireball: p->ramp += time3; if ((int)p->ramp >= 16) { p->die = -1; } else { p->color = ramp4[(int)p->ramp]; } break; case pt_acidball: p->ramp += time4*1.4; if ((int)p->ramp >= 23) { p->die = -1; } else if ((int)p->ramp >= 15) { p->color = ramp11[(int)p->ramp - 15]; } else { p->color = ramp10[(int)p->ramp]; } p->vel[2] -= grav; break; case pt_spit: p->ramp += time3; if ((int)p->ramp >= 16) { p->die = -1; } else { p->color = ramp6[(int)p->ramp]; } // p->vel[2] += grav*2; break; case pt_ice: p->ramp += time4; if ((int)p->ramp >= 16) { p->die = -1; } else { p->color = ramp5[(int)p->ramp]; } p->vel[2] -= grav; break; case pt_spell: p->ramp += time2; if ((int)p->ramp >= 16) { p->die = -1; } else { p->color = ramp7[(int)p->ramp]; } // p->vel[2] += grav*2; break; case pt_test: p->vel[2] += 1.3; p->ramp += time3; if ((int)p->ramp >= 13 || ((int)p->ramp > 10 && (int)p->vel[2] < 20) ) { p->die = -1; } else { p->color = ramp8[(int)p->ramp]; } break; case pt_quake: p->vel[0] *= 1.05; p->vel[1] *= 1.05; p->vel[2] -= grav*4; break; case pt_rd: if (!frametime) { break; } p->ramp += percent; if ((int)p->ramp > 50) { p->ramp = 50; p->die = -1; } p->color = 256+16+16 - (p->ramp/(50/16)); VectorSubtract(rider_origin, p->org, diff); /* p->org[0] += diff[0] * p->ramp / 80; p->org[1] += diff[1] * p->ramp / 80; p->org[2] += diff[2] * p->ramp / 80; */ vel0 = 1 / (51 - p->ramp); p->org[0] += diff[0] * vel0; p->org[1] += diff[1] * vel0; p->org[2] += diff[2] * vel0; break; case pt_gravwell: if (!frametime) { break; } p->ramp += percent; if ((int)p->ramp > 35) { p->ramp = 35; p->die = -1; } VectorSubtract(rider_origin, p->org, diff); /* p->org[0] += diff[0] * p->ramp / 80; p->org[1] += diff[1] * p->ramp / 80; p->org[2] += diff[2] * p->ramp / 80; */ vel0 = 1 / (36 - p->ramp); p->org[0] += diff[0] * vel0; p->org[1] += diff[1] * vel0; p->org[2] += diff[2] * vel0; break; case pt_vorpal: --p->color; if ((int)p->color <= 37 + 256) { p->die = -1; } break; case pt_setstaff: p->ramp += time1; if ((int)p->ramp >= 16) { p->die = -1; } else { p->color = ramp9[(int)p->ramp]; } p->vel[0] *= 1.08 * percent; p->vel[1] *= 1.08 * percent; p->vel[2] -= grav2; break; case pt_redfire: p->ramp += frametime*3; if ((int)p->ramp >= 8) { p->die = -1; } else { p->color = ramp12[(int)p->ramp]+256; } p->vel[0] *= .9; p->vel[1] *= .9; p->vel[2] += grav/2; break; case pt_magicmissile: --p->color; if ((int)p->color < 149) { p->color = 149; } p->ramp += time1; if ((int)p->ramp > 16) { p->die = -1; } break; case pt_boneshard: --p->color; if ((int)p->color < 368) { p->die = -1; } break; case pt_scarab: --p->color; if ((int)p->color < 250) { p->die = -1; } break; case pt_darken: p->vel[2] -= grav; //Also gravity --p->color; colindex=0; while(colindex<224) { if(colindex==192 || colindex == 200) { colindex+=8; } else { colindex+=16; } if (p->color==colindex) { p->die = -1; } } break; } } }
/* =============== R_SnowEffect MG =============== */ void R_SnowEffect (vec3_t org1,vec3_t org2,int flags,vec3_t alldir,int count) { int i,j,holdint; particle_t *p; mleaf_t *l; count *= Cvar_VariableValue("snow_active"); for (i=0 ; i<count ; i++) { p = AllocParticle(); if (!p) return; p->vel[0] = alldir[0]; //X and Y motion p->vel[1] = alldir[1]; p->vel[2] = alldir[2] * ((rand() & 15) + 7)/10; p->flags = flags; #ifdef GLQUAKE if(rand()&0x7f<=1)//have a console variable 'happy_snow' that makes all snowflakes happy snow! p->count = 69; //happy snow! else if(flags & SFL_FLUFFY || (flags&SFL_MIXED && (rand()&3))) p->count = (rand()&31)+10;//From 10 to 41 scale, will be divided else p->count = 10; #else if(flags & SFL_FLUFFY || (flags&SFL_MIXED && (rand()&3))) p->count = (rand()&3)+2;//From 2 to 5 extra else p->count = 1; //Only one particle #endif if(flags&SFL_HALF_BRIGHT)//Start darker p->color = 26 + (rand()%5); else p->color = 18 + (rand()%12); if(!(flags&SFL_NO_TRANS))//Start translucent p->color += 256; p->die = cl.time + 7; p->ramp = (rand()&3); //p->veer = veer; p->type = pt_snow; holdint=org2[0] - org1[0]; p->org[0] = org1[0] + (rand() % holdint); holdint=org2[1] - org1[1]; p->org[1] = org1[1] + (rand() % holdint); p->org[2] = org2[2]; j=50; l = Mod_PointInLeaf (p->org, cl.worldmodel); // while(SV_PointContents(p->org)!=CONTENTS_EMPTY && j<50) while(l->contents!=CONTENTS_EMPTY && j) {//Make sure it doesn't start in a solid holdint=org2[0] - org1[0]; p->org[0] = org1[0] + (rand() % holdint); holdint=org2[1] - org1[1]; p->org[1] = org1[1] + (rand() % holdint); j--;//No infinite loops l = Mod_PointInLeaf (p->org, cl.worldmodel); } if(l->contents!=CONTENTS_EMPTY) Sys_Error ("Snow entity top plane is not in an empty area (sorry!)"); VectorCopy(org1,p->min_org); VectorCopy(org2,p->max_org); } }
/* * R_AddLightOccluder */ bool R_AddLightOccluder( const entity_t *ent ) { int i; float maxSide; vec3_t origin; unsigned int hash_key; shadowGroup_t *group; mleaf_t *leaf; vec3_t mins, maxs, bbox[8]; bool bmodelRotated = false; if( rn.refdef.rdflags & RDF_NOWORLDMODEL ) { return false; } if( !ent->model || ent->model->type == mod_brush ) { return false; } VectorCopy( ent->lightingOrigin, origin ); if( ent->model->type == mod_brush ) { vec3_t t; VectorAdd( ent->model->mins, ent->model->maxs, t ); VectorMA( ent->origin, 0.5, t, origin ); } if( VectorCompare( origin, vec3_origin ) ) { return false; } // find lighting group containing entities with same lightingOrigin as ours hash_key = (unsigned int)( origin[0] * 7 + origin[1] * 5 + origin[2] * 3 ); hash_key &= ( SHADOWGROUPS_HASH_SIZE - 1 ); for( group = r_shadowGroups_hash[hash_key]; group; group = group->hashNext ) { if( VectorCompare( group->origin, origin ) ) { goto add; // found an existing one, add } } if( rsc.numShadowGroups == MAX_SHADOWGROUPS ) { return false; // no free groups } leaf = Mod_PointInLeaf( origin, rsh.worldModel ); // start a new group group = &rsc.shadowGroups[rsc.numShadowGroups]; memset( group, 0, sizeof( *group ) ); group->id = group - rsc.shadowGroups + 1; group->bit = ( 1 << rsc.numShadowGroups ); group->vis = Mod_ClusterPVS( leaf->cluster, rsh.worldModel ); group->useOrtho = true; group->alpha = r_shadows_alpha->value; // clear group bounds VectorCopy( origin, group->origin ); ClearBounds( group->mins, group->maxs ); ClearBounds( group->visMins, group->visMaxs ); // add to hash table group->hashNext = r_shadowGroups_hash[hash_key]; r_shadowGroups_hash[hash_key] = group; rsc.numShadowGroups++; add: // get model bounds if( ent->model->type == mod_alias ) { R_AliasModelBBox( ent, mins, maxs ); } else if( ent->model->type == mod_skeletal ) { R_SkeletalModelBBox( ent, mins, maxs ); } else if( ent->model->type == mod_brush ) { R_BrushModelBBox( ent, mins, maxs, &bmodelRotated ); } else { ClearBounds( mins, maxs ); } maxSide = 0; for( i = 0; i < 3; i++ ) { if( mins[i] >= maxs[i] ) { return false; } maxSide = max( maxSide, maxs[i] - mins[i] ); } // ignore tiny objects if( maxSide < 10 ) { return false; } rsc.entShadowGroups[R_ENT2NUM( ent )] = group->id; if( ent->flags & RF_WEAPONMODEL ) { return true; } if( ent->model->type == mod_brush ) { VectorCopy( mins, group->mins ); VectorCopy( maxs, group->maxs ); } else { // rotate local bounding box and compute the full bounding box for this group R_TransformBounds( ent->origin, ent->axis, mins, maxs, bbox ); for( i = 0; i < 8; i++ ) { AddPointToBounds( bbox[i], group->mins, group->maxs ); } } // increase projection distance if needed VectorSubtract( group->mins, origin, mins ); VectorSubtract( group->maxs, origin, maxs ); group->radius = RadiusFromBounds( mins, maxs ); group->projDist = max( group->projDist, group->radius + min( r_shadows_projection_distance->value, 64.0f ) ); return true; }