/* * CG_ViewWeapon_UpdateProjectionSource */ static void CG_ViewWeapon_UpdateProjectionSource( vec3_t hand_origin, mat3_t hand_axis, vec3_t weap_origin, mat3_t weap_axis ) { orientation_t *tag_result = &cg.weapon.projectionSource; orientation_t tag_weapon; weaponinfo_t *weaponInfo; VectorCopy( vec3_origin, tag_weapon.origin ); Matrix3_Copy( axis_identity, tag_weapon.axis ); // move to tag_weapon CG_MoveToTag( tag_weapon.origin, tag_weapon.axis, hand_origin, hand_axis, weap_origin, weap_axis ); weaponInfo = CG_GetWeaponInfo( cg.weapon.weapon ); // move to projectionSource tag if( weaponInfo ) { VectorCopy( vec3_origin, tag_result->origin ); Matrix3_Copy( axis_identity, tag_result->axis ); CG_MoveToTag( tag_result->origin, tag_result->axis, tag_weapon.origin, tag_weapon.axis, weaponInfo->tag_projectionsource.origin, weaponInfo->tag_projectionsource.axis ); return; } // fall back: copy gun origin and move it front by 16 units and 8 up VectorCopy( tag_weapon.origin, tag_result->origin ); Matrix3_Copy( tag_weapon.axis, tag_result->axis ); VectorMA( tag_result->origin, 16, &tag_result->axis[AXIS_FORWARD], tag_result->origin ); VectorMA( tag_result->origin, 8, &tag_result->axis[AXIS_UP], tag_result->origin ); }
/* * CG_RegisterModels */ static void CG_RegisterModels( void ) { int i; char *name; name = cgs.configStrings[CS_WORLDMODEL]; if( name[0] ) { trap_R_RegisterWorldModel( name ); CG_LoadingString( name ); } CG_LoadingString( "models" ); cgs.numWeaponModels = 1; Q_strncpyz( cgs.weaponModels[0], "generic/generic.md3", sizeof( cgs.weaponModels[0] ) ); for( i = 1; i < MAX_MODELS; i++ ) { name = cgs.configStrings[CS_MODELS+i]; if( !name[0] ) break; if( name[0] == '#' ) { // special player weapon model if( cgs.numWeaponModels < WEAP_TOTAL ) { Q_strncpyz( cgs.weaponModels[cgs.numWeaponModels], name+1, sizeof( cgs.weaponModels[cgs.numWeaponModels] ) ); cgs.numWeaponModels++; CG_LoadingItemName( name ); } } else if( name[0] == '$' ) { // indexed pmodel cgs.pModelsIndex[i] = CG_RegisterPlayerModel( name+1 ); CG_LoadingItemName( name ); } else { CG_LoadingItemName( name ); cgs.modelDraw[i] = CG_RegisterModel( name ); } } CG_RegisterMediaModels(); CG_RegisterBasePModel(); // never before registering the weapon models CG_RegisterWeaponModels(); // precache forcemodels if defined CG_RegisterForceModels(); // create a tag to offset the weapon models when seen in the world as items VectorSet( cgs.weaponItemTag.origin, 0, 0, 0 ); Matrix3_Copy( axis_identity, cgs.weaponItemTag.axis ); VectorMA( cgs.weaponItemTag.origin, -14, &cgs.weaponItemTag.axis[AXIS_FORWARD], cgs.weaponItemTag.origin ); }
/* * CG_PModel_SpawnTeleportEffect */ void CG_PModel_SpawnTeleportEffect( centity_t *cent ) { int j; cgs_skeleton_t *skel; lentity_t *le; vec3_t teleportOrigin; vec3_t rgb; skel = CG_SkeletonForModel( cent->ent.model ); if( !skel || !cent->ent.boneposes ) return; for( j = LOCALEFFECT_EV_PLAYER_TELEPORT_IN; j <= LOCALEFFECT_EV_PLAYER_TELEPORT_OUT; j++ ) { if( cent->localEffects[j] ) { cent->localEffects[j] = 0; VectorSet( rgb, 0.5, 0.5, 0.5 ); if( j == LOCALEFFECT_EV_PLAYER_TELEPORT_OUT ) { VectorCopy( cent->teleportedFrom, teleportOrigin ); } else { VectorCopy( cent->teleportedTo, teleportOrigin ); if( ISVIEWERENTITY( cent->current.number ) ) { VectorSet( rgb, 0.1, 0.1, 0.1 ); } } // spawn a dummy model le = CG_AllocModel( LE_RGB_FADE, teleportOrigin, vec3_origin, 10, rgb[0], rgb[1], rgb[2], 1, 0, 0, 0, 0, cent->ent.model, CG_MediaShader( cgs.media.shaderTeleportShellGfx ) ); if( cent->skel ) { // use static bone pose, no animation le->skel = cent->skel; le->static_boneposes = ( bonepose_t * )CG_Malloc( sizeof( bonepose_t ) * le->skel->numBones ); memcpy( le->static_boneposes, cent->ent.boneposes, sizeof( bonepose_t ) * le->skel->numBones ); le->ent.boneposes = le->static_boneposes; le->ent.oldboneposes = le->ent.boneposes; } le->ent.frame = cent->ent.frame; le->ent.oldframe = cent->ent.oldframe; le->ent.backlerp = 1.0f; Matrix3_Copy( cent->ent.axis, le->ent.axis ); } } }
/* * CG_DrawModel */ static void CG_DrawModel( int x, int y, int align, int w, int h, struct model_s *model, struct shader_s *shader, vec3_t origin, vec3_t angles, bool outline ) { refdef_t refdef; entity_t entity; if( !model ) return; x = CG_HorizontalAlignForWidth( x, align, w ); y = CG_VerticalAlignForHeight( y, align, h ); memset( &refdef, 0, sizeof( refdef ) ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.fov_x = 30; refdef.fov_y = CalcFov( refdef.fov_x, w, h ); refdef.time = cg.time; refdef.rdflags = RDF_NOWORLDMODEL; Matrix3_Copy( axis_identity, refdef.viewaxis ); refdef.scissor_x = x; refdef.scissor_y = y; refdef.scissor_width = w; refdef.scissor_height = h; memset( &entity, 0, sizeof( entity ) ); entity.model = model; entity.customShader = shader; entity.scale = 1.0f; entity.renderfx = RF_FULLBRIGHT | RF_NOSHADOW | RF_FORCENOLOD; VectorCopy( origin, entity.origin ); VectorCopy( entity.origin, entity.origin2 ); AnglesToAxis( angles, entity.axis ); if( outline ) { entity.outlineHeight = DEFAULT_OUTLINE_HEIGHT; Vector4Set( entity.outlineRGBA, 0, 0, 0, 255 ); } trap_R_ClearScene(); CG_SetBoneposesForTemporaryEntity( &entity ); CG_AddEntityToScene( &entity ); trap_R_RenderScene( &refdef ); }
/* * Matrix4_Modelview */ void Matrix4_Modelview( const vec3_t viewOrg, const mat3_t viewAxis, mat4_t m ) { mat3_t axis; mat4_t flip, view; #if 0 Matrix4_Identity( flip ); Matrix4_Rotate( flip, -90, 1, 0, 0 ); Matrix4_Rotate( flip, 90, 0, 0, 1 ); #else Vector4Set( &flip[0], 0, 0, -1, 0 ); Vector4Set( &flip[4], -1, 0, 0, 0 ); Vector4Set( &flip[8], 0, 1, 0, 0 ); Vector4Set( &flip[12], 0, 0, 0, 1 ); #endif Matrix3_Copy( viewAxis, axis ); view[0 ] = axis[0]; view[4 ] = axis[1]; view[8 ] = axis[2]; view[12] = -viewOrg[0] * view[0] + -viewOrg[1] * view[4] + -viewOrg[2] * view[8]; view[1 ] = axis[3]; view[5 ] = axis[4]; view[9 ] = axis[5]; view[13] = -viewOrg[0] * view[1] + -viewOrg[1] * view[5] + -viewOrg[2] * view[9]; view[2 ] = axis[6]; view[6 ] = axis[7]; view[10] = axis[8]; view[14] = -viewOrg[0] * view[2] + -viewOrg[1] * view[6] + -viewOrg[2] * view[10]; view[3] = 0; view[7] = 0; view[11] = 0; view[15] = 1; Matrix4_Multiply( flip, view, m ); }
/* * RB_SetCamera */ void RB_SetCamera( const vec3_t cameraOrigin, const mat3_t cameraAxis ) { VectorCopy( cameraOrigin, rb.cameraOrigin ); Matrix3_Copy( cameraAxis, rb.cameraAxis ); }
/* * CG_AddWeaponOnTag * * Add weapon model(s) positioned at the tag */ void CG_AddWeaponOnTag( entity_t *ent, orientation_t *tag, int weaponid, int effects, orientation_t *projectionSource, unsigned int flash_time, unsigned int barrel_time ) { entity_t weapon; weaponinfo_t *weaponInfo; float intensity; //don't try without base model if( !ent->model ) return; //don't try without a tag if( !tag ) return; weaponInfo = CG_GetWeaponInfo( weaponid ); if( !weaponInfo ) return; //if( ent->renderfx & RF_WEAPONMODEL ) // effects &= ~EF_RACEGHOST; //weapon memset( &weapon, 0, sizeof( weapon ) ); Vector4Set( weapon.shaderRGBA, 255, 255, 255, 255 ); weapon.scale = ent->scale; weapon.renderfx = ent->renderfx; weapon.frame = 0; weapon.oldframe = 0; weapon.model = weaponInfo->model[WEAPON]; CG_PlaceModelOnTag( &weapon, ent, tag ); CG_AddColoredOutLineEffect( &weapon, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &weapon ); if( !weapon.model ) return; CG_AddShellEffects( &weapon, effects ); // update projection source if( projectionSource != NULL ) { VectorCopy( vec3_origin, projectionSource->origin ); Matrix3_Copy( axis_identity, projectionSource->axis ); CG_MoveToTag( projectionSource->origin, projectionSource->axis, weapon.origin, weapon.axis, weaponInfo->tag_projectionsource.origin, weaponInfo->tag_projectionsource.axis ); } //expansion if( ( effects & EF_STRONG_WEAPON ) && weaponInfo->model[EXPANSION] ) { if( CG_GrabTag( tag, &weapon, "tag_expansion" ) ) { entity_t expansion; memset( &expansion, 0, sizeof( expansion ) ); Vector4Set( expansion.shaderRGBA, 255, 255, 255, 255 ); expansion.model = weaponInfo->model[EXPANSION]; expansion.scale = ent->scale; expansion.renderfx = ent->renderfx; expansion.frame = 0; expansion.oldframe = 0; CG_PlaceModelOnTag( &expansion, &weapon, tag ); CG_AddColoredOutLineEffect( &expansion, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &expansion ); // skelmod CG_AddShellEffects( &expansion, effects ); } } // barrel if( weaponInfo->model[BARREL] ) { if( CG_GrabTag( tag, &weapon, "tag_barrel" ) ) { orientation_t barrel_recoiled; vec3_t rotangles = { 0, 0, 0 }; entity_t barrel; memset( &barrel, 0, sizeof( barrel ) ); Vector4Set( barrel.shaderRGBA, 255, 255, 255, 255 ); barrel.model = weaponInfo->model[BARREL]; barrel.scale = ent->scale; barrel.renderfx = ent->renderfx; barrel.frame = 0; barrel.oldframe = 0; // rotation if( barrel_time > cg.time ) { intensity = (float)( barrel_time - cg.time ) / (float)weaponInfo->barrelTime; rotangles[2] = ( 360.0f * weaponInfo->barrelSpeed * intensity * intensity ); while( rotangles[2] > 360 ) rotangles[2] -= 360; // Check for tag_recoil if( CG_GrabTag( &barrel_recoiled, &weapon, "tag_recoil" ) ) VectorLerp( tag->origin, intensity, barrel_recoiled.origin, tag->origin ); } AnglesToAxis( rotangles, barrel.axis ); // barrel requires special tagging CG_PlaceRotatedModelOnTag( &barrel, &weapon, tag ); CG_AddColoredOutLineEffect( &barrel, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &barrel ); // skelmod CG_AddShellEffects( &barrel, effects ); } } if( flash_time < cg.time ) return; // flash if( !CG_GrabTag( tag, &weapon, "tag_flash" ) ) return; if( weaponInfo->model[FLASH] ) { entity_t flash; qbyte c; if( weaponInfo->flashFade ) { intensity = (float)( flash_time - cg.time )/(float)weaponInfo->flashTime; c = ( qbyte )( 255 * intensity ); } else { intensity = 1.0f; c = 255; } memset( &flash, 0, sizeof( flash ) ); Vector4Set( flash.shaderRGBA, c, c, c, c ); flash.model = weaponInfo->model[FLASH]; flash.scale = ent->scale; flash.renderfx = ent->renderfx | RF_NOSHADOW; flash.frame = 0; flash.oldframe = 0; CG_PlaceModelOnTag( &flash, &weapon, tag ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &flash ); CG_AddLightToScene( flash.origin, weaponInfo->flashRadius * intensity, weaponInfo->flashColor[0], weaponInfo->flashColor[1], weaponInfo->flashColor[2] ); } }
/* * R_DrawSkyPortal */ void R_DrawSkyPortal( const entity_t *e, skyportal_t *skyportal, vec3_t mins, vec3_t maxs ) { int x, y, w, h; if( !R_ScissorForEntity( e, mins, maxs, &x, &y, &w, &h ) ) { return; } if( !R_PushRefInst() ) { return; } rn.renderFlags = ( rn.renderFlags|RF_SKYPORTALVIEW|RF_SOFT_PARTICLES ); VectorCopy( skyportal->vieworg, rn.pvsOrigin ); rn.farClip = R_DefaultFarClip(); rn.clipFlags = 15; rn.shadowGroup = NULL; rn.meshlist = &r_skyportallist; //Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); if( skyportal->noEnts ) { rn.renderFlags |= RF_NOENTS; } if( skyportal->scale ) { vec3_t centre, diff; VectorAdd( rsh.worldModel->mins, rsh.worldModel->maxs, centre ); VectorScale( centre, 0.5f, centre ); VectorSubtract( centre, rn.viewOrigin, diff ); VectorMA( skyportal->vieworg, -skyportal->scale, diff, rn.refdef.vieworg ); } else { VectorCopy( skyportal->vieworg, rn.refdef.vieworg ); } // FIXME if( !VectorCompare( skyportal->viewanglesOffset, vec3_origin ) ) { vec3_t angles; mat3_t axis; Matrix3_Copy( rn.refdef.viewaxis, axis ); VectorInverse( &axis[AXIS_RIGHT] ); Matrix3_ToAngles( axis, angles ); VectorAdd( angles, skyportal->viewanglesOffset, angles ); AnglesToAxis( angles, axis ); Matrix3_Copy( axis, rn.refdef.viewaxis ); } rn.refdef.rdflags &= ~( RDF_UNDERWATER|RDF_CROSSINGWATER|RDF_SKYPORTALINVIEW ); if( skyportal->fov ) { rn.refdef.fov_x = skyportal->fov; rn.refdef.fov_y = CalcFov( rn.refdef.fov_x, rn.refdef.width, rn.refdef.height ); if( glConfig.wideScreen && !( rn.refdef.rdflags & RDF_NOFOVADJUSTMENT ) ) AdjustFov( &rn.refdef.fov_x, &rn.refdef.fov_y, glConfig.width, glConfig.height, qfalse ); } R_RenderView( &rn.refdef ); // restore modelview and projection matrices, scissoring, etc for the main view R_PopRefInst( ~GL_COLOR_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; 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_DrawSkyportal */ static void R_DrawSkyportal( const entity_t *e, skyportal_t *skyportal ) { if( !R_PushRefInst() ) { return; } rn.renderFlags = ( rn.renderFlags | RF_PORTALVIEW ); //rn.renderFlags &= ~RF_SOFT_PARTICLES; VectorCopy( skyportal->vieworg, rn.pvsOrigin ); rn.nearClip = Z_NEAR; rn.farClip = R_DefaultFarClip(); rn.polygonFactor = POLYOFFSET_FACTOR; rn.polygonUnits = POLYOFFSET_UNITS; rn.clipFlags = 15; rn.meshlist = &r_skyportallist; rn.portalmasklist = NULL; rn.rtLight = NULL; //Vector4Set( rn.scissor, rn.refdef.x + x, rn.refdef.y + y, w, h ); if( skyportal->noEnts ) { rn.renderFlags |= RF_ENVVIEW; } if( skyportal->scale ) { vec3_t centre, diff; VectorAdd( rsh.worldModel->mins, rsh.worldModel->maxs, centre ); VectorScale( centre, 0.5f, centre ); VectorSubtract( centre, rn.viewOrigin, diff ); VectorMA( skyportal->vieworg, -skyportal->scale, diff, rn.refdef.vieworg ); } else { VectorCopy( skyportal->vieworg, rn.refdef.vieworg ); } // FIXME if( !VectorCompare( skyportal->viewanglesOffset, vec3_origin ) ) { vec3_t angles; mat3_t axis; Matrix3_Copy( rn.refdef.viewaxis, axis ); VectorInverse( &axis[AXIS_RIGHT] ); Matrix3_ToAngles( axis, angles ); VectorAdd( angles, skyportal->viewanglesOffset, angles ); AnglesToAxis( angles, axis ); Matrix3_Copy( axis, rn.refdef.viewaxis ); } rn.refdef.rdflags &= ~( RDF_UNDERWATER | RDF_CROSSINGWATER | RDF_SKYPORTALINVIEW ); if( skyportal->fov ) { rn.refdef.fov_y = WidescreenFov( skyportal->fov ); rn.refdef.fov_x = CalcHorizontalFov( rn.refdef.fov_y, rn.refdef.width, rn.refdef.height ); } R_SetupViewMatrices( &rn.refdef ); R_SetupFrustum( &rn.refdef, rn.nearClip, rn.farClip, rn.frustum, rn.frustumCorners ); R_SetupPVS( &rn.refdef ); R_RenderView( &rn.refdef ); // restore modelview and projection matrices, scissoring, etc for the main view R_PopRefInst(); }
/* * 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(); }
/* * CG_SetupViewDef */ static void CG_SetupViewDef( cg_viewdef_t *view, int type, bool flipped ) { memset( view, 0, sizeof( cg_viewdef_t ) ); // // VIEW SETTINGS // view->type = type; view->flipped = flipped; if( view->type == VIEWDEF_PLAYERVIEW ) { view->POVent = cg.frame.playerState.POVnum; view->draw2D = true; // set up third-person if( cgs.demoPlaying ) view->thirdperson = CG_DemoCam_GetThirdPerson(); else if( chaseCam.mode == CAM_THIRDPERSON ) view->thirdperson = true; else view->thirdperson = ( cg_thirdPerson->integer != 0 ); if( cg_entities[view->POVent].serverFrame != cg.frame.serverFrame ) view->thirdperson = false; // check for drawing gun if( !view->thirdperson && view->POVent > 0 && view->POVent <= gs.maxclients ) { if( ( cg_entities[view->POVent].serverFrame == cg.frame.serverFrame ) && ( cg_entities[view->POVent].current.weapon != 0 ) ) view->drawWeapon = ( cg_gun->integer != 0 ) && ( cg_gun_alpha->value > 0 ); } // check for chase cams if( !( cg.frame.playerState.pmove.pm_flags & PMF_NO_PREDICTION ) ) { if( (unsigned)view->POVent == cgs.playerNum + 1 ) { if( cg_predict->integer && !cgs.demoPlaying ) { view->playerPrediction = true; } } } } else if( view->type == VIEWDEF_CAMERA ) { CG_DemoCam_GetViewDef( view ); } else { module_Error( "CG_SetupView: Invalid view type %i\n", view->type ); } // // SETUP REFDEF FOR THE VIEW SETTINGS // if( view->type == VIEWDEF_PLAYERVIEW ) { int i; vec3_t viewoffset; if( view->playerPrediction ) { CG_PredictMovement(); // fixme: crouching is predicted now, but it looks very ugly VectorSet( viewoffset, 0.0f, 0.0f, cg.predictedPlayerState.viewheight ); for( i = 0; i < 3; i++ ) { view->origin[i] = cg.predictedPlayerState.pmove.origin[i] + viewoffset[i] - ( 1.0f - cg.lerpfrac ) * cg.predictionError[i]; view->angles[i] = cg.predictedPlayerState.viewangles[i]; } CG_ViewSmoothPredictedSteps( view->origin ); // smooth out stair climbing if( cg_viewBob->integer && !cg_thirdPerson->integer ) { view->origin[2] += CG_ViewSmoothFallKick() * 6.5f; } } else { cg.predictingTimeStamp = cg.time; cg.predictFrom = 0; // we don't run prediction, but we still set cg.predictedPlayerState with the interpolation CG_InterpolatePlayerState( &cg.predictedPlayerState ); VectorSet( viewoffset, 0.0f, 0.0f, cg.predictedPlayerState.viewheight ); VectorAdd( cg.predictedPlayerState.pmove.origin, viewoffset, view->origin ); VectorCopy( cg.predictedPlayerState.viewangles, view->angles ); } view->refdef.fov_x = cg.predictedPlayerState.fov; CG_CalcViewBob(); VectorCopy( cg.predictedPlayerState.pmove.velocity, view->velocity ); } else if( view->type == VIEWDEF_CAMERA ) { view->refdef.fov_x = CG_DemoCam_GetOrientation( view->origin, view->angles, view->velocity ); } Matrix3_FromAngles( view->angles, view->axis ); if( view->flipped ) VectorInverse( &view->axis[AXIS_RIGHT] ); // view rectangle size view->refdef.x = scr_vrect.x; view->refdef.y = scr_vrect.y; view->refdef.width = scr_vrect.width; view->refdef.height = scr_vrect.height; view->refdef.time = cg.time; view->refdef.areabits = cg.frame.areabits; view->refdef.scissor_x = scr_vrect.x; view->refdef.scissor_y = scr_vrect.y; view->refdef.scissor_width = scr_vrect.width; view->refdef.scissor_height = scr_vrect.height; view->refdef.fov_y = CalcFov( view->refdef.fov_x, view->refdef.width, view->refdef.height ); AdjustFov( &view->refdef.fov_x, &view->refdef.fov_y, view->refdef.width, view->refdef.height, false ); view->fracDistFOV = tan( view->refdef.fov_x * ( M_PI/180 ) * 0.5f ); view->refdef.minLight = 0.3f; if( view->thirdperson ) CG_ThirdPersonOffsetView( view ); if( !view->playerPrediction ) cg.predictedWeaponSwitch = 0; VectorCopy( cg.view.origin, view->refdef.vieworg ); Matrix3_Copy( cg.view.axis, view->refdef.viewaxis ); VectorInverse( &view->refdef.viewaxis[AXIS_RIGHT] ); view->refdef.colorCorrection = NULL; if( cg_colorCorrection->integer ) { int colorCorrection = GS_ColorCorrection(); if( ( colorCorrection > 0 ) && ( colorCorrection < MAX_IMAGES ) ) view->refdef.colorCorrection = cgs.imagePrecache[colorCorrection]; } }