Mercurial > hg > forks > yadex
changeset 24:b2dc19044bf3
Import simple 3D preview, an old patch by Andrew Apted. Pressing 'R' key
enables it, use arrows etc. to move.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 24 Sep 2011 13:12:09 +0300 |
parents | ad9685c8cef1 |
children | 8eaf72e2041b |
files | GNUmakefile cache/pixlist docsrc/index.html src/editloop.cc src/gcolour1.cc src/gcolour2.cc src/gcolour2.h src/r_images.cc src/r_images.h src/r_render.cc src/r_render.h |
diffstat | 11 files changed, 1788 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/GNUmakefile Sat Sep 24 13:04:21 2011 +0300 +++ b/GNUmakefile Sat Sep 24 13:12:09 2011 +0300 @@ -159,7 +159,7 @@ s_misc s_prop s_slice s_split \ s_swapf s_vertices sanity scrnshot \ selbox selectn selpath selrect \ - serialnum spritdir sticker \ + serialnum spritdir sticker r_render r_images \ t_centre t_flags t_prop t_spin \ textures things trace v_centre \ v_merge v_polyg vectext verbmsg \ @@ -237,6 +237,7 @@ docsrc/legal.html \ docsrc/packagers_guide.html \ docsrc/palette.html \ + docsrc/preview.html \ docsrc/reporting.html \ docsrc/tips.html \ docsrc/trivia.html \
--- a/cache/pixlist Sat Sep 24 13:04:21 2011 +0300 +++ b/cache/pixlist Sat Sep 24 13:12:09 2011 +0300 @@ -1,8 +1,8 @@ 002f2f.png +e1.png E1.png +e2.png E2.png -e1.png -e2.png logo.png logo_small.png mirror0.png
--- a/docsrc/index.html Sat Sep 24 13:04:21 2011 +0300 +++ b/docsrc/index.html Sat Sep 24 13:12:09 2011 +0300 @@ -34,6 +34,7 @@ <ul> <li><a href="palette.html">Palette viewer</a> +<li><a href="preview.html">3D Level Preview</a> <li><a href="advanced.html">Advanced user's guide</a> <li><a href="../TODO"><code>TODO</code></a> <li><a href="yadex.6">The man page for Yadex</a>
--- a/src/editloop.cc Sat Sep 24 13:04:21 2011 +0300 +++ b/src/editloop.cc Sat Sep 24 13:12:09 2011 +0300 @@ -69,6 +69,7 @@ #include "x_exchng.h" #include "x_hover.h" #include "xref.h" +#include "r_render.h" static int zoom_fit (edit_t&); @@ -2229,6 +2230,13 @@ DumpSelection (e.Selected); } + // [R] Render 3D view (AJA) + else if (is.key == 'R') + { + Render3D (); + RedrawMap = 1; + } + // [T] Transfer properties to selected objects (AJA) else if (is.key == 'T' && e.Selected && e.highlighted.num >= 0)
--- a/src/gcolour1.cc Sat Sep 24 13:04:21 2011 +0300 +++ b/src/gcolour1.cc Sat Sep 24 13:12:09 2011 +0300 @@ -127,6 +127,21 @@ } verbmsg ("colours: colour %d remapped to %d (delta %d)\n", IMG_TRANSP, colour0, smallest_delta); + + rgb_c med_blue (0, 0, 128); + sky_colour = 0; + smallest_delta = INT_MAX; + + for (size_t n = 0; n < DOOM_COLOURS; n++) + { + int delta = med_blue - rgb_values[n]; + if (delta < smallest_delta) + { + sky_colour = n; + smallest_delta = delta; + } + } + verbmsg ("Sky Colour remapped to %d (delta %d)\n", sky_colour, smallest_delta); } #endif
--- a/src/gcolour2.cc Sat Sep 24 13:04:21 2011 +0300 +++ b/src/gcolour2.cc Sat Sep 24 13:12:09 2011 +0300 @@ -35,4 +35,5 @@ pcolour_t *game_colour = 0; // Pixel values for the DOOM_COLOURS game clrs. int colour0; // Game colour to which g. colour 0 is remapped +int sky_colour; // Game colour for a medium sky blue
--- a/src/gcolour2.h Sat Sep 24 13:04:21 2011 +0300 +++ b/src/gcolour2.h Sat Sep 24 13:12:09 2011 +0300 @@ -10,4 +10,5 @@ extern pcolour_t *game_colour; // Pixel values for the DOOM_COLOURS game clrs. extern int colour0; // Game colour to which g. colour 0 is remapped +extern int sky_colour; // Game colour for a medium blue sky
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/r_images.cc Sat Sep 24 13:12:09 2011 +0300 @@ -0,0 +1,400 @@ +/* + * r_images.cc + * AJA 2002-04-23 (based on textures.cc and flats.cc) + */ + + +/* +This file is part of Yadex. + +Yadex incorporates code from DEU 5.21 that was put in the public domain in +1994 by Raphaël Quinet and Brendon Wyber. + +The rest of Yadex is Copyright © 1997-2000 André Majorel. + +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 "yadex.h" +#ifdef Y_X11 +#include <X11/Xlib.h> +#endif +#include "dialog.h" +#include "game.h" /* yg_picture_format */ +#include "gfx.h" +#include "levels.h" +#include "lists.h" +#include "patchdir.h" +#include "pic2img.h" +#include "sticker.h" +#include "flats.h" +#include "textures.h" +#include "wadfile.h" +#include "wads.h" +#include "wadres.h" +#include "wstructs.h" + +#include "r_images.h" + + +/* + * flat_list_entry_match + * Function used by bsearch() to locate a particular + * flat in the FTexture. + */ +static int flat_list_entry_match (const void *key, const void *flat_list_entry) +{ +return y_strnicmp ((const char *) key, + ((const flat_list_entry_t *) flat_list_entry)->name, + WAD_FLAT_NAME); +} + + +/* + * load a flat into a new image. NULL if not found. + */ + +Img * Flat2Img (const wad_flat_name_t& fname) +{ +char name[WAD_FLAT_NAME + 1]; +strncpy (name, fname, WAD_FLAT_NAME); +name[WAD_FLAT_NAME] = 0; + +flat_list_entry_t *flat = (flat_list_entry_t *) + bsearch (name, flat_list, NumFTexture, sizeof *flat_list, + flat_list_entry_match); + +if (! flat) // Not found in list + return 0; + +int width = DOOM_FLAT_WIDTH; // Big deal ! +int height = DOOM_FLAT_HEIGHT; + +const Wad_file *wadfile = flat->wadfile; +wadfile->seek (flat->offset); + +Img *img = new Img (width, height, false); + +wadfile->read_bytes (img->wbuf (), (long) width * height); + +return img; +} + + +/* + * load a wall texture ("TEXTURE1" or "TEXTURE2" object) into an image. + * Returns NULL if not found or error. + */ + +Img * Tex2Img (const wad_tex_name_t& texname) +{ +MDirPtr dir = 0; /* main directory pointer to the TEXTURE* entries */ +i32 *offsets; /* array of offsets to texture names */ +int n; /* general counter */ +i16 width, height; /* size of the texture */ +i16 npatches; /* number of wall patches used to build this texture */ +i32 numtex; /* number of texture names in TEXTURE* list */ +i32 texofs; /* offset in the wad file to the texture data */ +char tname[WAD_TEX_NAME + 1]; /* texture name */ +char picname[WAD_PIC_NAME + 1]; /* wall patch name */ +bool have_dummy_bytes; +int header_size; +int item_size; + +char name[WAD_TEX_NAME + 1]; +strncpy (name, texname, WAD_TEX_NAME); +name[WAD_TEX_NAME] = 0; + +// Iwad-dependant details +if (yg_texture_format == YGTF_NAMELESS) + { + have_dummy_bytes = true; + header_size = 14; + item_size = 10; + } +else if (yg_texture_format == YGTF_NORMAL) + { + have_dummy_bytes = true; + header_size = 14; + item_size = 10; + } +else if (yg_texture_format == YGTF_STRIFE11) + { + have_dummy_bytes = false; + header_size = 10; + item_size = 6; + } +else + { + nf_bug ("Bad texture format %d.", (int) yg_texture_format); + return 0; + } + +/* offset for texture we want. */ +texofs = 0; +// Doom alpha 0.4 : "TEXTURES", no names +if (yg_texture_lumps == YGTL_TEXTURES && yg_texture_format == YGTF_NAMELESS) + { + dir = FindMasterDir (MasterDir, "TEXTURES"); + if (dir != NULL) + { + dir->wadfile->seek (dir->dir.start); + dir->wadfile->read_i32 (&numtex); + if (WAD_TEX_NAME < 7) nf_bug ("WAD_TEX_NAME too small"); // Sanity + if (! y_strnicmp (name, "TEX", 3) + && isdigit (name[3]) + && isdigit (name[4]) + && isdigit (name[5]) + && isdigit (name[6]) + && name[7] == '\0') + { + long num; + if (sscanf (name + 3, "%4ld", &num) == 1 + && num >= 0 && num < numtex) + { + dir->wadfile->seek (dir->dir.start + 4 + 4 * num); + dir->wadfile->read_i32 (&texofs); + texofs += dir->dir.start; + } + } + } + } +// Doom alpha 0.5 : only "TEXTURES" +else if (yg_texture_lumps == YGTL_TEXTURES + && (yg_texture_format == YGTF_NORMAL || yg_texture_format == YGTF_STRIFE11)) + { + // Is it in TEXTURES ? + dir = FindMasterDir (MasterDir, "TEXTURES"); + if (dir != NULL) // (Theoretically, it should always exist) + { + dir->wadfile->seek (dir->dir.start); + dir->wadfile->read_i32 (&numtex); + /* read in the offsets for texture1 names and info. */ + offsets = (i32 *) GetMemory ((long) numtex * 4); + dir->wadfile->read_i32 (offsets, numtex); + for (n = 0; n < numtex && !texofs; n++) + { + dir->wadfile->seek (dir->dir.start + offsets[n]); + dir->wadfile->read_bytes (&tname, WAD_TEX_NAME); + if (!y_strnicmp (tname, name, WAD_TEX_NAME)) + texofs = dir->dir.start + offsets[n]; + } + FreeMemory (offsets); + } + } +// Other iwads : "TEXTURE1" and "TEXTURE2" +else if (yg_texture_lumps == YGTL_NORMAL + && (yg_texture_format == YGTF_NORMAL || yg_texture_format == YGTF_STRIFE11)) + { + // Is it in TEXTURE1 ? + dir = FindMasterDir (MasterDir, "TEXTURE1"); + if (dir != NULL) // (Theoretically, it should always exist) + { + dir->wadfile->seek (dir->dir.start); + dir->wadfile->read_i32 (&numtex); + /* read in the offsets for texture1 names and info. */ + offsets = (i32 *) GetMemory ((long) numtex * 4); + dir->wadfile->read_i32 (offsets, numtex); + for (n = 0; n < numtex && !texofs; n++) + { + dir->wadfile->seek (dir->dir.start + offsets[n]); + dir->wadfile->read_bytes (&tname, WAD_TEX_NAME); + if (!y_strnicmp (tname, name, WAD_TEX_NAME)) + texofs = dir->dir.start + offsets[n]; + } + FreeMemory (offsets); + } + // Well, then is it in TEXTURE2 ? + if (texofs == 0) + { + dir = FindMasterDir (MasterDir, "TEXTURE2"); + if (dir != NULL) // Doom II has no TEXTURE2 + { + dir->wadfile->seek (dir->dir.start); + dir->wadfile->read_i32 (&numtex); + /* read in the offsets for texture2 names */ + offsets = (i32 *) GetMemory ((long) numtex * 4); + dir->wadfile->read_i32 (offsets, numtex); + for (n = 0; n < numtex && !texofs; n++) + { + dir->wadfile->seek (dir->dir.start + offsets[n]); + dir->wadfile->read_bytes (&tname, WAD_TEX_NAME); + if (!y_strnicmp (tname, name, WAD_TEX_NAME)) + texofs = dir->dir.start + offsets[n]; + } + FreeMemory (offsets); + } + } + } +else + nf_bug ("Invalid texture_format/texture_lumps combination."); + +/* texture name not found */ +if (texofs == 0) + return 0; + +/* read the info for this texture */ +i32 header_ofs; +if (yg_texture_format == YGTF_NAMELESS) + header_ofs = texofs; +else + header_ofs = texofs + WAD_TEX_NAME; +dir->wadfile->seek (header_ofs + 4); +dir->wadfile->read_i16 (&width); +dir->wadfile->read_i16 (&height); +if (have_dummy_bytes) + { + i16 dummy; + dir->wadfile->read_i16 (&dummy); + dir->wadfile->read_i16 (&dummy); + } +dir->wadfile->read_i16 (&npatches); + +/* Compose the texture */ +Img *texbuf = new Img (width, height, false); + +/* Paste onto the buffer all the patches that the texture is + made of. */ +for (n = 0; n < npatches; n++) + { + i16 xofs, yofs; // offset in texture space for the patch + i16 pnameind; // index of patch in PNAMES + + dir->wadfile->seek (header_ofs + header_size + (long) n * item_size); + dir->wadfile->read_i16 (&xofs); + dir->wadfile->read_i16 (&yofs); + dir->wadfile->read_i16 (&pnameind); + + if (have_dummy_bytes) + { + i16 stepdir; + i16 colormap; + dir->wadfile->read_i16 (&stepdir); // Always 1, unused. + dir->wadfile->read_i16 (&colormap); // Always 0, unused. + } + + /* AYM 1998-08-08: Yes, that's weird but that's what Doom + does. Without these two lines, the few textures that have + patches with negative y-offsets (BIGDOOR7, SKY1, TEKWALL1, + TEKWALL5 and a few others) would not look in the texture + viewer quite like in Doom. This should be mentioned in + the UDS, by the way. */ + if (yofs < 0) + yofs = 0; + + Lump_loc loc; + { + wad_pic_name_t *wname = patch_dir.name_for_num (pnameind); + if (wname == 0) + { + warn ("texture \"%.*s\": patch %2d has bad index %d.\n", + WAD_TEX_NAME, tname, (int) n, (int) pnameind); + continue; + } + patch_dir.loc_by_name ((const char *) *wname, loc); + *picname = '\0'; + strncat (picname, (const char *) *wname, sizeof picname - 1); + } + + if (LoadPicture (*texbuf, picname, loc, xofs, yofs, 0, 0)) + warn ("texture \"%.*s\": patch \"%.*s\" not found.\n", + WAD_TEX_NAME, tname, WAD_PIC_NAME, picname); + } + +return texbuf; +} + + +/* --- ImageCache methods --- */ + + +Img *ImageCache::GetFlat (const wad_flat_name_t& fname) +{ +std::string f_str = WadToString(fname); + +flat_map_t::iterator P = flats.find (f_str); + +if (P != flats.end ()) + return P->second; + +// flat not in the list yet. Add it. + +Img *result = Flat2Img (fname); +flats[f_str] = result; + +// note that a NULL return from Flat2Img is OK, it means that no +// such flat exists. Our renderer will revert to using a solid +// colour. + +return result; +} + + +Img *ImageCache::GetTex (const wad_tex_name_t& tname) +{ +if (tname[0] == 0 || tname[0] == '-') + return 0; + +std::string t_str = WadToString(tname); + +tex_map_t::iterator P = textures.find (t_str); + +if (P != textures.end ()) + return P->second; + +// texture not in the list yet. Add it. + +Img *result = Tex2Img (tname); +textures[t_str] = result; + +// note that a NULL return from Tex2Img is OK, it means that no +// such texture exists. Our renderer will revert to using a solid +// colour. + +return result; +} + + +Img *ImageCache::GetSprite (const wad_ttype_t& type) +{ +sprite_map_t::iterator P = sprites.find (type); + +if (P != sprites.end ()) + return P->second; + +// sprite not in the list yet. Add it. + +Img *result = 0; + +const char *sprite_root = get_thing_sprite (type); +if (sprite_root) + { + Lump_loc loc; + wad_res.sprites.loc_by_root (sprite_root, loc); + result = new Img (); + + if (LoadPicture (*result, sprite_root, loc, 0, 0) != 0) + { + delete result; + result = 0; + } + } + +// note that a NULL image is OK. Our renderer will just ignore the +// missing sprite. + +sprites[type] = result; +return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/r_images.h Sat Sep 24 13:12:09 2011 +0300 @@ -0,0 +1,69 @@ +/* + * r_images.h + * AJA 2002-04-27 + */ + + +#ifndef YH_R_IMAGES /* DO NOT INSERT ANYTHING BEFORE THIS LINE */ +#define YH_R_IMAGES + + +#include <map> +#include <algorithm> +#include <string> + + +struct ImageCache +{ +public: + typedef std::map<std::string, Img *> flat_map_t; + typedef std::map<std::string, Img *> tex_map_t; + typedef std::map<wad_ttype_t, Img *> sprite_map_t; + + flat_map_t flats; + tex_map_t textures; + sprite_map_t sprites; + + static std::string WadToString(const wad_flat_name_t& fname) + { + int len; + + for (len = 0; len < WAD_NAME && fname[len]; len++) + { } + + return std::string(fname, len); + } + + static void DeleteFlat(const flat_map_t::value_type& P) + { + delete P.second; + } + + static void DeleteTex(const tex_map_t::value_type& P) + { + delete P.second; + } + + static void DeleteSprite(const sprite_map_t::value_type& P) + { + delete P.second; + } + + ~ImageCache () + { + std::for_each (flats.begin (), flats.end (), DeleteFlat); + std::for_each (textures.begin (), textures.end (), DeleteTex); + std::for_each (sprites.begin (), sprites.end (), DeleteSprite); + + flats.clear (); + textures.clear (); + sprites.clear (); + } + + Img *GetFlat (const wad_flat_name_t& fname); + Img *GetTex (const wad_tex_name_t& tname); + Img *GetSprite (const wad_ttype_t& type); +}; + + +#endif /* DO NOT ADD ANYTHING AFTER THIS LINE */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/r_render.cc Sat Sep 24 13:12:09 2011 +0300 @@ -0,0 +1,1275 @@ +/* + * r_render.cc + * 3D Rendering + * AJA 2002-04-21 + */ + + +/* +This file is part of Yadex. + +Yadex incorporates code from DEU 5.21 that was put in the public domain in +1994 by Raphaël Quinet and Brendon Wyber. + +The rest of Yadex is Copyright © 1997-2000 André Majorel. + +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 "yadex.h" + +#include <math.h> +#include <vector> +#include <map> +#include <algorithm> + +#ifdef Y_X11 +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#endif +#include "levels.h" +#include "wstructs.h" +#include "gfx.h" +#include "img.h" +#include "sticker.h" +#include "gamesky.h" +#include "things.h" +#include "wadres.h" +#include "objid.h" +#include "objects.h" +#include "pic2img.h" +#include "rgb.h" +#include "gcolour2.h" + +#include "r_render.h" +#include "r_images.h" + + +#define ML_UPPER_UNPEGGED 0x08 +#define ML_LOWER_UNPEGGED 0x10 + + +struct Y_View +{ +public: + int p_type, px, py; + // player type and position. + + float x, y; + int z; + // view position. + + static const int EYE_HEIGHT = 41; + // standard height above the floor. + + float angle; + float Sin, Cos; + // view direction. + + int sw, sh; + Img *screen; + // screen image. + + bool texturing; + bool sprites; + bool walking; + + ImageCache *im_ch; + + int *thing_floors; + + Y_View () { memset (this, 0, sizeof *this); } + + void SetAngle (float new_ang) + { + angle = new_ang; + + if (angle >= TWOPI) + angle -= TWOPI; + else if (angle < 0) + angle += TWOPI; + + Sin = sin (angle); + Cos = cos (angle); + } + + void CalcViewZ () + { + Objid o; + GetCurObject (o, OBJ_SECTORS, int (x), int (y)); + int secnum = o.num; + if (secnum >= 0) + z = Sectors[secnum].floorh + EYE_HEIGHT; + } + + void ClearScreen () + { + memset (screen->wbuf (), colour0, sw * sh); + } + + void PutScreen (int x, int y) + { + DrawScreenBox3D (x, y, x + BOX_BORDER*2 + sw, y + BOX_BORDER*2 + sh); + + Sticker sticker (*screen, true); + + sticker.draw (drw, 't', x + BOX_BORDER, y + BOX_BORDER); + } + + void FindThingFloors () + { + thing_floors = new int[NumThings]; + + for (int i = 0; i < NumThings; i++) + { + Objid o; + GetCurObject (o, OBJ_SECTORS, Things[i].xpos, + Things[i].ypos); + int secnum = o.num; + + if (secnum < 0) + thing_floors[i] = 0; + else + thing_floors[i] = Sectors[secnum].floorh; + } + } +}; + + +static Y_View view; + + +struct DrawSurf +{ +public: + enum + { + K_INVIS = 0, + K_FLAT, + K_TEXTURE + }; + int kind; + + int h1, h2, tex_h; + // heights for the surface (h1 is above h2). + + Img *img; + img_pixel_t col; /* used if img is zero */ + + enum + { + SOLID_ABOVE = 1, + SOLID_BELOW = 2 + }; + int y_clip; + + /* CTor */ + + DrawSurf () { kind = K_INVIS; img = 0; } + + void FindFlat (const wad_flat_name_t& fname, Sector *sec) + { + if (view.texturing) + { + img = view.im_ch->GetFlat (fname); + + if (img != 0) + return; + } + col = 0x70 + ((sec - Sectors) % 48); + } + + void FindTex (const wad_tex_name_t& tname, LineDef *ld) + { + if (view.texturing) + { + img = view.im_ch->GetTex (tname); + + if (img != 0) + return; + } + col = 0x30 + ((ld - LineDefs) % 64); + + if (col >= 0x60) + col += 0x70; + } +}; + + +struct DrawWall +{ +public: + typedef std::vector<struct DrawWall *> vec_t; + + Thing *th; + // when `th' is non-zero, this is actually a sprite, and `ld' and + // `sd' will be zero. Sprites use the info in the `ceil' surface. + + LineDef *ld; + SideDef *sd; + Sector *sec; + + int side; + // which side this wall faces (0 right, 1 left) + + float ang1, dang, cur_ang; + float base_ang; + // clipped angles + + float dist, t_dist; + float normal; + // line constants + + double iz1, diz, cur_iz; + double mid_iz; + // distance values (inverted, so they can be lerped) + + float spr_tx1; + // translate coord, for sprite + + int sx1, sx2; + // screen X coordinates + + int oy1, oy2; + // for sprites, the remembered open space to clip to + + /* surfaces */ + + DrawSurf ceil; + DrawSurf upper; + DrawSurf lower; + DrawSurf floor; + + static const double IZ_EPSILON = 0.000001; + + /* PREDICATES */ + + struct MidDistCmp + { + inline bool operator() (const DrawWall * A, const DrawWall * B) const + { + return A->mid_iz > B->mid_iz; + } + }; + + struct DistCmp + { + inline bool operator() (const DrawWall * A, const DrawWall * B) const + { + if (fabs (A->cur_iz - B->cur_iz) < IZ_EPSILON) + return A->diz > B->diz; + + return A->cur_iz > B->cur_iz; + } + }; + + struct SX1Cmp + { + inline bool operator() (const DrawWall * A, const DrawWall * B) const + { + return A->sx1 < B->sx1; + } + + inline bool operator() (const DrawWall * A, int x) const + { + return A->sx1 < x; + } + + inline bool operator() (int x, const DrawWall * A) const + { + return x < A->sx1; + } + }; + + struct SX2Less + { + int x; + + SX2Less (int _x) : x (_x) { } + + inline bool operator() (const DrawWall * A) const + { + return A->sx2 < x; + } + }; + + /* methods */ + + void ComputeWallSurface () + { + Sector *front = sec; + Sector *back = 0; + + if (is_obj (side ? ld->sidedef1 : ld->sidedef2)) + { + SideDef *bsd = SideDefs + (side ? ld->sidedef1 : ld->sidedef2); + + if (is_obj (bsd->sector)) + back = Sectors + bsd->sector; + } + + bool sky_upper = back && is_sky (front->ceilt) && is_sky (back->ceilt); + + if ((front->ceilh > view.z || is_sky (front->ceilt)) && ! sky_upper) + { + ceil.kind = DrawSurf::K_FLAT; + ceil.h1 = +99999; + ceil.h2 = front->ceilh; + ceil.tex_h = ceil.h2; + ceil.y_clip = DrawSurf::SOLID_ABOVE; + + if (is_sky (front->ceilt)) + ceil.col = sky_colour; + else + ceil.FindFlat (front->ceilt, front); + } + + if (front->floorh < view.z) + { + floor.kind = DrawSurf::K_FLAT; + floor.h1 = front->floorh; + floor.h2 = -99999; + floor.tex_h = floor.h1; + floor.y_clip = DrawSurf::SOLID_BELOW; + + if (is_sky (front->floort)) + floor.col = sky_colour; + else + floor.FindFlat (front->floort, front); + } + + if (! back) + { + /* ONE-sided line */ + + lower.kind = DrawSurf::K_TEXTURE; + lower.h1 = front->ceilh; + lower.h2 = front->floorh; + lower.y_clip = DrawSurf::SOLID_ABOVE | DrawSurf::SOLID_BELOW; + + lower.FindTex (sd->tex3, ld); + + if (lower.img && (ld->flags & ML_LOWER_UNPEGGED)) + lower.tex_h = lower.h2 + lower.img->height (); + else + lower.tex_h = lower.h1; + } + else + { + /* TWO-sided line */ + + if (back->ceilh < front->ceilh && ! sky_upper) + { + upper.kind = DrawSurf::K_TEXTURE; + upper.h1 = front->ceilh; + upper.h2 = back->ceilh; + upper.tex_h = upper.h1; + upper.y_clip = DrawSurf::SOLID_ABOVE; + + upper.FindTex (sd->tex1, ld); + + if (upper.img && ! (ld->flags & ML_UPPER_UNPEGGED)) + upper.tex_h = upper.h2 + upper.img->height (); + else + upper.tex_h = upper.h1; + } + + if (back->floorh > front->floorh) + { + lower.kind = DrawSurf::K_TEXTURE; + lower.h1 = back->floorh; + lower.h2 = front->floorh; + lower.y_clip = DrawSurf::SOLID_BELOW; + + lower.FindTex (sd->tex2, ld); + + if (ld->flags & ML_LOWER_UNPEGGED) + lower.tex_h = front->ceilh; + else + lower.tex_h = lower.h1; + } + } + } +}; + + +struct RendInfo +{ +public: + DrawWall::vec_t walls; + // complete set of walls/sprites to draw. + + DrawWall::vec_t active; + // the active list. Pointers here are always duplicates of ones in + // the walls list (no need to `delete' any of them). + + std::vector<double> depth_x; + // inverse distances over X range, 0 when empty. + + int open_y1; + int open_y2; + + static const double Y_SLOPE = 1.70; + + static void DeleteWall (DrawWall *P) + { + delete P; + } + + ~RendInfo () + { + std::for_each (walls.begin (), walls.end (), DeleteWall); + + walls.clear (); + active.clear (); + } + + void InitDepthBuf (int width) + { + depth_x.resize (width); + + std::fill_n (depth_x.begin (), width, 0); + } + + static inline float PointToAngle (float x, float y) + { + if (-0.01 < x && x < 0.01) + return (y > 0) ? HALFPI : (3 * HALFPI); + + float angle = atan2(y, x); + + if (angle < 0) + angle += TWOPI; + + return angle; + } + + static inline int AngleToX (float ang) + { + float t = tan (HALFPI - ang); + + int x = int (view.sw * t); + + x = (view.sw + x) / 2; + + if (x < 0) + x = 0; + else if (x > view.sw) + x = view.sw; + + return x; + } + + static inline float XToAngle (int x) + { + x = x * 2 - view.sw; + + float ang = HALFPI + atan (x / float (view.sw)); + + if (ang < 0) + ang = 0; + else if (ang > ONEPI) + ang = ONEPI; + + return ang; + } + + static inline int DeltaToX (double iz, float tx) + { + int x = int (view.sw * tx * iz); + + x = (x + view.sw) / 2; + + return x; + } + + static inline float XToDelta (int x, double iz) + { + x = x * 2 - view.sw; + + float tx = x / iz / view.sw; + + return tx; + } + + static inline int DistToY (double iz, int sec_h) + { + if (sec_h > 32770) + return -9999; + + if (sec_h < -32770) + return +9999; + + sec_h -= view.z; + + int y = int (view.sh * sec_h * iz * Y_SLOPE); + + y = (view.sh - y) / 2; + + return y; + } + + static inline float YToDist (int y, int sec_h) + { + sec_h -= view.z; + + y = y * 2 - view.sh; + + if (y == 0) + return 999999; + + return view.sh * sec_h * Y_SLOPE / y; + } + + static inline float YToSecH (int y, double iz) + { + y = y * 2 - view.sh; + + return view.z - (float (y) / view.sh / iz / Y_SLOPE); + } + + void AddLine (int linenum) + { + LineDef *ld = LineDefs + linenum; + + if (! is_obj (ld->start) || ! is_obj (ld->end)) + return; + + float x1 = Vertices[ld->start].x - view.x; + float y1 = Vertices[ld->start].y - view.y; + float x2 = Vertices[ld->end].x - view.x; + float y2 = Vertices[ld->end].y - view.y; + + float tx1 = x1 * view.Sin - y1 * view.Cos; + float ty1 = x1 * view.Cos + y1 * view.Sin; + float tx2 = x2 * view.Sin - y2 * view.Cos; + float ty2 = x2 * view.Cos + y2 * view.Sin; + + // reject line if complete behind viewplane + if (ty1 <= 0 && ty2 <= 0) + return; + + float angle1 = PointToAngle (tx1, ty1); + float angle2 = PointToAngle (tx2, ty2); + float span = angle1 - angle2; + + if (span < 0) + span += TWOPI; + + int side = 0; + SideDef *sd; + + if (span >= ONEPI) + side = 1; + + // ignore the line when there is no facing sidedef + if (! is_obj (side ? ld->sidedef2 : ld->sidedef1)) + return; + + sd = SideDefs + (side ? ld->sidedef2 : ld->sidedef1); + + if (! is_obj (sd->sector)) + return; + + if (side == 1) + { + float tmp = angle1; + angle1 = angle2; + angle2 = tmp; + } + + // clip angles to view volume + + float base_ang = angle1; + + float leftclip = (3 * ONEPI / 4); + float rightclip = ONEPI / 4; + + float tspan1 = angle1 - rightclip; + float tspan2 = leftclip - angle2; + + if (tspan1 < 0) tspan1 += TWOPI; + if (tspan2 < 0) tspan2 += TWOPI; + + if (tspan1 > HALFPI) + { + // Totally off the left edge? + if (tspan2 >= ONEPI) + return; + + angle1 = leftclip; + } + + if (tspan2 > HALFPI) + { + // Totally off the left edge? + if (tspan1 >= ONEPI) + return; + + angle2 = rightclip; + } + + // convert angles to on-screen X positions + int sx1 = AngleToX (angle1); + int sx2 = AngleToX (angle2) - 1; + + if (sx1 > sx2) + return; + + // compute distance from eye to wall + float wdx = x2 - x1; + float wdy = y2 - y1; + + float wlen = sqrt (wdx * wdx + wdy * wdy); + float dist = fabs ((y1 * wdx / wlen) - (x1 * wdy / wlen)); + + if (dist < 0.01) + return; + + // compute normal of wall (translated coords) + float normal; + + if (side == 1) + normal = PointToAngle (ty2 - ty1, tx1 - tx2); + else + normal = PointToAngle (ty1 - ty2, tx2 - tx1); + + // compute inverse distances + double iz1 = cos (normal - angle1) / dist / cos (HALFPI - angle1); + double iz2 = cos (normal - angle2) / dist / cos (HALFPI - angle2); + + double diz = (iz2 - iz1) / y_max (1, sx2 - sx1); + + // create drawwall structure + + DrawWall *dw = new DrawWall; + + dw->th = 0; + dw->ld = ld; + dw->sd = sd; + dw->sec = Sectors + sd->sector; + + dw->side = side; + + dw->base_ang = base_ang; + dw->ang1 = angle1; + dw->dang = (angle2 - angle1) / y_max (1, sx2 - sx1); + + dw->dist = dist; + dw->normal = normal; + dw->t_dist = tan (base_ang - normal) * dist; + + dw->iz1 = iz1; + dw->diz = diz; + dw->mid_iz = iz1 + (sx2 - sx1 + 1) * diz / 2; + + dw->sx1 = sx1; dw->sx2 = sx2; + + walls.push_back (dw); + } + + void AddThing (int thingnum) + { + Thing *th = Things + thingnum; + + float x = th->xpos - view.x; + float y = th->ypos - view.y; + + float tx = x * view.Sin - y * view.Cos; + float ty = x * view.Cos + y * view.Sin; + + // reject sprite if complete behind viewplane + if (ty < 4) + return; + + Img *sprite = view.im_ch->GetSprite (th->type); + if (! sprite) + return; + + float tx1 = tx - sprite->width () / 2.0; + float tx2 = tx + sprite->width () / 2.0; + + double iz = 1 / ty; + + int sx1 = DeltaToX (iz, tx1); + int sx2 = DeltaToX (iz, tx2) - 1; + + if (sx1 < 0) + sx1 = 0; + + if (sx2 >= view.sw) + sx2 = view.sw - 1; + + if (sx1 > sx2) + return; + + int h2 = view.thing_floors[thingnum]; + int h1 = h2 + sprite->height (); + + // create drawwall structure + + DrawWall *dw = new DrawWall; + + dw->th = th; + dw->ld = 0; + dw->sd = 0; + dw->sec = 0; + + dw->spr_tx1 = tx1; + + dw->ang1 = dw->dang = 0; + + dw->iz1 = dw->mid_iz = iz; + dw->diz = 0; + + dw->sx1 = sx1; dw->sx2 = sx2; + + dw->ceil.img = sprite; + dw->ceil.h1 = h1; + dw->ceil.h2 = h2; + + walls.push_back (dw); + } + + void ComputeSurfaces () + { + DrawWall::vec_t::iterator S; + + for (S = walls.begin (); S != walls.end (); S++) + if ((*S)->ld) + (*S)->ComputeWallSurface (); + } + + void ClipSolids () + { + // perform a rough depth sort of the walls and sprites. + + std::sort (walls.begin (), walls.end (), DrawWall::MidDistCmp ()); + + // go forwards, from closest to furthest away + + DrawWall::vec_t::iterator S; + + for (S = walls.begin (); S != walls.end (); S++) + { + DrawWall *dw = (*S); + + if (! dw) + continue; + + int one_sided = dw->ld && ! is_obj (dw->ld->sidedef2); + int vis_count = dw->sx2 - dw->sx1 + 1; + + for (int x = dw->sx1; x <= dw->sx2; x++) + { + double iz = dw->iz1 + (dw->diz * (x - dw->sx1)); + + if (iz < depth_x[x]) + vis_count--; + else if (one_sided) + depth_x[x] = iz; + } + + if (vis_count == 0) + { + delete dw; + (*S) = 0; + } + } + + // remove null pointers + + S = std::remove (walls.begin (), walls.end (), (DrawWall *) 0); + + walls.erase (S, walls.end ()); + } + + void RenderFlatColumn (DrawWall *dw, DrawSurf& surf, + int x, int y1, int y2) + { + img_pixel_t *buf = view.screen->wbuf (); + img_pixel_t *wbuf = surf.img->wbuf (); + + int tw = surf.img->width (); + int th = surf.img->height (); + + float ang = XToAngle (x); + float modv = cos (ang - HALFPI); + + float t_cos = cos (ONEPI + -view.angle + ang) / modv; + float t_sin = sin (ONEPI + -view.angle + ang) / modv; + + buf += x + y1 * view.sw; + + for (; y1 <= y2; y1++, buf += view.sw) + { + float dist = YToDist (y1, surf.tex_h); + + int tx = int ( view.x + t_sin * dist) & (tw - 1); + int ty = int (-view.y - t_cos * dist) & (th - 1); + + *buf = wbuf[ty * tw + tx]; + } + } + + void RenderTexColumn (DrawWall *dw, DrawSurf& surf, + int x, int y1, int y2) + { + img_pixel_t *buf = view.screen->wbuf (); + img_pixel_t *wbuf = surf.img->wbuf (); + + int tw = surf.img->width (); + int th = surf.img->height (); + + /* compute texture X coord */ + + int tx = int (dw->t_dist - tan (dw->cur_ang - dw->normal) * dw->dist); + + tx = (dw->sd->xoff + tx) & (tw - 1); + + /* compute texture Y coords */ + + float base_h = surf.tex_h + dw->sd->yoff; + + float h1 = base_h - YToSecH (y1, dw->cur_iz); + float dh = base_h - YToSecH (y2, dw->cur_iz); + + dh = (dh - h1) / y_max (1, y2 - y1); + + buf += x + y1 * view.sw; + wbuf += tx; + + for (; y1 <= y2; y1++, h1 += dh, buf += view.sw) + { + int ty = int (h1) % th; + + // handle negative values (use % twice) + ty = (ty + th) % th; + + *buf = wbuf[ty * tw]; + } + } + + void RenderSolidColumn (DrawWall *w, DrawSurf& surf, + int x, int y1, int y2) + { + img_pixel_t *buf = view.screen->wbuf (); + + buf += x + y1 * view.sw; + + for (; y1 <= y2; y1++, buf += view.sw) + { + *buf = surf.col; + } + } + + inline void RenderWallSurface (DrawWall *dw, DrawSurf& surf, + int x) + { + if (surf.kind == DrawSurf::K_INVIS) + return; + + int y1 = DistToY (dw->cur_iz, surf.h1); + int y2 = DistToY (dw->cur_iz, surf.h2) - 1; + + if (y1 < open_y1) + y1 = open_y1; + + if (y2 > open_y2) + y2 = open_y2; + + if (y1 > y2) + return; + + /* clip the open region */ + + if (surf.y_clip & DrawSurf::SOLID_ABOVE) + if (y2 > open_y1) + open_y1 = y2; + + if (surf.y_clip & DrawSurf::SOLID_BELOW) + if (y1 < open_y2) + open_y2 = y1; + + /* fill pixels */ + + if (! surf.img) + { + RenderSolidColumn (dw, surf, x, y1, y2); + } + else switch (surf.kind) + { + case DrawSurf::K_FLAT: + RenderFlatColumn (dw, surf, x, y1, y2); + break; + + case DrawSurf::K_TEXTURE: + RenderTexColumn (dw, surf, x, y1, y2); + break; + } + } + + inline void RenderSprite (DrawWall *dw, int x) + { + int y1 = DistToY (dw->cur_iz, dw->ceil.h1); + int y2 = DistToY (dw->cur_iz, dw->ceil.h2) - 1; + + if (y1 < dw->oy1) + y1 = dw->oy1; + + if (y2 > dw->oy2) + y2 = dw->oy2; + + if (y1 > y2) + return; + + /* fill pixels */ + + img_pixel_t *buf = view.screen->wbuf (); + img_pixel_t *wbuf = dw->ceil.img->wbuf (); + + int tw = dw->ceil.img->width (); + int th = dw->ceil.img->height (); + + int tx = int (XToDelta (x, dw->cur_iz) - dw->spr_tx1); + + if (tx < 0 || tx >= tw) + return; + + float h1 = dw->ceil.h1 - YToSecH (y1, dw->cur_iz); + float dh = dw->ceil.h1 - YToSecH (y2, dw->cur_iz); + + dh = (dh - h1) / y_max (1, y2 - y1); + + buf += x + y1 * view.sw; + wbuf += tx; + + for (; y1 <= y2; y1++, h1 += dh, buf += view.sw) + { + int ty = int (h1); + + if (ty < 0 || ty >= th) + continue; + + img_pixel_t pix = wbuf[ty * tw]; + + if (pix != IMG_TRANSP) + *buf = pix; + } + } + + void UpdateActiveList (int x) + { + DrawWall::vec_t::iterator S, E, P; + + bool changes = false; + + // remove walls that have finished. + + S = active.begin (); + E = active.end (); + + S = std::remove_if (S, E, DrawWall::SX2Less (x)); + + if (S != E) + { + active.erase (S, E); + changes = true; + } + + // add new walls that start in this column. + + S = walls.begin (); + E = walls.end (); + + S = std::lower_bound (S, E, x, DrawWall::SX1Cmp ()); + E = std::upper_bound (S, E, x, DrawWall::SX1Cmp ()); + + if (S != E) + changes = true; + + for (; S != E; S++) + { + active.push_back (*S); + } + + // calculate new depth values + + S = active.begin (); + E = active.end (); + + for (P=S; (P != E); P++) + { + DrawWall *dw = (*P); + + dw->cur_iz = dw->iz1 + dw->diz * (x - dw->sx1); + + if (P != S && (*(P-1))->cur_iz < dw->cur_iz) + changes = true; + + dw->cur_ang = dw->ang1 + dw->dang * (x - dw->sx1); + } + + // if there are changes, re-sort the active list... + + if (changes) + { + std::sort (active.begin (), active.end (), DrawWall::DistCmp ()); + } + } + + void RenderWalls () + { + // sort walls by their starting column, to allow binary search. + + std::sort (walls.begin (), walls.end (), DrawWall::SX1Cmp ()); + + active.clear (); + + for (int x=0; x < view.sw; x++) + { + // clear vertical depth buffer + + open_y1 = 0; + open_y2 = view.sh - 1; + + UpdateActiveList (x); + + // render, front to back + + DrawWall::vec_t::iterator S, E, P; + + S = active.begin (); + E = active.end (); + + for (P=S; P != E; P++) + { + DrawWall *dw = (*P); + + // for things, just remember the open space + if (dw->th) + { + dw->oy1 = open_y1; + dw->oy2 = open_y2; + continue; + } + + RenderWallSurface (dw, dw->ceil, x); + RenderWallSurface (dw, dw->floor, x); + RenderWallSurface (dw, dw->upper, x); + RenderWallSurface (dw, dw->lower, x); + + if (open_y1 >= open_y2) + break; + } + + // now render things, back to front + + if (P == E) + P--; + + for (; P != (S-1); P--) + { + DrawWall *dw = (*P); + + if (dw->th) + RenderSprite (dw, x); + } + } + } + + void DoRender3D () + { + view.ClearScreen (); + + InitDepthBuf (view.sw); + + for (int i=0; i < NumLineDefs; i++) + AddLine (i); + + if (view.sprites) + for (int j=0; j < NumThings; j++) + AddThing (j); + + ClipSolids (); + ComputeSurfaces (); + RenderWalls (); + } +}; + + +static Thing *FindPlayer (int typenum) +{ +for (int i=0; i < NumThings; i++) + if (Things[i].type == typenum) + return Things + i; + +return 0; +} + + +/* + * Render a 3D view from the player's position. + */ + +void Render3D () +{ +if (! view.p_type) + { + view.p_type = THING_PLAYER1; + view.px = 99999; + } + +Thing *player = FindPlayer (view.p_type); + +if (! player) + { + if (view.p_type != THING_DEATHMATCH) + view.p_type = THING_DEATHMATCH; + + player = FindPlayer (view.p_type); + + if (! player) + return; + } + +if (view.px != player->xpos || view.py != player->ypos) + { + // if player moved, re-create view parameters + + view.x = view.px = player->xpos; + view.y = view.py = player->ypos; + + view.CalcViewZ (); + view.SetAngle (player->angle * ONEPI / 180.0); + } + +/* create image */ + +view.sw = 640; +view.sh = 400; + +view.screen = new Img ((unsigned short int) view.sw, (unsigned short int) view.sh, false); +view.im_ch = new ImageCache; + +view.FindThingFloors (); + +bool Redraw = true; + +/* input loop */ + +for (;;) + { + /* render image */ + + if (Redraw) + { + if (view.walking) + view.CalcViewZ (); + + RendInfo rend; + + rend.DoRender3D (); + + view.PutScreen (40, 40); + + Redraw = false; + } + + /* handle keypress */ + + int key = get_key (); + + if (key == YK_ESC || key == 'q') + break; + + if ((key & ~YK_SHIFT) == YK_LEFT) + { + view.SetAngle (view.angle + ONEPI / ((key & YK_SHIFT) ? 4 : 8)); + Redraw = true; + } + else if ((key & ~YK_SHIFT) == YK_RIGHT) + { + view.SetAngle (view.angle -ONEPI / ((key & YK_SHIFT) ? 4 : 8)); + Redraw = true; + } + else if ((key & ~YK_SHIFT) == YK_UP) + { + view.x += view.Cos * ((key & YK_SHIFT) ? 192 : 32); + view.y += view.Sin * ((key & YK_SHIFT) ? 192 : 32); + Redraw = true; + } + else if ((key & ~YK_SHIFT) == YK_DOWN) + { + view.x -= view.Cos * ((key & YK_SHIFT) ? 192 : 32); + view.y -= view.Sin * ((key & YK_SHIFT) ? 192 : 32); + Redraw = true; + } + else if (key == 'n' || key == 'N') + { + view.x -= view.Sin * ((key == 'N') ? 192 : 32); + view.y += view.Cos * ((key == 'N') ? 192 : 32); + Redraw = true; + } + else if (key == 'm' || key == 'M') + { + view.x += view.Sin * ((key == 'M') ? 192 : 32); + view.y -= view.Cos * ((key == 'M') ? 192 : 32); + Redraw = true; + } + else if (key == 'd' || key == 'D') + { + view.z += (key == 'D') ? 128 : 32; + Redraw = true; + } + else if (key == 'c' || key == 'C') + { + view.z -= (key == 'C') ? 128 : 32; + Redraw = true; + } + else if (key == 't') + { + view.texturing = ! view.texturing; + Redraw = true; + } + else if (key == 's') + { + view.sprites = ! view.sprites; + Redraw = true; + } + else if (key == 'w') + { + view.walking = ! view.walking; + Redraw = true; + } + else if (key) + { + // key no good, get another one + Beep (); + } + } + +/* all done */ + +delete view.screen; +view.screen = 0; + +delete view.im_ch; +view.im_ch = 0; + +delete[] view.thing_floors; +view.thing_floors = 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/r_render.h Sat Sep 24 13:12:09 2011 +0300 @@ -0,0 +1,14 @@ +/* + * r_render.h + * AJA 2002-04-27 + */ + + +#ifndef YH_R_RENDER /* DO NOT INSERT ANYTHING BEFORE THIS LINE */ +#define YH_R_RENDER + + +void Render3D (); + + +#endif /* DO NOT ADD ANYTHING AFTER THIS LINE */