gear/src/gl_missingtexture.c
2024-09-08 18:03:02 +12:00

541 lines
16 KiB
C

/* Emacs style mode select -*- C++ -*-
*-----------------------------------------------------------------------------
*
*
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
* based on BOOM, a modified and improved DOOM engine
* Copyright (C) 1999 by
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
* Copyright (C) 1999-2000 by
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
* Copyright 2005, 2006 by
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
*
* 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.
*
* DESCRIPTION:
*
*---------------------------------------------------------------------
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gl_opengl.h"
#include <SDL.h>
#ifdef HAVE_LIBSDL2_IMAGE
#include <SDL_image.h>
#endif
#include "doomstat.h"
#include "v_video.h"
#include "gl_intern.h"
#include "i_system.h"
#include "lprintf.h"
#include "i_video.h"
#include "hu_lib.h"
#include "hu_stuff.h"
#include "r_main.h"
#include "e6y.h"
typedef struct
{
int count; // size of the list with adjoining sectors
int validcount; // finding of the best sector in the group only once in tic
int ceiling; // this group is for ceilings or flats
sector_t *sector; // sector with the 'best' height for the sectors in list
sector_t **list; // list of adjoining sectors
} fakegroup_t;
typedef struct
{
sector_t *source; // The sector to receive a fake bleed-through flat
sector_t *target; // The floor sector whose properties should be copied
int ceiling; // Ceiling or floor
} bleedthrough_t;
static int numfakeplanes = 0;
static fakegroup_t *fakeplanes = NULL;
static sector_t **sectors2 = NULL;
static bleedthrough_t *bleedsectors = NULL;
static int numbleedsectors = 0;
static void gld_PrepareSectorSpecialEffects(void);
static void gld_PreprocessFakeSector(int ceiling, sector_t *sector, int groupid);
static void gld_RegisterBleedthroughSector(sector_t* source, sector_t* target, int ceiling);
static void gld_PrepareSectorSpecialEffects(void)
{
int i, num;
/* free memory if allocated by previous maps */
if (bleedsectors)
{
free(bleedsectors);
numbleedsectors = 0;
bleedsectors = NULL;
}
for (num = 0; num < numsectors; num++)
{
// the following is for specialeffects. see r_bsp.c in R_Subsector
sectors[num].flags |= (NO_TOPTEXTURES | NO_BOTTOMTEXTURES);
for (i=0; i<sectors[num].linecount; i++)
{
unsigned short sidenum0 = sectors[num].lines[i]->sidenum[0];
unsigned short sidenum1 = sectors[num].lines[i]->sidenum[1];
side_t *side0 = (sidenum0 == NO_INDEX ? NULL : &sides[sidenum0]);
side_t *side1 = (sidenum1 == NO_INDEX ? NULL : &sides[sidenum1]);
if (side0 && side1)
{
if (side0->toptexture != NO_TEXTURE)
sectors[num].flags &= ~NO_TOPTEXTURES;
if (side0->bottomtexture != NO_TEXTURE)
sectors[num].flags &= ~NO_BOTTOMTEXTURES;
if (side1->toptexture != NO_TEXTURE)
sectors[num].flags &= ~NO_TOPTEXTURES;
if (side1->bottomtexture != NO_TEXTURE)
sectors[num].flags &= ~NO_BOTTOMTEXTURES;
/* sides should not have null sectors, but we check anyway */
if (side0->sector && side1->sector)
{
dboolean front_floor_is_sky = (side0->sector->floorpic == skyflatnum);
dboolean front_ceil_is_sky = (side0->sector->ceilingpic == skyflatnum);
dboolean back_floor_is_sky = (side1->sector->floorpic == skyflatnum);
dboolean back_ceil_is_sky = (side1->sector->ceilingpic == skyflatnum);
dboolean needs_back_lower = !(front_floor_is_sky) && (side0->sector->floorheight > side1->sector->floorheight);
dboolean needs_front_lower = !(back_floor_is_sky) && (side0->sector->floorheight < side1->sector->floorheight);
dboolean needs_front_upper = !(back_ceil_is_sky) && (side0->sector->ceilingheight > side1->sector->ceilingheight);
dboolean needs_back_upper = !(front_ceil_is_sky) && (side0->sector->ceilingheight < side1->sector->ceilingheight);
/* now mark the sectors that may require HOM bleed-through */
if (needs_front_upper && side0->toptexture == NO_TEXTURE) {
side1->sector->flags |= MISSING_TOPTEXTURES;
gld_RegisterBleedthroughSector(side1->sector,side0->sector,1);
}
if (needs_back_upper && side1->toptexture == NO_TEXTURE) {
side0->sector->flags |= MISSING_TOPTEXTURES;
gld_RegisterBleedthroughSector(side0->sector,side1->sector,1);
}
if (needs_back_lower && side1->bottomtexture == NO_TEXTURE) {
side0->sector->flags |= MISSING_BOTTOMTEXTURES;
gld_RegisterBleedthroughSector(side0->sector,side1->sector,0);
}
if (needs_front_lower && side0->bottomtexture == NO_TEXTURE) {
side1->sector->flags |= MISSING_BOTTOMTEXTURES;
gld_RegisterBleedthroughSector(side1->sector,side0->sector,0);
}
}
}
else
{
sectors[num].flags &= ~NO_TOPTEXTURES;
sectors[num].flags &= ~NO_BOTTOMTEXTURES;
}
}
#ifdef PRBOOM_DEBUG
if (sectors[num].flags & NO_TOPTEXTURES)
lprintf(LO_INFO,"Sector %i has no toptextures\n",num);
if (sectors[num].flags & NO_BOTTOMTEXTURES)
lprintf(LO_INFO,"Sector %i has no bottomtextures\n",num);
#endif
}
}
static void gld_RegisterBleedthroughSector(sector_t* source, sector_t* target, int ceiling)
{
int i;
int source_idx = -1;
assert(source);
assert(target);
/* check whether the sector is processed already */
for (i = 0; i < numbleedsectors && source_idx == -1; i++)
if (bleedsectors[i].source == source && bleedsectors[i].ceiling == ceiling)
source_idx = i;
if (source_idx == -1)
{
/* allocate memory for new sector */
bleedsectors = (bleedthrough_t*) realloc(bleedsectors, (numbleedsectors + 1) * sizeof(bleedthrough_t));
if(!bleedsectors) I_Error("gld_RegisterBleedthroughSector: Out of memory");
memset(&bleedsectors[numbleedsectors], 0, sizeof(bleedthrough_t));
numbleedsectors++;
source_idx = numbleedsectors - 1;
}
bleedsectors[source_idx].source = source;
bleedsectors[source_idx].ceiling = ceiling;
/* either register the proposed target since it is first,
* or check if the new proposed target is a better option
* and register it instead */
if ((bleedsectors[source_idx].target == NULL) ||
(bleedsectors[source_idx].target &&
(
(ceiling && bleedsectors[source_idx].target->ceilingheight > target->ceilingheight)
||
(bleedsectors[source_idx].target->floorheight < target->floorheight)
)
)
)
{
bleedsectors[source_idx].target = target;
}
}
sector_t* GetBestBleedSector(sector_t* source, int ceiling)
{
int i;
for (i = 0; i < numbleedsectors; i++)
if (bleedsectors[i].source == source && bleedsectors[i].ceiling == ceiling)
return bleedsectors[i].target;
return NULL;
}
//
// Recursive mark of all adjoining sectors with no bottom/top texture
//
static void gld_PreprocessFakeSector(int ceiling, sector_t *sector, int groupid)
{
int i;
if (sector->fakegroup[ceiling] != groupid)
{
sector->fakegroup[ceiling] = groupid;
if (groupid >= numfakeplanes)
{
fakeplanes = realloc(fakeplanes, (numfakeplanes + 1) * sizeof(fakegroup_t));
memset(&fakeplanes[numfakeplanes], 0, sizeof(fakegroup_t));
numfakeplanes++;
}
sectors2[fakeplanes[groupid].count++] = sector;
}
for (i = 0; i < sector->linecount; i++)
{
sector_t *sec = NULL;
line_t *line = sector->lines[i];
if (line->frontsector && line->frontsector != sector)
{
sec = line->frontsector;
}
else
{
if (line->backsector && line->backsector != sector)
{
sec = line->backsector;
}
}
if (sec && sec->fakegroup[ceiling] == -1 &&
(sec->flags & (ceiling ? NO_TOPTEXTURES : NO_BOTTOMTEXTURES)))
{
gld_PreprocessFakeSector(ceiling, sec, groupid);
}
}
}
//
// Split of all sectors into groups
// with adjoining sectors with no bottom/top texture
//
void gld_PreprocessFakeSectors(void)
{
int i, j, k, ceiling;
int groupid;
if (gl_use_stencil)
{
// precalculate NO_TOPTEXTURES and NO_BOTTOMTEXTURES flags
gld_PrepareSectorSpecialEffects();
return;
}
// free memory
if (fakeplanes)
{
for (i = 0; i < numfakeplanes; i++)
{
fakeplanes[i].count = 0;
free(fakeplanes[i].list);
fakeplanes[i].list = NULL;
}
numfakeplanes = 0;
free(fakeplanes);
fakeplanes = NULL;
}
if (sectors2)
{
free(sectors2);
}
sectors2 = malloc(numsectors * sizeof(sector_t*));
// reset all groups with fake floors and ceils
// 0 - floor; 1 - ceil;
for (i = 0; i < numsectors; i++)
{
sectors[i].fakegroup[0] = -1;
sectors[i].fakegroup[1] = -1;
}
// precalculate NO_TOPTEXTURES and NO_BOTTOMTEXTURES flags
gld_PrepareSectorSpecialEffects();
groupid = 0;
for (ceiling = 0; ceiling <= 1; ceiling++)
{
unsigned int no_texture_flag = (ceiling ? NO_TOPTEXTURES : NO_BOTTOMTEXTURES);
do
{
for (i = 0; i < numsectors; i++)
{
if (!(sectors[i].flags & no_texture_flag)
&& (sectors[i].fakegroup[ceiling] == -1))
{
gld_PreprocessFakeSector(ceiling, &sectors[i], groupid);
fakeplanes[groupid].ceiling = ceiling;
fakeplanes[groupid].list = malloc(fakeplanes[groupid].count * sizeof(sector_t*));
for (j = 0, k = 0; k < fakeplanes[groupid].count; k++)
{
if (!(sectors2[k]->flags & no_texture_flag))
{
fakeplanes[groupid].list[j++] = sectors2[k];
}
}
fakeplanes[groupid].count = j;
groupid++;
break;
}
}
}
while (i < numsectors);
}
}
//
// Get highest surounding floorheight for flors and
// lowest surounding ceilingheight for ceilings
//
sector_t* GetBestFake(sector_t *sector, int ceiling, int validcount)
{
int i;
int groupid = sector->fakegroup[ceiling];
if (groupid == -1)
return NULL;
if (fakeplanes[groupid].validcount != validcount)
{
fakeplanes[groupid].validcount = validcount;
fakeplanes[groupid].sector = NULL;
if (fakeplanes[groupid].ceiling)
{
fixed_t min_height = INT_MAX;
for (i = 0; i < fakeplanes[groupid].count; i++)
{
if (!(fakeplanes[groupid].list[i]->flags & NO_TOPTEXTURES) &&
fakeplanes[groupid].list[i]->ceilingheight < min_height)
{
min_height = fakeplanes[groupid].list[i]->ceilingheight;
fakeplanes[groupid].sector = fakeplanes[groupid].list[i];
}
}
}
else
{
fixed_t max_height = INT_MIN;
for (i = 0; i < fakeplanes[groupid].count; i++)
{
if (!(fakeplanes[groupid].list[i]->flags & NO_BOTTOMTEXTURES) &&
fakeplanes[groupid].list[i]->floorheight > max_height)
{
max_height = fakeplanes[groupid].list[i]->floorheight;
fakeplanes[groupid].sector = fakeplanes[groupid].list[i];
}
}
}
}
if (fakeplanes[groupid].sector)
{
if (fakeplanes[groupid].ceiling)
{
if (sector->ceilingheight < fakeplanes[groupid].sector->ceilingheight)
{
return sector;
}
}
else
{
if (sector->floorheight > fakeplanes[groupid].sector->floorheight)
{
return sector;
}
}
}
return fakeplanes[groupid].sector;
}
//==========================================================================
//
// Flood gaps with the back side's ceiling/floor texture
// This requires a stencil because the projected plane interferes with
// the depth buffer
//
//==========================================================================
void gld_SetupFloodStencil(GLWall *wall)
{
int recursion = 0;
// Create stencil
glStencilFunc(GL_EQUAL, recursion, ~0); // create stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // don't write to the graphics buffer
gld_EnableTexture2D(GL_TEXTURE0_ARB, false);
glColor3f(1, 1, 1);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
glBegin(GL_TRIANGLE_FAN);
glVertex3f(wall->glseg->x1, wall->ytop, wall->glseg->z1);
glVertex3f(wall->glseg->x1, wall->ybottom, wall->glseg->z1);
glVertex3f(wall->glseg->x2, wall->ybottom, wall->glseg->z2);
glVertex3f(wall->glseg->x2, wall->ytop, wall->glseg->z2);
glEnd();
glStencilFunc(GL_EQUAL, recursion+1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // don't write to the graphics buffer
gld_EnableTexture2D(GL_TEXTURE0_ARB, true);
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
}
void gld_ClearFloodStencil(GLWall *wall)
{
int recursion = 0;
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
gld_EnableTexture2D(GL_TEXTURE0_ARB, false);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // don't write to the graphics buffer
glColor3f(1, 1, 1);
glBegin(GL_TRIANGLE_FAN);
glVertex3f(wall->glseg->x1, wall->ytop, wall->glseg->z1);
glVertex3f(wall->glseg->x1, wall->ybottom, wall->glseg->z1);
glVertex3f(wall->glseg->x2, wall->ybottom, wall->glseg->z2);
glVertex3f(wall->glseg->x2, wall->ytop, wall->glseg->z2);
glEnd();
// restore old stencil op.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, recursion, ~0);
gld_EnableTexture2D(GL_TEXTURE0_ARB, true);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}
//
// Calculation of the coordinates of the gap
//
void gld_SetupFloodedPlaneCoords(GLWall *wall, gl_strip_coords_t *c)
{
float prj_fac1, prj_fac2;
float k = 0.5f;
float ytop, ybottom, planez;
if (wall->flag == GLDWF_TOPFLUD)
{
ytop = wall->ybottom;
ybottom = wall->ytop;
planez = wall->ybottom;
}
else
{
ytop = wall->ytop;
ybottom = wall->ybottom;
planez = wall->ytop;
}
prj_fac1 = (ytop - zCamera) / (ytop - zCamera);
prj_fac2 = (ytop - zCamera) / (ybottom - zCamera);
c->v[0][0] = xCamera + prj_fac1 * (wall->glseg->x1 - xCamera);
c->v[0][1] = planez;
c->v[0][2] = yCamera + prj_fac1 * (wall->glseg->z1 - yCamera);
c->v[1][0] = xCamera + prj_fac2 * (wall->glseg->x1 - xCamera);
c->v[1][1] = planez;
c->v[1][2] = yCamera + prj_fac2 * (wall->glseg->z1 - yCamera);
c->v[2][0] = xCamera + prj_fac1 * (wall->glseg->x2 - xCamera);
c->v[2][1] = planez;
c->v[2][2] = yCamera + prj_fac1 * (wall->glseg->z2 - yCamera);
c->v[3][0] = xCamera + prj_fac2 * (wall->glseg->x2 - xCamera);
c->v[3][1] = planez;
c->v[3][2] = yCamera + prj_fac2 * (wall->glseg->z2 - yCamera);
c->t[0][0] = -c->v[0][0] / k;
c->t[0][1] = -c->v[0][2] / k;
c->t[1][0] = -c->v[1][0] / k;
c->t[1][1] = -c->v[1][2] / k;
c->t[2][0] = -c->v[2][0] / k;
c->t[2][1] = -c->v[2][2] / k;
c->t[3][0] = -c->v[3][0] / k;
c->t[3][1] = -c->v[3][2] / k;
}
void gld_SetupFloodedPlaneLight(GLWall *wall)
{
if (wall->seg->backsector)
{
float light;
light = gld_CalcLightLevel(wall->seg->backsector->lightlevel+(extralight<<5));
gld_StaticLightAlpha(light, wall->alpha);
}
else
{
gld_StaticLightAlpha(wall->light, wall->alpha);
}
}