mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
The largest new feature is that it is now possible to include other files into the RML by using the "include" tag. This is useful for making markup files more modular. Also added some more options for existing things (e.g. built-in detail options).
1502 lines
41 KiB
C++
1502 lines
41 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "bmpman/bmpman.h"
|
|
#include "cmdline/cmdline.h"
|
|
#include "ddsutils/ddsutils.h"
|
|
#include "debugconsole/console.h"
|
|
#include "freespace.h"
|
|
#include "jpgutils/jpgutils.h"
|
|
#include "mission/missionparse.h"
|
|
#include "nebula/neb.h"
|
|
#include "object/object.h"
|
|
#include "options/Option.h"
|
|
#include "parse/parselo.h"
|
|
#include "pcxutils/pcxutils.h"
|
|
#include "render/3d.h"
|
|
#include "ship/ship.h"
|
|
#include "starfield/starfield.h"
|
|
#include "tgautils/tgautils.h"
|
|
#include "tracing/tracing.h"
|
|
#include "graphics/light.h"
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------
|
|
// NEBULA DEFINES/VARS
|
|
//
|
|
|
|
bool Nebula_sexp_used = false;
|
|
|
|
static ubyte Neb2_fog_color_r = 0;
|
|
static ubyte Neb2_fog_color_g = 0;
|
|
static ubyte Neb2_fog_color_b = 0;
|
|
|
|
static ubyte *Neb2_htl_fog_data = NULL;
|
|
|
|
// #define NEB2_THUMBNAIL
|
|
|
|
/*
|
|
3D CARDS THAT FOG PROPERLY
|
|
Voodoo1
|
|
Voodoo2
|
|
G200
|
|
TNT
|
|
|
|
3D CARDS THAT DON'T FOG PROPERLY
|
|
Permedia2
|
|
AccelStar II
|
|
*/
|
|
|
|
// if nebula rendering is active (DCF stuff - not mission specific)
|
|
int Neb2_render_mode = NEB2_RENDER_NONE;
|
|
|
|
// array of neb2 poofs
|
|
char Neb2_poof_filenames[MAX_NEB2_POOFS][MAX_FILENAME_LEN] = {
|
|
"", "", "", "", "", ""
|
|
};
|
|
int Neb2_poofs[MAX_NEB2_POOFS] = { -1, -1, -1, -1, -1, -1 };
|
|
int Neb2_poof_flags = ( (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5) );
|
|
int Neb2_poof_count = 0;
|
|
|
|
// array of neb2 bitmaps
|
|
char Neb2_bitmap_filenames[MAX_NEB2_BITMAPS][MAX_FILENAME_LEN] = {
|
|
"", "", "", "", "", ""
|
|
};
|
|
int Neb2_bitmap[MAX_NEB2_BITMAPS] = { -1, -1, -1, -1, -1, -1 };
|
|
int Neb2_bitmap_count = 0;
|
|
|
|
// texture to use for this level
|
|
char Neb2_texture_name[MAX_FILENAME_LEN] = "";
|
|
|
|
// nebula flags
|
|
#define NF_USED (1<<0) // if this nebula slot is used
|
|
|
|
float max_rotation = 3.75f;
|
|
float neb2_flash_fade = 0.3f;
|
|
|
|
// fog values for different ship types
|
|
//Why the heck is this still here? Commenting out. -WMC
|
|
/*
|
|
float Neb_ship_fog_vals_glide[MAX_SHIP_TYPE_COUNTS][2] = {
|
|
{0.0f, 0.0f}, // SHIP_TYPE_NONE
|
|
{10.0f, 500.0f}, // SHIP_TYPE_CARGO
|
|
{10.0f, 500.0f}, // SHIP_TYPE_FIGHTER_BOMBER
|
|
{10.0f, 600.0f}, // SHIP_TYPE_CRUISER
|
|
{10.0f, 600.0f}, // SHIP_TYPE_FREIGHTER
|
|
{10.0f, 750.0f}, // SHIP_TYPE_CAPITAL
|
|
{10.0f, 500.0f}, // SHIP_TYPE_TRANSPORT
|
|
{10.0f, 500.0f}, // SHIP_TYPE_REPAIR_REARM
|
|
{10.0f, 500.0f}, // SHIP_TYPE_NAVBUOY
|
|
{10.0f, 500.0f}, // SHIP_TYPE_SENTRYGUN
|
|
{10.0f, 600.0f}, // SHIP_TYPE_ESCAPEPOD
|
|
{10.0f, 1000.0f}, // SHIP_TYPE_SUPERCAP
|
|
{10.0f, 500.0f}, // SHIP_TYPE_STEALTH
|
|
{10.0f, 500.0f}, // SHIP_TYPE_FIGHTER
|
|
{10.0f, 500.0f}, // SHIP_TYPE_BOMBER
|
|
{10.0f, 750.0f}, // SHIP_TYPE_DRYDOCK
|
|
{10.0f, 600.0f}, // SHIP_TYPE_AWACS
|
|
{10.0f, 600.0f}, // SHIP_TYPE_GAS_MINER
|
|
{10.0f, 600.0f}, // SHIP_TYPE_CORVETTE
|
|
{10.0f, 1000.0f}, // SHIP_TYPE_KNOSSOS_DEVICE
|
|
};
|
|
*/
|
|
/*
|
|
float Neb_ship_fog_vals_d3d[MAX_SHIP_TYPE_COUNTS][2] = {
|
|
{0.0f, 0.0f}, // SHIP_TYPE_NONE
|
|
{10.0f, 500.0f}, // SHIP_TYPE_CARGO
|
|
{10.0f, 500.0f}, // SHIP_TYPE_FIGHTER_BOMBER
|
|
{10.0f, 600.0f}, // SHIP_TYPE_CRUISER
|
|
{10.0f, 600.0f}, // SHIP_TYPE_FREIGHTER
|
|
{10.0f, 750.0f}, // SHIP_TYPE_CAPITAL
|
|
{10.0f, 500.0f}, // SHIP_TYPE_TRANSPORT
|
|
{10.0f, 500.0f}, // SHIP_TYPE_REPAIR_REARM
|
|
{10.0f, 500.0f}, // SHIP_TYPE_NAVBUOY
|
|
{10.0f, 500.0f}, // SHIP_TYPE_SENTRYGUN
|
|
{10.0f, 600.0f}, // SHIP_TYPE_ESCAPEPOD
|
|
{10.0f, 1000.0f}, // SHIP_TYPE_SUPERCAP
|
|
{10.0f, 500.0f}, // SHIP_TYPE_STEALTH
|
|
{10.0f, 500.0f}, // SHIP_TYPE_FIGHTER
|
|
{10.0f, 500.0f}, // SHIP_TYPE_BOMBER
|
|
{10.0f, 750.0f}, // SHIP_TYPE_DRYDOCK
|
|
{10.0f, 600.0f}, // SHIP_TYPE_AWACS
|
|
{10.0f, 600.0f}, // SHIP_TYPE_GAS_MINER
|
|
{10.0f, 600.0f}, // SHIP_TYPE_CORVETTE
|
|
{10.0f, 1000.0f}, // SHIP_TYPE_KNOSSOS_DEVICE
|
|
};
|
|
*/
|
|
//WMC - these were originally indexed to SHIP_TYPE_FIGHTER_BOMBER
|
|
const static float Default_fog_near = 10.0f;
|
|
const static float Default_fog_far = 750.0f;
|
|
|
|
// fog near and far values for rendering the background nebula
|
|
#define NEB_BACKG_FOG_NEAR_GLIDE 2.5f
|
|
#define NEB_BACKG_FOG_NEAR_D3D 4.5f
|
|
#define NEB_BACKG_FOG_FAR_GLIDE 10.0f
|
|
#define NEB_BACKG_FOG_FAR_D3D 10.0f
|
|
float Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_GLIDE;
|
|
float Neb_backg_fog_far = NEB_BACKG_FOG_FAR_GLIDE;
|
|
|
|
// stats
|
|
int pneb_tried = 0; // total pnebs tried to render
|
|
int pneb_tossed_alpha = 0; // pnebs tossed because of alpha optimization
|
|
int pneb_tossed_dot = 0; // pnebs tossed because of dot product
|
|
int pneb_tossed_off = 0; // pnebs tossed because of being offscree
|
|
int neb_tried = 0; // total nebs tried
|
|
int neb_tossed_alpha = 0; // nebs tossed because of alpha
|
|
int neb_tossed_dot = 0; // nebs tossed because of dot product
|
|
int neb_tossed_count = 0; // nebs tossed because of max render count
|
|
|
|
// the AWACS suppression level for the nebula
|
|
float Neb2_awacs = -1.0f;
|
|
|
|
// The visual render distance multipliers for the nebula
|
|
float Neb2_fog_near_mult = 1.0f;
|
|
float Neb2_fog_far_mult = 1.0f;
|
|
|
|
// how many "slices" are in the current player nebuls
|
|
int Neb2_slices = 5;
|
|
|
|
cube_poof Neb2_cubes[MAX_CPTS][MAX_CPTS][MAX_CPTS];
|
|
|
|
neb2_detail Neb2_detail[MAX_DETAIL_LEVEL] = {
|
|
{ // lowest detail level
|
|
0.575f, // max alpha for this detail level in Glide
|
|
0.71f, // max alpha for this detail level in D3d
|
|
0.13f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
|
|
150.0f, 150.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen
|
|
510.0f, // total dimension of player poof cube
|
|
50.0f, // inner radius of the player poof cube
|
|
250.0f, // outer radius of the player pood cube
|
|
120.0f, // radius of the poofs
|
|
1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0
|
|
},
|
|
{ // 2nd lowest detail level
|
|
0.575f, // max alpha for this detail level in Glide
|
|
0.71f, // max alpha for this detail level in D3d
|
|
0.125f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
|
|
300.0f, 300.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen
|
|
550.0f, // total dimension of player poof cube
|
|
100.0f, // inner radius of the player poof cube
|
|
250.0f, // outer radius of the player pood cube
|
|
125.0f, // radius of the poofs
|
|
1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0
|
|
},
|
|
{ // 2nd highest detail level
|
|
0.575f, // max alpha for this detail level in Glide
|
|
0.71f, // max alpha for this detail level in D3d
|
|
0.1f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
|
|
300.0f, 300.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen
|
|
550.0f, // total dimension of player poof cube
|
|
150.0f, // inner radius of the player poof cube
|
|
250.0f, // outer radius of the player pood cube
|
|
125.0f, // radius of the poofs
|
|
1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0
|
|
},
|
|
{ // higest detail level
|
|
0.475f, // max alpha for this detail level in Glide
|
|
0.575f, // max alpha for this detail level in D3d
|
|
0.05f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
|
|
200.0f, 200.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen
|
|
750.0f, // total dimension of player poof cube
|
|
200.0f, // inner radius of the player poof cube
|
|
360.0f, // outer radius of the player pood cube
|
|
150.0f, // radius of the poofs
|
|
1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0
|
|
},
|
|
};
|
|
neb2_detail *Nd = &Neb2_detail[MAX_DETAIL_LEVEL - 2];
|
|
|
|
int Neb2_background_color[3] = {0, 0, 255}; // rgb background color (used for lame rendering)
|
|
|
|
int Neb2_regen = 0;
|
|
|
|
const SCP_vector<std::pair<int, SCP_string>> DetailLevelValues = {{ 0, "Minimum" },
|
|
{ 1, "Low" },
|
|
{ 2, "Medium" },
|
|
{ 3, "High" },
|
|
{ 4, "Ultra" }, };
|
|
|
|
const auto ModelDetailOption = options::OptionBuilder<int>("Graphics.NebulaDetail",
|
|
"Nebula Detail",
|
|
"Detail level of nebulas").category("Graphics").values(
|
|
DetailLevelValues).default_val(MAX_DETAIL_LEVEL).importance(7).change_listener([](int val, bool initial) {
|
|
Detail.nebula_detail = val;
|
|
if (!initial) {
|
|
// This is only needed for changes after the game startup
|
|
neb2_set_detail_level(Detail.nebula_detail);
|
|
}
|
|
return true;
|
|
}).finish();
|
|
|
|
// --------------------------------------------------------------------------------------------------------
|
|
// NEBULA FORWARD DECLARATIONS
|
|
//
|
|
|
|
// return the alpha the passed poof should be rendered with, for a 2 shell nebula
|
|
float neb2_get_alpha_2shell(float inner_radius, float outer_radius, float magic_num, vec3d *v);
|
|
|
|
// return an alpha value for a bitmap offscreen based upon "break" value
|
|
float neb2_get_alpha_offscreen(float sx, float sy, float incoming_alpha);
|
|
|
|
// do a pre-render of the background nebula
|
|
void neb2_pre_render(camid cid);
|
|
|
|
// fill in the position of the eye for this frame
|
|
void neb2_get_eye_pos(vec3d *eye_vector);
|
|
|
|
// fill in the eye orient for this frame
|
|
void neb2_get_eye_orient(matrix *eye_matrix);
|
|
|
|
// get a (semi) random bitmap to use for a poof
|
|
int neb2_get_bitmap();
|
|
|
|
// regenerate the player nebula
|
|
void neb2_regen();
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------
|
|
// NEBULA FUNCTIONS
|
|
//
|
|
|
|
// initialize neb2 stuff at game startup
|
|
void neb2_init()
|
|
{
|
|
char name[MAX_FILENAME_LEN];
|
|
|
|
try
|
|
{
|
|
// read in the nebula.tbl
|
|
read_file_text("nebula.tbl", CF_TYPE_TABLES);
|
|
reset_parse();
|
|
|
|
// background bitmaps
|
|
Neb2_bitmap_count = 0;
|
|
while (!optional_string("#end")) {
|
|
// nebula
|
|
required_string("+Nebula:");
|
|
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
|
|
|
if (Neb2_bitmap_count < MAX_NEB2_BITMAPS) {
|
|
strcpy_s(Neb2_bitmap_filenames[Neb2_bitmap_count++], name);
|
|
}
|
|
else {
|
|
WarningEx(LOCATION, "nebula.tbl\nExceeded maximum number of nebulas (%d)!\nSkipping %s.", MAX_NEB2_BITMAPS, name);
|
|
}
|
|
}
|
|
|
|
// poofs
|
|
Neb2_poof_count = 0;
|
|
while (!optional_string("#end")) {
|
|
// nebula
|
|
required_string("+Poof:");
|
|
stuff_string(name, F_NAME, MAX_FILENAME_LEN);
|
|
|
|
if (Neb2_poof_count < MAX_NEB2_POOFS) {
|
|
strcpy_s(Neb2_poof_filenames[Neb2_poof_count++], name);
|
|
}
|
|
else {
|
|
WarningEx(LOCATION, "nebula.tbl\nExceeded maximum number of nebula poofs (%d)!\nSkipping %s.", MAX_NEB2_POOFS, name);
|
|
}
|
|
}
|
|
|
|
// should always have 6 neb poofs
|
|
Assert(Neb2_poof_count == 6);
|
|
}
|
|
catch (const parse::ParseException& e)
|
|
{
|
|
mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "nebula.tbl", e.what()));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// set detail level
|
|
void neb2_set_detail_level(int level)
|
|
{
|
|
// sanity
|
|
if (level < 0) {
|
|
Nd = &Neb2_detail[0];
|
|
return;
|
|
}
|
|
if (level >= MAX_DETAIL_LEVEL) {
|
|
Nd = &Neb2_detail[MAX_DETAIL_LEVEL-1];
|
|
return;
|
|
}
|
|
|
|
Nd = &Neb2_detail[level];
|
|
|
|
// regen the player neb
|
|
neb2_eye_changed();
|
|
}
|
|
|
|
void neb2_get_fog_color(ubyte *r, ubyte *g, ubyte *b)
|
|
{
|
|
if (r) *r = Neb2_fog_color_r;
|
|
if (g) *g = Neb2_fog_color_g;
|
|
if (b) *b = Neb2_fog_color_b;
|
|
}
|
|
|
|
void neb2_level_init()
|
|
{
|
|
Nebula_sexp_used = false;
|
|
}
|
|
|
|
// initialize nebula stuff - call from game_post_level_init(), so the mission has been loaded
|
|
void neb2_post_level_init()
|
|
{
|
|
int idx;
|
|
|
|
// standalone servers can bail here
|
|
if (Game_mode & GM_STANDALONE_SERVER) {
|
|
return;
|
|
}
|
|
|
|
// Skip actual rendering if we're in FRED.
|
|
if(Fred_running)
|
|
{
|
|
Neb2_render_mode = NEB2_RENDER_NONE;
|
|
return;
|
|
}
|
|
|
|
// if the mission is not a fullneb mission, skip
|
|
if ( !((The_mission.flags[Mission::Mission_Flags::Fullneb]) || Nebula_sexp_used) ) {
|
|
Neb2_render_mode = NEB2_RENDER_NONE;
|
|
Neb2_awacs = -1.0f;
|
|
return;
|
|
}
|
|
|
|
// Set a default colour just in case something goes wrong
|
|
Neb2_fog_color_r = 30;
|
|
Neb2_fog_color_g = 52;
|
|
Neb2_fog_color_b = 157;
|
|
|
|
// OK, lets try something a bit more interesting
|
|
if (strlen(Neb2_texture_name)) {
|
|
Neb2_htl_fog_data = new ubyte[768];
|
|
|
|
if ((Neb2_htl_fog_data != NULL) && (pcx_read_header(Neb2_texture_name, NULL, NULL, NULL, NULL, Neb2_htl_fog_data) == PCX_ERROR_NONE)) {
|
|
// based on the palette, get an average color value (this doesn't really account for actual pixel usage though)
|
|
ushort r = 0, g = 0, b = 0, pcount = 0;
|
|
for (idx = 0; idx < 768; idx += 3) {
|
|
if (Neb2_htl_fog_data[idx] || Neb2_htl_fog_data[idx+1] || Neb2_htl_fog_data[idx+2]) {
|
|
r = r + Neb2_htl_fog_data[idx];
|
|
g = g + Neb2_htl_fog_data[idx+1];
|
|
b = b + Neb2_htl_fog_data[idx+2];
|
|
pcount++;
|
|
}
|
|
}
|
|
|
|
if (pcount > 0) {
|
|
Neb2_fog_color_r = (ubyte)(r / pcount);
|
|
Neb2_fog_color_g = (ubyte)(g / pcount);
|
|
Neb2_fog_color_b = (ubyte)(b / pcount);
|
|
} else {
|
|
// it's just black
|
|
Neb2_fog_color_r = Neb2_fog_color_g = Neb2_fog_color_b = 0;
|
|
}
|
|
|
|
// done, now free up the palette data
|
|
if ( Neb2_htl_fog_data != NULL ) {
|
|
delete[] Neb2_htl_fog_data;
|
|
Neb2_htl_fog_data = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
Neb2_render_mode = NEB2_RENDER_HTL;
|
|
|
|
// load in all nebula bitmaps
|
|
for (idx=0; idx<Neb2_poof_count; idx++) {
|
|
if (Neb2_poofs[idx] < 0) {
|
|
Neb2_poofs[idx] = bm_load(Neb2_poof_filenames[idx]);
|
|
}
|
|
}
|
|
|
|
pneb_tried = 0;
|
|
pneb_tossed_alpha = 0;
|
|
pneb_tossed_dot = 0;
|
|
neb_tried = 0;
|
|
neb_tossed_alpha = 0;
|
|
neb_tossed_dot = 0;
|
|
neb_tossed_count = 0;
|
|
|
|
// setup proper fogging values
|
|
Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_D3D;
|
|
Neb_backg_fog_far = NEB_BACKG_FOG_FAR_D3D;
|
|
|
|
// regen the nebula
|
|
neb2_eye_changed();
|
|
|
|
// if we are going to use fullneb, but aren't fullneb yet, then be sure to reset our mode
|
|
if ( !(The_mission.flags[Mission::Mission_Flags::Fullneb]) ) {
|
|
Neb2_render_mode = NEB2_RENDER_NONE;
|
|
Neb2_awacs = -1.0f;
|
|
}
|
|
}
|
|
|
|
// shutdown nebula stuff
|
|
void neb2_level_close()
|
|
{
|
|
int idx;
|
|
|
|
// standalone servers can bail here
|
|
if (Game_mode & GM_STANDALONE_SERVER) {
|
|
return;
|
|
}
|
|
|
|
// if the mission is not a fullneb mission, skip
|
|
if ( !((The_mission.flags[Mission::Mission_Flags::Fullneb]) || Nebula_sexp_used) ) {
|
|
return;
|
|
}
|
|
|
|
// unload all nebula bitmaps
|
|
for (idx=0; idx<Neb2_poof_count; idx++) {
|
|
if (Neb2_poofs[idx] >= 0) {
|
|
bm_release(Neb2_poofs[idx]);
|
|
Neb2_poofs[idx] = -1;
|
|
}
|
|
}
|
|
|
|
// unflag the mission as being fullneb so stuff doesn't fog in the techdata room :D
|
|
The_mission.flags.remove(Mission::Mission_Flags::Fullneb);
|
|
|
|
if (Neb2_htl_fog_data) {
|
|
delete[] Neb2_htl_fog_data;
|
|
Neb2_htl_fog_data = NULL;
|
|
}
|
|
}
|
|
|
|
// call before beginning all rendering
|
|
void neb2_render_setup(camid cid)
|
|
{
|
|
GR_DEBUG_SCOPE("Nebula Setup");
|
|
TRACE_SCOPE(tracing::SetupNebula);
|
|
|
|
// standalone servers can bail here
|
|
if (Game_mode & GM_STANDALONE_SERVER) {
|
|
return;
|
|
}
|
|
|
|
// if the mission is not a fullneb mission, skip
|
|
if ( !(The_mission.flags[Mission::Mission_Flags::Fullneb]) ) {
|
|
return;
|
|
}
|
|
|
|
if (Neb2_render_mode == NEB2_RENDER_HTL) {
|
|
// RT The background needs to be the same colour as the fog and this seems
|
|
// to be the ideal place to do it
|
|
ubyte tr = gr_screen.current_clear_color.red;
|
|
ubyte tg = gr_screen.current_clear_color.green;
|
|
ubyte tb = gr_screen.current_clear_color.blue;
|
|
|
|
neb2_get_fog_color(
|
|
&gr_screen.current_clear_color.red,
|
|
&gr_screen.current_clear_color.green,
|
|
&gr_screen.current_clear_color.blue);
|
|
|
|
gr_clear();
|
|
|
|
gr_screen.current_clear_color.red = tr;
|
|
gr_screen.current_clear_color.green = tg;
|
|
gr_screen.current_clear_color.blue = tb;
|
|
|
|
return;
|
|
}
|
|
|
|
// pre-render the real background nebula
|
|
neb2_pre_render(cid);
|
|
}
|
|
|
|
// level paging code
|
|
void neb2_page_in()
|
|
{
|
|
int idx;
|
|
|
|
// load in all nebula bitmaps
|
|
if ( (The_mission.flags[Mission::Mission_Flags::Fullneb]) || Nebula_sexp_used ) {
|
|
for (idx = 0; idx < Neb2_poof_count; idx++) {
|
|
if ( (Neb2_poofs[idx] >= 0) && (Neb2_poof_flags & (1<<idx)) ) {
|
|
bm_page_in_texture(Neb2_poofs[idx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// should we not render this object because its obscured by the nebula?
|
|
int neb_skip_opt = 0;
|
|
DCF(neb_skip, "Toggles culling of objects obscured by nebula")
|
|
{
|
|
neb_skip_opt = !neb_skip_opt;
|
|
if (neb_skip_opt) {
|
|
dc_printf("Using neb object skipping!\n");
|
|
} else {
|
|
dc_printf("Not using neb object skipping!\n");
|
|
}
|
|
}
|
|
int neb2_skip_render(object *objp, float z_depth)
|
|
{
|
|
float fog_near, fog_far;
|
|
|
|
// if we're never skipping
|
|
if (!neb_skip_opt) {
|
|
return 0;
|
|
}
|
|
|
|
// get near and far fog values based upon object type and rendering mode
|
|
neb2_get_adjusted_fog_values(&fog_near, &fog_far, objp);
|
|
|
|
// by object type
|
|
switch( objp->type ) {
|
|
// some objects we always render
|
|
case OBJ_SHOCKWAVE:
|
|
case OBJ_JUMP_NODE:
|
|
case OBJ_NONE:
|
|
case OBJ_GHOST:
|
|
case OBJ_BEAM:
|
|
case OBJ_WAYPOINT:
|
|
return 0;
|
|
|
|
// any weapon over 500 meters away
|
|
// Use the "far" distance multiplier here
|
|
case OBJ_WEAPON:
|
|
if (z_depth >= 500.0f * Neb2_fog_far_mult) {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
// any small ship over the fog limit, or any cruiser 50% further than the fog limit
|
|
case OBJ_SHIP:
|
|
ship_info *sip;
|
|
if ( (objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0) ) {
|
|
sip = &Ship_info[Ships[objp->instance].ship_info_index];
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
// small ships over the fog limit by a small factor
|
|
if ( (sip->is_small_ship()) && (z_depth >= (fog_far * 1.5f)) ) {
|
|
return 1;
|
|
}
|
|
|
|
// big ships
|
|
if ( (sip->is_big_ship()) && (z_depth >= (fog_far * 2.0f)) ) {
|
|
return 1;
|
|
}
|
|
|
|
// huge ships
|
|
if ( (sip->is_huge_ship()) && (z_depth >= (fog_far * 3.0f)) ) {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
// any fireball over the fog limit for small ships
|
|
case OBJ_FIREBALL:
|
|
return 0;
|
|
break;
|
|
|
|
// any debris over the fog limit for small ships
|
|
case OBJ_DEBRIS:
|
|
return 0;
|
|
break;
|
|
|
|
// any asteroids 50% farther than the fog limit for small ships
|
|
case OBJ_ASTEROID:
|
|
if (z_depth >= (fog_far * 1.5f)) {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
// hmmm. unknown object type - should probably let it through
|
|
default:
|
|
Int3();
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// extend LOD
|
|
float neb2_get_lod_scale(int objnum)
|
|
{
|
|
ship *shipp;
|
|
ship_info *sip;
|
|
|
|
// bogus
|
|
if ( (objnum < 0)
|
|
|| (objnum >= MAX_OBJECTS)
|
|
|| (Objects[objnum].type != OBJ_SHIP)
|
|
|| (Objects[objnum].instance < 0)
|
|
|| (Objects[objnum].instance >= MAX_SHIPS)) {
|
|
return 1.0f;
|
|
}
|
|
shipp = &Ships[Objects[objnum].instance];
|
|
sip = &Ship_info[shipp->ship_info_index];
|
|
|
|
// small ship?
|
|
if (sip->is_small_ship()) {
|
|
return 1.8f;
|
|
} else if (sip->is_big_ship()) {
|
|
return 1.4f;
|
|
}
|
|
|
|
// hmm
|
|
return 1.0f;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------------
|
|
// NEBULA FORWARD DEFINITIONS
|
|
//
|
|
|
|
// return the alpha the passed poof should be rendered with, for a 2 shell nebula
|
|
float neb2_get_alpha_2shell(float inner_radius, float outer_radius, float magic_num, vec3d *v)
|
|
{
|
|
float dist;
|
|
float alpha;
|
|
vec3d eye_pos;
|
|
|
|
// get the eye position
|
|
neb2_get_eye_pos(&eye_pos);
|
|
|
|
// determine what alpha to draw this bitmap with
|
|
// higher alpha the closer the bitmap gets to the eye
|
|
dist = vm_vec_dist_quick(&eye_pos, v);
|
|
|
|
// if the point is inside the inner radius, alpha is based on distance to the player's eye,
|
|
// becoming more transparent as it gets close
|
|
if (dist <= inner_radius) {
|
|
// alpha per meter between the magic # and the inner radius
|
|
alpha = Nd->max_alpha_glide / (inner_radius - magic_num);
|
|
|
|
// above value times the # of meters away we are
|
|
alpha *= (dist - magic_num);
|
|
return alpha < 0.0f ? 0.0f : alpha;
|
|
}
|
|
// if the point is outside the inner radius, it starts out as completely transparent at max
|
|
// outer radius, and becomes more opaque as it moves towards inner radius
|
|
else if (dist <= outer_radius) {
|
|
// alpha per meter between the outer radius and the inner radius
|
|
alpha = Nd->max_alpha_glide / (outer_radius - inner_radius);
|
|
|
|
// above value times the range between the outer radius and the poof
|
|
return alpha < 0.0f ? 0.0f : alpha * (outer_radius - dist);
|
|
}
|
|
|
|
// otherwise transparent
|
|
return 0.0f;
|
|
}
|
|
|
|
// return an alpha value for a bitmap offscreen based upon "break" value
|
|
float neb2_get_alpha_offscreen(float sx, float sy, float incoming_alpha)
|
|
{
|
|
float alpha = 0.0f;
|
|
float per_pixel_x = incoming_alpha / (float)Nd->break_x;
|
|
float per_pixel_y = incoming_alpha / (float)Nd->break_y;
|
|
int off_x = ((sx < 0.0f) || (sx > (float)gr_screen.max_w));
|
|
int off_y = ((sy < 0.0f) || (sy > (float)gr_screen.max_h));
|
|
float off_x_amount = 0.0f;
|
|
float off_y_amount = 0.0f;
|
|
|
|
// determine how many pixels outside we are
|
|
if (off_x) {
|
|
if (sx < 0.0f) {
|
|
off_x_amount = fl_abs(sx);
|
|
} else {
|
|
off_x_amount = sx - (float)gr_screen.max_w;
|
|
}
|
|
}
|
|
if (off_y) {
|
|
if (sy < 0.0f) {
|
|
off_y_amount = fl_abs(sy);
|
|
} else {
|
|
off_y_amount = sy - (float)gr_screen.max_h;
|
|
}
|
|
}
|
|
|
|
// if offscreen X
|
|
if (off_x) {
|
|
// offscreen X and Y - and Y is greater
|
|
if (off_y && (off_y_amount > off_x_amount)) {
|
|
alpha = incoming_alpha - (off_y_amount * per_pixel_y);
|
|
} else {
|
|
alpha = incoming_alpha - (off_x_amount * per_pixel_x);
|
|
}
|
|
}
|
|
// offscreen y
|
|
else if (off_y) {
|
|
alpha = incoming_alpha - (off_y_amount * per_pixel_y);
|
|
}
|
|
// should never get here
|
|
else {
|
|
Int3();
|
|
}
|
|
|
|
return alpha < 0.0f ? 0.0f : alpha;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------
|
|
// WACKY LOCAL PLAYER NEBULA STUFF
|
|
//
|
|
|
|
vec3d cube_cen;
|
|
|
|
int crossed_border()
|
|
{
|
|
vec3d eye_pos;
|
|
float ws = Nd->cube_dim / (float)Neb2_slices;
|
|
float hs = Nd->cube_dim / (float)Neb2_slices;
|
|
float ds = Nd->cube_dim / (float)Neb2_slices;
|
|
|
|
// get the eye position
|
|
neb2_get_eye_pos(&eye_pos);
|
|
|
|
// check left, right (0, and 1, x and -x)
|
|
if (cube_cen.xyz.x - eye_pos.xyz.x > ws) {
|
|
return 0;
|
|
} else if (eye_pos.xyz.x - cube_cen.xyz.x > ws) {
|
|
return 1;
|
|
}
|
|
|
|
// check up, down (2, and 3, y and -y)
|
|
if (cube_cen.xyz.y - eye_pos.xyz.y > hs) {
|
|
return 2;
|
|
} else if (eye_pos.xyz.y - cube_cen.xyz.y > hs) {
|
|
return 3;
|
|
}
|
|
|
|
// check front, back (4, and 5, z and -z)
|
|
if (cube_cen.xyz.z - eye_pos.xyz.z > ds) {
|
|
return 4;
|
|
} else if (eye_pos.xyz.z - cube_cen.xyz.z > ds) {
|
|
return 5;
|
|
}
|
|
// nothing
|
|
return -1;
|
|
}
|
|
|
|
void neb2_copy(int xyz, int src, int dest)
|
|
{
|
|
int idx1, idx2;
|
|
|
|
switch(xyz) {
|
|
case 0:
|
|
for (idx1=0; idx1<Neb2_slices; idx1++) {
|
|
for(idx2=0; idx2<Neb2_slices; idx2++) {
|
|
Neb2_cubes[dest][idx1][idx2] = Neb2_cubes[src][idx1][idx2];
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
for (idx1=0; idx1<Neb2_slices; idx1++) {
|
|
for (idx2=0; idx2<Neb2_slices; idx2++) {
|
|
Neb2_cubes[idx1][dest][idx2] = Neb2_cubes[idx1][src][idx2];
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
for (idx1=0; idx1<Neb2_slices; idx1++) {
|
|
for (idx2=0; idx2<Neb2_slices; idx2++) {
|
|
Neb2_cubes[idx1][idx2][dest] = Neb2_cubes[idx1][idx2][src];
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Int3();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void neb2_gen_slice(int xyz, int src, vec3d *cube_center)
|
|
{
|
|
int idx1, idx2;
|
|
float h_incw, h_inch, h_incd;
|
|
float ws, hs, ds;
|
|
vec3d cube_corner;
|
|
vec3d *v;
|
|
|
|
ws = Nd->cube_dim / (float)Neb2_slices;
|
|
h_incw = ws / 2.0f;
|
|
hs = Nd->cube_dim / (float)Neb2_slices;
|
|
h_inch = hs / 2.0f;
|
|
ds = Nd->cube_dim / (float)Neb2_slices;
|
|
h_incd = ds / 2.0f;
|
|
cube_corner = *cube_center;
|
|
cube_corner.xyz.x -= (Nd->cube_dim / 2.0f);
|
|
cube_corner.xyz.y -= (Nd->cube_dim / 2.0f);
|
|
cube_corner.xyz.z -= (Nd->cube_dim / 2.0f);
|
|
switch(xyz) {
|
|
case 0:
|
|
for (idx1=0; idx1<Neb2_slices; idx1++) {
|
|
for (idx2=0; idx2<Neb2_slices; idx2++) {
|
|
v = &Neb2_cubes[src][idx1][idx2].pt;
|
|
|
|
v->xyz.x = h_incw + (ws * (float)src) + frand_range(-Nd->wj, Nd->wj);
|
|
v->xyz.y = h_inch + (hs * (float)idx1) + frand_range(-Nd->hj, Nd->hj);
|
|
v->xyz.z = h_incd + (ds * (float)idx2) + frand_range(-Nd->dj, Nd->dj);
|
|
vm_vec_add2(v, &cube_corner);
|
|
|
|
// set the bitmap
|
|
Neb2_cubes[src][idx1][idx2].bmap = neb2_get_bitmap();
|
|
|
|
// set the rotation speed
|
|
Neb2_cubes[src][idx1][idx2].rot = 0.0f;
|
|
Neb2_cubes[src][idx1][idx2].rot_speed = frand_range(-max_rotation, max_rotation);
|
|
Neb2_cubes[src][idx1][idx2].flash = 0.0f;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
for (idx1=0; idx1<Neb2_slices; idx1++) {
|
|
for (idx2=0; idx2<Neb2_slices; idx2++) {
|
|
v = &Neb2_cubes[idx1][src][idx2].pt;
|
|
|
|
v->xyz.x = h_incw + (ws * (float)idx1) + frand_range(-Nd->wj, Nd->wj);
|
|
v->xyz.y = h_inch + (hs * (float)src) + frand_range(-Nd->hj, Nd->hj);
|
|
v->xyz.z = h_incd + (ds * (float)idx2) + frand_range(-Nd->dj, Nd->dj);
|
|
vm_vec_add2(v, &cube_corner);
|
|
|
|
// set the bitmap
|
|
Neb2_cubes[idx1][src][idx2].bmap = neb2_get_bitmap();
|
|
|
|
// set the rotation speed
|
|
Neb2_cubes[idx1][src][idx2].rot = 0.0f;
|
|
Neb2_cubes[idx1][src][idx2].rot_speed = frand_range(-max_rotation, max_rotation);
|
|
Neb2_cubes[src][idx1][idx2].flash = 0.0f;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
for (idx1=0; idx1<Neb2_slices; idx1++) {
|
|
for (idx2=0; idx2<Neb2_slices; idx2++) {
|
|
v = &Neb2_cubes[idx1][idx2][src].pt;
|
|
|
|
v->xyz.x = h_incw + (ws * (float)idx1) + frand_range(-Nd->wj, Nd->wj);
|
|
v->xyz.y = h_inch + (hs * (float)idx2) + frand_range(-Nd->hj, Nd->hj);
|
|
v->xyz.z = h_incd + (ds * (float)src) + frand_range(-Nd->dj, Nd->dj);
|
|
vm_vec_add2(v, &cube_corner);
|
|
|
|
// set the bitmap
|
|
Neb2_cubes[idx1][idx2][src].bmap = neb2_get_bitmap();
|
|
|
|
// set the rotation speed
|
|
Neb2_cubes[idx1][idx2][src].rot = 0.0f;
|
|
Neb2_cubes[idx1][idx2][src].rot_speed = frand_range(-max_rotation, max_rotation);
|
|
Neb2_cubes[src][idx1][idx2].flash = 0.0f;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Int3();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// regenerate the player nebula
|
|
void neb2_regen()
|
|
{
|
|
int idx;
|
|
vec3d eye_pos;
|
|
matrix eye_orient;
|
|
|
|
mprintf(("Regenerating local nebula!\n"));
|
|
|
|
// get eye position and orientation
|
|
neb2_get_eye_pos(&eye_pos);
|
|
neb2_get_eye_orient(&eye_orient);
|
|
|
|
// determine the corner of the cube
|
|
cube_cen = eye_pos;
|
|
|
|
// generate slices of the cube
|
|
for (idx=0; idx<Neb2_slices; idx++) {
|
|
neb2_gen_slice(0, idx, &cube_cen);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* TODO: remove this
|
|
float max_area = 100000000.0f;
|
|
DCF(max_area, "")
|
|
{
|
|
dc_stuff_float(&max_area);
|
|
}
|
|
*/
|
|
|
|
int frames_total = 0;
|
|
int frame_count = 0;
|
|
float frame_avg;
|
|
void neb2_render_player()
|
|
{
|
|
GR_DEBUG_SCOPE("Nebula render player");
|
|
|
|
vertex p, ptemp;
|
|
int idx1, idx2, idx3;
|
|
float alpha;
|
|
int frame_rendered;
|
|
vec3d eye_pos;
|
|
matrix eye_orient;
|
|
|
|
// standalone servers can bail here
|
|
if (Game_mode & GM_STANDALONE_SERVER) {
|
|
return;
|
|
}
|
|
|
|
// if the mission is not a fullneb mission, skip
|
|
if (!(The_mission.flags[Mission::Mission_Flags::Fullneb])) {
|
|
return;
|
|
}
|
|
|
|
if (Neb2_regen) {
|
|
neb2_regen();
|
|
Neb2_regen = 0;
|
|
}
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
memset(&ptemp, 0, sizeof(ptemp));
|
|
|
|
// get eye position and orientation
|
|
neb2_get_eye_pos(&eye_pos);
|
|
neb2_get_eye_orient(&eye_orient);
|
|
|
|
// maybe swap stuff around if the player crossed a "border"
|
|
for (idx2=0; idx2<3; idx2++) {
|
|
switch(crossed_border()) {
|
|
case -1:
|
|
break;
|
|
|
|
case 0 :
|
|
cube_cen.xyz.x -= Nd->cube_dim / (float)Neb2_slices;
|
|
for (idx1=Neb2_slices-1; idx1>0; idx1--) {
|
|
neb2_copy(0, idx1-1, idx1);
|
|
}
|
|
neb2_gen_slice(0, 0, &cube_cen);
|
|
break;
|
|
|
|
case 1 :
|
|
cube_cen.xyz.x += Nd->cube_dim / (float)Neb2_slices;
|
|
for (idx1=0; idx1<Neb2_slices-1; idx1++) {
|
|
neb2_copy(0, idx1+1, idx1);
|
|
}
|
|
neb2_gen_slice(0, Neb2_slices - 1, &cube_cen);
|
|
break;
|
|
|
|
case 2 :
|
|
cube_cen.xyz.y -= Nd->cube_dim / (float)Neb2_slices;
|
|
for (idx1=Neb2_slices-1; idx1>0; idx1--) {
|
|
neb2_copy(1, idx1-1, idx1);
|
|
}
|
|
neb2_gen_slice(1, 0, &cube_cen);
|
|
break;
|
|
|
|
case 3 :
|
|
cube_cen.xyz.y += Nd->cube_dim / (float)Neb2_slices;
|
|
for (idx1=0; idx1<Neb2_slices-1; idx1++) {
|
|
neb2_copy(1, idx1+1, idx1);
|
|
}
|
|
neb2_gen_slice(1, Neb2_slices - 1, &cube_cen);
|
|
break;
|
|
|
|
case 4 :
|
|
cube_cen.xyz.z -= Nd->cube_dim / (float)Neb2_slices;
|
|
for (idx1=Neb2_slices-1; idx1>0; idx1--) {
|
|
neb2_copy(2, idx1-1, idx1);
|
|
}
|
|
neb2_gen_slice(2, 0, &cube_cen);
|
|
break;
|
|
|
|
case 5 :
|
|
cube_cen.xyz.z += Nd->cube_dim / (float)Neb2_slices;
|
|
for (idx1=0; idx1<Neb2_slices-1; idx1++) {
|
|
neb2_copy(2, idx1+1, idx1);
|
|
}
|
|
neb2_gen_slice(2, Neb2_slices - 1, &cube_cen);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we've switched nebula rendering off
|
|
if (Neb2_render_mode == NEB2_RENDER_NONE) {
|
|
return;
|
|
}
|
|
|
|
frame_rendered = 0;
|
|
// render the nebula
|
|
for (idx1=0; idx1<Neb2_slices; idx1++) {
|
|
for (idx2=0; idx2<Neb2_slices; idx2++) {
|
|
for (idx3=0; idx3<Neb2_slices; idx3++) {
|
|
|
|
// Miss this one out if the id is -1
|
|
if (Neb2_cubes[idx1][idx2][idx3].bmap == -1) {
|
|
continue;
|
|
}
|
|
|
|
pneb_tried++;
|
|
|
|
// rotate the poof
|
|
Neb2_cubes[idx1][idx2][idx3].rot += (Neb2_cubes[idx1][idx2][idx3].rot_speed * flFrametime);
|
|
if (Neb2_cubes[idx1][idx2][idx3].rot >= 360.0f) {
|
|
Neb2_cubes[idx1][idx2][idx3].rot = 0.0f;
|
|
}
|
|
|
|
// optimization 1 - don't draw backfacing poly's
|
|
// useless
|
|
if (vm_vec_dot_to_point(&eye_orient.vec.fvec, &eye_pos, &Neb2_cubes[idx1][idx2][idx3].pt) <= 0.0f) {
|
|
pneb_tossed_dot++;
|
|
continue;
|
|
}
|
|
|
|
g3_transfer_vertex(&p, &Neb2_cubes[idx1][idx2][idx3].pt);
|
|
|
|
// rotate and project the vertex into viewspace
|
|
g3_rotate_vertex(&ptemp, &Neb2_cubes[idx1][idx2][idx3].pt);
|
|
g3_project_vertex(&ptemp);
|
|
|
|
// get the proper alpha value
|
|
alpha = neb2_get_alpha_2shell(Nd->cube_inner, Nd->cube_outer, Nd->prad/4.0f, &Neb2_cubes[idx1][idx2][idx3].pt);
|
|
|
|
// optimization 2 - don't draw 0.0f or less poly's
|
|
// this amounts to big savings
|
|
if (alpha <= Nd->break_alpha) {
|
|
pneb_tossed_alpha++;
|
|
continue;
|
|
}
|
|
|
|
// drop poly's which are offscreen at all
|
|
// if the poly's are offscreen
|
|
if ( (ptemp.screen.xyw.x < 0.0f)
|
|
|| (ptemp.screen.xyw.x > (float)gr_screen.max_w)
|
|
|| (ptemp.screen.xyw.y < 0.0f)
|
|
|| (ptemp.screen.xyw.y > (float)gr_screen.max_h) )
|
|
{
|
|
alpha = neb2_get_alpha_offscreen(
|
|
ptemp.screen.xyw.x,
|
|
ptemp.screen.xyw.y,
|
|
alpha);
|
|
}
|
|
|
|
// optimization 2 - don't draw 0.0f or less poly's
|
|
// this amounts to big savings
|
|
if (alpha <= Nd->break_alpha) {
|
|
pneb_tossed_alpha++;
|
|
continue;
|
|
}
|
|
|
|
// set the bitmap and render
|
|
//gr_set_bitmap(Neb2_cubes[idx1][idx2][idx3].bmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, (alpha + Neb2_cubes[idx1][idx2][idx3].flash));
|
|
|
|
gr_set_lighting(false, false);
|
|
//g3_draw_rotated_bitmap(&p, fl_radians(Neb2_cubes[idx1][idx2][idx3].rot), Nd->prad, TMAP_FLAG_TEXTURED);
|
|
material mat_params;
|
|
material_set_unlit(&mat_params, Neb2_cubes[idx1][idx2][idx3].bmap, alpha + Neb2_cubes[idx1][idx2][idx3].flash, true, true);
|
|
mat_params.set_color_scale(3.f);
|
|
g3_render_rect_screen_aligned_rotated(&mat_params, &p, fl_radians(Neb2_cubes[idx1][idx2][idx3].rot), Nd->prad);
|
|
}
|
|
}
|
|
}
|
|
|
|
frames_total += frame_rendered;
|
|
frame_count++;
|
|
frame_avg = (float)frames_total / (float)frame_count;
|
|
|
|
// gr_set_color_fast(&Color_bright_red);
|
|
// gr_printf(30, 100, "Area %.3f", total_area);
|
|
#ifdef NEB2_THUMBNAIL
|
|
extern int tbmap;
|
|
if (tbmap != -1) {
|
|
gr_set_bitmap(tbmap);
|
|
gr_bitmap(0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// call this when the player's viewpoint has changed, this will cause the code to properly reset
|
|
// the eye's local poofs
|
|
void neb2_eye_changed()
|
|
{
|
|
Neb2_regen = 1;
|
|
}
|
|
|
|
/*
|
|
//Object types
|
|
#define OBJ_NONE 0 //unused object
|
|
#define OBJ_SHIP 1 //a ship
|
|
#define OBJ_WEAPON 2 //a laser, missile, etc
|
|
#define OBJ_FIREBALL 3 //an explosion
|
|
#define OBJ_START 4 //a starting point marker (player start, etc)
|
|
#define OBJ_WAYPOINT 5 //a waypoint object, maybe only ever used by Fred
|
|
#define OBJ_DEBRIS 6 //a flying piece of ship debris
|
|
#define OBJ_CMEASURE 7 //a countermeasure, such as chaff
|
|
#define OBJ_GHOST 8 //so far, just a placeholder for when a player dies.
|
|
#define OBJ_POINT 9 //generic object type to display a point in Fred.
|
|
#define OBJ_SHOCKWAVE 10 // a shockwave
|
|
#define OBJ_WING 11 // not really a type used anywhere, but I need it for Fred.
|
|
#define OBJ_OBSERVER 12 // used for multiplayer observers (possibly single player later)
|
|
#define OBJ_ASTEROID 13 // An asteroid, you know, a big rock, like debris, sort of.
|
|
#define OBJ_JUMP_NODE 14 // A jump node object, used only in Fred.
|
|
#define OBJ_BEAM 15 // beam weapons. we have to roll them into the object system to get the benefits of the collision pairs
|
|
*/
|
|
// get near and far fog values based upon object type and rendering mode
|
|
void neb2_get_fog_values(float *fnear, float *ffar, object *objp)
|
|
{
|
|
int type_index = -1;
|
|
|
|
//use defaults
|
|
*fnear = Default_fog_near;
|
|
*ffar = Default_fog_far;
|
|
|
|
if (objp == NULL) {
|
|
return;
|
|
}
|
|
|
|
// determine what fog index to use
|
|
if(objp->type == OBJ_SHIP) {
|
|
Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
|
|
if((objp->instance >= 0) && (objp->instance < MAX_SHIPS)) {
|
|
type_index = ship_query_general_type(objp->instance);
|
|
if(type_index > 0) {
|
|
*fnear = Ship_types[type_index].fog_start_dist;
|
|
*ffar = Ship_types[type_index].fog_complete_dist;
|
|
}
|
|
}
|
|
} else if (objp->type == OBJ_FIREBALL) { //mostly here for the warp effect
|
|
*fnear = objp->radius*2;
|
|
*ffar = (objp->radius*objp->radius*200)+objp->radius*200;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// This version of the function allows for global adjustment to fog values
|
|
void neb2_get_adjusted_fog_values(float *fnear, float *ffar, object *objp)
|
|
{
|
|
neb2_get_fog_values(fnear, ffar, objp);
|
|
|
|
// Multiply fog distances by mission multipliers
|
|
*fnear *= Neb2_fog_near_mult;
|
|
*ffar *= Neb2_fog_far_mult;
|
|
|
|
// Avoide divide-by-zero
|
|
if ((*fnear - *ffar) == 0)
|
|
*ffar = *fnear + 1.0f;
|
|
}
|
|
|
|
float nNf_near, nNf_far;
|
|
// given a position in space, return a value from 0.0 to 1.0 representing the fog level
|
|
float neb2_get_fog_intensity(object *obj)
|
|
{
|
|
float pct;
|
|
|
|
// get near and far fog values based upon object type and rendering mode
|
|
neb2_get_adjusted_fog_values(&nNf_near, &nNf_far, obj);
|
|
|
|
// get the fog pct
|
|
pct = vm_vec_dist_quick(&Eye_position, &obj->pos) / (nNf_far - nNf_near);
|
|
|
|
CLAMP(pct, 0.0f, 1.0f);
|
|
|
|
return pct;
|
|
}
|
|
|
|
//this only gets called after the one above has been called as it assumes you have set the near and far planes properly
|
|
//doun't use this outside of ship rendering
|
|
float neb2_get_fog_intensity(vec3d *pos)
|
|
{
|
|
float pct;
|
|
|
|
// get the fog pct
|
|
pct = vm_vec_dist_quick(&Eye_position, pos) / ((nNf_far*2) - nNf_near);
|
|
|
|
CLAMP(pct, 0.0f, 1.0f);
|
|
|
|
return pct;
|
|
}
|
|
|
|
// fogging stuff --------------------------------------------------------------------
|
|
|
|
// do a pre-render of the background nebula
|
|
#define ESIZE 32
|
|
ubyte tpixels[ESIZE * ESIZE * 4]; // for 32 bits
|
|
int last_esize = -1;
|
|
int this_esize = ESIZE;
|
|
float ex_scale, ey_scale;
|
|
int tbmap = -1;
|
|
// UnknownPlayer : Contained herein, the origins of the nebula rendering bug!
|
|
// I am really not entirely sure what this code achieves, but the old
|
|
// D3D calls were the cause of the nebula bug - they have been commented out.
|
|
// If you want to save some rendering time, I would suggest maybe kill this off.
|
|
// It doesn't use much, but it APPEARS to be fairly useless unless someone wants
|
|
// to enlighten me.
|
|
//
|
|
void neb2_pre_render(camid cid)
|
|
{
|
|
// if the mission is not a fullneb mission, skip
|
|
if (!(The_mission.flags[Mission::Mission_Flags::Fullneb])) {
|
|
return;
|
|
}
|
|
|
|
// bail early in lame and poly modes
|
|
if (Neb2_render_mode != NEB2_RENDER_POF) {
|
|
return;
|
|
}
|
|
|
|
// set the view clip
|
|
gr_screen.clip_width = this_esize;
|
|
gr_screen.clip_height = this_esize;
|
|
g3_start_frame(1); // Turn on zbuffering
|
|
g3_set_view(cid.getCamera());
|
|
gr_set_clip(0, 0, this_esize, this_esize);
|
|
|
|
// render the background properly
|
|
// hack - turn off nebula stuff
|
|
int neb_save = Neb2_render_mode;
|
|
Neb2_render_mode = NEB2_RENDER_NONE;
|
|
|
|
// draw background stuff nebula
|
|
stars_draw_background();
|
|
|
|
Neb2_render_mode = neb_save;
|
|
|
|
// grab the region
|
|
gr_get_region(0, this_esize, this_esize, (ubyte*)tpixels);
|
|
|
|
#ifdef NEB2_THUMBNAIL
|
|
if (tbmap == -1) {
|
|
tbmap = bm_create(16, this_esize, this_esize, tpixels, 0);
|
|
bm_lock(tbmap, 16, 0);
|
|
bm_unlock(tbmap);
|
|
}
|
|
#endif
|
|
|
|
// maybe do some swizzling
|
|
|
|
// end the frame
|
|
g3_end_frame();
|
|
|
|
gr_clear();
|
|
|
|
// if the size has changed between frames, make a new bitmap
|
|
if (this_esize != last_esize) {
|
|
last_esize = this_esize;
|
|
|
|
// recalculate ex_scale and ey_scale values for looking up color values
|
|
ex_scale = (float)this_esize / (float)gr_screen.max_w;
|
|
ey_scale = (float)this_esize / (float)gr_screen.max_h;
|
|
}
|
|
}
|
|
|
|
// wacky scheme for smoothing colors
|
|
int wacky_scheme = 3;
|
|
|
|
// fill in the position of the eye for this frame
|
|
void neb2_get_eye_pos(vec3d *eye_vector)
|
|
{
|
|
*eye_vector = Eye_position;
|
|
}
|
|
|
|
// fill in the eye orient for this frame
|
|
void neb2_get_eye_orient(matrix *eye_matrix)
|
|
{
|
|
*eye_matrix = Eye_matrix;
|
|
}
|
|
|
|
// get a (semi) random bitmap to use for a poof
|
|
int neb2_get_bitmap()
|
|
{
|
|
int count = 0;
|
|
int huh;
|
|
static int neb2_choose = 0;
|
|
|
|
// get a random count
|
|
count = (int)frand_range(1.0f, 5.0f);
|
|
|
|
// very ad-hoc
|
|
while(count > 0) {
|
|
// don't cycle too many times
|
|
huh = 0;
|
|
do {
|
|
if(neb2_choose == MAX_NEB2_POOFS - 1){
|
|
neb2_choose = 0;
|
|
} else {
|
|
neb2_choose++;
|
|
}
|
|
|
|
huh++;
|
|
}
|
|
while(!(Neb2_poof_flags & (1<<neb2_choose)) && (huh < 10));
|
|
count--;
|
|
}
|
|
|
|
// bitmap 0
|
|
if (Neb2_poofs[neb2_choose] < 0) {
|
|
return Neb2_poofs[0];
|
|
}
|
|
return Neb2_poofs[neb2_choose];
|
|
}
|
|
|
|
// nebula DCF functions ------------------------------------------------------
|
|
// TODO: With the new debug parser in place, most of these sub-commands can now be handled by neb2. This should clear up the DCF list a bit
|
|
DCF(neb2, "list nebula console commands")
|
|
{
|
|
// dc_printf("neb2_fog <X> <float> <float> : set near and far fog planes for ship type X\n");
|
|
// dc_printf("where X is an integer from 1 - 11\n");
|
|
// dc_printf("1 = cargo containers, 2 = fighters/bombers, 3 = cruisers\n");
|
|
// dc_printf("4 = freighters, 5 = capital ships, 6 = transports, 7 = support ships\n");
|
|
// dc_printf("8 = navbuoys, 9 = sentryguns, 10 = escape pods, 11 = background nebula polygons\n\n");
|
|
|
|
dc_printf("neb2_max_alpha : max alpha value (0.0 to 1.0) for cloud poofs. 0.0 is completely transparent\n");
|
|
dc_printf("neb2_break_alpha : alpha value (0.0 to 1.0) at which faded polygons are not drawn. higher values generally equals higher framerate, with more visual cloud popping\n");
|
|
dc_printf("neb2_break_off : how many pixels offscreen (left, right, top, bottom) when a cloud poof becomes fully transparent. Lower values cause quicker fading\n");
|
|
dc_printf("neb2_smooth : magic fog smoothing modes (0 - 3)\n");
|
|
dc_printf("neb2_select : <int> <int> where the first # is the bitmap to be adjusting (0 through 5), and the second int is a 0 or 1, to turn off and on\n");
|
|
dc_printf("neb2_rot : set max rotation speed for poofs\n");
|
|
dc_printf("neb2_prad : set cloud poof radius\n");
|
|
dc_printf("neb2_cdim : poof cube dimension\n");
|
|
dc_printf("neb2_cinner : poof cube inner dimension\n");
|
|
dc_printf("neb2_couter : poof cube outer dimension\n");
|
|
dc_printf("neb2_jitter : poof jitter\n");
|
|
dc_printf("neb2_mode : switch between no nebula, polygon background, pof background, lame, or HTL rendering (0, 1, 2, 3 and 4 respectively)\n\n");
|
|
dc_printf("neb2_ff : flash fade/sec\n");
|
|
dc_printf("neb2_background : rgb background color\n");
|
|
dc_printf("neb2_fog_color : rgb fog color\n");
|
|
|
|
// dc_printf("neb2_fog_vals : display all the current settings for all above values\n");
|
|
}
|
|
|
|
DCF(neb2_prad, "set cloud poof radius")
|
|
{
|
|
dc_stuff_float(&Nd->prad);
|
|
}
|
|
DCF(neb2_cdim, "poof cube dimension")
|
|
{
|
|
dc_stuff_float(&Nd->cube_dim);
|
|
}
|
|
|
|
DCF(neb2_cinner, "poof cube inner dimension")
|
|
{
|
|
dc_stuff_float(&Nd->cube_inner);
|
|
}
|
|
|
|
DCF(neb2_couter, "poof cube outer dimension")
|
|
{
|
|
dc_stuff_float(&Nd->cube_outer);
|
|
}
|
|
|
|
DCF(neb2_jitter, "poof jitter")
|
|
{
|
|
float value;
|
|
dc_stuff_float(&value);
|
|
Nd->hj = Nd->dj = Nd->wj = value;
|
|
}
|
|
|
|
DCF(neb2_max_alpha, "max alpha value (0.0 to 1.0) for cloud poofs.")
|
|
{
|
|
dc_stuff_float(&Nd->max_alpha_glide);
|
|
}
|
|
|
|
DCF(neb2_break_alpha, "alpha value (0.0 to 1.0) at which faded polygons are not drawn.")
|
|
{
|
|
dc_stuff_float(&Nd->break_alpha);
|
|
}
|
|
|
|
DCF(neb2_break_off, "how many pixels offscreen (left, right, top, bottom) when a cloud poof becomes fully transparent.")
|
|
{
|
|
int value;
|
|
dc_stuff_int(&value);
|
|
Nd->break_y = (float)value;
|
|
Nd->break_x = Nd->break_y * gr_screen.aspect;
|
|
}
|
|
|
|
DCF(neb2_smooth, "magic fog smoothing modes (0 - 3)")
|
|
{
|
|
int index;
|
|
dc_stuff_int(&index);
|
|
if ( (index >= 0) && (index <= 3) ) {
|
|
wacky_scheme = index;
|
|
} else {
|
|
dc_printf("Invalid smooth mode %i", index);
|
|
}
|
|
}
|
|
|
|
DCF(neb2_select, "Enables/disables a poof bitmap")
|
|
{
|
|
int bmap;
|
|
bool val_b;
|
|
|
|
dc_stuff_int(&bmap);
|
|
|
|
if ( (bmap >= 0) && (bmap < Neb2_poof_count) ) {
|
|
dc_stuff_boolean(&val_b);
|
|
|
|
val_b ? (Neb2_poof_flags |= (1<<bmap)) : (Neb2_poof_flags &= ~(1<<bmap));
|
|
}
|
|
}
|
|
|
|
DCF(neb2_rot, "set max rotation speed for poofs")
|
|
{
|
|
dc_stuff_float(&max_rotation);
|
|
}
|
|
|
|
DCF(neb2_ff, "flash fade/sec")
|
|
{
|
|
dc_stuff_float(&neb2_flash_fade);
|
|
}
|
|
|
|
DCF(neb2_mode, "Switches nebula render modes")
|
|
{
|
|
int mode;
|
|
dc_stuff_int(&mode);
|
|
|
|
switch (mode) {
|
|
case NEB2_RENDER_NONE:
|
|
Neb2_render_mode = NEB2_RENDER_NONE;
|
|
break;
|
|
|
|
case NEB2_RENDER_POF:
|
|
Neb2_render_mode = NEB2_RENDER_POF;
|
|
stars_set_background_model(BACKGROUND_MODEL_FILENAME, "Eraseme3");
|
|
stars_set_background_orientation();
|
|
break;
|
|
|
|
case NEB2_RENDER_HTL:
|
|
Neb2_render_mode = NEB2_RENDER_HTL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DCF(neb2_slices, "Sets how many 'slices' are used in the nebula")
|
|
{
|
|
dc_stuff_int(&Neb2_slices);
|
|
neb2_eye_changed();
|
|
}
|
|
|
|
DCF(neb2_background, "Sets the RGB background color (lame rendering)")
|
|
{
|
|
int r, g, b;
|
|
|
|
dc_stuff_int(&r);
|
|
dc_stuff_int(&g);
|
|
dc_stuff_int(&b);
|
|
|
|
Neb2_background_color[0] = r;
|
|
Neb2_background_color[1] = g;
|
|
Neb2_background_color[2] = b;
|
|
}
|
|
|
|
DCF(neb2_fog_color, "Sets the RGB fog color (HTL)")
|
|
{
|
|
ubyte r, g, b;
|
|
|
|
dc_stuff_ubyte(&r);
|
|
dc_stuff_ubyte(&g);
|
|
dc_stuff_ubyte(&b);
|
|
|
|
Neb2_fog_color_r = r;
|
|
Neb2_fog_color_g = g;
|
|
Neb2_fog_color_b = b;
|
|
}
|