Descent3/model/newstyle.cpp

1448 lines
35 KiB
C++
Raw Normal View History

2024-04-16 03:43:29 +00:00
/*
* $Logfile: /DescentIII/Main/model/newstyle.cpp $
* $Revision: 122 $
* $Date: 3/20/00 12:28p $
* $Author: Matt $
*
* Polygon model code
*
* $Log: /DescentIII/Main/model/newstyle.cpp $
*
* 122 3/20/00 12:28p Matt
* Merge of Duane's post-1.3 changes.
* Check for NULL pointer.
*
* 121 7/08/99 5:47p Jason
* changes for new bumpmapping system in 1.1 update patch
*
* 120 6/08/99 1:00p Jason
* changes for bumpmapping
*
* 119 5/18/99 10:31a Jason
* polymodel stuff wasn't getting paged in before drawing
*
* 118 5/14/99 2:03p Jason
* added polymodel errors to catch bugs
*
* 117 5/14/99 12:02a Jason
* more speedups for g3_DrawPoly
*
* 116 5/13/99 8:33p Matt
* Consolidated Mac & Windows code.
*
* 115 5/13/99 5:07p Ardussi
* changes for compiling on the Mac
*
* 114 5/04/99 4:34p Jason
* changes for bumpmapping
*
* 113 4/22/99 8:29p Kevin
* made psrand.h come after stdlib.h
*
* 112 4/21/99 11:05a Kevin
* new ps_rand and ps_srand to replace rand & srand
*
* 111 4/16/99 5:32p Jason
* fixed smooth specularity with objects
*
* 110 4/14/99 1:36a Jeff
* fixed case mismatched #includes
*
* 109 4/09/99 12:06p Jason
* made model setup code faster
*
* 108 3/31/99 12:26p Jason
* fixed some issues related to non wbuffer cards
*
* 107 3/24/99 10:22a Matt
* Fixed null-pointer bug
*
*/
#include "pserror.h"
#include "pstypes.h"
#include "3d.h"
#include "vecmat.h"
#include "grdefs.h"
#include "polymodel.h"
#include "gametexture.h"
#include "BYTESWAP.H"
#include "renderer.h"
#include "lighting.h"
#include "game.h"
#include "render.h"
#include "fireball.h"
#include "lightmap_info.h"
#include "lightmap.h"
#include "lighting.h"
#include "findintersection.h"
#include <stdlib.h>
#include <search.h>
#include <string.h>
#include "psrand.h"
static float face_depth[MAX_POLYGON_VECS];
static ubyte triangulated_faces[MAX_FACES_PER_ROOM];
static ubyte FacingPass=0;
static int Multicolor_texture=-1;
static vector Fog_plane;
static float Fog_distance,Fog_eye_distance;
static vector Fog_view_pos,Specular_view_pos,Bump_view_pos;
static matrix Unscaled_bumpmap_matrix;
static int ModelFaceSortFunc(const short *a, const short *b)
{
float az,bz;
az = face_depth[*a];
bz = face_depth[*b];
if (az < bz)
return -1;
else if (az > bz)
return 1;
else
return 0;
}
#ifdef _DEBUG
void model_draw_outline(int nverts,g3Point **pointlist)
{
int i;
for (i=0;i<nverts-1;i++)
g3_DrawLine(GR_RGB(255,255,255),pointlist[i],pointlist[i+1]);
g3_DrawLine(GR_RGB(255,255,255),pointlist[i],pointlist[0]);
}
void DrawSubmodelFaceOutline (int nv,g3Point **pointlist)
{
int i;
g3Point tpnt[64];
g3Point *tpnt_list[64];
ASSERT (nv<64);
for (i=0;i<nv;i++)
{
tpnt[i]=*pointlist[i];
tpnt_list[i]=&tpnt[i];
}
for (i=0;i<nv-1;i++)
g3_DrawLine(GR_RGB(255,255,0),tpnt_list[i],tpnt_list[i+1]);
g3_DrawLine(GR_RGB(255,255,0),tpnt_list[i],tpnt_list[0]);
}
#endif
#define CROSS_WIDTH 8.0
int Lightmap_debug_subnum=-1;
int Lightmap_debug_facenum=-1;
int Lightmap_debug_model=-1;
inline void RenderSubmodelFace (poly_model *pm,bsp_info *sm,int facenum)
{
g3Point *pointlist[100];
int bm_handle;
int smooth=0;
polyface *fp=&sm->faces[facenum];
int modelnum=sm-pm->submodel;
texture *texp=NULL;
int t;
int custom=0;
g3Codes face_cc;
int triface=0;
face_cc.cc_and=0xff;
face_cc.cc_or=0;
triangulated_faces[facenum]=0;
if (sm->flags & SOF_CUSTOM)
custom=1;
// Setup texturing
if (fp->texnum!=-1)
texp=&GameTextures[pm->textures[fp->texnum]];
if (texp && custom && Polymodel_use_effect && (Polymodel_effect.type & PEF_CUSTOM_TEXTURE))
texp=&GameTextures[Polymodel_effect.custom_texture];
// Set radiosity lightmaps if needed
if (Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP)
{
rend_SetOverlayMap (LightmapInfo[Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].lmi_handle].lm_handle);
}
// Do bump mapping
if ((Polymodel_effect.type & PEF_BUMPMAPPED) &&texp && texp->bumpmap!=-1 && Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD )
{
rend_SetOverlayType (OT_NONE);
rend_SetBumpmapReadyState(1,texp->bumpmap);
if ((GameTextures[fp->texnum].flags & TF_SMOOTH_SPECULAR))
smooth=1;
}
float uchange=0,vchange=0;
// Figure out if there is any texture sliding
if (texp && texp->slide_u!=0)
{
int int_time=Gametime/texp->slide_u;
float norm_time=Gametime-(int_time*texp->slide_u);
norm_time/=texp->slide_u;
uchange=norm_time;
}
if (texp && texp->slide_v!=0)
{
int int_time=Gametime/texp->slide_v;
float norm_time=Gametime-(int_time*texp->slide_v);
norm_time/=texp->slide_v;
vchange=norm_time;
}
ASSERT (fp->nverts<100);
// Setup the points for this face
for (t=0;t<fp->nverts;t++)
{
g3Point *p = &Robot_points[fp->vertnums[t]];
pointlist[t] = p;
if (texp)
{
p->p3_uvl.u = fp->u[t]+uchange;
p->p3_uvl.v = fp->v[t]+vchange;
p->p3_uvl.a = sm->alpha[fp->vertnums[t]];
p->p3_flags |=PF_UV+PF_RGBA+PF_L;
// Assign bump mapping coords
if ((Polymodel_effect.type & PEF_BUMPMAPPED) && texp->bumpmap!=-1 && Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD )
{
p->p3_flags|=PF_UV2;
vector vert=sm->verts[fp->vertnums[t]];
vector vertnorm;
if (smooth)
vertnorm=sm->vertnorms[fp->vertnums[t]];
else
vertnorm=fp->normal;
vector subvec=Bump_view_pos-vert;
vm_NormalizeVectorFast (&subvec);
vector incident_norm=vert-Polymodel_bump_pos;
vm_NormalizeVectorFast (&incident_norm);
float d=incident_norm * vertnorm;
vector upvec=d * vertnorm;
incident_norm-=(2*upvec);
float dotp=(subvec * incident_norm);
if (dotp<0)
dotp=0;
if (dotp>1)
dotp=1;
float val=dotp*.5;
p->p3_uvl.u2=val;
p->p3_uvl.v2=val;
}
}
if (Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP)
{
p->p3_flags |=PF_UV2;
p->p3_uvl.u2=Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].u2[t];
p->p3_uvl.v2=Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].v2[t];
}
face_cc.cc_or|=p->p3_codes;
face_cc.cc_and&=p->p3_codes;
}
if (face_cc.cc_or && Polymodel_use_effect && (Polymodel_effect.type & (PEF_FOGGED_MODEL|PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES)))
{
triface=1;
triangulated_faces[facenum]=1;
}
// If there is a texture, set it up
if (texp)
{
bm_handle=GetTextureBitmap(texp-GameTextures,0);
rend_SetTextureType (TT_LINEAR);
if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD)
rend_SetLighting (LS_GOURAUD);
// If this is a light texture, make the texture full bright
if (texp->flags & TF_LIGHT)
{
rend_SetLighting (LS_FLAT_GOURAUD);
rend_SetFlatColor (GR_RGB(255,255,255));
}
// Setup custom color if there is one
if (Polymodel_use_effect && (Polymodel_effect.type & PEF_CUSTOM_COLOR) && (texp-GameTextures)==Multicolor_texture)
{
int r,g,b;
rend_SetLighting (LS_FLAT_GOURAUD);
r=GR_COLOR_RED (Polymodel_effect.custom_color);
g=GR_COLOR_GREEN (Polymodel_effect.custom_color);
b=GR_COLOR_BLUE (Polymodel_effect.custom_color);
if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD)
{
if (Polymodel_use_effect && Polymodel_effect.type & PEF_COLOR)
{
r=Polymodel_effect.r*(float)r*Polylighting_static_red;
g=Polymodel_effect.g*(float)g*Polylighting_static_green;
b=Polymodel_effect.b*(float)b*Polylighting_static_blue;
}
else
{
r=(float)r*Polylighting_static_red;
g=(float)g*Polylighting_static_green;
b=(float)b*Polylighting_static_blue;
}
}
rend_SetFlatColor(GR_RGB(r,g,b));
}
if (Polymodel_use_effect && (Polymodel_effect.type & PEF_ALPHA))
rend_SetAlphaValue (texp->alpha * Polymodel_effect.alpha * 255 );
else
rend_SetAlphaValue (texp->alpha*255);
if (texp->flags & TF_SATURATE)
rend_SetAlphaType (AT_SATURATE_CONSTANT_VERTEX);
else
{
if ((texp->flags & TF_ALPHA) || (Polymodel_use_effect && (Polymodel_effect.type & PEF_ALPHA)))
rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX);
else
rend_SetAlphaType (ATF_TEXTURE+ATF_VERTEX);
}
}
else
{
rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX);
if (Polymodel_use_effect && (Polymodel_effect.type & PEF_ALPHA))
rend_SetAlphaValue (Polymodel_effect.alpha * 255 );
else
rend_SetAlphaValue (255);
rend_SetLighting(LS_NONE);
rend_SetTextureType (TT_FLAT);
int r,g,b;
r=GR_COLOR_RED (fp->color);
g=GR_COLOR_GREEN (fp->color);
b=GR_COLOR_BLUE (fp->color);
if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD)
{
if (Polymodel_use_effect && Polymodel_effect.type & PEF_COLOR)
{
r=Polymodel_effect.r*(float)r*Polylighting_static_red;
g=Polymodel_effect.g*(float)g*Polylighting_static_green;
b=Polymodel_effect.b*(float)b*Polylighting_static_blue;
}
else
{
r=(float)r*Polylighting_static_red;
g=(float)g*Polylighting_static_green;
b=(float)b*Polylighting_static_blue;
}
}
rend_SetFlatColor(GR_RGB(r,g,b));
bm_handle=0;
}
if (triface)
g3_SetTriangulationTest(1);
g3_DrawPoly(fp->nverts,pointlist,bm_handle,MAP_TYPE_BITMAP,&face_cc);
if (triface)
g3_SetTriangulationTest(0);
if (texp && (Polymodel_effect.type & PEF_BUMPMAPPED) && texp->bumpmap!=-1 && Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD )
{
rend_SetBumpmapReadyState(0,0);
}
#ifdef _DEBUG
if (Polymodel_outline_mode)
DrawSubmodelFaceOutline (fp->nverts,pointlist);
/* if (Lightmap_debug_model==(pm-Poly_models) && Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP && Lightmap_debug_subnum==modelnum && Lightmap_debug_facenum==facenum)
{
int lmi_handle=Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].lmi_handle;
lightmap_object_face *lfp=&Polylighting_lightmap_object->lightmap_faces[modelnum][facenum];
lightmap_info *lmi_ptr=&LightmapInfo[lmi_handle];
int w=lmi_w (lmi_handle);
int h=lmi_h (lmi_handle);
vector rvec=lfp->rvec*lmi_ptr->xspacing;
vector uvec=lfp->uvec*lmi_ptr->yspacing;
ushort *src_data=(ushort *)lm_data(lmi_ptr->lm_handle);
for (int i=0;i<w*h;i++)
{
int t;
g3Point epoints[20];
vector evec[20];
int y=i/w;
int x=i%w;
evec[0]=lmi_ptr->upper_left-(y*uvec)+(x*rvec);
g3_RotatePoint(&epoints[0],&evec[0]);
pointlist[0] = &epoints[0];
evec[1]=lmi_ptr->upper_left-(y*uvec)+((x+1)*rvec);
g3_RotatePoint(&epoints[1],&evec[1]);
pointlist[1] = &epoints[1];
evec[2]=lmi_ptr->upper_left-((y+1)*uvec)+((x+1)*rvec);
g3_RotatePoint(&epoints[2],&evec[2]);
pointlist[2] = &epoints[2];
evec[3]=lmi_ptr->upper_left-((y+1)*uvec)+(x*rvec);
g3_RotatePoint(&epoints[3],&evec[3]);
pointlist[3] = &epoints[3];
if (!(src_data[y*w+x] & OPAQUE_FLAG))
{
for (t=0;t<4;t++)
g3_DrawLine(GR_RGB(255,0,255),pointlist[t],pointlist[(t+1)%4]);
}
else
{
for (t=0;t<4;t++)
g3_DrawLine(GR_RGB(255,255,255),pointlist[t],pointlist[(t+1)%4]);
}
}
// Draw red cross where upper left is
ubyte c0;
g3Point p0;
p0.p3_flags=0;
c0 = g3_RotatePoint(&p0,&LightmapInfo[lmi_handle].upper_left);
if (! c0)
{
//Draw a little cross at the current vert
g3_ProjectPoint(&p0); //make sure projected
rend_SetFlatColor(GR_RGB(255,0,0));
rend_DrawLine(p0.p3_sx-CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy-CROSS_WIDTH);
rend_DrawLine(p0.p3_sx,p0.p3_sy-CROSS_WIDTH,p0.p3_sx+CROSS_WIDTH,p0.p3_sy);
rend_DrawLine(p0.p3_sx+CROSS_WIDTH,p0.p3_sy,p0.p3_sx,p0.p3_sy+CROSS_WIDTH);
rend_DrawLine(p0.p3_sx,p0.p3_sy+CROSS_WIDTH,p0.p3_sx-CROSS_WIDTH,p0.p3_sy);
}
}*/
#endif
}
inline void RenderSubmodelLightmapFace (poly_model *pm,bsp_info *sm,int facenum)
{
g3Point *pointlist[100];
polyface *fp=&sm->faces[facenum];
int modelnum=sm-pm->submodel;
int t;
int lm_handle=LightmapInfo[Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].lmi_handle].lm_handle;
float xscalar=(float)GameLightmaps[lm_handle].width/(float)GameLightmaps[lm_handle].square_res;
float yscalar=(float)GameLightmaps[lm_handle].height/(float)GameLightmaps[lm_handle].square_res;
ASSERT (fp->nverts<100);
// Setup the points for this face
for (t=0;t<fp->nverts;t++)
{
g3Point *p = &Robot_points[fp->vertnums[t]];
pointlist[t] = p;
p->p3_uvl.u = Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].u2[t]*xscalar;
p->p3_uvl.v = Polylighting_lightmap_object->lightmap_faces[modelnum][facenum].v2[t]*yscalar;
p->p3_uvl.l = 1.0;
p->p3_flags |=PF_UV2+PF_RGBA+PF_L;
}
if (triangulated_faces[facenum])
g3_SetTriangulationTest (1);
g3_DrawPoly(fp->nverts,pointlist,lm_handle,MAP_TYPE_LIGHTMAP);
if (triangulated_faces[facenum])
g3_SetTriangulationTest (0);
}
inline void RenderSubmodelFaceFogged (poly_model *pm,bsp_info *sm,int facenum)
{
g3Point *pointlist[100];
polyface *fp=&sm->faces[facenum];
int modelnum=sm-pm->submodel;
int t;
for (t=0;t<fp->nverts;t++)
{
g3Point *p = &Robot_points[fp->vertnums[t]];
pointlist[t] = p;
float mag;
if (Polymodel_effect.fog_plane_check==1)
{
mag = vm_DotProduct(&Fog_plane,&sm->verts[fp->vertnums[t]])+Fog_distance;
}
else
{
vector *vec=&sm->verts[fp->vertnums[t]];
// Now we must generate the split point. This is simply
// an equation in the form Origin + t*Direction
float dist = (*vec*Polymodel_fog_plane)+ Fog_distance;
vector subvec=*vec-Fog_view_pos;
float t = Fog_eye_distance / (Fog_eye_distance - dist);
vector portal_point=Fog_view_pos+(t*subvec);
float eye_distance=-(vm_DotProduct (&Fog_plane,&portal_point));
mag = vm_DotProduct(&Fog_plane,vec)+eye_distance;
}
float scalar=mag/Polymodel_effect.fog_depth;
if (scalar>1)
scalar=1;
if (scalar<0)
scalar=0;
p->p3_a=scalar;
p->p3_flags |= PF_RGBA;
}
if (triangulated_faces[facenum])
g3_SetTriangulationTest (1);
g3_DrawPoly(fp->nverts,pointlist,0);
if (triangulated_faces[facenum])
g3_SetTriangulationTest (0);
}
inline void RenderSubmodelFaceSpecular (poly_model *pm,bsp_info *sm,int facenum)
{
g3Point *pointlist[100];
polyface *fp=&sm->faces[facenum];
int modelnum=sm-pm->submodel;
int t;
bool smooth=0;
if ((Polymodel_effect.type & PEF_SPECULAR_FACES) && (GameTextures[fp->texnum].flags & TF_SMOOTH_SPECULAR))
smooth=1;
for (t=0;t<fp->nverts;t++)
{
g3Point *p = &Robot_points[fp->vertnums[t]];
vector vert=sm->verts[fp->vertnums[t]];
vector vertnorm;
if (smooth)
vertnorm=sm->vertnorms[fp->vertnums[t]];
else
vertnorm=fp->normal;
pointlist[t] = p;
p->p3_flags |= PF_RGBA;
p->p3_a=0.0;
vector subvec=Specular_view_pos-vert;
vm_NormalizeVectorFast (&subvec);
vector incident_norm=vert-Polymodel_specular_pos;
vm_NormalizeVectorFast (&incident_norm);
float d=incident_norm * vertnorm;
vector upvec=d * vertnorm;
incident_norm-=(2*upvec);
float dotp=subvec * incident_norm;
if (dotp<0)
continue;
if (dotp>1)
dotp=1;
if (dotp>0)
{
int index=((float)(MAX_SPECULAR_INCREMENTS-1)*dotp);
float val=Specular_tables[2][index];
p->p3_a=val*Polymodel_effect.spec_scalar;
}
}
if (triangulated_faces[facenum])
g3_SetTriangulationTest (1);
g3_DrawPoly(fp->nverts,pointlist,0);
if (triangulated_faces[facenum])
g3_SetTriangulationTest (0);
}
#define MAX_PARTS 100
// Draws a glowing cone of light that represents thrusters
void DrawThrusterEffect (vector *pos,float r,float g,float b,vector *norm,float size,float length)
{
vector cur_pos=*pos;
float cur_length=0;
vector glow_pos[MAX_PARTS];
float glow_size[MAX_PARTS];
int total_parts=0;
if (length<.1)
return;
int num_divs=length*3;
if (num_divs>MAX_PARTS)
num_divs=MAX_PARTS;
float size_change=size/num_divs;
float pos_change=length/num_divs;
if (!UseHardware)
return; // No software stuff here!
rend_SetZBufferWriteMask (0);
rend_SetAlphaType (AT_SATURATE_TEXTURE);
rend_SetAlphaValue (.3*255);
rend_SetLighting (LS_GOURAUD);
rend_SetColorModel (CM_RGB);
ddgr_color color=GR_RGB(r*255,g*255,b*255);
int bm_handle=Fireballs[GRADIENT_BALL_INDEX].bm_handle;
int t;
// We must draw the small ones first, but we're starting the iteration from the
// large one. Consequently, we must store our variables so we can draw in reverse order
for (t=0;t<num_divs && size>.05;t++)
{
glow_pos[t]=cur_pos;
glow_size[t]=size;
size-=size_change;
cur_pos+=((*norm)*pos_change);
total_parts++;
}
for (t=total_parts-1;t>=0;t--)
{
rend_SetZBias (-glow_size[t]);
g3_DrawBitmap (&glow_pos[t],glow_size[t],(glow_size[t]*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color);
}
rend_SetZBias (0.0);
rend_SetZBufferWriteMask (1);
}
// Draws a glowing cone of light
void DrawGlowEffect (vector *pos,float r,float g,float b,vector *norm,float size)
{
if (!UseHardware)
return; // No software stuff here!
if (Polymodel_use_effect && Polymodel_effect.type & PEF_NO_GLOWS)
return;
rend_SetZBufferWriteMask (0);
rend_SetAlphaType (AT_SATURATE_TEXTURE);
rend_SetAlphaValue (.8*255);
rend_SetLighting (LS_GOURAUD);
rend_SetColorModel (CM_RGB);
ddgr_color color=GR_RGB(r*255,g*255,b*255);
int bm_handle=Fireballs[GRADIENT_BALL_INDEX].bm_handle;
rend_SetZBias (-size);
g3_DrawBitmap (pos,size,(size*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle,color);
rend_SetZBias (0.0);
rend_SetZBufferWriteMask (1);
}
void RenderSubmodelFacesSorted (poly_model *pm,bsp_info *sm)
{
int i,t;
int rcount;
int model_render_order[MAX_POLYGON_VECS];
int modelnum=sm-pm->submodel;
ASSERT (sm->nverts<MAX_POLYGON_VECS);
//Build list of visible (non-backfacing) faces, & compute average face depths
for (i=rcount=0;i<sm->num_faces;i++)
{
polyface *fp = &sm->faces[i];
//check for visible face
if (g3_CheckNormalFacing(&sm->verts[fp->vertnums[0]],&fp->normal))
{
face_depth[i] = 0;
for (t=0;t<fp->nverts;t++)
face_depth[i] += Robot_points[fp->vertnums[t]].p3_z;
face_depth[i] /= fp->nverts;
//initialize order list
model_render_order[rcount] = i;
rcount++;
ASSERT (rcount<MAX_POLYGON_VECS);
}
}
//Sort the faces
qsort(model_render_order,rcount,sizeof(*model_render_order),(int (*)(const void*,const void*)) ModelFaceSortFunc);
for (i=rcount-1;i>=0;i--)
{
int facenum=model_render_order[i];
RenderSubmodelFace (pm,sm,facenum);
}
}
void RenderSubmodelFacesUnsorted (poly_model *pm,bsp_info *sm)
{
int i;
int modelnum=sm-pm->submodel;
short alpha_faces[MAX_FACES_PER_ROOM],num_alpha_faces=0;
int rcount=0;
vector view_pos;
g3_GetViewPosition (&view_pos);
g3_GetUnscaledMatrix (&Unscaled_bumpmap_matrix);
Specular_view_pos=view_pos;
Bump_view_pos=view_pos;
if (modelnum<0 || modelnum>=pm->n_models)
{
Error ("Got bad model number %d from polymodel %s!",modelnum,pm->name);
return;
}
if (sm->flags & SOF_CUSTOM)
rend_SetZBias (-.5);
for (i=0;i<sm->num_faces;i++)
{
vector tempv;
polyface *fp=&sm->faces[i];
texture *texp;
// Check to see if this face even faces us!
tempv = view_pos - sm->verts[fp->vertnums[0]];
if ((tempv * fp->normal)<0)
continue;
if (fp->texnum!=-1)
{
texp=&GameTextures[pm->textures[fp->texnum]];
if (texp->flags & TF_ALPHA || texp->flags & TF_SATURATE)
{
alpha_faces[num_alpha_faces++] = i;
continue;
}
}
if (StateLimited)
{
State_elements[rcount].facenum=i;
State_elements[rcount].sort_key=pm->textures[fp->texnum];
rcount++;
}
else
RenderSubmodelFace (pm,sm,i);
}
if (StateLimited)
{
SortStates (State_elements,rcount);
for (i=rcount-1;i>=0;i--)
{
int facenum=State_elements[i].facenum;
RenderSubmodelFace (pm,sm,facenum);
}
if (!NoLightmaps)
{
if (!UseMultitexture && Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP)
{
rend_SetAlphaType(AT_LIGHTMAP_BLEND);
rend_SetLighting (LS_GOURAUD);
rend_SetColorModel (CM_MONO);
rend_SetOverlayType (OT_NONE);
rend_SetTextureType(TT_PERSPECTIVE);
rend_SetWrapType (WT_CLAMP);
rend_SetMipState (0);
for (i=rcount-1;i>=0;i--)
{
int facenum=State_elements[i].facenum;
RenderSubmodelLightmapFace (pm,sm,facenum);
}
rend_SetWrapType (WT_WRAP);
rend_SetMipState (1);
}
}
}
// Now render all alpha faces
//rend_SetZBufferWriteMask (0);
for (i=0;i<num_alpha_faces;i++)
RenderSubmodelFace(pm,sm,alpha_faces[i]);
//rend_SetZBufferWriteMask (1);
if (sm->flags & SOF_CUSTOM)
rend_SetZBias (0);
// Draw specular faces if needed
if (Polymodel_use_effect && Polymodel_effect.type & (PEF_SPECULAR_MODEL|PEF_SPECULAR_FACES))
{
rend_SetOverlayType (OT_NONE);
rend_SetTextureType (TT_FLAT);
rend_SetLighting (LS_NONE);
rend_SetColorModel (CM_MONO);
rend_SetAlphaType (AT_SATURATE_VERTEX);
rend_SetAlphaValue (255);
rend_SetZBufferWriteMask (0);
rend_SetFlatColor (GR_RGB((int)(Polymodel_effect.spec_r*255.0),(int)(Polymodel_effect.spec_g*255.0),(int)(Polymodel_effect.spec_b*255.0)));
for (i=0;i<sm->num_faces;i++)
{
polyface *fp=&sm->faces[i];
if (!g3_CheckNormalFacing(&sm->verts[fp->vertnums[0]],&fp->normal))
continue;
/* vector subvec=sm->verts[fp->vertnums[0]]-Polymodel_specular_pos;
if ((fp->normal * subvec)> 0)
continue;*/
if ((Polymodel_effect.type & PEF_SPECULAR_MODEL) || (fp->texnum!=-1 && GameTextures[pm->textures[fp->texnum]].flags & TF_SPECULAR))
RenderSubmodelFaceSpecular (pm,sm,i);
}
rend_SetZBufferWriteMask (1);
}
// Draw fog if need be
if (Polymodel_use_effect && Polymodel_effect.type & PEF_FOGGED_MODEL)
{
matrix mat;
g3_GetUnscaledMatrix (&mat);
g3_GetViewPosition (&Fog_view_pos);
Fog_plane=mat.fvec;
if (Polymodel_effect.fog_plane_check==1)
Fog_distance=-(vm_DotProduct (&Fog_plane,&Fog_view_pos));
else
{
Fog_distance=-(vm_DotProduct (&Polymodel_fog_plane,&Polymodel_fog_portal_vert));
Fog_eye_distance = (Fog_view_pos*Polymodel_fog_plane)+ Fog_distance;
}
rend_SetOverlayType (OT_NONE);
rend_SetTextureType (TT_FLAT);
rend_SetLighting (LS_NONE);
rend_SetColorModel (CM_MONO);
rend_SetAlphaType (AT_VERTEX);
rend_SetAlphaValue (255);
rend_SetZBufferWriteMask (0);
rend_SetCoplanarPolygonOffset(1);
rend_SetFlatColor (GR_RGB((int)(Polymodel_effect.fog_r*255.0),(int)(Polymodel_effect.fog_g*255.0),(int)(Polymodel_effect.fog_b*255.0)));
for (i=0;i<sm->num_faces;i++)
{
polyface *fp=&sm->faces[i];
if (!g3_CheckNormalFacing(&sm->verts[fp->vertnums[0]],&fp->normal))
continue;
RenderSubmodelFaceFogged (pm,sm,i);
}
rend_SetCoplanarPolygonOffset(0);
rend_SetZBufferWriteMask (1);
}
}
void BuildModelAngleMatrix( matrix *mat, angle ang,vector *axis);
void StartLightInstance (vector *,matrix *);
void DoneLightInstance();
// Rotates all of the points of a submodel, plus supplies color info
void RotateModelPoints (poly_model *pm,bsp_info *sm)
{
// Figure out lighting
if (Polymodel_light_type==POLYMODEL_LIGHTING_STATIC)
{
if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER))
{
for (int i=0;i<sm->nverts;i++)
{
vector vec=sm->verts[i];
float val=((ps_rand()%1000)-500.0)/500.0;
vec*=1.0+(Polymodel_effect.deform_range*val);
g3_RotatePoint(&Robot_points[i],&vec);
}
}
else
{
for (int i=0;i<sm->nverts;i++)
g3_RotatePoint(&Robot_points[i],&sm->verts[i]);
}
}
else if (Polymodel_light_type==POLYMODEL_LIGHTING_LIGHTMAP)
{
if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER))
{
for (int i=0;i<sm->nverts;i++)
{
vector vec=sm->verts[i];
float val=((ps_rand()%1000)-500.0)/500.0;
vec*=1.0+(Polymodel_effect.deform_range*val);
g3_RotatePoint(&Robot_points[i],&vec);
Robot_points[i].p3_r=1.0;
Robot_points[i].p3_g=1.0;
Robot_points[i].p3_b=1.0;
}
}
else
{
for (int i=0;i<sm->nverts;i++)
{
g3_RotatePoint(&Robot_points[i],&sm->verts[i]);
Robot_points[i].p3_r=1.0;
Robot_points[i].p3_g=1.0;
Robot_points[i].p3_b=1.0;
}
}
}
else if (Polymodel_light_type==POLYMODEL_LIGHTING_GOURAUD)
{
if (Polymodel_use_effect && Polymodel_effect.type & PEF_COLOR)
{
if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER))
{
for (int i=0;i<sm->nverts;i++)
{
vector vec=sm->verts[i];
float val=((ps_rand()%1000)-500.0)/500.0;
vec*=1.0+(Polymodel_effect.deform_range*val);
g3_RotatePoint(&Robot_points[i],&vec);
vector normvec=sm->vertnorms[i];
val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2;
Robot_points[i].p3_r=Polymodel_effect.r*val*Polylighting_static_red;
Robot_points[i].p3_g=Polymodel_effect.g*val*Polylighting_static_green;
Robot_points[i].p3_b=Polymodel_effect.b*val*Polylighting_static_blue;
}
}
else
{
if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER))
{
for (int i=0;i<sm->nverts;i++)
{
vector vec=sm->verts[i];
float val=((ps_rand()%1000)-500.0)/500.0;
vec*=1.0+(Polymodel_effect.deform_range*val);
g3_RotatePoint(&Robot_points[i],&vec);
vector normvec=sm->vertnorms[i];
val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2;
Robot_points[i].p3_r=Polymodel_effect.r*val*Polylighting_static_red;
Robot_points[i].p3_g=Polymodel_effect.g*val*Polylighting_static_green;
Robot_points[i].p3_b=Polymodel_effect.b*val*Polylighting_static_blue;
}
}
else
{
for (int i=0;i<sm->nverts;i++)
{
g3_RotatePoint(&Robot_points[i],&sm->verts[i]);
vector normvec=sm->vertnorms[i];
float val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2;
Robot_points[i].p3_r=Polymodel_effect.r*val*Polylighting_static_red;
Robot_points[i].p3_g=Polymodel_effect.g*val*Polylighting_static_green;
Robot_points[i].p3_b=Polymodel_effect.b*val*Polylighting_static_blue;
}
}
}
}
else
{
if ((Polymodel_use_effect && (Polymodel_effect.type & PEF_DEFORM)) || (sm->flags & SOF_JITTER))
{
for (int i=0;i<sm->nverts;i++)
{
vector vec=sm->verts[i];
float val=((ps_rand()%1000)-500.0)/500.0;
vec*=1.0+(Polymodel_effect.deform_range*val);
g3_RotatePoint(&Robot_points[i],&vec);
vector normvec=sm->vertnorms[i];
val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2;
Robot_points[i].p3_r=val*Polylighting_static_red;
Robot_points[i].p3_g=val*Polylighting_static_green;
Robot_points[i].p3_b=val*Polylighting_static_blue;
}
}
else
{
for (int i=0;i<sm->nverts;i++)
{
g3_RotatePoint(&Robot_points[i],&sm->verts[i]);
vector normvec=sm->vertnorms[i];
float val=(-vm_DotProduct (Polymodel_light_direction,&normvec)+1.0)/2;
Robot_points[i].p3_r=val*Polylighting_static_red;
Robot_points[i].p3_g=val*Polylighting_static_green;
Robot_points[i].p3_b=val*Polylighting_static_blue;
}
}
}
}
#ifndef RELEASE
if (!UseHardware)
{
for (int i=0;i<sm->nverts;i++)
Robot_points[i].p3_l=Robot_points[i].p3_g;
rend_SetColorModel (CM_MONO);
}
#endif
}
void RenderSubmodel (poly_model *pm,bsp_info *sm, uint f_render_sub)
{
int i;
matrix lightmatrix;
// Don't render door housings
if (IsNonRenderableSubmodel (pm,sm-pm->submodel))
return;
if (Polymodel_light_type!=POLYMODEL_LIGHTING_LIGHTMAP)
{
// Turn off bumpmapping if not needed
rend_SetBumpmapReadyState(0,0);
}
else
{
if (!StateLimited || UseMultitexture)
rend_SetOverlayType (OT_BLEND);
}
if (Multicolor_texture==-1 && Polymodel_use_effect && (Polymodel_effect.type & PEF_CUSTOM_COLOR))
Multicolor_texture=FindTextureName ("MultiColor");
rend_SetColorModel (CM_RGB);
StartPolyModelPosInstance(&sm->mod_pos);
vector temp_vec=sm->mod_pos+sm->offset;
g3_StartInstanceAngles(&temp_vec,&sm->angs );
vm_AnglesToMatrix (&lightmatrix,sm->angs.p,sm->angs.h,sm->angs.b);
StartLightInstance(&temp_vec,&lightmatrix);
// Check my bit to see if I get drawn
if(f_render_sub & (0x00000001 << (sm - pm->submodel)))
{
if (sm->flags & SOF_CUSTOM)
{
if (!(Polymodel_effect.type & PEF_CUSTOM_TEXTURE))
goto pop_lighting;
}
// Check to draw glow faces
if (sm->flags & (SOF_GLOW | SOF_THRUSTER))
{
if (!FacingPass)
goto pop_lighting;
vector zero_pos={0,0,0};
rend_SetOverlayType (OT_NONE);
if (Polymodel_use_effect && Polymodel_effect.type & PEF_GLOW_SCALAR)
{
if (Polymodel_effect.type & PEF_CUSTOM_GLOW)
DrawThrusterEffect (&zero_pos,Polymodel_effect.glow_r,Polymodel_effect.glow_g,Polymodel_effect.glow_b,&sm->glow_info->normal,sm->glow_info->glow_size*Polymodel_effect.glow_size_scalar,3*Polymodel_effect.glow_length_scalar);
else
DrawThrusterEffect (&zero_pos,sm->glow_info->glow_r,sm->glow_info->glow_g,sm->glow_info->glow_b,&sm->glow_info->normal,sm->glow_info->glow_size*Polymodel_effect.glow_size_scalar,3*Polymodel_effect.glow_length_scalar);
}
else
{
if (Polymodel_use_effect && Polymodel_effect.type & PEF_CUSTOM_GLOW)
DrawGlowEffect (&zero_pos,Polymodel_effect.glow_r,Polymodel_effect.glow_g,Polymodel_effect.glow_b,&sm->glow_info->normal,sm->glow_info->glow_size);
else
DrawGlowEffect (&zero_pos,sm->glow_info->glow_r,sm->glow_info->glow_g,sm->glow_info->glow_b,&sm->glow_info->normal,sm->glow_info->glow_size);
}
goto pop_lighting;
}
else if (sm->flags & SOF_FACING)
{
if (!FacingPass)
goto pop_lighting;
vector pos;
rend_SetLighting (LS_NONE);
rend_SetColorModel (CM_MONO);
rend_SetOverlayType (OT_NONE);
int bm_handle=GetTextureBitmap(pm->textures[sm->faces[0].texnum],0);
rend_SetAlphaValue (GameTextures[pm->textures[sm->faces[0].texnum]].alpha*255);
vm_MakeZero (&pos);
if (GameTextures[pm->textures[sm->faces[0].texnum]].flags & TF_SATURATE)
rend_SetAlphaType (AT_SATURATE_TEXTURE);
else
rend_SetAlphaType (ATF_CONSTANT+ATF_TEXTURE);
rend_SetZBufferWriteMask (0);
g3_DrawBitmap (&pos,sm->rad,(sm->rad*bm_h(bm_handle,0))/bm_w(bm_handle,0),bm_handle);
rend_SetZBufferWriteMask (1);
goto pop_lighting;
}
else
{
if (FacingPass)
goto pop_lighting;
}
RotateModelPoints (pm,sm);
if (!UseHardware)
RenderSubmodelFacesSorted (pm, sm);
else
RenderSubmodelFacesUnsorted (pm, sm);
}
pop_lighting:
for (i=0;i<sm->num_children;i++)
{
RenderSubmodel(pm,&pm->submodel[sm->children[i]], f_render_sub);
}
g3_DoneInstance();
DonePolyModelPosInstance();
DoneLightInstance();
}
int RenderPolygonModel(poly_model * pm, uint f_render_sub)
{
ASSERT (pm->new_style==1);
int i=0;
rend_SetAlphaType (ATF_CONSTANT+ATF_VERTEX);
rend_SetWrapType (WT_WRAP);
FacingPass=0;
for (i=0;i<pm->n_models;i++)
{
bsp_info *sm=&pm->submodel[i];
if (sm->parent==-1)
RenderSubmodel (pm,sm, f_render_sub);
}
// Now render any facing submodels
if (pm->flags & PMF_FACING)
{
// Don't render if we have it set for no glows
FacingPass=1;
rend_SetOverlayType (OT_NONE);
for (i=0;i<pm->n_models;i++)
{
bsp_info *sm=&pm->submodel[i];
if (sm->parent==-1)
RenderSubmodel (pm,sm, f_render_sub);
}
}
FacingPass=0;
return 1;
}
float ComputeDefaultSizeFunc(int handle, float *size_ptr, vector *offset_ptr, bool f_use_all_frames)
{
poly_model *pm;
matrix m;
float normalized_time[MAX_SUBOBJECTS];
int i, j, n;
float cur_dist;
float size = 0.0;
int start_frame = 0;
int end_frame = 0;
vector geometric_center = Zero_vector;
// Chris: Come see me when you are ready to deal with the paging problem - JL
pm = GetPolymodelPointer(handle);
ASSERT(start_frame <= end_frame);
ASSERT(end_frame <= pm->frame_max);
if(f_use_all_frames)
{
end_frame = pm->frame_max;
}
if(offset_ptr)
{
vector min_xyz;
vector max_xyz;
for(n = start_frame; n <= end_frame; n++)
{
// Because size changes with animation, we need the worst case point -- so, check every keyframe
// NOTE: This code does not currently account for all $turret and $rotate positions
SetNormalizedTimeAnim(n, normalized_time, pm);
SetModelAnglesAndPos (pm,normalized_time);
for (i = 0;i < pm->n_models; i++)
{
bsp_info *sm=&pm->submodel[i];
// For every vertex
for(j = 0; j < sm->nverts; j++)
{
vector pnt;
int mn;
// Get the point and its current sub-object
pnt = sm->verts[j];
mn = i;
// Instance up the tree
while (mn != -1)
{
vector tpnt;
vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b);
vm_TransposeMatrix(&m);
tpnt = pnt * m;
pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos;
mn = pm->submodel[mn].parent;
}
// Maybe use for other code -- Accounts for world coordinates
// m = obj->orient;
// vm_TransposeMatrix(&m);
//
// pnt = pnt * m;
//
// *gun_point += obj->pos;
// Find the min_xyz and max_xyz
if(n == start_frame && i == 0 && j == 0)
{
min_xyz = max_xyz = pnt;
}
else
{
if(pnt.x < min_xyz.x) min_xyz.x = pnt.x;
else if(pnt.x > max_xyz.x) max_xyz.x = pnt.x;
if(pnt.y < min_xyz.y) min_xyz.y = pnt.y;
else if(pnt.y > max_xyz.y) max_xyz.y = pnt.y;
if(pnt.z < min_xyz.z) min_xyz.z = pnt.z;
else if(pnt.z > max_xyz.z) max_xyz.z = pnt.z;
}
}
}
}
geometric_center = (max_xyz + min_xyz)/2.0;
*offset_ptr = geometric_center;
}
for(n = start_frame; n <= end_frame; n++)
{
// Because size changes with animation, we need the worst case point -- so, check every keyframe
// NOTE: This code does not currently account for all $turret and $rotate positions
SetNormalizedTimeAnim(n, normalized_time, pm);
SetModelAnglesAndPos (pm,normalized_time);
for (i = 0;i < pm->n_models; i++)
{
bsp_info *sm=&pm->submodel[i];
// For every vertex
for(j = 0; j < sm->nverts; j++)
{
vector pnt;
int mn;
// Get the point and its current sub-object
pnt = sm->verts[j];
mn = i;
// Instance up the tree
while (mn != -1)
{
vector tpnt;
vm_AnglesToMatrix(&m, pm->submodel[mn].angs.p,pm->submodel[mn].angs.h, pm->submodel[mn].angs.b);
vm_TransposeMatrix(&m);
tpnt = pnt * m;
pnt = tpnt + pm->submodel[mn].offset + pm->submodel[mn].mod_pos;
mn = pm->submodel[mn].parent;
}
// Maybe use for other code -- Accounts for world coordinates
// m = obj->orient;
// vm_TransposeMatrix(&m);
//
// pnt = pnt * m;
//
// *gun_point += obj->pos;
cur_dist = vm_VectorDistance(&geometric_center, &pnt);
if(cur_dist > size) size = cur_dist;
}
}
}
// This is a arbitary value. It allows for some turret and rotations to be caught
size = size + 0.01f;
if(size_ptr) //DAJ
*size_ptr = size;
return size;
}
float ComputeDefaultSize(int type, int handle, float *size_ptr)
{
float size = ComputeDefaultSizeFunc(handle, size_ptr, NULL, true);
if(type != OBJ_WEAPON && type != OBJ_DEBRIS && type != OBJ_POWERUP)
{
ComputeDefaultSizeFunc(handle, &Poly_models[handle].wall_size, &Poly_models[handle].wall_size_offset, false);
ComputeDefaultSizeFunc(handle, &Poly_models[handle].anim_size, &Poly_models[handle].anim_size_offset, true);
if (type == OBJ_PLAYER)
{
Poly_models[handle].anim_size *= PLAYER_SIZE_SCALAR;
Poly_models[handle].anim_size_offset = Zero_vector;
}
}
else
{
if(type == OBJ_POWERUP)
{
size *= 2.0f;
*size_ptr *= 2.0f;
}
Poly_models[handle].wall_size = size;
Poly_models[handle].wall_size_offset = Zero_vector;
Poly_models[handle].anim_size = size;
Poly_models[handle].anim_size_offset = Zero_vector;
}
Poly_models[handle].flags |= PMF_SIZE_COMPUTED;
return size;
}