1998-04-07 00:00:00 +00:00
|
|
|
// Emacs style mode select -*- C++ -*-
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// $Id:$
|
|
|
|
//
|
|
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
|
|
//
|
|
|
|
// This source is available for distribution and/or modification
|
|
|
|
// only under the terms of the DOOM Source Code License as
|
|
|
|
// published by id Software. All rights reserved.
|
|
|
|
//
|
|
|
|
// The source is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
|
|
// for more details.
|
|
|
|
//
|
|
|
|
// $Log:$
|
|
|
|
//
|
|
|
|
// DESCRIPTION:
|
|
|
|
// The actual span/column drawing functions.
|
|
|
|
// Here find the main potential for optimization,
|
|
|
|
// e.g. inline assembly, different algorithms.
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
#include "m_alloc.h"
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
#include "doomdef.h"
|
|
|
|
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "z_zone.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
|
|
|
|
#include "r_local.h"
|
|
|
|
|
|
|
|
// Needs access to LFB (guess what).
|
|
|
|
#include "v_video.h"
|
|
|
|
|
|
|
|
// State.
|
|
|
|
#include "doomstat.h"
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
#include "st_stuff.h"
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
#undef RANGECHECK
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// status bar height at bottom of screen
|
1998-07-14 00:00:00 +00:00
|
|
|
// [RH] status bar position at bottom of screen
|
|
|
|
extern int ST_Y;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// All drawing to the view buffer is accomplished in this file.
|
|
|
|
// The other refresh files only know about ccordinates,
|
|
|
|
// not the architecture of the frame buffer.
|
|
|
|
// Conveniently, the frame buffer is a linear one,
|
|
|
|
// and we need only the base address,
|
|
|
|
// and the total size == width*height*depth/8.,
|
|
|
|
//
|
|
|
|
|
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
byte* viewimage;
|
1998-04-07 00:00:00 +00:00
|
|
|
int viewwidth;
|
|
|
|
int scaledviewwidth;
|
|
|
|
int viewheight;
|
|
|
|
int viewwindowx;
|
1998-07-14 00:00:00 +00:00
|
|
|
int viewwindowy;
|
1998-04-07 00:00:00 +00:00
|
|
|
byte** ylookup;
|
1998-07-14 00:00:00 +00:00
|
|
|
int* columnofs;
|
|
|
|
|
|
|
|
int realviewwidth; // [RH] Physical width of view window
|
|
|
|
int realviewheight; // [RH] Physical height of view window
|
|
|
|
int detailxshift; // [RH] X shift for horizontal detail level
|
|
|
|
int detailyshift; // [RH] Y shift for vertical detail level
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
// [RH] Pointers to the different column drawers.
|
|
|
|
// These get changed depending on the current
|
|
|
|
// screen depth.
|
1999-02-17 00:00:00 +00:00
|
|
|
void (*R_DrawColumnHoriz)(void);
|
1998-07-14 00:00:00 +00:00
|
|
|
void (*R_DrawColumn)(void);
|
|
|
|
void (*R_DrawFuzzColumn)(void);
|
|
|
|
void (*R_DrawTranslucentColumn)(void);
|
|
|
|
void (*R_DrawTranslatedColumn)(void);
|
|
|
|
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// R_DrawColumn
|
|
|
|
// Source is the top of the column to scale.
|
|
|
|
//
|
1998-07-14 00:00:00 +00:00
|
|
|
int dc_pitch=0x12345678; // [RH] Distance between rows
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
lighttable_t* dc_colormap;
|
|
|
|
int dc_x;
|
|
|
|
int dc_yl;
|
|
|
|
int dc_yh;
|
|
|
|
fixed_t dc_iscale;
|
|
|
|
fixed_t dc_texturemid;
|
1999-02-17 00:00:00 +00:00
|
|
|
int dc_color; // [RH] Color for column filler
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// first pixel in a column (possibly virtual)
|
|
|
|
byte* dc_source;
|
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
// [RH] Tutti-Frutti fix
|
|
|
|
unsigned int dc_mask;
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
// just for profiling
|
|
|
|
int dccount;
|
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
/************************************/
|
|
|
|
/* */
|
|
|
|
/* Palettized drawers (C versions) */
|
|
|
|
/* */
|
|
|
|
/************************************/
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
#ifndef USEASM
|
1998-04-07 00:00:00 +00:00
|
|
|
//
|
|
|
|
// A column is a vertical slice/span from a wall texture that,
|
|
|
|
// given the DOOM style restrictions on the view orientation,
|
|
|
|
// will always have constant z depth.
|
|
|
|
// Thus a special case loop for very fast rendering can
|
|
|
|
// be used. It has also been used with Wolfenstein 3D.
|
|
|
|
//
|
1999-02-17 00:00:00 +00:00
|
|
|
void R_DrawColumnP_C (void)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
byte* dest;
|
1998-04-07 00:00:00 +00:00
|
|
|
fixed_t frac;
|
1999-02-17 00:00:00 +00:00
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Zero length, column does not exceed a pixel.
|
1999-02-17 00:00:00 +00:00
|
|
|
if (count < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1998-04-07 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height) {
|
1999-02-17 00:00:00 +00:00
|
|
|
Printf (PRINT_HIGH, "R_DrawColumnP_C: %i to %i at %i\n", dc_yl, dc_yh, dc_x);
|
1998-04-07 00:00:00 +00:00
|
|
|
return;
|
|
|
|
}
|
1999-02-17 00:00:00 +00:00
|
|
|
#endif
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Framebuffer destination address.
|
|
|
|
// Use ylookup LUT to avoid multiply with ScreenWidth.
|
1999-02-17 00:00:00 +00:00
|
|
|
// Use columnofs LUT for subwindows?
|
|
|
|
dest = ylookup[dc_yl] + columnofs[dc_x];
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Determine scaling,
|
|
|
|
// which is the only mapping to be done.
|
|
|
|
fracstep = dc_iscale;
|
1999-02-17 00:00:00 +00:00
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
// [RH] Get local copies of these variables so that the compiler
|
|
|
|
// has a better chance of optimizing this well.
|
|
|
|
byte *colormap = dc_colormap;
|
|
|
|
int mask = dc_mask;
|
|
|
|
byte *source = dc_source;
|
|
|
|
int pitch = dc_pitch;
|
|
|
|
|
|
|
|
// Inner loop that does the actual texture mapping,
|
|
|
|
// e.g. a DDA-lile scaling.
|
|
|
|
// This is as fast as it gets.
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Re-map color indices from wall texture column
|
|
|
|
// using a lighting/special effects LUT.
|
|
|
|
*dest = colormap[source[(frac>>FRACBITS)&mask]];
|
|
|
|
|
|
|
|
dest += pitch;
|
|
|
|
frac += fracstep;
|
|
|
|
|
|
|
|
} while (--count);
|
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
#endif // USEASM
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
|
|
|
|
// [RH] Same as R_DrawColumnP_C except that it doesn't do any colormapping.
|
|
|
|
// Used by the sky drawer because the sky is always fullbright.
|
|
|
|
void R_StretchColumnP_C (void)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
byte* dest;
|
|
|
|
fixed_t frac;
|
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
|
|
|
|
|
|
|
if (count < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1999-02-17 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height) {
|
1999-02-17 00:00:00 +00:00
|
|
|
Printf (PRINT_HIGH, "R_StretchColumnP_C: %i to %i at %i\n", dc_yl, dc_yh, dc_x);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dest = ylookup[dc_yl] + columnofs[dc_x];
|
|
|
|
fracstep = dc_iscale;
|
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
|
|
|
|
|
|
|
{
|
|
|
|
int mask = dc_mask;
|
|
|
|
byte *source = dc_source;
|
|
|
|
int pitch = dc_pitch;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
*dest = source[(frac>>FRACBITS)&mask];
|
|
|
|
dest += pitch;
|
|
|
|
frac += fracstep;
|
|
|
|
} while (--count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Just fills a column with a color
|
|
|
|
void R_FillColumnP (void)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
byte* dest;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
|
|
|
|
|
|
|
if (count < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1999-02-17 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height) {
|
1999-02-17 00:00:00 +00:00
|
|
|
Printf (PRINT_HIGH, "R_StretchColumnP_C: %i to %i at %i\n", dc_yl, dc_yh, dc_x);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dest = ylookup[dc_yl] + columnofs[dc_x];
|
|
|
|
|
|
|
|
{
|
|
|
|
int pitch = dc_pitch;
|
|
|
|
byte color = dc_color;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
*dest = color;
|
|
|
|
dest += pitch;
|
|
|
|
} while (--count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
//
|
|
|
|
// Spectre/Invisibility.
|
|
|
|
//
|
1998-04-07 00:00:00 +00:00
|
|
|
// [RH] FUZZTABLE changed from 50 to 64
|
|
|
|
#define FUZZTABLE 64
|
1999-02-21 00:00:00 +00:00
|
|
|
#define FUZZOFF (screen.pitch)
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
int fuzzoffset[FUZZTABLE];
|
|
|
|
/*
|
|
|
|
FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,
|
|
|
|
FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,
|
|
|
|
FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,
|
|
|
|
FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,
|
|
|
|
FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,
|
|
|
|
FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,
|
|
|
|
FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const signed char fuzzinit[FUZZTABLE] = {
|
1998-04-07 00:00:00 +00:00
|
|
|
1,-1, 1,-1, 1, 1,-1, 1,
|
|
|
|
1,-1, 1, 1, 1,-1, 1, 1,
|
|
|
|
1,-1,-1,-1,-1, 1,-1,-1,
|
|
|
|
1, 1, 1, 1,-1, 1,-1, 1,
|
|
|
|
1,-1,-1, 1, 1,-1,-1,-1,
|
|
|
|
-1, 1, 1, 1, 1,-1, 1, 1,
|
|
|
|
-1, 1, 1, 1,-1, 1, 1, 1,
|
|
|
|
-1, 1, 1,-1, 1, 1,-1, 1
|
1998-04-07 00:00:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
int fuzzpos = 0;
|
|
|
|
|
|
|
|
void R_InitFuzzTable (void)
|
|
|
|
{
|
|
|
|
int i;
|
1998-07-14 00:00:00 +00:00
|
|
|
int fuzzoff;
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
V_LockScreen (&screen);
|
1998-07-14 00:00:00 +00:00
|
|
|
fuzzoff = FUZZOFF << detailyshift;
|
1999-02-21 00:00:00 +00:00
|
|
|
V_UnlockScreen (&screen);
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
for (i = 0; i < FUZZTABLE; i++)
|
1998-07-14 00:00:00 +00:00
|
|
|
fuzzoffset[i] = fuzzinit[i] * fuzzoff;
|
1998-04-07 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
#ifndef USEASM
|
1998-04-07 00:00:00 +00:00
|
|
|
//
|
|
|
|
// Framebuffer postprocessing.
|
|
|
|
// Creates a fuzzy image by copying pixels
|
|
|
|
// from adjacent ones to left and right.
|
|
|
|
// Used with an all black colormap, this
|
|
|
|
// could create the SHADOW effect,
|
|
|
|
// i.e. spectres and invisible players.
|
|
|
|
//
|
1999-02-17 00:00:00 +00:00
|
|
|
void R_DrawFuzzColumnP_C (void)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
byte *dest;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
// Adjust borders. Low...
|
|
|
|
if (!dc_yl)
|
1998-04-07 00:00:00 +00:00
|
|
|
dc_yl = 1;
|
|
|
|
|
|
|
|
// .. and high.
|
1999-02-17 00:00:00 +00:00
|
|
|
if (dc_yh == realviewheight-1)
|
|
|
|
dc_yh = realviewheight - 2;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Zero length.
|
1999-02-17 00:00:00 +00:00
|
|
|
if (count < 0)
|
|
|
|
return;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
count++;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
|
|
|
|| dc_yl < 0 || dc_yh >= screen.height)
|
1998-04-07 00:00:00 +00:00
|
|
|
{
|
1998-07-14 00:00:00 +00:00
|
|
|
I_Error ("R_DrawFuzzColumnP_C: %i to %i at %i",
|
1998-04-07 00:00:00 +00:00
|
|
|
dc_yl, dc_yh, dc_x);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
dest = ylookup[dc_yl] + columnofs[dc_x];
|
|
|
|
|
|
|
|
// Looks like an attempt at dithering,
|
|
|
|
// using the colormap #6 (of 0-31, a bit
|
|
|
|
// brighter than average).
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
// [RH] Make local copies of global vars to try and improve
|
|
|
|
// the optimizations made by the compiler.
|
|
|
|
int pitch = dc_pitch;
|
|
|
|
int fuzz = fuzzpos;
|
|
|
|
byte *map = DefaultPalette->maps.colormaps + 6*256;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Lookup framebuffer, and retrieve
|
|
|
|
// a pixel that is either one column
|
|
|
|
// left or right of the current one.
|
|
|
|
// Add index from colormap to index.
|
|
|
|
*dest = map[dest[fuzzoffset[fuzz]]];
|
|
|
|
|
|
|
|
// Clamp table lookup index.
|
|
|
|
fuzz = (fuzz + 1) & (FUZZTABLE - 1);
|
|
|
|
|
|
|
|
dest += pitch;
|
|
|
|
} while (--count);
|
|
|
|
|
|
|
|
fuzzpos = (fuzz + 3) & (FUZZTABLE - 1);
|
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
#endif // USEASM
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
//
|
|
|
|
// R_DrawTranlucentColumn
|
|
|
|
//
|
1998-04-07 00:00:00 +00:00
|
|
|
byte *dc_transmap;
|
|
|
|
|
|
|
|
#ifndef USEASM
|
1999-02-17 00:00:00 +00:00
|
|
|
void R_DrawTranslucentColumnP_C (void)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
byte* dest;
|
1998-04-07 00:00:00 +00:00
|
|
|
fixed_t frac;
|
1999-02-17 00:00:00 +00:00
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
|
|
|
if (count < 0)
|
|
|
|
return;
|
|
|
|
count++;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1998-04-07 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height)
|
1998-04-07 00:00:00 +00:00
|
|
|
{
|
1998-07-14 00:00:00 +00:00
|
|
|
I_Error ( "R_DrawTranslucentColumnP_C: %i to %i at %i",
|
1998-04-07 00:00:00 +00:00
|
|
|
dc_yl, dc_yh, dc_x);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
dest = ylookup[dc_yl] + columnofs[dc_x];
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
fracstep = dc_iscale;
|
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
byte *transmap = dc_transmap;
|
|
|
|
byte *colormap = dc_colormap;
|
|
|
|
byte *source = dc_source;
|
|
|
|
int mask = dc_mask;
|
|
|
|
int pitch = dc_pitch;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
*dest = transmap[colormap[source[(frac>>FRACBITS)&mask]] | ((*dest)<<8)];
|
|
|
|
dest += pitch;
|
|
|
|
|
|
|
|
frac += fracstep;
|
|
|
|
} while (--count);
|
|
|
|
}
|
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
#endif // USEASM
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// R_DrawTranslatedColumn
|
|
|
|
// Used to draw player sprites
|
|
|
|
// with the green colorramp mapped to others.
|
|
|
|
// Could be used with different translation
|
|
|
|
// tables, e.g. the lighter colored version
|
|
|
|
// of the BaronOfHell, the HellKnight, uses
|
|
|
|
// identical sprites, kinda brightened up.
|
|
|
|
//
|
|
|
|
byte* dc_translation;
|
|
|
|
byte* translationtables;
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
void R_DrawTranslatedColumnP_C (void)
|
1998-04-07 00:00:00 +00:00
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
int count;
|
|
|
|
byte* dest;
|
1998-04-07 00:00:00 +00:00
|
|
|
fixed_t frac;
|
1999-02-17 00:00:00 +00:00
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
1998-04-07 00:00:00 +00:00
|
|
|
if (count < 0)
|
1999-02-17 00:00:00 +00:00
|
|
|
return;
|
|
|
|
count++;
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1998-04-07 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height)
|
1998-04-07 00:00:00 +00:00
|
|
|
{
|
1998-07-14 00:00:00 +00:00
|
|
|
I_Error ( "R_DrawTranslatedColumnP_C: %i to %i at %i",
|
1998-04-07 00:00:00 +00:00
|
|
|
dc_yl, dc_yh, dc_x);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
dest = ylookup[dc_yl] + columnofs[dc_x];
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Looks familiar.
|
1999-02-17 00:00:00 +00:00
|
|
|
fracstep = dc_iscale;
|
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Here we do an additional index re-mapping.
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
// [RH] Local copies of global vars to improve compiler optimizations
|
|
|
|
byte *colormap = dc_colormap;
|
|
|
|
byte *translation = dc_translation;
|
|
|
|
byte *source = dc_source;
|
|
|
|
int pitch = dc_pitch;
|
|
|
|
int mask = dc_mask;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Translation tables are used
|
|
|
|
// to map certain colorramps to other ones,
|
|
|
|
// used with PLAY sprites.
|
|
|
|
// Thus the "green" ramp of the player 0 sprite
|
|
|
|
// is mapped to gray, red, black/indigo.
|
|
|
|
*dest = colormap[translation[source[(frac>>FRACBITS) & mask]]];
|
|
|
|
dest += pitch;
|
|
|
|
|
|
|
|
frac += fracstep;
|
|
|
|
} while (--count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Draw a column that is both translated and translucent
|
|
|
|
void R_DrawTlatedLucentColumnP_C (void)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
byte* dest;
|
|
|
|
fixed_t frac;
|
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
|
|
|
if (count < 0)
|
|
|
|
return;
|
|
|
|
count++;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1999-02-17 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height)
|
1999-02-17 00:00:00 +00:00
|
|
|
{
|
|
|
|
I_Error ( "R_DrawTlatedLucentColumnP_C: %i to %i at %i",
|
|
|
|
dc_yl, dc_yh, dc_x);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dest = ylookup[dc_yl] + columnofs[dc_x];
|
|
|
|
|
|
|
|
fracstep = dc_iscale;
|
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
|
|
|
|
|
|
|
{
|
|
|
|
byte *translation = dc_translation;
|
|
|
|
byte *transmap = dc_transmap;
|
|
|
|
byte *colormap = dc_colormap;
|
|
|
|
byte *source = dc_source;
|
|
|
|
int mask = dc_mask;
|
|
|
|
int pitch = dc_pitch;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
*dest = transmap[colormap[translation[source[(frac>>FRACBITS)&mask]]] | ((*dest)<<8)];
|
|
|
|
dest += pitch;
|
|
|
|
|
|
|
|
frac += fracstep;
|
|
|
|
} while (--count);
|
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// R_DrawSpan
|
|
|
|
// With DOOM style restrictions on view orientation,
|
|
|
|
// the floors and ceilings consist of horizontal slices
|
|
|
|
// or spans with constant z depth.
|
|
|
|
// However, rotation around the world z axis is possible,
|
|
|
|
// thus this mapping, while simpler and faster than
|
|
|
|
// perspective correct texture mapping, has to traverse
|
|
|
|
// the texture at an angle in all but a few cases.
|
|
|
|
// In consequence, flats are not stored by column (like walls),
|
|
|
|
// and the inner loop has to step in texture space u and v.
|
|
|
|
//
|
1998-07-14 00:00:00 +00:00
|
|
|
int ds_colsize=0x12345678; // [RH] Distance between columns
|
|
|
|
int ds_colshift=1; // [RH] (1<<ds_colshift)=ds_colsize
|
1999-02-17 00:00:00 +00:00
|
|
|
int ds_color; // [RH] color for non-textured spans
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
int ds_y;
|
|
|
|
int ds_x1;
|
|
|
|
int ds_x2;
|
|
|
|
|
|
|
|
lighttable_t* ds_colormap;
|
|
|
|
|
|
|
|
fixed_t ds_xfrac;
|
|
|
|
fixed_t ds_yfrac;
|
|
|
|
fixed_t ds_xstep;
|
|
|
|
fixed_t ds_ystep;
|
|
|
|
|
|
|
|
// start of a 64*64 tile image
|
|
|
|
byte* ds_source;
|
|
|
|
|
|
|
|
// just for profiling
|
|
|
|
int dscount;
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Draws the actual span.
|
1998-07-14 00:00:00 +00:00
|
|
|
void R_DrawSpanP (void)
|
1998-04-07 00:00:00 +00:00
|
|
|
{
|
|
|
|
fixed_t xfrac;
|
|
|
|
fixed_t yfrac;
|
|
|
|
byte* dest;
|
|
|
|
int count;
|
|
|
|
int spot;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
|
|
|
if (ds_x2 < ds_x1
|
|
|
|
|| ds_x1<0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| ds_x2>=screen.width
|
|
|
|
|| ds_y>screen.height)
|
1998-04-07 00:00:00 +00:00
|
|
|
{
|
|
|
|
I_Error( "R_DrawSpan: %i to %i at %i",
|
|
|
|
ds_x1,ds_x2,ds_y);
|
|
|
|
}
|
|
|
|
// dscount++;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
xfrac = ds_xfrac;
|
|
|
|
yfrac = ds_yfrac;
|
|
|
|
|
|
|
|
dest = ylookup[ds_y] + columnofs[ds_x1];
|
|
|
|
|
|
|
|
// We do not check for zero spans here?
|
1998-07-14 00:00:00 +00:00
|
|
|
count = ds_x2 - ds_x1 + 1;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
/* [Petteri] Do optimized loop if enough to do: */
|
|
|
|
#ifdef USEASM
|
1998-07-14 00:00:00 +00:00
|
|
|
if ( count > 1 ) {
|
1998-04-07 00:00:00 +00:00
|
|
|
DrawSpan8Loop(xfrac, yfrac, count & (~1), dest);
|
1998-04-07 00:00:00 +00:00
|
|
|
if ( (count & 1) == 0 )
|
|
|
|
return;
|
|
|
|
xfrac += ds_xstep * (count & (~1));
|
|
|
|
yfrac += ds_ystep * (count & (~1));
|
1998-07-14 00:00:00 +00:00
|
|
|
dest += (count & (~1)) << ds_colshift;
|
|
|
|
// count = 1;
|
1998-04-07 00:00:00 +00:00
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
#else
|
1998-04-07 00:00:00 +00:00
|
|
|
do {
|
1998-07-14 00:00:00 +00:00
|
|
|
#endif
|
1998-04-07 00:00:00 +00:00
|
|
|
// Current texture index in u,v.
|
|
|
|
spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
|
|
|
|
|
|
|
|
// Lookup pixel from flat texture tile,
|
|
|
|
// re-index using light/colormap.
|
1998-07-14 00:00:00 +00:00
|
|
|
*dest = ds_colormap[ds_source[spot]];
|
|
|
|
#ifndef USEASM
|
|
|
|
dest += ds_colsize;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Next step in u,v.
|
|
|
|
xfrac += ds_xstep;
|
|
|
|
yfrac += ds_ystep;
|
1998-07-14 00:00:00 +00:00
|
|
|
} while (--count);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
// [RH] Just fill a span with a color
|
|
|
|
void R_FillSpan (void)
|
|
|
|
{
|
|
|
|
byte *dest;
|
|
|
|
int count;
|
|
|
|
int color = ds_color;
|
|
|
|
int colsize = ds_colsize;
|
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
|
|
|
if (ds_x2 < ds_x1
|
|
|
|
|| ds_x1<0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| ds_x2>=screen.width
|
|
|
|
|| ds_y>screen.height)
|
1999-02-17 00:00:00 +00:00
|
|
|
{
|
|
|
|
I_Error( "R_FillSpan: %i to %i at %i",
|
|
|
|
ds_x1,ds_x2,ds_y);
|
|
|
|
}
|
|
|
|
// dscount++;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dest = ylookup[ds_y] + columnofs[ds_x1];
|
|
|
|
|
|
|
|
count = ds_x2 - ds_x1 + 1;
|
|
|
|
|
|
|
|
do {
|
|
|
|
*dest = color;
|
|
|
|
dest += colsize;
|
|
|
|
} while (--count);
|
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************/
|
|
|
|
/* */
|
|
|
|
/* [RH] ARGB8888 drawers (C versions) */
|
|
|
|
/* */
|
|
|
|
/****************************************/
|
|
|
|
|
1998-12-22 00:00:00 +00:00
|
|
|
#define dc_shademap ((unsigned int *)dc_colormap)
|
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
void R_DrawColumnD_C (void)
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
int count;
|
|
|
|
unsigned int* dest;
|
1998-07-14 00:00:00 +00:00
|
|
|
fixed_t frac;
|
1999-02-17 00:00:00 +00:00
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
// Zero length, column does not exceed a pixel.
|
1999-02-17 00:00:00 +00:00
|
|
|
if (count < 0)
|
|
|
|
return;
|
|
|
|
count++;
|
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1998-07-14 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height) {
|
1999-02-17 00:00:00 +00:00
|
|
|
Printf (PRINT_HIGH, "R_DrawColumnD_C: %i to %i at %i\n", dc_yl, dc_yh, dc_x);
|
1998-07-14 00:00:00 +00:00
|
|
|
return;
|
|
|
|
}
|
1999-02-17 00:00:00 +00:00
|
|
|
#endif
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
dest = (unsigned int *)(ylookup[dc_yl] + columnofs[dc_x]);
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
fracstep = dc_iscale;
|
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
unsigned int *shademap = dc_shademap;
|
|
|
|
byte *source = dc_source;
|
|
|
|
int mask = dc_mask;
|
|
|
|
int pitch = dc_pitch >> 2;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
*dest = shademap[source[(frac>>FRACBITS)&mask]];
|
|
|
|
|
|
|
|
dest += pitch;
|
|
|
|
frac += fracstep;
|
|
|
|
|
|
|
|
} while (--count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_DrawFuzzColumnD_C (void)
|
|
|
|
{
|
1998-07-14 00:00:00 +00:00
|
|
|
int count;
|
|
|
|
unsigned int* dest;
|
|
|
|
|
|
|
|
// Adjust borders. Low...
|
|
|
|
if (!dc_yl)
|
|
|
|
dc_yl = 1;
|
|
|
|
|
|
|
|
// .. and high.
|
|
|
|
if (dc_yh == realviewheight-1)
|
|
|
|
dc_yh = realviewheight - 2;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
|
|
|
|
|
|
|
// Zero length.
|
|
|
|
if (count < 0)
|
|
|
|
return;
|
1999-02-17 00:00:00 +00:00
|
|
|
count++;
|
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
|
|
|
|| dc_yl < 0 || dc_yh >= screen.height)
|
1998-07-14 00:00:00 +00:00
|
|
|
{
|
|
|
|
I_Error ("R_DrawFuzzColumnD_C: %i to %i at %i",
|
|
|
|
dc_yl, dc_yh, dc_x);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dest = (unsigned int *)(ylookup[dc_yl] + columnofs[dc_x]);
|
|
|
|
|
|
|
|
// [RH] This is actually slightly brighter than
|
|
|
|
// the indexed version, but it's close enough.
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
int fuzz = fuzzpos;
|
|
|
|
int pitch = dc_pitch >> 2;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
unsigned int work = dest[fuzzoffset[fuzz]>>2];
|
|
|
|
*dest = work - ((work >> 2) & 0x3f3f3f);
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
// Clamp table lookup index.
|
|
|
|
fuzz = (fuzz + 1) & (FUZZTABLE - 1);
|
|
|
|
|
|
|
|
dest += pitch;
|
|
|
|
} while (--count);
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
fuzzpos = (fuzz + 3) & (FUZZTABLE - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void R_DrawTranslucentColumnD_C (void)
|
1998-07-14 00:00:00 +00:00
|
|
|
{
|
|
|
|
int count;
|
|
|
|
unsigned int* dest;
|
|
|
|
fixed_t frac;
|
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
|
|
|
if (count < 0)
|
|
|
|
return;
|
1999-02-17 00:00:00 +00:00
|
|
|
count++;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1998-07-14 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height)
|
1998-07-14 00:00:00 +00:00
|
|
|
{
|
|
|
|
I_Error ( "R_DrawTranslucentColumnD_C: %i to %i at %i",
|
|
|
|
dc_yl, dc_yh, dc_x);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dest = (unsigned int *)(ylookup[dc_yl] + columnofs[dc_x]);
|
|
|
|
|
|
|
|
fracstep = dc_iscale;
|
1999-02-17 00:00:00 +00:00
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
unsigned int *shademap = dc_shademap;
|
|
|
|
byte *source = dc_source;
|
|
|
|
int mask = dc_mask;
|
|
|
|
int pitch = dc_pitch >> 2;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
*dest = ((*dest >> 1) & 0x7f7f7f) +
|
|
|
|
((shademap[source[(frac>>FRACBITS)&mask]] >> 1) & 0x7f7f7f);
|
|
|
|
dest += pitch;
|
|
|
|
|
|
|
|
frac += fracstep;
|
|
|
|
} while (--count);
|
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void R_DrawTranslatedColumnD_C (void)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
unsigned int* dest;
|
|
|
|
fixed_t frac;
|
|
|
|
fixed_t fracstep;
|
|
|
|
|
|
|
|
count = dc_yh - dc_yl;
|
|
|
|
if (count < 0)
|
|
|
|
return;
|
1999-02-17 00:00:00 +00:00
|
|
|
count++;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
1999-02-21 00:00:00 +00:00
|
|
|
if (dc_x >= screen.width
|
1998-07-14 00:00:00 +00:00
|
|
|
|| dc_yl < 0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| dc_yh >= screen.height)
|
1998-07-14 00:00:00 +00:00
|
|
|
{
|
|
|
|
I_Error ( "R_DrawTranslatedColumnD_C: %i to %i at %i",
|
|
|
|
dc_yl, dc_yh, dc_x);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
dest = (unsigned int *)(ylookup[dc_yl] + columnofs[dc_x]);
|
|
|
|
|
|
|
|
fracstep = dc_iscale;
|
|
|
|
frac = dc_texturemid + (dc_yl-centery)*fracstep;
|
|
|
|
|
|
|
|
// Here we do an additional index re-mapping.
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
byte *source = dc_source;
|
|
|
|
unsigned int *shademap = dc_shademap;
|
|
|
|
byte *translation = dc_translation;
|
|
|
|
int mask = dc_mask;
|
|
|
|
int pitch = dc_pitch >> 2;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
*dest = shademap[translation[source[(frac>>FRACBITS) & mask]]];
|
|
|
|
dest += pitch;
|
|
|
|
|
|
|
|
frac += fracstep;
|
|
|
|
} while (--count);
|
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
void R_DrawSpanD (void)
|
1998-07-14 00:00:00 +00:00
|
|
|
{
|
|
|
|
fixed_t xfrac;
|
1999-02-17 00:00:00 +00:00
|
|
|
fixed_t yfrac;
|
|
|
|
unsigned int* dest;
|
1998-07-14 00:00:00 +00:00
|
|
|
int count;
|
1999-02-17 00:00:00 +00:00
|
|
|
int spot;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
#ifdef RANGECHECK
|
|
|
|
if (ds_x2 < ds_x1
|
|
|
|
|| ds_x1<0
|
1999-02-21 00:00:00 +00:00
|
|
|
|| ds_x2>=screen.width
|
|
|
|
|| ds_y>screen.height)
|
1998-07-14 00:00:00 +00:00
|
|
|
{
|
|
|
|
I_Error( "R_DrawSpan: %i to %i at %i",
|
|
|
|
ds_x1,ds_x2,ds_y);
|
|
|
|
}
|
1999-02-17 00:00:00 +00:00
|
|
|
// dscount++;
|
|
|
|
#endif
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
xfrac = ds_xfrac;
|
|
|
|
yfrac = ds_yfrac;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
dest = (unsigned int *)(ylookup[ds_y] + columnofs[ds_x1]);
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
count = ds_x2 - ds_x1 + 1;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
{
|
|
|
|
byte *source = ds_source;
|
|
|
|
unsigned int *shademap = (unsigned int *)ds_colormap;
|
|
|
|
int colsize = ds_colsize >> 2;
|
|
|
|
int xstep = ds_xstep;
|
|
|
|
int ystep = ds_ystep;
|
|
|
|
|
|
|
|
do {
|
|
|
|
spot = ((yfrac>>(16-6))&(63*64)) + ((xfrac>>16)&63);
|
|
|
|
|
|
|
|
// Lookup pixel from flat texture tile,
|
|
|
|
// re-index using light/colormap.
|
|
|
|
*dest = shademap[source[spot]];
|
|
|
|
dest += colsize;
|
|
|
|
|
|
|
|
// Next step in u,v.
|
|
|
|
xfrac += xstep;
|
|
|
|
yfrac += ystep;
|
|
|
|
} while (--count);
|
|
|
|
}
|
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************/
|
|
|
|
/****************************************************/
|
|
|
|
|
|
|
|
//
|
|
|
|
// R_InitTranslationTables
|
|
|
|
// Creates the translation tables to map
|
|
|
|
// the green color ramp to gray, brown, red.
|
|
|
|
// Assumes a given structure of the PLAYPAL.
|
|
|
|
// Could be read from a lump instead.
|
|
|
|
//
|
|
|
|
extern byte *Ranges;
|
|
|
|
|
|
|
|
void R_InitTranslationTables (void)
|
|
|
|
{
|
|
|
|
static const char ranges[11][8] = {
|
|
|
|
"CRBRICK",
|
|
|
|
"CRTAN",
|
|
|
|
"CRGRAY",
|
|
|
|
"CRGREEN",
|
|
|
|
"CRBROWN",
|
|
|
|
"CRGOLD",
|
|
|
|
"CRRED",
|
1999-02-17 00:00:00 +00:00
|
|
|
"CRBLUE2",
|
1998-07-14 00:00:00 +00:00
|
|
|
"CRORANGE",
|
|
|
|
"CRYELLOW",
|
1999-02-17 00:00:00 +00:00
|
|
|
"CRBLUE"
|
1998-07-14 00:00:00 +00:00
|
|
|
};
|
|
|
|
int i;
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
translationtables = Z_Malloc (256*(MAXPLAYERS+3+11)+255, PU_STATIC, 0);
|
1998-07-14 00:00:00 +00:00
|
|
|
translationtables = (byte *)(( (int)translationtables + 255 )& ~255);
|
|
|
|
|
|
|
|
// [RH] Each player now gets their own translation table
|
|
|
|
// (soon to be palettes). These are set up during
|
|
|
|
// netgame arbitration and as-needed rather than
|
|
|
|
// in here. We do, however load some text translation
|
|
|
|
// tables from our PWAD (ala BOOM).
|
|
|
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
translationtables[i] = i;
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
for (i = 1; i < MAXPLAYERS+3; i++)
|
1998-07-14 00:00:00 +00:00
|
|
|
memcpy (translationtables + i*256, translationtables, 256);
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
// create translation tables for dehacked patches that expect them
|
|
|
|
for (i = 0x70; i < 0x80; i++) {
|
|
|
|
// map green ramp to gray, brown, red
|
|
|
|
translationtables[i+(MAXPLAYERS+0)*256] = 0x60 + (i&0xf);
|
|
|
|
translationtables[i+(MAXPLAYERS+1)*256] = 0x40 + (i&0xf);
|
|
|
|
translationtables[i+(MAXPLAYERS+2)*256] = 0x20 + (i&0xf);
|
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
Ranges = translationtables + (MAXPLAYERS+3)*256;
|
1998-07-14 00:00:00 +00:00
|
|
|
for (i = 0; i < 11; i++)
|
|
|
|
W_ReadLump (W_GetNumForName (ranges[i]), Ranges + 256 * i);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Create a player's translation table based on
|
|
|
|
// a given mid-range color.
|
|
|
|
void R_BuildPlayerTranslation (int player, int color)
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
palette_t *pal = GetDefaultPalette();
|
|
|
|
byte *table = &translationtables[player * 256];
|
|
|
|
int i;
|
|
|
|
float r = (float)RPART(color) / 255.0f;
|
|
|
|
float g = (float)GPART(color) / 255.0f;
|
|
|
|
float b = (float)BPART(color) / 255.0f;
|
|
|
|
float h, s, v;
|
|
|
|
float sdelta, vdelta;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
RGBtoHSV (r, g, b, &h, &s, &v);
|
|
|
|
|
|
|
|
s -= 0.23f;
|
|
|
|
if (s < 0.0f)
|
|
|
|
s = 0.0f;
|
|
|
|
sdelta = 0.014375f;
|
|
|
|
|
|
|
|
v += 0.1f;
|
|
|
|
if (v > 1.0f)
|
|
|
|
v = 1.0f;
|
|
|
|
vdelta = -0.05882f;
|
|
|
|
|
|
|
|
for (i = 0x70; i < 0x80; i++) {
|
|
|
|
HSVtoRGB (&r, &g, &b, h, s, v);
|
|
|
|
table[i] = BestColor (pal->basecolors,
|
|
|
|
(int)(r * 255.0f),
|
|
|
|
(int)(g * 255.0f),
|
|
|
|
(int)(b * 255.0f),
|
|
|
|
pal->numcolors);
|
|
|
|
s += sdelta;
|
|
|
|
if (s > 1.0f) {
|
|
|
|
s = 1.0f;
|
|
|
|
sdelta = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
v += vdelta;
|
|
|
|
if (v < 0.0f) {
|
|
|
|
v = 0.0f;
|
|
|
|
vdelta = 0.0f;
|
1998-07-14 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// R_InitBuffer
|
|
|
|
// Creats lookup tables that avoid
|
1999-02-17 00:00:00 +00:00
|
|
|
// multiplies and other hassles
|
1998-04-07 00:00:00 +00:00
|
|
|
// for getting the framebuffer address
|
|
|
|
// of a pixel to draw.
|
|
|
|
//
|
1998-07-14 00:00:00 +00:00
|
|
|
void R_InitBuffer (int width, int height)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
byte *buffer;
|
|
|
|
int pitch;
|
|
|
|
int xshift;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Handle resize,
|
|
|
|
// e.g. smaller view windows
|
|
|
|
// with border and/or status bar.
|
1999-02-21 00:00:00 +00:00
|
|
|
viewwindowx = (screen.width-(width<<detailxshift))>>1;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1998-07-14 00:00:00 +00:00
|
|
|
// [RH] Adjust column offset according to bytes per pixel
|
|
|
|
// and detail mode
|
1999-02-21 00:00:00 +00:00
|
|
|
xshift = (screen.is8bit) ? 0 : 2;
|
1998-07-14 00:00:00 +00:00
|
|
|
xshift += detailxshift;
|
|
|
|
|
|
|
|
// Column offset. For windows
|
1999-02-17 00:00:00 +00:00
|
|
|
for (i = 0; i < width; i++)
|
|
|
|
columnofs[i] = viewwindowx + (i << xshift);
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
// Same with base row offset.
|
1999-02-21 00:00:00 +00:00
|
|
|
if ((width<<detailxshift) == screen.width)
|
1998-07-14 00:00:00 +00:00
|
|
|
viewwindowy = 0;
|
|
|
|
else
|
|
|
|
viewwindowy = (ST_Y-(height<<detailyshift)) >> 1;
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
V_LockScreen (&screen);
|
|
|
|
buffer = screen.buffer;
|
|
|
|
pitch = screen.pitch;
|
|
|
|
V_UnlockScreen (&screen);
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
// Precalculate all row offsets.
|
|
|
|
for (i=0 ; i<height ; i++)
|
|
|
|
ylookup[i] = buffer + ((i<<detailyshift)+viewwindowy)*pitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
void R_DrawBorder (int x1, int y1, int x2, int y2)
|
1998-07-14 00:00:00 +00:00
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
int lump;
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
lump = R_FlatNumForName ((gamemode == commercial) ? "GRNROCK" : "FLOOR7_2");
|
1999-02-21 00:00:00 +00:00
|
|
|
V_FlatFill (x1 & ~63, y1, x2, y2, &screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpNum (lump + firstflat, PU_CACHE));
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
|
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= R_DrawViewBorder
|
|
|
|
=
|
|
|
|
= Draws the border around the view for different size windows
|
|
|
|
==================
|
|
|
|
*/
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
BOOL BorderNeedRefresh;
|
|
|
|
|
|
|
|
void V_MarkRect (int x, int y, int width, int height);
|
1998-07-14 00:00:00 +00:00
|
|
|
|
|
|
|
void R_DrawViewBorder (void)
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
int x, y;
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1998-04-07 00:00:00 +00:00
|
|
|
// [RH] Redraw the status bar if SCREENWIDTH > status bar width.
|
|
|
|
// Will draw borders around itself, too.
|
1999-02-21 00:00:00 +00:00
|
|
|
if (screen.width > 320)
|
1999-02-17 00:00:00 +00:00
|
|
|
{
|
|
|
|
SB_state = -1;
|
1998-04-07 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
if (realviewwidth == screen.width) {
|
1998-07-14 00:00:00 +00:00
|
|
|
return;
|
1998-04-07 00:00:00 +00:00
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
R_DrawBorder (0, 0, screen.width, viewwindowy);
|
1999-02-17 00:00:00 +00:00
|
|
|
R_DrawBorder (0, viewwindowy, viewwindowx, realviewheight + viewwindowy);
|
1999-02-21 00:00:00 +00:00
|
|
|
R_DrawBorder (viewwindowx + realviewwidth, viewwindowy, screen.width, realviewheight + viewwindowy);
|
|
|
|
R_DrawBorder (0, viewwindowy + realviewheight, screen.width, ST_Y);
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
for (x = viewwindowx; x < viewwindowx + realviewwidth; x += 8)
|
|
|
|
{
|
1999-02-21 00:00:00 +00:00
|
|
|
V_DrawPatch (x, viewwindowy - 8, &screen, W_CacheLumpName ("brdr_t", PU_CACHE));
|
|
|
|
V_DrawPatch (x, viewwindowy + realviewheight, &screen, W_CacheLumpName ("brdr_b", PU_CACHE));
|
1999-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
for (y = viewwindowy; y < viewwindowy + realviewheight; y += 8)
|
|
|
|
{
|
1999-02-21 00:00:00 +00:00
|
|
|
V_DrawPatch (viewwindowx - 8, y, &screen, W_CacheLumpName ("brdr_l", PU_CACHE));
|
|
|
|
V_DrawPatch (viewwindowx + realviewwidth, y, &screen, W_CacheLumpName ("brdr_r", PU_CACHE));
|
1999-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
// Draw beveled edge.
|
|
|
|
V_DrawPatch (viewwindowx-8,
|
|
|
|
viewwindowy-8,
|
1999-02-21 00:00:00 +00:00
|
|
|
&screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName ("brdr_tl",PU_CACHE));
|
|
|
|
|
|
|
|
V_DrawPatch (viewwindowx+realviewwidth,
|
|
|
|
viewwindowy-8,
|
1999-02-21 00:00:00 +00:00
|
|
|
&screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName ("brdr_tr",PU_CACHE));
|
|
|
|
|
|
|
|
V_DrawPatch (viewwindowx-8,
|
|
|
|
viewwindowy+realviewheight,
|
1999-02-21 00:00:00 +00:00
|
|
|
&screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName ("brdr_bl",PU_CACHE));
|
|
|
|
|
|
|
|
V_DrawPatch (viewwindowx+realviewwidth,
|
|
|
|
viewwindowy+realviewheight,
|
1999-02-21 00:00:00 +00:00
|
|
|
&screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName ("brdr_br",PU_CACHE));
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
V_MarkRect (0,0,screen.width, ST_Y);
|
1999-02-17 00:00:00 +00:00
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= R_DrawTopBorder
|
|
|
|
=
|
|
|
|
= Draws the top border around the view for different size windows
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
BOOL BorderTopRefresh;
|
|
|
|
|
|
|
|
void R_DrawTopBorder (void)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
if (realviewwidth == screen.width)
|
1999-02-17 00:00:00 +00:00
|
|
|
return;
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
R_DrawBorder (0, 0, screen.width, 34);
|
1998-07-14 00:00:00 +00:00
|
|
|
|
1999-02-17 00:00:00 +00:00
|
|
|
if (viewwindowy < 35)
|
|
|
|
{
|
|
|
|
for (x = viewwindowx; x < viewwindowx + realviewwidth; x += 8)
|
|
|
|
{
|
1999-02-21 00:00:00 +00:00
|
|
|
V_DrawPatch (x, viewwindowy-8, &screen, W_CacheLumpName("brdr_t", PU_CACHE));
|
1999-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
for (y = viewwindowy; y < 35; y += 8)
|
|
|
|
{
|
1999-02-21 00:00:00 +00:00
|
|
|
V_DrawPatch(viewwindowx-8, y, &screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName ("brdr_l", PU_CACHE));
|
1999-02-21 00:00:00 +00:00
|
|
|
V_DrawPatch(viewwindowx+realviewwidth, y, &screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName("brdr_r", PU_CACHE));
|
|
|
|
}
|
1998-04-07 00:00:00 +00:00
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
V_DrawPatch(viewwindowx-8, viewwindowy-8, &screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName("brdr_tl", PU_CACHE));
|
1999-02-21 00:00:00 +00:00
|
|
|
V_DrawPatch(viewwindowx+realviewwidth, viewwindowy-8, &screen,
|
1999-02-17 00:00:00 +00:00
|
|
|
W_CacheLumpName("brdr_tr", PU_CACHE));
|
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// [RH] Double pixels in the view window horizontally
|
|
|
|
// and/or vertically (or not at all).
|
|
|
|
void R_DetailDouble (void)
|
|
|
|
{
|
1999-02-17 00:00:00 +00:00
|
|
|
switch ((detailxshift << 1) | detailyshift) {
|
|
|
|
case 1: // y-double
|
|
|
|
{
|
1999-02-21 00:00:00 +00:00
|
|
|
int rowsize = realviewwidth << ((screen.is8bit) ? 0 : 2);
|
|
|
|
int pitch = screen.pitch;
|
1999-02-17 00:00:00 +00:00
|
|
|
int y;
|
|
|
|
byte *line;
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
line = screen.buffer + viewwindowy*pitch + viewwindowx;
|
1999-02-17 00:00:00 +00:00
|
|
|
for (y = 0; y < viewheight; y++, line += pitch<<1) {
|
|
|
|
memcpy (line+pitch, line, rowsize);
|
1998-07-14 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
1999-02-17 00:00:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: // x-double
|
|
|
|
{
|
|
|
|
int rowsize = realviewwidth >> 2;
|
1999-02-21 00:00:00 +00:00
|
|
|
int pitch = screen.pitch >> (2-detailyshift);
|
1999-02-17 00:00:00 +00:00
|
|
|
int y,x;
|
|
|
|
unsigned *line,a,b;
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
line = (unsigned *)(screen.buffer + viewwindowy*screen.pitch + viewwindowx);
|
1999-02-17 00:00:00 +00:00
|
|
|
for (y = 0; y < viewheight; y++, line += pitch) {
|
|
|
|
for (x = 0; x < rowsize; x += 2) {
|
|
|
|
a = line[x+0];
|
|
|
|
b = line[x+1];
|
|
|
|
a &= 0x00ff00ff;
|
|
|
|
b &= 0x00ff00ff;
|
|
|
|
line[x+0] = a | (a << 8);
|
|
|
|
line[x+1] = b | (b << 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: // x- and y-double
|
|
|
|
{
|
|
|
|
int rowsize = realviewwidth >> 2;
|
1999-02-21 00:00:00 +00:00
|
|
|
int pitch = screen.pitch >> (2-detailyshift);
|
|
|
|
int realpitch = screen.pitch >> 2;
|
1999-02-17 00:00:00 +00:00
|
|
|
int y,x;
|
|
|
|
unsigned *line,a,b;
|
|
|
|
|
1999-02-21 00:00:00 +00:00
|
|
|
line = (unsigned *)(screen.buffer + viewwindowy*screen.pitch + viewwindowx);
|
1999-02-17 00:00:00 +00:00
|
|
|
for (y = 0; y < viewheight; y++, line += pitch) {
|
|
|
|
for (x = 0; x < rowsize; x += 2) {
|
|
|
|
a = line[x+0];
|
|
|
|
b = line[x+1];
|
|
|
|
a &= 0x00ff00ff;
|
|
|
|
b &= 0x00ff00ff;
|
|
|
|
line[x+0] = a | (a << 8);
|
|
|
|
line[x+0+realpitch] = a | (a << 8);
|
|
|
|
line[x+1] = b | (b << 8);
|
|
|
|
line[x+1+realpitch] = b | (b << 8);
|
|
|
|
}
|
|
|
|
}
|
1998-07-14 00:00:00 +00:00
|
|
|
}
|
1999-02-17 00:00:00 +00:00
|
|
|
break;
|
1998-07-14 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Initialize the column drawer pointers
|
|
|
|
void R_InitColumnDrawers (BOOL is8bit)
|
|
|
|
{
|
|
|
|
if (is8bit) {
|
|
|
|
#ifdef USEASM
|
1999-02-21 00:00:00 +00:00
|
|
|
if (screen.height <= 240)
|
1999-02-17 00:00:00 +00:00
|
|
|
R_DrawColumn = R_DrawColumnP_Unrolled;
|
|
|
|
else
|
|
|
|
R_DrawColumn = R_DrawColumnP_ASM;
|
|
|
|
R_DrawColumnHoriz = R_DrawColumnHorizP_ASM;
|
1998-07-14 00:00:00 +00:00
|
|
|
R_DrawFuzzColumn = R_DrawFuzzColumnP_ASM;
|
|
|
|
R_DrawTranslucentColumn = R_DrawTranslucentColumnP_ASM;
|
|
|
|
R_DrawTranslatedColumn = R_DrawTranslatedColumnP_C;
|
1999-02-21 00:00:00 +00:00
|
|
|
if (screen.width <= 320)
|
1999-02-17 00:00:00 +00:00
|
|
|
R_DrawSpan = R_DrawSpanP_Unrolled;
|
|
|
|
else
|
|
|
|
R_DrawSpan = R_DrawSpanP;
|
1998-07-14 00:00:00 +00:00
|
|
|
#else
|
1999-02-17 00:00:00 +00:00
|
|
|
R_DrawColumnHoriz = R_DrawColumnHorizP_C;
|
1998-07-14 00:00:00 +00:00
|
|
|
R_DrawColumn = R_DrawColumnP_C;
|
|
|
|
R_DrawFuzzColumn = R_DrawFuzzColumnP_C;
|
|
|
|
R_DrawTranslucentColumn = R_DrawTranslucentColumnP_C;
|
|
|
|
R_DrawTranslatedColumn = R_DrawTranslatedColumnP_C;
|
|
|
|
R_DrawSpan = R_DrawSpanP;
|
1999-02-17 00:00:00 +00:00
|
|
|
#endif
|
1998-07-14 00:00:00 +00:00
|
|
|
} else {
|
|
|
|
#ifdef USEASM
|
1999-02-17 00:00:00 +00:00
|
|
|
R_DrawColumnHoriz = R_DrawColumnHorizP_ASM;
|
1998-07-14 00:00:00 +00:00
|
|
|
R_DrawColumn = R_DrawColumnD_C;
|
|
|
|
R_DrawFuzzColumn = R_DrawFuzzColumnD_C;
|
|
|
|
R_DrawTranslucentColumn = R_DrawTranslucentColumnD_C;
|
|
|
|
R_DrawTranslatedColumn = R_DrawTranslatedColumnD_C;
|
|
|
|
#else
|
1999-02-17 00:00:00 +00:00
|
|
|
R_DrawColumnHoriz = R_DrawColumnHorizP_C;
|
1998-07-14 00:00:00 +00:00
|
|
|
R_DrawColumn = R_DrawColumnD_C;
|
|
|
|
R_DrawFuzzColumn = R_DrawFuzzColumnD_C;
|
|
|
|
R_DrawTranslucentColumn = R_DrawTranslucentColumnD_C;
|
|
|
|
R_DrawTranslatedColumn = R_DrawTranslatedColumnD_C;
|
|
|
|
#endif
|
|
|
|
R_DrawSpan = R_DrawSpanD;
|
|
|
|
}
|
|
|
|
}
|