/* * R_TraceLine */ msurface_t *R_TraceLine( rtrace_t *tr, const vec3_t start, const vec3_t end, int surfumask ) { unsigned int i; msurface_t *surf; if( !rsh.worldBrushModel ) { return NULL; } if( rsc.worldent->model != rsh.worldModel ) { // may happen if new client frame arrives before world model registration return NULL; } // trace against world surf = R_TransformedTraceLine( tr, start, end, rsc.worldent, surfumask ); // trace against bmodels for( i = 0; i < rsc.numBmodelEntities; i++ ) { rtrace_t t2; msurface_t *s2; s2 = R_TransformedTraceLine( &t2, start, end, R_NUM2ENT( rsc.bmodelEntities[i] ), surfumask ); if( t2.fraction < tr->fraction ) { *tr = t2; // closer impact point surf = s2; } } return surf; }
/* * R_AddEntityToScene */ void R_AddEntityToScene( const entity_t *ent ) { if( !r_drawentities->integer ) return; if( ( rsc.numEntities < MAX_ENTITIES ) && ent ) { int eNum = rsc.numEntities; entity_t *de = R_NUM2ENT(eNum); *de = *ent; if( r_outlines_scale->value <= 0 ) de->outlineHeight = 0; rsc.entShadowBits[eNum] = 0; rsc.entShadowGroups[eNum] = 0; if( de->rtype == RT_MODEL ) { if( de->model && de->model->type == mod_brush ) { rsc.bmodelEntities[rsc.numBmodelEntities++] = de; } if( !(de->renderfx & RF_NOSHADOW) ) { R_AddLightOccluder( de ); // build groups and mark shadow casters } } if( de->renderfx & RF_ALPHAHACK ) { if( de->shaderRGBA[3] == 255 ) { de->renderfx &= ~RF_ALPHAHACK; } } rsc.numEntities++; } }
/* * R_ClearScene */ void R_ClearScene( void ) { rsc.numDlights = 0; rsc.numPolys = 0; rsc.numEntities = 0; rsc.worldent = R_NUM2ENT(0); rsc.worldent->scale = 1.0f; rsc.worldent->model = rsh.worldModel; rsc.worldent->rtype = RT_MODEL; Matrix3_Identity( rsc.worldent->axis ); rsc.numEntities = 1; rsc.numBmodelEntities = 0; rsc.debugSurface = NULL; rsc.renderedShadowBits = 0; rsc.frameCount++; R_ClearDebugBounds(); R_ClearShadowGroups(); R_ClearSkeletalCache(); }
/* * R_RenderDebugSurface */ static void R_RenderDebugSurface( const refdef_t *fd ) { rtrace_t tr; vec3_t forward; vec3_t start, end; msurface_t *surf; if( fd->rdflags & RDF_NOWORLDMODEL ) return; if( r_speeds->integer != 4 && r_speeds->integer != 5 ) return; VectorCopy( &fd->viewaxis[AXIS_FORWARD], forward ); VectorCopy( fd->vieworg, start ); VectorMA( start, 4096, forward, end ); surf = R_TraceLine( &tr, start, end, 0 ); if( surf && surf->drawSurf && !r_showtris->integer ) { R_ClearDrawList( rn.meshlist ); R_ClearDrawList( rn.portalmasklist ); if( !R_AddSurfToDrawList( rn.meshlist, R_NUM2ENT(tr.ent), NULL, surf->shader, 0, 0, NULL, surf->drawSurf ) ) { return; } if( rn.refdef.rdflags & RDF_FLIPPED ) RB_FlipFrontFace(); rsc.debugSurface = surf; if( r_speeds->integer == 5 ) { // VBO debug mode R_AddVBOSlice( surf->drawSurf - rsh.worldBrushModel->drawSurfaces, surf->drawSurf->numVerts, surf->drawSurf->numElems, 0, 0 ); } else { // classic mode (showtris for individual surface) R_AddVBOSlice( surf->drawSurf - rsh.worldBrushModel->drawSurfaces, surf->mesh->numVerts, surf->mesh->numElems, surf->firstDrawSurfVert, surf->firstDrawSurfElem ); } R_DrawOutlinedSurfaces( rn.meshlist ); if( rn.refdef.rdflags & RDF_FLIPPED ) RB_FlipFrontFace(); } }
/* * R_AddEntityToScene */ void R_AddEntityToScene( const entity_t *ent ) { if( !r_drawentities->integer ) return; if( ( ( rsc.numEntities - rsc.numLocalEntities ) < MAX_ENTITIES ) && ent ) { int eNum = rsc.numEntities; entity_t *de = R_NUM2ENT(eNum); *de = *ent; if( r_outlines_scale->value <= 0 ) de->outlineHeight = 0; rsc.entShadowBits[eNum] = 0; rsc.entShadowGroups[eNum] = 0; if( de->rtype == RT_MODEL ) { if( de->model && de->model->type == mod_brush ) { rsc.bmodelEntities[rsc.numBmodelEntities++] = de; } if( !(de->renderfx & RF_NOSHADOW) ) { R_AddLightOccluder( de ); // build groups and mark shadow casters } } else if( de->rtype == RT_SPRITE ) { // simplifies further checks de->model = NULL; } if( de->renderfx & RF_ALPHAHACK ) { if( de->shaderRGBA[3] == 255 ) { de->renderfx &= ~RF_ALPHAHACK; } } rsc.numEntities++; // add invisible fake entity for depth write if( (de->renderfx & (RF_WEAPONMODEL|RF_ALPHAHACK)) == (RF_WEAPONMODEL|RF_ALPHAHACK) ) { entity_t tent = *ent; tent.renderfx &= ~RF_ALPHAHACK; tent.renderfx |= RF_NOCOLORWRITE|RF_NOSHADOW; R_AddEntityToScene( &tent ); } } }
/* * 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(); }