1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/lib/src/star/RCS/star.c $
21  * $Revision: 1.2 $
22  * $Author: buzzard $
23  * $Date: 1994/11/11 19:51:04 $
24  *
25  * Main star library source
26  *
27  * $Log: star.c $
28  * Revision 1.2  1994/11/11  19:51:04  buzzard
29  * Anti/aliasing stars in high resolutions.
30  * Ugly hacked for system shock rather than changing interface.
31  *
32  * Revision 1.1  1994/10/24  23:27:31  jaemz
33  * Initial revision
34  *
35  * Revision 1.2  1994/10/21  16:45:47  jaemz
36  * *** empty log message ***
37  *
38  * Revision 1.1  1994/09/07  17:41:29  jaemz
39  * Initial revision
40  *
41  */
42 
43 #include <stdlib.h>
44 #include "star.h"
45 
46 #include "OpenGL.h"
47 
48 //#define  STAR_SPEW
49 #define STARS_ANTI_ALIAS
50 
51 #ifdef STEREO_ON
52 extern uchar g3d_stereo;
53 extern fix g3d_eyesep_raw;
54 extern uchar *g3d_rt_canv_bits;
55 extern uchar *g3d_lt_canv_bits;
56 #endif
57 
58 // globals for state
59 sts_vec *std_vec;
60 uchar *std_col;
61 int std_num;
62 
63 fix std_min_z = 0x7fffffff;
64 fix std_max_rad = 0;
65 
66 int std_size = 1;
67 
68 #ifdef STARS_ANTI_ALIAS
69 // The canvas must be more than <std_alias_size> pixels wide
70 // for us to anti-alias the stars (which makes them bigger,
71 // hence the size restriction)
72 int std_alias_size = 640;
73 // This size is chosen so anti-aliasing starts happening
74 // in full screen 640x400 modes.  Won't happen in demo mode
75 // if demo mode uses a subcanvas
76 
77 // We must record the meaning of the colors of stars so
78 // we can anti-alias them
79 int std_color_base, std_color_range;
80 
81 // gamma-correct star colors
82 uchar std_alias_color_table[256];
83 #endif
84 
85 extern g3s_vector _matrix_scale;
86 extern g3s_phandle _vbuf2;
87 extern int _n_verts;
88 
89 // prototypes
90 fix mag2_point(g3s_phandle p);
91 void do_aa_star_pixel(int x, int y, int fx, int fy, int c);
92 void do_aa_star(fix fx, fix fy, int c);
93 void star_init_alias_table(void);
94 
95 // sets global pointers in the star library
96 // to the number of stars, their positions, their colors
star_set(int n,sts_vec * vlist,uchar * clist)97 void star_set(int n, sts_vec *vlist, uchar *clist) {
98     std_num = n;
99     std_vec = vlist;
100     std_col = clist;
101 }
102 
103 // allocates the necessary space for stars using alloc
star_alloc(int n)104 int star_alloc(int n) {
105     std_vec = (sts_vec *)malloc(n * sizeof(sts_vec) + n);
106     if (std_vec == NULL)
107         return -1;
108     std_col = (uchar *)(std_vec + n);
109     std_num = n;
110     return n;
111 }
112 
113 // frees star space using free, only if you've used
114 // alloc to allocate it
star_free(void)115 void star_free(void) { free(std_vec); }
116 
117 // renders star field in the polygon defined by the vertex list
118 // uses your 3d context, so make sure that's been set
119 // what we do is render a zero polygon, which is black,
120 // then render stars on that field.  Beware of CLUT modes or
121 // other FILL modes.  In general, will hurt this and we'll
122 // have to code around it.
123 // Then we rotate star field around viewer and draw them
124 // wherever there is black.   Not dissimilar to Kevins
125 // per tmap hack
126 
127 extern g3s_vector view_position;
128 
129 #ifdef STAR_SPEW
130 extern int star_num_behind;
131 extern int star_num_projected;
132 #endif
133 
134 // for the love of god, I hate 3d scaling.
mag2_point(g3s_phandle p)135 fix mag2_point(g3s_phandle p) {
136     fix a, f;
137 
138     a = fix_div(p->gX, _matrix_scale.gX);
139     f = fix_mul(a, a);
140 
141     a = fix_div(p->gY, _matrix_scale.gY);
142     f += fix_mul(a, a);
143 
144     a = fix_div(p->gZ, _matrix_scale.gZ);
145     f += fix_mul(a, a);
146 
147     return f;
148 }
149 
150 // in stereo mode, we can leave this normal
151 // and it should work, assuming the two
152 // polygons are similar enough
star_poly(int n,g3s_phandle * vp)153 void star_poly(int n, g3s_phandle *vp) {
154     if(use_opengl()) {
155         // Stencil out this area where we want stars to draw
156         opengl_set_stencil(0xFF);
157         opengl_draw_poly(0x00, n, vp, 0);
158         opengl_set_stencil(0x00);
159     }
160     else {
161         // draw star poly in color zero (note that 255 is hacked black).  This part very
162         // important, if not zero, won't work.
163         g3_draw_poly(0xff, n, vp);
164     }
165 
166     star_empty(n, vp); //fix disappearing stars; std_min_z was not being set below its max value,
167                        //which caused star_render() to abort
168 }
169 
star_empty(int n,g3s_phandle * vp)170 void star_empty(int n, g3s_phandle *vp) {
171     int i;
172     fix m;
173     int n1;
174     g3s_phandle dest[10]; // assume max clip 10
175 
176     // clip it just like you would when you render
177     // then run through it
178     // set max out the maximum it could be, which would be eyesep raw
179     n1 = g3_clip_polygon(n, vp, dest);
180     for (i = 0; i < n1; ++i) {
181         if (dest[i]->gZ < std_min_z)
182             std_min_z = dest[i]->gZ;
183         m = mag2_point(dest[i]);
184         if (m > std_max_rad)
185             std_max_rad = m;
186     }
187 }
188 
189 // render stars to a sky-like thing,
190 // no viewports, clips to half sphere
star_sky(void)191 void star_sky(void) {
192     std_min_z = 0;
193     std_max_rad = FIX_UNIT;
194 }
195 
196 #ifdef STARS_ANTI_ALIAS
197 // render a single pixel of an anti-aliased star
do_aa_star_pixel(int x,int y,int fx,int fy,int c)198 void do_aa_star_pixel(int x, int y, int fx, int fy, int c) {
199     int q;
200 
201     if (gr_get_pixel(x, y) == 0xff) {
202         q = fx * fy * c;
203 
204         // fx is % 0..256; fy is % 0..256; c is % 0..255.
205 
206         q = q >> 16;
207 
208         // q is now 0..255, which we rescale back into color range
209 
210         gr_set_pixel(std_alias_color_table[q], x, y);
211     }
212 }
213 
214 // render an anti-aliased star.
215 // this is a prime candidate for being made table driven
do_aa_star(fix fx,fix fy,int c)216 void do_aa_star(fix fx, fix fy, int c) {
217     // isolate the fractions and the integers
218     int x_frac = fix_frac(fx) >> 8;
219     int y_frac = fix_frac(fy) >> 8;
220     int x = fix_int(fx);
221     int y = fix_int(fy);
222 
223     int color = (std_color_base + std_color_range - 1 - c);
224 
225     // rescale the color so that it's 0..255, 0 = dark, 255 = light
226     color = (255 * color) / (std_color_range + 1);
227 
228     // ok, now compute the weightings for each pixel
229 
230     do_aa_star_pixel(x, y, 256 - x_frac, 256 - y_frac, color);
231     do_aa_star_pixel(x + 1, y, x_frac, 256 - y_frac, color);
232     do_aa_star_pixel(x, y + 1, 256 - x_frac, y_frac, color);
233     do_aa_star_pixel(x + 1, y + 1, x_frac, y_frac, color);
234 }
235 
star_init_alias_table(void)236 void star_init_alias_table(void) {
237     // init gamma corrected table
238     int i, a;
239     fix b, gamma;
240 
241     gamma = fix_make(0, 30000);
242 
243     a = std_color_base + std_color_range - 1;
244     b = fix_make(-(std_color_range - 1), 0);
245 
246     for (i = 0; i < 256; ++i)
247         std_alias_color_table[i] = a + fix_int(fix_mul(b, fix_pow(fix_make(i, 0) / 255, gamma)));
248 }
249 
250 #endif
251 
star_render(void)252 void star_render(void) {
253     int i;
254     g3s_phandle s;
255     int x, y;
256     int x1, y1;
257     g3s_vector v;
258 #ifdef STEREO_ON
259     uchar old_stereo;
260 #endif
261 #ifdef STARS_ANTI_ALIAS
262     int anti_alias = grd_bm.w >= std_alias_size;
263 #endif
264 
265 #ifdef STAR_SPEW
266     star_num_behind = 0;
267     star_num_projected = 0;
268 #endif
269 
270 #if defined(STARS_ANTI_ALIAS) && defined(STEREO_ON)
271     if (g3d_stereo)
272         anti_alias = 0;
273 #endif
274 
275     // exit if no one every drew a star field anywhere visible
276     if (std_min_z == 0x7fffffff) {
277 #ifdef STAR_SPEW
278         mprintf("ignored\n");
279 #endif
280         return;
281     }
282 
283     if(use_opengl()) {
284         opengl_begin_stars();
285     }
286 
287 #ifdef STAR_SPEW
288     mprintf("max_rad = %x min_z = %x\n", fix_sqrt(star_max_rad), star_min_z);
289 #endif
290     if (std_min_z < 0)
291         std_min_z = 0;
292 
293 // scale by max radius
294 #ifndef STEREO_ON
295     g3_scale_object(fix_sqrt(std_max_rad));
296 #else
297     // add in eyesep raw cause that's as much bigger it could be
298     g3_scale_object(fix_sqrt(std_max_rad) + (g3d_stereo ? g3d_eyesep_raw : 0));
299 #endif
300 
301 #ifdef STEREO_ON
302     old_stereo = g3d_stereo;
303     g3d_stereo = 0;
304 #endif
305 
306     for (i = 0; i < std_num; ++i) {
307         // in theory if codes aren't set it's on the screen
308 
309         // unpack star vec to a normal vec
310         v.gX = ((fix)std_vec[i].x) << 1;
311         v.gY = ((fix)std_vec[i].y) << 1;
312         v.gZ = ((fix)std_vec[i].z) << 1;
313 
314         s = star_transform_point(&v);
315 
316         if (s->codes == 0) {
317             x = fix_rint(s->sx);
318             y = fix_rint(s->sy);
319 
320             if(use_opengl()) {
321                 opengl_draw_star(s->sx, s->sy, std_col[i], anti_alias);
322                 continue;
323             }
324 
325             if (std_size <= 1) {
326 #ifdef STARS_ANTI_ALIAS
327                 if (anti_alias) {
328                     do_aa_star(s->sx, s->sy, std_col[i]);
329                 } else
330 #endif
331                     if (gr_get_pixel(x, y) == 0xff)
332                     gr_set_pixel(std_col[i], x, y);
333             } else {
334                 for (x1 = x; x1 < x + std_size; ++x1) {
335                     for (y1 = y; y1 < y + std_size; ++y1) {
336                         if (gr_get_pixel(x1, y1) == 0xff)
337                             gr_set_pixel(std_col[i], x1, y1);
338                     }
339                 }
340             }
341 
342 #ifdef STEREO_ON
343             if (old_stereo) {
344                 // switch canvases quickly
345                 grd_bm.bits = g3d_rt_canv_bits;
346                 if (std_size <= 1) {
347                     if (gr_get_pixel(x, y) == 0xff)
348                         gr_set_pixel(std_col[i], x, y);
349                 } else {
350                     for (x1 = x; x1 < x + std_size; ++x1) {
351                         for (y1 = y; y1 < y + std_size; ++y1) {
352                             if (gr_get_pixel(x1, y1) == 0xff)
353                                 gr_set_pixel(std_col[i], x1, y1);
354                         }
355                     }
356                 }
357                 // switch back
358                 grd_bm.bits = g3d_lt_canv_bits;
359             }
360 #endif
361         }
362 
363         g3_free_point(s);
364     }
365 
366     if(use_opengl()) {
367         opengl_end_stars();
368     }
369 
370 #ifdef STEREO_ON
371     g3d_stereo = old_stereo;
372 #endif
373 
374 #ifdef STAR_SPEW
375     mprintf("stars = %d behind = %d proj = %d\n", st_num, star_num_behind, star_num_projected);
376 #endif
377 
378     // reset min z and max rad
379     std_min_z = 0x7fffffff;
380     std_max_rad = 0;
381 }
382 
383 // stuffs random vectors and colors into the set areas
384 // randomly assigning a color range to them
385 // feel free to seed
star_rand(uchar col,uchar range)386 void star_rand(uchar col, uchar range) {
387     int i;
388     g3s_vector v;
389     sts_vec *s;
390     fix m;
391 
392 #ifdef STARS_ANTI_ALIAS
393     // SYSTEM SHOCK HACK!
394     std_color_base = 208; // col;
395     std_color_range = 16; // range;
396 
397     star_init_alias_table();
398 #endif
399 
400     for (i = 0; i < std_num; ++i) {
401         s = &std_vec[i];
402 
403         v.gX = ((rand() % 4000) - 2000) << 8;
404         v.gY = ((rand() % 4000) - 2000) << 8;
405         v.gZ = ((rand() % 4000) - 2000) << 8;
406 
407         m = fix_mul(v.gX, v.gX) + fix_mul(v.gY, v.gY) + fix_mul(v.gZ, v.gZ);
408 
409         if (m < FIX_UNIT / 100) {
410             i = i - 1;
411             continue;
412         }
413 
414         m = fix_sqrt(m);
415 
416         // normalize for fun hack
417         v.gX = fix_div(v.gX, m);
418         v.gY = fix_div(v.gY, m);
419         v.gZ = fix_div(v.gZ, m);
420 
421         // put into star vec
422         s->x = (v.gX >> 1);
423         s->y = (v.gY >> 1);
424         s->z = (v.gZ >> 1);
425 
426         // assign color
427         std_col[i] = rand() % range + col;
428     }
429 }
430 
431 extern g3s_point *first_free;
432 extern g3s_matrix view_matrix;
433 extern int code_point(g3s_point *pt);
434 
435 // matrix rotate and code a star point.  Project if clip codes
436 // are not set, rotate fully if in front of viewer
437 // (or smaller than a boundary
438 // takes pointer to vector
439 // returns point
star_transform_point(g3s_vector * v)440 g3s_phandle star_transform_point(g3s_vector *v) {
441     g3s_point *point;
442     int64_t r;
443     fix temp;
444 
445     getpnt(point);
446     point->p3_flags = 0;
447 
448     // third column (z)
449     r =  fix64_mul(v->gX, vm3) + fix64_mul(v->gY, vm6) + fix64_mul(v->gZ, vm9);
450     temp = fix64_to_fix(r);
451 
452     // check out z, see if behind
453     if (temp < std_min_z) {
454         point->codes = CC_BEHIND;
455         return (point);
456     }
457 
458     point->gZ = temp; // save z
459 
460     // first column (x)
461     r =  fix64_mul(v->gX, vm1) + fix64_mul(v->gY, vm4) + fix64_mul(v->gZ, vm7);
462     point->gX = fix64_to_fix(r);
463 
464     // second column (y)
465     r =  fix64_mul(v->gX, vm2) + fix64_mul(v->gY, vm5) + fix64_mul(v->gZ, vm8);
466     point->gY = fix64_to_fix(r);
467 
468     // call clip codes
469     if (code_point(point))
470         return (point);
471 
472     // transform if not clipped
473     g3_project_point(point);
474     return (point);
475 }
476