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 <climits>
13
14 #include "freespace.h"
15 #include "cmdline/cmdline.h"
16 #include "debugconsole/console.h"
17 #include "graphics/matrix.h"
18 #include "graphics/paths/PathRenderer.h"
19 #include "hud/hud.h"
20 #include "hud/hudtarget.h"
21 #include "io/timer.h"
22 #include "lighting/lighting.h"
23 #include "math/vecmat.h"
24 #include "mission/missionparse.h"
25 #include "model/modelrender.h"
26 #include "nebula/neb.h"
27 #include "options/Option.h"
28 #include "parse/parselo.h"
29 #include "render/3d.h"
30 #include "render/batching.h"
31 #include "starfield/nebula.h"
32 #include "starfield/starfield.h"
33 #include "starfield/supernova.h"
34 #include "tracing/tracing.h"
35 #include "utils/Random.h"
36
37 #define MAX_DEBRIS_VCLIPS 4
38 #define DEBRIS_ROT_MIN 10000
39 #define DEBRIS_ROT_RANGE 8
40 #define DEBRIS_ROT_RANGE_SCALER 10000
41 #define RND_MAX_MASK 0x3fff
42 #define HALF_RND_MAX 0x2000
43
44 typedef struct {
45 vec3d pos;
46 int vclip;
47 float size;
48 } motion_debris;
49
50 const int MAX_DEBRIS = 300;
51 const int MAX_STARS = 2000;
52 const float MAX_DIST_RANGE = 80.0f;
53 const float MIN_DIST_RANGE = 14.0f;
54 const float BASE_SIZE = 0.04f;
55 const float BASE_SIZE_NEB = 0.15f;
56
57 static int Subspace_model_inner = -1;
58 static int Subspace_model_outer = -1;
59 static int Rendering_to_env = 0;
60
61 int Num_stars = 500;
62 fix starfield_timestamp = 0;
63
64 #define MAX_FLARE_COUNT 10
65 #define MAX_FLARE_BMP 6
66
67 typedef struct flare_info {
68 float pos;
69 float scale;
70 int tex_num;
71 } flare_info;
72
73 typedef struct flare_bitmap {
74 char filename[MAX_FILENAME_LEN];
75 int bitmap_id;
76 } flare_bitmap;
77
78
79 // global info (not individual instances)
80 typedef struct starfield_bitmap {
81 char filename[MAX_FILENAME_LEN]; // bitmap filename
82 char glow_filename[MAX_FILENAME_LEN]; // only for suns
83 int bitmap_id; // bitmap handle
84 int n_frames;
85 int fps;
86 int glow_bitmap; // only for suns
87 int glow_n_frames;
88 int glow_fps;
89 int xparent;
90 float r, g, b, i, spec_r, spec_g, spec_b; // only for suns
91 int glare; // only for suns
92 int flare; // Is there a lens-flare for this sun?
93 flare_info flare_infos[MAX_FLARE_COUNT]; // each flare can use a texture in flare_bmp, with different scale
94 flare_bitmap flare_bitmaps[MAX_FLARE_BMP]; // bitmaps for different lens flares (can be re-used)
95 int n_flares; // number of flares actually used
96 int n_flare_bitmaps; // number of flare bitmaps available
97 int used_this_level;
98 int preload;
99 } starfield_bitmap;
100
101 // starfield bitmap instance
102 typedef struct starfield_bitmap_instance {
103 float scale_x, scale_y; // x and y scale
104 int div_x, div_y; // # of x and y divisions
105 angles ang; // angles from FRED
106 int star_bitmap_index; // index into starfield_bitmap array
107 int n_verts;
108 vertex *verts;
109
starfield_bitmap_instancestarfield_bitmap_instance110 starfield_bitmap_instance() : scale_x(1.0f), scale_y(1.0f), div_x(1), div_y(1), star_bitmap_index(0), n_verts(0), verts(NULL) {
111 ang.p = 0.0f;
112 ang.b = 0.0f;
113 ang.h = 0.0f;
114 }
115 } starfield_bitmap_instance;
116
117 // for drawing cool stuff on the background - comes from a table
118 static SCP_vector<starfield_bitmap> Starfield_bitmaps;
119 static SCP_vector<starfield_bitmap_instance> Starfield_bitmap_instances;
120
121 // sun bitmaps and sun glow bitmaps
122 static SCP_vector<starfield_bitmap> Sun_bitmaps;
123 static SCP_vector<starfield_bitmap_instance> Suns;
124
125 // Goober5000
126 int Cur_background = -1;
127 SCP_vector<background_t> Backgrounds;
128
129 SCP_vector<int> Preload_background_indexes;
130 void stars_preload_background(int background_idx);
131
132 int last_stars_filled = 0;
133 color star_colors[8];
134 color star_aacolors[8];
135
136 typedef struct star {
137 vec3d pos;
138 vec3d last_star_pos;
139 color col;
140 } star;
141
142 typedef struct vDist {
143 int x;
144 int y;
145 } vDist;
146
147 star Stars[MAX_STARS];
148
149 motion_debris Motion_debris[MAX_DEBRIS];
150
151
152 typedef struct debris_vclip {
153 int bm;
154 int nframes;
155 char name[MAX_FILENAME_LEN];
156 } debris_vclip;
157 extern debris_vclip Debris_vclips_normal[];
158 extern debris_vclip Debris_vclips_nebula[];
159 extern debris_vclip *Debris_vclips;
160
161 //XSTR:OFF
162 debris_vclip Debris_vclips_normal[MAX_DEBRIS_VCLIPS] = { { -1, -1, "debris01" }, { -1, -1, "debris02" }, { -1, -1, "debris03" }, { -1, -1, "debris04" } };
163 debris_vclip Debris_vclips_nebula[MAX_DEBRIS_VCLIPS] = { { -1, -1, "nebdeb01" }, { -1, -1, "nebdeb02" }, { -1, -1, "nebdeb03" }, { -1, -1, "nebdeb04" } };
164 debris_vclip *Debris_vclips = Debris_vclips_normal;
165 //XSTR:ON
166
167 int stars_debris_loaded = 0; // 0 = not loaded, 1 = normal vclips, 2 = nebula vclips
168
169 // background data
170 int Stars_background_inited = 0; // if we're inited
171 int Nmodel_num = -1; // model num
172 int Nmodel_instance_num = -1; // model instance num
173 matrix Nmodel_orient = IDENTITY_MATRIX; // model orientation
174 int Nmodel_flags = DEFAULT_NMODEL_FLAGS; // model flags
175 int Nmodel_bitmap = -1; // model texture
176
177 int Num_debris_normal = 0;
178 int Num_debris_nebula = 0;
179
180 bool Dynamic_environment = false;
181 bool Motion_debris_override = false;
182
183 bool Motion_debris_enabled = true;
184
185 auto MotionDebrisOption = options::OptionBuilder<bool>("Graphics.MotionDebris", "Motion Debris",
186 "Controls whether motion debris are shown or not")
187 .category("Graphics")
188 .bind_to_once(&Motion_debris_enabled)
189 .default_val(true)
190 .level(options::ExpertLevel::Advanced)
191 .importance(67)
192 .finish();
193
194 static int Default_env_map = -1;
195 static int Mission_env_map = -1;
196 static bool Env_cubemap_drawn = false;
197
stars_release_debris_vclips(debris_vclip * vclips)198 void stars_release_debris_vclips(debris_vclip *vclips)
199 {
200 int i;
201
202 if (vclips == NULL)
203 return;
204
205 for (i = 0; i < MAX_DEBRIS_VCLIPS; i++) {
206 if ( (vclips[i].bm >= 0) && bm_release(vclips[i].bm) ) {
207 vclips[i].bm = -1;
208 vclips[i].nframes = -1;
209 }
210 }
211 }
212
stars_load_debris_vclips(debris_vclip * vclips)213 void stars_load_debris_vclips(debris_vclip *vclips)
214 {
215 int i;
216
217 if (vclips == NULL) {
218 Int3();
219 return;
220 }
221
222 for (i = 0; i < MAX_DEBRIS_VCLIPS; i++) {
223 vclips[i].bm = bm_load_animation( vclips[i].name, &vclips[i].nframes, nullptr, nullptr, nullptr, true );
224
225 if ( vclips[i].bm < 0 ) {
226 // try loading it as a single bitmap
227 vclips[i].bm = bm_load(Debris_vclips[i].name);
228 vclips[i].nframes = 1;
229
230 if (vclips[i].bm <= 0) {
231 Error( LOCATION, "Couldn't load animation/bitmap '%s'\n", vclips[i].name );
232 }
233 }
234 }
235 }
236
stars_load_debris(int fullneb)237 void stars_load_debris(int fullneb)
238 {
239 if (!Motion_debris_enabled) {
240 return;
241 }
242
243 // if we're in nebula mode
244 if ( fullneb && (stars_debris_loaded != 2) ) {
245 stars_release_debris_vclips(Debris_vclips);
246 stars_load_debris_vclips(Debris_vclips_nebula);
247 Debris_vclips = Debris_vclips_nebula;
248 stars_debris_loaded = 2;
249 } else if (stars_debris_loaded != 1) {
250 stars_release_debris_vclips(Debris_vclips);
251 stars_load_debris_vclips(Debris_vclips_normal);
252 Debris_vclips = Debris_vclips_normal;
253 stars_debris_loaded = 1;
254 }
255 }
256
257 const int MAX_PERSPECTIVE_DIVISIONS = 5;
258 const float p_phi = 10.0f, p_theta = 10.0f;
259
260 extern void stars_project_2d_onto_sphere( vec3d *pnt, float rho, float phi, float theta );
261
starfield_create_bitmap_buffer(const int si_idx)262 static void starfield_create_bitmap_buffer(const int si_idx)
263 {
264 vec3d s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
265 vec3d t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1];
266
267 vertex v[4];
268 matrix m, m_bank;
269 int idx, s_idx;
270 float ui, vi;
271 angles bank_first;
272
273 starfield_bitmap_instance *sbi = &Starfield_bitmap_instances[si_idx];
274
275 angles *a = &sbi->ang;
276 float scale_x = sbi->scale_x;
277 float scale_y = sbi->scale_y;
278 int div_x = sbi->div_x;
279 int div_y = sbi->div_y;
280
281 // cap division values
282 // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x;
283 div_x = 1;
284 div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y;
285
286 if (sbi->verts != NULL) {
287 delete [] sbi->verts;
288 }
289
290 sbi->verts = new(std::nothrow) vertex[div_x * div_y * 6];
291
292 if (sbi->verts == NULL) {
293 sbi->star_bitmap_index = -1;
294 return;
295 }
296
297 sbi->n_verts = div_x * div_y * 6;
298
299 // texture increment values
300 ui = 1.0f / (float)div_x;
301 vi = 1.0f / (float)div_y;
302
303 // adjust for aspect ratio
304 //scale_x *= (gr_screen.clip_aspect + 0.55f); // fudge factor
305 //scale_x *= (gr_screen.clip_aspect + (0.7333333f/gr_screen.clip_aspect)); // fudge factor
306 scale_x *= 1.883333f; //fudge factor
307
308 float s_phi = 0.5f + (((p_phi * scale_x) / 360.0f) / 2.0f);
309 float s_theta = (((p_theta * scale_y) / 360.0f) / 2.0f);
310 float d_phi = -(((p_phi * scale_x) / 360.0f) / (float)(div_x));
311 float d_theta = -(((p_theta * scale_y) / 360.0f) / (float)(div_y));
312
313 // bank matrix
314 bank_first.p = 0.0f;
315 bank_first.b = a->b;
316 bank_first.h = 0.0f;
317 vm_angles_2_matrix(&m_bank, &bank_first);
318
319 // convert angles to matrix
320 angles a_temp = *a;
321 a_temp.b = 0.0f;
322 vm_angles_2_matrix(&m, &a_temp);
323
324 // generate the bitmap points
325 for(idx=0; idx<=div_x; idx++) {
326 for(s_idx=0; s_idx<=div_y; s_idx++) {
327 // get world spherical coords
328 stars_project_2d_onto_sphere(&s_points[idx][s_idx], 1000.0f, s_phi + ((float)idx*d_phi), s_theta + ((float)s_idx*d_theta));
329
330 // bank the bitmap first
331 vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank);
332
333 // rotate on the sphere
334 vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m);
335 }
336 }
337
338 memset(v, 0, sizeof(vertex) * 4);
339
340 int j = 0;
341
342 vertex *verts = sbi->verts;
343
344 for (idx = 0; idx < div_x; idx++) {
345 for (s_idx = 0; s_idx < div_y; s_idx++) {
346 // stuff texture coords
347 v[0].texture_position.u = ui * float(idx);
348 v[0].texture_position.v = vi * float(s_idx);
349
350 v[1].texture_position.u = ui * float(idx+1);
351 v[1].texture_position.v = vi * float(s_idx);
352
353 v[2].texture_position.u = ui * float(idx+1);
354 v[2].texture_position.v = vi * float(s_idx+1);
355
356 v[3].texture_position.u = ui * float(idx);
357 v[3].texture_position.v = vi * float(s_idx+1);
358
359 g3_transfer_vertex(&v[0], &s_points[idx][s_idx]);
360 g3_transfer_vertex(&v[1], &s_points[idx+1][s_idx]);
361 g3_transfer_vertex(&v[2], &s_points[idx+1][s_idx+1]);
362 g3_transfer_vertex(&v[3], &s_points[idx][s_idx+1]);
363
364 // poly 1
365 verts[j++] = v[0];
366 verts[j++] = v[1];
367 verts[j++] = v[2];
368 // poly 2
369 verts[j++] = v[0];
370 verts[j++] = v[2];
371 verts[j++] = v[3];
372 }
373 }
374
375 Assert( j == sbi->n_verts );
376 }
377
378 // take the Starfield_bitmap_instances[] and make all the vertex buffers that you'll need to draw it
starfield_generate_bitmap_buffers()379 static void starfield_generate_bitmap_buffers()
380 {
381 int idx;
382
383 int sb_instances = (int)Starfield_bitmap_instances.size();
384
385 for (idx = 0; idx < sb_instances; idx++) {
386 if (Starfield_bitmap_instances[idx].star_bitmap_index < 0) {
387 continue;
388 }
389
390 starfield_create_bitmap_buffer(idx);
391 }
392 }
393
starfield_bitmap_entry_init(starfield_bitmap * sbm)394 static void starfield_bitmap_entry_init(starfield_bitmap *sbm)
395 {
396 int i;
397
398 Assert( sbm != NULL );
399
400 memset( sbm, 0, sizeof(starfield_bitmap) );
401
402 sbm->bitmap_id = -1;
403 sbm->glow_bitmap = -1;
404 sbm->glow_n_frames = 1;
405
406 for (i = 0; i < MAX_FLARE_BMP; i++) {
407 sbm->flare_bitmaps[i].bitmap_id = -1;
408 }
409
410 for (i = 0; i < MAX_FLARE_COUNT; i++) {
411 sbm->flare_infos[i].tex_num = -1;
412 }
413 }
414
415 #define CHECK_END() { \
416 if (in_check) { \
417 required_string("#end"); \
418 in_check = false; \
419 run_count = 0; \
420 } \
421 }
422
parse_startbl(const char * filename)423 void parse_startbl(const char *filename)
424 {
425 char name[MAX_FILENAME_LEN], tempf[16];
426 starfield_bitmap sbm;
427 int idx;
428 bool in_check = false;
429 int rc = -1;
430 int run_count = 0;
431
432 try
433 {
434 read_file_text(filename, CF_TYPE_TABLES);
435 reset_parse();
436
437 // freaky! ;)
438 while (!check_for_eof()) {
439 while ((rc = optional_string_either("$Bitmap:", "$BitmapX:")) != -1) {
440 in_check = true;
441
442 starfield_bitmap_entry_init(&sbm);
443
444 stuff_string(sbm.filename, F_NAME, MAX_FILENAME_LEN);
445 sbm.xparent = rc; // 0 == intensity alpha bitmap, 1 == green xparency bitmap
446
447 if ((idx = stars_find_bitmap(sbm.filename)) >= 0) {
448 if (sbm.xparent == Starfield_bitmaps[idx].xparent) {
449 if (!Parsing_modular_table)
450 Warning(LOCATION, "Starfield bitmap '%s' listed more than once!! Only using the first entry!", sbm.filename);
451 }
452 else {
453 Warning(LOCATION, "Starfield bitmap '%s' already listed as a %s bitmap!! Only using the xparent version!",
454 sbm.filename, (rc) ? "xparent" : "non-xparent");
455 }
456 }
457 else {
458 Starfield_bitmaps.push_back(sbm);
459 }
460 }
461
462 CHECK_END();
463
464 while (optional_string("$Sun:")) {
465 in_check = true;
466
467 starfield_bitmap_entry_init(&sbm);
468
469 stuff_string(sbm.filename, F_NAME, MAX_FILENAME_LEN);
470
471 // associated glow
472 required_string("$Sunglow:");
473 stuff_string(sbm.glow_filename, F_NAME, MAX_FILENAME_LEN);
474
475 // associated lighting values
476 required_string("$SunRGBI:");
477 stuff_float(&sbm.r);
478 stuff_float(&sbm.g);
479 stuff_float(&sbm.b);
480 stuff_float(&sbm.i);
481
482 if (optional_string("$SunSpecularRGB:")) {
483 stuff_float(&sbm.spec_r);
484 stuff_float(&sbm.spec_g);
485 stuff_float(&sbm.spec_b);
486 }
487 else {
488 sbm.spec_r = sbm.r;
489 sbm.spec_g = sbm.g;
490 sbm.spec_b = sbm.b;
491 }
492
493 // lens flare stuff
494 if (optional_string("$Flare:")) {
495 sbm.flare = 1;
496
497 required_string("+FlareCount:");
498 stuff_int(&sbm.n_flares);
499
500 // if there's a flare, it has to have at least one texture
501 required_string("$FlareTexture1:");
502 stuff_string(sbm.flare_bitmaps[0].filename, F_NAME, MAX_FILENAME_LEN);
503
504 sbm.n_flare_bitmaps = 1;
505
506 for (idx = 1; idx < MAX_FLARE_BMP; idx++) {
507 // allow 9999 textures (theoretically speaking, that is)
508 sprintf(tempf, "$FlareTexture%d:", idx + 1);
509
510 if (optional_string(tempf)) {
511 sbm.n_flare_bitmaps++;
512 stuff_string(sbm.flare_bitmaps[idx].filename, F_NAME, MAX_FILENAME_LEN);
513 }
514 // else break; //don't allow flaretexture1 and then 3, etc.
515 }
516
517 required_string("$FlareGlow1:");
518
519 required_string("+FlareTexture:");
520 stuff_int(&sbm.flare_infos[0].tex_num);
521
522 required_string("+FlarePos:");
523 stuff_float(&sbm.flare_infos[0].pos);
524
525 required_string("+FlareScale:");
526 stuff_float(&sbm.flare_infos[0].scale);
527
528 sbm.n_flares = 1;
529
530 for (idx = 1; idx < MAX_FLARE_COUNT; idx++) {
531 // allow a lot of glows
532 sprintf(tempf, "$FlareGlow%d:", idx + 1);
533
534 if (optional_string(tempf)) {
535 sbm.n_flares++;
536
537 required_string("+FlareTexture:");
538 stuff_int(&sbm.flare_infos[idx].tex_num);
539
540 required_string("+FlarePos:");
541 stuff_float(&sbm.flare_infos[idx].pos);
542
543 required_string("+FlareScale:");
544 stuff_float(&sbm.flare_infos[idx].scale);
545 }
546 // else break; //don't allow "flare 1" and then "flare 3"
547 }
548 }
549
550 sbm.glare = !optional_string("$NoGlare:");
551
552 sbm.xparent = 1;
553
554 if ((idx = stars_find_sun(sbm.filename)) >= 0) {
555 if (Parsing_modular_table)
556 Sun_bitmaps[idx] = sbm;
557 else
558 Warning(LOCATION, "Sun bitmap '%s' listed more than once!! Only using the first entry!", sbm.filename);
559 }
560 else {
561 Sun_bitmaps.push_back(sbm);
562 }
563 }
564
565 CHECK_END();
566
567 // normal debris pieces
568 while (optional_string("$Debris:")) {
569 in_check = true;
570
571 stuff_string(name, F_NAME, MAX_FILENAME_LEN);
572
573 if (Num_debris_normal < MAX_DEBRIS_VCLIPS) {
574 strcpy_s(Debris_vclips_normal[Num_debris_normal++].name, name);
575 }
576 else {
577 Warning(LOCATION, "Could not load normal motion debris '%s'; maximum of %d exceeded.", name, MAX_DEBRIS_VCLIPS);
578 }
579 }
580
581 CHECK_END();
582
583 // nebula debris pieces
584 while (optional_string("$DebrisNeb:")) {
585 in_check = true;
586
587 stuff_string(name, F_NAME, MAX_FILENAME_LEN);
588
589 if (Num_debris_nebula < MAX_DEBRIS_VCLIPS) {
590 strcpy_s(Debris_vclips_nebula[Num_debris_nebula++].name, name);
591 }
592 else {
593 Warning(LOCATION, "Could not load nebula motion debris '%s'; maximum of %d exceeded.", name, MAX_DEBRIS_VCLIPS);
594 }
595 }
596
597 CHECK_END();
598
599 // since it's possible for some idiot to have a tbl screwed up enough
600 // that this ends up in an endless loop, give an opportunity to advance
601 // through the file no matter what, because even the retail tbl has an
602 // extra "#end" line in it.
603 if (optional_string("#end") || (run_count++ > 5)) {
604 run_count = 0;
605 advance_to_eoln(NULL);
606 }
607 }
608 }
609 catch (const parse::ParseException& e)
610 {
611 mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
612 return;
613 }
614 }
615
stars_load_all_bitmaps()616 void stars_load_all_bitmaps()
617 {
618 int idx, i;
619 starfield_bitmap *sb = NULL;
620 static int Star_bitmaps_loaded = 0;
621
622 if (Star_bitmaps_loaded)
623 return;
624
625 // pre-load all starfield bitmaps. ONLY SHOULD DO THIS FOR FRED!!
626 // this can get nasty when a lot of bitmaps are in use so spare it for
627 // the normal game and only do this in FRED
628 int mprintf_count = 0;
629 for (idx = 0; idx < (int)Starfield_bitmaps.size(); idx++) {
630 sb = &Starfield_bitmaps[idx];
631
632 if (sb->bitmap_id < 0) {
633 sb->bitmap_id = bm_load(sb->filename);
634
635 // maybe didn't load a static image so try for an animated one
636 if (sb->bitmap_id < 0) {
637 sb->bitmap_id = bm_load_animation(sb->filename, &sb->n_frames, &sb->fps, nullptr, nullptr, true);
638
639 if (sb->bitmap_id < 0) {
640 mprintf(("Unable to load starfield bitmap: '%s'!\n", sb->filename));
641 mprintf_count++;
642 }
643 }
644 }
645 }
646 if (mprintf_count > 0) {
647 Warning(LOCATION, "Unable to load %d starfield bitmap(s)!\n", mprintf_count);
648 }
649
650 for (idx = 0; idx < (int)Sun_bitmaps.size(); idx++) {
651 sb = &Sun_bitmaps[idx];
652
653 // normal bitmap
654 if (sb->bitmap_id < 0) {
655 sb->bitmap_id = bm_load(sb->filename);
656
657 // maybe didn't load a static image so try for an animated one
658 if (sb->bitmap_id < 0) {
659 sb->bitmap_id = bm_load_animation(sb->filename, &sb->n_frames, &sb->fps, nullptr, nullptr, true);
660
661 if (sb->bitmap_id < 0) {
662 Warning(LOCATION, "Unable to load sun bitmap: '%s'!\n", sb->filename);
663 }
664 }
665 }
666
667 // glow bitmap
668 if (sb->glow_bitmap < 0) {
669 sb->glow_bitmap = bm_load(sb->glow_filename);
670
671 // maybe didn't load a static image so try for an animated one
672 if (sb->glow_bitmap < 0) {
673 sb->glow_bitmap = bm_load_animation(sb->glow_filename, &sb->glow_n_frames, &sb->glow_fps, nullptr, nullptr, true);
674
675 if (sb->glow_bitmap < 0) {
676 Warning(LOCATION, "Unable to load sun glow bitmap: '%s'!\n", sb->glow_filename);
677 }
678 }
679 }
680
681 if (sb->flare) {
682 for (i = 0; i < MAX_FLARE_BMP; i++) {
683 if ( !strlen(sb->flare_bitmaps[i].filename) )
684 continue;
685
686 if (sb->flare_bitmaps[i].bitmap_id < 0) {
687 sb->flare_bitmaps[i].bitmap_id = bm_load(sb->flare_bitmaps[i].filename);
688
689 if (sb->flare_bitmaps[i].bitmap_id < 0) {
690 Warning(LOCATION, "Unable to load sun flare bitmap: '%s'!\n", sb->flare_bitmaps[i].filename);
691 continue;
692 }
693 }
694 }
695 }
696 }
697
698 Star_bitmaps_loaded = 1;
699 }
700
stars_clear_instances()701 void stars_clear_instances()
702 {
703 for (uint i = 0; i < Starfield_bitmap_instances.size(); i++) {
704 delete [] Starfield_bitmap_instances[i].verts;
705 Starfield_bitmap_instances[i].verts = NULL;
706 }
707
708 Starfield_bitmap_instances.clear();
709 Suns.clear();
710 }
711
712 // call on game startup
stars_init()713 void stars_init()
714 {
715 // starfield bitmaps
716 Num_debris_normal = 0;
717 Num_debris_nebula = 0;
718
719 // parse stars.tbl
720 parse_startbl("stars.tbl");
721
722 parse_modular_table("*-str.tbm", parse_startbl);
723
724 if (Cmdline_env) {
725 ENVMAP = Default_env_map = bm_load("cubemap");
726 }
727 }
728
729 // call only from game_shutdown()!!
stars_close()730 void stars_close()
731 {
732 stars_clear_instances();
733
734 // any other code goes here
735 }
736
737 // called before mission parse so we can clear out all of the old stuff
stars_pre_level_init(bool clear_backgrounds)738 void stars_pre_level_init(bool clear_backgrounds)
739 {
740 uint idx, i;
741 starfield_bitmap *sb = NULL;
742
743 // we used to clear all the array entries, but now we can just wipe the vector
744 if (clear_backgrounds)
745 Backgrounds.clear();
746
747 stars_clear_instances();
748
749 stars_set_background_model(NULL, NULL);
750 stars_set_background_orientation();
751
752 // mark all starfield and sun bitmaps as unused for this mission and release any current bitmaps
753 // NOTE: that because of how we have to load the bitmaps it's important to release all of
754 // them first thing rather than after we have marked and loaded only what's needed
755 // NOTE2: there is a reason that we don't check for release before setting the handle to -1 so
756 // be aware that this is NOT a bug. also, bmpman should NEVER return 0 as a valid handle!
757 if ( !Fred_running ) {
758 for (idx = 0; idx < Starfield_bitmaps.size(); idx++) {
759 sb = &Starfield_bitmaps[idx];
760
761 if (sb->bitmap_id > 0) {
762 bm_release(sb->bitmap_id);
763 sb->bitmap_id = -1;
764 }
765
766 sb->used_this_level = 0;
767 sb->preload = 0;
768 }
769
770 for (idx = 0; idx < Sun_bitmaps.size(); idx++) {
771 sb = &Sun_bitmaps[idx];
772
773 if (sb->bitmap_id > 0) {
774 bm_release(sb->bitmap_id);
775 sb->bitmap_id = -1;
776 }
777
778 if (sb->glow_bitmap > 0) {
779 bm_release(sb->glow_bitmap);
780 sb->glow_bitmap = -1;
781 }
782
783 for (i = 0; i < MAX_FLARE_BMP; i++) {
784 if (sb->flare_bitmaps[i].bitmap_id > 0) {
785 bm_release(sb->flare_bitmaps[i].bitmap_id);
786 sb->flare_bitmaps[i].bitmap_id = -1;
787 }
788 }
789
790 sb->used_this_level = 0;
791 sb->preload = 0;
792 }
793 }
794
795 // also clear the preload indexes
796 Preload_background_indexes.clear();
797
798 Dynamic_environment = false;
799 Motion_debris_override = false;
800
801 Env_cubemap_drawn = false;
802 }
803
804 // setup the render target ready for this mission's environment map
environment_map_gen()805 static void environment_map_gen()
806 {
807 const int size = 512;
808 int gen_flags = (BMP_FLAG_RENDER_TARGET_STATIC | BMP_FLAG_CUBEMAP | BMP_FLAG_RENDER_TARGET_MIPMAP);
809
810 if ( !Cmdline_env ) {
811 return;
812 }
813
814 if (gr_screen.envmap_render_target >= 0) {
815 if ( !bm_release(gr_screen.envmap_render_target, 1) ) {
816 Warning(LOCATION, "Unable to release environment map render target.");
817 }
818
819 gr_screen.envmap_render_target = -1;
820 }
821
822 if ( Dynamic_environment || (The_mission.flags[Mission::Mission_Flags::Subspace]) ) {
823 Dynamic_environment = true;
824 gen_flags &= ~BMP_FLAG_RENDER_TARGET_STATIC;
825 gen_flags |= BMP_FLAG_RENDER_TARGET_DYNAMIC;
826 }
827 // bail if we are going to be static, and have an envmap specified already
828 else if ( strlen(The_mission.envmap_name) ) {
829 // Load the mission map so we can use it later
830 Mission_env_map = bm_load(The_mission.envmap_name);
831 return;
832 }
833
834 gr_screen.envmap_render_target = bm_make_render_target(size, size, gen_flags);
835 }
836
837 // call this in game_post_level_init() so we know whether we're running in full nebula mode or not
stars_post_level_init()838 void stars_post_level_init()
839 {
840 int i;
841 vec3d v;
842 float dist, dist_max;
843 ubyte red,green,blue,alpha;
844
845 // in FRED, make sure we always have at least one background
846 if (Fred_running)
847 {
848 if (Backgrounds.empty())
849 Backgrounds.emplace_back();
850 }
851 // in FSO, see if we have any backgrounds to preload
852 // (since the backgrounds aren't parsed at the time we parse the sexps)
853 else
854 {
855 for (int idx : Preload_background_indexes)
856 stars_preload_background(idx);
857 }
858
859 stars_set_background_model(The_mission.skybox_model, NULL, The_mission.skybox_flags);
860 stars_set_background_orientation(&The_mission.skybox_orientation);
861
862 stars_load_debris( ((The_mission.flags[Mission::Mission_Flags::Fullneb]) || Nebula_sexp_used) );
863
864 // following code randomly distributes star points within a sphere volume, which
865 // avoids there being denser areas along the edges and in corners that we had in the
866 // old rectangular distribution scheme.
867 dist_max = (float) (HALF_RND_MAX * HALF_RND_MAX);
868 for (i=0; i<MAX_STARS; i++) {
869 dist = dist_max;
870 while (dist >= dist_max) {
871 v.xyz.x = (float) ((Random::next() & RND_MAX_MASK) - HALF_RND_MAX);
872 v.xyz.y = (float) ((Random::next() & RND_MAX_MASK) - HALF_RND_MAX);
873 v.xyz.z = (float) ((Random::next() & RND_MAX_MASK) - HALF_RND_MAX);
874
875 dist = v.xyz.x * v.xyz.x + v.xyz.y * v.xyz.y + v.xyz.z * v.xyz.z;
876 }
877 vm_vec_copy_normalize(&Stars[i].pos, &v);
878
879 {
880 red = (ubyte)Random::next(192, 255);
881 green = (ubyte)Random::next(192, 255);
882 blue = (ubyte)Random::next(192, 255);
883 alpha = (ubyte)Random::next(24, 216);
884
885 gr_init_alphacolor(&Stars[i].col, red, green, blue, alpha, AC_TYPE_BLEND);
886 }
887
888 }
889
890 memset( &Motion_debris, 0, sizeof(motion_debris) * MAX_DEBRIS );
891
892
893 for (i=0; i<8; i++ ) {
894 ubyte intensity = (ubyte)((i + 1) * 24);
895 gr_init_alphacolor(&star_aacolors[i], 255, 255, 255, intensity, AC_TYPE_BLEND );
896 gr_init_color(&star_colors[i], intensity, intensity, intensity );
897 }
898
899 last_stars_filled = 0;
900
901 // if we have no sun instances, create one
902 if ( !Suns.size() ) {
903 if ( !strlen(Sun_bitmaps[0].filename) ) {
904 mprintf(("Trying to add default sun but no default exists!!\n"));
905 } else {
906 mprintf(("Adding default sun.\n"));
907
908 starfield_bitmap_instance def_sun;
909
910 // stuff some values
911 def_sun.ang.h = fl_radians(60.0f);
912
913 Suns.push_back(def_sun);
914 }
915 }
916
917 // FRED doesn't do normal page_in stuff so we need to load up the bitmaps here instead
918 if (Fred_running) {
919 stars_load_all_bitmaps();
920
921 // see whether we are missing any suns or bitmaps
922 for (i = 0; i < (int)Backgrounds.size(); ++i) {
923 SCP_string failed_suns;
924 for (auto &sun : Backgrounds[i].suns) {
925 if (stars_find_sun(sun.filename) < 0) {
926 failed_suns += sun.filename;
927 failed_suns += "\n";
928 }
929 }
930
931 SCP_string failed_bitmaps;
932 for (auto &bitmap : Backgrounds[i].bitmaps) {
933 if (stars_find_bitmap(bitmap.filename) < 0) {
934 failed_bitmaps += bitmap.filename;
935 failed_bitmaps += "\n";
936 }
937 }
938
939 if (!failed_suns.empty()) {
940 Warning(LOCATION, "In Background %d, failed to load the following suns:\n%s", (i + 1), failed_suns.c_str());
941 }
942 if (!failed_bitmaps.empty()) {
943 Warning(LOCATION, "In Background %d, failed to load the following bitmaps:\n%s", (i + 1), failed_bitmaps.c_str());
944 }
945 }
946 }
947
948 starfield_generate_bitmap_buffers();
949
950 environment_map_gen();
951 }
952
stars_level_close()953 void stars_level_close() {
954 if (gr_screen.envmap_render_target >= 0) {
955 if ( bm_release(gr_screen.envmap_render_target, 1) ) {
956 gr_screen.envmap_render_target = -1;
957 }
958 }
959
960 if (Mission_env_map >= 0) {
961 bm_release(Mission_env_map);
962 Mission_env_map = -1;
963 }
964
965 ENVMAP = Default_env_map;
966 }
967
968
969 extern object * Player_obj;
970
971 #define STAR_AMOUNT_DEFAULT 0.75f
972 #define STAR_DIM_DEFAULT 7800.0f
973 #define STAR_CAP_DEFAULT 75.0f
974 #define STAR_MAX_LENGTH_DEFAULT 0.04f // 312
975
976 float Star_amount = STAR_AMOUNT_DEFAULT;
977 float Star_dim = STAR_DIM_DEFAULT;
978 float Star_cap = STAR_CAP_DEFAULT;
979 float Star_max_length = STAR_MAX_LENGTH_DEFAULT;
980
981 #define STAR_FLAG_TAIL (1<<0) // Draw a tail when moving
982 #define STAR_FLAG_DIM (1<<1) // Dim as you move
983 #define STAR_FLAG_ANTIALIAS (1<<2) // Draw the star using antialiased lines
984 #define STAR_FLAG_DEFAULT (STAR_FLAG_TAIL | STAR_FLAG_DIM)
985
986 uint Star_flags = STAR_FLAG_DEFAULT;
987
988 //XSTR:OFF
989 DCF(stars,"Set parameters for starfield")
990 {
991 SCP_string arg;
992 float val_f;
993 int val_i;
994
995 if (dc_optional_string_either("help", "--help")) {
996 dc_printf( "Usage: stars keyword\nWhere keyword can be in the following forms:\n" );
997 dc_printf( "stars default Resets stars to all default values\n" );
998 dc_printf( "stars num X Sets number of stars to X. Between 0 and %d.\n", MAX_STARS );
999 dc_printf( "stars tail X Where X is the percent of 'tail' between 0 and 1.0\n" );
1000 dc_printf( "stars dim X Where X is the amount stars dim between 0 and 255.0\n" );
1001 dc_printf( "stars cap X Where X is the cap of dimming between 0 and 255.\n" );
1002 dc_printf( "stars len X Where X is the cap of length.\n" );
1003 dc_printf( "stars m0 Macro0. Old 'pixel type' crappy stars. flags=none\n" );
1004 dc_printf( "stars m1 Macro1. (default) tail=.75, dim=20.0, cap=75.0, flags=dim,tail\n" );
1005 dc_printf( "stars m2 Macro2. tail=.75, dim=20.0, cap=75.0, flags=dim,tail,aa\n" );
1006 dc_printf( "stars flag X Toggles flag X, where X is tail or dim or aa (aa=antialias)\n" );
1007 dc_printf( "\nHINT: set cap to 0 to get dim rate and tail down, then use\n" );
1008 dc_printf( "cap to keep the lines from going away when moving too fast.\n" );
1009 dc_printf( "\nUse '? stars' to see current values.\n" );
1010 return; // don't print status if help is printed. Too messy.
1011 }
1012
1013 if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1014 dc_printf( "Num_stars: %d\n", Num_stars );
1015 dc_printf( "Tail: %.2f\n", Star_amount );
1016 dc_printf( "Dim : %.2f\n", Star_dim );
1017 dc_printf( "Cap : %.2f\n", Star_cap );
1018 dc_printf( "Max length: %.2f\n", Star_max_length );
1019 dc_printf( "Flags:\n" );
1020 dc_printf( " Tail : %s\n", (Star_flags&STAR_FLAG_TAIL?"On":"Off") );
1021 dc_printf( " Dim : %s\n", (Star_flags&STAR_FLAG_DIM?"On":"Off") );
1022 dc_printf( " Antialias: %s\n", (Star_flags&STAR_FLAG_ANTIALIAS?"On":"Off") );
1023 dc_printf( "\nTHESE AREN'T SAVED TO DISK, SO IF YOU TWEAK\n" );
1024 dc_printf( "THESE AND LIKE THEM, WRITE THEM DOWN!!\n" );
1025 return;
1026 }
1027
1028 dc_stuff_string_white(arg);
1029 // "stars default" is handled by "stars m1"
1030 if (arg == "num") {
1031 dc_stuff_int(&val_i);
1032
1033 CLAMP(val_i, 0, MAX_STARS);
1034 Num_stars = val_i;
1035
1036 dc_printf("Num_stars set to %i\n", Num_stars);
1037
1038 } else if (arg == "tail") {
1039 dc_stuff_float(&val_f);
1040 CLAMP(val_f, 0.0, 1.0);
1041 Star_amount = val_f;
1042
1043 dc_printf("Star_amount set to %f\n", Star_amount);
1044
1045 } else if (arg == "dim") {
1046 dc_stuff_float(&val_f);
1047
1048 if (val_f > 0.0f ) {
1049 Star_dim = val_f;
1050 dc_printf("Star_dim set to %f\n", Star_dim);
1051
1052 } else {
1053 dc_printf("Error: Star_dim value must be non-negative\n");
1054 }
1055
1056 } else if (arg == "cap") {
1057 dc_stuff_float(&val_f);
1058 CLAMP(val_f, 0.0, 255);
1059 Star_cap = val_f;
1060
1061 dc_printf("Star_cap set to %f\n", Star_cap);
1062
1063 } else if (arg == "len") {
1064 dc_stuff_float(&Star_max_length);
1065
1066 dc_printf("Star_max_length set to %f\n", Star_max_length);
1067
1068 } else if (arg == "m0") {
1069 Star_amount = 0.0f;
1070 Star_dim = 0.0f;
1071 Star_cap = 0.0f;
1072 Star_flags = 0;
1073 Star_max_length = STAR_MAX_LENGTH_DEFAULT;
1074
1075 dc_printf("Starfield set: Old 'pixel type' crappy stars. flags=none\n");
1076
1077 } else if ((arg == "m1") || (arg == "default")) {
1078 Star_amount = STAR_AMOUNT_DEFAULT;
1079 Star_dim = STAR_DIM_DEFAULT;
1080 Star_cap = STAR_CAP_DEFAULT;
1081 Star_flags = STAR_FLAG_DEFAULT;
1082 Star_max_length = STAR_MAX_LENGTH_DEFAULT;
1083
1084 dc_printf("Starfield set: (default) tail=.75, dim=20.0, cap=75.0, flags=dim,tail\n");
1085
1086 } else if (arg == "m2") {
1087 Star_amount = 0.75f;
1088 Star_dim = 20.0f;
1089 Star_cap = 75.0f;
1090 Star_flags = STAR_FLAG_TAIL|STAR_FLAG_DIM|STAR_FLAG_ANTIALIAS;
1091 Star_max_length = STAR_MAX_LENGTH_DEFAULT;
1092
1093 dc_printf("Starfield set: tail=.75, dim=20.0, cap=75.0, flags=dim,tail,aa\n");
1094
1095 } else if (arg == "flag") {
1096 dc_stuff_string_white(arg);
1097 if (arg == "tail") {
1098 Star_flags ^= STAR_FLAG_TAIL;
1099 } else if (arg == "dim" ) {
1100 Star_flags ^= STAR_FLAG_DIM;
1101 } else if (arg == "aa" ) {
1102 Star_flags ^= STAR_FLAG_ANTIALIAS;
1103 } else {
1104 dc_printf("Error: unknown flag argument '%s'\n", arg.c_str());
1105 }
1106
1107 } else {
1108 dc_printf("Error: Unknown argument '%s'", arg.c_str());
1109 }
1110 }
1111 //XSTR:ON
1112
1113 bool refresh_motion_debris = true; // If set to true, then regenerate the positions of motion debris
1114 // Call this if camera "cuts" or moves long distances
1115 // so blur effect doesn't draw lines all over the screen.
stars_camera_cut()1116 void stars_camera_cut()
1117 {
1118 last_stars_filled = 0;
1119 refresh_motion_debris = true;
1120 }
1121
1122 //#define TIME_STAR_CODE // enable to time star code
1123
1124 extern int Sun_drew;
1125
1126 // get the world coords of the sun pos on the unit sphere.
stars_get_sun_pos(int sun_n,vec3d * pos)1127 void stars_get_sun_pos(int sun_n, vec3d *pos)
1128 {
1129 vec3d temp;
1130 matrix rot;
1131
1132 // sanity
1133 Assert( sun_n < (int)Suns.size() );
1134
1135 if ( (sun_n >= (int)Suns.size()) || (sun_n < 0) ) {
1136 return;
1137 }
1138
1139 // rotate the sun properly
1140 temp = vmd_zero_vector;
1141 temp.xyz.z = 1.0f;
1142
1143 // rotation matrix
1144 vm_angles_2_matrix(&rot, &Suns[sun_n].ang);
1145 vm_vec_rotate(pos, &temp, &rot);
1146 }
1147
1148 // draw sun
stars_draw_sun(int show_sun)1149 void stars_draw_sun(int show_sun)
1150 {
1151 GR_DEBUG_SCOPE("Draw Suns");
1152 TRACE_SCOPE(tracing::DrawSuns);
1153
1154 int idx;
1155 vec3d sun_pos;
1156 vec3d sun_dir;
1157 vertex sun_vex;
1158 starfield_bitmap *bm;
1159 float local_scale;
1160
1161 // should we even be here?
1162 if (!show_sun)
1163 return;
1164
1165 // no suns drew yet
1166 Sun_drew = 0;
1167
1168 // draw all suns
1169 int num_suns = (int)Suns.size();
1170
1171 for (idx = 0; idx < num_suns; idx++) {
1172 // get the instance
1173 if (Suns[idx].star_bitmap_index < 0)
1174 return;
1175
1176 bm = &Sun_bitmaps[Suns[idx].star_bitmap_index];
1177
1178 // if no bitmap then bail...
1179 if (bm->bitmap_id < 0)
1180 continue;
1181
1182 memset( &sun_vex, 0, sizeof(vertex) );
1183
1184 // get sun pos
1185 sun_pos = vmd_zero_vector;
1186 sun_pos.xyz.y = 1.0f;
1187 stars_get_sun_pos(idx, &sun_pos);
1188
1189 // get the direction
1190 sun_dir = sun_pos;
1191 vm_vec_normalize(&sun_dir);
1192
1193 // add the light source corresponding to the sun, except when rendering to an envmap
1194 if ( !Rendering_to_env )
1195 light_add_directional(&sun_dir, bm->i, bm->r, bm->g, bm->b, bm->spec_r, bm->spec_g, bm->spec_b, true);
1196
1197 // if supernova
1198 if ( supernova_active() && (idx == 0) )
1199 local_scale = 1.0f + (SUPERNOVA_SUN_SCALE * supernova_pct_complete());
1200 else
1201 local_scale = 1.0f;
1202
1203 // draw the sun itself, keep track of how many we drew
1204 int bitmap_id = -1;
1205 if (bm->fps) {
1206 //gr_set_bitmap(bm->bitmap_id + ((timestamp() / (int)(bm->fps)) % bm->n_frames), GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.999f);
1207 bitmap_id = bm->bitmap_id + ((timestamp() / (int)(bm->fps)) % bm->n_frames);
1208 } else {
1209 //gr_set_bitmap(bm->bitmap_id, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.999f);
1210 bitmap_id = bm->bitmap_id;
1211 }
1212
1213 g3_rotate_faraway_vertex(&sun_vex, &sun_pos);
1214
1215 if ( sun_vex.codes & (CC_BEHIND|CC_OFF_USER) ) {
1216 continue;
1217 }
1218
1219 if ( !(sun_vex.flags & PF_PROJECTED) ) {
1220 g3_project_vertex(&sun_vex);
1221 }
1222
1223 if ( sun_vex.flags & PF_OVERFLOW ) {
1224 continue;
1225 }
1226
1227 material mat_params;
1228 material_set_unlit(&mat_params, bitmap_id, 0.999f, true, false);
1229 g3_render_rect_screen_aligned_2d(&mat_params, &sun_vex, 0, 0.05f * Suns[idx].scale_x * local_scale);
1230 Sun_drew++;
1231
1232 // if ( !g3_draw_bitmap(&sun_vex, 0, 0.05f * Suns[idx].scale_x * local_scale, TMAP_FLAG_TEXTURED) )
1233 // Sun_drew++;
1234 }
1235 }
1236
1237 // draw a star's lens-flare
stars_draw_lens_flare(vertex * sun_vex,int sun_n)1238 void stars_draw_lens_flare(vertex *sun_vex, int sun_n)
1239 {
1240 starfield_bitmap *bm;
1241 int i,j;
1242 float dx,dy;
1243 vertex flare_vex = *sun_vex; //copy over to flare_vex to get all sorts of properties
1244
1245 Assert( sun_n < (int)Suns.size() );
1246
1247 if ( (sun_n >= (int)Suns.size()) || (sun_n < 0) ) {
1248 return;
1249 }
1250
1251 // get the instance
1252 if (Suns[sun_n].star_bitmap_index < 0) {
1253 return;
1254 } else {
1255 bm = &Sun_bitmaps[Suns[sun_n].star_bitmap_index];
1256 }
1257
1258 if (!bm->flare)
1259 return;
1260
1261 /* (dx,dy) is a 2d vector equal to two times the vector from the sun's
1262 position to the center fo the screen meaning it is the vector to the
1263 opposite position on the screen. */
1264 dx = 2.0f*(i2fl(gr_screen.clip_right-gr_screen.clip_left)*0.5f - sun_vex->screen.xyw.x);
1265 dy = 2.0f*(i2fl(gr_screen.clip_bottom-gr_screen.clip_top)*0.5f - sun_vex->screen.xyw.y);
1266
1267 for (j = 0; j < bm->n_flare_bitmaps; j++)
1268 {
1269 // if no bitmap then bail...
1270 if (bm->flare_bitmaps[j].bitmap_id < 0)
1271 continue;
1272
1273 //gr_set_bitmap(bm->flare_bitmaps[j].bitmap_id, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.999f);
1274
1275 for (i = 0; i < bm->n_flares; i++) {
1276 // draw sorted by texture, to minimize texture changes. not the most efficient way, but better than non-sorted
1277 if (bm->flare_infos[i].tex_num == j) {
1278 flare_vex.screen.xyw.x = sun_vex->screen.xyw.x + dx * bm->flare_infos[i].pos;
1279 flare_vex.screen.xyw.y = sun_vex->screen.xyw.y + dy * bm->flare_infos[i].pos;
1280 //g3_draw_bitmap(&flare_vex, 0, 0.05f * bm->flare_infos[i].scale, TMAP_FLAG_TEXTURED);
1281 material mat_params;
1282 material_set_unlit(&mat_params, bm->flare_bitmaps[j].bitmap_id, 0.999f, true, false);
1283 g3_render_rect_screen_aligned_2d(&mat_params, &flare_vex, 0, 0.05f * bm->flare_infos[i].scale);
1284 }
1285 }
1286 }
1287 }
1288
1289 // draw the corresponding glow for sun_n
stars_draw_sun_glow(int sun_n)1290 void stars_draw_sun_glow(int sun_n)
1291 {
1292 starfield_bitmap *bm;
1293 vec3d sun_pos, sun_dir;
1294 vertex sun_vex;
1295 float local_scale;
1296
1297 // sanity
1298 //WMC - Dunno why this is getting hit...
1299 //Assert( sun_n < (int)Suns.size() );
1300
1301 if ( (sun_n >= (int)Suns.size()) || (sun_n < 0) ) {
1302 return;
1303 }
1304
1305 // get the instance
1306 if (Suns[sun_n].star_bitmap_index < 0)
1307 return;
1308
1309 bm = &Sun_bitmaps[Suns[sun_n].star_bitmap_index];
1310
1311 // if no bitmap then bail...
1312 if (bm->glow_bitmap < 0)
1313 return;
1314
1315 memset( &sun_vex, 0, sizeof(vertex) );
1316
1317 // get sun pos
1318 sun_pos = vmd_zero_vector;
1319 sun_pos.xyz.y = 1.0f;
1320 stars_get_sun_pos(sun_n, &sun_pos);
1321
1322 // get the direction
1323 sun_dir = sun_pos;
1324 vm_vec_normalize(&sun_dir);
1325
1326 // if supernova
1327 if ( supernova_active() && (sun_n == 0) )
1328 local_scale = 1.0f + (SUPERNOVA_SUN_SCALE * supernova_pct_complete());
1329 else
1330 local_scale = 1.0f;
1331
1332 // draw the sun itself, keep track of how many we drew
1333 int bitmap_id = -1;
1334 if (bm->glow_fps) {
1335 //gr_set_bitmap(bm->glow_bitmap + ((timestamp() / (int)(bm->glow_fps)) % bm->glow_n_frames), GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.5f);
1336 bitmap_id = bm->glow_bitmap + ((timestamp() / (int)(bm->glow_fps)) % bm->glow_n_frames);
1337 } else {
1338 //gr_set_bitmap(bm->glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 0.5f);
1339 bitmap_id = bm->glow_bitmap;
1340 }
1341
1342 g3_rotate_faraway_vertex(&sun_vex, &sun_pos);
1343 //int zbuff = gr_zbuffer_set(GR_ZBUFF_NONE);
1344 //g3_draw_bitmap(&sun_vex, 0, 0.10f * Suns[sun_n].scale_x * local_scale, TMAP_FLAG_TEXTURED);
1345 material mat_params;
1346 material_set_unlit(&mat_params, bitmap_id, 0.5f, true, false);
1347 g3_render_rect_screen_aligned_2d(&mat_params, &sun_vex, 0, 0.10f * Suns[sun_n].scale_x * local_scale);
1348
1349 if (bm->flare) {
1350 vec3d light_dir;
1351 vec3d local_light_dir;
1352 light_get_global_dir(&light_dir, sun_n);
1353 vm_vec_rotate(&local_light_dir, &light_dir, &Eye_matrix);
1354 float dot=vm_vec_dot( &light_dir, &Eye_matrix.vec.fvec );
1355 if (dot > 0.7f) // Only render the flares if the sun is reasonably near the center of the screen
1356 stars_draw_lens_flare(&sun_vex, sun_n);
1357 }
1358
1359 //gr_zbuffer_set(zbuff);
1360 }
1361
stars_draw_bitmaps(int show_bitmaps)1362 void stars_draw_bitmaps(int show_bitmaps)
1363 {
1364 GR_DEBUG_SCOPE("Draw Bitmaps");
1365 TRACE_SCOPE(tracing::DrawBitmaps);
1366
1367 int idx;
1368 int star_index;
1369
1370 // should we even be here?
1371 if ( !show_bitmaps )
1372 return;
1373
1374 // if we're in the nebula, don't render any backgrounds
1375 if (The_mission.flags[Mission::Mission_Flags::Fullneb])
1376 return;
1377
1378 // detail settings
1379 if ( !Detail.planets_suns )
1380 return;
1381
1382 gr_start_instance_matrix(&Eye_position, &vmd_identity_matrix);
1383
1384 int sb_instances = (int)Starfield_bitmap_instances.size();
1385
1386 for (idx = 0; idx < sb_instances; idx++) {
1387 // lookup the info index
1388 star_index = Starfield_bitmap_instances[idx].star_bitmap_index;
1389
1390 if (star_index < 0) {
1391 continue;
1392 }
1393
1394 // if no bitmap then bail...
1395 if (Starfield_bitmaps[star_index].bitmap_id < 0) {
1396 continue;
1397 }
1398
1399 int bitmap_id;
1400 bool blending = false;
1401 float alpha = 1.0f;
1402
1403 if (Starfield_bitmaps[star_index].xparent) {
1404 if (Starfield_bitmaps[star_index].fps) {
1405 bitmap_id = Starfield_bitmaps[star_index].bitmap_id + ((timestamp() / (int)(Starfield_bitmaps[star_index].fps)) % Starfield_bitmaps[star_index].n_frames);
1406 } else {
1407 bitmap_id = Starfield_bitmaps[star_index].bitmap_id;
1408 }
1409 } else {
1410 if (Starfield_bitmaps[star_index].fps) {
1411 bitmap_id = Starfield_bitmaps[star_index].bitmap_id + ((timestamp() / (int)(Starfield_bitmaps[star_index].fps)) % Starfield_bitmaps[star_index].n_frames);
1412 blending = true;
1413 alpha = 0.9999f;
1414 } else {
1415 bitmap_id = Starfield_bitmaps[star_index].bitmap_id;
1416 blending = true;
1417 alpha = 0.9999f;
1418 }
1419 }
1420
1421 material material_params;
1422 material_set_unlit(&material_params, bitmap_id, alpha, blending, false);
1423 g3_render_primitives_textured(&material_params, Starfield_bitmap_instances[idx].verts, Starfield_bitmap_instances[idx].n_verts, PRIM_TYPE_TRIS, false);
1424 }
1425
1426 gr_end_instance_matrix();
1427 }
1428
1429 extern int Interp_subspace;
1430 extern float Interp_subspace_offset_u;
1431 extern float Interp_subspace_offset_u;
1432 extern float Interp_subspace_offset_v;
1433
1434 float subspace_offset_u = 0.0f;
1435 float subspace_offset_u_inner = 0.0f;
1436 float subspace_offset_v = 0.0f;
1437
1438 float subspace_u_speed = 0.07f; // how fast u changes
1439 float subspace_v_speed = 0.05f; // how fast v changes
1440
1441 int Subspace_glow_bitmap = -1;
1442
1443 float Subspace_glow_frame = 0.0f;
1444 float Subspace_glow_rate = 1.0f;
1445
1446
1447 //XSTR:OFF
1448 DCF(subspace_set,"Set parameters for subspace effect")
1449 {
1450 SCP_string arg;
1451 float value;
1452
1453 if (dc_optional_string_either("help", "--help")) {
1454 dc_printf( "Usage: subspace [--status] <axis> <speed>\n");
1455 dc_printf("[--status] -- Displays the current speeds for both axes\n");
1456 dc_printf("<axis> -- May be either 'u' or 'v', and corresponds to the texture axis\n");
1457 dc_printf("<speed> -- is the speed along the axis that the texture is moved\n");
1458 return;
1459 }
1460
1461 if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1462 dc_printf( "u: %.2f\n", subspace_u_speed );
1463 dc_printf( "v: %.2f\n", subspace_v_speed );
1464 return;
1465 }
1466
1467 dc_stuff_string_white(arg);
1468 if (arg == "u") {
1469 dc_stuff_float(&value);
1470
1471 if ( value < 0.0f ) {
1472 dc_printf("Error: speed must be non-negative");
1473 return;
1474 }
1475 subspace_u_speed = value;
1476
1477 } else if (arg == "v") {
1478 dc_stuff_float(&value);
1479
1480 if (value < 0.0f) {
1481 dc_printf("Error: speed must be non-negative");
1482 return;
1483 }
1484 subspace_v_speed = value;
1485
1486 } else {
1487 dc_printf("Error: Unknown axis '%s'", arg.c_str());
1488 }
1489 }
1490 //XSTR:ON
1491
subspace_render()1492 void subspace_render()
1493 {
1494 int framenum = 0;
1495
1496 if ( Subspace_model_inner == -1 ) {
1497 Subspace_model_inner = model_load( "subspace_small.pof", 0, NULL );
1498 Assert(Subspace_model_inner >= 0);
1499 }
1500
1501 if ( Subspace_model_outer == -1 ) {
1502 Subspace_model_outer = model_load( "subspace_big.pof", 0, NULL );
1503 Assert(Subspace_model_outer >= 0);
1504 }
1505
1506 if ( Subspace_glow_bitmap == -1 ) {
1507 Subspace_glow_bitmap = bm_load( NOX("SunGlow01"));
1508 Assert(Subspace_glow_bitmap >= 0);
1509 }
1510
1511 if ( !Rendering_to_env ) {
1512 Subspace_glow_frame += flFrametime * 1.0f;
1513
1514 float total_time = i2fl(NOISE_NUM_FRAMES) / 15.0f;
1515
1516 // Sanity checks
1517 if ( Subspace_glow_frame < 0.0f )
1518 Subspace_glow_frame = 0.0f;
1519 if ( Subspace_glow_frame > 100.0f )
1520 Subspace_glow_frame = 0.0f;
1521
1522 while ( Subspace_glow_frame > total_time ) {
1523 Subspace_glow_frame -= total_time;
1524 }
1525
1526 framenum = fl2i( (Subspace_glow_frame*NOISE_NUM_FRAMES) / total_time );
1527
1528 if ( framenum < 0 )
1529 framenum = 0;
1530 if ( framenum >= NOISE_NUM_FRAMES )
1531 framenum = NOISE_NUM_FRAMES-1;
1532
1533 subspace_offset_u += flFrametime*subspace_u_speed;
1534 if (subspace_offset_u > 1.0f ) {
1535 subspace_offset_u -= 1.0f;
1536 }
1537
1538 subspace_offset_u_inner += flFrametime*subspace_u_speed*3.0f;
1539 if (subspace_offset_u > 1.0f ) {
1540 subspace_offset_u -= 1.0f;
1541 }
1542
1543 subspace_offset_v += flFrametime*subspace_v_speed;
1544 if (subspace_offset_v > 1.0f ) {
1545 subspace_offset_v -= 1.0f;
1546 }
1547 }
1548
1549
1550 matrix tmp;
1551 angles angs = vmd_zero_angles;
1552
1553 angs.b = subspace_offset_v * PI2;
1554
1555 vm_angles_2_matrix(&tmp,&angs);
1556
1557 int saved_gr_zbuffering = gr_zbuffer_get();
1558
1559 gr_zbuffer_set(GR_ZBUFF_NONE);
1560
1561 int render_flags = MR_NO_LIGHTING | MR_ALL_XPARENT;
1562
1563 Interp_subspace = 1;
1564 Interp_subspace_offset_u = 1.0f - subspace_offset_u;
1565 Interp_subspace_offset_v = 0.0f;
1566
1567 model_render_params render_info;
1568 render_info.set_alpha(1.0f);
1569 render_info.set_flags(render_flags);
1570
1571 gr_set_texture_panning(Interp_subspace_offset_v, Interp_subspace_offset_u, true);
1572
1573 model_render_immediate( &render_info, Subspace_model_outer, &tmp, &Eye_position); //MR_NO_CORRECT|MR_SHOW_OUTLINE
1574
1575 gr_set_texture_panning(0, 0, false);
1576
1577 Interp_subspace = 1;
1578 Interp_subspace_offset_u = 1.0f - subspace_offset_u_inner;
1579 Interp_subspace_offset_v = 0.0f;
1580
1581 angs.b = -subspace_offset_v * PI2;
1582
1583 vm_angles_2_matrix(&tmp,&angs);
1584
1585 render_info.set_color(255, 255, 255);
1586 render_info.set_alpha(1.0f);
1587 render_info.set_flags(render_flags);
1588
1589 gr_set_texture_panning(Interp_subspace_offset_v, Interp_subspace_offset_u, true);
1590
1591 model_render_immediate( &render_info, Subspace_model_inner, &tmp, &Eye_position ); //MR_NO_CORRECT|MR_SHOW_OUTLINE
1592
1593 gr_set_texture_panning(0, 0, false);
1594
1595 //Render subspace glows here and not as thrusters - Valathil
1596 vec3d glow_pos;
1597 vertex glow_vex;
1598
1599 glow_pos.xyz.x = 0.0f;
1600 glow_pos.xyz.y = 0.0f;
1601 glow_pos.xyz.z = 100.0f;
1602
1603 //gr_set_bitmap(Subspace_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f);
1604 material mat_params;
1605 material_set_unlit(&mat_params, Subspace_glow_bitmap, 1.0f, true, false);
1606
1607 g3_rotate_faraway_vertex(&glow_vex, &glow_pos);
1608 //g3_draw_bitmap(&glow_vex, 0, 17.0f + 0.5f * Noise[framenum], TMAP_FLAG_TEXTURED);
1609 g3_render_rect_screen_aligned_2d(&mat_params, &glow_vex, 0, 17.0f + 0.5f * Noise[framenum]);
1610
1611 glow_pos.xyz.z = -100.0f;
1612
1613 g3_rotate_faraway_vertex(&glow_vex, &glow_pos);
1614 //g3_draw_bitmap(&glow_vex, 0, 17.0f + 0.5f * Noise[framenum], TMAP_FLAG_TEXTURED);
1615 g3_render_rect_screen_aligned_2d(&mat_params, &glow_vex, 0, 17.0f + 0.5f * Noise[framenum]);
1616
1617 Interp_subspace = 0;
1618 gr_zbuffer_set(saved_gr_zbuffering);
1619 }
1620
stars_draw_stars()1621 void stars_draw_stars()
1622 {
1623 GR_DEBUG_SCOPE("Draw Starfield");
1624 TRACE_SCOPE(tracing::DrawStarfield);
1625
1626 int i;
1627 star *sp;
1628 float dist = 0.0f;
1629 float ratio;
1630 vDist vDst;
1631 vertex p1, p2;
1632 int can_draw = 1;
1633
1634 if ( !last_stars_filled ) {
1635 for (i = 0; i < Num_stars; i++) {
1636 g3_rotate_faraway_vertex(&p2, &Stars[i].pos);
1637 Stars[i].last_star_pos = p2.world;
1638 }
1639 }
1640
1641 int tmp_num_stars = 0;
1642
1643 tmp_num_stars = (Detail.num_stars * Num_stars) / MAX_DETAIL_LEVEL;
1644 CLAMP(tmp_num_stars, 0, Num_stars);
1645
1646 auto path = graphics::paths::PathRenderer::instance();
1647
1648 path->saveState();
1649 path->resetState();
1650
1651 path->beginFrame();
1652
1653 for (i = 0; i < tmp_num_stars; i++) {
1654 sp = &Stars[i];
1655
1656 can_draw = 1;
1657 memset(&p1, 0, sizeof(vertex));
1658 memset(&p2, 0, sizeof(vertex));
1659
1660 // This makes a star look "proper" by not translating the
1661 // point around the viewer's eye before rotation. In other
1662 // words, when the ship translates, the stars do not change.
1663
1664 g3_rotate_faraway_vertex(&p2, &sp->pos);
1665 if ( p2.codes ) {
1666 can_draw = 0;
1667 } else {
1668 g3_project_vertex(&p2);
1669 if ( p2.flags & PF_OVERFLOW ) {
1670 can_draw = 0;
1671 }
1672 }
1673
1674 if ( can_draw && (Star_flags & (STAR_FLAG_TAIL|STAR_FLAG_DIM)) ) {
1675 dist = vm_vec_dist_quick( &sp->last_star_pos, &p2.world );
1676
1677 if ( dist > Star_max_length ) {
1678 ratio = Star_max_length / dist;
1679 dist = Star_max_length;
1680 } else {
1681 ratio = 1.0f;
1682 }
1683
1684 ratio *= Star_amount;
1685
1686 vm_vec_sub(&p1.world, &sp->last_star_pos, &p2.world);
1687 vm_vec_scale(&p1.world, ratio);
1688 vm_vec_add2(&p1.world, &p2.world);
1689
1690 p1.flags = 0; // not projected
1691 g3_code_vertex( &p1 );
1692
1693 if ( p1.codes ) {
1694 can_draw = 0;
1695 } else {
1696 g3_project_vertex(&p1);
1697 if ( p1.flags & PF_OVERFLOW ) {
1698 can_draw = 0;
1699 }
1700 }
1701 }
1702
1703 sp->last_star_pos = p2.world;
1704
1705 if ( !can_draw )
1706 continue;
1707
1708 vDst.x = fl2i(p1.screen.xyw.x) - fl2i(p2.screen.xyw.x);
1709 vDst.y = fl2i(p1.screen.xyw.y) - fl2i(p2.screen.xyw.y);
1710
1711 if ( ((vDst.x * vDst.x) + (vDst.y * vDst.y)) <= 4 ) {
1712 p1.screen.xyw.x = p2.screen.xyw.x + 1.0f;
1713 p1.screen.xyw.y = p2.screen.xyw.y;
1714 }
1715 path->beginPath();
1716
1717 path->moveTo(p1.screen.xyw.x, p1.screen.xyw.y);
1718 path->lineTo(p2.screen.xyw.x, p2.screen.xyw.y);
1719
1720 path->setStrokeColor(&sp->col);
1721 path->stroke();
1722 }
1723 path->endFrame();
1724
1725 path->restoreState();
1726 }
1727
stars_draw_motion_debris()1728 void stars_draw_motion_debris()
1729 {
1730 GR_DEBUG_SCOPE("Draw motion debris");
1731 TRACE_SCOPE(tracing::DrawMotionDebris);
1732
1733 if (Motion_debris_override)
1734 return;
1735
1736 if (!Motion_debris_enabled) {
1737 return;
1738 }
1739
1740 for (motion_debris &mdebris : Motion_debris) {
1741 float vdist = vm_vec_dist(&mdebris.pos, &Eye_position);
1742
1743 if ((vdist < MIN_DIST_RANGE) || (vdist > MAX_DIST_RANGE)) {
1744 // if we just had a camera "cut" and should refresh the debris then generate in the sphere, else just on its surface
1745 vm_vec_random_in_sphere(&mdebris.pos, &Eye_position, MAX_DIST_RANGE, !refresh_motion_debris);
1746 vdist = vm_vec_dist(&mdebris.pos, &Eye_position);
1747
1748 mdebris.vclip = Random::next(MAX_DEBRIS_VCLIPS); //rand()
1749
1750 // if we're in full neb mode
1751 const float size_multiplier = i2fl(Random::next(4));
1752 if((The_mission.flags[Mission::Mission_Flags::Fullneb]) && (Neb2_render_mode != NEB2_RENDER_NONE)) {
1753 mdebris.size = size_multiplier * BASE_SIZE_NEB;
1754 } else {
1755 mdebris.size = size_multiplier * BASE_SIZE;
1756 }
1757 }
1758
1759 vertex pnt;
1760 g3_rotate_vertex(&pnt, &mdebris.pos);
1761
1762 if (pnt.codes == 0) {
1763 int frame = Missiontime / (DEBRIS_ROT_MIN + (1 % DEBRIS_ROT_RANGE) * DEBRIS_ROT_RANGE_SCALER);
1764 frame %= Debris_vclips[mdebris.vclip].nframes;
1765
1766 float alpha;
1767
1768 if ( (The_mission.flags[Mission::Mission_Flags::Fullneb]) && (Neb2_render_mode != NEB2_RENDER_NONE) ) {
1769 alpha = 0.3f;
1770 } else {
1771 alpha = 1.0f;
1772 }
1773
1774 // scale alpha from 0 at max range to full at 60% range
1775 alpha *= (vdist - MAX_DIST_RANGE) / -(MAX_DIST_RANGE * 0.6f);
1776
1777 g3_transfer_vertex(&pnt, &mdebris.pos);
1778
1779 batching_add_bitmap(Debris_vclips[mdebris.vclip].bm + frame, &pnt, 0, mdebris.size, alpha);
1780 }
1781 }
1782
1783 if (refresh_motion_debris)
1784 refresh_motion_debris = false;
1785 }
1786
stars_draw(int show_stars,int show_suns,int,int show_subspace,int env,bool in_mission)1787 void stars_draw(int show_stars, int show_suns, int /*show_nebulas*/, int show_subspace, int env, bool in_mission)
1788 {
1789 GR_DEBUG_SCOPE("Draw Stars");
1790 TRACE_SCOPE(tracing::DrawStars);
1791
1792 int gr_zbuffering_save = gr_zbuffer_get();
1793 gr_zbuffer_set(GR_ZBUFF_NONE);
1794
1795 Rendering_to_env = env;
1796
1797 if (show_subspace)
1798 subspace_render();
1799
1800 if (Num_stars >= MAX_STARS)
1801 Num_stars = MAX_STARS;
1802
1803 #ifdef TIME_STAR_CODE
1804 fix xt1, xt2;
1805 xt1 = timer_get_fixed_seconds();
1806 #endif
1807
1808 // draw background stuff
1809 if ( show_stars ) {
1810 // semi-hack, do we don't fog the background
1811 int neb_save = Neb2_render_mode;
1812 Neb2_render_mode = NEB2_RENDER_NONE;
1813 stars_draw_background();
1814 Neb2_render_mode = neb_save;
1815 }
1816 else if ( !show_subspace ) // don't render the background pof when rendering subspace
1817 {
1818 stars_draw_background();
1819 }
1820
1821 if ( !env && show_stars && (Nmodel_num < 0) && (Game_detail_flags & DETAIL_FLAG_STARS) && !(The_mission.flags[Mission::Mission_Flags::Fullneb]) && (supernova_active() < 3) ) {
1822 stars_draw_stars();
1823 }
1824
1825 last_stars_filled = 1;
1826
1827 #ifdef TIME_STAR_CODE
1828 xt2 = timer_get_fixed_seconds();
1829 mprintf(( "Stars: %d\n", xt2-xt1 ));
1830 #endif
1831
1832 if ( !Rendering_to_env && (Game_detail_flags & DETAIL_FLAG_MOTION) && (!Fred_running) && (supernova_active() < 3) && in_mission) {
1833 stars_draw_motion_debris();
1834 }
1835
1836 //if we're not drawing them, quit here
1837 if (show_suns) {
1838 stars_draw_sun( show_suns );
1839 stars_draw_bitmaps( show_suns );
1840 }
1841
1842 gr_zbuffer_set( gr_zbuffering_save );
1843 Rendering_to_env = 0;
1844 }
1845
stars_preload_background(const char * token)1846 void stars_preload_background(const char *token)
1847 {
1848 if (!token)
1849 return;
1850
1851 // we can only preload raw numbers, but that's probably sufficient
1852 if (!can_construe_as_integer(token))
1853 return;
1854
1855 int background_idx = atoi(token);
1856
1857 // human/computer offset
1858 background_idx--;
1859
1860 // store it for now
1861 Preload_background_indexes.push_back(background_idx);
1862 }
1863
stars_preload_background(int background_idx)1864 void stars_preload_background(int background_idx)
1865 {
1866 // range check
1867 if (background_idx < 0 || background_idx >= (int)Backgrounds.size())
1868 return;
1869
1870 // preload all the stuff for this background
1871 for (auto &bgsun : Backgrounds[background_idx].suns)
1872 stars_preload_sun_bitmap(bgsun.filename);
1873 for (auto &bitmap : Backgrounds[background_idx].bitmaps)
1874 stars_preload_background_bitmap(bitmap.filename);
1875 }
1876
stars_preload_sun_bitmap(const char * fname)1877 void stars_preload_sun_bitmap(const char *fname)
1878 {
1879 int idx;
1880
1881 if (fname == NULL)
1882 return;
1883
1884 idx = stars_find_sun(fname);
1885
1886 if (idx == -1) {
1887 return;
1888 }
1889
1890 Sun_bitmaps[idx].preload = 1;
1891 }
1892
stars_preload_background_bitmap(const char * fname)1893 void stars_preload_background_bitmap(const char *fname)
1894 {
1895 int idx;
1896
1897 if (fname == NULL)
1898 return;
1899
1900 idx = stars_find_bitmap(fname);
1901
1902 if (idx == -1) {
1903 return;
1904 }
1905
1906 Starfield_bitmaps[idx].preload = 1;
1907 }
1908
stars_page_in()1909 void stars_page_in()
1910 {
1911 int idx, i;
1912 starfield_bitmap_instance *sbi;
1913 starfield_bitmap *sb;
1914
1915 // Initialize the subspace stuff
1916
1917 if ( Game_subspace_effect ) {
1918 Subspace_model_inner = model_load( "subspace_small.pof", 0, NULL );
1919 Assert(Subspace_model_inner >= 0);
1920 Subspace_model_outer = model_load( "subspace_big.pof", 0, NULL );
1921 Assert(Subspace_model_outer >= 0);
1922
1923 polymodel *pm;
1924
1925 pm = model_get(Subspace_model_inner);
1926
1927 nprintf(( "Paging", "Paging in textures for subspace effect.\n" ));
1928
1929 for (idx = 0; idx < pm->n_textures; idx++) {
1930 pm->maps[idx].PageIn();
1931 }
1932
1933 pm = model_get(Subspace_model_outer);
1934
1935 nprintf(( "Paging", "Paging in textures for subspace effect.\n" ));
1936
1937 for (idx = 0; idx < pm->n_textures; idx++) {
1938 pm->maps[idx].PageIn();
1939 }
1940
1941 if (Subspace_glow_bitmap < 0) {
1942 Subspace_glow_bitmap = bm_load( NOX("SunGlow01"));
1943 }
1944
1945 bm_page_in_xparent_texture(Subspace_glow_bitmap);
1946 } else {
1947 Subspace_model_inner = -1;
1948 Subspace_model_outer = -1;
1949
1950 if (Subspace_glow_bitmap > 0) {
1951 bm_release(Subspace_glow_bitmap);
1952 Subspace_glow_bitmap = -1;
1953 }
1954 }
1955
1956 // extra SEXP related checks to preload anything that might get used from there
1957 for (idx = 0; idx < (int)Starfield_bitmaps.size(); idx++) {
1958 sb = &Starfield_bitmaps[idx];
1959
1960 if (sb->used_this_level)
1961 continue;
1962
1963 if (sb->preload) {
1964 if (sb->bitmap_id < 0) {
1965 sb->bitmap_id = bm_load(sb->filename);
1966
1967 // maybe didn't load a static image so try for an animated one
1968 if (sb->bitmap_id < 0) {
1969 sb->bitmap_id = bm_load_animation(sb->filename, &sb->n_frames, &sb->fps, nullptr, nullptr, true);
1970
1971 if (sb->bitmap_id < 0) {
1972 Warning(LOCATION, "Unable to load starfield bitmap: '%s'!\n", sb->filename);
1973 }
1974 }
1975 }
1976
1977 // this happens whether it loaded properly or not, no harm should come from it
1978 if (sb->xparent) {
1979 bm_page_in_xparent_texture(sb->bitmap_id);
1980 } else {
1981 bm_page_in_texture(sb->bitmap_id);
1982 }
1983
1984 sb->used_this_level++;
1985 }
1986 }
1987
1988 for (idx = 0; idx < (int)Sun_bitmaps.size(); idx++) {
1989 sb = &Sun_bitmaps[idx];
1990
1991 if (sb->used_this_level)
1992 continue;
1993
1994 if (sb->preload) {
1995 // normal bitmap
1996 if (sb->bitmap_id < 0) {
1997 sb->bitmap_id = bm_load(sb->filename);
1998
1999 // maybe didn't load a static image so try for an animated one
2000 if (sb->bitmap_id < 0) {
2001 sb->bitmap_id = bm_load_animation(sb->filename, &sb->n_frames, &sb->fps, nullptr, nullptr, true);
2002
2003 if (sb->bitmap_id < 0) {
2004 Warning(LOCATION, "Unable to load sun bitmap: '%s'!\n", sb->filename);
2005 }
2006 }
2007 }
2008
2009 // glow bitmap
2010 if (sb->glow_bitmap < 0) {
2011 sb->glow_bitmap = bm_load(sb->glow_filename);
2012
2013 // maybe didn't load a static image so try for an animated one
2014 if (sb->glow_bitmap < 0) {
2015 sb->glow_bitmap = bm_load_animation(sb->glow_filename, &sb->glow_n_frames, &sb->glow_fps, nullptr, nullptr, true);
2016
2017 if (sb->glow_bitmap < 0) {
2018 Warning(LOCATION, "Unable to load sun glow bitmap: '%s'!\n", sb->glow_filename);
2019 }
2020 }
2021 }
2022
2023 if (sb->flare) {
2024 for (i = 0; i < MAX_FLARE_BMP; i++) {
2025 if ( !strlen(sb->flare_bitmaps[i].filename) )
2026 continue;
2027
2028 if (sb->flare_bitmaps[i].bitmap_id < 0) {
2029 sb->flare_bitmaps[i].bitmap_id = bm_load(sb->flare_bitmaps[i].filename);
2030
2031 if (sb->flare_bitmaps[i].bitmap_id < 0) {
2032 Warning(LOCATION, "Unable to load sun flare bitmap: '%s'!\n", sb->flare_bitmaps[i].filename);
2033 continue;
2034 }
2035 }
2036
2037 bm_page_in_texture(sb->flare_bitmaps[i].bitmap_id);
2038 }
2039 }
2040
2041 bm_page_in_texture(sb->bitmap_id);
2042 bm_page_in_texture(sb->glow_bitmap);
2043
2044 sb->used_this_level++;
2045 }
2046 }
2047
2048 // load and page in needed starfield bitmaps
2049 for (idx = 0; idx < (int)Starfield_bitmap_instances.size(); idx++) {
2050 sbi = &Starfield_bitmap_instances[idx];
2051
2052 if (sbi->star_bitmap_index < 0)
2053 continue;
2054
2055 sb = &Starfield_bitmaps[sbi->star_bitmap_index];
2056
2057 if (sb->used_this_level)
2058 continue;
2059
2060 if (sb->bitmap_id < 0 ) {
2061 sb->bitmap_id = bm_load(sb->filename);
2062
2063 // maybe didn't load a static image so try for an animated one
2064 if (sb->bitmap_id < 0) {
2065 sb->bitmap_id = bm_load_animation(sb->filename, &sb->n_frames, &sb->fps, nullptr, nullptr, true);
2066
2067 if (sb->bitmap_id < 0) {
2068 Warning(LOCATION, "Unable to load starfield bitmap: '%s'!\n", sb->filename);
2069 }
2070 }
2071 }
2072
2073 // this happens whether it loaded properly or not, no harm should come from it
2074 if (sb->xparent) {
2075 bm_page_in_xparent_texture(sb->bitmap_id);
2076 } else {
2077 bm_page_in_texture(sb->bitmap_id);
2078 }
2079
2080 sb->used_this_level++;
2081 }
2082
2083 // now for sun bitmaps and glows
2084 for (idx = 0; idx < (int)Suns.size(); idx++) {
2085 sbi = &Suns[idx];
2086
2087 if (sbi->star_bitmap_index < 0)
2088 continue;
2089
2090 sb = &Sun_bitmaps[sbi->star_bitmap_index];
2091
2092 if (sb->used_this_level)
2093 continue;
2094
2095 // normal bitmap
2096 if (sb->bitmap_id < 0) {
2097 sb->bitmap_id = bm_load(sb->filename);
2098
2099 // maybe didn't load a static image so try for an animated one
2100 if (sb->bitmap_id < 0) {
2101 sb->bitmap_id = bm_load_animation(sb->filename, &sb->n_frames, &sb->fps, nullptr, nullptr, true);
2102
2103 if (sb->bitmap_id < 0) {
2104 Warning(LOCATION, "Unable to load sun bitmap: '%s'!\n", sb->filename);
2105 }
2106 }
2107 }
2108
2109 // glow bitmap
2110 if (sb->glow_bitmap < 0) {
2111 sb->glow_bitmap = bm_load(sb->glow_filename);
2112
2113 // maybe didn't load a static image so try for an animated one
2114 if (sb->glow_bitmap < 0) {
2115 sb->glow_bitmap = bm_load_animation(sb->glow_filename, &sb->glow_n_frames, &sb->glow_fps, nullptr, nullptr, true);
2116
2117 if (sb->glow_bitmap < 0) {
2118 Warning(LOCATION, "Unable to load sun glow bitmap: '%s'!\n", sb->glow_filename);
2119 }
2120 }
2121 }
2122
2123 if (sb->flare) {
2124 for (i = 0; i < MAX_FLARE_BMP; i++) {
2125 if ( !strlen(sb->flare_bitmaps[i].filename) )
2126 continue;
2127
2128 if (sb->flare_bitmaps[i].bitmap_id < 0) {
2129 sb->flare_bitmaps[i].bitmap_id = bm_load(sb->flare_bitmaps[i].filename);
2130
2131 if (sb->flare_bitmaps[i].bitmap_id < 0) {
2132 Warning(LOCATION, "Unable to load sun flare bitmap: '%s'!\n", sb->flare_bitmaps[i].filename);
2133 continue;
2134 }
2135 }
2136
2137 bm_page_in_texture(sb->flare_bitmaps[i].bitmap_id);
2138 }
2139 }
2140
2141 bm_page_in_texture(sb->bitmap_id);
2142 bm_page_in_texture(sb->glow_bitmap);
2143
2144 sb->used_this_level++;
2145 }
2146
2147
2148 if (!Motion_debris_enabled)
2149 return;
2150
2151 for (idx = 0; idx < MAX_DEBRIS_VCLIPS; idx++) {
2152 bm_page_in_xparent_texture(Debris_vclips[idx].bm, Debris_vclips[idx].nframes);
2153 }
2154 }
2155
2156 // background nebula models and planets
stars_draw_background()2157 void stars_draw_background()
2158 {
2159 GR_DEBUG_SCOPE("Draw Background");
2160 TRACE_SCOPE(tracing::DrawBackground);
2161
2162 if (Nmodel_num < 0)
2163 return;
2164
2165 model_render_params render_info;
2166
2167 if (Nmodel_bitmap >= 0) {
2168 render_info.set_forced_bitmap(Nmodel_bitmap);
2169 }
2170
2171 // draw the model at the player's eye with no z-buffering
2172 render_info.set_alpha(1.0f);
2173 render_info.set_flags(Nmodel_flags | MR_SKYBOX);
2174
2175 model_render_immediate(&render_info, Nmodel_num, &Nmodel_orient, &Eye_position, MODEL_RENDER_ALL, false);
2176 }
2177
2178 // call this to set a specific model as the background model
stars_set_background_model(const char * model_name,const char * texture_name,int flags)2179 void stars_set_background_model(const char *model_name, const char *texture_name, int flags)
2180 {
2181 if (Nmodel_bitmap >= 0) {
2182 bm_unload(Nmodel_bitmap);
2183 Nmodel_bitmap = -1;
2184 }
2185
2186 if (Nmodel_num >= 0) {
2187 model_unload(Nmodel_num);
2188 Nmodel_num = -1;
2189 }
2190
2191 if (Nmodel_instance_num >= 0) {
2192 model_delete_instance(Nmodel_instance_num);
2193 Nmodel_instance_num = -1;
2194 }
2195
2196 Nmodel_flags = flags;
2197
2198 if ( (model_name == NULL) || (*model_name == '\0') )
2199 return;
2200
2201 Nmodel_num = model_load(model_name, 0, NULL, -1);
2202 Nmodel_bitmap = bm_load(texture_name);
2203
2204 if (Nmodel_num >= 0) {
2205 model_page_in_textures(Nmodel_num);
2206
2207 if (model_get(Nmodel_num)->flags & PM_FLAG_HAS_INTRINSIC_ROTATE) {
2208 Nmodel_instance_num = model_create_instance(false, Nmodel_num);
2209 }
2210 }
2211
2212 // Since we have a new skybox we need to rerender the environment map
2213 stars_invalidate_environment_map();
2214 }
2215
2216 // call this to set a specific orientation for the background
stars_set_background_orientation(const matrix * orient)2217 void stars_set_background_orientation(const matrix *orient)
2218 {
2219 if (orient == NULL) {
2220 vm_set_identity(&Nmodel_orient);
2221 } else {
2222 Nmodel_orient = *orient;
2223 }
2224 }
2225
2226 // lookup a starfield bitmap, return index or -1 on fail
stars_find_bitmap(const char * name)2227 int stars_find_bitmap(const char *name)
2228 {
2229 int idx;
2230
2231 if (name == NULL)
2232 return -1;
2233
2234 // lookup
2235 for (idx = 0; idx < (int)Starfield_bitmaps.size(); idx++) {
2236 if ( !stricmp(name, Starfield_bitmaps[idx].filename) ) {
2237 return idx;
2238 }
2239 }
2240
2241 // not found
2242 return -1;
2243 }
2244
2245 // lookup a sun by bitmap filename, return index or -1 on fail
stars_find_sun(const char * name)2246 int stars_find_sun(const char *name)
2247 {
2248 int idx;
2249
2250 if (name == NULL)
2251 return -1;
2252
2253 // lookup
2254 for (idx = 0; idx < (int)Sun_bitmaps.size(); idx++) {
2255 if ( !stricmp(name, Sun_bitmaps[idx].filename) ) {
2256 return idx;
2257 }
2258 }
2259
2260 // not found
2261 return -1;
2262 }
2263
2264 // add an instance for a sun (something actually used in a mission)
2265 // NOTE that we assume a duplicate is ok here
stars_add_sun_entry(starfield_list_entry * sun_ptr)2266 int stars_add_sun_entry(starfield_list_entry *sun_ptr)
2267 {
2268 int idx, i;
2269 starfield_bitmap_instance sbi;
2270
2271 Assert(sun_ptr != NULL);
2272
2273 // copy information
2274 sbi.ang.p = sun_ptr->ang.p;
2275 sbi.ang.b = sun_ptr->ang.b;
2276 sbi.ang.h = sun_ptr->ang.h;
2277 sbi.scale_x = sun_ptr->scale_x;
2278 sbi.scale_y = sun_ptr->scale_y;
2279 sbi.div_x = sun_ptr->div_x;
2280 sbi.div_y = sun_ptr->div_y;
2281
2282 idx = stars_find_sun(sun_ptr->filename);
2283
2284 if (idx == -1) {
2285 if (!Fred_running) {
2286 Warning(LOCATION, "Trying to add a sun '%s' that does not exist in stars.tbl!", sun_ptr->filename);
2287 }
2288 return -1;
2289 }
2290
2291 sbi.star_bitmap_index = idx;
2292
2293 // make sure any needed bitmaps are loaded
2294 if (Sun_bitmaps[idx].bitmap_id < 0) {
2295 // normal bitmap
2296 Sun_bitmaps[idx].bitmap_id = bm_load(Sun_bitmaps[idx].filename);
2297
2298 // maybe didn't load a static image so try for an animated one
2299 if (Sun_bitmaps[idx].bitmap_id < 0) {
2300 Sun_bitmaps[idx].bitmap_id = bm_load_animation(Sun_bitmaps[idx].filename, &Sun_bitmaps[idx].n_frames, &Sun_bitmaps[idx].fps, nullptr, nullptr, true);
2301
2302 if (Sun_bitmaps[idx].bitmap_id < 0) {
2303 // failed
2304 return -1;
2305 }
2306 }
2307
2308 // glow bitmap
2309 if (Sun_bitmaps[idx].glow_bitmap < 0) {
2310 Sun_bitmaps[idx].glow_bitmap = bm_load(Sun_bitmaps[idx].glow_filename);
2311
2312 // maybe didn't load a static image so try for an animated one
2313 if (Sun_bitmaps[idx].glow_bitmap < 0) {
2314 Sun_bitmaps[idx].glow_bitmap = bm_load_animation(Sun_bitmaps[idx].glow_filename, &Sun_bitmaps[idx].glow_n_frames, &Sun_bitmaps[idx].glow_fps, nullptr, nullptr, true);
2315
2316 if (Sun_bitmaps[idx].glow_bitmap < 0) {
2317 Warning(LOCATION, "Unable to load sun glow bitmap: '%s'!\n", Sun_bitmaps[idx].glow_filename);
2318 }
2319 }
2320 }
2321
2322 if (Sun_bitmaps[idx].flare) {
2323 for (i = 0; i < MAX_FLARE_BMP; i++) {
2324 flare_bitmap* fbp = &Sun_bitmaps[idx].flare_bitmaps[i];
2325 if ( !strlen(fbp->filename) )
2326 continue;
2327
2328 if (fbp->bitmap_id < 0) {
2329 fbp->bitmap_id = bm_load(fbp->filename);
2330
2331 if (fbp->bitmap_id < 0) {
2332 Warning(LOCATION, "Unable to load sun flare bitmap: '%s'!\n", Sun_bitmaps[idx].flare_bitmaps[i].filename);
2333 continue;
2334 }
2335 }
2336 }
2337 }
2338 }
2339
2340 // The background changed so we need to invalidate the environment map
2341 stars_invalidate_environment_map();
2342
2343 // now check if we can make use of a previously discarded instance entry
2344 // this should never happen with FRED
2345 if ( !Fred_running ) {
2346 for (i = 0; i < (int)Suns.size(); i++) {
2347 if ( Suns[i].star_bitmap_index < 0 ) {
2348 Suns[i] = sbi;
2349 return i;
2350 }
2351 }
2352 }
2353
2354 // ... or add a new one
2355 Suns.push_back(sbi);
2356
2357 return (int)(Suns.size() - 1);
2358 }
2359
2360 // add an instance for a starfield bitmap (something actually used in a mission)
2361 // NOTE that we assume a duplicate is ok here
stars_add_bitmap_entry(starfield_list_entry * sle)2362 int stars_add_bitmap_entry(starfield_list_entry *sle)
2363 {
2364 int idx;
2365 starfield_bitmap_instance sbi;
2366
2367 Assert(sle != NULL);
2368
2369 // copy information
2370 sbi.ang.p = sle->ang.p;
2371 sbi.ang.b = sle->ang.b;
2372 sbi.ang.h = sle->ang.h;
2373 sbi.scale_x = sle->scale_x;
2374 sbi.scale_y = sle->scale_y;
2375 sbi.div_x = sle->div_x;
2376 sbi.div_y = sle->div_y;
2377
2378 idx = stars_find_bitmap(sle->filename);
2379
2380 if (idx == -1) {
2381 if (!Fred_running) {
2382 Warning(LOCATION, "Trying to add a bitmap '%s' that does not exist in stars.tbl!", sle->filename);
2383 }
2384 return -1;
2385 }
2386
2387 sbi.star_bitmap_index = idx;
2388
2389 // make sure any needed bitmaps are loaded
2390 if (Starfield_bitmaps[idx].bitmap_id < 0) {
2391 Starfield_bitmaps[idx].bitmap_id = bm_load(Starfield_bitmaps[idx].filename);
2392
2393 // maybe didn't load a static image so try for an animated one
2394 if (Starfield_bitmaps[idx].bitmap_id < 0) {
2395 Starfield_bitmaps[idx].bitmap_id = bm_load_animation(Starfield_bitmaps[idx].filename, &Starfield_bitmaps[idx].n_frames, &Starfield_bitmaps[idx].fps, nullptr, nullptr, true);
2396
2397 if (Starfield_bitmaps[idx].bitmap_id < 0) {
2398 // failed
2399 return -1;
2400 }
2401 }
2402 }
2403
2404 // The background changed so we need to invalidate the environment map
2405 stars_invalidate_environment_map();
2406
2407 // now check if we can make use of a previously discarded instance entry
2408 for (int i = 0; i < (int)Starfield_bitmap_instances.size(); i++) {
2409 if ( Starfield_bitmap_instances[i].star_bitmap_index < 0 ) {
2410 // starfield_update_index_buffers(i, 0);
2411 Starfield_bitmap_instances[i] = sbi;
2412 starfield_create_bitmap_buffer(i);
2413 return i;
2414 }
2415 }
2416
2417 // ... or add a new one
2418 Starfield_bitmap_instances.push_back(sbi);
2419 starfield_create_bitmap_buffer((int)(Starfield_bitmap_instances.size() - 1));
2420
2421 return (int)(Starfield_bitmap_instances.size() - 1);
2422 }
2423
2424 // get the number of entries that each vector contains
2425 // "is_a_sun" will get sun instance counts, otherwise it gets normal starfield bitmap instance counts
2426 // "bitmap_count" will get number of starfield_bitmap entries rather than starfield_bitmap_instance entries
stars_get_num_entries(bool is_a_sun,bool bitmap_count)2427 int stars_get_num_entries(bool is_a_sun, bool bitmap_count)
2428 {
2429 // try for instance counts first
2430 if (!bitmap_count) {
2431 if (is_a_sun) {
2432 return (int)Suns.size();
2433 } else {
2434 return (int)Starfield_bitmap_instances.size();
2435 }
2436 }
2437 // looks like we want bitmap counts (probably only FRED uses this)
2438 else {
2439 if (is_a_sun) {
2440 return (int)Sun_bitmaps.size();
2441 } else {
2442 return (int)Starfield_bitmaps.size();
2443 }
2444 }
2445 }
2446
2447
2448 // get a starfield_bitmap entry providing only an instance
stars_get_bitmap_entry(int index,bool is_a_sun)2449 starfield_bitmap *stars_get_bitmap_entry(int index, bool is_a_sun)
2450 {
2451 int max_index = (is_a_sun) ? (int)Suns.size() : (int)Starfield_bitmap_instances.size();
2452
2453 //WMC - Commented out because it keeps happening, and I don't know what this means.
2454 //Assert( (index >= 0) && (index < max_index) );
2455
2456 if ( (index < 0) || (index >= max_index) )
2457 return NULL;
2458
2459 if (is_a_sun && (Suns[index].star_bitmap_index >= 0)) {
2460 return &Sun_bitmaps[Suns[index].star_bitmap_index];
2461 } else if (!is_a_sun && (Starfield_bitmap_instances[index].star_bitmap_index >= 0)) {
2462 return &Starfield_bitmaps[Starfield_bitmap_instances[index].star_bitmap_index];
2463 }
2464
2465 return NULL;
2466 }
2467
stars_sun_has_glare(int index)2468 bool stars_sun_has_glare(int index)
2469 {
2470 starfield_bitmap *sb = stars_get_bitmap_entry(index, true);
2471 return (sb && sb->glare);
2472 }
2473
2474 // set an instace to not render
stars_mark_instance_unused(int index,bool is_a_sun)2475 void stars_mark_instance_unused(int index, bool is_a_sun)
2476 {
2477 int max_index = (is_a_sun) ? (int)Suns.size() : (int)Starfield_bitmap_instances.size();
2478
2479 Assert( (index >= 0) && (index < max_index) );
2480
2481 if ( (index < 0) || (index >= max_index) )
2482 return;
2483
2484 if (is_a_sun) {
2485 Suns[index].star_bitmap_index = -1;
2486 } else {
2487 Starfield_bitmap_instances[index].star_bitmap_index = -1;
2488 }
2489
2490 if ( !is_a_sun ) {
2491 delete [] Starfield_bitmap_instances[index].verts;
2492 Starfield_bitmap_instances[index].verts = NULL;
2493 }
2494
2495 // The background changed so we need to invalidate the environment map
2496 stars_invalidate_environment_map();
2497 }
2498
2499 // retrieves the name from starfield_bitmap for the instance index
2500 // NOTE: it's unsafe to return NULL here so use <none> for invalid entries
stars_get_name_from_instance(int index,bool is_a_sun)2501 const char *stars_get_name_from_instance(int index, bool is_a_sun)
2502 {
2503 int max_index = (is_a_sun) ? (int)Suns.size() : (int)Starfield_bitmap_instances.size();
2504
2505 Assert( (index >= 0) && (index < max_index) );
2506
2507 if ( (index < 0) || (index >= max_index) )
2508 return NOX("<none>");
2509
2510 if (is_a_sun && (Suns[index].star_bitmap_index >= 0)) {
2511 return Sun_bitmaps[Suns[index].star_bitmap_index].filename;
2512 } else if (!is_a_sun && (Starfield_bitmap_instances[index].star_bitmap_index >= 0)) {
2513 return Starfield_bitmaps[Starfield_bitmap_instances[index].star_bitmap_index].filename;
2514 }
2515
2516 return NOX("<none>");
2517 }
2518
2519 // WMC/Goober5000
stars_set_nebula(bool activate,float range)2520 void stars_set_nebula(bool activate, float range)
2521 {
2522 The_mission.flags.set(Mission::Mission_Flags::Fullneb, activate);
2523
2524 if (activate)
2525 {
2526 Toggle_text_alpha = TOGGLE_TEXT_NEBULA_ALPHA;
2527 HUD_contrast = 1;
2528
2529 Neb2_render_mode = NEB2_RENDER_HTL;
2530 Neb2_awacs = range;
2531 if (Neb2_awacs < 0.1f)
2532 Neb2_awacs = 3000.0f; // this is also the default in the background editor
2533
2534 // this function is not currently called in FRED, but this would be needed for FRED support
2535 if (Fred_running)
2536 {
2537 Neb2_render_mode = NEB2_RENDER_POF;
2538 stars_set_background_model(BACKGROUND_MODEL_FILENAME, Neb2_texture_name);
2539 stars_set_background_orientation();
2540 }
2541 }
2542 else
2543 {
2544 Toggle_text_alpha = TOGGLE_TEXT_NORMAL_ALPHA;
2545 HUD_contrast = 0;
2546
2547 Neb2_render_mode = NEB2_RENDER_NONE;
2548 Neb2_awacs = -1.0f;
2549 }
2550
2551 // (DahBlount)
2552 // This needs to be done regardless of whether we're in nebula or not
2553 // Should ensure that we're actually loading the needed anims into BMPMan
2554 stars_load_debris(static_cast<int>(activate));
2555
2556 // We need to reload the environment map now
2557 stars_invalidate_environment_map();
2558 }
2559
2560 // retrieves the name from starfield_bitmap, really only used by FRED2
2561 // NOTE: it is unsafe to return NULL here, but because that's bad anyway it really shouldn't happen, so we do return NULL.
stars_get_name_FRED(int index,bool is_a_sun)2562 const char *stars_get_name_FRED(int index, bool is_a_sun)
2563 {
2564 if (!Fred_running)
2565 return NULL;
2566
2567 int max_index = (is_a_sun) ? (int)Sun_bitmaps.size() : (int)Starfield_bitmaps.size();
2568
2569 Assert( (index >= 0) && (index < max_index) );
2570
2571 if ( (index < 0) || (index >= max_index) )
2572 return NULL;
2573
2574 if (is_a_sun) {
2575 return Sun_bitmaps[index].filename;
2576 } else {
2577 return Starfield_bitmaps[index].filename;
2578 }
2579 }
2580
2581 // modify an existing starfield bitmap instance, or add a new one if needed
stars_modify_entry_FRED(int index,const char * name,starfield_list_entry * sbi_new,bool is_a_sun)2582 void stars_modify_entry_FRED(int index, const char *name, starfield_list_entry *sbi_new, bool is_a_sun)
2583 {
2584 if (!Fred_running)
2585 return;
2586
2587 starfield_bitmap_instance sbi;
2588 int idx;
2589 int add_new = index > ((is_a_sun) ? (int)Sun_bitmaps.size() : (int)Starfield_bitmaps.size());
2590
2591 Assert( index >= 0 );
2592 Assert( sbi_new != NULL );
2593
2594 // copy information
2595 sbi.ang.p = sbi_new->ang.p;
2596 sbi.ang.b = sbi_new->ang.b;
2597 sbi.ang.h = sbi_new->ang.h;
2598 sbi.scale_x = sbi_new->scale_x;
2599 sbi.scale_y = sbi_new->scale_y;
2600 sbi.div_x = sbi_new->div_x;
2601 sbi.div_y = sbi_new->div_y;
2602
2603 if (is_a_sun) {
2604 idx = stars_find_sun((char*)name);
2605 } else {
2606 idx = stars_find_bitmap((char*)name);
2607 }
2608
2609 // this shouldn't ever happen from FRED since you select the name from a list of those available
2610 if (idx == -1)
2611 return;
2612
2613 sbi.star_bitmap_index = idx;
2614
2615 if (add_new) {
2616 if (is_a_sun) {
2617 Suns.push_back( sbi );
2618 } else {
2619 Starfield_bitmap_instances.push_back( sbi );
2620 }
2621 } else {
2622 if (is_a_sun) {
2623 Suns[index] = sbi;
2624 } else {
2625 Starfield_bitmap_instances[index] = sbi;
2626 }
2627 }
2628
2629 if ( !is_a_sun ) {
2630 starfield_create_bitmap_buffer(index);
2631 }
2632 }
2633
2634 // erase an instance, note that this is very slow so it should only be done in FRED
stars_delete_entry_FRED(int index,bool is_a_sun)2635 void stars_delete_entry_FRED(int index, bool is_a_sun)
2636 {
2637 if (!Fred_running)
2638 return;
2639
2640 int max_index = (is_a_sun) ? (int)Suns.size() : (int)Starfield_bitmap_instances.size();
2641
2642 Assert( (index >= 0) && (index < max_index) );
2643
2644 if ( (index < 0) || (index >= max_index) )
2645 return;
2646
2647 if (is_a_sun) {
2648 Suns.erase( Suns.begin() + index );
2649 } else {
2650 Starfield_bitmap_instances.erase( Starfield_bitmap_instances.begin() + index );
2651 }
2652 }
2653
2654 // Goober5000
stars_load_first_valid_background()2655 void stars_load_first_valid_background()
2656 {
2657 int background_idx = stars_get_first_valid_background();
2658 stars_load_background(background_idx);
2659 }
2660
2661 // Goober5000
stars_get_first_valid_background()2662 int stars_get_first_valid_background()
2663 {
2664 uint i, j;
2665
2666 if (Backgrounds.empty())
2667 return -1;
2668
2669 // scan every background except the last and return the first one that has all its suns and bitmaps present
2670 for (i = 0; i < Backgrounds.size() - 1; i++)
2671 {
2672 bool valid = true;
2673 background_t *background = &Backgrounds[i];
2674
2675 for (j = 0; j < background->suns.size(); j++)
2676 {
2677 if (stars_find_sun(background->suns[j].filename) < 0)
2678 {
2679 mprintf(("Failed to load sun %s for background %d, falling back to background %d\n",
2680 background->suns[j].filename, i + 1, i + 2));
2681 valid = false;
2682 break;
2683 }
2684 }
2685
2686 if (valid)
2687 {
2688 for (j = 0; j < background->bitmaps.size(); j++)
2689 {
2690 if (stars_find_bitmap(background->bitmaps[j].filename) < 0)
2691 {
2692 mprintf(("Failed to load bitmap %s for background %d, falling back to background %d\n",
2693 background->bitmaps[j].filename, i + 1, i + 2));
2694 valid = false;
2695 break;
2696 }
2697 }
2698 }
2699
2700 if (valid)
2701 return i;
2702 }
2703
2704 // didn't find a valid background yet, so return the last one
2705 return (int)Backgrounds.size() - 1;
2706 }
2707
2708 // Goober5000
stars_load_background(int background_idx)2709 void stars_load_background(int background_idx)
2710 {
2711 uint j;
2712
2713 stars_clear_instances();
2714 Cur_background = background_idx;
2715
2716 if (Cur_background >= 0)
2717 {
2718 background_t *background = &Backgrounds[Cur_background];
2719
2720 int failed_suns = 0;
2721 for (j = 0; j < background->suns.size(); j++)
2722 {
2723 if ((stars_add_sun_entry(&background->suns[j]) < 0) && !Fred_running)
2724 {
2725 nprintf(("General", "Failed to add sun '%s' to the mission!", background->suns[j].filename));
2726 failed_suns++;
2727 }
2728 }
2729 if (failed_suns > 0)
2730 Warning(LOCATION, "Failed to add %d sun bitmaps to the mission!", failed_suns);
2731
2732 int failed_stars = 0;
2733 for (j = 0; j < background->bitmaps.size(); j++)
2734 {
2735 if ((stars_add_bitmap_entry(&background->bitmaps[j]) < 0) && !Fred_running)
2736 {
2737 nprintf(("General", "Failed to add starfield bitmap '%s' to the mission!", background->bitmaps[j].filename));
2738 failed_stars++;
2739 }
2740 }
2741 if (failed_stars > 0)
2742 Warning(LOCATION, "Failed to add %d starfield bitmaps to the mission!", failed_stars);
2743 }
2744 }
2745
2746 // Goober5000
stars_copy_background(background_t * dest,background_t * src)2747 void stars_copy_background(background_t *dest, background_t *src)
2748 {
2749 dest->suns.assign(src->suns.begin(), src->suns.end());
2750 dest->bitmaps.assign(src->bitmaps.begin(), src->bitmaps.end());
2751 }
2752
2753 // Goober5000
stars_swap_backgrounds(int idx1,int idx2)2754 void stars_swap_backgrounds(int idx1, int idx2)
2755 {
2756 background_t temp;
2757 stars_copy_background(&temp, &Backgrounds[idx1]);
2758 stars_copy_background(&Backgrounds[idx1], &Backgrounds[idx2]);
2759 stars_copy_background(&Backgrounds[idx2], &temp);
2760 }
2761
2762 // Goober5000
stars_background_empty(const background_t & bg)2763 bool stars_background_empty(const background_t &bg)
2764 {
2765 return (bg.suns.empty() && bg.bitmaps.empty());
2766 }
2767
2768 // Goober5000
stars_pack_backgrounds()2769 void stars_pack_backgrounds()
2770 {
2771 // remove all empty backgrounds
2772 Backgrounds.erase(
2773 std::remove_if(Backgrounds.begin(), Backgrounds.end(), stars_background_empty),
2774 Backgrounds.end());
2775 }
2776
render_environment(int i,vec3d * eye_pos,matrix * new_orient,float new_zoom)2777 static void render_environment(int i, vec3d *eye_pos, matrix *new_orient, float new_zoom)
2778 {
2779 bm_set_render_target(gr_screen.envmap_render_target, i);
2780
2781 gr_clear();
2782
2783 g3_set_view_matrix( eye_pos, new_orient, new_zoom );
2784
2785 gr_set_proj_matrix( PI_2 * new_zoom, 1.0f, Min_draw_distance, Max_draw_distance);
2786 gr_set_view_matrix( &Eye_position, &Eye_matrix );
2787
2788 if ( Game_subspace_effect ) {
2789 stars_draw(0, 0, 0, 1, 1);
2790 } else {
2791 stars_draw(0, 1, 1, 0, 1);
2792 }
2793
2794 gr_end_view_matrix();
2795 gr_end_proj_matrix();
2796 }
2797
stars_setup_environment_mapping(camid cid)2798 void stars_setup_environment_mapping(camid cid) {
2799 matrix new_orient = IDENTITY_MATRIX;
2800
2801 extern float View_zoom;
2802 float old_zoom = View_zoom, new_zoom = 1.0f;//0.925f;
2803 int i = 0;
2804
2805 if(!cid.isValid())
2806 return;
2807
2808 vec3d cam_pos;
2809 matrix cam_orient;
2810 cid.getCamera()->get_info(&cam_pos, &cam_orient);
2811
2812 // prefer the mission specified envmap over the static-generated envmap, but
2813 // the dynamic envmap should always get preference if in a subspace mission
2814 if ( !Dynamic_environment && Mission_env_map >= 0 ) {
2815 ENVMAP = Mission_env_map;
2816 return;
2817 }
2818
2819 if (gr_screen.envmap_render_target < 0) {
2820 if (ENVMAP >= 0)
2821 return;
2822
2823 if (Mission_env_map >= 0) {
2824 ENVMAP = Mission_env_map;
2825 } else {
2826 ENVMAP = Default_env_map;
2827 }
2828
2829 return;
2830 }
2831
2832 if (Env_cubemap_drawn) {
2833 // Nothing to do here anymore
2834 return;
2835 }
2836
2837 GR_DEBUG_SCOPE("Environment Mapping");
2838 TRACE_SCOPE(tracing::EnvironmentMapping);
2839
2840 ENVMAP = gr_screen.envmap_render_target;
2841
2842 /*
2843 * Envmap matrix setup -- left-handed
2844 * -------------------------------------------------
2845 * Face -- Forward Up Right
2846 * px +X +Y -Z
2847 * nx -X +Y +Z
2848 * py +Y -Z +X
2849 * ny -Y +Z +X
2850 * pz +Z +Y +X
2851 * nz -Z +Y -X
2852 */
2853 // NOTE: OpenGL needs up/down reversed
2854
2855 // Save the previous render target so we can reset it once we are done here
2856 auto previous_target = gr_screen.rendering_to_texture;
2857
2858 // face 1 (px / right)
2859 memset( &new_orient, 0, sizeof(matrix) );
2860 new_orient.vec.fvec.xyz.x = 1.0f;
2861 new_orient.vec.uvec.xyz.y = 1.0f;
2862 new_orient.vec.rvec.xyz.z = -1.0f;
2863 render_environment(i, &cam_pos, &new_orient, new_zoom);
2864 i++; // bump!
2865
2866 // face 2 (nx / left)
2867 memset( &new_orient, 0, sizeof(matrix) );
2868 new_orient.vec.fvec.xyz.x = -1.0f;
2869 new_orient.vec.uvec.xyz.y = 1.0f;
2870 new_orient.vec.rvec.xyz.z = 1.0f;
2871 render_environment(i, &cam_pos, &new_orient, new_zoom);
2872 i++; // bump!
2873
2874 // face 3 (py / up)
2875 memset( &new_orient, 0, sizeof(matrix) );
2876 new_orient.vec.fvec.xyz.y = 1.0f;
2877 new_orient.vec.uvec.xyz.z = -1.0f;
2878 new_orient.vec.rvec.xyz.x = 1.0f;
2879 render_environment(i, &cam_pos, &new_orient, new_zoom);
2880 i++; // bump!
2881
2882 // face 4 (ny / down)
2883 memset( &new_orient, 0, sizeof(matrix) );
2884 new_orient.vec.fvec.xyz.y = -1.0f;
2885 new_orient.vec.uvec.xyz.z = 1.0f ;
2886 new_orient.vec.rvec.xyz.x = 1.0f;
2887 render_environment(i, &cam_pos, &new_orient, new_zoom);
2888 i++; // bump!
2889
2890 // face 5 (pz / forward)
2891 memset( &new_orient, 0, sizeof(matrix) );
2892 new_orient.vec.fvec.xyz.z = 1.0f;
2893 new_orient.vec.uvec.xyz.y = 1.0f;
2894 new_orient.vec.rvec.xyz.x = 1.0f;
2895 render_environment(i, &cam_pos, &new_orient, new_zoom);
2896 i++; // bump!
2897
2898 // face 6 (nz / back)
2899 memset( &new_orient, 0, sizeof(matrix) );
2900 new_orient.vec.fvec.xyz.z = -1.0f;
2901 new_orient.vec.uvec.xyz.y = 1.0f;
2902 new_orient.vec.rvec.xyz.x = -1.0f;
2903 render_environment(i, &cam_pos, &new_orient, new_zoom);
2904
2905
2906 // we're done, so now reset
2907 bm_set_render_target(previous_target);
2908 g3_set_view_matrix( &cam_pos, &cam_orient, old_zoom );
2909
2910 if ( !Dynamic_environment ) {
2911 Env_cubemap_drawn = true;
2912 }
2913 }
stars_set_dynamic_environment(bool dynamic)2914 void stars_set_dynamic_environment(bool dynamic) {
2915 Dynamic_environment = dynamic;
2916 stars_invalidate_environment_map();
2917 }
stars_invalidate_environment_map()2918 void stars_invalidate_environment_map() {
2919 // This will cause a redraw in the next frame
2920 Env_cubemap_drawn = false;
2921 }
2922