1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12 #include "bmpman/bmpman.h"
13 #include "cmdline/cmdline.h"
14 #include "ddsutils/ddsutils.h"
15 #include "debugconsole/console.h"
16 #include "freespace.h"
17 #include "jpgutils/jpgutils.h"
18 #include "mission/missionparse.h"
19 #include "nebula/neb.h"
20 #include "object/object.h"
21 #include "options/Option.h"
22 #include "parse/parselo.h"
23 #include "pcxutils/pcxutils.h"
24 #include "render/3d.h"
25 #include "render/batching.h"
26 #include "ship/ship.h"
27 #include "starfield/starfield.h"
28 #include "tgautils/tgautils.h"
29 #include "tracing/tracing.h"
30 #include "graphics/light.h"
31
32
33 // --------------------------------------------------------------------------------------------------------
34 // NEBULA DEFINES/VARS
35 //
36
37 bool Nebula_sexp_used = false;
38
39 ubyte Neb2_fog_color[3] = { 0,0,0 };
40
41 static ubyte *Neb2_htl_fog_data = NULL;
42
43 // #define NEB2_THUMBNAIL
44
45 /*
46 3D CARDS THAT FOG PROPERLY
47 Voodoo1
48 Voodoo2
49 G200
50 TNT
51
52 3D CARDS THAT DON'T FOG PROPERLY
53 Permedia2
54 AccelStar II
55 */
56
57 // if nebula rendering is active (DCF stuff - not mission specific)
58 int Neb2_render_mode = NEB2_RENDER_NONE;
59
60 SCP_vector<poof_info> Poof_info;
61
62 float Poof_dist_threshold;
63 vec3d Poof_last_gen_pos;
64 float Poof_accum[MAX_NEB2_POOFS];
65 float Poof_density_multiplier;
66
67 const float UPKEEP_DIST_MULT = 1.2f;
68
69 const float PROBABLY_TOO_MANY_POOFS = 100000.0f;
70
71 // array of neb2 poofs
72 int32_t Neb2_poof_flags = 0;
73
74 // array of neb2 bitmaps
75 char Neb2_bitmap_filenames[MAX_NEB2_BITMAPS][MAX_FILENAME_LEN] = {
76 "", "", "", "", "", ""
77 };
78 int Neb2_bitmap[MAX_NEB2_BITMAPS] = { -1, -1, -1, -1, -1, -1 };
79 int Neb2_bitmap_count = 0;
80
81 // texture to use for this level
82 char Neb2_texture_name[MAX_FILENAME_LEN] = "";
83
84 float max_rotation = 3.75f;
85 float neb2_flash_fade = 0.3f;
86
87 //WMC - these were originally indexed to SHIP_TYPE_FIGHTER_BOMBER
88 const static float Default_fog_near = 10.0f;
89 const static float Default_fog_far = 750.0f;
90
91 // fog near and far values for rendering the background nebula
92 #define NEB_BACKG_FOG_NEAR_GLIDE 2.5f
93 #define NEB_BACKG_FOG_NEAR_D3D 4.5f
94 #define NEB_BACKG_FOG_FAR_GLIDE 10.0f
95 #define NEB_BACKG_FOG_FAR_D3D 10.0f
96 float Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_GLIDE;
97 float Neb_backg_fog_far = NEB_BACKG_FOG_FAR_GLIDE;
98
99 // stats
100 int pneb_tried = 0; // total pnebs tried to render
101 int pneb_tossed_alpha = 0; // pnebs tossed because of alpha optimization
102 int pneb_tossed_dot = 0; // pnebs tossed because of dot product
103 int pneb_tossed_off = 0; // pnebs tossed because of being offscree
104 int neb_tried = 0; // total nebs tried
105 int neb_tossed_alpha = 0; // nebs tossed because of alpha
106 int neb_tossed_dot = 0; // nebs tossed because of dot product
107 int neb_tossed_count = 0; // nebs tossed because of max render count
108
109 // the AWACS suppression level for the nebula
110 float Neb2_awacs = -1.0f;
111
112 // The visual render distance multipliers for the nebula
113 float Neb2_fog_near_mult = 1.0f;
114 float Neb2_fog_far_mult = 1.0f;
115
116
117 // this is the percent of visibility at the fog far distance
118 const float NEB_FOG_FAR_PCT = 0.1f;
119
120 SCP_vector<poof> Neb2_poofs;
121
122 int Neb2_background_color[3] = {0, 0, 255}; // rgb background color (used for lame rendering)
123
124 const SCP_vector<std::pair<int, SCP_string>> DetailLevelValues = {{ 0, "Minimum" },
125 { 1, "Low" },
126 { 2, "Medium" },
127 { 3, "High" },
128 { 4, "Ultra" }, };
129
130 const auto ModelDetailOption = options::OptionBuilder<int>("Graphics.NebulaDetail",
131 "Nebula Detail",
132 "Detail level of nebulas").category("Graphics").values(
__anon14e217050102(int val, bool) 133 DetailLevelValues).default_val(MAX_DETAIL_LEVEL).importance(7).change_listener([](int val, bool) {
134 Detail.nebula_detail = val;
135 return true;
136 }).finish();
137
138 // --------------------------------------------------------------------------------------------------------
139 // NEBULA FORWARD DECLARATIONS
140 //
141
142 // return the alpha the passed poof should be rendered with, for a 2 shell nebula
143 float neb2_get_alpha_2shell(float inner_radius, float outer_radius, float magic_num, vec3d *v);
144
145 // return an alpha value for a bitmap offscreen based upon "break" value
146 float neb2_get_alpha_offscreen(float sx, float sy, float incoming_alpha);
147
148 // do a pre-render of the background nebula
149 void neb2_pre_render(camid cid);
150
151 // fill in the position of the eye for this frame
152 void neb2_get_eye_pos(vec3d *eye_vector);
153
154 // fill in the eye orient for this frame
155 void neb2_get_eye_orient(matrix *eye_matrix);
156
157 // --------------------------------------------------------------------------------------------------------
158 // NEBULA FUNCTIONS
159 //
160
161 // initialize neb2 stuff at game startup
neb2_init()162 void neb2_init()
163 {
164 char name[MAX_FILENAME_LEN];
165
166 try
167 {
168 // read in the nebula.tbl
169 read_file_text("nebula.tbl", CF_TYPE_TABLES);
170 reset_parse();
171
172 // background bitmaps
173 Neb2_bitmap_count = 0;
174 while (!optional_string("#end")) {
175 // nebula
176 required_string("+Nebula:");
177 stuff_string(name, F_NAME, MAX_FILENAME_LEN);
178
179 if (Neb2_bitmap_count < MAX_NEB2_BITMAPS) {
180 strcpy_s(Neb2_bitmap_filenames[Neb2_bitmap_count++], name);
181 }
182 else {
183 WarningEx(LOCATION, "nebula.tbl\nExceeded maximum number of nebulas (%d)!\nSkipping %s.", MAX_NEB2_BITMAPS, name);
184 }
185 }
186
187 // poofs
188 while (required_string_one_of(3, "#end", "+Poof:", "$Name:")) {
189
190 if (Poof_info.size() < MAX_NEB2_POOFS) {
191 poof_info new_poof;
192
193 if (optional_string("+Poof:")) { // retail style
194 stuff_string(name, F_NAME, MAX_FILENAME_LEN);
195 strcpy_s(new_poof.bitmap_filename, name);
196
197 strcpy_s(new_poof.name, name);
198
199 Poof_info.push_back(new_poof);
200 } else if (optional_string("$Name:")) { // new style
201 stuff_string(new_poof.name, F_NAME, NAME_LENGTH);
202
203 required_string("$Bitmap:");
204 stuff_string(new_poof.bitmap_filename, F_NAME, MAX_FILENAME_LEN);
205
206 if (optional_string("$Scale:"))
207 new_poof.scale = ::util::parseUniformRange<float>(0.01f, 100000.0f);
208
209 if (optional_string("$Density:")) {
210 stuff_float(&new_poof.density);
211 if (new_poof.density <= 0.0f) {
212 Warning(LOCATION, "Poof %s must have a density greater than 0.", new_poof.name);
213 new_poof.density = 150.0f;
214 }
215 new_poof.density = 1 / (new_poof.density * new_poof.density * new_poof.density);
216 }
217
218 if (optional_string("$Rotation:"))
219 new_poof.rotation = ::util::parseUniformRange<float>(-1000.0f, 1000.0f);
220
221 if (optional_string("$View Distance:")) {
222 stuff_float(&new_poof.view_dist);
223 if (new_poof.view_dist < 0.0f) {
224 Warning(LOCATION, "Poof %s must have a positive view distance.", new_poof.name);
225 new_poof.view_dist = 360.f;
226 }
227
228 float volume = PI * 4 / 3 * (new_poof.view_dist * new_poof.view_dist * new_poof.view_dist);
229 if (volume * new_poof.density > PROBABLY_TOO_MANY_POOFS) {
230 Warning(LOCATION, "Poof %s will have over 100,000 poofs on the field at once, and could cause serious performance issues. "
231 "Remember that as $Density decreases and $View Distance increases, the total number of poofs increases exponentially.", new_poof.name);
232 }
233 }
234
235 if (optional_string("$Alpha:")) {
236 new_poof.alpha = ::util::parseUniformRange<float>(0.0f, 1.0f);
237 }
238
239 Poof_info.push_back(new_poof);
240 }
241 }
242 else {
243 WarningEx(LOCATION, "nebula.tbl\nExceeded maximum number of nebula poofs (%d)!\nSkipping %s.", (int)MAX_NEB2_POOFS, name);
244 }
245 }
246 }
247 catch (const parse::ParseException& e)
248 {
249 mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "nebula.tbl", e.what()));
250 return;
251 }
252 }
253
poof_is_used(size_t idx)254 bool poof_is_used(size_t idx) {
255 return (Neb2_poof_flags & (1 << idx)) != 0;
256 }
257
neb2_get_fog_color(ubyte * r,ubyte * g,ubyte * b)258 void neb2_get_fog_color(ubyte *r, ubyte *g, ubyte *b)
259 {
260 if (r) *r = Neb2_fog_color[0];
261 if (g) *g = Neb2_fog_color[1];
262 if (b) *b = Neb2_fog_color[2];
263 }
264
neb2_level_init()265 void neb2_level_init()
266 {
267 Nebula_sexp_used = false;
268 }
269
270 float nNf_near, nNf_density;
271
neb2_poof_setup()272 void neb2_poof_setup() {
273 if (!Neb2_poof_flags)
274 return;
275
276 // make the total density of poofs be the average of all poofs, and each poofs density is its relative proportion compared to others
277 // this way we maintain the retail way of not affecting total density by having more poof types
278 float Poof_density_sum_square = 0.0f;
279 float Poof_density_sum = 0.0f;
280
281 // also determine the minimum distance before re-triggering a poof upkeep
282 Poof_dist_threshold = 9999.0f;
283 for (size_t i = 0; i < Poof_info.size(); i++) {
284 if (poof_is_used(i)) {
285 Poof_density_sum_square += Poof_info[i].density * Poof_info[i].density;
286 Poof_density_sum += Poof_info[i].density;
287
288 float dist_threshold = Poof_info[i].view_dist * (UPKEEP_DIST_MULT - 1.0f);
289 if (dist_threshold < Poof_dist_threshold)
290 Poof_dist_threshold = dist_threshold;
291 }
292 }
293 Poof_density_multiplier = Poof_density_sum_square / (Poof_density_sum * Poof_density_sum);
294 Poof_density_multiplier *= (Detail.nebula_detail + 0.5f) / (MAX_DETAIL_LEVEL + 0.5f); // scale the poofs down based on detail level
295 }
296
297 // initialize nebula stuff - call from game_post_level_init(), so the mission has been loaded
neb2_post_level_init()298 void neb2_post_level_init()
299 {
300 int idx;
301
302 // standalone servers can bail here
303 if (Game_mode & GM_STANDALONE_SERVER) {
304 return;
305 }
306
307 // Skip actual rendering if we're in FRED.
308 if(Fred_running)
309 {
310 Neb2_render_mode = NEB2_RENDER_NONE;
311 return;
312 }
313
314 // if the mission is not a fullneb mission, skip
315 if ( !((The_mission.flags[Mission::Mission_Flags::Fullneb]) || Nebula_sexp_used) ) {
316 Neb2_render_mode = NEB2_RENDER_NONE;
317 Neb2_awacs = -1.0f;
318 return;
319 }
320
321 // OK, lets try something a bit more interesting
322 if (strlen(Neb2_texture_name)) {
323 // Set a default colour just in case something goes wrong
324 Neb2_fog_color[0] = 30;
325 Neb2_fog_color[1] = 52;
326 Neb2_fog_color[2] = 157;
327
328 Neb2_htl_fog_data = new ubyte[768];
329
330 if ((Neb2_htl_fog_data != NULL) && (pcx_read_header(Neb2_texture_name, NULL, NULL, NULL, NULL, Neb2_htl_fog_data) == PCX_ERROR_NONE)) {
331 // based on the palette, get an average color value (this doesn't really account for actual pixel usage though)
332 ushort r = 0, g = 0, b = 0, pcount = 0;
333 for (idx = 0; idx < 768; idx += 3) {
334 if (Neb2_htl_fog_data[idx] || Neb2_htl_fog_data[idx+1] || Neb2_htl_fog_data[idx+2]) {
335 r = r + Neb2_htl_fog_data[idx];
336 g = g + Neb2_htl_fog_data[idx+1];
337 b = b + Neb2_htl_fog_data[idx+2];
338 pcount++;
339 }
340 }
341
342 if (pcount > 0) {
343 Neb2_fog_color[0] = (ubyte)(r / pcount);
344 Neb2_fog_color[1] = (ubyte)(g / pcount);
345 Neb2_fog_color[2] = (ubyte)(b / pcount);
346 } else {
347 // it's just black
348 Neb2_fog_color[0] = Neb2_fog_color[1] = Neb2_fog_color[2] = 0;
349 }
350
351 // done, now free up the palette data
352 if ( Neb2_htl_fog_data != NULL ) {
353 delete[] Neb2_htl_fog_data;
354 Neb2_htl_fog_data = NULL;
355 }
356 }
357 }
358
359 Neb2_render_mode = NEB2_RENDER_HTL;
360
361 // load in all nebula bitmaps
362 for (poof_info &pinfo : Poof_info) {
363 if (pinfo.bitmap < 0) {
364 pinfo.bitmap = bm_load(pinfo.bitmap_filename);
365 }
366 }
367
368 pneb_tried = 0;
369 pneb_tossed_alpha = 0;
370 pneb_tossed_dot = 0;
371 neb_tried = 0;
372 neb_tossed_alpha = 0;
373 neb_tossed_dot = 0;
374 neb_tossed_count = 0;
375
376 // setup proper fogging values
377 Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_D3D;
378 Neb_backg_fog_far = NEB_BACKG_FOG_FAR_D3D;
379
380 // if we are going to use fullneb, but aren't fullneb yet, then be sure to reset our mode
381 if ( !(The_mission.flags[Mission::Mission_Flags::Fullneb]) ) {
382 Neb2_render_mode = NEB2_RENDER_NONE;
383 Neb2_awacs = -1.0f;
384 }
385
386 // truncate the poof flags down to the poofs we have
387 if (Poof_info.size() < MAX_NEB2_POOFS) {
388 int available_poofs_mask = (1 << Poof_info.size()) - 1;
389
390 // check for negative here too, because if we're not at max, 32, then we're gauranteed not to have the sign bit
391 if (Neb2_poof_flags > available_poofs_mask || Neb2_poof_flags < 0)
392 Warning(LOCATION, "One or more invalid nebula poofs detected!");
393
394 Neb2_poof_flags = Neb2_poof_flags & available_poofs_mask;
395 }
396
397 // set the mission fog near dist and density
398 float fog_far;
399 neb2_get_adjusted_fog_values(&nNf_near, &fog_far, &nNf_density, nullptr);
400
401 for (float& accum : Poof_accum)
402 accum = 0.0f;
403
404 // a bit awkward but this will force a full sphere gen
405 vm_vec_make(&Poof_last_gen_pos, 999999.0f, 999999.0f, 999999.0f);
406
407 neb2_poof_setup();
408 }
409
410 // shutdown nebula stuff
neb2_level_close()411 void neb2_level_close()
412 {
413 // standalone servers can bail here
414 if (Game_mode & GM_STANDALONE_SERVER) {
415 return;
416 }
417
418 // if the mission is not a fullneb mission, skip
419 if ( !((The_mission.flags[Mission::Mission_Flags::Fullneb]) || Nebula_sexp_used) ) {
420 return;
421 }
422
423 // unload all nebula bitmaps
424 for (poof_info& pinfo : Poof_info) {
425 if (pinfo.bitmap >= 0) {
426 bm_release(pinfo.bitmap);
427 pinfo.bitmap = -1;
428 }
429 }
430
431 // clear da poofs
432 Neb2_poofs.clear();
433
434 // unflag the mission as being fullneb so stuff doesn't fog in the techdata room :D
435 The_mission.flags.remove(Mission::Mission_Flags::Fullneb);
436
437 if (Neb2_htl_fog_data) {
438 delete[] Neb2_htl_fog_data;
439 Neb2_htl_fog_data = NULL;
440 }
441 }
442
443 // call before beginning all rendering
neb2_render_setup(camid cid)444 void neb2_render_setup(camid cid)
445 {
446 GR_DEBUG_SCOPE("Nebula Setup");
447 TRACE_SCOPE(tracing::SetupNebula);
448
449 // standalone servers can bail here
450 if (Game_mode & GM_STANDALONE_SERVER) {
451 return;
452 }
453
454 // if the mission is not a fullneb mission, skip
455 if ( !(The_mission.flags[Mission::Mission_Flags::Fullneb]) ) {
456 return;
457 }
458
459 if (Neb2_render_mode == NEB2_RENDER_HTL) {
460 // RT The background needs to be the same colour as the fog and this seems
461 // to be the ideal place to do it
462 ubyte tr = gr_screen.current_clear_color.red;
463 ubyte tg = gr_screen.current_clear_color.green;
464 ubyte tb = gr_screen.current_clear_color.blue;
465
466 neb2_get_fog_color(
467 &gr_screen.current_clear_color.red,
468 &gr_screen.current_clear_color.green,
469 &gr_screen.current_clear_color.blue);
470
471 gr_clear();
472
473 gr_screen.current_clear_color.red = tr;
474 gr_screen.current_clear_color.green = tg;
475 gr_screen.current_clear_color.blue = tb;
476
477 return;
478 }
479
480 // pre-render the real background nebula
481 neb2_pre_render(cid);
482 }
483
484 // level paging code
neb2_page_in()485 void neb2_page_in()
486 {
487 // load in all nebula bitmaps
488 if ( (The_mission.flags[Mission::Mission_Flags::Fullneb]) || Nebula_sexp_used ) {
489 for (size_t idx = 0; idx < Poof_info.size(); idx++) {
490 if (Poof_info[idx].bitmap >= 0 && poof_is_used(idx)) {
491 bm_page_in_texture(Poof_info[idx].bitmap);
492 }
493 }
494 }
495 }
496
497 // should we not render this object because its obscured by the nebula?
498 int neb_skip_opt = 0;
499 DCF(neb_skip, "Toggles culling of objects obscured by nebula")
500 {
501 neb_skip_opt = !neb_skip_opt;
502 if (neb_skip_opt) {
503 dc_printf("Using neb object skipping!\n");
504 } else {
505 dc_printf("Not using neb object skipping!\n");
506 }
507 }
neb2_skip_render(object * objp,float z_depth)508 int neb2_skip_render(object *objp, float z_depth)
509 {
510 float fog_near, fog_far, fog_density;
511
512 // if we're never skipping
513 if (!neb_skip_opt) {
514 return 0;
515 }
516
517 // get near and far fog values based upon object type and rendering mode
518 neb2_get_adjusted_fog_values(&fog_near, &fog_far, &fog_density);
519 float fog = pow(fog_density, z_depth - fog_near + objp->radius);
520
521 // by object type
522 switch( objp->type ) {
523 // some objects we always render
524 case OBJ_SHOCKWAVE:
525 case OBJ_JUMP_NODE:
526 case OBJ_NONE:
527 case OBJ_GHOST:
528 case OBJ_BEAM:
529 case OBJ_WAYPOINT:
530 return 0;
531
532 // any weapon over 500 meters away
533 // Use the "far" distance multiplier here
534 case OBJ_WEAPON:
535 if (fog < 0.05f) {
536 return 1;
537 }
538 break;
539
540 // any ship less than 3% visible at their closest point
541 case OBJ_SHIP:
542 if (fog < 0.03f)
543 return 1;
544 break;
545
546 // any fireball over the fog limit for small ships
547 case OBJ_FIREBALL:
548 return 0;
549 break;
550
551 // any debris over the fog limit for small ships
552 case OBJ_DEBRIS:
553 return 0;
554 break;
555
556 // any asteroid less than 3% visible at their closest point
557 case OBJ_ASTEROID:
558 if (fog < 0.03f)
559 return 1;
560 break;
561
562 // hmmm. unknown object type - should probably let it through
563 default:
564 Int3();
565 return 0;
566 }
567 return 0;
568 }
569
570 // extend LOD
neb2_get_lod_scale(int objnum)571 float neb2_get_lod_scale(int objnum)
572 {
573 ship *shipp;
574 ship_info *sip;
575
576 // bogus
577 if ( (objnum < 0)
578 || (objnum >= MAX_OBJECTS)
579 || (Objects[objnum].type != OBJ_SHIP)
580 || (Objects[objnum].instance < 0)
581 || (Objects[objnum].instance >= MAX_SHIPS)) {
582 return 1.0f;
583 }
584 shipp = &Ships[Objects[objnum].instance];
585 sip = &Ship_info[shipp->ship_info_index];
586
587 // small ship?
588 if (sip->is_small_ship()) {
589 return 1.8f;
590 } else if (sip->is_big_ship()) {
591 return 1.4f;
592 }
593
594 // hmm
595 return 1.0f;
596 }
597
598
599 // --------------------------------------------------------------------------------------------------------
600 // NEBULA FORWARD DEFINITIONS
601 //
602
603 // return the alpha the passed poof should be rendered with, for a 2 shell nebula
neb2_get_alpha_2shell(float alpha,float inner_radius,float outer_radius,float magic_num,vec3d * v)604 float neb2_get_alpha_2shell(float alpha, float inner_radius, float outer_radius, float magic_num, vec3d *v)
605 {
606 float dist;
607 vec3d eye_pos;
608
609 // get the eye position
610 neb2_get_eye_pos(&eye_pos);
611
612 // determine what alpha to draw this bitmap with
613 // higher alpha the closer the bitmap gets to the eye
614 dist = vm_vec_dist_quick(&eye_pos, v);
615
616 // if the point is inside the inner radius, alpha is based on distance to the player's eye,
617 // becoming more transparent as it gets close
618 if (dist <= inner_radius) {
619 // alpha per meter between the magic # and the inner radius
620 alpha = alpha / (inner_radius - magic_num);
621
622 // above value times the # of meters away we are
623 alpha *= (dist - magic_num);
624 return alpha < 0.0f ? 0.0f : alpha;
625 }
626 // if the point is outside the inner radius, it starts out as completely transparent at max
627 // outer radius, and becomes more opaque as it moves towards inner radius
628 else if (dist <= outer_radius) {
629 // alpha per meter between the outer radius and the inner radius
630 alpha = alpha / (outer_radius - inner_radius);
631
632 // above value times the range between the outer radius and the poof
633 return alpha < 0.0f ? 0.0f : alpha * (outer_radius - dist);
634 }
635
636 // otherwise transparent
637 return 0.0f;
638 }
639
640 // -------------------------------------------------------------------------------------------------
641 // WACKY LOCAL PLAYER NEBULA STUFF
642 //
643
neb2_toggle_poof(int poof_idx,bool enabling)644 void neb2_toggle_poof(int poof_idx, bool enabling) {
645
646 if (enabling) Neb2_poof_flags |= (1 << poof_idx);
647 else Neb2_poof_flags &= ~(1 << poof_idx);
648
649 Neb2_poofs.clear();
650
651 // a bit awkward but this will force a full sphere gen
652 vm_vec_make(&Poof_last_gen_pos, 999999.0f, 999999.0f, 999999.0f);
653
654 neb2_poof_setup();
655 }
656
new_poof(size_t poof_info_idx,vec3d * pos)657 void new_poof(size_t poof_info_idx, vec3d* pos) {
658 poof new_poof;
659 poof_info* pinfo = &Poof_info[poof_info_idx];
660
661 new_poof.poof_info_index = poof_info_idx;
662 new_poof.flash = 0;
663 new_poof.radius = pinfo->scale.next();
664 new_poof.pt = *pos;
665 new_poof.rot_speed = fl_radians(pinfo->rotation.next());
666 new_poof.alpha = pinfo->alpha.next();
667 vm_vec_rand_vec(&new_poof.up_vec);
668
669 Neb2_poofs.push_back(new_poof);
670 }
671
672 static uint neb_rand_seed = 0;
673
upkeep_poofs()674 void upkeep_poofs()
675 {
676 vec3d eye_pos;
677 neb2_get_eye_pos(&eye_pos);
678
679 // cull distant poofs
680 if (!Neb2_poofs.empty()) {
681 for (size_t i = 0; i < Neb2_poofs.size();) {
682 if (vm_vec_dist(&Neb2_poofs[i].pt, &eye_pos) > Poof_info[Neb2_poofs[i].poof_info_index].view_dist * UPKEEP_DIST_MULT) {
683 Neb2_poofs[i] = Neb2_poofs.back();
684 Neb2_poofs.pop_back();
685 }
686 else // if we needed to cull we should not advance because we just moved a new poof into this spot
687 i++;
688 }
689 }
690
691 neb_rand_seed = 0;
692
693 // make new poofs
694 for (size_t i = 0; i < Poof_info.size(); i++) {
695 if (!poof_is_used(i))
696 continue;
697 poof_info* pinfo = &Poof_info[i];
698
699 float gen_side_length = (pinfo->view_dist * UPKEEP_DIST_MULT) * 2;
700 float gen_density = pinfo->density * Poof_density_multiplier;
701
702 float poofs_to_gen = gen_side_length * gen_side_length * gen_side_length * gen_density;
703
704 // store the fractional part, take the integer part
705 Poof_accum[i] = modff(Poof_accum[i] + (poofs_to_gen), &poofs_to_gen);
706 for (int j = 0; j < poofs_to_gen; j++) {
707 vec3d pos = eye_pos;
708 vec3d offset = eye_pos / gen_side_length;
709 vec3d rand_pos = vm_well_distributed_rand_vec(neb_rand_seed, &offset);
710 neb_rand_seed++;
711
712 rand_pos.xyz.x *= gen_side_length / 2;
713 rand_pos.xyz.y *= gen_side_length / 2;
714 rand_pos.xyz.z *= gen_side_length / 2;
715
716 pos += rand_pos;
717
718 // we generated poofs in a cube, now keep only those that are within the view sphere, and weren't within the last view sphere
719 // not terribly efficient but very simple
720 if (vm_vec_dist(&eye_pos, &pos) <= (gen_side_length / 2) &&
721 vm_vec_dist(&Poof_last_gen_pos, &pos) > (gen_side_length / 2))
722 new_poof(i, &pos);
723 }
724 }
725 }
726
neb2_render_poofs()727 void neb2_render_poofs()
728 {
729 GR_DEBUG_SCOPE("Nebula render player");
730 TRACE_SCOPE(tracing::DrawPoofs);
731
732 vertex p, ptemp;
733 float alpha;
734 vec3d eye_pos;
735 matrix eye_orient;
736
737 // standalone servers can bail here
738 if (Game_mode & GM_STANDALONE_SERVER) {
739 return;
740 }
741
742 // if the mission is not a fullneb mission, skip
743 if (!(The_mission.flags[Mission::Mission_Flags::Fullneb])) {
744 return;
745 }
746
747 memset(&p, 0, sizeof(p));
748 memset(&ptemp, 0, sizeof(ptemp));
749
750 // get eye position and orientation
751 neb2_get_eye_pos(&eye_pos);
752 neb2_get_eye_orient(&eye_orient);
753
754 // maybe swap stuff around if the player crossed the dist threshold
755 if (vm_vec_dist(&eye_pos, &Poof_last_gen_pos) > Poof_dist_threshold) {
756 upkeep_poofs();
757 Poof_last_gen_pos = eye_pos;
758 }
759
760 // if we've switched nebula rendering off
761 if (Neb2_render_mode == NEB2_RENDER_NONE) {
762 return;
763 }
764
765 // render the nebula
766 for (poof &pf : Neb2_poofs) {
767 poof_info* pinfo = &Poof_info[pf.poof_info_index];
768
769 // Miss this one out if the id is -1
770 if (pinfo->bitmap < 0)
771 continue;
772
773 // generate the bitmap orient
774 // If the bitmap is large and distant and points in the view fvec direction (like retail poofs do) this looks good when moving,
775 // but bad when you rotate (since the bitmaps rotate in place with you, which looks weird).
776 // Conversely, if the bitmap is close and small (like retail size-ish) and points at the view position, it looks good
777 // when rotating (since the bitmaps remain static) but bad when moving (as they rotate in place to continue pointing at you)
778 //
779 // So blend between the two styles based on promixity and size, since distant (relative to their size) bitmaps
780 // are more affected by rotating than moving and close bitmaps are vice versa
781 // We will scale the bitmap direction from the view position to "off to infinity" in the negative view fvec direction - Asteroth
782 matrix orient;
783 vec3d view_pos;
784 {
785 float scalar = -1 / powf((vm_vec_dist(&eye_pos, &pf.pt) / (10 * pf.radius)), 3.f);
786
787 vm_vec_scale_add(&view_pos, &eye_pos, &eye_orient.vec.fvec, scalar);
788
789 view_pos -= pf.pt;
790 vm_vec_normalize(&view_pos);
791
792 vm_vector_2_matrix(&orient, &view_pos, &pf.up_vec, nullptr);
793 }
794
795 // update the poof's up vector to be perpindicular to the camera and also rotated by however much its rotating
796 vec3d poof_direction;
797 vm_vec_normalized_dir(&poof_direction, &pf.pt, &eye_pos);
798 vm_project_point_onto_plane(&pf.up_vec, &pf.up_vec, &view_pos, &vmd_zero_vector);
799 vm_vec_normalize(&pf.up_vec);
800 vm_rot_point_around_line(&pf.up_vec, &pf.up_vec, pf.rot_speed * flFrametime, &vmd_zero_vector, &view_pos);
801
802 // optimization 1 - don't draw backfacing poly's
803 if (vm_vec_dot_to_point(&eye_orient.vec.fvec, &eye_pos, &pf.pt) <= 0.0f)
804 continue;
805
806 // get the proper alpha value
807 alpha = neb2_get_alpha_2shell(pf.alpha, pf.radius, pinfo->view_dist, pf.radius/4, &pf.pt);
808
809 // optimization 2 - don't draw 0.0f or less poly's
810 // this amounts to big savings
811 if (alpha <= 0.0f)
812 continue;
813
814 // render!
815 batching_add_polygon(pinfo->bitmap, &pf.pt, &orient, pf.radius, pf.radius, alpha);
816 }
817
818 // gr_set_color_fast(&Color_bright_red);
819 // gr_printf(30, 100, "Area %.3f", total_area);
820 #ifdef NEB2_THUMBNAIL
821 extern int tbmap;
822 if (tbmap != -1) {
823 gr_set_bitmap(tbmap);
824 gr_bitmap(0, 0);
825 }
826 #endif
827 }
828
829 /*
830 //Object types
831 #define OBJ_NONE 0 //unused object
832 #define OBJ_SHIP 1 //a ship
833 #define OBJ_WEAPON 2 //a laser, missile, etc
834 #define OBJ_FIREBALL 3 //an explosion
835 #define OBJ_START 4 //a starting point marker (player start, etc)
836 #define OBJ_WAYPOINT 5 //a waypoint object, maybe only ever used by Fred
837 #define OBJ_DEBRIS 6 //a flying piece of ship debris
838 #define OBJ_CMEASURE 7 //a countermeasure, such as chaff
839 #define OBJ_GHOST 8 //so far, just a placeholder for when a player dies.
840 #define OBJ_POINT 9 //generic object type to display a point in Fred.
841 #define OBJ_SHOCKWAVE 10 // a shockwave
842 #define OBJ_WING 11 // not really a type used anywhere, but I need it for Fred.
843 #define OBJ_OBSERVER 12 // used for multiplayer observers (possibly single player later)
844 #define OBJ_ASTEROID 13 // An asteroid, you know, a big rock, like debris, sort of.
845 #define OBJ_JUMP_NODE 14 // A jump node object, used only in Fred.
846 #define OBJ_BEAM 15 // beam weapons. we have to roll them into the object system to get the benefits of the collision pairs
847 */
848 // get near and far fog values based upon object type and rendering mode
neb2_get_fog_values(float * fnear,float * ffar,object * objp)849 void neb2_get_fog_values(float *fnear, float *ffar, object *objp)
850 {
851 int type_index = -1;
852
853 //use defaults
854 *fnear = Default_fog_near;
855 *ffar = Default_fog_far;
856
857 if (objp == NULL) {
858 return;
859 }
860
861 // determine what fog index to use
862 if(objp->type == OBJ_SHIP) {
863 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
864 if((objp->instance >= 0) && (objp->instance < MAX_SHIPS)) {
865 type_index = ship_query_general_type(objp->instance);
866 if(type_index > 0) {
867 *fnear = Ship_types[type_index].fog_start_dist;
868 *ffar = Ship_types[type_index].fog_complete_dist;
869 }
870 }
871 } else if (objp->type == OBJ_FIREBALL) { //mostly here for the warp effect
872 *fnear = objp->radius*2;
873 *ffar = (objp->radius*objp->radius*200)+objp->radius*200;
874 return;
875 }
876 }
877
878 // This version of the function allows for global adjustment to fog values
neb2_get_adjusted_fog_values(float * fnear,float * ffar,float * fdensity,object * objp)879 void neb2_get_adjusted_fog_values(float *fnear, float *ffar, float *fdensity, object *objp)
880 {
881 neb2_get_fog_values(fnear, ffar, objp);
882
883 // Multiply fog distances by mission multipliers
884 *fnear *= Neb2_fog_near_mult;
885 *ffar *= Neb2_fog_far_mult;
886
887 // Avoide divide-by-zero
888 if ((*fnear - *ffar) == 0)
889 *ffar = *fnear + 1.0f;
890
891 if (fdensity != nullptr)
892 *fdensity = powf(NEB_FOG_FAR_PCT, 1 / (*ffar - *fnear));
893 }
894
895 // given a position, returns 0 - 1 the fog visibility of that position, 0 = completely obscured
896 // distance_mult will multiply the result, use for things that can be obscured but can 'shine through' the nebula more than normal
neb2_get_fog_visibility(vec3d * pos,float distance_mult)897 float neb2_get_fog_visibility(vec3d *pos, float distance_mult)
898 {
899 float pct;
900
901 // get the fog pct
902 pct = powf(nNf_density, (vm_vec_dist(&Eye_position, pos) - nNf_near) / distance_mult);
903
904 CLAMP(pct, 0.0f, 1.0f);
905
906 return pct;
907 }
908
909 // fogging stuff --------------------------------------------------------------------
910
911 // do a pre-render of the background nebula
912 #define ESIZE 32
913 ubyte tpixels[ESIZE * ESIZE * 4]; // for 32 bits
914 int last_esize = -1;
915 int this_esize = ESIZE;
916 float ex_scale, ey_scale;
917 int tbmap = -1;
918 // UnknownPlayer : Contained herein, the origins of the nebula rendering bug!
919 // I am really not entirely sure what this code achieves, but the old
920 // D3D calls were the cause of the nebula bug - they have been commented out.
921 // If you want to save some rendering time, I would suggest maybe kill this off.
922 // It doesn't use much, but it APPEARS to be fairly useless unless someone wants
923 // to enlighten me.
924 //
neb2_pre_render(camid cid)925 void neb2_pre_render(camid cid)
926 {
927 // if the mission is not a fullneb mission, skip
928 if (!(The_mission.flags[Mission::Mission_Flags::Fullneb])) {
929 return;
930 }
931
932 // bail early in lame and poly modes
933 if (Neb2_render_mode != NEB2_RENDER_POF) {
934 return;
935 }
936
937 // set the view clip
938 gr_screen.clip_width = this_esize;
939 gr_screen.clip_height = this_esize;
940 g3_start_frame(1); // Turn on zbuffering
941 g3_set_view(cid.getCamera());
942 gr_set_clip(0, 0, this_esize, this_esize);
943
944 // render the background properly
945 // hack - turn off nebula stuff
946 int neb_save = Neb2_render_mode;
947 Neb2_render_mode = NEB2_RENDER_NONE;
948
949 // draw background stuff nebula
950 stars_draw_background();
951
952 Neb2_render_mode = neb_save;
953
954 // grab the region
955 gr_get_region(0, this_esize, this_esize, (ubyte*)tpixels);
956
957 #ifdef NEB2_THUMBNAIL
958 if (tbmap == -1) {
959 tbmap = bm_create(16, this_esize, this_esize, tpixels, 0);
960 bm_lock(tbmap, 16, 0);
961 bm_unlock(tbmap);
962 }
963 #endif
964
965 // maybe do some swizzling
966
967 // end the frame
968 g3_end_frame();
969
970 gr_clear();
971
972 // if the size has changed between frames, make a new bitmap
973 if (this_esize != last_esize) {
974 last_esize = this_esize;
975
976 // recalculate ex_scale and ey_scale values for looking up color values
977 ex_scale = (float)this_esize / (float)gr_screen.max_w;
978 ey_scale = (float)this_esize / (float)gr_screen.max_h;
979 }
980 }
981
982 // fill in the position of the eye for this frame
neb2_get_eye_pos(vec3d * eye_vector)983 void neb2_get_eye_pos(vec3d *eye_vector)
984 {
985 *eye_vector = Eye_position;
986 }
987
988 // fill in the eye orient for this frame
neb2_get_eye_orient(matrix * eye_matrix)989 void neb2_get_eye_orient(matrix *eye_matrix)
990 {
991 *eye_matrix = Eye_matrix;
992 }
993
994 // nebula DCF functions ------------------------------------------------------
995 // 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
996 DCF(neb2, "list nebula console commands")
997 {
998 // dc_printf("neb2_fog <X> <float> <float> : set near and far fog planes for ship type X\n");
999 // dc_printf("where X is an integer from 1 - 11\n");
1000 // dc_printf("1 = cargo containers, 2 = fighters/bombers, 3 = cruisers\n");
1001 // dc_printf("4 = freighters, 5 = capital ships, 6 = transports, 7 = support ships\n");
1002 // dc_printf("8 = navbuoys, 9 = sentryguns, 10 = escape pods, 11 = background nebula polygons\n\n");
1003
1004 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");
1005 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");
1006 dc_printf("neb2_ff : flash fade/sec\n");
1007 dc_printf("neb2_background : rgb background color\n");
1008 dc_printf("neb2_fog_color : rgb fog color\n");
1009
1010 // dc_printf("neb2_fog_vals : display all the current settings for all above values\n");
1011 }
1012
1013 DCF(neb2_select, "Enables/disables a poof bitmap")
1014 {
1015 int bmap;
1016 bool val_b;
1017
1018 dc_stuff_int(&bmap);
1019
1020 if ( (bmap >= 0) && (bmap < (int)Poof_info.size()) ) {
1021 dc_stuff_boolean(&val_b);
1022
1023 val_b ? (Neb2_poof_flags |= (1<<bmap)) : (Neb2_poof_flags &= ~(1<<bmap));
1024 }
1025 }
1026
1027 DCF(neb2_ff, "flash fade/sec")
1028 {
1029 dc_stuff_float(&neb2_flash_fade);
1030 }
1031
1032 DCF(neb2_mode, "Switches nebula render modes")
1033 {
1034 int mode;
1035 dc_stuff_int(&mode);
1036
1037 switch (mode) {
1038 case NEB2_RENDER_NONE:
1039 Neb2_render_mode = NEB2_RENDER_NONE;
1040 break;
1041
1042 case NEB2_RENDER_POF:
1043 Neb2_render_mode = NEB2_RENDER_POF;
1044 stars_set_background_model(BACKGROUND_MODEL_FILENAME, "Eraseme3");
1045 stars_set_background_orientation();
1046 break;
1047
1048 case NEB2_RENDER_HTL:
1049 Neb2_render_mode = NEB2_RENDER_HTL;
1050 break;
1051 }
1052 }
1053
1054 DCF(neb2_background, "Sets the RGB background color (lame rendering)")
1055 {
1056 int r, g, b;
1057
1058 dc_stuff_int(&r);
1059 dc_stuff_int(&g);
1060 dc_stuff_int(&b);
1061
1062 Neb2_background_color[0] = r;
1063 Neb2_background_color[1] = g;
1064 Neb2_background_color[2] = b;
1065 }
1066
1067 DCF(neb2_fog_color, "Sets the RGB fog color (HTL)")
1068 {
1069 ubyte r, g, b;
1070
1071 dc_stuff_ubyte(&r);
1072 dc_stuff_ubyte(&g);
1073 dc_stuff_ubyte(&b);
1074
1075 Neb2_fog_color[0] = r;
1076 Neb2_fog_color[1] = g;
1077 Neb2_fog_color[2] = b;
1078 }
1079