doom-ios/code/iphone/iphone_render.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 = &sectors[fromSector];
sector_t *intoSectorPtr = &sectors[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 = &sectorPlanes[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 );
}
}