int R_DrawCinematic( msurface_t *surf, texture_t *t ) { if( !RI.currententity->curstate.body ) { GL_Bind( GL_TEXTURE0, t->gl_texturenum ); return 0; // just disabled } // draw the cinematic mextrasurf_t *es = SURF_INFO( surf, RI.currentmodel ); // found the corresponding cinstate const char *cinname = gEngfuncs.pEventAPI->EV_EventForIndex( RI.currententity->curstate.sequence ); int cinhandle = R_PrecacheCinematic( cinname ); if( cinhandle >= 0 && es->mirrortexturenum <= 0 ) es->mirrortexturenum = R_AllocateCinematicTexture( TF_SCREEN ); if( cinhandle == -1 || es->mirrortexturenum <= 0 || CIN_IS_ACTIVE( tr.cinematics[cinhandle].state ) == false ) { // cinematic textures limit exceeded, so remove SURF_MOVIE flag GL_Bind( GL_TEXTURE0, t->gl_texturenum ); surf->flags &= ~SURF_MOVIE; return 0; } gl_movie_t *cin = &tr.cinematics[cinhandle]; float cin_time; if( RI.currententity->curstate.iuser1 & CF_LOOPED_MOVIE ) { // advances cinematic time cin_time = fmod( RI.currententity->curstate.fuser2, cin->length ); } else { cin_time = RI.currententity->curstate.fuser2; } // read the next frame int cin_frame = CIN_GET_FRAME_NUMBER( cin->state, cin_time ); if( cin_frame != es->checkcount ) { GL_SelectTexture( GL_TEXTURE0 ); // upload the new frame byte *raw = CIN_GET_FRAMEDATA( cin->state, cin_frame ); CIN_UPLOAD_FRAME( tr.cinTextures[es->mirrortexturenum-1], cin->xres, cin->yres, cin->xres, cin->yres, raw ); es->checkcount = cin_frame; } else { // have valid cinematic texture GL_Bind( GL_TEXTURE0, tr.cinTextures[es->mirrortexturenum-1] ); } return 1; }
/* ================ R_BeginDrawMirror Setup texture matrix for mirror texture ================ */ void R_BeginDrawMirror( msurface_t *fa ) { matrix4x4 m1, m2, matrix; GLfloat genVector[4][4]; mextrasurf_t *es; int i; es = SURF_INFO( fa, RI.currentmodel ); Matrix4x4_Copy( matrix, es->mirrormatrix ); Matrix4x4_LoadIdentity( m1 ); Matrix4x4_ConcatScale( m1, 0.5f ); Matrix4x4_Concat( m2, m1, matrix ); Matrix4x4_LoadIdentity( m1 ); Matrix4x4_ConcatTranslate( m1, 0.5f, 0.5f, 0.5f ); Matrix4x4_Concat( matrix, m1, m2 ); for( i = 0; i < 4; i++ ) { genVector[0][i] = i == 0 ? 1 : 0; genVector[1][i] = i == 1 ? 1 : 0; genVector[2][i] = i == 2 ? 1 : 0; genVector[3][i] = i == 3 ? 1 : 0; } GL_TexGen( GL_S, GL_OBJECT_LINEAR ); GL_TexGen( GL_T, GL_OBJECT_LINEAR ); GL_TexGen( GL_R, GL_OBJECT_LINEAR ); GL_TexGen( GL_Q, GL_OBJECT_LINEAR ); pglTexGenfv( GL_S, GL_OBJECT_PLANE, genVector[0] ); pglTexGenfv( GL_T, GL_OBJECT_PLANE, genVector[1] ); pglTexGenfv( GL_R, GL_OBJECT_PLANE, genVector[2] ); pglTexGenfv( GL_Q, GL_OBJECT_PLANE, genVector[3] ); GL_LoadTexMatrix( matrix ); }
/* ================= R_FindBmodelMirrors Check all bmodel surfaces and make personal mirror chain ================= */ void R_FindBmodelMirrors( cl_entity_t *e, qboolean static_entity ) { mextrasurf_t *extrasurf; vec3_t mins, maxs; msurface_t *psurf; model_t *clmodel; qboolean rotated; int i, clipFlags; clmodel = e->model; if( static_entity ) { Matrix4x4_LoadIdentity( RI.objectMatrix ); if( R_CullBox( clmodel->mins, clmodel->maxs, RI.clipFlags )) return; VectorCopy( RI.cullorigin, tr.modelorg ); clipFlags = RI.clipFlags; } else { if( !VectorIsNull( e->angles )) { for( i = 0; i < 3; i++ ) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } rotated = true; } else { VectorAdd( e->origin, clmodel->mins, mins ); VectorAdd( e->origin, clmodel->maxs, maxs ); rotated = false; } if( R_CullBox( mins, maxs, RI.clipFlags )) return; if( !VectorIsNull( e->origin ) || !VectorIsNull( e->angles )) { if( rotated ) Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f ); else Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, 1.0f ); } else Matrix4x4_LoadIdentity( RI.objectMatrix ); e->visframe = tr.framecount; // visible if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg ); else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg ); clipFlags = 0; } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) { if(!( psurf->flags & SURF_REFLECT )) continue; if( R_CullSurface( psurf, clipFlags )) continue; extrasurf = SURF_INFO( psurf, RI.currentmodel ); extrasurf->mirrorchain = tr.mirror_entities[tr.num_mirror_entities].chain; tr.mirror_entities[tr.num_mirror_entities].chain = extrasurf; } // store new mirror entity if( !static_entity && tr.mirror_entities[tr.num_mirror_entities].chain != NULL ) { tr.mirror_entities[tr.num_mirror_entities].ent = RI.currententity; tr.num_mirror_entities++; } }
/* ================ R_RecursiveMirrorNode ================ */ void R_RecursiveMirrorNode( mnode_t *node, uint clipflags ) { mextrasurf_t *extrasurf; const mplane_t *clipplane; int i, clipped; msurface_t *surf, **mark; mleaf_t *pleaf; int c, side; float dot; if( node->contents == CONTENTS_SOLID ) return; // hit a solid leaf if( node->visframe != tr.visframecount ) return; if( clipflags ) { for( i = 0, clipplane = RI.frustum; i < 6; i++, clipplane++ ) { if(!( clipflags & ( 1<<i ))) continue; clipped = BoxOnPlaneSide( node->minmaxs, node->minmaxs + 3, clipplane ); if( clipped == 2 ) return; if( clipped == 1 ) clipflags &= ~(1<<i); } } // if a leaf node, draw stuff if( node->contents < 0 ) { pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if( c ) { do { (*mark)->visframe = tr.framecount; mark++; } while( --c ); } return; } // node is just a decision point, so go down the apropriate sides // find which side of the node we are on dot = PlaneDiff( tr.modelorg, node->plane ); side = (dot >= 0) ? 0 : 1; // recurse down the children, front side first R_RecursiveMirrorNode( node->children[side], clipflags ); // draw stuff for( c = node->numsurfaces, surf = cl.worldmodel->surfaces + node->firstsurface; c; c--, surf++ ) { if(!( surf->flags & SURF_REFLECT )) continue; if( R_CullSurface( surf, clipflags )) continue; extrasurf = SURF_INFO( surf, RI.currentmodel ); extrasurf->mirrorchain = tr.mirror_entities[0].chain; tr.mirror_entities[0].chain = extrasurf; } // recurse down the back side R_RecursiveMirrorNode( node->children[!side], clipflags ); }
/* ================ R_ReLightGrass We already have a valid spot on texture Just find lightmap point and update grass color ================ */ void R_ReLightGrass( msurface_t *surf, bool force ) { mextrasurf_t *es = SURF_INFO( surf, RI.currentmodel ); grasshdr_t *hdr = es->grass; if( !hdr ) return; for( int maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++ ) { if( force || ( RI.lightstylevalue[surf->styles[maps]] != hdr->cached_light[maps] )) { unsigned int lightcolor[3]; grass_t *g = hdr->g; for( int i = 0; i < hdr->count; i++, g++ ) { if( !RI.currentmodel->lightdata ) { // just to get fullbright g->color[0] = g->color[1] = g->color[2] = 255; continue; } if( !surf->samples ) { // no light here g->color[0] = g->color[1] = g->color[2] = 0; continue; } mtexinfo_t *tex = surf->texinfo; // NOTE: don't need to call R_LightForPoint here because we already have a valid surface spot // just read lightmap color and out int s = (DotProduct( g->pos, tex->vecs[0] ) + tex->vecs[0][3] - surf->texturemins[0]) / LM_SAMPLE_SIZE; int t = (DotProduct( g->pos, tex->vecs[1] ) + tex->vecs[1][3] - surf->texturemins[1]) / LM_SAMPLE_SIZE; color24 *lm = surf->samples + (t * ((surf->extents[0] / LM_SAMPLE_SIZE ) + 1) + s); int size = ((surf->extents[0] / LM_SAMPLE_SIZE) + 1) * ((surf->extents[1] / LM_SAMPLE_SIZE) + 1); lightcolor[0] = lightcolor[1] = lightcolor[2] = 0; for( int map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ ) { unsigned int scale = RI.lightstylevalue[surf->styles[map]]; lightcolor[0] += TEXTURE_TO_TEXGAMMA( lm->r ) * scale; lightcolor[1] += TEXTURE_TO_TEXGAMMA( lm->g ) * scale; lightcolor[2] += TEXTURE_TO_TEXGAMMA( lm->b ) * scale; lm += size; // skip to next lightmap } // convert to normal rangle g->color[0] = min((lightcolor[0] >> 7), 255 ); g->color[1] = min((lightcolor[1] >> 7), 255 ); g->color[2] = min((lightcolor[2] >> 7), 255 ); for( int j = 0; j < 16; j++ ) *(int *)g->mesh[j].c = *(int *)g->color; } // NOTE: 'maps' used twice but is no matter because we breaking main cycle for( maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++ ) hdr->cached_light[maps] = RI.lightstylevalue[surf->styles[maps]]; break; } }
/* ================= R_CullSurface cull invisible surfaces ================= */ bool R_CullSurfaceExt( msurface_t *surf, const mplane_t frustum[6], unsigned int clipflags ) { mextrasurf_t *info; cl_entity_t *e = RI.currententity; if( !surf || !surf->texinfo || !surf->texinfo->texture ) return true; if( surf->flags & SURF_WATERCSG && !( RI.currententity->curstate.effects & EF_NOWATERCSG )) return true; if( surf->flags & SURF_NOCULL ) return false; // don't cull transparent surfaces because we should be draw decals on them if( surf->pdecals && ( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd )) return false; if( r_nocull->value ) return false; // world surfaces can be culled by vis frame too if( RI.currententity == GET_ENTITY( 0 ) && surf->visframe != tr.framecount ) return true; if( r_faceplanecull->value && glState.faceCull != 0 ) { if(!(surf->flags & SURF_DRAWTURB) || !RI.currentWaveHeight ) { if( surf->plane->normal != g_vecZero ) { float dist; if( RI.drawOrtho ) dist = surf->plane->normal[2]; else dist = PlaneDiff( tr.modelorg, surf->plane ); if( glState.faceCull == GL_FRONT || ( RI.params & RP_MIRRORVIEW )) { if( surf->flags & SURF_PLANEBACK ) { if( dist >= -BACKFACE_EPSILON ) return true; // wrong side } else { if( dist <= BACKFACE_EPSILON ) return true; // wrong side } } else if( glState.faceCull == GL_BACK ) { if( surf->flags & SURF_PLANEBACK ) { if( dist <= BACKFACE_EPSILON ) return true; // wrong side } else { if( dist >= -BACKFACE_EPSILON ) return true; // wrong side } } } } } info = SURF_INFO( surf, RI.currentmodel ); return ( clipflags && R_CullBoxExt( frustum, info->mins, info->maxs, clipflags )); }