mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-05-06 19:01:04 +03:00
476 lines
12 KiB
C
476 lines
12 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
Copyright (C) 2006-2011 Robert Beckebans <trebor_7@users.sourceforge.net>
|
|
|
|
This file is part of XreaL source code.
|
|
|
|
XreaL source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
XreaL source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with XreaL source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// tr_mesh.c -- triangle model functions
|
|
#include "tr_local.h"
|
|
|
|
/*
|
|
=============
|
|
R_CullMDV
|
|
=============
|
|
*/
|
|
static void R_CullMDV(mdvModel_t * model, trRefEntity_t * ent)
|
|
{
|
|
mdvFrame_t *oldFrame, *newFrame;
|
|
int i;
|
|
vec3_t v;
|
|
vec3_t transformed;
|
|
|
|
// compute frame pointers
|
|
newFrame = model->frames + ent->e.frame;
|
|
oldFrame = model->frames + ent->e.oldframe;
|
|
|
|
// calculate a bounding box in the current coordinate system
|
|
for(i = 0; i < 3; i++)
|
|
{
|
|
ent->localBounds[0][i] =
|
|
oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i];
|
|
ent->localBounds[1][i] =
|
|
oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i];
|
|
}
|
|
|
|
// setup world bounds for intersection tests
|
|
ClearBounds(ent->worldBounds[0], ent->worldBounds[1]);
|
|
|
|
for(i = 0; i < 8; i++)
|
|
{
|
|
v[0] = ent->localBounds[i & 1][0];
|
|
v[1] = ent->localBounds[(i >> 1) & 1][1];
|
|
v[2] = ent->localBounds[(i >> 2) & 1][2];
|
|
|
|
// transform local bounds vertices into world space
|
|
R_LocalPointToWorld(v, transformed);
|
|
|
|
AddPointToBounds(transformed, ent->worldBounds[0], ent->worldBounds[1]);
|
|
}
|
|
|
|
// cull bounding sphere ONLY if this is not an upscaled entity
|
|
if(!ent->e.nonNormalizedAxes)
|
|
{
|
|
if(ent->e.frame == ent->e.oldframe)
|
|
{
|
|
switch (R_CullLocalPointAndRadius(newFrame->localOrigin, newFrame->radius))
|
|
{
|
|
case CULL_OUT:
|
|
tr.pc.c_sphere_cull_mdx_out++;
|
|
ent->cull = CULL_OUT;
|
|
return;
|
|
|
|
case CULL_IN:
|
|
tr.pc.c_sphere_cull_mdx_in++;
|
|
ent->cull = CULL_IN;
|
|
return;
|
|
|
|
case CULL_CLIP:
|
|
tr.pc.c_sphere_cull_mdx_clip++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int sphereCull, sphereCullB;
|
|
|
|
sphereCull = R_CullLocalPointAndRadius(newFrame->localOrigin, newFrame->radius);
|
|
if(newFrame == oldFrame)
|
|
{
|
|
sphereCullB = sphereCull;
|
|
}
|
|
else
|
|
{
|
|
sphereCullB = R_CullLocalPointAndRadius(oldFrame->localOrigin, oldFrame->radius);
|
|
}
|
|
|
|
if(sphereCull == sphereCullB)
|
|
{
|
|
if(sphereCull == CULL_OUT)
|
|
{
|
|
tr.pc.c_sphere_cull_mdx_out++;
|
|
ent->cull = CULL_OUT;
|
|
return;
|
|
}
|
|
else if(sphereCull == CULL_IN)
|
|
{
|
|
tr.pc.c_sphere_cull_mdx_in++;
|
|
ent->cull = CULL_IN;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
tr.pc.c_sphere_cull_mdx_clip++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (R_CullLocalBox(ent->localBounds))
|
|
{
|
|
case CULL_IN:
|
|
tr.pc.c_box_cull_mdx_in++;
|
|
ent->cull = CULL_IN;
|
|
return;
|
|
|
|
case CULL_CLIP:
|
|
tr.pc.c_box_cull_mdx_clip++;
|
|
ent->cull = CULL_CLIP;
|
|
return;
|
|
|
|
case CULL_OUT:
|
|
default:
|
|
tr.pc.c_box_cull_mdx_out++;
|
|
ent->cull = CULL_OUT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
R_ComputeLOD
|
|
=================
|
|
*/
|
|
int R_ComputeLOD(trRefEntity_t * ent)
|
|
{
|
|
float radius;
|
|
float flod, lodscale;
|
|
float projectedRadius;
|
|
mdvFrame_t *frame;
|
|
int lod;
|
|
|
|
if(tr.currentModel->numLods < 2)
|
|
{
|
|
// model has only 1 LOD level, skip computations and bias
|
|
lod = 0;
|
|
}
|
|
else
|
|
{
|
|
// multiple LODs exist, so compute projected bounding sphere
|
|
// and use that as a criteria for selecting LOD
|
|
|
|
frame = tr.currentModel->mdv[0]->frames;
|
|
frame += ent->e.frame;
|
|
|
|
radius = RadiusFromBounds(frame->bounds[0], frame->bounds[1]);
|
|
|
|
if((projectedRadius = R_ProjectRadius(radius, ent->e.origin)) != 0)
|
|
{
|
|
lodscale = r_lodScale->value;
|
|
if(lodscale > 20)
|
|
lodscale = 20;
|
|
flod = 1.0f - projectedRadius * lodscale;
|
|
}
|
|
else
|
|
{
|
|
// object intersects near view plane, e.g. view weapon
|
|
flod = 0;
|
|
}
|
|
|
|
flod *= tr.currentModel->numLods;
|
|
lod = Q_ftol(flod);
|
|
|
|
if(lod < 0)
|
|
{
|
|
lod = 0;
|
|
}
|
|
else if(lod >= tr.currentModel->numLods)
|
|
{
|
|
lod = tr.currentModel->numLods - 1;
|
|
}
|
|
}
|
|
|
|
lod += r_lodBias->integer;
|
|
|
|
if(lod >= tr.currentModel->numLods)
|
|
lod = tr.currentModel->numLods - 1;
|
|
if(lod < 0)
|
|
lod = 0;
|
|
|
|
return lod;
|
|
}
|
|
|
|
|
|
static shader_t *GetMDVSurfaceShader(const trRefEntity_t * ent, mdvSurface_t * mdvSurface)
|
|
{
|
|
shader_t *shader = 0;
|
|
|
|
if(ent->e.customShader)
|
|
{
|
|
shader = R_GetShaderByHandle(ent->e.customShader);
|
|
}
|
|
else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins)
|
|
{
|
|
skin_t *skin;
|
|
int j;
|
|
|
|
skin = R_GetSkinByHandle(ent->e.customSkin);
|
|
|
|
// match the surface name to something in the skin file
|
|
shader = tr.defaultShader;
|
|
for(j = 0; j < skin->numSurfaces; j++)
|
|
{
|
|
// the names have both been lowercased
|
|
if(!strcmp(skin->surfaces[j]->name, mdvSurface->name))
|
|
{
|
|
shader = skin->surfaces[j]->shader;
|
|
break;
|
|
}
|
|
}
|
|
if(shader == tr.defaultShader)
|
|
{
|
|
ri.Printf(PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", mdvSurface->name, skin->name);
|
|
}
|
|
else if(shader->defaultShader)
|
|
{
|
|
ri.Printf(PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shader = mdvSurface->shader;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddMDVSurfaces
|
|
=================
|
|
*/
|
|
void R_AddMDVSurfaces(trRefEntity_t * ent)
|
|
{
|
|
int i;
|
|
mdvModel_t *model = 0;
|
|
mdvSurface_t *mdvSurface = 0;
|
|
shader_t *shader = 0;
|
|
int lod;
|
|
qboolean personalModel;
|
|
int fogNum;
|
|
|
|
// don't add third_person objects if not in a portal
|
|
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
|
|
|
|
if(ent->e.renderfx & RF_WRAP_FRAMES)
|
|
{
|
|
ent->e.frame %= tr.currentModel->mdv[0]->numFrames;
|
|
ent->e.oldframe %= tr.currentModel->mdv[0]->numFrames;
|
|
}
|
|
|
|
// compute LOD
|
|
if(ent->e.renderfx & RF_FORCENOLOD)
|
|
{
|
|
lod = 0;
|
|
}
|
|
else
|
|
{
|
|
lod = R_ComputeLOD(ent);
|
|
}
|
|
|
|
// Validate the frames so there is no chance of a crash.
|
|
// This will write directly into the entity structure, so
|
|
// when the surfaces are rendered, they don't need to be
|
|
// range checked again.
|
|
if((ent->e.frame >= tr.currentModel->mdv[lod]->numFrames)
|
|
|| (ent->e.frame < 0) || (ent->e.oldframe >= tr.currentModel->mdv[lod]->numFrames) || (ent->e.oldframe < 0))
|
|
{
|
|
ri.Printf(PRINT_DEVELOPER, "R_AddMDVSurfaces: no such frame %d to %d for '%s' (%d)\n",
|
|
ent->e.oldframe, ent->e.frame, tr.currentModel->name, tr.currentModel->mdv[lod]->numFrames);
|
|
ent->e.frame = 0;
|
|
ent->e.oldframe = 0;
|
|
}
|
|
|
|
model = tr.currentModel->mdv[lod];
|
|
|
|
// cull the entire model if merged bounding box of both frames
|
|
// is outside the view frustum.
|
|
R_CullMDV(model, ent);
|
|
if(ent->cull == CULL_OUT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// set up lighting now that we know we aren't culled
|
|
if(!personalModel || r_shadows->integer > SHADOWING_BLOB)
|
|
{
|
|
R_SetupEntityLighting(&tr.refdef, ent, NULL);
|
|
}
|
|
|
|
// see if we are in a fog volume
|
|
fogNum = R_FogWorldBox(ent->worldBounds);
|
|
|
|
// draw all surfaces
|
|
if(r_vboModels->integer && model->numVBOSurfaces)
|
|
{
|
|
int i;
|
|
srfVBOMDVMesh_t *vboSurface;
|
|
shader_t *shader;
|
|
|
|
for(i = 0; i < model->numVBOSurfaces; i++)
|
|
{
|
|
vboSurface = model->vboSurfaces[i];
|
|
mdvSurface = vboSurface->mdvSurface;
|
|
|
|
shader = GetMDVSurfaceShader(ent, mdvSurface);
|
|
|
|
// don't add third_person objects if not viewing through a portal
|
|
if(!personalModel)
|
|
{
|
|
R_AddDrawSurf((void *)vboSurface, shader, -1, fogNum);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i = 0, mdvSurface = model->surfaces; i < model->numSurfaces; i++, mdvSurface++)
|
|
{
|
|
shader = GetMDVSurfaceShader(ent, mdvSurface);
|
|
|
|
// we will add shadows even if the main object isn't visible in the view
|
|
|
|
// don't add third_person objects if not viewing through a portal
|
|
if(!personalModel)
|
|
{
|
|
R_AddDrawSurf((void *)mdvSurface, shader, -1, fogNum);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AddMDVInteractions
|
|
=================
|
|
*/
|
|
void R_AddMDVInteractions(trRefEntity_t * ent, trRefLight_t * light)
|
|
{
|
|
int i;
|
|
mdvModel_t *model = 0;
|
|
mdvSurface_t *mdvSurface = 0;
|
|
shader_t *shader = 0;
|
|
int lod;
|
|
qboolean personalModel;
|
|
byte cubeSideBits;
|
|
interactionType_t iaType = IA_DEFAULT;
|
|
|
|
// cull the entire model if merged bounding box of both frames
|
|
// is outside the view frustum and we don't care about proper shadowing
|
|
if(ent->cull == CULL_OUT)
|
|
{
|
|
if(r_shadows->integer <= SHADOWING_BLOB || light->l.noShadows)
|
|
return;
|
|
else
|
|
iaType = IA_SHADOWONLY;
|
|
}
|
|
|
|
// avoid drawing of certain objects
|
|
#if defined(USE_REFENTITY_NOSHADOWID)
|
|
if(light->l.inverseShadows)
|
|
{
|
|
if(iaType != IA_LIGHTONLY && (light->l.noShadowID && (light->l.noShadowID != ent->e.noShadowID)))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if(iaType != IA_LIGHTONLY && (light->l.noShadowID && (light->l.noShadowID == ent->e.noShadowID)))
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// don't add third_person objects if not in a portal
|
|
personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal;
|
|
|
|
// compute LOD
|
|
lod = R_ComputeLOD(ent);
|
|
|
|
model = tr.currentModel->mdv[lod];
|
|
|
|
// do a quick AABB cull
|
|
if(!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], ent->worldBounds[0], ent->worldBounds[1]))
|
|
{
|
|
tr.pc.c_dlightSurfacesCulled += model->numSurfaces;
|
|
return;
|
|
}
|
|
|
|
// do a more expensive and precise light frustum cull
|
|
if(!r_noLightFrustums->integer)
|
|
{
|
|
if(R_CullLightWorldBounds(light, ent->worldBounds) == CULL_OUT)
|
|
{
|
|
tr.pc.c_dlightSurfacesCulled += model->numSurfaces;
|
|
return;
|
|
}
|
|
}
|
|
|
|
cubeSideBits = R_CalcLightCubeSideBits(light, ent->worldBounds);
|
|
|
|
// generate interactions with all surfaces
|
|
if(r_vboModels->integer && model->numVBOSurfaces)
|
|
{
|
|
// new brute force method: just render everthing with static VBOs
|
|
int i;
|
|
srfVBOMDVMesh_t *vboSurface;
|
|
shader_t *shader;
|
|
|
|
|
|
// static VBOs are fine for lighting and shadow mapping
|
|
for(i = 0; i < model->numVBOSurfaces; i++)
|
|
{
|
|
vboSurface = model->vboSurfaces[i];
|
|
mdvSurface = vboSurface->mdvSurface;
|
|
|
|
shader = GetMDVSurfaceShader(ent, mdvSurface);
|
|
|
|
// skip all surfaces that don't matter for lighting only pass
|
|
if(shader->isSky || (!shader->interactLight && shader->noShadows))
|
|
continue;
|
|
|
|
// we will add shadows even if the main object isn't visible in the view
|
|
|
|
// don't add third_person objects if not viewing through a portal
|
|
if(!personalModel)
|
|
{
|
|
R_AddLightInteraction(light, (void *)vboSurface, shader, cubeSideBits, iaType);
|
|
tr.pc.c_dlightSurfaces++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i = 0, mdvSurface = model->surfaces; i < model->numSurfaces; i++, mdvSurface++)
|
|
{
|
|
shader = GetMDVSurfaceShader(ent, mdvSurface);
|
|
|
|
// skip all surfaces that don't matter for lighting only pass
|
|
if(shader->isSky || (!shader->interactLight && shader->noShadows))
|
|
continue;
|
|
|
|
// we will add shadows even if the main object isn't visible in the view
|
|
|
|
// don't add third_person objects if not viewing through a portal
|
|
if(!personalModel)
|
|
{
|
|
R_AddLightInteraction(light, (void *)mdvSurface, shader, cubeSideBits, iaType);
|
|
tr.pc.c_dlightSurfaces++;
|
|
}
|
|
}
|
|
}
|
|
}
|