2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
*
* Rendering Stuff
*
*/
2012-11-11 22:12:51 +00:00
# include <algorithm>
2014-10-02 03:02:34 +00:00
# include <bitset>
2014-08-20 01:28:22 +00:00
# include <limits>
2014-11-30 17:03:00 +00:00
# include <cstdlib>
2006-03-20 17:12:09 +00:00
# include <stdio.h>
# include <string.h>
2011-02-14 21:27:07 +00:00
# include <math.h>
2015-08-03 03:11:25 +00:00
# include "render_state.h"
2006-03-20 17:12:09 +00:00
# include "inferno.h"
# include "segment.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2006-03-20 17:12:09 +00:00
# include "bm.h"
# include "texmap.h"
# include "render.h"
# include "game.h"
# include "object.h"
# include "laser.h"
# include "textures.h"
# include "screens.h"
# include "segpoint.h"
# include "wall.h"
# include "texmerge.h"
# include "physics.h"
# include "3d.h"
# include "gameseg.h"
# include "vclip.h"
# include "lighting.h"
# include "cntrlcen.h"
# include "newdemo.h"
# include "automap.h"
# include "endlevel.h"
# include "key.h"
# include "newmenu.h"
# include "u_mem.h"
# include "piggy.h"
2011-02-14 21:27:07 +00:00
# include "timer.h"
2011-02-23 16:46:39 +00:00
# include "effects.h"
# include "playsave.h"
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2006-03-20 17:12:09 +00:00
# include "ogl_init.h"
# endif
2013-03-03 19:41:09 +00:00
# include "args.h"
2006-03-20 17:12:09 +00:00
2014-09-13 22:05:52 +00:00
# include "compiler-integer_sequence.h"
2014-07-30 02:52:36 +00:00
# include "compiler-range_for.h"
2014-09-08 03:31:05 +00:00
# include "partial_range.h"
2014-07-30 02:52:36 +00:00
# include "segiter.h"
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
# include "editor/editor.h"
2013-03-16 03:10:55 +00:00
# include "editor/esegment.h"
2006-03-20 17:12:09 +00:00
# endif
2012-11-11 22:12:51 +00:00
using std : : min ;
using std : : max ;
2008-04-06 20:23:28 +00:00
// (former) "detail level" values
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2008-11-21 16:09:41 +00:00
int Render_depth = MAX_RENDER_SEGS ; //how many segments deep to render
# else
2008-04-06 20:23:28 +00:00
int Render_depth = 20 ; //how many segments deep to render
2014-09-20 23:47:27 +00:00
unsigned Max_linear_depth = 50 ; // Deepest segment at which linear interpolation will be used.
2015-04-22 02:44:29 +00:00
# endif
2008-04-06 20:23:28 +00:00
2006-03-20 17:12:09 +00:00
//used for checking if points have been rotated
int Clear_window_color = - 1 ;
int Clear_window = 2 ; // 1 = Clear whole background window, 2 = clear view portals into rest of world, 0 = no clear
2014-08-20 01:28:22 +00:00
static uint16_t s_current_generation ;
2006-03-20 17:12:09 +00:00
// When any render function needs to know what's looking at it, it should
// access Viewer members.
2015-12-22 04:18:50 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
object * Viewer = NULL ;
2015-12-22 04:18:50 +00:00
}
2006-03-20 17:12:09 +00:00
vms_vector Viewer_eye ; //valid during render
2017-01-15 00:03:12 +00:00
# if !DXX_USE_EDITOR && defined(RELEASE)
constexpr
# endif
2006-03-20 17:12:09 +00:00
fix Render_zoom = 0x9000 ; //the player's zoom factor
# ifndef NDEBUG
2014-10-02 03:02:34 +00:00
static std : : bitset < MAX_OBJECTS > object_rendered ;
2006-03-20 17:12:09 +00:00
# endif
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
int Render_only_bottom = 0 ;
int Bottom_bitmap_num = 9 ;
# endif
2015-12-13 18:00:49 +00:00
namespace dcx {
2015-12-05 22:57:24 +00:00
2007-12-29 14:18:49 +00:00
//Global vars for window clip test
int Window_clip_left , Window_clip_top , Window_clip_right , Window_clip_bot ;
2006-03-20 17:12:09 +00:00
2015-12-05 22:57:24 +00:00
}
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
int _search_mode = 0 ; //true if looking for curseg,side,face
short _search_x , _search_y ; //pixel we're looking at
2014-12-13 17:47:01 +00:00
static int found_side , found_face , found_poly ;
static segnum_t found_seg ;
static objnum_t found_obj ;
2006-03-20 17:12:09 +00:00
# else
2016-07-16 16:52:04 +00:00
constexpr int _search_mode = 0 ;
2006-03-20 17:12:09 +00:00
# endif
# ifdef NDEBUG //if no debug code, set these vars to constants
# else
2016-01-03 20:21:35 +00:00
int Outline_mode = 0 ;
2006-03-20 17:12:09 +00:00
int toggle_outline_mode ( void )
{
return Outline_mode = ! Outline_mode ;
}
2013-07-14 22:41:04 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-07-14 22:41:04 +00:00
# ifndef NDEBUG
2017-02-11 21:42:41 +00:00
# if DXX_USE_OGL
# define draw_outline(C,a,b) draw_outline(a,b)
# endif
static void draw_outline ( grs_canvas & canvas , const int nverts , cg3s_point * const * const pointlist )
2006-03-20 17:12:09 +00:00
{
int i ;
2016-02-12 04:02:28 +00:00
const uint8_t color = BM_XRGB ( 63 , 63 , 63 ) ;
2006-03-20 17:12:09 +00:00
for ( i = 0 ; i < nverts - 1 ; i + + )
2017-02-11 21:42:41 +00:00
g3_draw_line ( canvas , * pointlist [ i ] , * pointlist [ i + 1 ] , color ) ;
g3_draw_line ( canvas , * pointlist [ i ] , * pointlist [ 0 ] , color ) ;
2006-03-20 17:12:09 +00:00
}
# endif
fix flash_scale ;
# define FLASH_CYCLE_RATE f1_0
2016-07-16 16:52:04 +00:00
constexpr fix Flash_rate = FLASH_CYCLE_RATE ;
2006-03-20 17:12:09 +00:00
//cycle the flashing light for when mine destroyed
2016-08-25 04:05:32 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
void flash_frame ( )
{
static fixang flash_ang = 0 ;
if ( ! Control_center_destroyed & & ! Seismic_tremor_magnitude )
return ;
if ( Endlevel_sequence )
return ;
if ( PaletteBlueAdd > 10 ) //whiting out
return ;
// flash_ang += fixmul(FLASH_CYCLE_RATE,FrameTime);
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Seismic_tremor_magnitude ) {
fix added_flash ;
added_flash = abs ( Seismic_tremor_magnitude ) ;
if ( added_flash < F1_0 )
added_flash * = 16 ;
flash_ang + = fixmul ( Flash_rate , fixmul ( FrameTime , added_flash + F1_0 ) ) ;
2015-02-05 03:03:50 +00:00
flash_scale = fix_fastsin ( flash_ang ) ;
2006-03-20 17:12:09 +00:00
flash_scale = ( flash_scale + F1_0 * 3 ) / 4 ; // gets in range 0.5 to 1.0
2013-03-03 01:03:33 +00:00
} else
# endif
{
2006-03-20 17:12:09 +00:00
flash_ang + = fixmul ( Flash_rate , FrameTime ) ;
2015-02-05 03:03:50 +00:00
flash_scale = fix_fastsin ( flash_ang ) ;
2006-03-20 17:12:09 +00:00
flash_scale = ( flash_scale + f1_0 ) / 2 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Difficulty_level = = 0 )
flash_scale = ( flash_scale + F1_0 * 3 ) / 4 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
}
2016-08-25 04:05:32 +00:00
namespace dsx {
2013-08-18 17:53:50 +00:00
static inline int is_alphablend_eclip ( int eclip_num )
{
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-08-18 17:53:50 +00:00
if ( eclip_num = = ECLIP_NUM_FORCE_FIELD )
return 1 ;
2013-03-03 01:03:33 +00:00
# endif
2013-08-18 17:53:50 +00:00
return eclip_num = = ECLIP_NUM_FUELCEN ;
}
2016-08-25 04:05:32 +00:00
}
2013-08-18 17:53:50 +00:00
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------
// Render a face.
// It would be nice to not have to pass in segnum and sidenum, but
// they are used for our hideously hacked in headlight system.
// vp is a pointer to vertex ids.
// tmap1, tmap2 are texture map ids. tmap2 is the pasty one.
2016-08-25 04:05:32 +00:00
namespace dsx {
Scrub invalid primary textures at level load
Past releases, when rendering an invalid primary texture, would
Int3() and then reset the texture to zero. Commit d767f7c changed the
logic to return without resetting the texture, since the reset seemed to
be unnecessary. Unfortunately, it is necessary. Some levels, including
those shipped with the retail game data, specify bogus primary textures
on some surfaces. After d767f7c, rendering a surface with an invalid
primary texture causes the surface to be invisible, even if it has a
valid secondary texture.
Remove the return statement added in d767f7c. Extend
validate_segment_side to validate the primary texture on the tested
side. When an invalid texture is found, reset it and log a diagnostic.
For built-in levels, log at level CON_VERBOSE since players cannot
readily fix the level. For external levels, log at level CON_URGENT so
that level authors know to fix their level before releasing it.
Fixes: d767f7cd5ee99c5a4ce50c995045f0b7e422983a ("Pass vcsegptridx to render_face")
2016-12-22 05:21:16 +00:00
static void render_face ( const segment & segp , const unsigned sidenum , const unsigned nv , const array < int , 4 > & vp , const int tmap1 , const int tmap2 , array < g3s_uvl , 4 > uvl_copy , const WALL_IS_DOORWAY_result_t wid_flags )
2006-03-20 17:12:09 +00:00
{
grs_bitmap * bm ;
2014-11-16 19:14:51 +00:00
array < cg3s_point * , 4 > pointlist ;
2006-03-20 17:12:09 +00:00
2014-11-15 03:10:06 +00:00
Assert ( nv < = pointlist . size ( ) ) ;
2006-03-20 17:12:09 +00:00
2014-09-20 23:47:27 +00:00
for ( uint_fast32_t i = 0 ; i < nv ; i + + ) {
2006-03-20 17:12:09 +00:00
pointlist [ i ] = & Segment_points [ vp [ i ] ] ;
}
2015-01-18 01:58:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-10-18 21:01:18 +00:00
( void ) segp ;
2015-01-18 01:58:33 +00:00
( void ) wid_flags ;
2016-09-11 18:49:16 +00:00
# if !DXX_USE_EDITOR
2015-10-18 21:01:18 +00:00
( void ) sidenum ;
# endif
2015-01-18 01:58:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//handle cloaked walls
if ( wid_flags & WID_CLOAKED_FLAG ) {
Scrub invalid primary textures at level load
Past releases, when rendering an invalid primary texture, would
Int3() and then reset the texture to zero. Commit d767f7c changed the
logic to return without resetting the texture, since the reset seemed to
be unnecessary. Unfortunately, it is necessary. Some levels, including
those shipped with the retail game data, specify bogus primary textures
on some surfaces. After d767f7c, rendering a surface with an invalid
primary texture causes the surface to be invisible, even if it has a
valid secondary texture.
Remove the return statement added in d767f7c. Extend
validate_segment_side to validate the primary texture on the tested
side. When an invalid texture is found, reset it and log a diagnostic.
For built-in levels, log at level CON_VERBOSE since players cannot
readily fix the level. For external levels, log at level CON_URGENT so
that level authors know to fix their level before releasing it.
Fixes: d767f7cd5ee99c5a4ce50c995045f0b7e422983a ("Pass vcsegptridx to render_face")
2016-12-22 05:21:16 +00:00
const auto wall_num = segp . sides [ sidenum ] . wall_num ;
2017-01-01 00:45:44 +00:00
gr_settransblend ( * grd_curcanv , vcwallptr ( wall_num ) - > cloak_value , GR_BLEND_NORMAL ) ;
2016-02-12 04:02:28 +00:00
const uint8_t color = BM_XRGB ( 0 , 0 , 0 ) ;
2016-02-12 04:02:28 +00:00
// set to black (matters for s3)
2006-03-20 17:12:09 +00:00
2017-02-11 21:42:41 +00:00
g3_draw_poly ( * grd_curcanv , nv , pointlist , color ) ; // draw as flat poly
2017-01-01 00:45:44 +00:00
gr_settransblend ( * grd_curcanv , GR_FADE_OFF , GR_BLEND_NORMAL ) ;
2006-03-20 17:12:09 +00:00
return ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
if ( tmap1 > = NumTextures ) {
Int3 ( ) ;
}
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2016-09-11 18:49:14 +00:00
grs_bitmap * bm2 = nullptr ;
2015-12-18 04:08:24 +00:00
if ( ! CGameArg . DbgUseOldTextureMerge )
{
2006-03-20 17:12:09 +00:00
PIGGY_PAGE_IN ( Textures [ tmap1 ] ) ;
bm = & GameBitmaps [ Textures [ tmap1 ] . index ] ;
if ( tmap2 ) {
PIGGY_PAGE_IN ( Textures [ tmap2 & 0x3FFF ] ) ;
bm2 = & GameBitmaps [ Textures [ tmap2 & 0x3FFF ] . index ] ;
2017-01-15 00:03:13 +00:00
if ( bm2 - > get_flag_mask ( BM_FLAG_SUPER_TRANSPARENT ) )
{
2016-09-11 18:49:14 +00:00
bm2 = nullptr ;
2014-11-30 22:09:19 +00:00
bm = & texmerge_get_cached_bitmap ( tmap1 , tmap2 ) ;
2016-09-11 18:49:14 +00:00
}
2006-03-20 17:12:09 +00:00
}
2007-06-10 16:21:53 +00:00
} else
2006-03-20 17:12:09 +00:00
# endif
// New code for overlapping textures...
if ( tmap2 ! = 0 ) {
2014-11-30 22:09:19 +00:00
bm = & texmerge_get_cached_bitmap ( tmap1 , tmap2 ) ;
2006-03-20 17:12:09 +00:00
} else {
bm = & GameBitmaps [ Textures [ tmap1 ] . index ] ;
PIGGY_PAGE_IN ( Textures [ tmap1 ] ) ;
}
2017-01-15 00:03:13 +00:00
assert ( ! bm - > get_flag_mask ( BM_FLAG_PAGED_OUT ) ) ;
2006-03-20 17:12:09 +00:00
2016-09-11 18:49:14 +00:00
array < g3s_lrgb , 4 > dyn_light ;
2016-09-11 18:49:14 +00:00
const auto seismic_tremor_magnitude = Seismic_tremor_magnitude ;
const auto control_center_destroyed = Control_center_destroyed ;
const auto need_flashing_lights = ( control_center_destroyed | seismic_tremor_magnitude ) ; //make lights flash
2006-03-20 17:12:09 +00:00
//set light values for each vertex & build pointlist
2014-09-20 23:47:27 +00:00
for ( uint_fast32_t i = 0 ; i < nv ; i + + )
2006-03-20 17:12:09 +00:00
{
2016-09-11 18:49:14 +00:00
auto & dli = dyn_light [ i ] ;
auto & uvli = uvl_copy [ i ] ;
auto & Dlvpi = Dynamic_light [ vp [ i ] ] ;
dli . r = dli . g = dli . b = uvli . l ;
2011-09-26 23:31:19 +00:00
//the uvl struct has static light already in it
//scale static light for destruction effect
2016-09-11 18:49:14 +00:00
if ( need_flashing_lights ) //make lights flash
uvli . l = fixmul ( flash_scale , uvli . l ) ;
2011-09-26 23:31:19 +00:00
//add in dynamic light (from explosions, etc.)
2016-09-11 18:49:14 +00:00
uvli . l + = ( Dlvpi . r + Dlvpi . g + Dlvpi . b ) / 3 ;
2011-09-26 23:31:19 +00:00
//saturate at max value
2016-09-11 18:49:14 +00:00
if ( uvli . l > MAX_LIGHT )
uvli . l = MAX_LIGHT ;
2011-09-26 23:31:19 +00:00
// And now the same for the ACTUAL (rgb) light we want to use
//scale static light for destruction effect
2016-09-11 18:49:15 +00:00
if ( need_flashing_lights ) //make lights flash
2011-04-07 20:32:51 +00:00
{
2016-09-11 18:49:14 +00:00
dli . g = dli . b = fixmul ( flash_scale , uvli . l ) ;
2016-09-11 18:49:15 +00:00
dli . r = ( ! seismic_tremor_magnitude & & PlayerCfg . DynLightColor )
? fixmul ( std : : max ( static_cast < double > ( flash_scale ) , f0_5 * 1.5 ) , uvli . l ) // let the mine glow red a little
: dli . g ;
2006-03-20 17:12:09 +00:00
}
2011-09-26 23:31:19 +00:00
// add light color
2016-09-11 18:49:14 +00:00
dli . r + = Dlvpi . r ;
dli . g + = Dlvpi . g ;
dli . b + = Dlvpi . b ;
2011-09-26 23:31:19 +00:00
// saturate at max value
2016-09-11 18:49:14 +00:00
if ( dli . r > MAX_LIGHT )
dli . r = MAX_LIGHT ;
if ( dli . g > MAX_LIGHT )
dli . g = MAX_LIGHT ;
if ( dli . b > MAX_LIGHT )
dli . b = MAX_LIGHT ;
2013-04-18 10:08:51 +00:00
if ( PlayerCfg . AlphaEffects ) // due to additive blending, transparent sprites will become invivible in font of white surfaces (lamps). Fix that with a little desaturation
{
2016-09-11 18:49:14 +00:00
dli . r * = .93 ;
dli . g * = .93 ;
dli . b * = .93 ;
2013-04-18 10:08:51 +00:00
}
2006-03-20 17:12:09 +00:00
}
2015-08-21 03:12:35 +00:00
bool alpha = false ;
if ( PlayerCfg . AlphaBlendEClips & & is_alphablend_eclip ( TmapInfo [ tmap1 ] . eclip_num ) ) // set nice transparency/blending for some special effects (if we do more, we should maybe use switch here)
{
alpha = true ;
2017-01-01 00:45:44 +00:00
gr_settransblend ( * grd_curcanv , GR_FADE_OFF , GR_BLEND_ADDITIVE_C ) ;
2015-08-21 03:12:35 +00:00
}
2011-02-23 16:46:39 +00:00
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
if ( ( Render_only_bottom ) & & ( sidenum = = WBOTTOM ) )
2017-02-11 21:42:39 +00:00
g3_draw_tmap ( * grd_curcanv , nv , pointlist , uvl_copy , dyn_light , GameBitmaps [ Textures [ Bottom_bitmap_num ] . index ] ) ;
2006-03-20 17:12:09 +00:00
else
# endif
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2006-03-20 17:12:09 +00:00
if ( bm2 ) {
2017-02-11 21:42:41 +00:00
g3_draw_tmap_2 ( * grd_curcanv , nv , pointlist , uvl_copy , dyn_light , * bm , * bm2 , ( ( tmap2 & 0xC000 ) > > 14 ) & 3 ) ;
2006-03-20 17:12:09 +00:00
} else
# endif
2017-02-11 21:42:39 +00:00
g3_draw_tmap ( * grd_curcanv , nv , pointlist , uvl_copy , dyn_light , * bm ) ;
2006-03-20 17:12:09 +00:00
2015-08-21 03:12:35 +00:00
if ( alpha )
2017-01-01 00:45:44 +00:00
gr_settransblend ( * grd_curcanv , GR_FADE_OFF , GR_BLEND_NORMAL ) ; // revert any transparency/blending setting back to normal
2011-02-23 16:46:39 +00:00
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
2017-02-11 21:42:41 +00:00
if ( Outline_mode ) draw_outline ( * grd_curcanv , nv , & pointlist [ 0 ] ) ;
2006-03-20 17:12:09 +00:00
# endif
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------
// Only called if editor active.
// Used to determine which face was clicked on.
Scrub invalid primary textures at level load
Past releases, when rendering an invalid primary texture, would
Int3() and then reset the texture to zero. Commit d767f7c changed the
logic to return without resetting the texture, since the reset seemed to
be unnecessary. Unfortunately, it is necessary. Some levels, including
those shipped with the retail game data, specify bogus primary textures
on some surfaces. After d767f7c, rendering a surface with an invalid
primary texture causes the surface to be invisible, even if it has a
valid secondary texture.
Remove the return statement added in d767f7c. Extend
validate_segment_side to validate the primary texture on the tested
side. When an invalid texture is found, reset it and log a diagnostic.
For built-in levels, log at level CON_VERBOSE since players cannot
readily fix the level. For external levels, log at level CON_URGENT so
that level authors know to fix their level before releasing it.
Fixes: d767f7cd5ee99c5a4ce50c995045f0b7e422983a ("Pass vcsegptridx to render_face")
2016-12-22 05:21:16 +00:00
static void check_face ( const vsegidx_t segnum , const unsigned sidenum , const unsigned facenum , const unsigned nv , const array < int , 4 > & vp , const int tmap1 , const int tmap2 , const array < g3s_uvl , 4 > & uvl_copy )
2006-03-20 17:12:09 +00:00
{
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
if ( _search_mode ) {
int save_lighting ;
2014-09-13 00:34:18 +00:00
array < g3s_lrgb , 4 > dyn_light { } ;
2014-11-16 19:14:51 +00:00
array < cg3s_point * , 4 > pointlist ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2015-01-18 01:58:33 +00:00
( void ) tmap1 ;
( void ) tmap2 ;
# else
2006-03-20 17:12:09 +00:00
grs_bitmap * bm ;
if ( tmap2 > 0 )
2014-11-30 22:09:19 +00:00
bm = & texmerge_get_cached_bitmap ( tmap1 , tmap2 ) ;
2006-03-20 17:12:09 +00:00
else
bm = & GameBitmaps [ Textures [ tmap1 ] . index ] ;
2012-04-15 14:15:21 +00:00
# endif
2014-09-20 23:47:27 +00:00
for ( uint_fast32_t i = 0 ; i < nv ; i + + ) {
2014-09-13 00:34:18 +00:00
dyn_light [ i ] . r = dyn_light [ i ] . g = dyn_light [ i ] . b = uvl_copy [ i ] . l ;
2006-03-20 17:12:09 +00:00
pointlist [ i ] = & Segment_points [ vp [ i ] ] ;
}
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2012-04-15 05:45:45 +00:00
ogl_end_frame ( ) ;
# endif
2016-05-21 17:24:51 +00:00
{
2016-02-12 04:02:28 +00:00
uint8_t color = 0 ;
2016-12-29 03:27:09 +00:00
gr_pixel ( * grd_curcanv , _search_x , _search_y , color ) ; //set our search pixel to color zero
2016-05-21 17:24:51 +00:00
}
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2017-02-11 21:42:33 +00:00
ogl_start_frame ( * grd_curcanv ) ;
2012-04-15 05:45:45 +00:00
# endif
2016-02-12 04:02:28 +00:00
{
const uint8_t color = 1 ;
2012-04-15 05:45:45 +00:00
save_lighting = Lighting_on ;
Lighting_on = 2 ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2017-02-11 21:42:41 +00:00
g3_draw_poly ( * grd_curcanv , nv , pointlist , color ) ;
2012-04-15 05:45:45 +00:00
# else
2016-02-12 04:02:28 +00:00
( void ) color ;
2017-02-11 21:42:39 +00:00
g3_draw_tmap ( * grd_curcanv , nv , pointlist , uvl_copy , dyn_light , * bm ) ;
2012-04-15 05:45:45 +00:00
# endif
2016-02-12 04:02:28 +00:00
}
2012-04-15 05:45:45 +00:00
Lighting_on = save_lighting ;
2006-03-20 17:12:09 +00:00
2014-11-30 22:09:18 +00:00
if ( gr_ugpixel ( grd_curcanv - > cv_bitmap , _search_x , _search_y ) = = 1 ) {
2006-03-20 17:12:09 +00:00
found_seg = segnum ;
2014-12-13 17:47:01 +00:00
found_obj = object_none ;
2006-03-20 17:12:09 +00:00
found_side = sidenum ;
found_face = facenum ;
}
}
2014-09-12 23:13:32 +00:00
# else
( void ) segnum ;
( void ) sidenum ;
( void ) facenum ;
( void ) nv ;
( void ) vp ;
( void ) tmap1 ;
( void ) tmap2 ;
2014-09-13 00:34:18 +00:00
( void ) uvl_copy ;
2006-03-20 17:12:09 +00:00
# endif
2014-09-12 23:13:32 +00:00
}
2014-09-13 22:05:52 +00:00
template < std : : size_t . . . N >
Scrub invalid primary textures at level load
Past releases, when rendering an invalid primary texture, would
Int3() and then reset the texture to zero. Commit d767f7c changed the
logic to return without resetting the texture, since the reset seemed to
be unnecessary. Unfortunately, it is necessary. Some levels, including
those shipped with the retail game data, specify bogus primary textures
on some surfaces. After d767f7c, rendering a surface with an invalid
primary texture causes the surface to be invisible, even if it has a
valid secondary texture.
Remove the return statement added in d767f7c. Extend
validate_segment_side to validate the primary texture on the tested
side. When an invalid texture is found, reset it and log a diagnostic.
For built-in levels, log at level CON_VERBOSE since players cannot
readily fix the level. For external levels, log at level CON_URGENT so
that level authors know to fix their level before releasing it.
Fixes: d767f7cd5ee99c5a4ce50c995045f0b7e422983a ("Pass vcsegptridx to render_face")
2016-12-22 05:21:16 +00:00
static inline void check_render_face ( index_sequence < N . . . > , const vcsegptridx_t segnum , const unsigned sidenum , const unsigned facenum , const array < int , 4 > & ovp , const int tmap1 , const int tmap2 , const array < uvl , 4 > & uvlp , const WALL_IS_DOORWAY_result_t wid_flags , const std : : size_t nv )
2014-09-12 23:13:32 +00:00
{
2016-09-14 01:59:33 +00:00
const array < int , 4 > vp { { ovp [ N ] . . . } } ;
2014-09-13 22:05:52 +00:00
const array < g3s_uvl , 4 > uvl_copy { {
{ uvlp [ N ] . u , uvlp [ N ] . v , uvlp [ N ] . l } . . .
} } ;
2014-09-13 00:34:18 +00:00
render_face ( segnum , sidenum , nv , vp , tmap1 , tmap2 , uvl_copy , wid_flags ) ;
check_face ( segnum , sidenum , facenum , nv , vp , tmap1 , tmap2 , uvl_copy ) ;
2014-09-12 23:13:32 +00:00
}
2006-03-20 17:12:09 +00:00
2014-09-13 22:05:52 +00:00
template < std : : size_t N0 , std : : size_t N1 , std : : size_t N2 , std : : size_t N3 >
Scrub invalid primary textures at level load
Past releases, when rendering an invalid primary texture, would
Int3() and then reset the texture to zero. Commit d767f7c changed the
logic to return without resetting the texture, since the reset seemed to
be unnecessary. Unfortunately, it is necessary. Some levels, including
those shipped with the retail game data, specify bogus primary textures
on some surfaces. After d767f7c, rendering a surface with an invalid
primary texture causes the surface to be invisible, even if it has a
valid secondary texture.
Remove the return statement added in d767f7c. Extend
validate_segment_side to validate the primary texture on the tested
side. When an invalid texture is found, reset it and log a diagnostic.
For built-in levels, log at level CON_VERBOSE since players cannot
readily fix the level. For external levels, log at level CON_URGENT so
that level authors know to fix their level before releasing it.
Fixes: d767f7cd5ee99c5a4ce50c995045f0b7e422983a ("Pass vcsegptridx to render_face")
2016-12-22 05:21:16 +00:00
static inline void check_render_face ( index_sequence < N0 , N1 , N2 , N3 > is , const vcsegptridx_t segnum , const unsigned sidenum , const unsigned facenum , const array < int , 4 > & vp , const int tmap1 , const int tmap2 , const array < uvl , 4 > & uvlp , const WALL_IS_DOORWAY_result_t wid_flags )
2014-09-13 22:05:52 +00:00
{
check_render_face ( is , segnum , sidenum , facenum , vp , tmap1 , tmap2 , uvlp , wid_flags , 4 ) ;
}
/* Avoid default constructing final element of uvl_copy; if any members
* are default constructed , gcc zero initializes all members .
*/
template < std : : size_t N0 , std : : size_t N1 , std : : size_t N2 >
Scrub invalid primary textures at level load
Past releases, when rendering an invalid primary texture, would
Int3() and then reset the texture to zero. Commit d767f7c changed the
logic to return without resetting the texture, since the reset seemed to
be unnecessary. Unfortunately, it is necessary. Some levels, including
those shipped with the retail game data, specify bogus primary textures
on some surfaces. After d767f7c, rendering a surface with an invalid
primary texture causes the surface to be invisible, even if it has a
valid secondary texture.
Remove the return statement added in d767f7c. Extend
validate_segment_side to validate the primary texture on the tested
side. When an invalid texture is found, reset it and log a diagnostic.
For built-in levels, log at level CON_VERBOSE since players cannot
readily fix the level. For external levels, log at level CON_URGENT so
that level authors know to fix their level before releasing it.
Fixes: d767f7cd5ee99c5a4ce50c995045f0b7e422983a ("Pass vcsegptridx to render_face")
2016-12-22 05:21:16 +00:00
static inline void check_render_face ( index_sequence < N0 , N1 , N2 > , const vcsegptridx_t segnum , const unsigned sidenum , const unsigned facenum , const array < int , 4 > & vp , const int tmap1 , const int tmap2 , const array < uvl , 4 > & uvlp , const WALL_IS_DOORWAY_result_t wid_flags )
2014-09-13 22:05:52 +00:00
{
check_render_face ( index_sequence < N0 , N1 , N2 , 3 > ( ) , segnum , sidenum , facenum , vp , tmap1 , tmap2 , uvlp , wid_flags , 3 ) ;
}
2016-07-16 16:52:04 +00:00
constexpr fix Tulate_min_dot = ( F1_0 / 4 ) ;
2006-03-20 17:12:09 +00:00
//--unused-- fix Tulate_min_ratio = (2*F1_0);
2016-07-16 16:52:04 +00:00
constexpr fix Min_n0_n1_dot = ( F1_0 * 15 / 16 ) ;
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------------
// Render a side.
// Check for normal facing. If so, render faces on side dictated by sidep->type.
2016-08-25 04:05:32 +00:00
namespace dsx {
2014-10-02 03:02:34 +00:00
static void render_side ( const vcsegptridx_t segp , int sidenum )
2006-03-20 17:12:09 +00:00
{
2006-07-11 16:14:17 +00:00
fix min_dot , max_dot ;
2014-09-06 22:26:11 +00:00
auto wid_flags = WALL_IS_DOORWAY ( segp , sidenum ) ;
2006-03-20 17:12:09 +00:00
if ( ! ( wid_flags & WID_RENDER_FLAG ) ) //if (WALL_IS_DOORWAY(segp, sidenum) == WID_NO_WALL)
return ;
2014-12-14 05:23:00 +00:00
const auto vertnum_list = get_side_verts ( segp , sidenum ) ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
// Regardless of whether this side is comprised of a single quad, or two triangles, we need to know one normal, so
// deal with it, get the dot product.
2016-01-03 20:21:35 +00:00
const auto sidep = & segp - > sides [ sidenum ] ;
2014-11-13 03:14:36 +00:00
const unsigned which_vertnum =
# if defined(DXX_BUILD_DESCENT_II)
/* Silly, but consistent with how it was at release */
( sidep - > get_type ( ) = = SIDE_IS_QUAD ) ? 0 :
2013-03-03 01:03:33 +00:00
# endif
2014-11-13 03:14:36 +00:00
( sidep - > get_type ( ) = = SIDE_IS_TRI_13 )
? 1
: 0 ;
const auto tvec = vm_vec_normalized_quick ( vm_vec_sub ( Viewer_eye , Vertices [ vertnum_list [ which_vertnum ] ] ) ) ;
2016-01-03 20:21:35 +00:00
auto & normals = sidep - > normals ;
2014-11-13 03:14:36 +00:00
const auto v_dot_n0 = vm_vec_dot ( tvec , normals [ 0 ] ) ;
2006-03-20 17:12:09 +00:00
// ========== Mark: Here is the change...beginning here: ==========
2014-09-13 22:05:52 +00:00
index_sequence < 0 , 1 , 2 , 3 > is_quad ;
2014-09-06 22:36:58 +00:00
if ( sidep - > get_type ( ) = = SIDE_IS_QUAD ) {
2006-03-20 17:12:09 +00:00
if ( v_dot_n0 > = 0 ) {
2014-10-02 03:02:34 +00:00
check_render_face ( is_quad , segp , sidenum , 0 , vertnum_list , sidep - > tmap_num , sidep - > tmap_num2 , sidep - > uvls , wid_flags ) ;
2006-03-20 17:12:09 +00:00
}
} else {
// ========== Mark: The change ends here. ==========
// Although this side has been triangulated, because it is not planar, see if it is acceptable
// to render it as a single quadrilateral. This is a function of how far away the viewer is, how non-planar
// the face is, how normal to the surfaces the view is.
// Now, if both dot products are close to 1.0, then render two triangles as a single quad.
2014-11-13 03:14:36 +00:00
const auto v_dot_n1 = vm_vec_dot ( tvec , normals [ 1 ] ) ;
2006-03-20 17:12:09 +00:00
if ( v_dot_n0 < v_dot_n1 ) {
min_dot = v_dot_n0 ;
max_dot = v_dot_n1 ;
} else {
min_dot = v_dot_n1 ;
max_dot = v_dot_n0 ;
}
// Determine whether to detriangulate side: (speed hack, assumes Tulate_min_ratio == F1_0*2, should fixmul(min_dot, Tulate_min_ratio))
2014-10-02 03:02:34 +00:00
if ( DETRIANGULATION & & ( ( min_dot + F1_0 / 256 > max_dot ) | | ( ( Viewer - > segnum ! = segp ) & & ( min_dot > Tulate_min_dot ) & & ( max_dot < min_dot * 2 ) ) ) ) {
2006-03-20 17:12:09 +00:00
fix n0_dot_n1 ;
// The other detriangulation code doesn't deal well with badly non-planar sides.
2014-09-28 21:11:48 +00:00
n0_dot_n1 = vm_vec_dot ( normals [ 0 ] , normals [ 1 ] ) ;
2006-03-20 17:12:09 +00:00
if ( n0_dot_n1 < Min_n0_n1_dot )
goto im_so_ashamed ;
2014-10-02 03:02:34 +00:00
check_render_face ( is_quad , segp , sidenum , 0 , vertnum_list , sidep - > tmap_num , sidep - > tmap_num2 , sidep - > uvls , wid_flags ) ;
2006-03-20 17:12:09 +00:00
} else {
im_so_ashamed : ;
2014-09-06 22:36:58 +00:00
if ( sidep - > get_type ( ) = = SIDE_IS_TRI_02 ) {
2006-03-20 17:12:09 +00:00
if ( v_dot_n0 > = 0 ) {
2014-10-02 03:02:34 +00:00
check_render_face ( index_sequence < 0 , 1 , 2 > ( ) , segp , sidenum , 0 , vertnum_list , sidep - > tmap_num , sidep - > tmap_num2 , sidep - > uvls , wid_flags ) ;
2006-03-20 17:12:09 +00:00
}
if ( v_dot_n1 > = 0 ) {
2014-09-13 22:05:52 +00:00
// want to render from vertices 0, 2, 3 on side
2014-10-02 03:02:34 +00:00
check_render_face ( index_sequence < 0 , 2 , 3 > ( ) , segp , sidenum , 1 , vertnum_list , sidep - > tmap_num , sidep - > tmap_num2 , sidep - > uvls , wid_flags ) ;
2006-03-20 17:12:09 +00:00
}
2014-09-06 22:36:58 +00:00
} else if ( sidep - > get_type ( ) = = SIDE_IS_TRI_13 ) {
2006-03-20 17:12:09 +00:00
if ( v_dot_n1 > = 0 ) {
2014-09-13 22:05:52 +00:00
// rendering 1,2,3, so just skip 0
2014-10-02 03:02:34 +00:00
check_render_face ( index_sequence < 1 , 2 , 3 > ( ) , segp , sidenum , 1 , vertnum_list , sidep - > tmap_num , sidep - > tmap_num2 , sidep - > uvls , wid_flags ) ;
2006-03-20 17:12:09 +00:00
}
if ( v_dot_n0 > = 0 ) {
2014-09-13 22:05:52 +00:00
// want to render from vertices 0,1,3
2014-10-02 03:02:34 +00:00
check_render_face ( index_sequence < 0 , 1 , 3 > ( ) , segp , sidenum , 0 , vertnum_list , sidep - > tmap_num , sidep - > tmap_num2 , sidep - > uvls , wid_flags ) ;
2006-03-20 17:12:09 +00:00
}
} else
2014-09-06 22:36:58 +00:00
throw side : : illegal_type ( segp , sidep ) ;
2006-03-20 17:12:09 +00:00
}
}
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2014-10-02 03:02:34 +00:00
static void render_object_search ( const vobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
int changed = 0 ;
//note that we draw each pixel object twice, since we cannot control
//what color the object draws in, so we try color 0, then color 1,
//in case the object itself is rendering color 0
2016-02-12 04:02:28 +00:00
{
const uint8_t color = 0 ;
2016-02-12 04:02:28 +00:00
//set our search pixel to color zero
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2012-04-15 05:45:45 +00:00
ogl_end_frame ( ) ;
2013-01-17 05:14:44 +00:00
// For OpenGL we use gr_rect instead of gr_pixel,
// because in some implementations (like my Macbook Pro 5,1)
// point smoothing can't be turned off.
// Point smoothing would change the pixel to dark grey, but it MUST be black.
// Making a 3x3 rectangle wouldn't matter
// (but it only seems to draw a single pixel anyway)
2017-01-01 00:45:45 +00:00
gr_rect ( * grd_curcanv , _search_x - 1 , _search_y - 1 , _search_x + 1 , _search_y + 1 , color ) ;
2013-01-17 05:14:44 +00:00
2017-02-11 21:42:33 +00:00
ogl_start_frame ( * grd_curcanv ) ;
2013-01-17 05:14:44 +00:00
# else
2016-12-29 03:27:09 +00:00
gr_pixel ( * grd_curcanv , _search_x , _search_y , color ) ;
2012-04-15 05:45:45 +00:00
# endif
2016-02-12 04:02:28 +00:00
}
2006-03-20 17:12:09 +00:00
render_object ( obj ) ;
2014-11-30 22:09:18 +00:00
if ( gr_ugpixel ( grd_curcanv - > cv_bitmap , _search_x , _search_y ) ! = 0 )
2006-03-20 17:12:09 +00:00
changed = 1 ;
2016-02-12 04:02:28 +00:00
{
const uint8_t color = 1 ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2012-04-15 05:45:45 +00:00
ogl_end_frame ( ) ;
2017-01-01 00:45:45 +00:00
gr_rect ( * grd_curcanv , _search_x - 1 , _search_y - 1 , _search_x + 1 , _search_y + 1 , color ) ;
2017-02-11 21:42:33 +00:00
ogl_start_frame ( * grd_curcanv ) ;
2013-01-17 05:14:44 +00:00
# else
2016-12-29 03:27:09 +00:00
gr_pixel ( * grd_curcanv , _search_x , _search_y , color ) ;
2012-04-15 05:45:45 +00:00
# endif
2016-02-12 04:02:28 +00:00
}
2006-03-20 17:12:09 +00:00
render_object ( obj ) ;
2014-11-30 22:09:18 +00:00
if ( gr_ugpixel ( grd_curcanv - > cv_bitmap , _search_x , _search_y ) ! = 1 )
2006-03-20 17:12:09 +00:00
changed = 1 ;
if ( changed ) {
2013-12-26 22:21:16 +00:00
if ( obj - > segnum ! = segment_none )
2015-07-13 01:09:37 +00:00
Cursegp = segptridx ( obj - > segnum ) ;
2014-12-13 17:47:01 +00:00
found_seg = segment_none ;
found_obj = obj ;
2006-03-20 17:12:09 +00:00
}
}
# endif
2016-08-25 04:05:32 +00:00
namespace dsx {
2014-12-13 04:11:12 +00:00
static void do_render_object ( const vobjptridx_t obj , window_rendered_data & window )
2006-03-20 17:12:09 +00:00
{
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
int save_3d_outline = 0 ;
# endif
int count = 0 ;
# ifndef NDEBUG
2014-01-11 23:02:43 +00:00
if ( object_rendered [ obj ] ) { //already rendered this...
2006-03-20 17:12:09 +00:00
Int3 ( ) ; //get Matt!!!
return ;
}
2014-10-02 03:02:34 +00:00
object_rendered [ obj ] = true ;
2006-03-20 17:12:09 +00:00
# endif
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Newdemo_state = = ND_STATE_PLAYBACK )
{
2014-01-11 23:02:43 +00:00
if ( ( DemoDoingLeft = = 6 | | DemoDoingRight = = 6 ) & & obj - > type = = OBJ_PLAYER )
2006-03-20 17:12:09 +00:00
{
// A nice fat hack: keeps the player ship from showing up in the
// small extra view when guiding a missile in the big window
return ;
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// Added by MK on 09/07/94 (at about 5:28 pm, CDT, on a beautiful, sunny late summer day!) so
// that the guided missile system will know what objects to look at.
// I didn't know we had guided missiles before the release of D1. --MK
2015-10-18 21:01:19 +00:00
if ( obj - > type = = OBJ_ROBOT )
2014-12-13 04:11:12 +00:00
window . rendered_robots . emplace_back ( obj ) ;
2006-03-20 17:12:09 +00:00
2014-01-11 23:02:43 +00:00
if ( ( count + + > MAX_OBJECTS ) | | ( obj - > next = = obj ) ) {
2006-03-20 17:12:09 +00:00
Int3 ( ) ; // infinite loop detected
2013-12-26 22:21:16 +00:00
obj - > next = object_none ; // won't this clean things up?
2006-03-20 17:12:09 +00:00
return ; // get out of this infinite loop!
}
//g3_draw_object(obj->class_id,&obj->pos,&obj->orient,obj->size);
//check for editor object
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2014-01-11 23:02:43 +00:00
if ( EditorWindow & & obj = = Cur_object_index ) {
2006-03-20 17:12:09 +00:00
save_3d_outline = g3d_interp_outline ;
g3d_interp_outline = 1 ;
}
# endif
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
if ( _search_mode )
render_object_search ( obj ) ;
else
# endif
//NOTE LINK TO ABOVE
render_object ( obj ) ;
2015-10-18 21:01:19 +00:00
for ( auto n = obj - > attached_obj ; n ! = object_none ; )
{
2015-12-22 04:18:51 +00:00
const auto & & o = obj . absolute_sibling ( n ) ;
2014-12-23 04:20:27 +00:00
Assert ( o - > type = = OBJ_FIREBALL ) ;
Assert ( o - > control_type = = CT_EXPLOSION ) ;
Assert ( o - > flags & OF_ATTACHED ) ;
2015-10-18 21:01:19 +00:00
n = o - > ctype . expl_info . next_attach ;
2006-03-20 17:12:09 +00:00
2014-12-23 04:20:27 +00:00
render_object ( o ) ;
2006-03-20 17:12:09 +00:00
}
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2014-01-11 23:02:43 +00:00
if ( EditorWindow & & obj = = Cur_object_index )
2006-03-20 17:12:09 +00:00
g3d_interp_outline = save_3d_outline ;
# endif
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
}
//increment counter for checking if points rotated
//This must be called at the start of the frame if rotate_list() will be used
void render_start_frame ( )
{
2014-08-20 01:28:22 +00:00
if ( s_current_generation = = std : : numeric_limits < decltype ( s_current_generation ) > : : max ( ) )
{
Segment_points = { } ;
s_current_generation = 0 ;
2006-03-20 17:12:09 +00:00
}
2014-08-20 01:28:22 +00:00
+ + s_current_generation ;
2006-03-20 17:12:09 +00:00
}
//Given a lit of point numbers, rotate any that haven't been rotated this frame
2014-10-26 03:24:09 +00:00
g3s_codes rotate_list ( std : : size_t nv , const int * pointnumlist )
2006-03-20 17:12:09 +00:00
{
g3s_codes cc ;
2015-10-18 21:01:18 +00:00
const auto current_generation = s_current_generation ;
const auto cheats_acid = cheats . acid ;
float f = likely ( ! cheats_acid )
? 0.0f /* unused */
: 2.0f * ( static_cast < float > ( timer_query ( ) ) / F1_0 ) ;
2006-03-20 17:12:09 +00:00
2015-02-05 03:03:49 +00:00
range_for ( const auto pnum , unchecked_partial_range ( pointnumlist , nv ) )
2014-12-13 16:57:09 +00:00
{
2014-10-02 03:02:35 +00:00
auto & pnt = Segment_points [ pnum ] ;
2015-10-18 21:01:18 +00:00
if ( pnt . p3_last_generation ! = current_generation )
2011-02-14 21:27:07 +00:00
{
2015-10-18 21:01:18 +00:00
pnt . p3_last_generation = current_generation ;
const auto & v = Vertices [ pnum ] ;
vertex tmpv ;
g3_rotate_point ( pnt , likely ( ! cheats_acid ) ? v : (
tmpv = v ,
tmpv . x + = fl2f ( sinf ( f + f2fl ( tmpv . x ) ) ) ,
tmpv . y + = fl2f ( sinf ( f * 1.5f + f2fl ( tmpv . y ) ) ) ,
tmpv . z + = fl2f ( sinf ( f * 2.5f + f2fl ( tmpv . z ) ) ) ,
tmpv
) ) ;
2006-03-20 17:12:09 +00:00
}
2014-10-02 03:02:35 +00:00
cc . uand & = pnt . p3_codes ;
cc . uor | = pnt . p3_codes ;
2006-03-20 17:12:09 +00:00
}
return cc ;
}
//Given a lit of point numbers, project any that haven't been projected
2017-01-15 00:03:12 +00:00
static void project_list ( const array < int , 8 > & pointnumlist )
2006-03-20 17:12:09 +00:00
{
2015-02-05 03:03:49 +00:00
range_for ( const auto pnum , pointnumlist )
2014-08-16 18:14:00 +00:00
{
2017-01-15 00:03:12 +00:00
auto & p = Segment_points [ pnum ] ;
if ( ! ( p . p3_flags & PF_PROJECTED ) )
g3_project_point ( p ) ;
2006-03-20 17:12:09 +00:00
}
}
// -----------------------------------------------------------------------------------
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL
2016-08-25 04:05:32 +00:00
namespace dsx {
2015-07-21 02:57:27 +00:00
static void render_segment ( const vcsegptridx_t seg )
2006-03-20 17:12:09 +00:00
{
int sn ;
2015-10-18 21:01:18 +00:00
if ( ! rotate_list ( seg - > verts ) . uand )
{ //all off screen?
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Viewer - > type ! = OBJ_ROBOT )
2013-03-03 01:03:33 +00:00
# endif
2015-07-21 02:57:27 +00:00
Automap_visited [ seg ] = 1 ;
2006-03-20 17:12:09 +00:00
for ( sn = 0 ; sn < MAX_SIDES_PER_SEGMENT ; sn + + )
render_side ( seg , sn ) ;
}
//draw any objects that happen to be in this segment
//sort objects!
//object_sort_segment_objects( seg );
}
2016-08-25 04:05:32 +00:00
}
2013-10-26 19:14:51 +00:00
# endif
2006-03-20 17:12:09 +00:00
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
2016-07-16 16:52:04 +00:00
constexpr fix CROSS_WIDTH = i2f ( 8 ) ;
constexpr fix CROSS_HEIGHT = i2f ( 8 ) ;
2015-03-26 02:42:26 +00:00
2006-03-20 17:12:09 +00:00
//draw outline for curside
2014-10-02 03:02:34 +00:00
static void outline_seg_side ( const vcsegptr_t seg , int _side , int edge , int vert )
2006-03-20 17:12:09 +00:00
{
2015-10-18 21:01:18 +00:00
if ( ! rotate_list ( seg - > verts ) . uand )
{ //all off screen?
2006-03-20 17:12:09 +00:00
g3s_point * pnt ;
//render curedge of curside of curseg in green
2016-02-12 04:02:28 +00:00
const uint8_t color = BM_XRGB ( 0 , 63 , 0 ) ;
auto & sv = Side_to_verts [ _side ] ;
2017-02-11 21:42:33 +00:00
g3_draw_line ( * grd_curcanv , Segment_points [ seg - > verts [ sv [ edge ] ] ] , Segment_points [ seg - > verts [ sv [ ( edge + 1 ) % 4 ] ] ] , color ) ;
2006-03-20 17:12:09 +00:00
//draw a little cross at the current vert
pnt = & Segment_points [ seg - > verts [ Side_to_verts [ _side ] [ vert ] ] ] ;
2014-11-13 03:21:33 +00:00
g3_project_point ( * pnt ) ; //make sure projected
2006-03-20 17:12:09 +00:00
// gr_line(pnt->p3_sx-CROSS_WIDTH,pnt->p3_sy,pnt->p3_sx+CROSS_WIDTH,pnt->p3_sy);
// gr_line(pnt->p3_sx,pnt->p3_sy-CROSS_HEIGHT,pnt->p3_sx,pnt->p3_sy+CROSS_HEIGHT);
2016-12-29 03:27:10 +00:00
gr_line ( * grd_curcanv , pnt - > p3_sx - CROSS_WIDTH , pnt - > p3_sy , pnt - > p3_sx , pnt - > p3_sy - CROSS_HEIGHT , color ) ;
gr_line ( * grd_curcanv , pnt - > p3_sx , pnt - > p3_sy - CROSS_HEIGHT , pnt - > p3_sx + CROSS_WIDTH , pnt - > p3_sy , color ) ;
gr_line ( * grd_curcanv , pnt - > p3_sx + CROSS_WIDTH , pnt - > p3_sy , pnt - > p3_sx , pnt - > p3_sy + CROSS_HEIGHT , color ) ;
gr_line ( * grd_curcanv , pnt - > p3_sx , pnt - > p3_sy + CROSS_HEIGHT , pnt - > p3_sx - CROSS_WIDTH , pnt - > p3_sy , color ) ;
2006-03-20 17:12:09 +00:00
}
}
2014-08-18 00:11:51 +00:00
# endif
2006-03-20 17:12:09 +00:00
# endif
2014-11-23 21:53:46 +00:00
static ubyte code_window_point ( fix x , fix y , const rect & w )
2006-03-20 17:12:09 +00:00
{
ubyte code = 0 ;
2014-11-23 21:53:46 +00:00
if ( x < = w . left ) code | = 1 ;
if ( x > = w . right ) code | = 2 ;
2006-03-20 17:12:09 +00:00
2014-11-23 21:53:46 +00:00
if ( y < = w . top ) code | = 4 ;
if ( y > = w . bot ) code | = 8 ;
2006-03-20 17:12:09 +00:00
return code ;
}
# ifndef NDEBUG
2015-10-18 21:01:18 +00:00
static array < char , MAX_SEGMENTS > visited2 ;
2006-03-20 17:12:09 +00:00
# endif
2016-07-16 16:52:04 +00:00
using visited_twobit_array_t = visited_segment_multibit_array_t < 2 > ;
2015-08-12 03:11:46 +00:00
2006-03-20 17:12:09 +00:00
//Given two sides of segment, tell the two verts which form the
//edge between them
2016-07-16 16:52:04 +00:00
constexpr array <
array <
array < int_fast8_t , 2 > ,
6 > ,
6 > Two_sides_to_edge = { {
2014-10-04 15:04:44 +00:00
{ { { { edge_none , edge_none } } , { { 3 , 7 } } , { { edge_none , edge_none } } , { { 2 , 6 } } , { { 6 , 7 } } , { { 2 , 3 } } } } ,
{ { { { 3 , 7 } } , { { edge_none , edge_none } } , { { 0 , 4 } } , { { edge_none , edge_none } } , { { 4 , 7 } } , { { 0 , 3 } } } } ,
{ { { { edge_none , edge_none } } , { { 0 , 4 } } , { { edge_none , edge_none } } , { { 1 , 5 } } , { { 4 , 5 } } , { { 0 , 1 } } } } ,
{ { { { 2 , 6 } } , { { edge_none , edge_none } } , { { 1 , 5 } } , { { edge_none , edge_none } } , { { 5 , 6 } } , { { 1 , 2 } } } } ,
{ { { { 6 , 7 } } , { { 4 , 7 } } , { { 4 , 5 } } , { { 5 , 6 } } , { { edge_none , edge_none } } , { { edge_none , edge_none } } } } ,
{ { { { 2 , 3 } } , { { 0 , 3 } } , { { 0 , 1 } } , { { 1 , 2 } } , { { edge_none , edge_none } } , { { edge_none , edge_none } } } }
2014-08-08 02:51:31 +00:00
} } ;
2006-03-20 17:12:09 +00:00
//given an edge specified by two verts, give the two sides on that edge
2016-07-16 16:52:04 +00:00
constexpr array <
array <
array < int_fast8_t , 2 > ,
8 > ,
8 > Edge_to_sides = { {
2014-10-04 15:04:44 +00:00
{ { { { side_none , side_none } } , { { 2 , 5 } } , { { side_none , side_none } } , { { 1 , 5 } } , { { 1 , 2 } } , { { side_none , side_none } } , { { side_none , side_none } } , { { side_none , side_none } } } } ,
{ { { { 2 , 5 } } , { { side_none , side_none } } , { { 3 , 5 } } , { { side_none , side_none } } , { { side_none , side_none } } , { { 2 , 3 } } , { { side_none , side_none } } , { { side_none , side_none } } } } ,
{ { { { side_none , side_none } } , { { 3 , 5 } } , { { side_none , side_none } } , { { 0 , 5 } } , { { side_none , side_none } } , { { side_none , side_none } } , { { 0 , 3 } } , { { side_none , side_none } } } } ,
{ { { { 1 , 5 } } , { { side_none , side_none } } , { { 0 , 5 } } , { { side_none , side_none } } , { { side_none , side_none } } , { { side_none , side_none } } , { { side_none , side_none } } , { { 0 , 1 } } } } ,
{ { { { 1 , 2 } } , { { side_none , side_none } } , { { side_none , side_none } } , { { side_none , side_none } } , { { side_none , side_none } } , { { 2 , 4 } } , { { side_none , side_none } } , { { 1 , 4 } } } } ,
{ { { { side_none , side_none } } , { { 2 , 3 } } , { { side_none , side_none } } , { { side_none , side_none } } , { { 2 , 4 } } , { { side_none , side_none } } , { { 3 , 4 } } , { { side_none , side_none } } } } ,
{ { { { side_none , side_none } } , { { side_none , side_none } } , { { 0 , 3 } } , { { side_none , side_none } } , { { side_none , side_none } } , { { 3 , 4 } } , { { side_none , side_none } } , { { 0 , 4 } } } } ,
{ { { { side_none , side_none } } , { { side_none , side_none } } , { { side_none , side_none } } , { { 0 , 1 } } , { { 1 , 4 } } , { { side_none , side_none } } , { { 0 , 4 } } , { { side_none , side_none } } } } ,
2014-08-08 02:51:31 +00:00
} } ;
2006-03-20 17:12:09 +00:00
//@@//perform simple check on tables
//@@check_check()
//@@{
//@@ int i,j;
//@@
//@@ for (i=0;i<8;i++)
//@@ for (j=0;j<8;j++)
//@@ Assert(Edge_to_sides[i][j][0] == Edge_to_sides[j][i][0] &&
//@@ Edge_to_sides[i][j][1] == Edge_to_sides[j][i][1]);
//@@
//@@ for (i=0;i<6;i++)
//@@ for (j=0;j<6;j++)
//@@ Assert(Two_sides_to_edge[i][j][0] == Two_sides_to_edge[j][i][0] &&
//@@ Two_sides_to_edge[i][j][1] == Two_sides_to_edge[j][i][1]);
//@@
//@@
//@@}
//given an edge, tell what side is on that edge
2015-01-17 18:31:39 +00:00
__attribute_warn_unused_result
2014-10-02 03:02:34 +00:00
static int find_seg_side ( const vcsegptr_t seg , const array < int , 2 > & verts , unsigned notside )
2006-03-20 17:12:09 +00:00
{
2014-08-12 03:08:39 +00:00
if ( notside > = MAX_SIDES_PER_SEGMENT )
throw std : : logic_error ( " invalid notside " ) ;
2006-03-20 17:12:09 +00:00
2016-07-16 16:52:04 +00:00
const auto v0 = verts [ 0 ] ;
const auto v1 = verts [ 1 ] ;
2006-03-20 17:12:09 +00:00
2014-08-16 17:59:36 +00:00
auto b = begin ( seg - > verts ) ;
auto e = end ( seg - > verts ) ;
auto iv0 = e ;
auto iv1 = e ;
for ( auto i = b ; ; )
{
if ( iv0 = = e & & * i = = v0 )
{
iv0 = i ;
if ( iv1 ! = e )
2006-03-20 17:12:09 +00:00
break ;
}
2014-08-16 17:59:36 +00:00
if ( iv1 = = e & & * i = = v1 )
{
iv1 = i ;
if ( iv0 ! = e )
2006-03-20 17:12:09 +00:00
break ;
}
2014-08-16 17:59:36 +00:00
if ( + + i = = e )
2014-10-04 15:04:44 +00:00
return side_none ;
2006-03-20 17:12:09 +00:00
}
2014-08-16 17:59:36 +00:00
const auto & eptr = Edge_to_sides [ std : : distance ( b , iv0 ) ] [ std : : distance ( b , iv1 ) ] ;
2006-03-20 17:12:09 +00:00
2016-07-16 16:52:04 +00:00
const auto side0 = eptr [ 0 ] ;
const auto side1 = eptr [ 1 ] ;
2006-03-20 17:12:09 +00:00
2014-10-04 15:04:44 +00:00
Assert ( side0 ! = side_none & & side1 ! = side_none ) ;
2006-03-20 17:12:09 +00:00
if ( side0 ! = notside ) {
Assert ( side1 = = notside ) ;
return side0 ;
}
else {
Assert ( side0 = = notside ) ;
return side1 ;
}
}
2015-01-17 18:31:39 +00:00
__attribute_warn_unused_result
static bool compare_child ( const vcsegptridx_t seg , const vcsegptridx_t cseg , const sidenum_fast_t edgeside )
{
const auto & cside = cseg - > sides [ edgeside ] ;
const auto & sv = Side_to_verts [ edgeside ] [ cside . get_type ( ) = = SIDE_IS_TRI_13 ? 1 : 0 ] ;
const auto & temp = vm_vec_sub ( Viewer_eye , Vertices [ seg - > verts [ sv ] ] ) ;
const auto & cnormal = cseg - > sides [ edgeside ] . normals ;
return vm_vec_dot ( cnormal [ 0 ] , temp ) < 0 | | vm_vec_dot ( cnormal [ 1 ] , temp ) < 0 ;
}
//see if the order matters for these two children.
//returns 0 if order doesn't matter, 1 if c0 before c1, -1 if c1 before c0
__attribute_warn_unused_result
static bool compare_children ( const vcsegptridx_t seg , sidenum_fast_t s0 , sidenum_fast_t s1 )
2006-03-20 17:12:09 +00:00
{
2014-10-04 15:04:44 +00:00
Assert ( s0 ! = side_none & & s1 ! = side_none ) ;
2006-03-20 17:12:09 +00:00
2015-01-17 18:31:39 +00:00
if ( Side_opposite [ s0 ] = = s1 )
return false ;
//find normals of adjoining sides
2014-08-12 02:58:08 +00:00
const array < int , 2 > edge_verts = {
{ seg - > verts [ Two_sides_to_edge [ s0 ] [ s1 ] [ 0 ] ] , seg - > verts [ Two_sides_to_edge [ s0 ] [ s1 ] [ 1 ] ] }
} ;
if ( edge_verts [ 0 ] = = - 1 | | edge_verts [ 1 ] = = - 1 )
throw std : : logic_error ( " invalid edge vert " ) ;
2015-12-22 04:18:51 +00:00
const auto & & seg0 = seg . absolute_sibling ( seg - > children [ s0 ] ) ;
2014-09-06 04:06:18 +00:00
auto edgeside0 = find_seg_side ( seg0 , edge_verts , find_connect_side ( seg , seg0 ) ) ;
2015-01-17 18:31:39 +00:00
if ( edgeside0 = = side_none )
return false ;
auto r0 = compare_child ( seg , seg0 , edgeside0 ) ;
if ( ! r0 )
return r0 ;
2015-12-22 04:18:51 +00:00
const auto & & seg1 = seg . absolute_sibling ( seg - > children [ s1 ] ) ;
2014-09-06 04:06:18 +00:00
auto edgeside1 = find_seg_side ( seg1 , edge_verts , find_connect_side ( seg , seg1 ) ) ;
2015-01-17 18:31:39 +00:00
if ( edgeside1 = = side_none )
2015-01-17 04:31:17 +00:00
return false ;
2015-01-17 18:31:39 +00:00
return ! compare_child ( seg , seg1 , edgeside1 ) ;
2006-03-20 17:12:09 +00:00
}
//short the children of segment to render in the correct order
//returns non-zero if swaps were made
2015-01-17 04:31:17 +00:00
typedef array < sidenum_fast_t , MAX_SIDES_PER_SEGMENT > sort_child_array_t ;
static void sort_seg_children ( const vcsegptridx_t seg , const partial_range_t < sort_child_array_t : : iterator > & r )
2006-03-20 17:12:09 +00:00
{
//for each child, compare with other children and see if order matters
//if order matters, fix if wrong
2015-01-17 04:31:17 +00:00
auto predicate = [ seg ] ( sidenum_fast_t a , sidenum_fast_t b )
2014-09-08 03:31:05 +00:00
{
2015-01-17 04:31:17 +00:00
return compare_children ( seg , a , b ) ;
2014-09-08 03:31:05 +00:00
} ;
std : : sort ( r . begin ( ) , r . end ( ) , predicate ) ;
2006-03-20 17:12:09 +00:00
}
2014-11-21 03:16:00 +00:00
static void add_obj_to_seglist ( render_state_t & rstate , objnum_t objnum , segnum_t segnum )
2006-03-20 17:12:09 +00:00
{
2014-11-21 03:16:00 +00:00
auto p = rstate . render_seg_map . emplace ( segnum , render_state_t : : per_segment_state_t { } ) ;
auto & o = p . first - > second . objects ;
if ( p . second )
o . reserve ( 16 ) ;
o . emplace_back ( render_state_t : : per_segment_state_t : : distant_object { objnum } ) ;
2006-03-20 17:12:09 +00:00
}
2015-01-24 19:16:34 +00:00
namespace {
class render_compare_context_t
2013-12-22 22:03:07 +00:00
{
2015-10-18 21:01:19 +00:00
typedef render_state_t : : per_segment_state_t : : distant_object distant_object ;
2014-11-21 03:29:28 +00:00
struct element
{
fix64 dist_squared ;
2014-11-21 03:31:39 +00:00
# if defined(DXX_BUILD_DESCENT_II)
object * objp ;
# endif
2014-11-21 03:29:28 +00:00
} ;
typedef array < element , MAX_OBJECTS > array_t ;
2016-05-21 17:24:51 +00:00
array_t m_array ;
2015-01-24 19:16:34 +00:00
public :
2016-05-21 17:24:51 +00:00
array_t : : reference operator [ ] ( std : : size_t i ) { return m_array [ i ] ; }
array_t : : const_reference operator [ ] ( std : : size_t i ) const { return m_array [ i ] ; }
2014-11-21 03:29:28 +00:00
render_compare_context_t ( const render_state_t : : per_segment_state_t & segstate )
{
2015-02-05 03:03:49 +00:00
range_for ( const auto t , segstate . objects )
2014-11-21 03:29:28 +00:00
{
2015-09-15 02:48:04 +00:00
const auto & & objp = vobjptr ( t . objnum ) ;
2014-11-21 03:29:28 +00:00
auto & e = ( * this ) [ t . objnum ] ;
2014-11-21 03:31:39 +00:00
# if defined(DXX_BUILD_DESCENT_II)
e . objp = objp ;
# endif
2014-11-21 03:29:28 +00:00
e . dist_squared = vm_vec_dist2 ( objp - > pos , Viewer_eye ) ;
}
}
2015-10-18 21:01:19 +00:00
bool operator ( ) ( const distant_object & a , const distant_object & b ) const ;
2013-12-22 22:03:07 +00:00
} ;
2006-03-20 17:12:09 +00:00
//compare function for object sort.
2015-10-18 21:01:19 +00:00
bool render_compare_context_t : : operator ( ) ( const distant_object & a , const distant_object & b ) const
2006-03-20 17:12:09 +00:00
{
2015-10-18 21:01:19 +00:00
const auto delta_dist_squared = ( * this ) [ a . objnum ] . dist_squared - ( * this ) [ b . objnum ] . dist_squared ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-10-18 21:01:19 +00:00
const auto obj_a = ( * this ) [ a . objnum ] . objp ;
const auto obj_b = ( * this ) [ b . objnum ] . objp ;
2006-03-20 17:12:09 +00:00
2014-11-12 03:04:23 +00:00
auto abs_delta_dist_squared = std : : abs ( delta_dist_squared ) ;
fix combined_size = obj_a - > size + obj_b - > size ;
/*
* First check without squaring . If true , the square can be
* skipped .
*/
if ( abs_delta_dist_squared < combined_size | | abs_delta_dist_squared < ( combined_size * combined_size ) )
{ //same position
2006-03-20 17:12:09 +00:00
//these two objects are in the same position. see if one is a fireball
//or laser or something that should plot on top. Don't do this for
//the afterburner blobs, though.
2015-11-27 03:56:13 +00:00
if ( obj_a - > type = = OBJ_WEAPON | | ( obj_a - > type = = OBJ_FIREBALL & & get_fireball_id ( * obj_a ) ! = VCLIP_AFTERBURNER_BLOB ) )
2015-03-22 18:49:21 +00:00
{
2006-03-20 17:12:09 +00:00
if ( ! ( obj_b - > type = = OBJ_WEAPON | | obj_b - > type = = OBJ_FIREBALL ) )
2014-11-13 03:07:58 +00:00
return true ; //a is weapon, b is not, so say a is closer
2015-03-22 18:49:21 +00:00
//both are weapons
}
2006-03-20 17:12:09 +00:00
else
2015-03-22 18:49:21 +00:00
{
2015-11-27 03:56:13 +00:00
if ( obj_b - > type = = OBJ_WEAPON | | ( obj_b - > type = = OBJ_FIREBALL & & get_fireball_id ( * obj_b ) ! = VCLIP_AFTERBURNER_BLOB ) )
2014-11-13 03:07:58 +00:00
return false ; //b is weapon, a is not, so say a is farther
2015-03-22 18:49:21 +00:00
}
2006-03-20 17:12:09 +00:00
//no special case, fall through to normal return
}
2013-03-03 01:03:33 +00:00
# endif
2015-10-25 17:16:34 +00:00
return delta_dist_squared > 0 ; //return distance
2006-03-20 17:12:09 +00:00
}
2015-10-18 21:01:19 +00:00
}
2014-11-21 03:29:28 +00:00
static void sort_segment_object_list ( render_state_t : : per_segment_state_t & segstate )
{
render_compare_context_t context ( segstate ) ;
auto & v = segstate . objects ;
2015-10-18 21:01:19 +00:00
std : : sort ( v . begin ( ) , v . end ( ) , std : : cref ( context ) ) ;
2014-11-21 03:29:28 +00:00
}
2016-08-25 04:05:32 +00:00
namespace dsx {
2014-09-07 23:39:38 +00:00
static void build_object_lists ( render_state_t & rstate )
2006-03-20 17:12:09 +00:00
{
int nn ;
2015-10-18 21:01:19 +00:00
const auto viewer = Viewer ;
2014-09-07 23:39:38 +00:00
for ( nn = 0 ; nn < rstate . N_render_segs ; nn + + ) {
2016-10-29 23:16:17 +00:00
const auto segnum = rstate . Render_list [ nn ] ;
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2016-10-29 23:16:17 +00:00
range_for ( const auto obj , objects_in ( vcsegptr ( segnum ) ) )
2014-07-30 02:52:36 +00:00
{
2013-12-29 04:28:07 +00:00
int list_pos ;
2012-04-17 07:20:14 +00:00
if ( obj - > type = = OBJ_NONE )
2015-10-18 21:01:19 +00:00
{
assert ( obj - > type ! = OBJ_NONE ) ;
continue ;
}
if ( unlikely ( obj = = viewer ) & & likely ( obj - > attached_obj = = object_none ) )
2012-04-17 07:20:14 +00:00
continue ;
2006-03-20 17:12:09 +00:00
if ( obj - > flags & OF_ATTACHED )
continue ; //ignore this object
2014-11-20 03:00:36 +00:00
auto new_segnum = segnum ;
2006-03-20 17:12:09 +00:00
list_pos = nn ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
int did_migrate ;
if ( obj - > type ! = OBJ_CNTRLCEN ) //don't migrate controlcen
# elif defined(DXX_BUILD_DESCENT_II)
const int did_migrate = 0 ;
2013-10-07 23:52:33 +00:00
if ( obj - > type ! = OBJ_CNTRLCEN & & ! ( obj - > type = = OBJ_ROBOT & & get_robot_id ( obj ) = = 65 ) ) //don't migrate controlcen
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
do {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
did_migrate = 0 ;
# endif
2015-10-18 21:01:19 +00:00
const uint_fast32_t sidemask = get_seg_masks ( obj - > pos , vcsegptr ( new_segnum ) , obj - > size ) . sidemask ;
2006-03-20 17:12:09 +00:00
2015-10-18 21:01:19 +00:00
if ( sidemask ) {
2006-03-20 17:12:09 +00:00
int sn , sf ;
for ( sn = 0 , sf = 1 ; sn < 6 ; sn + + , sf < < = 1 )
2015-10-18 21:01:19 +00:00
if ( sidemask & sf )
{
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-07-12 01:04:21 +00:00
const auto & & seg = vcsegptr ( obj - > segnum ) ;
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-07-12 01:04:21 +00:00
const auto & & seg = vcsegptr ( new_segnum ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
if ( WALL_IS_DOORWAY ( seg , sn ) & WID_FLY_FLAG ) { //can explosion migrate through
int child = seg - > children [ sn ] ;
int checknp ;
for ( checknp = list_pos ; checknp - - ; )
2014-08-12 02:28:03 +00:00
if ( rstate . Render_list [ checknp ] = = child ) {
2006-03-20 17:12:09 +00:00
new_segnum = child ;
list_pos = checknp ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
did_migrate = 1 ;
# endif
2006-03-20 17:12:09 +00:00
}
}
2015-10-18 21:01:19 +00:00
if ( sidemask < = sf )
break ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
} while ( did_migrate ) ;
2014-11-21 03:16:00 +00:00
add_obj_to_seglist ( rstate , obj , new_segnum ) ;
2006-03-20 17:12:09 +00:00
}
}
}
//now that there's a list for each segment, sort the items in those lists
2016-02-12 04:02:28 +00:00
range_for ( const auto segnum , partial_const_range ( rstate . Render_list , rstate . N_render_segs ) )
2014-12-13 16:57:09 +00:00
{
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2014-11-21 03:29:28 +00:00
sort_segment_object_list ( rstate . render_seg_map [ segnum ] ) ;
2006-03-20 17:12:09 +00:00
}
}
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
//--unused-- int Total_num_tmaps_drawn=0;
int Rear_view = 0 ;
2015-12-22 04:18:50 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
//renders onto current canvas
2014-12-13 04:11:21 +00:00
void render_frame ( fix eye_offset , window_rendered_data & window )
2006-03-20 17:12:09 +00:00
{
if ( Endlevel_sequence ) {
render_endlevel_frame ( eye_offset ) ;
return ;
}
if ( Newdemo_state = = ND_STATE_RECORDING & & eye_offset > = 0 ) {
if ( RenderingType = = 0 )
2008-10-16 17:27:02 +00:00
newdemo_record_start_frame ( FrameTime ) ;
2006-03-20 17:12:09 +00:00
if ( RenderingType ! = 255 )
2014-12-23 04:20:27 +00:00
newdemo_record_viewer_object ( vobjptridx ( Viewer ) ) ;
2006-03-20 17:12:09 +00:00
}
//Here:
2016-01-09 16:38:13 +00:00
start_lighting_frame ( vobjptr ( Viewer ) ) ; //this is for ugly light-smoothing hack
2006-03-20 17:12:09 +00:00
2017-02-11 21:42:34 +00:00
g3_start_frame ( * grd_curcanv ) ;
2006-03-20 17:12:09 +00:00
Viewer_eye = Viewer - > pos ;
2010-01-28 00:04:29 +00:00
// if (Viewer->type == OBJ_PLAYER && (PlayerCfg.CockpitMode[1]!=CM_REAR_VIEW))
2006-03-20 17:12:09 +00:00
// vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.fvec,(Viewer->size*3)/4);
if ( eye_offset ) {
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( Viewer_eye , Viewer - > orient . rvec , eye_offset ) ;
2006-03-20 17:12:09 +00:00
}
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2012-03-19 06:09:26 +00:00
if ( EditorWindow )
2006-03-20 17:12:09 +00:00
Viewer_eye = Viewer - > pos ;
# endif
2016-01-09 16:38:13 +00:00
auto start_seg_num = find_point_seg ( Viewer_eye , vsegptridx ( Viewer - > segnum ) ) ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( start_seg_num = = segment_none )
2016-01-09 16:38:14 +00:00
start_seg_num = segptridx ( Viewer - > segnum ) ;
2006-03-20 17:12:09 +00:00
2016-09-26 00:50:08 +00:00
g3_set_view_matrix ( Viewer_eye ,
( Rear_view & & Viewer = = ConsoleObject )
? vm_matrix_x_matrix ( Viewer - > orient , vm_angles_2_matrix ( vms_angvec { 0 , 0 , 0x7fff } ) )
: Viewer - > orient , Render_zoom ) ;
2006-03-20 17:12:09 +00:00
if ( Clear_window = = 1 ) {
if ( Clear_window_color = = - 1 )
Clear_window_color = BM_XRGB ( 0 , 0 , 0 ) ; //BM_XRGB(31, 15, 7);
2017-01-01 00:45:45 +00:00
gr_clear_canvas ( * grd_curcanv , Clear_window_color ) ;
2006-03-20 17:12:09 +00:00
}
2014-12-13 04:11:21 +00:00
render_mine ( start_seg_num , eye_offset , window ) ;
2006-03-20 17:12:09 +00:00
g3_end_frame ( ) ;
//RenderingType=0;
// -- Moved from here by MK, 05/17/95, wrong if multiple renders/frame! FrameCount++; //we have rendered a frame
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2014-12-13 04:11:04 +00:00
void update_rendered_data ( window_rendered_data & window , const vobjptr_t viewer , int rear_view_flag )
2006-03-20 17:12:09 +00:00
{
2014-12-13 04:11:04 +00:00
window . time = timer_query ( ) ;
window . viewer = viewer ;
window . rear_view = rear_view_flag ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
//build a list of segments to be rendered
//fills in Render_list & N_render_segs
2016-12-05 00:26:10 +00:00
static void build_segment_list ( render_state_t & rstate , visited_twobit_array_t & visited , unsigned & first_terminal_seg , const vcsegidx_t start_seg_num )
2006-03-20 17:12:09 +00:00
{
int lcnt , scnt , ecnt ;
2014-09-08 03:31:05 +00:00
int l ;
2006-03-20 17:12:09 +00:00
2014-08-12 02:28:03 +00:00
rstate . render_pos . fill ( - 1 ) ;
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
2015-10-18 21:01:18 +00:00
visited2 = { } ;
2006-03-20 17:12:09 +00:00
# endif
lcnt = scnt = 0 ;
2014-08-12 02:28:03 +00:00
rstate . Render_list [ lcnt ] = start_seg_num ;
visited [ start_seg_num ] = 1 ;
2006-03-20 17:12:09 +00:00
lcnt + + ;
ecnt = lcnt ;
2014-08-12 02:28:03 +00:00
rstate . render_pos [ start_seg_num ] = 0 ;
2014-12-11 02:33:50 +00:00
{
auto & rsm_start_seg = rstate . render_seg_map [ start_seg_num ] ;
2014-12-13 16:54:16 +00:00
auto & rw = rsm_start_seg . render_window ;
2014-12-11 02:33:50 +00:00
rw . left = rw . top = 0 ;
rw . right = grd_curcanv - > cv_bitmap . bm_w - 1 ;
rw . bot = grd_curcanv - > cv_bitmap . bm_h - 1 ;
}
2006-03-20 17:12:09 +00:00
//breadth-first renderer
//build list
for ( l = 0 ; l < Render_depth ; l + + ) {
for ( scnt = 0 ; scnt < ecnt ; scnt + + ) {
2014-11-20 03:00:36 +00:00
auto segnum = rstate . Render_list [ scnt ] ;
2015-10-18 21:01:19 +00:00
if ( unlikely ( segnum = = segment_none ) )
{
assert ( segnum ! = segment_none ) ;
continue ;
}
2006-03-20 17:12:09 +00:00
2014-11-23 21:53:45 +00:00
auto & srsm = rstate . render_seg_map [ segnum ] ;
auto & processed = srsm . processed ;
if ( processed )
continue ;
2014-12-13 16:54:16 +00:00
const auto & check_w = srsm . render_window ;
2014-11-23 21:53:45 +00:00
processed = true ;
2017-01-15 00:03:12 +00:00
const auto & & seg = vcsegptridx ( segnum ) ;
2015-10-18 21:01:18 +00:00
const auto uor = rotate_list ( seg - > verts ) . uor & CC_BEHIND ;
2006-03-20 17:12:09 +00:00
//look at all sides of this segment.
//tricky code to look at sides in correct order follows
2015-01-17 04:31:17 +00:00
sort_child_array_t child_list ; //list of ordered sides to process
2014-09-08 03:31:05 +00:00
uint_fast32_t n_children = 0 ; //how many sides in child_list
for ( uint_fast32_t c = 0 ; c < MAX_SIDES_PER_SEGMENT ; c + + ) { //build list of sides
2014-09-06 22:26:11 +00:00
auto wid = WALL_IS_DOORWAY ( seg , c ) ;
2014-09-03 01:33:46 +00:00
if ( wid & WID_RENDPAST_FLAG )
{
2015-10-18 21:01:18 +00:00
if ( auto codes_and = uor )
2014-09-04 01:51:03 +00:00
{
2015-02-05 03:03:49 +00:00
range_for ( const auto i , Side_to_verts [ c ] )
2014-08-12 03:01:43 +00:00
codes_and & = Segment_points [ seg - > verts [ i ] ] . p3_codes ;
2015-10-18 21:01:18 +00:00
if ( codes_and )
continue ;
2006-03-20 17:12:09 +00:00
}
child_list [ n_children + + ] = c ;
}
}
2015-01-17 04:31:17 +00:00
if ( ! n_children )
continue ;
2006-03-20 17:12:09 +00:00
//now order the sides in some magical way
2016-02-12 04:02:28 +00:00
const auto & & child_range = partial_range ( child_list , n_children ) ;
2015-01-17 04:31:17 +00:00
sort_seg_children ( seg , child_range ) ;
project_list ( seg - > verts ) ;
2015-02-05 03:03:49 +00:00
range_for ( const auto siden , child_range )
2015-01-17 04:31:17 +00:00
{
2017-01-15 00:03:12 +00:00
const auto ch = seg - > children [ siden ] ;
2006-03-20 17:12:09 +00:00
{
2014-09-03 01:33:46 +00:00
{
2015-01-17 04:31:17 +00:00
short min_x = 32767 , max_x = - 32767 , min_y = 32767 , max_y = - 32767 ;
2006-03-20 17:12:09 +00:00
int no_proj_flag = 0 ; //a point wasn't projected
2015-01-17 04:31:17 +00:00
uint8_t codes_and_3d = 0xff , codes_and_2d = codes_and_3d ;
2015-02-05 03:03:49 +00:00
range_for ( const auto i , Side_to_verts [ siden ] )
2015-01-17 04:31:17 +00:00
{
g3s_point * pnt = & Segment_points [ seg - > verts [ i ] ] ;
2006-03-20 17:12:09 +00:00
if ( ! ( pnt - > p3_flags & PF_PROJECTED ) ) { no_proj_flag = 1 ; break ; }
2015-01-17 04:31:17 +00:00
const int16_t _x = f2i ( pnt - > p3_sx ) , _y = f2i ( pnt - > p3_sy ) ;
2006-03-20 17:12:09 +00:00
if ( _x < min_x ) min_x = _x ;
if ( _x > max_x ) max_x = _x ;
if ( _y < min_y ) min_y = _y ;
if ( _y > max_y ) max_y = _y ;
2015-01-17 04:31:17 +00:00
codes_and_3d & = pnt - > p3_codes ;
codes_and_2d & = code_window_point ( _x , _y , check_w ) ;
2006-03-20 17:12:09 +00:00
}
if ( no_proj_flag | | ( ! codes_and_3d & & ! codes_and_2d ) ) { //maybe add this segment
2014-08-12 02:28:03 +00:00
auto rp = rstate . render_pos [ ch ] ;
2014-12-11 02:33:55 +00:00
rect nw ;
2006-03-20 17:12:09 +00:00
2014-12-11 02:33:55 +00:00
if ( no_proj_flag )
nw = check_w ;
2006-03-20 17:12:09 +00:00
else {
2014-12-11 02:33:55 +00:00
nw . left = max ( check_w . left , min_x ) ;
nw . right = min ( check_w . right , max_x ) ;
nw . top = max ( check_w . top , min_y ) ;
nw . bot = min ( check_w . bot , max_y ) ;
2006-03-20 17:12:09 +00:00
}
//see if this seg already visited, and if so, does current window
//expand the old window?
if ( rp ! = - 1 ) {
2014-12-13 16:54:16 +00:00
auto & old_w = rstate . render_seg_map [ rstate . Render_list [ rp ] ] . render_window ;
2014-12-11 02:33:55 +00:00
if ( nw . left < old_w . left | |
nw . top < old_w . top | |
nw . right > old_w . right | |
nw . bot > old_w . bot ) {
2006-03-20 17:12:09 +00:00
2014-12-11 02:33:55 +00:00
nw . left = min ( nw . left , old_w . left ) ;
nw . right = max ( nw . right , old_w . right ) ;
nw . top = min ( nw . top , old_w . top ) ;
nw . bot = max ( nw . bot , old_w . bot ) ;
2006-03-20 17:12:09 +00:00
2014-09-03 01:35:06 +00:00
{
2006-03-20 17:12:09 +00:00
//no_render_flag[lcnt] = 1;
2014-12-08 01:02:08 +00:00
rstate . render_seg_map [ ch ] . processed = false ; //force reprocess
2014-08-12 02:28:03 +00:00
rstate . Render_list [ lcnt ] = segment_none ;
2014-12-11 02:33:55 +00:00
old_w = nw ; //get updated window
2006-03-20 17:12:09 +00:00
goto no_add ;
}
}
else goto no_add ;
}
2014-08-12 02:28:03 +00:00
rstate . render_pos [ ch ] = lcnt ;
rstate . Render_list [ lcnt ] = ch ;
2014-12-13 16:54:16 +00:00
{
auto & chrsm = rstate . render_seg_map [ ch ] ;
chrsm . Seg_depth = l ;
chrsm . render_window = nw ;
}
2006-03-20 17:12:09 +00:00
lcnt + + ;
2008-04-06 20:23:28 +00:00
if ( lcnt > = MAX_RENDER_SEGS ) { goto done_list ; }
2006-03-20 17:12:09 +00:00
visited [ ch ] = 1 ;
no_add :
;
}
}
}
}
}
scnt = ecnt ;
ecnt = lcnt ;
}
done_list :
first_terminal_seg = scnt ;
2014-09-07 23:39:38 +00:00
rstate . N_render_segs = lcnt ;
2006-03-20 17:12:09 +00:00
}
//renders onto current canvas
2016-12-05 00:26:10 +00:00
void render_mine ( const vcsegidx_t start_seg_num , fix eye_offset , window_rendered_data & window )
2006-03-20 17:12:09 +00:00
{
2014-12-13 17:19:28 +00:00
using std : : advance ;
2014-08-12 02:28:03 +00:00
render_state_t rstate ;
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
2014-10-02 03:02:34 +00:00
object_rendered = { } ;
2006-03-20 17:12:09 +00:00
# endif
//set up for rendering
render_start_frame ( ) ;
2012-12-02 18:57:09 +00:00
visited_twobit_array_t visited ;
2006-03-20 17:12:09 +00:00
2016-01-03 20:21:34 +00:00
unsigned first_terminal_seg ;
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( _search_mode | | eye_offset > 0 )
# elif defined(DXX_BUILD_DESCENT_II)
if ( _search_mode )
# endif
{
2016-01-03 20:21:34 +00:00
first_terminal_seg = 0 ;
2006-03-20 17:12:09 +00:00
}
2016-11-11 07:45:11 +00:00
//else
2006-03-20 17:12:09 +00:00
# endif
2016-11-11 07:45:11 +00:00
//NOTE LINK TO ABOVE!! -Link killed by kreatordxx to get editor selection working again
2016-01-03 20:21:34 +00:00
build_segment_list ( rstate , visited , first_terminal_seg , start_seg_num ) ; //fills in Render_list & N_render_segs
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
const auto & & render_range = partial_const_range ( rstate . Render_list , rstate . N_render_segs ) ;
2015-05-22 03:33:21 +00:00
const auto & & reversed_render_range = render_range . reversed ( ) ;
2006-03-20 17:12:09 +00:00
//render away
# ifndef NDEBUG
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( ! ( _search_mode | | eye_offset > 0 ) )
# elif defined(DXX_BUILD_DESCENT_II)
if ( ! ( _search_mode ) )
# endif
{
2015-02-05 03:03:49 +00:00
range_for ( const auto segnum , render_range )
2014-12-13 16:57:09 +00:00
{
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none )
2006-03-20 17:12:09 +00:00
{
if ( visited2 [ segnum ] )
Int3 ( ) ; //get Matt
else
visited2 [ segnum ] = 1 ;
}
}
}
# endif
2016-11-11 07:45:11 +00:00
//if (!(_search_mode))
2014-09-07 23:39:38 +00:00
build_object_lists ( rstate ) ;
2006-03-20 17:12:09 +00:00
2013-04-18 09:56:18 +00:00
if ( eye_offset < = 0 ) // Do for left eye or zero.
2014-08-12 02:28:03 +00:00
set_dynamic_light ( rstate ) ;
2006-03-20 17:12:09 +00:00
2014-12-13 17:19:28 +00:00
if ( reversed_render_range . empty ( ) )
/* Impossible, but later code has undefined behavior if this
* happens
*/
return ;
2006-03-20 17:12:09 +00:00
if ( ! _search_mode & & Clear_window = = 2 ) {
2014-09-07 23:39:38 +00:00
if ( first_terminal_seg < rstate . N_render_segs ) {
2006-03-20 17:12:09 +00:00
if ( Clear_window_color = = - 1 )
Clear_window_color = BM_XRGB ( 0 , 0 , 0 ) ; //BM_XRGB(31, 15, 7);
2016-02-12 04:02:28 +00:00
const uint8_t color = Clear_window_color ;
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
range_for ( const auto segnum , partial_const_range ( rstate . Render_list , first_terminal_seg , rstate . N_render_segs ) )
2014-12-13 16:57:09 +00:00
{
if ( segnum ! = segment_none ) {
const auto & rw = rstate . render_seg_map [ segnum ] . render_window ;
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
2014-12-11 02:33:50 +00:00
if ( rw . left = = - 1 | | rw . top = = - 1 | | rw . right = = - 1 | | rw . bot = = - 1 )
2006-03-20 17:12:09 +00:00
Int3 ( ) ;
else
# endif
//NOTE LINK TO ABOVE!
2017-01-01 00:45:45 +00:00
gr_rect ( * grd_curcanv , rw . left , rw . top , rw . right , rw . bot , color ) ;
2006-03-20 17:12:09 +00:00
}
}
}
}
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL
2015-02-05 03:03:49 +00:00
range_for ( const auto segnum , reversed_render_range )
2014-12-13 16:57:09 +00:00
{
2006-03-20 17:12:09 +00:00
// Interpolation_method = 0;
2014-12-11 02:33:50 +00:00
auto & srsm = rstate . render_seg_map [ segnum ] ;
2006-03-20 17:12:09 +00:00
//if (!no_render_flag[nn])
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none & & ( _search_mode | | visited [ segnum ] ! = 3 ) ) {
2006-03-20 17:12:09 +00:00
//set global render window vars
2014-12-13 17:18:07 +00:00
Current_seg_depth = srsm . Seg_depth ;
2014-09-03 01:33:46 +00:00
{
2014-12-13 16:54:16 +00:00
const auto & rw = srsm . render_window ;
2014-12-11 02:33:50 +00:00
Window_clip_left = rw . left ;
Window_clip_top = rw . top ;
Window_clip_right = rw . right ;
Window_clip_bot = rw . bot ;
2006-03-20 17:12:09 +00:00
}
2015-07-21 02:57:27 +00:00
render_segment ( vcsegptridx ( segnum ) ) ;
2013-12-31 21:24:25 +00:00
visited [ segnum ] = 3 ;
2014-12-13 17:18:07 +00:00
if ( srsm . objects . empty ( ) )
continue ;
2006-03-20 17:12:09 +00:00
2014-09-03 01:33:46 +00:00
{ //reset for objects
2006-03-20 17:12:09 +00:00
Window_clip_left = Window_clip_top = 0 ;
Window_clip_right = grd_curcanv - > cv_bitmap . bm_w - 1 ;
Window_clip_bot = grd_curcanv - > cv_bitmap . bm_h - 1 ;
}
2014-09-03 01:35:56 +00:00
{
2006-03-20 17:12:09 +00:00
//int n_expl_objs=0,expl_objs[5],i;
2015-04-22 02:44:29 +00:00
const auto save_linear_depth = exchange ( Max_linear_depth , Max_linear_depth_objects ) ;
2014-12-11 02:33:50 +00:00
range_for ( auto & v , srsm . objects )
2014-11-21 03:16:00 +00:00
{
2016-01-26 03:45:06 +00:00
do_render_object ( vobjptridx ( v . objnum ) , window ) ; // note link to above else
2006-03-20 17:12:09 +00:00
}
Max_linear_depth = save_linear_depth ;
}
}
}
2009-01-30 11:50:25 +00:00
# else
2015-10-25 17:16:34 +00:00
// Two pass rendering. Since sprites and some level geometry can have transparency (blending), we need some fancy sorting.
// GL_DEPTH_TEST helps to sort everything in view but we should make sure translucent sprites are rendered after geometry to prevent them to turn walls invisible (if rendered BEFORE geometry but still in FRONT of it).
// If walls use blending, they should be rendered along with objects (in same pass) to prevent some ugly clipping.
// First Pass: render opaque level geometry and level geometry with alpha pixels (high Alpha-Test func)
range_for ( const auto segnum , reversed_render_range )
2009-01-30 11:50:25 +00:00
{
2014-12-11 02:33:50 +00:00
auto & srsm = rstate . render_seg_map [ segnum ] ;
2009-01-30 11:50:25 +00:00
2015-10-25 17:16:34 +00:00
if ( segnum ! = segment_none & & ( _search_mode | | visited [ segnum ] ! = 3 ) ) {
2009-01-30 11:50:25 +00:00
//set global render window vars
2015-10-25 17:16:34 +00:00
Current_seg_depth = srsm . Seg_depth ;
2014-09-03 01:33:46 +00:00
{
2014-12-13 16:54:16 +00:00
const auto & rw = srsm . render_window ;
2014-12-11 02:33:50 +00:00
Window_clip_left = rw . left ;
Window_clip_top = rw . top ;
Window_clip_right = rw . right ;
Window_clip_bot = rw . bot ;
2009-01-30 11:50:25 +00:00
}
// render segment
{
2015-07-12 01:04:21 +00:00
const auto & & seg = vcsegptridx ( segnum ) ;
2009-01-30 11:50:25 +00:00
int sn ;
2013-12-26 22:21:16 +00:00
Assert ( segnum ! = segment_none & & segnum < = Highest_segment_index ) ;
2015-10-18 21:01:18 +00:00
if ( ! rotate_list ( seg - > verts ) . uand )
{ //all off screen?
2009-01-30 11:50:25 +00:00
2016-10-31 10:36:48 +00:00
if ( Viewer - > type ! = OBJ_ROBOT )
Automap_visited [ segnum ] = 1 ;
2009-01-30 11:50:25 +00:00
for ( sn = 0 ; sn < MAX_SIDES_PER_SEGMENT ; sn + + )
2014-09-02 22:15:07 +00:00
{
auto wid = WALL_IS_DOORWAY ( seg , sn ) ;
if ( wid = = WID_TRANSPARENT_WALL | | wid = = WID_TRANSILLUSORY_WALL
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2014-09-02 22:15:07 +00:00
| | ( wid & WID_CLOAKED_FLAG )
2013-03-03 01:03:33 +00:00
# endif
)
2009-01-30 11:50:25 +00:00
{
2015-10-25 17:16:34 +00:00
if ( PlayerCfg . AlphaBlendEClips & & is_alphablend_eclip ( TmapInfo [ seg - > sides [ sn ] . tmap_num ] . eclip_num ) ) // Do NOT render geometry with blending textures. Since we've not rendered any objects, yet, they would disappear behind them.
continue ;
glAlphaFunc ( GL_GEQUAL , 0.8 ) ; // prevent ugly outlines if an object (which is rendered later) is shown behind a grate, door, etc. if texture filtering is enabled. These sides are rendered later again with normal AlphaFunc
2009-01-30 11:50:25 +00:00
render_side ( seg , sn ) ;
glAlphaFunc ( GL_GEQUAL , 0.02 ) ;
}
else
render_side ( seg , sn ) ;
2014-09-02 22:15:07 +00:00
}
2009-01-30 11:50:25 +00:00
}
}
}
}
2015-10-25 17:16:34 +00:00
// Second pass: Render objects and level geometry with alpha pixels (normal Alpha-Test func) and eclips with blending
range_for ( const auto segnum , reversed_render_range )
2009-01-30 11:50:25 +00:00
{
2014-12-11 02:33:50 +00:00
auto & srsm = rstate . render_seg_map [ segnum ] ;
2009-01-30 11:50:25 +00:00
2015-10-25 17:16:34 +00:00
if ( segnum ! = segment_none & & ( _search_mode | | visited [ segnum ] ! = 3 ) ) {
2009-01-30 11:50:25 +00:00
//set global render window vars
2014-12-13 17:18:07 +00:00
Current_seg_depth = srsm . Seg_depth ;
2014-09-03 01:33:46 +00:00
{
2014-12-13 16:54:16 +00:00
const auto & rw = srsm . render_window ;
2014-12-11 02:33:50 +00:00
Window_clip_left = rw . left ;
Window_clip_top = rw . top ;
Window_clip_right = rw . right ;
Window_clip_bot = rw . bot ;
2009-01-30 11:50:25 +00:00
}
// render segment
{
2015-07-12 01:04:21 +00:00
const auto & & seg = vcsegptridx ( segnum ) ;
2009-01-30 11:50:25 +00:00
int sn ;
2013-12-26 22:21:16 +00:00
Assert ( segnum ! = segment_none & & segnum < = Highest_segment_index ) ;
2015-10-18 21:01:18 +00:00
if ( ! rotate_list ( seg - > verts ) . uand )
{ //all off screen?
2015-10-25 17:16:34 +00:00
2016-10-31 10:36:48 +00:00
if ( Viewer - > type ! = OBJ_ROBOT )
Automap_visited [ segnum ] = 1 ;
2015-10-25 17:16:34 +00:00
2009-01-30 11:50:25 +00:00
for ( sn = 0 ; sn < MAX_SIDES_PER_SEGMENT ; sn + + )
2014-09-02 22:15:07 +00:00
{
auto wid = WALL_IS_DOORWAY ( seg , sn ) ;
if ( wid = = WID_TRANSPARENT_WALL | | wid = = WID_TRANSILLUSORY_WALL
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2014-09-02 22:15:07 +00:00
| | ( wid & WID_CLOAKED_FLAG )
2013-03-03 01:03:33 +00:00
# endif
)
2015-10-25 17:16:34 +00:00
{
2009-01-30 11:50:25 +00:00
render_side ( seg , sn ) ;
2015-10-25 17:16:34 +00:00
}
2014-09-02 22:15:07 +00:00
}
2009-01-30 11:50:25 +00:00
}
}
2013-12-31 21:24:25 +00:00
visited [ segnum ] = 3 ;
2015-10-25 17:16:34 +00:00
if ( srsm . objects . empty ( ) )
continue ;
{ //reset for objects
Window_clip_left = Window_clip_top = 0 ;
Window_clip_right = grd_curcanv - > cv_bitmap . bm_w - 1 ;
Window_clip_bot = grd_curcanv - > cv_bitmap . bm_h - 1 ;
}
{
range_for ( auto & v , srsm . objects )
{
2016-01-09 16:38:14 +00:00
do_render_object ( vobjptridx ( v . objnum ) , window ) ; // note link to above else
2015-10-25 17:16:34 +00:00
}
}
2009-01-30 11:50:25 +00:00
}
}
# endif
2006-03-20 17:12:09 +00:00
// -- commented out by mk on 09/14/94...did i do a good thing?? object_render_targets();
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
//draw curedge stuff
if ( Outline_mode ) outline_seg_side ( Cursegp , Curside , Curedge , Curvert ) ;
# endif
# endif
2015-12-22 04:18:50 +00:00
}
2006-03-20 17:12:09 +00:00
}
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
//finds what segment is at a given x&y - seg,side,face are filled in
//works on last frame rendered. returns true if found
//if seg<0, then an object was found, and the object number is -seg-1
2014-12-13 17:47:01 +00:00
int find_seg_side_face ( short x , short y , segnum_t & seg , objnum_t & obj , int & side , int & face , int & poly )
2006-03-20 17:12:09 +00:00
{
_search_mode = - 1 ;
_search_x = x ; _search_y = y ;
2014-12-13 17:47:01 +00:00
found_seg = segment_none ;
found_obj = object_none ;
2006-03-20 17:12:09 +00:00
if ( render_3d_in_big_window ) {
2012-03-31 04:38:14 +00:00
gr_set_current_canvas ( LargeView . ev_canv ) ;
2006-03-20 17:12:09 +00:00
}
else {
2012-03-31 04:38:14 +00:00
gr_set_current_canvas ( Canv_editor_game ) ;
2006-03-20 17:12:09 +00:00
}
2014-12-18 04:12:38 +00:00
render_frame ( 0 ) ;
2006-03-20 17:12:09 +00:00
_search_mode = 0 ;
2014-12-13 17:47:01 +00:00
seg = found_seg ;
obj = found_obj ;
side = found_side ;
face = found_face ;
poly = found_poly ;
return found_seg ! = segment_none | | found_obj ! = object_none ;
2006-03-20 17:12:09 +00:00
}
# endif