mirror of
https://github.com/id-Software/DOOM-iOS.git
synced 2024-11-10 06:31:53 +00:00
1759 lines
54 KiB
C
1759 lines
54 KiB
C
/*
|
|
* iphoneRender.c
|
|
* doom
|
|
*
|
|
* Created by John Carmack on 4/29/09.
|
|
* Copyright 2009 id Software. All rights reserved.
|
|
*
|
|
*/
|
|
/*
|
|
|
|
Copyright (C) 2009 Id Software, Inc.
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "SDL_opengl.h"
|
|
#include "doomtype.h"
|
|
#include "w_wad.h"
|
|
#include "m_argv.h"
|
|
#include "d_event.h"
|
|
#include "v_video.h"
|
|
#include "doomstat.h"
|
|
#include "r_bsp.h"
|
|
#include "r_main.h"
|
|
#include "r_draw.h"
|
|
#include "r_sky.h"
|
|
#include "r_plane.h"
|
|
#include "r_data.h"
|
|
#include "r_things.h"
|
|
#include "r_fps.h"
|
|
#include "p_maputl.h"
|
|
#include "m_bbox.h"
|
|
#include "lprintf.h"
|
|
#include "gl_intern.h"
|
|
#include "gl_struct.h"
|
|
|
|
// If the Doom levels had been built with realistic visibility
|
|
// taken into account for the sky areas, we could just draw the
|
|
// sky first and then the walls, but that gives artifacts where
|
|
// you see some sectors floating in the sky now. This causes
|
|
// the walls to draw extended top and bottom sections for skies.
|
|
#define SKYWALLS
|
|
|
|
#define MINZ (FRACUNIT*4)
|
|
#define BASEYCENTER 100
|
|
#define MINZ_FLOAT 4
|
|
|
|
typedef struct {
|
|
GLTexture *tex;
|
|
side_t *side;
|
|
int flag; // GLDWF_TOP, GLDWF_M1S, etc
|
|
} sortLine_t;
|
|
|
|
#define MAX_SORT_LINES 4096
|
|
sortLine_t sortLines[MAX_SORT_LINES];
|
|
int numSortLines;
|
|
|
|
typedef struct {
|
|
GLTexture *texture;
|
|
sector_t *sector;
|
|
boolean ceiling;
|
|
} sortSectorPlane_t;
|
|
|
|
#define MAX_SECTOR_PLANES 1024
|
|
sortSectorPlane_t sectorPlanes[MAX_SECTOR_PLANES];
|
|
int numSectorPlanes;
|
|
|
|
typedef struct {
|
|
GLTexture *tex;
|
|
} sortSprite_t;
|
|
|
|
// Cleared to 0 at frame start.
|
|
// Individual columns will be set to 1 as occluding segments are processed.
|
|
// An occluding segment is either a one-sided line, a line that has a back
|
|
// sector with equal floor and ceiling heights, a line with a back ceiling
|
|
// height lower than the fron floor height, or a line with a back floor height
|
|
// higher than the front ceiling height.
|
|
// Entire nodes are culled when their bounds does not include a 0 column.
|
|
// Individual line segments are culled when their span does not include 0 columns.
|
|
// Sprites could be checked against it, but it may not be worth it.
|
|
char occlusion[MAX_SCREENWIDTH+2]; // +2 for guard columns to avoid clamping
|
|
|
|
// when the iphone is upside down, the occlusion segments are reversed
|
|
boolean reversedLandscape;
|
|
|
|
// this matrix is exactly what GL uses, but there will still
|
|
// be floating point differences between the GPU and CPU
|
|
float glMVPmatrix[16];
|
|
|
|
// if any sector textures are the sky texture, we will draw the sky and
|
|
// ignore those sector geometries
|
|
boolean skyIsVisible;
|
|
|
|
// these should really be initialized based on viewwidth somewhere...
|
|
float halfWidthFloat = 240.0f;
|
|
|
|
// used during debugging to isolate incorrect culling
|
|
int failCount;
|
|
|
|
// Some of the two sided line segments in the original game don't have a valid
|
|
// texture, so stick something there instead of leaving a gaping hole in the world.
|
|
GLTexture *defaultTexture;
|
|
|
|
// just for the sky texture setup
|
|
float yaw;
|
|
|
|
// counters
|
|
int c_occludedSprites;
|
|
int c_sectors;
|
|
int c_subsectors;
|
|
|
|
// test options
|
|
int testClear = 0;
|
|
int testNewRenderer = 1;
|
|
int showRenderTime;
|
|
int blendAll;
|
|
|
|
|
|
void BuildIndexedTriangles();
|
|
void BuildSideSegs();
|
|
|
|
void IR_MergeSectors( int fromSector, int intoSector ) {
|
|
// E3M8 (and possibly others somewhere) has a bad sector
|
|
// classification with two stray lines in sector 2 that
|
|
// should be a part of sector 1. This makes both of the
|
|
// sectors "broken" and unable to be properly tesselated.
|
|
assert( (unsigned)fromSector < numsectors );
|
|
assert( (unsigned)intoSector < numsectors );
|
|
sector_t *fromSectorPtr = §ors[fromSector];
|
|
sector_t *intoSectorPtr = §ors[intoSector];
|
|
|
|
int moveLines = 0;
|
|
for ( int i = 0 ; i < numlines ; i++ ) {
|
|
if ( lines[i].frontsector == fromSectorPtr ) {
|
|
moveLines++;
|
|
} else if ( lines[i].backsector == fromSectorPtr ) {
|
|
moveLines++;
|
|
}
|
|
}
|
|
|
|
// add these lines to intoSector
|
|
// Unfortunately, the sector->lines list is not allocated per-sector, but
|
|
// is a single block for the entire level, so we can't realloc it. I'm
|
|
// going to just let the new table leak.
|
|
line_t **newLines = Z_Malloc( ( intoSectorPtr->linecount + moveLines ) * sizeof( *intoSectorPtr->lines ),
|
|
PU_LEVEL,0);
|
|
memcpy( newLines, intoSectorPtr->lines, intoSectorPtr->linecount * sizeof( *newLines ) );
|
|
intoSectorPtr->lines = newLines;
|
|
for ( int i = 0 ; i < numlines ; i++ ) {
|
|
if ( lines[i].frontsector == fromSectorPtr ) {
|
|
intoSectorPtr->lines[intoSectorPtr->linecount++] = &lines[i];
|
|
lines[i].frontsector = intoSectorPtr;
|
|
} else if ( lines[i].backsector == fromSectorPtr ) {
|
|
intoSectorPtr->lines[intoSectorPtr->linecount++] = &lines[i];
|
|
lines[i].backsector = intoSectorPtr;
|
|
}
|
|
}
|
|
|
|
// change all the segs
|
|
for ( int i = 0 ; i < numsegs ; i++ ) {
|
|
if ( segs[i].frontsector == fromSectorPtr ) {
|
|
segs[i].frontsector = intoSectorPtr;
|
|
}
|
|
if ( segs[i].backsector == fromSectorPtr ) {
|
|
segs[i].backsector = intoSectorPtr;
|
|
}
|
|
}
|
|
|
|
// change all the sides to point to the new one
|
|
for ( int i = 0 ; i < numsides ; i++ ) {
|
|
if ( sides[i].sector == fromSectorPtr ) {
|
|
sides[i].sector = intoSectorPtr;
|
|
}
|
|
}
|
|
|
|
// change all the subsectors to point to the new one
|
|
for ( int i = 0 ; i < numsubsectors ; i++ ) {
|
|
if ( subsectors[i].sector == fromSectorPtr ) {
|
|
subsectors[i].sector = intoSectorPtr;
|
|
}
|
|
}
|
|
|
|
|
|
// make fromSector vestigial so it doesn't get tesselated
|
|
fromSectorPtr->linecount = 0;
|
|
}
|
|
|
|
void IR_InitLevel() {
|
|
BuildIndexedTriangles(); // convert the loops into indexed triangles
|
|
BuildSideSegs(); // create a seg_t for each side_t so we can draw the
|
|
// unclipped versions that fit perfectly with the sectors
|
|
|
|
// find something else used in the level for a default texture
|
|
for ( int i = 0 ; i < numsides ; i++ ) {
|
|
if ( sides[i].toptexture ) {
|
|
defaultTexture=gld_RegisterTexture(sides[i].toptexture, true, false);
|
|
if ( defaultTexture ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
assert( defaultTexture );
|
|
}
|
|
|
|
|
|
float lightDistance = 10.0f; // in prBoom MAP_SCALE units, increasing this makes things get dimmer faster
|
|
#define MAX_LIGHT_DROP 96
|
|
float lightingVector[3]; // transform and scale [ x y 1 ] to get color units to subtract
|
|
static int FadedLighting( float x, float y, int sectorLightLevel ) {
|
|
// Ramp down the lightover lightDistance world units.
|
|
// Triangles that extend across behind the view origin and past
|
|
// the lightDistance clamping boundary will not have completely linear fading,
|
|
// but nobody should notice.
|
|
|
|
// A proportional drop in lighting sounds like a better idea, but
|
|
// this linear drop seems to look nicer. It's not like Doom's
|
|
// lighting is realistic in any case...
|
|
|
|
int idist = x * lightingVector[0] + y * lightingVector[1] + lightingVector[2];
|
|
if ( idist < 0 ) {
|
|
idist = 0;
|
|
} else if ( idist > MAX_LIGHT_DROP ) {
|
|
idist = MAX_LIGHT_DROP;
|
|
}
|
|
sectorLightLevel -= idist;
|
|
if ( sectorLightLevel < 0 ) {
|
|
sectorLightLevel = 0;
|
|
}
|
|
if ( sectorLightLevel > 255 ) {
|
|
sectorLightLevel = 255;
|
|
}
|
|
return sectorLightLevel | (sectorLightLevel<<8) | (sectorLightLevel<<16) | (255<<24);
|
|
}
|
|
|
|
|
|
//
|
|
// IR_ProjectSprite
|
|
// Generates a vissprite for a thing if it might be visible.
|
|
//
|
|
|
|
static void IR_ProjectSprite (mobj_t* thing, int lightlevel)
|
|
{
|
|
fixed_t gzt; // killough 3/27/98
|
|
fixed_t tx;
|
|
fixed_t xscale;
|
|
int x1;
|
|
int x2;
|
|
spritedef_t *sprdef;
|
|
spriteframe_t *sprframe;
|
|
int lump;
|
|
boolean flip;
|
|
|
|
// transform the origin point
|
|
fixed_t tr_x, tr_y;
|
|
fixed_t fx, fy, fz;
|
|
fixed_t gxt, gyt;
|
|
fixed_t tz;
|
|
int width;
|
|
|
|
fx = thing->x;
|
|
fy = thing->y;
|
|
fz = thing->z;
|
|
|
|
tr_x = fx - viewx;
|
|
tr_y = fy - viewy;
|
|
|
|
gxt = FixedMul(tr_x,viewcos);
|
|
gyt = -FixedMul(tr_y,viewsin);
|
|
|
|
tz = gxt-gyt;
|
|
|
|
// thing is behind view plane?
|
|
if (tz < MINZ)
|
|
return;
|
|
|
|
xscale = FixedDiv(projection, tz);
|
|
|
|
gxt = -FixedMul(tr_x,viewsin);
|
|
gyt = FixedMul(tr_y,viewcos);
|
|
tx = -(gyt+gxt);
|
|
|
|
// too far off the side?
|
|
if (D_abs(tx)>(tz<<2))
|
|
return;
|
|
|
|
// decide which patch to use for sprite relative to player
|
|
#ifdef RANGECHECK
|
|
if ((unsigned) thing->sprite >= (unsigned)numsprites)
|
|
I_Error ("R_ProjectSprite: Invalid sprite number %i", thing->sprite);
|
|
#endif
|
|
|
|
sprdef = &sprites[thing->sprite];
|
|
|
|
#ifdef RANGECHECK
|
|
if ((thing->frame&FF_FRAMEMASK) >= sprdef->numframes)
|
|
I_Error ("R_ProjectSprite: Invalid sprite frame %i : %i", thing->sprite,
|
|
thing->frame);
|
|
#endif
|
|
|
|
if (!sprdef->spriteframes)
|
|
I_Error ("R_ProjectSprite: Missing spriteframes %i : %i", thing->sprite,
|
|
thing->frame);
|
|
|
|
sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK];
|
|
|
|
if (sprframe->rotate)
|
|
{
|
|
// choose a different rotation based on player view
|
|
// JDC: this could be better...
|
|
angle_t ang = R_PointToAngle(fx, fy);
|
|
unsigned rot = (ang-thing->angle+(unsigned)(ANG45/2)*9)>>29;
|
|
lump = sprframe->lump[rot];
|
|
flip = (boolean) sprframe->flip[rot];
|
|
}
|
|
else
|
|
{
|
|
// use single rotation for all views
|
|
lump = sprframe->lump[0];
|
|
flip = (boolean) sprframe->flip[0];
|
|
}
|
|
|
|
{
|
|
const rpatch_t* patch = R_CachePatchNum(lump+firstspritelump);
|
|
|
|
/* calculate edges of the shape
|
|
* cph 2003/08/1 - fraggle points out that this offset must be flipped
|
|
* if the sprite is flipped; e.g. FreeDoom imp is messed up by this. */
|
|
if (flip) {
|
|
tx -= (patch->width - patch->leftoffset) << FRACBITS;
|
|
} else {
|
|
tx -= patch->leftoffset << FRACBITS;
|
|
}
|
|
x1 = (centerxfrac + FixedMul(tx,xscale)) >> FRACBITS;
|
|
|
|
tx += patch->width<<FRACBITS;
|
|
x2 = ((centerxfrac + FixedMul (tx,xscale) ) >> FRACBITS) - 1;
|
|
|
|
gzt = fz + (patch->topoffset << FRACBITS);
|
|
width = patch->width;
|
|
|
|
// JDC: we don't care if they never get freed,
|
|
// so don't bother changing the zone tag status each time
|
|
//R_UnlockPatchNum(lump+firstspritelump);
|
|
}
|
|
|
|
// off the side?
|
|
if (x1 > viewwidth || x2 < 0)
|
|
return;
|
|
|
|
// killough 4/9/98: clip things which are out of view due to height
|
|
// e6y: fix of hanging decoration disappearing in Batman Doom MAP02
|
|
// centeryfrac -> viewheightfrac
|
|
if (fz > viewz + FixedDiv(viewheightfrac, xscale) ||
|
|
gzt < viewz - FixedDiv(viewheightfrac-viewheight, xscale))
|
|
return;
|
|
|
|
// JDC: clip to the occlusio buffer
|
|
int testLow = x1 < 0 ? 0 : x1;
|
|
int testHigh = x2 >= viewwidth ? viewwidth - 1 : x2;
|
|
if ( reversedLandscape ) {
|
|
testLow = viewwidth-1-testLow;
|
|
testHigh = viewwidth-1-testHigh;
|
|
}
|
|
if ( !memchr( occlusion+testLow, 0, testHigh - testLow ) ) {
|
|
c_occludedSprites++;
|
|
return;
|
|
}
|
|
|
|
// ------------ gld_AddSprite ----------
|
|
mobj_t *pSpr= thing;
|
|
GLSprite sprite;
|
|
float voff,hoff;
|
|
|
|
sprite.scale= FixedDiv(projectiony, tz);
|
|
if (pSpr->frame & FF_FULLBRIGHT)
|
|
sprite.light = 255;
|
|
else
|
|
sprite.light = pSpr->subsector->sector->lightlevel+(extralight<<5);
|
|
sprite.cm=CR_LIMIT+(int)((pSpr->flags & MF_TRANSLATION) >> (MF_TRANSSHIFT));
|
|
sprite.gltexture=gld_RegisterPatch(lump+firstspritelump,sprite.cm);
|
|
if (!sprite.gltexture)
|
|
return;
|
|
sprite.shadow = (pSpr->flags & MF_SHADOW) != 0;
|
|
sprite.trans = (pSpr->flags & MF_TRANSLUCENT) != 0;
|
|
if (movement_smooth)
|
|
{
|
|
sprite.x = (float)(-pSpr->PrevX + FixedMul (tic_vars.frac, -pSpr->x - (-pSpr->PrevX)))/MAP_SCALE;
|
|
sprite.y = (float)(pSpr->PrevZ + FixedMul (tic_vars.frac, pSpr->z - pSpr->PrevZ))/MAP_SCALE;
|
|
sprite.z = (float)(pSpr->PrevY + FixedMul (tic_vars.frac, pSpr->y - pSpr->PrevY))/MAP_SCALE;
|
|
}
|
|
else
|
|
{
|
|
sprite.x=-(float)pSpr->x/MAP_SCALE;
|
|
sprite.y= (float)pSpr->z/MAP_SCALE;
|
|
sprite.z= (float)pSpr->y/MAP_SCALE;
|
|
}
|
|
|
|
sprite.vt=0.0f;
|
|
sprite.vb=(float)sprite.gltexture->height/(float)sprite.gltexture->tex_height;
|
|
if (flip)
|
|
{
|
|
sprite.ul=0.0f;
|
|
sprite.ur=(float)sprite.gltexture->width/(float)sprite.gltexture->tex_width;
|
|
}
|
|
else
|
|
{
|
|
sprite.ul=(float)sprite.gltexture->width/(float)sprite.gltexture->tex_width;
|
|
sprite.ur=0.0f;
|
|
}
|
|
hoff=(float)sprite.gltexture->leftoffset/(float)(MAP_COEFF);
|
|
voff=(float)sprite.gltexture->topoffset/(float)(MAP_COEFF);
|
|
sprite.x1=hoff-((float)sprite.gltexture->realtexwidth/(float)(MAP_COEFF));
|
|
sprite.x2=hoff;
|
|
sprite.y1=voff;
|
|
sprite.y2=voff-((float)sprite.gltexture->realtexheight/(float)(MAP_COEFF));
|
|
|
|
// JDC: don't let sprites poke below the ground level.
|
|
// Software rendering Doom didn't use depth buffering,
|
|
// so sprites always got drawn on top of the flat they
|
|
// were on, but in GL they tend to get a couple pixel
|
|
// rows clipped off.
|
|
if ( sprite.y2 < 0 ) {
|
|
sprite.y1 -= sprite.y2;
|
|
sprite.y2 = 0;
|
|
}
|
|
|
|
if (gld_drawinfo.num_sprites>=gld_drawinfo.max_sprites)
|
|
{
|
|
gld_drawinfo.max_sprites+=128;
|
|
gld_drawinfo.sprites=Z_Realloc(gld_drawinfo.sprites,gld_drawinfo.max_sprites*sizeof(GLSprite),PU_LEVEL,0);
|
|
}
|
|
gld_drawinfo.sprites[gld_drawinfo.num_sprites++]=sprite;
|
|
}
|
|
|
|
// JDC: removed the 0.001f epsilons that were presumably added
|
|
// to try to hide T-junction cracks, but now that we are drawing
|
|
// source lines instead of clipped segs, it is a non-problem.
|
|
#define LINE seg->linedef
|
|
#define CALC_Y_VALUES(w, lineheight, floor_height, ceiling_height)\
|
|
(w).ytop=((float)(ceiling_height)/(float)MAP_SCALE);\
|
|
(w).ybottom=((float)(floor_height)/(float)MAP_SCALE);\
|
|
lineheight=((float)fabs(((ceiling_height)/(float)FRACUNIT)-((floor_height)/(float)FRACUNIT)))
|
|
|
|
#define OU(w,seg) (((float)((seg)->sidedef->textureoffset+(seg)->offset)/(float)FRACUNIT)/(float)(w).gltexture->buffer_width)
|
|
#define OV(w,seg) (((float)((seg)->sidedef->rowoffset)/(float)FRACUNIT)/(float)(w).gltexture->buffer_height)
|
|
#define OV_PEG(w,seg,v_offset) (OV((w),(seg))-(((float)(v_offset)/(float)FRACUNIT)/(float)(w).gltexture->buffer_height))
|
|
|
|
#define CALC_TEX_VALUES_TOP(w, seg, peg, linelength, lineheight)\
|
|
(w).flag=GLDWF_TOP;\
|
|
(w).ul=OU((w),(seg))+(0.0f);\
|
|
(w).ur=OU((w),(seg))+((linelength)/(float)(w).gltexture->buffer_width);\
|
|
(peg)?\
|
|
(\
|
|
(w).vb=OV((w),(seg))+((float)(w).gltexture->height/(float)(w).gltexture->tex_height),\
|
|
(w).vt=((w).vb-((float)(lineheight)/(float)(w).gltexture->buffer_height))\
|
|
):(\
|
|
(w).vt=OV((w),(seg))+(0.0f),\
|
|
(w).vb=OV((w),(seg))+((float)(lineheight)/(float)(w).gltexture->buffer_height)\
|
|
)
|
|
|
|
#define CALC_TEX_VALUES_MIDDLE1S(w, seg, peg, linelength, lineheight)\
|
|
(w).flag=GLDWF_M1S;\
|
|
(w).ul=OU((w),(seg))+(0.0f);\
|
|
(w).ur=OU((w),(seg))+((linelength)/(float)(w).gltexture->buffer_width);\
|
|
(peg)?\
|
|
(\
|
|
(w).vb=OV((w),(seg))+((float)(w).gltexture->height/(float)(w).gltexture->tex_height),\
|
|
(w).vt=((w).vb-((float)(lineheight)/(float)(w).gltexture->buffer_height))\
|
|
):(\
|
|
(w).vt=OV((w),(seg))+(0.0f),\
|
|
(w).vb=OV((w),(seg))+((float)(lineheight)/(float)(w).gltexture->buffer_height)\
|
|
)
|
|
|
|
#define CALC_TEX_VALUES_MIDDLE2S(w, seg, peg, linelength, lineheight)\
|
|
(w).flag=GLDWF_M2S;\
|
|
(w).ul=OU((w),(seg))+(0.0f);\
|
|
(w).ur=OU((w),(seg))+((linelength)/(float)(w).gltexture->buffer_width);\
|
|
(peg)?\
|
|
(\
|
|
(w).vb=((float)(w).gltexture->height/(float)(w).gltexture->tex_height),\
|
|
(w).vt=((w).vb-((float)(lineheight)/(float)(w).gltexture->buffer_height))\
|
|
):(\
|
|
(w).vt=(0.0f),\
|
|
(w).vb=((float)(lineheight)/(float)(w).gltexture->buffer_height)\
|
|
)
|
|
|
|
#define CALC_TEX_VALUES_BOTTOM(w, seg, peg, linelength, lineheight, v_offset)\
|
|
(w).flag=GLDWF_BOT;\
|
|
(w).ul=OU((w),(seg))+(0.0f);\
|
|
(w).ur=OU((w),(seg))+((linelength)/(float)(w).gltexture->realtexwidth);\
|
|
(peg)?\
|
|
(\
|
|
(w).vb=OV_PEG((w),(seg),(v_offset))+((float)(w).gltexture->height/(float)(w).gltexture->tex_height),\
|
|
(w).vt=((w).vb-((float)(lineheight)/(float)(w).gltexture->buffer_height))\
|
|
):(\
|
|
(w).vt=OV((w),(seg))+(0.0f),\
|
|
(w).vb=OV((w),(seg))+((float)(lineheight)/(float)(w).gltexture->buffer_height)\
|
|
)
|
|
|
|
// e6y
|
|
// Sky textures with a zero index should be forced
|
|
// See third episode of requiem.wad
|
|
#define SKYTEXTURE_PRBOOM(sky1,sky2)\
|
|
if ((sky1) & PL_SKYFLAT)\
|
|
{\
|
|
const line_t *l = &lines[sky1 & ~PL_SKYFLAT];\
|
|
const side_t *s = *l->sidenum + sides;\
|
|
wall.gltexture=gld_RegisterTexture(texturetranslation[s->toptexture], false, texturetranslation[s->toptexture]==skytexture);\
|
|
wall.skyyaw=-2.0f*((-(float)((viewangle+s->textureoffset)>>ANGLETOFINESHIFT)*360.0f/FINEANGLES)/90.0f);\
|
|
wall.skyymid = 200.0f/319.5f*(((float)s->rowoffset/(float)FRACUNIT - 28.0f)/100.0f);\
|
|
wall.flag = l->special==272 ? GLDWF_SKY : GLDWF_SKYFLIP;\
|
|
}\
|
|
else\
|
|
if ((sky2) & PL_SKYFLAT)\
|
|
{\
|
|
const line_t *l = &lines[sky2 & ~PL_SKYFLAT];\
|
|
const side_t *s = *l->sidenum + sides;\
|
|
wall.gltexture=gld_RegisterTexture(texturetranslation[s->toptexture], false, texturetranslation[s->toptexture]==skytexture);\
|
|
wall.skyyaw=-2.0f*((-(float)((viewangle+s->textureoffset)>>ANGLETOFINESHIFT)*360.0f/FINEANGLES)/90.0f);\
|
|
wall.skyymid = 200.0f/319.5f*(((float)s->rowoffset/(float)FRACUNIT - 28.0f)/100.0f);\
|
|
wall.flag = l->special==272 ? GLDWF_SKY : GLDWF_SKYFLIP;\
|
|
}\
|
|
else\
|
|
{\
|
|
wall.gltexture=gld_RegisterTexture(skytexture, false, true);\
|
|
wall.skyyaw=-2.0f*((yaw+90.0f)/90.0f);\
|
|
wall.skyymid = 200.0f/319.5f*((100.0f)/100.0f);\
|
|
wall.flag = GLDWF_SKY;\
|
|
};
|
|
|
|
#define SKYTEXTURE(sky1,sky2)\
|
|
wall.gltexture=NULL;\
|
|
wall.flag = GLDWF_SKY;
|
|
|
|
#define ADDWALL(wall)\
|
|
{\
|
|
if (gld_drawinfo.num_walls>=gld_drawinfo.max_walls)\
|
|
{\
|
|
gld_drawinfo.max_walls+=128;\
|
|
gld_drawinfo.walls=Z_Realloc(gld_drawinfo.walls,gld_drawinfo.max_walls*sizeof(GLWall),PU_LEVEL,0);\
|
|
}\
|
|
gld_drawinfo.walls[gld_drawinfo.num_walls++]=*wall;\
|
|
};
|
|
|
|
extern GLSeg *gl_segs;
|
|
extern byte rendermarker;
|
|
extern byte *segrendered;
|
|
|
|
void IR_AddWall(seg_t *seg)
|
|
{
|
|
GLWall wall;
|
|
GLTexture *temptex;
|
|
sector_t *frontsector;
|
|
sector_t *backsector;
|
|
float lineheight;
|
|
int rellight = 0;
|
|
|
|
wall.glseg=NULL;
|
|
wall.side = seg->sidedef;
|
|
frontsector = seg->frontsector;
|
|
|
|
// JDC: improve this lighting tweak
|
|
rellight = seg->linedef->dx==0? +8 : seg->linedef->dy==0 ? -8 : 0;
|
|
int light = frontsector->lightlevel+rellight+(extralight<<5);
|
|
wall.light = MAX(MIN((light),255),0);
|
|
wall.alpha=1.0f;
|
|
wall.gltexture=NULL;
|
|
|
|
if (!seg->backsector) /* onesided */
|
|
{
|
|
#ifdef SKYWALLS
|
|
if (frontsector->ceilingpic==skyflatnum)
|
|
{
|
|
wall.ytop=255.0f;
|
|
wall.ybottom=(float)frontsector->ceilingheight/MAP_SCALE;
|
|
SKYTEXTURE(frontsector->sky,frontsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
if (frontsector->floorpic==skyflatnum)
|
|
{
|
|
wall.ytop=(float)frontsector->floorheight/MAP_SCALE;
|
|
wall.ybottom=-255.0f;
|
|
SKYTEXTURE(frontsector->sky,frontsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
#endif
|
|
temptex=gld_RegisterTexture(texturetranslation[seg->sidedef->midtexture], true, false);
|
|
if (temptex)
|
|
{
|
|
wall.gltexture=temptex;
|
|
CALC_Y_VALUES(wall, lineheight, frontsector->floorheight, frontsector->ceilingheight);
|
|
CALC_TEX_VALUES_MIDDLE1S(
|
|
wall, seg, (LINE->flags & ML_DONTPEGBOTTOM)>0,
|
|
seg->length, lineheight
|
|
);
|
|
ADDWALL(&wall);
|
|
}
|
|
}
|
|
else /* twosided */
|
|
{
|
|
int floor_height,ceiling_height;
|
|
|
|
backsector=seg->backsector;
|
|
/* toptexture */
|
|
ceiling_height=frontsector->ceilingheight;
|
|
floor_height=backsector->ceilingheight;
|
|
#ifdef SKYWALLS
|
|
if (frontsector->ceilingpic==skyflatnum)
|
|
{
|
|
wall.ytop=255.0f;
|
|
if (
|
|
// e6y
|
|
// Fix for HOM in the starting area on Memento Mori map29 and on map30.
|
|
// old code: (backsector->ceilingheight==backsector->floorheight) &&
|
|
(backsector->ceilingheight==backsector->floorheight||(backsector->ceilingheight<=frontsector->floorheight)) &&
|
|
(backsector->ceilingpic==skyflatnum)
|
|
)
|
|
{
|
|
wall.ybottom=(float)backsector->floorheight/MAP_SCALE;
|
|
SKYTEXTURE(frontsector->sky,backsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
else
|
|
{
|
|
if ( (texturetranslation[seg->sidedef->toptexture]!=NO_TEXTURE) )
|
|
{
|
|
// e6y
|
|
// It corrects some problem with sky, but I do not remember which one
|
|
// old code: wall.ybottom=(float)frontsector->ceilingheight/MAP_SCALE;
|
|
wall.ybottom=(float)MAX(frontsector->ceilingheight,backsector->ceilingheight)/MAP_SCALE;
|
|
|
|
SKYTEXTURE(frontsector->sky,backsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
else
|
|
if ( (backsector->ceilingheight <= frontsector->floorheight) ||
|
|
(backsector->ceilingpic != skyflatnum) )
|
|
{
|
|
wall.ybottom=(float)backsector->ceilingheight/MAP_SCALE;
|
|
SKYTEXTURE(frontsector->sky,backsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (floor_height<ceiling_height)
|
|
{
|
|
if (!((frontsector->ceilingpic==skyflatnum) && (backsector->ceilingpic==skyflatnum)))
|
|
{
|
|
temptex=gld_RegisterTexture(texturetranslation[seg->sidedef->toptexture], true, false);
|
|
if ( !temptex ) {
|
|
temptex = defaultTexture; // it seems some line segments have bad data...
|
|
}
|
|
wall.gltexture=temptex;
|
|
CALC_Y_VALUES(wall, lineheight, floor_height, ceiling_height);
|
|
CALC_TEX_VALUES_TOP(
|
|
wall, seg, (LINE->flags & (ML_DONTPEGBOTTOM | ML_DONTPEGTOP))==0,
|
|
seg->length, lineheight
|
|
);
|
|
ADDWALL(&wall);
|
|
}
|
|
}
|
|
|
|
/* midtexture */
|
|
//e6y
|
|
if (comp[comp_maskedanim])
|
|
temptex=gld_RegisterTexture(seg->sidedef->midtexture, true, false);
|
|
else
|
|
// e6y
|
|
// Animated middle textures with a zero index should be forced
|
|
// See spacelab.wad (http://www.doomworld.com/idgames/index.php?id=6826)
|
|
temptex=gld_RegisterTexture(texturetranslation[seg->sidedef->midtexture], true, true);
|
|
|
|
if (temptex && seg->sidedef->midtexture != NO_TEXTURE)
|
|
{
|
|
wall.gltexture=temptex;
|
|
if ( (LINE->flags & ML_DONTPEGBOTTOM) >0)
|
|
{
|
|
if (seg->backsector->ceilingheight<=seg->frontsector->floorheight)
|
|
goto bottomtexture;
|
|
floor_height=MAX(seg->frontsector->floorheight,seg->backsector->floorheight)+(seg->sidedef->rowoffset);
|
|
ceiling_height=floor_height+(wall.gltexture->realtexheight<<FRACBITS);
|
|
}
|
|
else
|
|
{
|
|
if (seg->backsector->ceilingheight<=seg->frontsector->floorheight)
|
|
goto bottomtexture;
|
|
ceiling_height=MIN(seg->frontsector->ceilingheight,seg->backsector->ceilingheight)+(seg->sidedef->rowoffset);
|
|
floor_height=ceiling_height-(wall.gltexture->realtexheight<<FRACBITS);
|
|
}
|
|
// e6y
|
|
// The fix for wrong middle texture drawing
|
|
// if it exceeds the boundaries of its floor and ceiling
|
|
|
|
/*CALC_Y_VALUES(wall, lineheight, floor_height, ceiling_height);
|
|
CALC_TEX_VALUES_MIDDLE2S(
|
|
wall, seg, (LINE->flags & ML_DONTPEGBOTTOM)>0,
|
|
segs[seg->iSegID].length, lineheight
|
|
);*/
|
|
{
|
|
int floormax, ceilingmin, linelen;
|
|
float mip;
|
|
mip = (float)wall.gltexture->realtexheight/(float)wall.gltexture->buffer_height;
|
|
// if ( (texturetranslation[seg->sidedef->bottomtexture]!=R_TextureNumForName("-")) )
|
|
if (seg->sidedef->bottomtexture)
|
|
floormax=MAX(seg->frontsector->floorheight,seg->backsector->floorheight);
|
|
else
|
|
floormax=floor_height;
|
|
if (seg->sidedef->toptexture)
|
|
ceilingmin=MIN(seg->frontsector->ceilingheight,seg->backsector->ceilingheight);
|
|
else
|
|
ceilingmin=ceiling_height;
|
|
linelen=abs(ceiling_height-floor_height);
|
|
wall.ytop=((float)MIN(ceilingmin, ceiling_height)/(float)MAP_SCALE);
|
|
wall.ybottom=((float)MAX(floormax, floor_height)/(float)MAP_SCALE);
|
|
wall.flag=GLDWF_M2S;
|
|
wall.ul=OU((wall),(seg))+(0.0f);
|
|
wall.ur=OU(wall,(seg))+((seg->length)/(float)wall.gltexture->buffer_width);
|
|
if (floormax<=floor_height)
|
|
wall.vb=mip*1.0f;
|
|
else
|
|
wall.vb=mip*((float)(ceiling_height - floormax))/linelen;
|
|
if (ceilingmin>=ceiling_height)
|
|
wall.vt=0.0f;
|
|
else
|
|
wall.vt=mip*((float)(ceiling_height - ceilingmin))/linelen;
|
|
}
|
|
|
|
wall.alpha=1.0f;
|
|
ADDWALL(&wall);
|
|
}
|
|
bottomtexture:
|
|
/* bottomtexture */
|
|
ceiling_height=backsector->floorheight;
|
|
floor_height=frontsector->floorheight;
|
|
#ifdef SKYWALLS
|
|
if (frontsector->floorpic==skyflatnum)
|
|
{
|
|
wall.ybottom=-255.0f;
|
|
if (
|
|
(backsector->ceilingheight==backsector->floorheight) &&
|
|
(backsector->floorpic==skyflatnum)
|
|
)
|
|
{
|
|
wall.ytop=(float)backsector->floorheight/MAP_SCALE;
|
|
SKYTEXTURE(frontsector->sky,backsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
else
|
|
{
|
|
if ( (texturetranslation[seg->sidedef->bottomtexture]!=NO_TEXTURE) )
|
|
{
|
|
wall.ytop=(float)frontsector->floorheight/MAP_SCALE;
|
|
SKYTEXTURE(frontsector->sky,backsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
else
|
|
if ( (backsector->floorheight >= frontsector->ceilingheight) ||
|
|
(backsector->floorpic != skyflatnum) )
|
|
{
|
|
wall.ytop=(float)backsector->floorheight/MAP_SCALE;
|
|
SKYTEXTURE(frontsector->sky,backsector->sky);
|
|
ADDWALL(&wall);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (floor_height<ceiling_height)
|
|
{
|
|
temptex=gld_RegisterTexture(texturetranslation[seg->sidedef->bottomtexture], true, false);
|
|
if ( !temptex ) {
|
|
temptex = defaultTexture; // it seems some line segments have bad data...
|
|
}
|
|
wall.gltexture=temptex;
|
|
CALC_Y_VALUES(wall, lineheight, floor_height, ceiling_height);
|
|
CALC_TEX_VALUES_BOTTOM(
|
|
wall, seg, (LINE->flags & ML_DONTPEGBOTTOM)>0,
|
|
seg->length, lineheight,
|
|
floor_height-frontsector->ceilingheight
|
|
);
|
|
ADDWALL(&wall);
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef LINE
|
|
#undef CALC_Y_VALUES
|
|
#undef OU
|
|
#undef OV
|
|
#undef OV_PEG
|
|
#undef CALC_TEX_VALUES_TOP
|
|
#undef CALC_TEX_VALUES_MIDDLE1S
|
|
#undef CALC_TEX_VALUES_MIDDLE2S
|
|
#undef CALC_TEX_VALUES_BOTTOM
|
|
#undef SKYTEXTURE
|
|
#undef ADDWALL
|
|
|
|
|
|
|
|
/*
|
|
TransformAndClipSegment
|
|
|
|
Converts a world coordinate line segment to screen space.
|
|
Returns false if the segment is off screen.
|
|
|
|
There would be some savings if all the points in a subsector
|
|
were transformed and clip tested as a unit, instead of as discrete segments.
|
|
*/
|
|
boolean TransformAndClipSegment( float v[2][2], float ends[2] ) {
|
|
float clip[2][4];
|
|
|
|
// if we are in iphone reverse-landscape mode, we need
|
|
// to flip the coordinates around
|
|
float *v0, *v1;
|
|
if ( reversedLandscape ) {
|
|
v0 = v[1];
|
|
v1 = v[0];
|
|
} else {
|
|
v0 = v[0];
|
|
v1 = v[1];
|
|
}
|
|
|
|
// transform from model to clip space
|
|
// because the iPhone screen hardware is portrait mode,
|
|
// we need to look at the Y axis for the segment ends,
|
|
// not the X axis.
|
|
clip[0][1] = v0[0] * glMVPmatrix[1] + v0[1] * glMVPmatrix[2*4+1] + glMVPmatrix[3*4+1];
|
|
clip[0][3] = v0[0] * glMVPmatrix[3] + v0[1] * glMVPmatrix[2*4+3] + glMVPmatrix[3*4+3];
|
|
|
|
clip[1][1] = v1[0] * glMVPmatrix[1] + v1[1] * glMVPmatrix[2*4+1] + glMVPmatrix[3*4+1];
|
|
clip[1][3] = v1[0] * glMVPmatrix[3] + v1[1] * glMVPmatrix[2*4+3] + glMVPmatrix[3*4+3];
|
|
|
|
float d0, d1;
|
|
|
|
// clip to the near plane
|
|
float nearClip = 0.01f;
|
|
d0 = clip[0][3] - nearClip;
|
|
d1 = clip[1][3] - nearClip;
|
|
if ( d0 < 0 && d1 < 0 ) {
|
|
// near clipped
|
|
return false;
|
|
}
|
|
if ( d0 < 0 ) {
|
|
float f = d0 / ( d0 - d1 );
|
|
clip[0][1] = clip[0][1] + f * ( clip[1][1] - clip[0][1] );
|
|
clip[0][3] = nearClip;
|
|
} else if ( d1 < 0 ) {
|
|
float f = d1 / ( d1 - d0 );
|
|
clip[1][1] = clip[1][1] + f * ( clip[0][1] - clip[1][1] );
|
|
clip[1][3] = nearClip;
|
|
}
|
|
|
|
if ( clip[0][1] > clip[0][3] ) {
|
|
// entire segment is off the right side of the screen
|
|
return false;
|
|
}
|
|
if ( clip[1][1] < -clip[1][3] ) {
|
|
// entire segment is off the left side of the screen
|
|
return false;
|
|
}
|
|
|
|
// project
|
|
for ( int i = 0 ; i < 2 ; i++ ) {
|
|
float x = viewwidth * ( ( clip[i][1] / clip[i][3] ) * 0.5 + 0.5 );
|
|
if ( x < 0 ) {
|
|
x = 0;
|
|
} else if ( x > viewwidth ) {
|
|
x = viewwidth;
|
|
}
|
|
ends[i] = x;
|
|
}
|
|
|
|
// part of the segment is on screen
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
IR_Subsector
|
|
|
|
All possible culling should be performed here, but most calculations should be
|
|
deferred until draw time, rather than storing intermediate values that are
|
|
later referenced.
|
|
|
|
Don't make this static, or the compiler inlines it in the recursive node
|
|
function, which bloats the stack.
|
|
*/
|
|
void IR_Subsector(int num)
|
|
{
|
|
subsector_t *sub = &subsectors[num];
|
|
c_subsectors++;
|
|
// at this point we know that at least part of the subsector is
|
|
// not covered in the occlusion array
|
|
|
|
// if the sector that this subsector is a part of has not already had its
|
|
// planes and sprites added, add them now.
|
|
sector_t *frontsector = sub->sector;
|
|
int lightlevel = frontsector->lightlevel+(extralight<<5);
|
|
|
|
// There can be several subsectors in each sector due to non-convex
|
|
// sectors or BSP splits, but we draw the floors, ceilings and lines
|
|
// with a single draw call for the entire thing, so ensure that they
|
|
// are only added once per frame.
|
|
if ( frontsector->validcount != validcount ) {
|
|
frontsector->validcount = validcount;
|
|
|
|
c_sectors++;
|
|
GLFlat flat;
|
|
flat.sectornum = frontsector->iSectorID;
|
|
flat.light = lightlevel;
|
|
flat.uoffs= 0; // no support in standard doom
|
|
flat.voffs= 0;
|
|
|
|
if ( frontsector->floorheight < viewz ) {
|
|
if (frontsector->floorpic == skyflatnum) {
|
|
skyIsVisible = true;
|
|
} else {
|
|
// get the texture. flattranslation is maintained by doom and
|
|
// contains the number of the current animation frame
|
|
GLTexture *tex = gld_RegisterFlat(flattranslation[frontsector->floorpic], true);
|
|
if ( tex ) {
|
|
sectorPlanes[numSectorPlanes].texture = tex;
|
|
sectorPlanes[numSectorPlanes].ceiling = false;
|
|
sectorPlanes[numSectorPlanes].sector = frontsector;
|
|
numSectorPlanes++;
|
|
}
|
|
}
|
|
}
|
|
if ( frontsector->ceilingheight > viewz ) {
|
|
if (frontsector->ceilingpic == skyflatnum) {
|
|
skyIsVisible = true;
|
|
} else {
|
|
// get the texture. flattranslation is maintained by doom and
|
|
// contains the number of the current animation frame
|
|
GLTexture *tex = gld_RegisterFlat(flattranslation[frontsector->ceilingpic], true);
|
|
if ( tex ) {
|
|
sectorPlanes[numSectorPlanes].texture = tex;
|
|
sectorPlanes[numSectorPlanes].ceiling = true;
|
|
sectorPlanes[numSectorPlanes].sector = frontsector;
|
|
numSectorPlanes++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add all the sprites in this sector.
|
|
// It would be better if they were linked into all the subsectors, because
|
|
// we could do more accurate occlusion culling. With non-convex sectors,
|
|
// occasionally a sprite will be added in a rear portion of the sector that
|
|
// would have been occluded away if everything was done in BSP subsector order.
|
|
for ( mobj_t *thing = frontsector->thinglist; thing; thing = thing->snext) {
|
|
IR_ProjectSprite( thing, lightlevel );
|
|
}
|
|
}
|
|
|
|
// If a segment in this subsector is not fully occluded, mark
|
|
// the line that it is a part of as needing to be drawn. Because
|
|
// we are using a depth buffer, we can draw complete line segments
|
|
// instead of just segments.
|
|
for ( int i = 0 ; i < sub->numlines ; i++ ) {
|
|
seg_t *seg = &segs[sub->firstline+i];
|
|
|
|
line_t *line = seg->linedef;
|
|
|
|
// Determine if it will completely occlude farther objects.
|
|
// Given that changing sector heights is much less common than
|
|
// traversing lines during every render, it would be marginally better if
|
|
// lines had an "occluder" flag on them that was updated as sectors
|
|
// moved, but it hardly matters.
|
|
boolean occluder;
|
|
if ( seg->backsector == NULL ||
|
|
seg->backsector->floorheight >= seg->backsector->ceilingheight ||
|
|
seg->backsector->floorheight >= seg->frontsector->ceilingheight ||
|
|
seg->backsector->ceilingheight <= seg->frontsector->floorheight ) {
|
|
// this segment can't be seen past, so fill in the occlusion table
|
|
occluder = true;
|
|
} else {
|
|
// If the line has already been made visible and we don't need to
|
|
// update the occlusion buffer, we don't need to do anything else here.
|
|
// This happens when a line is split into multiple segs, and also
|
|
// when the line is reached from the backsector. In the backsector
|
|
// case, it would be back-face culled, but this test throws it out
|
|
// without having to transform and clip the ends.
|
|
if ( line->validcount == validcount ) {
|
|
continue;
|
|
}
|
|
|
|
// check to see if the seg won't draw any walls at all
|
|
|
|
// we won't fill in the occlusion table for this
|
|
occluder = false;
|
|
}
|
|
|
|
// transform and clip the two endpoints
|
|
float v[2][2];
|
|
float floatEnds[2];
|
|
v[0][0] = -seg->v1->x/MAP_SCALE;
|
|
v[0][1] = seg->v1->y/MAP_SCALE;
|
|
v[1][0] = -seg->v2->x/MAP_SCALE;
|
|
v[1][1] = seg->v2->y/MAP_SCALE;
|
|
if ( !TransformAndClipSegment( v, floatEnds ) ) {
|
|
// the line is off to the side or facing away
|
|
continue;
|
|
}
|
|
|
|
// Allow segs that we consider to be slightly back
|
|
// facing to still pass through, because GPU floating
|
|
// point calculations may not see them exactly the same.
|
|
if ( floatEnds[0] > floatEnds[1] + 1.0f ) {
|
|
// back face
|
|
continue;
|
|
}
|
|
// Check it against the occlusion buffer.
|
|
// Because the perspective divide is not going to be bit-exact between
|
|
// the CPU and GPU, we check an extra column here. That will result
|
|
// in an occasional line being drawn that might not need to be, but
|
|
// it avoids missing columns.
|
|
int checkMin = floor( floatEnds[0] - 1 );
|
|
int checkMax = ceil( floatEnds[1] + 1 );
|
|
if ( checkMin < 0 ) {
|
|
checkMin = 0;
|
|
}
|
|
if ( checkMax > viewwidth ) {
|
|
checkMax = viewwidth;
|
|
}
|
|
if ( !memchr( occlusion + checkMin, 0, checkMax - checkMin ) ) {
|
|
failCount++;
|
|
// every column it would touch is already solid, so it isn't visible
|
|
continue;
|
|
}
|
|
if ( occluder ) {
|
|
// It is important to update the occlusion array as individual
|
|
// segs are processed to maintain pure front to back order. If
|
|
// the occlusion buffer was updated by complete lines, it would
|
|
// result in some elements being incorrectly occlusion culled.
|
|
|
|
// Use a consistant fill rule for the occlusion, which is only
|
|
// referenced by the CPU, and should be water tight.
|
|
int fillMin = (int) (floatEnds[0]+0.5);
|
|
int fillMax = (int) (floatEnds[1]+0.5);
|
|
if ( fillMax > fillMin ) {
|
|
memset( occlusion + fillMin, 1, fillMax-fillMin );
|
|
}
|
|
}
|
|
|
|
if ( line->validcount == validcount ) {
|
|
continue;
|
|
}
|
|
line->validcount = validcount;
|
|
|
|
// this line can show up on the automap now
|
|
line->flags |= ML_MAPPED;
|
|
|
|
// Adding a line may generate up to four drawn walls -- a top wall,
|
|
// a bottom wall, a perforated middle wall, and a sky wall.
|
|
|
|
// Use the complete, unclipped segment for the side
|
|
IR_AddWall( &seg->sidedef->sideSeg );
|
|
}
|
|
}
|
|
|
|
PUREFUNC static int IR_PointOnSide(fixed_t x, fixed_t y, const node_t *node)
|
|
{
|
|
// JDC: these early tests probably aren't worth it on iphone...
|
|
if (!node->dx)
|
|
return x <= node->x ? node->dy > 0 : node->dy < 0;
|
|
|
|
if (!node->dy)
|
|
return y <= node->y ? node->dx < 0 : node->dx > 0;
|
|
|
|
x -= node->x;
|
|
y -= node->y;
|
|
|
|
// Try to quickly decide by looking at sign bits.
|
|
if ((node->dy ^ node->dx ^ x ^ y) < 0)
|
|
return (node->dy ^ x) < 0; // (left is negative)
|
|
|
|
return (__int64_t)y * (__int64_t)node->dx >= (__int64_t)x * (__int64_t)node->dy;
|
|
}
|
|
|
|
|
|
static const int checkcoord[12][4] = // killough -- static const
|
|
{
|
|
{3,0,2,1},
|
|
{3,0,2,0},
|
|
{3,1,2,0},
|
|
{0},
|
|
{2,0,2,1},
|
|
{0,0,0,0},
|
|
{3,1,3,0},
|
|
{0},
|
|
{2,0,3,1},
|
|
{2,1,3,1},
|
|
{2,1,3,0}
|
|
};
|
|
static boolean IR_IsBBoxCompletelyOccluded(const fixed_t *bspcoord) {
|
|
// conservatively accept if close to the box, so
|
|
// we don't need to worry about the near clip plane
|
|
// in TrnasformAndClipSegment. Mapscale is 128*fracunit
|
|
// and nearclip is 0.1, so accepting 2 fracunits away works.
|
|
if ( viewx > bspcoord[BOXLEFT]-2*FRACUNIT && viewx < bspcoord[BOXRIGHT] + 2*FRACUNIT
|
|
&& viewy > bspcoord[BOXBOTTOM]-2*FRACUNIT && viewy < bspcoord[BOXTOP] + 2*FRACUNIT ) {
|
|
return false;
|
|
}
|
|
|
|
// Find the corners of the box
|
|
// that define the edges from current viewpoint.
|
|
int boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT ] ? 1 : 2) +
|
|
(viewy >= bspcoord[BOXTOP ] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8);
|
|
|
|
const int *check = checkcoord[boxpos];
|
|
float v[2][2];
|
|
|
|
v[0][0] = -bspcoord[check[0]]/MAP_SCALE;
|
|
v[0][1] = bspcoord[check[1]]/MAP_SCALE;
|
|
|
|
v[1][0] = -bspcoord[check[2]]/MAP_SCALE;
|
|
v[1][1] = bspcoord[check[3]]/MAP_SCALE;
|
|
float ends[2];
|
|
if ( !TransformAndClipSegment( v, ends ) ) {
|
|
// the line is off to the side or facing away
|
|
return true;
|
|
}
|
|
|
|
if ( ends[0] >= ends[1] ) {
|
|
return true;
|
|
}
|
|
|
|
assert( ends[0] >= 0 );
|
|
assert( ends[1] <= viewwidth );
|
|
|
|
// Check it against the occlusion buffer, with an extra column
|
|
// of slop to account for GPU / CPU floating point differences.
|
|
if ( !memchr( occlusion + (int)ends[0], 0, (int)ends[1]-(int)ends[0]+1 ) ) {
|
|
// every column it would touch is already solid, so it isn't visible
|
|
return true;
|
|
}
|
|
// there are gaps, so we may need to draw something
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
RenderBSPNode
|
|
|
|
Renders all subsectors below a given node,
|
|
traversing subtree recursively.
|
|
Because this function is recursive, avoid doing work that
|
|
would give a large stack frame. Important that the compiler
|
|
doesn't inline big functions.
|
|
*/
|
|
static void IR_RenderBSPNode( int bspnum ) {
|
|
while (!(bspnum & NF_SUBSECTOR)) {
|
|
// decision node
|
|
const node_t *bsp = &nodes[bspnum];
|
|
|
|
// Decide which side the view point is on.
|
|
int side = IR_PointOnSide(viewx, viewy, bsp);
|
|
|
|
// check the front space
|
|
if ( !IR_IsBBoxCompletelyOccluded(bsp->bbox[side]) ) {
|
|
IR_RenderBSPNode( bsp->children[side] );
|
|
}
|
|
// continue down the back space
|
|
if ( IR_IsBBoxCompletelyOccluded( bsp->bbox[side^1]) ) {
|
|
return;
|
|
}
|
|
bspnum = bsp->children[side^1];
|
|
}
|
|
|
|
// subsector with contents
|
|
// add all the drawable elements in the subsector
|
|
if ( bspnum == -1 ) {
|
|
bspnum = 0;
|
|
}
|
|
bspnum &= ~NF_SUBSECTOR;
|
|
IR_Subsector( bspnum );
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
void *texture;
|
|
void *user;
|
|
} texSort_t;
|
|
|
|
static int TexSort( const void *a, const void *b ) {
|
|
if ( ((texSort_t *)a)->texture < ((texSort_t *)b)->texture ) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int SysIphoneMicroseconds();
|
|
void SetImmediateModeGLVertexArrays();
|
|
extern float yaw;
|
|
extern GLTexture **gld_GLTextures;
|
|
extern GLTexture **gld_GLPatchTextures;
|
|
extern GLVertex *gld_vertexes;
|
|
extern GLTexcoord *gld_texcoords;
|
|
|
|
#define MAX_DRAW_INDEXES 0x10000
|
|
unsigned short drawIndexes[MAX_DRAW_INDEXES];
|
|
int numDrawIndexes;
|
|
|
|
drawVert_t drawVerts[MAX_DRAW_VERTS];
|
|
int numDrawVerts;
|
|
|
|
void NewDrawScene(player_t *player) // JDC: new version
|
|
{
|
|
int i,k;
|
|
|
|
glDisable( GL_ALPHA_TEST );
|
|
glDisable(GL_CULL_FACE);
|
|
glDisable(GL_FOG);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
// Use the 2x texture combiner mode to allow the game to be brightened up
|
|
// above the normal maximum. All calculated color values for lighting will
|
|
// be multiplied by a value ranging from 0.5 for original brightness, up to
|
|
// 1.0 for 2x brightness. This combine mode will be canceled after all
|
|
// the 3D and view weapon drawing is completed, so the hud elements are
|
|
// drawn at normal brightness.
|
|
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
|
|
glTexEnvf( GL_TEXTURE_ENV, GL_RGB_SCALE, 2.0 );
|
|
|
|
// opaque skies, flats, and walls write to the depth buffer and don't blend
|
|
glDisable( GL_BLEND );
|
|
glDepthMask( 1 );
|
|
|
|
// debug tool to check for things being drawn that shouldn't be
|
|
if ( blendAll ) {
|
|
glClearColor( 0, 0, 0, 0 );
|
|
glClear( GL_COLOR_BUFFER_BIT );
|
|
glEnable( GL_BLEND );
|
|
glDisable( GL_DEPTH_TEST );
|
|
glBlendFunc( GL_ONE, GL_ONE );
|
|
skyIsVisible = false;
|
|
}
|
|
|
|
// debug tool to look for pixel cracks
|
|
if ( testClear ) {
|
|
glClearColor( 1, 0, 0, 0 );
|
|
glClear( GL_COLOR_BUFFER_BIT );
|
|
skyIsVisible = false;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// draw the sky if needed
|
|
//-----------------------------------------
|
|
if ( skyIsVisible ) {
|
|
float s;
|
|
float y;
|
|
|
|
// Note that these texcoords would have to be corrected
|
|
// for different screen aspect ratios or fields of view!
|
|
s = ((yaw+90.0f)/90.0f);
|
|
y = 1 - 2 * 128.0 / 200;
|
|
|
|
// With identity matricies, the vertex coordinates
|
|
// can just be in the 0-1 range.
|
|
glMatrixMode( GL_MODELVIEW );
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glMatrixMode( GL_PROJECTION );
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
iphoneRotateForLandscape();
|
|
|
|
gld_BindTexture( gld_RegisterTexture( skytexture, true, true ) );
|
|
glColor4f( 0.5, 0.5, 0.5, 1.0 ); // native texture color, not double bright
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glTexCoord2f( s, 1 ); glVertex3f(-1,y,0.999);
|
|
glTexCoord2f( s, 0 ); glVertex3f(-1,1,0.999);
|
|
glTexCoord2f( s+1, 1 ); glVertex3f(1,y,0.999);
|
|
glTexCoord2f( s+1, 0 ); glVertex3f(1,1,0.999);
|
|
glEnd();
|
|
|
|
// back to the normal drawing matrix
|
|
glPopMatrix();
|
|
glMatrixMode( GL_MODELVIEW );
|
|
glPopMatrix();
|
|
}
|
|
|
|
// walls and flats use the drawVerts array for everything
|
|
glTexCoordPointer(2,GL_FLOAT,sizeof(drawVert_t),drawVerts[0].st);
|
|
glVertexPointer(3,GL_FLOAT,sizeof(drawVert_t),drawVerts[0].xyz);
|
|
glColorPointer(4,GL_UNSIGNED_BYTE,sizeof(drawVert_t),drawVerts[0].rgba);
|
|
|
|
// everything will draw at full brightness in this case
|
|
if (player->fixedcolormap) {
|
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f );
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
} else {
|
|
glEnableClientState( GL_COLOR_ARRAY );
|
|
}
|
|
|
|
int c_drawElements = 0;
|
|
numDrawIndexes = 0;
|
|
|
|
//-----------------------------------------
|
|
// draw all the walls, sky walls sorted first
|
|
//-----------------------------------------
|
|
// sort the walls by texture
|
|
texSort_t *wallSort = alloca( sizeof( wallSort[0] ) * gld_drawinfo.num_walls );
|
|
for (i=0 ; i < gld_drawinfo.num_walls ; i++ ) {
|
|
GLWall *wall = &gld_drawinfo.walls[i];
|
|
wallSort[i].texture = wall->gltexture;
|
|
wallSort[i].user = wall;
|
|
}
|
|
qsort( wallSort, gld_drawinfo.num_walls, sizeof( wallSort[0] ), TexSort );
|
|
|
|
// continue building drawverts after the floor and ceiling data
|
|
int tempDrawVert = numDrawVerts;
|
|
|
|
// alpha tested walls will use half alpha to get the best edging effects
|
|
glAlphaFunc( GL_GREATER, 0.5 );
|
|
|
|
for (i=0 ; i < gld_drawinfo.num_walls ; i++ ) {
|
|
texSort_t *sort = &wallSort[i];
|
|
GLWall *wall = sort->user;
|
|
rendered_segs++;
|
|
|
|
// add two tris to draw the wall
|
|
drawIndexes[numDrawIndexes+0] = tempDrawVert;
|
|
drawIndexes[numDrawIndexes+1] = tempDrawVert+1;
|
|
drawIndexes[numDrawIndexes+2] = tempDrawVert+2;
|
|
|
|
drawIndexes[numDrawIndexes+3] = tempDrawVert+1;
|
|
drawIndexes[numDrawIndexes+4] = tempDrawVert+2;
|
|
drawIndexes[numDrawIndexes+5] = tempDrawVert+3;
|
|
|
|
numDrawIndexes += 6;
|
|
|
|
unsigned char rgba[4];
|
|
rgba[0] = rgba[1] = rgba[2] = wall->light;
|
|
rgba[3] = 255;
|
|
int lightInt = *(int *)rgba;
|
|
|
|
drawVerts[tempDrawVert].st[0] = wall->ul;
|
|
drawVerts[tempDrawVert].st[1] = wall->vt;
|
|
drawVerts[tempDrawVert].xyz[0] = -wall->side->sideSeg.v1->x / MAP_SCALE;
|
|
drawVerts[tempDrawVert].xyz[1] = wall->ytop;
|
|
drawVerts[tempDrawVert].xyz[2] = wall->side->sideSeg.v1->y / MAP_SCALE;
|
|
|
|
lightInt = FadedLighting( drawVerts[tempDrawVert].xyz[0], drawVerts[tempDrawVert].xyz[2], wall->light );
|
|
|
|
*(int *)drawVerts[tempDrawVert].rgba = lightInt;
|
|
tempDrawVert++;
|
|
|
|
drawVerts[tempDrawVert].st[0] = wall->ul;
|
|
drawVerts[tempDrawVert].st[1] = wall->vb;
|
|
drawVerts[tempDrawVert].xyz[0] = -wall->side->sideSeg.v1->x / MAP_SCALE;
|
|
drawVerts[tempDrawVert].xyz[1] = wall->ybottom;
|
|
drawVerts[tempDrawVert].xyz[2] = wall->side->sideSeg.v1->y / MAP_SCALE;
|
|
*(int *)drawVerts[tempDrawVert].rgba = lightInt;
|
|
tempDrawVert++;
|
|
|
|
drawVerts[tempDrawVert].st[0] = wall->ur;
|
|
drawVerts[tempDrawVert].st[1] = wall->vt;
|
|
drawVerts[tempDrawVert].xyz[0] = -wall->side->sideSeg.v2->x / MAP_SCALE;
|
|
drawVerts[tempDrawVert].xyz[1] = wall->ytop;
|
|
drawVerts[tempDrawVert].xyz[2] = wall->side->sideSeg.v2->y / MAP_SCALE;
|
|
|
|
lightInt = FadedLighting( drawVerts[tempDrawVert].xyz[0], drawVerts[tempDrawVert].xyz[2], wall->light );
|
|
*(int *)drawVerts[tempDrawVert].rgba = lightInt;
|
|
tempDrawVert++;
|
|
|
|
drawVerts[tempDrawVert].st[0] = wall->ur;
|
|
drawVerts[tempDrawVert].st[1] = wall->vb;
|
|
drawVerts[tempDrawVert].xyz[0] = -wall->side->sideSeg.v2->x / MAP_SCALE;
|
|
drawVerts[tempDrawVert].xyz[1] = wall->ybottom;
|
|
drawVerts[tempDrawVert].xyz[2] = wall->side->sideSeg.v2->y / MAP_SCALE;
|
|
*(int *)drawVerts[tempDrawVert].rgba = lightInt;
|
|
tempDrawVert++;
|
|
|
|
// only draw when textures change
|
|
if ( i == gld_drawinfo.num_walls-1 || sort->texture != (sort+1)->texture ) {
|
|
if ( numDrawIndexes ) {
|
|
if ( wall->flag == GLDWF_SKY ) {
|
|
// we aren't actually drawing anything with this,
|
|
// we are just masking off areas in the depth
|
|
// buffer so nothing can overwrite the already
|
|
// drawn sky image
|
|
glColorMask( 0, 0, 0, 0 );
|
|
}
|
|
if ( wall->flag == GLDWF_M2S ) {
|
|
glEnable( GL_ALPHA_TEST );
|
|
}
|
|
|
|
if ( wall->gltexture ) { // skies are texture = NULL
|
|
gld_BindTexture( wall->gltexture );
|
|
}
|
|
|
|
glDrawElements( GL_TRIANGLES, numDrawIndexes, GL_UNSIGNED_SHORT,
|
|
drawIndexes );
|
|
|
|
if ( wall->flag == GLDWF_M1S ) {
|
|
glDisable( GL_ALPHA_TEST );
|
|
}
|
|
if ( wall->flag == GLDWF_SKY ) {
|
|
glColorMask( 1, 1, 1, 1 );
|
|
}
|
|
|
|
numDrawIndexes = 0;
|
|
tempDrawVert = numDrawVerts;
|
|
c_drawElements++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// draw all the flats
|
|
//
|
|
// If we were able to directly fill the GPU command buffers,
|
|
// we would be using multiple DrawArrays instead of a single DrawElements,
|
|
// and we would fill the plane height and color in as we copied vertexes
|
|
// from a single set of verts per sector, but because the driver validation
|
|
// overhead is the main poison on the iPhone currently, it is better to
|
|
// duplicate the windings for the floors and ceilings, patch the
|
|
// vertex data, and generate new index lists to minimize the number of
|
|
// draw calls.
|
|
//-----------------------------------------
|
|
|
|
// sort the flats by texture
|
|
qsort( sectorPlanes, numSectorPlanes, sizeof( sectorPlanes[0] ), TexSort );
|
|
|
|
// draw them in texture order
|
|
for (i=0 ; i < numSectorPlanes ; i++ ) {
|
|
sortSectorPlane_t *sort = §orPlanes[i];
|
|
sector_t *sector = sort->sector;
|
|
|
|
drawVert_t *dv = sector->verts[(int)sort->ceiling];
|
|
|
|
// Patch the height values if they have changed since the last draw.
|
|
float y = sort->ceiling ? sector->ceilingheight : sector->floorheight;
|
|
y *= ( 1.0 / MAP_SCALE );
|
|
if ( y != dv->xyz[1] ) {
|
|
for ( int j = 0 ; j < sector->numVerts ; j++ ) {
|
|
(dv+j)->xyz[1] = y;
|
|
}
|
|
}
|
|
|
|
// per-vertex faded light color
|
|
int light = sector->lightlevel + (extralight<<5);
|
|
if ( light > 255 ) {
|
|
light = 255;
|
|
}
|
|
for ( int j = 0 ; j < sector->numVerts ; j++ ) {
|
|
*(int *)(dv+j)->rgba = FadedLighting( (dv+j)->xyz[0], (dv+j)->xyz[2], light );
|
|
}
|
|
|
|
// copy the indexes
|
|
assert( numDrawIndexes + sector->numIndexes < MAX_DRAW_INDEXES );
|
|
memcpy( drawIndexes + numDrawIndexes, sector->indexes[(int)sort->ceiling], sector->numIndexes*sizeof(drawIndexes[0]) );
|
|
numDrawIndexes += sector->numIndexes;
|
|
|
|
// only change textures when necessary
|
|
if ( i == numSectorPlanes - 1 || sort->texture != (sort+1)->texture ) {
|
|
if ( numDrawIndexes ) {
|
|
gld_BindFlat( sort->texture );
|
|
glDrawElements( GL_TRIANGLES, numDrawIndexes, GL_UNSIGNED_SHORT,
|
|
drawIndexes );
|
|
numDrawIndexes = 0;
|
|
c_drawElements++;
|
|
}
|
|
}
|
|
}
|
|
|
|
glDisableClientState( GL_COLOR_ARRAY );
|
|
|
|
// back to our immediate mode vertex arrays
|
|
SetImmediateModeGLVertexArrays();
|
|
|
|
|
|
//-----------------------------------------
|
|
// draw all the sprites
|
|
//-----------------------------------------
|
|
|
|
// transparent sprites blend and don't write to the depth buffer
|
|
glEnable( GL_BLEND );
|
|
glDepthMask( 0 );
|
|
|
|
glEnable( GL_ALPHA_TEST );
|
|
|
|
// get the screen space vector for sprites
|
|
float yaws = -sin( yaw * 3.141592657 / 180.0 );
|
|
float yawc = -cos( yaw * 3.141592657 / 180.0 );
|
|
int c_spriteBind = 0;
|
|
int c_spriteDraw = 0;
|
|
while( 1 )
|
|
{
|
|
// pick out the sprites from farthest to nearest
|
|
fixed_t max_scale=INT_MAX;
|
|
k=-1;
|
|
for (i=0 ; i < gld_drawinfo.num_sprites ; i++ ) {
|
|
GLSprite *sprite = &gld_drawinfo.sprites[i];
|
|
if (sprite->scale<max_scale)
|
|
{
|
|
max_scale=sprite->scale;
|
|
k=i;
|
|
}
|
|
}
|
|
if ( k == -1 ) {
|
|
break;
|
|
}
|
|
|
|
GLSprite *sprite = &gld_drawinfo.sprites[k];
|
|
sprite->scale=INT_MAX;
|
|
|
|
if ( sprite->gltexture != last_gltexture ) {
|
|
c_spriteBind++;
|
|
}
|
|
c_spriteDraw++;
|
|
|
|
gld_BindPatch(sprite->gltexture,sprite->cm);
|
|
if(sprite->shadow)
|
|
{
|
|
glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
|
|
glColor4f(0.2f,0.2f,0.2f,0.33f);
|
|
glAlphaFunc( GL_GREATER, 0.1 ); // don't alpha test away the blended-down version
|
|
}
|
|
else
|
|
{
|
|
float flight = (float)sprite->light*(1.0f/255);
|
|
|
|
// We could do the distance-lighting here, but leaving the sprites
|
|
// brighter is a good accent in most cases. There are a few places
|
|
// where environmental sprites look a little wrong, but it is probably
|
|
// better in general.
|
|
|
|
if (player->fixedcolormap) {
|
|
flight = 1.0; // light amp goggles
|
|
}
|
|
glColor4f(flight, flight, flight, 1.0f );
|
|
}
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glTexCoord2f(sprite->ul, sprite->vt);
|
|
glVertex3f(sprite->x + sprite->x1 * yawc, sprite->y + sprite->y1, sprite->z + sprite->x1 * yaws );
|
|
glTexCoord2f(sprite->ur, sprite->vt);
|
|
glVertex3f(sprite->x + sprite->x2 * yawc, sprite->y + sprite->y1, sprite->z + sprite->x2 * yaws );
|
|
glTexCoord2f(sprite->ul, sprite->vb);
|
|
glVertex3f(sprite->x + sprite->x1 * yawc, sprite->y + sprite->y2, sprite->z + sprite->x1 * yaws );
|
|
glTexCoord2f(sprite->ur, sprite->vb);
|
|
glVertex3f(sprite->x + sprite->x2 * yawc, sprite->y + sprite->y2, sprite->z + sprite->x2 * yaws );
|
|
glEnd();
|
|
|
|
if(sprite->shadow)
|
|
{
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glAlphaFunc( GL_GREATER, 0.5 );
|
|
}
|
|
}
|
|
|
|
glDisable( GL_ALPHA_TEST );
|
|
glDepthMask( 1 );
|
|
}
|
|
|
|
|
|
static float roll = 0.0f;
|
|
/* JDC static */ float yaw = 0.0f;
|
|
static float inv_yaw = 0.0f;
|
|
static float pitch = 0.0f;
|
|
|
|
#define __glPi 3.14159265358979323846
|
|
|
|
static void infinitePerspective(GLdouble fovy, GLdouble aspect, GLdouble znear)
|
|
{
|
|
GLfloat left, right, bottom, top; // JDC: was GLdouble
|
|
GLfloat m[16]; // JDC: was GLdouble
|
|
|
|
top = znear * tan(fovy * __glPi / 360.0);
|
|
bottom = -top;
|
|
left = bottom * aspect;
|
|
right = top * aspect;
|
|
|
|
//qglFrustum(left, right, bottom, top, znear, zfar);
|
|
|
|
m[ 0] = (2 * znear) / (right - left);
|
|
m[ 4] = 0;
|
|
m[ 8] = (right + left) / (right - left);
|
|
m[12] = 0;
|
|
|
|
m[ 1] = 0;
|
|
m[ 5] = (2 * znear) / (top - bottom);
|
|
m[ 9] = (top + bottom) / (top - bottom);
|
|
m[13] = 0;
|
|
|
|
m[ 2] = 0;
|
|
m[ 6] = 0;
|
|
//m[10] = - (zfar + znear) / (zfar - znear);
|
|
//m[14] = - (2 * zfar * znear) / (zfar - znear);
|
|
m[10] = -1;
|
|
m[14] = -2 * znear;
|
|
|
|
m[ 3] = 0;
|
|
m[ 7] = 0;
|
|
m[11] = -1;
|
|
m[15] = 0;
|
|
|
|
glMultMatrixf(m); // JDC: was glMultMatrixd
|
|
}
|
|
|
|
/*
|
|
IR_RenderPlayerView
|
|
|
|
Replace the prBoom rendering code with a higher performance
|
|
version. Most of the fancy new features are gone, because I
|
|
have no idea what the reight test cases would be for them.
|
|
*/
|
|
void IR_RenderPlayerView (player_t* player) {
|
|
int start = 0;
|
|
if ( showRenderTime ) {
|
|
start = SysIphoneMicroseconds();
|
|
}
|
|
|
|
viewplayer = player;
|
|
tic_vars.frac = FRACUNIT;
|
|
|
|
viewx = player->mo->x;
|
|
viewy = player->mo->y;
|
|
viewz = player->viewz;
|
|
viewangle = player->mo->angle;
|
|
extralight = player->extralight; // gun flashes
|
|
|
|
yaw=270.0f-(float)(viewangle>>ANGLETOFINESHIFT)*360.0f/FINEANGLES;
|
|
|
|
viewsin = finesine[viewangle>>ANGLETOFINESHIFT];
|
|
viewcos = finecosine[viewangle>>ANGLETOFINESHIFT];
|
|
|
|
// IR goggles
|
|
if (player->fixedcolormap) {
|
|
fixedcolormap = fullcolormap + player->fixedcolormap*256*sizeof(lighttable_t);
|
|
} else {
|
|
fixedcolormap = 0;
|
|
}
|
|
|
|
// this is used to tell if a line, sector, or sprite is going to be drawn this frame
|
|
validcount++;
|
|
|
|
// clear the 1D occlusion buffer, set a final guard column to occluded so we can
|
|
// check an extra pixel to account for slight floating point differences between
|
|
// the GPU and CPU transformations
|
|
assert( viewwidth <= MAX_SCREENWIDTH );
|
|
memset( occlusion, 0, sizeof( occlusion ) );
|
|
occlusion[viewwidth] = 1;
|
|
|
|
float trY ;
|
|
float xCamera,yCamera;
|
|
|
|
extern int screenblocks;
|
|
int height;
|
|
|
|
gld_SetPalette(-1);
|
|
|
|
if (screenblocks == 11)
|
|
height = SCREENHEIGHT;
|
|
else if (screenblocks == 10)
|
|
height = SCREENHEIGHT;
|
|
else
|
|
height = (screenblocks*SCREENHEIGHT/10) & ~7;
|
|
|
|
glViewport(viewwindowx, SCREENHEIGHT-(height+viewwindowy-((height-viewheight)/2)), viewwidth, height);
|
|
glScissor(viewwindowx, SCREENHEIGHT-(viewheight+viewwindowy), viewwidth, viewheight);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_FOG);
|
|
glShadeModel(GL_SMOOTH);
|
|
|
|
// Player coordinates
|
|
xCamera=-(float)viewx/MAP_SCALE;
|
|
yCamera=(float)viewy/MAP_SCALE;
|
|
trY=(float)viewz/MAP_SCALE;
|
|
|
|
yaw=270.0f-(float)(viewangle>>ANGLETOFINESHIFT)*360.0f/FINEANGLES;
|
|
inv_yaw=-90.0f+(float)(viewangle>>ANGLETOFINESHIFT)*360.0f/FINEANGLES;
|
|
|
|
#ifdef _DEBUG
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
#else
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
#endif
|
|
|
|
// To make it easier to accurately mimic the GL model to screen transformation,
|
|
// this is set up so that the projection transformation is also done in the
|
|
// modelview matrix, leaving the projection matrix as an identity. This means
|
|
// that things done in eye space, like lighting and fog, won't work, but
|
|
// we don't need them.
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
// make the 320x480 hardware seem like 480x320 in two different orientations
|
|
// and note if the occlusion segmenrs need to be reversed
|
|
reversedLandscape = iphoneRotateForLandscape();
|
|
|
|
infinitePerspective(64.0f, 320.0f/200.0f, 5.0f/100.0f);
|
|
|
|
glRotatef(roll, 0.0f, 0.0f, 1.0f);
|
|
glRotatef(pitch, 1.0f, 0.0f, 0.0f);
|
|
glRotatef(yaw, 0.0f, 1.0f, 0.0f);
|
|
glTranslatef(-xCamera, -trY, -yCamera);
|
|
|
|
// read back the matrix so we can do exact calculations that match
|
|
// what GL is doing. It would probably be better to build the matricies
|
|
// ourselves and just do a loadMatrix...
|
|
glGetFloatv( GL_MODELVIEW_MATRIX, glMVPmatrix );
|
|
|
|
// setup the vector for calculating light fades, which is just a scale
|
|
// of the forward vector
|
|
lightingVector[0] = lightDistance * glMVPmatrix[2];
|
|
lightingVector[1] = lightDistance * glMVPmatrix[10];
|
|
lightingVector[2] = lightDistance * glMVPmatrix[14];
|
|
|
|
|
|
rendermarker++;
|
|
gld_drawinfo.num_walls=0;
|
|
gld_drawinfo.num_flats=0;
|
|
gld_drawinfo.num_sprites=0;
|
|
gld_drawinfo.num_drawitems=0;
|
|
|
|
c_occludedSprites = 0;
|
|
c_sectors = 0;
|
|
c_subsectors = 0;
|
|
numSectorPlanes = 0;
|
|
failCount = 0;
|
|
|
|
// Find everything we need to draw, but don't draw anything yet,
|
|
// because we want to sort by texture to reduce GL driver overhead.
|
|
IR_RenderBSPNode( numnodes-1 );
|
|
|
|
NewDrawScene(player);
|
|
|
|
gld_EndDrawScene();
|
|
|
|
if ( showRenderTime ) {
|
|
int end = SysIphoneMicroseconds();
|
|
printf( "%i usec\n", end - start );
|
|
}
|
|
}
|