r_refdef.view.useperspective = true;
r_refdef.view.isoverlay = false;
- r_refdef.view.frustum_x = tan(90 * M_PI / 360.0);
- r_refdef.view.frustum_y = tan(90 * M_PI / 360.0);
+ r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
+ r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
buffer1 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
buffer2 = (unsigned char *)Mem_Alloc(tempmempool, size * size * 3);
return false;
entrender = cl.csqcrenderentities + edictnum;
r_refdef.scene.entities[r_refdef.scene.numentities++] = entrender;
- entrender->entitynumber = edictnum;
+ entrender->entitynumber = edictnum + MAX_EDICTS;
//entrender->shadertime = 0; // shadertime was set by spawn()
entrender->flags = 0;
entrender->alpha = 1;
return r;
}
+
+qboolean CL_VM_TransformView(int entnum, matrix4x4_t *viewmatrix, mplane_t *clipplane, vec3_t visorigin)
+{
+ qboolean ret = false;
+ prvm_edict_t *ed;
+ prvm_eval_t *val, *valforward, *valright, *valup, *valendpos;
+ vec3_t forward, left, up, origin, ang;
+ matrix4x4_t mat, matq;
+
+ CSQC_BEGIN
+ ed = PRVM_EDICT_NUM(entnum);
+ // camera:
+ // camera_transform
+ if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.camera_transform)) && val->function)
+ {
+ ret = true;
+ if(viewmatrix || clipplane || visorigin)
+ {
+ valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward);
+ valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right);
+ valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up);
+ valendpos = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos);
+ if(valforward && valright && valup && valendpos)
+ {
+ Matrix4x4_ToVectors(viewmatrix, forward, left, up, origin);
+ AnglesFromVectors(ang, forward, up, false);
+ prog->globals.client->time = cl.time;
+ prog->globals.client->self = entnum;
+ VectorCopy(origin, PRVM_G_VECTOR(OFS_PARM0));
+ VectorCopy(ang, PRVM_G_VECTOR(OFS_PARM1));
+ VectorCopy(forward, valforward->vector);
+ VectorScale(left, -1, valright->vector);
+ VectorCopy(up, valup->vector);
+ VectorCopy(origin, valendpos->vector);
+ PRVM_ExecuteProgram(val->function, "QC function e.camera_transform is missing");
+ VectorCopy(PRVM_G_VECTOR(OFS_RETURN), origin);
+ VectorCopy(valforward->vector, forward);
+ VectorScale(valright->vector, -1, left);
+ VectorCopy(valup->vector, up);
+ VectorCopy(valendpos->vector, visorigin);
+ Matrix4x4_Invert_Full(&mat, viewmatrix);
+ Matrix4x4_FromVectors(viewmatrix, forward, left, up, origin);
+ Matrix4x4_Concat(&matq, viewmatrix, &mat);
+ Matrix4x4_TransformPositivePlane(&matq, clipplane->normal[0], clipplane->normal[1], clipplane->normal[2], clipplane->dist, &clipplane->normal[0]);
+ }
+ }
+ }
+ CSQC_END
+
+ return ret;
+}
qboolean CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out);
+qboolean CL_VM_TransformView(int entnum, matrix4x4_t *viewmatrix, mplane_t *clipplane, vec3_t visorigin);
+
#endif
#include "polygon.h"
#include "image.h"
#include "ft2.h"
+#include "csprogs.h"
mempool_t *r_main_mempool;
rtexturepool_t *r_main_texturepool;
cvar_t r_drawportals = {0, "r_drawportals", "0", "shows portals (separating polygons) in world interior in quake1 maps"};
cvar_t r_drawentities = {0, "r_drawentities","1", "draw entities (doors, players, projectiles, etc)"};
cvar_t r_drawviewmodel = {0, "r_drawviewmodel","1", "draw your weapon model"};
+cvar_t r_drawexteriormodel = {0, "r_drawexteriormodel","1", "draw your player model (e.g. in chase cam, reflections)"};
cvar_t r_cullentities_trace = {0, "r_cullentities_trace", "1", "probabistically cull invisible entities"};
cvar_t r_cullentities_trace_samples = {0, "r_cullentities_trace_samples", "2", "number of samples to test for entity culling (in addition to center sample)"};
cvar_t r_cullentities_trace_tempentitysamples = {0, "r_cullentities_trace_tempentitysamples", "-1", "number of samples to test for entity culling of temp entities (including all CSQC entities), -1 disables trace culling on these entities to prevent flicker (pvs still applies)"};
// distorted background
if (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERSHADER)
mode = SHADERMODE_WATER;
- else
+ else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFRACTION)
mode = SHADERMODE_REFRACTION;
+ else
+ {
+ mode = SHADERMODE_GENERIC;
+ permutation |= SHADERPERMUTATION_DIFFUSE;
+ }
R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
Cvar_RegisterVariable(&r_cullentities_trace_enlarge);
Cvar_RegisterVariable(&r_cullentities_trace_delay);
Cvar_RegisterVariable(&r_drawviewmodel);
+ Cvar_RegisterVariable(&r_drawexteriormodel);
Cvar_RegisterVariable(&r_speeds);
Cvar_RegisterVariable(&r_fullbrights);
Cvar_RegisterVariable(&r_wateralpha);
: RENDER_EXTERIORMODEL;
if (!r_drawviewmodel.integer)
renderimask |= RENDER_VIEWMODEL;
+ if (!r_drawexteriormodel.integer)
+ renderimask |= RENDER_EXTERIORMODEL;
if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs)
{
// worldmodel can check visibility
if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
r_refdef.viewcache.entityvisible[i] = true;
}
- if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight)
+ if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane)
+ // sorry, this check doesn't work for portal/reflection/refraction renders as the view origin is not useful for culling
{
for (i = 0;i < r_refdef.scene.numentities;i++)
{
GL_CullFace(r_refdef.view.cullface_back);
}
+/*
+================
+R_RenderView_UpdateViewVectors
+================
+*/
+static void R_RenderView_UpdateViewVectors(void)
+{
+ // break apart the view matrix into vectors for various purposes
+ // it is important that this occurs outside the RenderScene function because that can be called from reflection renders, where the vectors come out wrong
+ // however the r_refdef.view.origin IS updated in RenderScene intentionally - otherwise the sky renders at the wrong origin, etc
+ Matrix4x4_ToVectors(&r_refdef.view.matrix, r_refdef.view.forward, r_refdef.view.left, r_refdef.view.up, r_refdef.view.origin);
+ VectorNegate(r_refdef.view.left, r_refdef.view.right);
+ // make an inverted copy of the view matrix for tracking sprites
+ Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
+}
+
void R_RenderScene(void);
void R_RenderWaterPlanes(void);
static void R_Water_StartFrame(void)
{
int i;
- int waterwidth, waterheight, texturewidth, textureheight;
+ int waterwidth, waterheight, texturewidth, textureheight, camerawidth, cameraheight;
r_waterstate_waterplane_t *p;
if (vid.width > (int)vid.maxtexturesize_2d || vid.height > (int)vid.maxtexturesize_2d)
// calculate desired texture sizes
// can't use water if the card does not support the texture size
if (!r_water.integer || r_showsurfaces.integer)
- texturewidth = textureheight = waterwidth = waterheight = 0;
+ texturewidth = textureheight = waterwidth = waterheight = camerawidth = cameraheight = 0;
else if (vid.support.arb_texture_non_power_of_two)
{
texturewidth = waterwidth;
textureheight = waterheight;
+ camerawidth = waterwidth;
+ cameraheight = waterheight;
}
else
{
for (texturewidth = 1;texturewidth < waterwidth ;texturewidth *= 2);
for (textureheight = 1;textureheight < waterheight;textureheight *= 2);
+ for (camerawidth = 1;camerawidth <= waterwidth; camerawidth *= 2); camerawidth /= 2;
+ for (cameraheight = 1;cameraheight <= waterheight;cameraheight *= 2); cameraheight /= 2;
}
// allocate textures as needed
- if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight)
+ if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight || r_waterstate.camerawidth != camerawidth || r_waterstate.cameraheight != cameraheight)
{
r_waterstate.maxwaterplanes = MAX_WATERPLANES;
for (i = 0, p = r_waterstate.waterplanes;i < r_waterstate.maxwaterplanes;i++, p++)
if (p->texture_reflection)
R_FreeTexture(p->texture_reflection);
p->texture_reflection = NULL;
+ if (p->texture_camera)
+ R_FreeTexture(p->texture_camera);
+ p->texture_camera = NULL;
}
memset(&r_waterstate, 0, sizeof(r_waterstate));
r_waterstate.texturewidth = texturewidth;
r_waterstate.textureheight = textureheight;
+ r_waterstate.camerawidth = camerawidth;
+ r_waterstate.cameraheight = cameraheight;
}
if (r_waterstate.texturewidth)
vec3_t normal;
vec3_t center;
mplane_t plane;
+ int cam_ent;
r_waterstate_waterplane_t *p;
texture_t *t = R_GetCurrentTexture(surface->texture);
+ cam_ent = t->camera_entity;
+ if(!(t->currentmaterialflags & MATERIALFLAG_CAMERA))
+ cam_ent = 0;
+
// just use the first triangle with a valid normal for any decisions
VectorClear(normal);
for (triangleindex = 0, e = rsurface.modelelement3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
// find a matching plane if there is one
for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
- if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
- break;
+ if(p->camera_entity == t->camera_entity)
+ if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
+ break;
if (planeindex >= r_waterstate.maxwaterplanes)
return; // nothing we can do, out of planes
// clear materialflags and pvs
p->materialflags = 0;
p->pvsvalid = false;
+ p->camera_entity = t->camera_entity;
}
// merge this surface's materialflags into the waterplane
p->materialflags |= t->currentmaterialflags;
- // merge this surface's PVS into the waterplane
- VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
- if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
- && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0)
+ if(!(p->materialflags & MATERIALFLAG_CAMERA))
{
- r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, center, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid);
- p->pvsvalid = true;
+ // merge this surface's PVS into the waterplane
+ VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
+ if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
+ && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0)
+ {
+ r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, center, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid);
+ p->pvsvalid = true;
+ }
}
}
r_refdef_view_t myview;
int planeindex;
r_waterstate_waterplane_t *p;
+ vec3_t visorigin;
originalview = r_refdef.view;
if (!p->texture_refraction)
goto error;
}
+ else if (p->materialflags & MATERIALFLAG_CAMERA)
+ {
+ if (!p->texture_camera)
+ p->texture_camera = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_camera", planeindex), r_waterstate.camerawidth, r_waterstate.cameraheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR, NULL);
+ if (!p->texture_camera)
+ goto error;
+ }
if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
{
{
r_waterstate.renderingrefraction = true;
r_refdef.view = myview;
+
r_refdef.view.clipplane = p->plane;
VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
+
+ if((p->materialflags & MATERIALFLAG_CAMERA) && p->camera_entity)
+ {
+ // we need to perform a matrix transform to render the view... so let's get the transformation matrix
+ r_waterstate.renderingrefraction = false; // we don't want to hide the player model from these ones
+ CL_VM_TransformView(p->camera_entity - MAX_EDICTS, &r_refdef.view.matrix, &r_refdef.view.clipplane, visorigin);
+ R_RenderView_UpdateViewVectors();
+ r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
+ }
+
PlaneClassify(&r_refdef.view.clipplane);
R_ResetViewRendering3D();
R_Mesh_CopyToTexture(p->texture_refraction, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
r_waterstate.renderingrefraction = false;
}
+ else if (p->materialflags & MATERIALFLAG_CAMERA)
+ {
+ r_refdef.view = myview;
+
+ r_refdef.view.clipplane = p->plane;
+ VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
+ r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
+
+ r_refdef.view.width = r_waterstate.camerawidth;
+ r_refdef.view.height = r_waterstate.cameraheight;
+ r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
+ r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
+
+ if(p->camera_entity)
+ {
+ // we need to perform a matrix transform to render the view... so let's get the transformation matrix
+ CL_VM_TransformView(p->camera_entity - MAX_EDICTS, &r_refdef.view.matrix, &r_refdef.view.clipplane, visorigin);
+ }
+
+ // reverse the cullface settings for this render
+ r_refdef.view.cullface_front = GL_FRONT;
+ r_refdef.view.cullface_back = GL_BACK;
+ // also reverse the view matrix
+ Matrix4x4_ConcatScale3(&r_refdef.view.matrix, 1, -1, 1);
+ R_RenderView_UpdateViewVectors();
+ if(p->camera_entity)
+ r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
+
+ // camera needs no clipplane
+ r_refdef.view.useclipplane = false;
+
+ PlaneClassify(&r_refdef.view.clipplane);
+
+ R_ResetViewRendering3D();
+ R_ClearScreen(r_refdef.fogenabled);
+ R_View_Update();
+ R_RenderScene();
+
+ R_Mesh_CopyToTexture(p->texture_camera, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
+ r_waterstate.renderingrefraction = false;
+ }
}
r_waterstate.renderingscene = false;
r_refdef.view.colorscale = r_hdr_scenebrightness.value;
- // break apart the view matrix into vectors for various purposes
- // it is important that this occurs outside the RenderScene function because that can be called from reflection renders, where the vectors come out wrong
- // however the r_refdef.view.origin IS updated in RenderScene intentionally - otherwise the sky renders at the wrong origin, etc
- Matrix4x4_ToVectors(&r_refdef.view.matrix, r_refdef.view.forward, r_refdef.view.left, r_refdef.view.up, r_refdef.view.origin);
- VectorNegate(r_refdef.view.left, r_refdef.view.right);
- // make an inverted copy of the view matrix for tracking sprites
- Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
+ R_RenderView_UpdateViewVectors();
R_Shadow_UpdateWorldLightSelection();
t->update_lastrenderframe = r_textureframe;
t->update_lastrenderentity = (void *)ent;
+ if(ent && ent->entitynumber >= MAX_EDICTS && ent->entitynumber < 2 * MAX_EDICTS)
+ t->camera_entity = ent->entitynumber;
+ else
+ t->camera_entity = 0;
+
// switch to an alternate material if this is a q1bsp animated material
{
texture_t *texture = t;
if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay)
t->currentalpha *= t->r_water_wateralpha;
if(!r_waterstate.enabled || r_refdef.view.isoverlay)
- t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION);
+ t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA);
if (!(rsurface.ent_flags & RENDER_LIGHT))
t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT;
else if (rsurface.modeltexcoordlightmap2f == NULL && !(t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
t->currentmaterialflags |= MATERIALFLAG_VERTEXTEXTUREBLEND;
if (t->currentmaterialflags & MATERIALFLAG_BLENDED)
{
- if (t->currentmaterialflags & (MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER))
+ if (t->currentmaterialflags & (MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER | MATERIALFLAG_CAMERA))
t->currentmaterialflags &= ~MATERIALFLAG_BLENDED;
}
else
- t->currentmaterialflags &= ~(MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER);
+ t->currentmaterialflags &= ~(MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER | MATERIALFLAG_CAMERA);
if ((t->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST)) == MATERIALFLAG_BLENDED && r_transparentdepthmasking.integer && !(t->basematerialflags & MATERIALFLAG_BLENDED))
t->currentmaterialflags |= MATERIALFLAG_TRANSDEPTH;
bestp = NULL;
for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
{
+ if(p->camera_entity != rsurface.texture->camera_entity)
+ continue;
d = 0;
for (vertexindex = 0, v = rsurface.modelvertex3f + surface->num_firstvertex * 3;vertexindex < surface->num_vertices;vertexindex++, v += 3)
{
case RENDERPATH_CGGL:
#ifdef SUPPORTCG
if (r_cg_permutation->fp_Texture_Refraction) CG_BindTexture(r_cg_permutation->fp_Texture_Refraction, bestp ? bestp->texture_refraction : r_texture_black);CHECKCGERROR
+ else if (r_cg_permutation->fp_Texture_First) CG_BindTexture(r_cg_permutation->fp_Texture_First, bestp ? bestp->texture_camera : r_texture_black);CHECKCGERROR
if (r_cg_permutation->fp_Texture_Reflection) CG_BindTexture(r_cg_permutation->fp_Texture_Reflection, bestp ? bestp->texture_reflection : r_texture_black);CHECKCGERROR
#endif
break;
case RENDERPATH_GL20:
if (r_glsl_permutation->loc_Texture_Refraction >= 0) R_Mesh_TexBind(GL20TU_REFRACTION, bestp ? bestp->texture_refraction : r_texture_black);
+ else if (r_glsl_permutation->loc_Texture_First >= 0) R_Mesh_TexBind(GL20TU_FIRST, bestp ? bestp->texture_camera : r_texture_black);
if (r_glsl_permutation->loc_Texture_Reflection >= 0) R_Mesh_TexBind(GL20TU_REFLECTION, bestp ? bestp->texture_reflection : r_texture_black);
break;
case RENDERPATH_GL13:
extern rtexture_t *r_shadow_prepasslightingspeculartexture;
static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean prepass)
{
- if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION)))
+ if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
return;
RSurf_PrepareVerticesForBatch(true, true, texturenumsurfaces, texturesurfacelist);
if (prepass)
R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_DEFERREDGEOMETRY);
RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
}
- else if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION)) && !r_waterstate.renderingscene)
+ else if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA)) && !r_waterstate.renderingscene)
{
// render water or distortion background, then blend surface on top
GL_DepthMask(true);
#include "quakedef.h"
#include "r_shadow.h"
#include "portals.h"
+#include "csprogs.h"
cvar_t r_ambient = {0, "r_ambient", "0", "brightens map, value is 0-128"};
cvar_t r_lockpvs = {0, "r_lockpvs", "0", "disables pvs switching, allows you to walk around and inspect what is visible from a given location in the map (anything not visible from your current location will not be drawn)"};
R_DrawModelSurfaces(ent, true, true, false, false, false);
}
-extern void R_Water_AddWaterPlane(msurface_t *surface);
+extern void R_Water_AddWaterPlane(msurface_t *surface, int entno);
void R_Q1BSP_DrawAddWaterPlanes(entity_render_t *ent)
{
- int i, j, flagsmask;
+ int i, j, n, flagsmask;
dp_model_t *model = ent->model;
msurface_t *surfaces;
if (model == NULL)
RSurf_ActiveModelEntity(ent, false, false, false);
surfaces = model->data_surfaces;
- flagsmask = MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION;
+ flagsmask = MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA;
// add visible surfaces to draw list
if (ent == r_refdef.scene.worldentity)
j = model->sortedmodelsurfaces[i];
if (r_refdef.viewcache.world_surfacevisible[j])
if (surfaces[j].texture->basematerialflags & flagsmask)
- R_Water_AddWaterPlane(surfaces + j);
+ R_Water_AddWaterPlane(surfaces + j, 0);
}
}
else
{
+ if(ent->entitynumber >= MAX_EDICTS) // && CL_VM_TransformView(ent->entitynumber - MAX_EDICTS, NULL, NULL, NULL))
+ n = ent->entitynumber;
+ else
+ n = 0;
for (i = 0;i < model->nummodelsurfaces;i++)
{
j = model->sortedmodelsurfaces[i];
if (surfaces[j].texture->basematerialflags & flagsmask)
- R_Water_AddWaterPlane(surfaces + j);
+ R_Water_AddWaterPlane(surfaces + j, n);
}
}
rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
// now figure out what to do with this particular range of surfaces
if (!(rsurface.texture->currentmaterialflags & MATERIALFLAG_WALL))
continue;
- if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION)))
+ if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
continue;
if (rsurface.texture->currentmaterialflags & MATERIALFLAGMASK_DEPTHSORTED)
{
mod->DrawSky = R_Q1BSP_DrawSky;
for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
- if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
+ if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
break;
if (j < mod->nummodelsurfaces)
mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
mod->DrawSky = R_Q1BSP_DrawSky;
for (j = 0;j < mod->nummodelsurfaces;j++)
- if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
+ if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
break;
if (j < mod->nummodelsurfaces)
mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
#define MATERIALFLAG_CUSTOMSURFACE 16777216
// causes MATERIALFLAG_BLENDED to render a depth pass before rendering, hiding backfaces and other hidden geometry
#define MATERIALFLAG_TRANSDEPTH 33554432
+// like refraction, but doesn't distort etc.
+#define MATERIALFLAG_CAMERA 67108864
// combined mask of all attributes that require depth sorted rendering
#define MATERIALFLAGMASK_DEPTHSORTED (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST)
// combined mask of all attributes that cause some sort of transparency
shader.reflectfactor = atof(parameter[1]);
Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
}
+ else if (!strcasecmp(parameter[0], "dpcamera"))
+ {
+ shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
+ }
else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
{
shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
}
// fix up multiple reflection types
if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
- shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION);
+ shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
Q3Shader_AddToHash (&shader);
}
texture->basematerialflags |= MATERIALFLAG_REFLECTION;
if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
+ if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
+ texture->basematerialflags |= MATERIALFLAG_CAMERA;
texture->customblendfunc[0] = GL_ONE;
texture->customblendfunc[1] = GL_ZERO;
if (shader->numlayers > 0)
#define Q3TEXTUREFLAG_REFRACTION 256
#define Q3TEXTUREFLAG_REFLECTION 512
#define Q3TEXTUREFLAG_WATERSHADER 1024
+#define Q3TEXTUREFLAG_CAMERA 2048
#define Q3PATHLENGTH 64
#define TEXTURE_MAXFRAMES 64
float reflectfactor; // amount of reflection distort (1.0 = like the cvar specifies)
vec4_t reflectcolor4f; // color tint of reflection (including alpha factor)
float r_water_wateralpha; // additional wateralpha to apply when r_water is active
+ int camera_entity; // entity number for use by cameras
// offsetmapping
dpoffsetmapping_technique_t offsetmapping;
int enemy; // ssqc / csqc (physics)
int aiment; // ssqc / csqc (physics)
int movedir; // ssqc / csqc (physics)
+
+ int camera_transform; // csqc (warpzones)
}
prvm_prog_fieldoffsets_t;
prog->fieldoffsets.jointtype = PRVM_ED_FindFieldOffset("jointtype");
prog->fieldoffsets.movedir = PRVM_ED_FindFieldOffset("movedir");
+ prog->fieldoffsets.camera_transform = PRVM_ED_FindFieldOffset("camera_transform");
+
prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
#define MAX_SAVEGAMES 12
#define SAVEGAME_COMMENT_LENGTH 39
#define MAX_CLIENTNETWORKEYES 2
+#define MAX_LEVELNETWORKEYES 0 // no portal support
#define MAX_OCCLUSION_QUERIES 256
#define MAX_WATERPLANES 2
#define MAX_DEMONAME 16 ///< max demo name length for demos command
#define MAX_SAVEGAMES 12 ///< max savegames listed in savegame menu
#define SAVEGAME_COMMENT_LENGTH 39 ///< max comment length of savegame in menu
-#define MAX_CLIENTNETWORKEYES 2 ///< max number of locations that can be added to pvs when culling network entities (must be at least 2 for prediction)
+#define MAX_CLIENTNETWORKEYES 8 ///< max number of locations that can be added to pvs when culling network entities (must be at least 2 for prediction)
+#define MAX_LEVELNETWORKEYES 64 ///< max number of locations that can be added to pvs when culling network entities (must be at least 2 for prediction)
#define MAX_OCCLUSION_QUERIES 4096 ///< max number of GL_ARB_occlusion_query objects that can be used in one frame
#define MAX_WATERPLANES 16 ///< max number of water planes visible (each one causes additional view renders)
{
if(ent->entitynumber != 0)
{
- // networked entity - might be attached in some way (then we should use the parent's light direction, to not tear apart attached entities)
- int entnum, entnum2, recursion;
- entnum = entnum2 = ent->entitynumber;
- for(recursion = 32; recursion > 0; --recursion)
+ if(ent->entitynumber >= MAX_EDICTS) // csqc entity
{
- entnum2 = cl.entities[entnum].state_current.tagentity;
- if(entnum2 >= 1 && entnum2 < cl.num_entities && cl.entities_active[entnum2])
- entnum = entnum2;
- else
- break;
+ // FIXME handle this
+ VectorNegate(ent->modellight_lightdir, relativelightdirection);
}
- if(recursion && recursion != 32) // if we followed a valid non-empty attachment chain
+ else
{
- VectorNegate(cl.entities[entnum].render.modellight_lightdir, relativelightdirection);
- // transform into modelspace of OUR entity
- Matrix4x4_Transform3x3(&cl.entities[entnum].render.matrix, relativelightdirection, tmp);
- Matrix4x4_Transform3x3(&ent->inversematrix, tmp, relativelightdirection);
+ // networked entity - might be attached in some way (then we should use the parent's light direction, to not tear apart attached entities)
+ int entnum, entnum2, recursion;
+ entnum = entnum2 = ent->entitynumber;
+ for(recursion = 32; recursion > 0; --recursion)
+ {
+ entnum2 = cl.entities[entnum].state_current.tagentity;
+ if(entnum2 >= 1 && entnum2 < cl.num_entities && cl.entities_active[entnum2])
+ entnum = entnum2;
+ else
+ break;
+ }
+ if(recursion && recursion != 32) // if we followed a valid non-empty attachment chain
+ {
+ VectorNegate(cl.entities[entnum].render.modellight_lightdir, relativelightdirection);
+ // transform into modelspace of OUR entity
+ Matrix4x4_Transform3x3(&cl.entities[entnum].render.matrix, relativelightdirection, tmp);
+ Matrix4x4_Transform3x3(&ent->inversematrix, tmp, relativelightdirection);
+ }
+ else
+ VectorNegate(ent->modellight_lightdir, relativelightdirection);
}
- else
- VectorNegate(ent->modellight_lightdir, relativelightdirection);
}
else
VectorNegate(ent->modellight_lightdir, relativelightdirection);
{
rtexture_t *texture_refraction;
rtexture_t *texture_reflection;
+ rtexture_t *texture_camera;
mplane_t plane;
int materialflags; // combined flags of all water surfaces on this plane
unsigned char pvsbits[(MAX_MAP_LEAFS+7)>>3]; // FIXME: buffer overflow on huge maps
qboolean pvsvalid;
+ int camera_entity;
}
r_waterstate_waterplane_t;
int waterwidth, waterheight;
int texturewidth, textureheight;
+ int camerawidth, cameraheight;
int maxwaterplanes; // same as MAX_WATERPLANES
int numwaterplanes;
sv.sententities[s->number] = sv.sententitiesmark;
}
+#if MAX_LEVELNETWORKEYES > 0
+#define MAX_EYE_RECURSION 1 // increase if recursion gets supported by portals
+void SV_AddCameraEyes(void)
+{
+ int e, i, j, k;
+ prvm_edict_t *ed;
+ int cameras[MAX_LEVELNETWORKEYES];
+ vec3_t camera_origins[MAX_LEVELNETWORKEYES];
+ int eye_levels[MAX_CLIENTNETWORKEYES];
+ int n_cameras = 0;
+ vec3_t mi, ma;
+ prvm_eval_t *valendpos, *val;
+
+ if(!prog->fieldoffsets.camera_transform)
+ return;
+ valendpos = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos);
+ if(!valendpos)
+ return;
+
+ for(i = 0; i < sv.writeentitiestoclient_numeyes; ++i)
+ eye_levels[i] = 0;
+
+ // check line of sight to portal entities and add them to PVS
+ for (e = 1, ed = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ed = PRVM_NEXT_EDICT(ed))
+ {
+ if (!ed->priv.server->free)
+ {
+ if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.camera_transform)) && val->function)
+ {
+ prog->globals.server->self = e;
+ prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber;
+ VectorCopy(sv.writeentitiestoclient_eyes[0], valendpos->vector);
+ VectorCopy(sv.writeentitiestoclient_eyes[0], PRVM_G_VECTOR(OFS_PARM0));
+ VectorClear(PRVM_G_VECTOR(OFS_PARM1));
+ PRVM_ExecuteProgram(val->function, "QC function e.camera_transform is missing");
+ if(!VectorCompare(valendpos->vector, sv.writeentitiestoclient_eyes[0]))
+ VectorCopy(valendpos->vector, camera_origins[n_cameras]);
+ cameras[n_cameras] = e;
+ ++n_cameras;
+ if(n_cameras >= MAX_LEVELNETWORKEYES)
+ break;
+ }
+ }
+ }
+
+ if(!n_cameras)
+ return;
+
+ // i is loop counter, is reset to 0 when an eye got added
+ // j is camera index to check
+ for(i = 0, j = 0; sv.writeentitiestoclient_numeyes < MAX_CLIENTNETWORKEYES && i < n_cameras; ++i, ++j, j %= n_cameras)
+ {
+ if(!cameras[j])
+ continue;
+ ed = PRVM_EDICT_NUM(cameras[j]);
+ VectorAdd(ed->fields.server->origin, ed->fields.server->mins, mi);
+ VectorAdd(ed->fields.server->origin, ed->fields.server->maxs, ma);
+ for(k = 0; k < sv.writeentitiestoclient_numeyes; ++k)
+ if(eye_levels[k] <= MAX_EYE_RECURSION)
+ {
+ if(SV_CanSeeBox(sv_cullentities_trace_samples.integer, sv_cullentities_trace_enlarge.value, sv.writeentitiestoclient_eyes[k], mi, ma))
+ {
+ eye_levels[sv.writeentitiestoclient_numeyes] = eye_levels[k] + 1;
+ VectorCopy(camera_origins[j], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
+ // Con_Printf("added eye %d: %f %f %f because we can see %f %f %f .. %f %f %f from eye %d\n", j, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][0], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][1], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][2], mi[0], mi[1], mi[2], ma[0], ma[1], ma[2], k);
+ sv.writeentitiestoclient_numeyes++;
+ cameras[j] = 0;
+ i = 0;
+ break;
+ }
+ }
+ }
+}
+#else
+void SV_AddCameraEyes(void)
+{
+}
+#endif
+
void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize)
{
qboolean need_empty = false;
// Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n");
}
- // TODO: check line of sight to portal entities and add them to PVS
+ SV_AddCameraEyes();
+
+ // build PVS from the new eyes
+ if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
+ for(i = 1; i < sv.writeentitiestoclient_numeyes; ++i)
+ sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_eyes[i], 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs), sv.writeentitiestoclient_pvsbytes != 0);
sv.sententitiesmark++;
SV_Impact
Two entities have touched, so run their touch functions
-returns true if the impact kept the origin of the touching entity intact
==================
*/
extern void VM_SetTraceGlobals(const trace_t *trace);
extern sizebuf_t vm_tempstringsbuf;
-qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
+void SV_Impact (prvm_edict_t *e1, trace_t *trace)
{
int restorevm_tempstringsbuf_cursize;
int old_self, old_other;
- vec3_t org;
prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
prvm_eval_t *val;
old_other = prog->globals.server->other;
restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
- VectorCopy(e1->fields.server->origin, org);
-
VM_SetTraceGlobals(trace);
prog->globals.server->time = sv.time;
prog->globals.server->self = old_self;
prog->globals.server->other = old_other;
vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
-
- return VectorCompare(e1->fields.server->origin, org);
}
{
int type;
int bump;
- vec3_t original;
+ vec3_t original, original_velocity;
vec3_t end;
VectorCopy(ent->fields.server->origin, original);
if (trace->bmodelstartsolid && failonbmodelstartsolid)
return true;
-
VectorCopy (trace->endpos, ent->fields.server->origin);
+
+ VectorCopy(ent->fields.server->origin, original);
+ VectorCopy(ent->fields.server->velocity, original_velocity);
+
SV_LinkEdict(ent);
#if 0
SV_LinkEdict_TouchAreaGrid(ent);
if((ent->fields.server->solid >= SOLID_TRIGGER && trace->ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace->ent))))
- return SV_Impact (ent, trace);
+ SV_Impact (ent, trace);
- return true;
+ return VectorCompare(ent->fields.server->origin, original) && VectorCompare(ent->fields.server->velocity, original_velocity);
}