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 .
*/
/*
*
* Lighting functions .
*
*/
2012-11-11 22:12:51 +00:00
# include <algorithm>
2015-10-18 21:01:19 +00:00
# include <bitset>
2014-08-12 03:02:50 +00:00
# include <numeric>
2006-03-20 17:12:09 +00:00
# include <stdio.h>
# include <string.h> // for memset()
2015-08-03 03:11:25 +00:00
# include "render_state.h"
2012-07-01 02:54:33 +00:00
# include "maths.h"
2006-03-20 17:12:09 +00:00
# include "vecmat.h"
# include "gr.h"
# 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 "render.h"
# include "game.h"
# include "vclip.h"
# include "lighting.h"
# include "3d.h"
2011-04-07 20:32:51 +00:00
# include "interp.h"
2013-12-26 04:18:28 +00:00
# include "gameseg.h"
2006-03-20 17:12:09 +00:00
# include "laser.h"
# include "timer.h"
# include "player.h"
2011-04-07 20:32:51 +00:00
# include "playsave.h"
2006-03-20 17:12:09 +00:00
# include "weapon.h"
# include "powerup.h"
# include "fvi.h"
2014-11-23 04:36:58 +00:00
# include "object.h"
2006-03-20 17:12:09 +00:00
# include "robot.h"
# include "multi.h"
2011-04-07 20:32:51 +00:00
# include "palette.h"
# include "bm.h"
# include "rle.h"
2011-07-09 19:39:22 +00:00
# include "wall.h"
2006-03-20 17:12:09 +00:00
2014-10-12 23:05:46 +00:00
# include "compiler-range_for.h"
2014-12-05 03:08:10 +00:00
# include "partial_range.h"
2014-10-12 23:05:46 +00:00
2012-11-11 22:12:51 +00:00
using std : : min ;
using std : : max ;
2014-12-22 04:35:49 +00:00
static int Do_dynamic_light = 1 ;
static int use_fcd_lighting ;
2015-04-02 02:36:52 +00:00
array < g3s_lrgb , MAX_VERTICES > Dynamic_light ;
2006-03-20 17:12:09 +00:00
# define HEADLIGHT_CONE_DOT (F1_0*9 / 10)
# define HEADLIGHT_SCALE (F1_0*10)
2014-08-16 18:15:16 +00:00
static void add_light_div ( g3s_lrgb & d , const g3s_lrgb & light , const fix & scale )
{
d . r + = fixdiv ( light . r , scale ) ;
d . g + = fixdiv ( light . g , scale ) ;
d . b + = fixdiv ( light . b , scale ) ;
}
static void add_light_dot_square ( g3s_lrgb & d , const g3s_lrgb & light , const fix & dot )
{
auto square = fixmul ( dot , dot ) ;
d . r + = fixmul ( square , light . r ) / 8 ;
d . g + = fixmul ( square , light . g ) / 8 ;
d . b + = fixmul ( square , light . b ) / 8 ;
}
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------------------------
2016-08-25 04:05:32 +00:00
namespace dsx {
2016-01-09 16:38:14 +00:00
static void apply_light ( g3s_lrgb obj_light_emission , const vcsegptridx_t obj_seg , const vms_vector & obj_pos , int n_render_vertices , array < int , MAX_VERTICES > & render_vertices , const array < segnum_t , MAX_VERTICES > & vert_segnum_list , objnum_t objnum )
2006-03-20 17:12:09 +00:00
{
2011-04-07 20:32:51 +00:00
if ( ( ( obj_light_emission . r + obj_light_emission . g + obj_light_emission . b ) / 3 ) > 0 )
{
fix obji_64 = ( ( obj_light_emission . r + obj_light_emission . g + obj_light_emission . b ) / 3 ) * 64 ;
2011-09-26 17:03:20 +00:00
sbyte is_marker = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-12-26 22:21:16 +00:00
if ( objnum ! = object_none )
2011-09-26 17:03:20 +00:00
if ( Objects [ objnum ] . type = = OBJ_MARKER )
is_marker = 1 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// for pretty dim sources, only process vertices in object's own segment.
// 12/04/95, MK, markers only cast light in own segment.
2011-09-26 17:03:20 +00:00
if ( ( abs ( obji_64 ) < = F1_0 * 8 ) | | is_marker ) {
2016-01-09 16:38:14 +00:00
auto & vp = obj_seg - > verts ;
2006-03-20 17:12:09 +00:00
2015-02-05 03:03:49 +00:00
range_for ( const auto vertnum , vp )
2014-12-05 03:08:10 +00:00
{
2006-03-20 17:12:09 +00:00
fix dist ;
2014-11-01 03:18:32 +00:00
const auto & vertpos = Vertices [ vertnum ] ;
dist = vm_vec_dist_quick ( obj_pos , vertpos ) ;
2013-04-18 09:56:32 +00:00
dist = fixmul ( dist / 4 , dist / 4 ) ;
if ( dist < abs ( obji_64 ) ) {
if ( dist < MIN_LIGHT_DIST )
dist = MIN_LIGHT_DIST ;
2014-08-16 18:15:16 +00:00
add_light_div ( Dynamic_light [ vertnum ] , obj_light_emission , dist ) ;
2006-03-20 17:12:09 +00:00
}
}
} else {
int headlight_shift = 0 ;
fix max_headlight_dist = F1_0 * 200 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-12-26 22:21:16 +00:00
if ( objnum ! = object_none )
2015-12-03 03:26:49 +00:00
{
const auto & & objp = vcobjptr ( objnum ) ;
if ( objp - > type = = OBJ_PLAYER )
if ( objp - > ctype . player_info . powerup_flags & PLAYER_FLAGS_HEADLIGHT_ON ) {
2011-09-26 17:03:20 +00:00
headlight_shift = 3 ;
2015-12-03 03:26:49 +00:00
if ( get_player_id ( objp ) ! = Player_num )
2015-11-27 03:56:13 +00:00
{
2011-09-26 17:03:20 +00:00
fvi_query fq ;
fvi_info hit_data ;
int fate ;
2015-12-03 03:26:49 +00:00
const auto tvec = vm_vec_scale_add ( objp - > pos , objp - > orient . fvec , F1_0 * 200 ) ;
2011-09-26 17:03:20 +00:00
2015-12-03 03:26:49 +00:00
fq . startseg = objp - > segnum ;
fq . p0 = & objp - > pos ;
2011-09-26 17:03:20 +00:00
fq . p1 = & tvec ;
fq . rad = 0 ;
fq . thisobjnum = objnum ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2011-09-26 17:03:20 +00:00
fq . flags = FQ_TRANSWALL ;
2015-01-20 02:46:42 +00:00
fate = find_vector_intersection ( fq , hit_data ) ;
2011-09-26 17:03:20 +00:00
if ( fate ! = HIT_NONE )
2015-12-03 03:26:49 +00:00
max_headlight_dist = vm_vec_mag_quick ( vm_vec_sub ( hit_data . hit_pnt , objp - > pos ) ) + F1_0 * 4 ;
2011-09-26 17:03:20 +00:00
}
2006-03-20 17:12:09 +00:00
}
2015-12-03 03:26:49 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2014-09-26 02:42:10 +00:00
for ( int vv = 0 ; vv < n_render_vertices ; vv + + ) {
2013-12-29 04:28:07 +00:00
int vertnum ;
2006-03-20 17:12:09 +00:00
fix dist ;
2011-07-09 19:39:22 +00:00
int apply_light = 0 ;
2006-03-20 17:12:09 +00:00
vertnum = render_vertices [ vv ] ;
2014-11-20 03:00:36 +00:00
auto vsegnum = vert_segnum_list [ vv ] ;
2014-11-01 03:18:32 +00:00
const auto & vertpos = Vertices [ vertnum ] ;
2006-03-20 17:12:09 +00:00
2013-04-18 09:56:32 +00:00
if ( use_fcd_lighting & & abs ( obji_64 ) > F1_0 * 32 )
{
2016-01-09 16:38:14 +00:00
dist = find_connected_distance ( obj_pos , obj_seg , vertpos , vsegptridx ( vsegnum ) , n_render_vertices , WID_RENDPAST_FLAG | WID_FLY_FLAG ) ;
2013-04-18 09:56:32 +00:00
if ( dist > = 0 )
2011-07-09 19:39:22 +00:00
apply_light = 1 ;
2013-04-18 09:56:32 +00:00
}
else
{
2014-11-01 03:18:32 +00:00
dist = vm_vec_dist_quick ( obj_pos , vertpos ) ;
2013-04-18 09:56:32 +00:00
apply_light = 1 ;
}
2011-07-09 19:39:22 +00:00
2013-04-18 09:56:32 +00:00
if ( apply_light & & ( ( dist > > headlight_shift ) < abs ( obji_64 ) ) ) {
2006-03-20 17:12:09 +00:00
2013-04-18 09:56:32 +00:00
if ( dist < MIN_LIGHT_DIST )
dist = MIN_LIGHT_DIST ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( headlight_shift & & objnum ! = object_none )
2013-04-18 09:56:32 +00:00
{
fix dot ;
2014-10-29 03:24:31 +00:00
// MK, Optimization note: You compute distance about 15 lines up, this is partially redundant
2014-11-01 03:18:32 +00:00
const auto vec_to_point = vm_vec_normalized_quick ( vm_vec_sub ( vertpos , obj_pos ) ) ;
2015-12-03 03:26:49 +00:00
dot = vm_vec_dot ( vec_to_point , vcobjptr ( objnum ) - > orient . fvec ) ;
2013-04-18 09:56:32 +00:00
if ( dot < F1_0 / 2 )
{
// Do the normal thing, but darken around headlight.
2014-08-16 18:15:16 +00:00
add_light_div ( Dynamic_light [ vertnum ] , obj_light_emission , fixmul ( HEADLIGHT_SCALE , dist ) ) ;
2013-04-18 09:56:32 +00:00
}
else
{
if ( Game_mode & GM_MULTI )
2011-07-09 19:39:22 +00:00
{
2013-04-18 09:56:32 +00:00
if ( dist < max_headlight_dist )
2011-07-09 19:39:22 +00:00
{
2014-08-16 18:15:16 +00:00
add_light_dot_square ( Dynamic_light [ vertnum ] , obj_light_emission , dot ) ;
2011-07-09 19:39:22 +00:00
}
2011-04-07 20:32:51 +00:00
}
2013-04-18 09:56:32 +00:00
else
{
2014-08-16 18:15:16 +00:00
add_light_dot_square ( Dynamic_light [ vertnum ] , obj_light_emission , dot ) ;
2013-04-18 09:56:32 +00:00
}
2006-03-20 17:12:09 +00:00
}
2013-04-18 09:56:32 +00:00
}
else
{
2014-08-16 18:15:16 +00:00
add_light_div ( Dynamic_light [ vertnum ] , obj_light_emission , dist ) ;
2006-03-20 17:12:09 +00:00
}
}
}
}
}
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
2011-04-07 20:32:51 +00:00
# define FLASH_LEN_FIXED_SECONDS (F1_0 / 3)
# define FLASH_SCALE (3*F1_0 / FLASH_LEN_FIXED_SECONDS)
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------------------------
2015-05-05 03:20:42 +00:00
static void cast_muzzle_flash_light ( int n_render_vertices , array < int , MAX_VERTICES > & render_vertices , const array < segnum_t , MAX_VERTICES > & vert_segnum_list )
2006-03-20 17:12:09 +00:00
{
2010-12-10 23:18:17 +00:00
fix64 current_time ;
2011-04-07 20:32:51 +00:00
short time_since_flash ;
2006-03-20 17:12:09 +00:00
2010-12-10 23:18:17 +00:00
current_time = timer_query ( ) ;
2006-03-20 17:12:09 +00:00
2015-02-14 22:48:27 +00:00
range_for ( auto & i , Muzzle_data )
2011-04-07 20:32:51 +00:00
{
2015-02-14 22:48:27 +00:00
if ( i . create_time )
2011-04-07 20:32:51 +00:00
{
2015-02-14 22:48:27 +00:00
time_since_flash = current_time - i . create_time ;
2006-03-20 17:12:09 +00:00
if ( time_since_flash < FLASH_LEN_FIXED_SECONDS )
2011-04-07 20:32:51 +00:00
{
g3s_lrgb ml ;
ml . r = ml . g = ml . b = ( ( FLASH_LEN_FIXED_SECONDS - time_since_flash ) * FLASH_SCALE ) ;
2016-01-09 16:38:14 +00:00
apply_light ( ml , vsegptridx ( i . segnum ) , i . pos , n_render_vertices , render_vertices , vert_segnum_list , object_none ) ;
2011-04-07 20:32:51 +00:00
}
2006-03-20 17:12:09 +00:00
else
2011-04-07 20:32:51 +00:00
{
2015-02-14 22:48:27 +00:00
i . create_time = 0 ; // turn off this muzzle flash
2011-04-07 20:32:51 +00:00
}
2006-03-20 17:12:09 +00:00
}
}
}
2011-04-07 20:32:51 +00:00
// Translation table to make flares flicker at different rates
2015-04-02 02:36:52 +00:00
const array < fix , 16 > Obj_light_xlate { { 0x1234 , 0x3321 , 0x2468 , 0x1735 ,
2011-04-07 20:32:51 +00:00
0x0123 , 0x19af , 0x3f03 , 0x232a ,
0x2123 , 0x39af , 0x0f03 , 0x132a ,
2015-04-02 02:36:52 +00:00
0x3123 , 0x29af , 0x1f03 , 0x032a
} } ;
2011-04-07 20:32:51 +00:00
# define MAX_HEADLIGHTS 8
2015-02-14 22:48:27 +00:00
static unsigned Num_headlights ;
2015-07-12 01:04:20 +00:00
static array < const object * , MAX_HEADLIGHTS > Headlights ;
2006-03-20 17:12:09 +00:00
// ---------------------------------------------------------
2016-08-25 04:05:32 +00:00
namespace dsx {
2015-10-18 21:01:19 +00:00
static g3s_lrgb compute_light_emission ( const vobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
2011-04-07 20:32:51 +00:00
int compute_color = 0 ;
float cscale = 255.0 ;
2013-03-03 01:03:33 +00:00
fix light_intensity = 0 ;
2011-04-07 20:32:51 +00:00
g3s_lrgb lemission , obj_color = { 255 , 255 , 255 } ;
2006-03-20 17:12:09 +00:00
2011-04-07 20:32:51 +00:00
switch ( obj - > type )
{
2006-03-20 17:12:09 +00:00
case OBJ_PLAYER :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-11-07 21:55:59 +00:00
if ( obj - > ctype . player_info . powerup_flags & PLAYER_FLAGS_HEADLIGHT_ON )
2011-04-07 20:32:51 +00:00
{
2006-03-20 17:12:09 +00:00
if ( Num_headlights < MAX_HEADLIGHTS )
Headlights [ Num_headlights + + ] = obj ;
2011-04-07 20:32:51 +00:00
light_intensity = HEADLIGHT_SCALE ;
}
2015-11-07 21:55:58 +00:00
else if ( game_mode_hoard ( ) & & obj - > ctype . player_info . secondary_ammo [ PROXIMITY_INDEX ] ) // If hoard game and player, add extra light based on how many orbs you have Pulse as well.
2011-04-07 20:32:51 +00:00
{
2015-02-05 03:03:50 +00:00
fix hoardlight ;
2015-11-07 21:55:58 +00:00
hoardlight = i2f ( obj - > ctype . player_info . secondary_ammo [ PROXIMITY_INDEX ] ) / 2 ; //i2f(12));
2006-03-20 17:12:09 +00:00
hoardlight + + ;
2015-02-05 03:03:50 +00:00
auto s = fix_sin ( static_cast < fix > ( GameTime64 / 2 ) & 0xFFFF ) ; // probably a bad way to do it
2011-04-07 20:32:51 +00:00
s + = F1_0 ;
2006-03-20 17:12:09 +00:00
s > > = 1 ;
2011-04-07 20:32:51 +00:00
hoardlight = fixmul ( s , hoardlight ) ;
light_intensity = ( hoardlight ) ;
}
2006-03-20 17:12:09 +00:00
else
2013-03-03 01:03:33 +00:00
# endif
2010-03-28 09:53:12 +00:00
{
fix k = fixmuldiv ( obj - > mtype . phys_info . mass , obj - > mtype . phys_info . drag , ( f1_0 - obj - > mtype . phys_info . drag ) ) ;
// smooth thrust value like set_thrust_from_velocity()
2014-11-02 03:43:47 +00:00
auto sthrust = vm_vec_copy_scale ( obj - > mtype . phys_info . velocity , k ) ;
2015-03-12 02:21:19 +00:00
light_intensity = max ( static_cast < fix > ( vm_vec_mag_quick ( sthrust ) / 4 ) , F1_0 * 2 ) + F1_0 / 2 ;
2010-03-28 09:53:12 +00:00
}
2006-03-20 17:12:09 +00:00
break ;
case OBJ_FIREBALL :
2011-04-07 20:32:51 +00:00
{
2015-11-27 03:56:13 +00:00
const auto oid = get_fireball_id ( obj ) ;
if ( oid < Vclip . size ( ) )
{
auto & v = Vclip [ oid ] ;
2015-10-18 21:01:19 +00:00
light_intensity = v . light_value ;
2006-03-20 17:12:09 +00:00
if ( obj - > lifeleft < F1_0 * 4 )
2015-10-18 21:01:19 +00:00
light_intensity = fixmul ( fixdiv ( obj - > lifeleft , v . play_time ) , light_intensity ) ;
2011-04-07 20:32:51 +00:00
}
else
light_intensity = 0 ;
2015-11-27 03:56:13 +00:00
}
2006-03-20 17:12:09 +00:00
break ;
case OBJ_ROBOT :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
light_intensity = F1_0 / 2 ; // F1_0*Robot_info[obj->id].lightcast;
# elif defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
light_intensity = F1_0 * Robot_info [ get_robot_id ( obj ) ] . lightcast ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
break ;
2011-04-07 20:32:51 +00:00
case OBJ_WEAPON :
{
2013-10-07 23:52:33 +00:00
fix tval = Weapon_info [ get_weapon_id ( obj ) ] . light ;
2015-12-03 03:26:49 +00:00
if ( get_weapon_id ( obj ) = = weapon_id_type : : FLARE_ID )
2015-10-18 21:01:19 +00:00
light_intensity = 2 * ( min ( tval , obj - > lifeleft ) + ( ( static_cast < fix > ( GameTime64 ) ^ Obj_light_xlate [ obj . get_unchecked_index ( ) % Obj_light_xlate . size ( ) ] ) & 0x3fff ) ) ;
2006-03-20 17:12:09 +00:00
else
2011-04-07 20:32:51 +00:00
light_intensity = tval ;
break ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-04-07 20:32:51 +00:00
case OBJ_MARKER :
{
fix lightval = obj - > lifeleft ;
2006-03-20 17:12:09 +00:00
lightval & = 0xffff ;
lightval = 8 * abs ( F1_0 / 2 - lightval ) ;
if ( obj - > lifeleft < F1_0 * 1000 )
2011-04-07 20:32:51 +00:00
obj - > lifeleft + = F1_0 ; // Make sure this object doesn't go out.
2006-03-20 17:12:09 +00:00
2011-04-07 20:32:51 +00:00
light_intensity = lightval ;
break ;
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
case OBJ_POWERUP :
2013-10-07 23:52:33 +00:00
light_intensity = Powerup_info [ get_powerup_id ( obj ) ] . light ;
2006-03-20 17:12:09 +00:00
break ;
case OBJ_DEBRIS :
2011-04-07 20:32:51 +00:00
light_intensity = F1_0 / 4 ;
2006-03-20 17:12:09 +00:00
break ;
case OBJ_LIGHT :
2011-04-07 20:32:51 +00:00
light_intensity = obj - > ctype . light_info . intensity ;
2006-03-20 17:12:09 +00:00
break ;
default :
2011-04-07 20:32:51 +00:00
light_intensity = 0 ;
break ;
}
lemission . r = lemission . g = lemission . b = light_intensity ;
if ( ! PlayerCfg . DynLightColor ) // colored lights not desired so use intensity only OR no intensity (== no light == no color) at all
return lemission ;
switch ( obj - > type ) // find out if given object should cast colored light and compute if so
{
2016-11-20 23:12:00 +00:00
default :
break ;
2011-04-07 20:32:51 +00:00
case OBJ_FIREBALL :
case OBJ_WEAPON :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-04-07 20:32:51 +00:00
case OBJ_MARKER :
2013-03-03 01:03:33 +00:00
# endif
2011-04-07 20:32:51 +00:00
compute_color = 1 ;
break ;
case OBJ_POWERUP :
{
2013-10-07 23:52:33 +00:00
switch ( get_powerup_id ( obj ) )
2011-04-07 20:32:51 +00:00
{
case POW_EXTRA_LIFE :
case POW_ENERGY :
case POW_SHIELD_BOOST :
case POW_KEY_BLUE :
case POW_KEY_RED :
case POW_KEY_GOLD :
case POW_CLOAK :
case POW_INVULNERABILITY :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-04-07 20:32:51 +00:00
case POW_HOARD_ORB :
2013-03-03 01:03:33 +00:00
# endif
2011-04-07 20:32:51 +00:00
compute_color = 1 ;
break ;
default :
break ;
}
2006-03-20 17:12:09 +00:00
break ;
2011-04-07 20:32:51 +00:00
}
}
if ( compute_color )
{
2014-09-26 02:42:10 +00:00
int t_idx_s = - 1 , t_idx_e = - 1 ;
2011-04-12 01:02:51 +00:00
2011-04-07 20:32:51 +00:00
if ( light_intensity < F1_0 ) // for every effect we want color, increase light_intensity so the effect becomes barely visible
light_intensity = F1_0 ;
2011-04-12 01:02:51 +00:00
obj_color . r = obj_color . g = obj_color . b = 255 ;
switch ( obj - > render_type )
{
2011-06-20 09:30:12 +00:00
case RT_NONE :
break ; // no object - no light
2011-04-12 01:02:51 +00:00
case RT_POLYOBJ :
{
polymodel * po = & Polygon_models [ obj - > rtype . pobj_info . model_num ] ;
if ( po - > n_textures < = 0 )
{
2014-07-24 03:12:57 +00:00
int color = g3_poly_get_color ( po - > model_data . get ( ) ) ;
2011-04-12 01:02:51 +00:00
if ( color )
{
2013-01-06 21:11:53 +00:00
obj_color . r = gr_current_pal [ color ] . r ;
obj_color . g = gr_current_pal [ color ] . g ;
obj_color . b = gr_current_pal [ color ] . b ;
2011-04-12 01:02:51 +00:00
}
}
else
{
t_idx_s = ObjBitmaps [ ObjBitmapPtrs [ po - > first_texture ] ] . index ;
t_idx_e = t_idx_s + po - > n_textures - 1 ;
}
break ;
}
2011-05-05 18:45:24 +00:00
case RT_LASER :
{
2013-10-07 23:52:33 +00:00
t_idx_s = t_idx_e = Weapon_info [ get_weapon_id ( obj ) ] . bitmap . index ;
2011-05-05 18:45:24 +00:00
break ;
}
2011-06-01 13:49:06 +00:00
case RT_POWERUP :
{
t_idx_s = Vclip [ obj - > rtype . vclip_info . vclip_num ] . frames [ 0 ] . index ;
t_idx_e = Vclip [ obj - > rtype . vclip_info . vclip_num ] . frames [ Vclip [ obj - > rtype . vclip_info . vclip_num ] . num_frames - 1 ] . index ;
break ;
}
2011-04-12 01:02:51 +00:00
case RT_WEAPON_VCLIP :
{
2013-10-07 23:52:33 +00:00
t_idx_s = Vclip [ Weapon_info [ get_weapon_id ( obj ) ] . weapon_vclip ] . frames [ 0 ] . index ;
t_idx_e = Vclip [ Weapon_info [ get_weapon_id ( obj ) ] . weapon_vclip ] . frames [ Vclip [ Weapon_info [ get_weapon_id ( obj ) ] . weapon_vclip ] . num_frames - 1 ] . index ;
2011-04-12 01:02:51 +00:00
break ;
}
default :
{
2015-11-27 03:56:13 +00:00
const auto & vc = Vclip [ obj - > id ] ;
t_idx_s = vc . frames [ 0 ] . index ;
t_idx_e = vc . frames [ vc . num_frames - 1 ] . index ;
2011-04-12 01:02:51 +00:00
break ;
}
}
2011-04-07 20:32:51 +00:00
2011-04-12 01:02:51 +00:00
if ( t_idx_s ! = - 1 & & t_idx_e ! = - 1 )
{
obj_color . r = obj_color . g = obj_color . b = 0 ;
2014-09-26 02:42:10 +00:00
for ( int i = t_idx_s ; i < = t_idx_e ; i + + )
2011-04-12 01:02:51 +00:00
{
grs_bitmap * bm = & GameBitmaps [ i ] ;
2011-06-04 08:04:39 +00:00
bitmap_index bi ;
bi . index = i ;
PIGGY_PAGE_IN ( bi ) ;
2011-04-12 01:02:51 +00:00
obj_color . r + = bm - > avg_color_rgb [ 0 ] ;
obj_color . g + = bm - > avg_color_rgb [ 1 ] ;
obj_color . b + = bm - > avg_color_rgb [ 2 ] ;
}
}
2011-04-07 20:32:51 +00:00
2011-04-13 19:18:18 +00:00
// obviously this object did not give us any usable color. so let's do our own but with blackjack and hookers!
if ( obj_color . r < = 0 & & obj_color . g < = 0 & & obj_color . b < = 0 )
obj_color . r = obj_color . g = obj_color . b = 255 ;
2011-04-07 20:32:51 +00:00
// scale color to light intensity
2016-06-05 01:04:26 +00:00
cscale = ( static_cast < float > ( light_intensity * 3 ) / ( obj_color . r + obj_color . g + obj_color . b ) ) ;
2011-04-07 20:32:51 +00:00
lemission . r = obj_color . r * cscale ;
lemission . g = obj_color . g * cscale ;
lemission . b = obj_color . b * cscale ;
2006-03-20 17:12:09 +00:00
}
2011-04-07 20:32:51 +00:00
return lemission ;
2006-03-20 17:12:09 +00:00
}
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------------------------
2014-08-12 02:28:03 +00:00
void set_dynamic_light ( render_state_t & rstate )
2006-03-20 17:12:09 +00:00
{
2015-05-05 03:20:42 +00:00
array < int , MAX_VERTICES > render_vertices ;
array < segnum_t , MAX_VERTICES > vert_segnum_list ;
2013-04-18 09:56:32 +00:00
static fix light_time ;
2006-03-20 17:12:09 +00:00
Num_headlights = 0 ;
if ( ! Do_dynamic_light )
return ;
2013-04-18 09:56:32 +00:00
light_time + = FrameTime ;
if ( light_time < ( F1_0 / 60 ) ) // it's enough to stress the CPU 60 times per second
return ;
light_time = light_time - ( F1_0 / 60 ) ;
2011-06-04 08:04:39 +00:00
2015-10-18 21:01:19 +00:00
std : : bitset < MAX_VERTICES > render_vertex_flags ;
2006-03-20 17:12:09 +00:00
// Create list of vertices that need to be looked at for setting of ambient light.
2014-12-05 03:08:10 +00:00
uint_fast32_t n_render_vertices = 0 ;
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-05 03:08:10 +00:00
{
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2014-08-16 18:14:00 +00:00
auto & vp = Segments [ segnum ] . verts ;
2015-02-05 03:03:49 +00:00
range_for ( const auto vnum , vp )
2014-12-05 03:08:10 +00:00
{
2006-03-20 17:12:09 +00:00
if ( vnum < 0 | | vnum > Highest_vertex_index ) {
Int3 ( ) ; //invalid vertex number
continue ; //ignore it, and go on to next one
}
2015-10-18 21:01:19 +00:00
auto & & b = render_vertex_flags [ vnum ] ;
if ( ! b )
{
b = true ;
2011-07-09 19:39:22 +00:00
render_vertices [ n_render_vertices ] = vnum ;
vert_segnum_list [ n_render_vertices ] = segnum ;
n_render_vertices + + ;
2015-10-18 21:01:19 +00:00
Dynamic_light [ vnum ] = { } ;
2006-03-20 17:12:09 +00:00
}
}
}
}
2011-07-09 19:39:22 +00:00
cast_muzzle_flash_light ( n_render_vertices , render_vertices , vert_segnum_list ) ;
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
range_for ( const auto & & obj , vobjptridx )
2011-04-12 01:02:51 +00:00
{
2015-10-18 21:01:19 +00:00
const auto & & obj_light_emission = compute_light_emission ( obj ) ;
2006-03-20 17:12:09 +00:00
2011-04-12 01:02:51 +00:00
if ( ( ( obj_light_emission . r + obj_light_emission . g + obj_light_emission . b ) / 3 ) > 0 )
2016-01-09 16:38:14 +00:00
apply_light ( obj_light_emission , vsegptridx ( obj - > segnum ) , obj - > pos , n_render_vertices , render_vertices , vert_segnum_list , obj ) ;
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
void toggle_headlight_active ( )
{
2016-10-02 00:34:40 +00:00
auto & player_info = get_local_plrobj ( ) . ctype . player_info ;
if ( player_info . powerup_flags & PLAYER_FLAGS_HEADLIGHT ) {
player_info . powerup_flags ^ = PLAYER_FLAGS_HEADLIGHT_ON ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
2011-04-07 20:32:51 +00:00
multi_send_flags ( Player_num ) ;
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
# define HEADLIGHT_BOOST_SCALE 8 //how much to scale light when have headlight boost
# define MAX_DIST_LOG 6 //log(MAX_DIST-expressed-as-integer)
# define MAX_DIST (f1_0<<MAX_DIST_LOG) //no light beyond this dist
2016-04-23 17:59:47 +00:00
static fix compute_headlight_light_on_object ( const object_base & objp )
2006-03-20 17:12:09 +00:00
{
fix light ;
// Let's just illuminate players and robots for speed reasons, ok?
2016-04-23 17:59:47 +00:00
if ( objp . type ! = OBJ_ROBOT & & objp . type ! = OBJ_PLAYER )
2006-03-20 17:12:09 +00:00
return 0 ;
light = 0 ;
2016-02-12 04:02:28 +00:00
range_for ( const auto light_objp , partial_const_range ( Headlights , Num_headlights ) )
2015-02-14 22:48:27 +00:00
{
2006-03-20 17:12:09 +00:00
fix dot , dist ;
2016-04-23 17:59:47 +00:00
auto vec_to_obj = vm_vec_sub ( objp . pos , light_objp - > pos ) ;
2014-09-28 21:11:04 +00:00
dist = vm_vec_normalize_quick ( vec_to_obj ) ;
2006-03-20 17:12:09 +00:00
if ( dist > 0 ) {
2014-09-28 21:11:48 +00:00
dot = vm_vec_dot ( light_objp - > orient . fvec , vec_to_obj ) ;
2006-03-20 17:12:09 +00:00
if ( dot < F1_0 / 2 )
light + = fixdiv ( HEADLIGHT_SCALE , fixmul ( HEADLIGHT_SCALE , dist ) ) ; // Do the normal thing, but darken around headlight.
else
light + = fixmul ( fixmul ( dot , dot ) , HEADLIGHT_SCALE ) / 8 ;
}
}
return light ;
}
//compute the average dynamic light in a segment. Takes the segment number
2015-10-18 21:01:19 +00:00
static g3s_lrgb compute_seg_dynamic_light ( const vcsegptr_t seg )
2006-03-20 17:12:09 +00:00
{
2014-08-12 03:02:50 +00:00
auto op = [ ] ( g3s_lrgb r , unsigned v ) {
r . r + = Dynamic_light [ v ] . r ;
r . g + = Dynamic_light [ v ] . g ;
r . b + = Dynamic_light [ v ] . b ;
return r ;
} ;
g3s_lrgb sum = std : : accumulate ( begin ( seg - > verts ) , end ( seg - > verts ) , g3s_lrgb { 0 , 0 , 0 } , op ) ;
2015-10-18 21:01:19 +00:00
sum . r > > = 3 ;
sum . g > > = 3 ;
sum . b > > = 3 ;
return sum ;
2006-03-20 17:12:09 +00:00
}
2015-08-12 03:11:46 +00:00
static array < g3s_lrgb , MAX_OBJECTS > object_light ;
2015-03-22 18:49:21 +00:00
static array < object_signature_t , MAX_OBJECTS > object_sig ;
2006-03-20 17:12:09 +00:00
object * old_viewer ;
2015-08-12 03:11:46 +00:00
static int reset_lighting_hack ;
2011-04-07 20:32:51 +00:00
# define LIGHT_RATE i2f(4) //how fast the light ramps up
2006-03-20 17:12:09 +00:00
2015-10-18 21:01:19 +00:00
void start_lighting_frame ( const vobjptr_t viewer )
2006-03-20 17:12:09 +00:00
{
reset_lighting_hack = ( viewer ! = old_viewer ) ;
old_viewer = viewer ;
}
//compute the lighting for an object. Takes a pointer to the object,
//and possibly a rotated 3d point. If the point isn't specified, the
//object's center point is rotated.
2014-10-30 03:36:22 +00:00
g3s_lrgb compute_object_light ( const vobjptridx_t obj , const vms_vector * rotated_pnt )
2006-03-20 17:12:09 +00:00
{
2011-04-07 20:32:51 +00:00
g3s_lrgb light , seg_dl ;
fix mlight ;
2006-03-20 17:12:09 +00:00
g3s_point objpnt ;
2014-01-11 22:58:58 +00:00
int objnum = obj ;
2006-03-20 17:12:09 +00:00
2011-04-07 20:32:51 +00:00
if ( ! rotated_pnt )
{
2014-10-02 03:02:35 +00:00
g3_rotate_point ( objpnt , obj - > pos ) ;
2006-03-20 17:12:09 +00:00
rotated_pnt = & objpnt . p3_vec ;
}
2011-04-07 20:32:51 +00:00
//First, get static (mono) light for this segment
2015-10-18 21:01:19 +00:00
const auto & & objsegp = vcsegptr ( obj - > segnum ) ;
light . r = light . g = light . b = objsegp - > static_light ;
2006-03-20 17:12:09 +00:00
//Now, maybe return different value to smooth transitions
2011-04-07 20:32:51 +00:00
if ( ! reset_lighting_hack & & object_sig [ objnum ] = = obj - > signature )
{
fix frame_delta ;
g3s_lrgb delta_light ;
2006-03-20 17:12:09 +00:00
2011-04-07 20:32:51 +00:00
delta_light . r = light . r - object_light [ objnum ] . r ;
delta_light . g = light . g - object_light [ objnum ] . g ;
delta_light . b = light . b - object_light [ objnum ] . b ;
2006-03-20 17:12:09 +00:00
frame_delta = fixmul ( LIGHT_RATE , FrameTime ) ;
2011-04-07 20:32:51 +00:00
if ( abs ( ( ( delta_light . r + delta_light . g + delta_light . b ) / 3 ) ) < = frame_delta )
{
2006-03-20 17:12:09 +00:00
object_light [ objnum ] = light ; //we've hit the goal
2011-04-07 20:32:51 +00:00
}
2006-03-20 17:12:09 +00:00
else
2011-04-07 20:32:51 +00:00
{
if ( ( ( delta_light . r + delta_light . g + delta_light . b ) / 3 ) < 0 )
{
light . r = object_light [ objnum ] . r - = frame_delta ;
light . g = object_light [ objnum ] . g - = frame_delta ;
light . b = object_light [ objnum ] . b - = frame_delta ;
}
2006-03-20 17:12:09 +00:00
else
2011-04-07 20:32:51 +00:00
{
light . r = object_light [ objnum ] . r + = frame_delta ;
light . g = object_light [ objnum ] . g + = frame_delta ;
light . b = object_light [ objnum ] . b + = frame_delta ;
}
}
2006-03-20 17:12:09 +00:00
}
2011-04-07 20:32:51 +00:00
else //new object, initialize
{
2006-03-20 17:12:09 +00:00
object_sig [ objnum ] = obj - > signature ;
2011-04-07 20:32:51 +00:00
object_light [ objnum ] . r = light . r ;
object_light [ objnum ] . g = light . g ;
object_light [ objnum ] . b = light . b ;
2006-03-20 17:12:09 +00:00
}
2011-04-07 20:32:51 +00:00
//Next, add in (NOTE: WHITE) headlight on this object
mlight = compute_headlight_light_on_object ( obj ) ;
light . r + = mlight ;
light . g + = mlight ;
light . b + = mlight ;
2006-03-20 17:12:09 +00:00
//Finally, add in dynamic light for this segment
2015-10-18 21:01:19 +00:00
seg_dl = compute_seg_dynamic_light ( objsegp ) ;
2011-04-07 20:32:51 +00:00
light . r + = seg_dl . r ;
light . g + = seg_dl . g ;
light . b + = seg_dl . b ;
2006-03-20 17:12:09 +00:00
return light ;
}