static qboolean IsMirror( const drawSurf_t *drawSurf, int entityNum ) { int i; cplane_t originalPlane, plane; trRefEntity_t *e; float d; // create plane axis for the portal we are seeing R_PlaneForSurface( drawSurf->surface, &originalPlane ); // rotate the plane if necessary if ( entityNum != REFENTITYNUM_WORLD ) { tr.currentEntityNum = entityNum; tr.currentEntity = &tr.refdef.entities[entityNum]; // get the orientation of the entity R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.ori ); // rotate the plane, but keep the non-rotated version for matching // against the portalSurface entities R_LocalNormalToWorld( originalPlane.normal, plane.normal ); plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.ori.origin ); // translate the original plane originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.ori.origin ); } else { plane = originalPlane; } // locate the portal entity closest to this plane. // origin will be the origin of the portal, origin2 will be // the origin of the camera for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) { e = &tr.refdef.entities[i]; if ( e->e.reType != RT_PORTALSURFACE ) { continue; } d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; if ( d > 64 || d < -64) { continue; } // if the entity is just a mirror, don't use as a camera point if ( e->e.oldorigin[0] == e->e.origin[0] && e->e.oldorigin[1] == e->e.origin[1] && e->e.oldorigin[2] == e->e.origin[2] ) { return qtrue; } return qfalse; } return qfalse; }
/* ======================== R_MirrorViewBySurface ======================== */ static viewDef_t *R_MirrorViewBySurface( drawSurf_t *drawSurf ) { viewDef_t *parms; orientation_t surface, camera; idPlane originalPlane, plane; // copy the viewport size from the original parms = (viewDef_t *)R_FrameAlloc( sizeof( *parms ) ); *parms = *tr.viewDef; parms->renderView.viewID = 0; // clear to allow player bodies to show up, and suppress view weapons parms->isSubview = true; parms->isMirror = true; // create plane axis for the portal we are seeing R_PlaneForSurface( drawSurf->geo, originalPlane ); R_LocalPlaneToGlobal( drawSurf->space->modelMatrix, originalPlane, plane ); surface.origin = plane.Normal() * -plane[3]; surface.axis[0] = plane.Normal(); surface.axis[0].NormalVectors( surface.axis[1], surface.axis[2] ); surface.axis[2] = -surface.axis[2]; camera.origin = surface.origin; camera.axis[0] = -surface.axis[0]; camera.axis[1] = surface.axis[1]; camera.axis[2] = surface.axis[2]; // set the mirrored origin and axis R_MirrorPoint( tr.viewDef->renderView.vieworg, &surface, &camera, parms->renderView.vieworg ); R_MirrorVector( tr.viewDef->renderView.viewaxis[0], &surface, &camera, parms->renderView.viewaxis[0] ); R_MirrorVector( tr.viewDef->renderView.viewaxis[1], &surface, &camera, parms->renderView.viewaxis[1] ); R_MirrorVector( tr.viewDef->renderView.viewaxis[2], &surface, &camera, parms->renderView.viewaxis[2] ); // make the view origin 16 units away from the center of the surface idVec3 viewOrigin = ( drawSurf->geo->bounds[0] + drawSurf->geo->bounds[1] ) * 0.5; viewOrigin += ( originalPlane.Normal() * 16 ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, viewOrigin, parms->initialViewAreaOrigin ); // set the mirror clip plane parms->numClipPlanes = 1; parms->clipPlanes[0] = -camera.axis[0]; parms->clipPlanes[0][3] = -( camera.origin * parms->clipPlanes[0].Normal() ); return parms; }
/* ================= R_GetPortalOrientation entityNum is the entity that the portal surface is a part of, which may be moving and rotating. Returns qtrue if it should be mirrored ================= */ qboolean R_GetPortalOrientations( drawSurf_t *drawSurf, int entityNum, orientation_t *surface, orientation_t *camera, vec3_t pvsOrigin, qboolean *mirror ) { int i; cplane_t originalPlane, plane; trRefEntity_t *e; float d; vec3_t transformed; // create plane axis for the portal we are seeing R_PlaneForSurface( drawSurf->surface, &originalPlane ); // rotate the plane if necessary if ( entityNum != REFENTITYNUM_WORLD ) { tr.currentEntityNum = entityNum; tr.currentEntity = &tr.refdef.entities[entityNum]; // get the orientation of the entity R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.ori ); // rotate the plane, but keep the non-rotated version for matching // against the portalSurface entities R_LocalNormalToWorld( originalPlane.normal, plane.normal ); plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.ori.origin ); // translate the original plane originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.ori.origin ); } else { plane = originalPlane; } VectorCopy( plane.normal, surface->axis[0] ); PerpendicularVector( surface->axis[1], surface->axis[0] ); CrossProduct( surface->axis[0], surface->axis[1], surface->axis[2] ); // locate the portal entity closest to this plane. // origin will be the origin of the portal, origin2 will be // the origin of the camera for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) { e = &tr.refdef.entities[i]; if ( e->e.reType != RT_PORTALSURFACE ) { continue; } d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; if ( d > 64 || d < -64) { continue; } // get the pvsOrigin from the entity VectorCopy( e->e.oldorigin, pvsOrigin ); // if the entity is just a mirror, don't use as a camera point if ( e->e.oldorigin[0] == e->e.origin[0] && e->e.oldorigin[1] == e->e.origin[1] && e->e.oldorigin[2] == e->e.origin[2] ) { VectorScale( plane.normal, plane.dist, surface->origin ); VectorCopy( surface->origin, camera->origin ); VectorSubtract( vec3_origin, surface->axis[0], camera->axis[0] ); VectorCopy( surface->axis[1], camera->axis[1] ); VectorCopy( surface->axis[2], camera->axis[2] ); *mirror = qtrue; return qtrue; } // project the origin onto the surface plane to get // an origin point we can rotate around d = DotProduct( e->e.origin, plane.normal ) - plane.dist; VectorMA( e->e.origin, -d, surface->axis[0], surface->origin ); // now get the camera origin and orientation VectorCopy( e->e.oldorigin, camera->origin ); AxisCopy( e->e.axis, camera->axis ); VectorSubtract( vec3_origin, camera->axis[0], camera->axis[0] ); VectorSubtract( vec3_origin, camera->axis[1], camera->axis[1] ); // optionally rotate if ( e->e.oldframe ) { // if a speed is specified if ( e->e.frame ) { // continuous rotate d = (tr.refdef.time/1000.0f) * e->e.frame; VectorCopy( camera->axis[1], transformed ); RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); } else { // bobbing rotate, with skinNum being the rotation offset d = sin( tr.refdef.time * 0.003f ); d = e->e.skinNum + d * 4; VectorCopy( camera->axis[1], transformed ); RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); } } else if ( e->e.skinNum ) { d = e->e.skinNum; VectorCopy( camera->axis[1], transformed ); RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); } *mirror = qfalse; return qtrue; } // if we didn't locate a portal entity, don't render anything. // We don't want to just treat it as a mirror, because without a // portal entity the server won't have communicated a proper entity set // in the snapshot // unfortunately, with local movement prediction it is easily possible // to see a surface before the server has communicated the matching // portal surface entity, so we don't want to print anything here... //ri->Printf( PRINT_ALL, "Portal surface without a portal entity\n" ); return qfalse; }
/* ======================== R_MirrorViewBySurface ======================== */ static viewDef_t* R_MirrorViewBySurface( const drawSurf_t* drawSurf ) { // copy the viewport size from the original viewDef_t* parms = ( viewDef_t* )R_FrameAlloc( sizeof( *parms ) ); *parms = *tr.viewDef; parms->renderView.viewID = 0; // clear to allow player bodies to show up, and suppress view weapons parms->isSubview = true; parms->isMirror = true; parms->isObliqueProjection = false; // create plane axis for the portal we are seeing idPlane originalPlane, plane; R_PlaneForSurface( drawSurf->frontEndGeo, originalPlane ); R_LocalPlaneToGlobal( drawSurf->space->modelMatrix, originalPlane, plane ); orientation_t surface; surface.origin = plane.Normal() * -plane[3]; surface.axis[0] = plane.Normal(); surface.axis[0].NormalVectors( surface.axis[1], surface.axis[2] ); surface.axis[2] = -surface.axis[2]; orientation_t camera; camera.origin = surface.origin; camera.axis[0] = -surface.axis[0]; camera.axis[1] = surface.axis[1]; camera.axis[2] = surface.axis[2]; // set the mirrored origin and axis R_MirrorPoint( tr.viewDef->renderView.vieworg, &surface, &camera, parms->renderView.vieworg ); R_MirrorVector( tr.viewDef->renderView.viewaxis[0], &surface, &camera, parms->renderView.viewaxis[0] ); R_MirrorVector( tr.viewDef->renderView.viewaxis[1], &surface, &camera, parms->renderView.viewaxis[1] ); R_MirrorVector( tr.viewDef->renderView.viewaxis[2], &surface, &camera, parms->renderView.viewaxis[2] ); // make the view origin 16 units away from the center of the surface const idVec3 center = (drawSurf->frontEndGeo->bounds[0] + drawSurf->frontEndGeo->bounds[1]) * 0.5f; const idVec3 viewOrigin = center + (originalPlane.Normal() * 16.0f); R_LocalPointToGlobal(drawSurf->space->modelMatrix, viewOrigin, parms->initialViewAreaOrigin); // set the mirror clip plane parms->numClipPlanes = 1; parms->clipPlanes[0] = -camera.axis[0]; parms->clipPlanes[0][3] = -( camera.origin * parms->clipPlanes[0].Normal() ); if (r_waterReflectFix.GetBool() && !parms->is2Dgui && drawSurf->material->GetSurfaceType() == SURFTYPE_MIRROR) { parms->isObliqueProjection = true; float dist = parms->clipPlanes[0].Dist(); float viewdist = parms->renderView.vieworg * parms->clipPlanes[0].Normal(); float fDist = -dist + viewdist; static const float fudge = 2.f; //fudge avoids depth precision artifacts when performing oblique projection if (fDist > fudge || fDist < -fudge) { if (fDist < 0.f) fDist += fudge; else fDist -= fudge; } parms->clipPlanes[0][3] = fDist; R_SetupViewMatrix(parms); R_SetupProjectionMatrix(parms); R_ObliqueProjection(parms); } return parms; }