Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Sky.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: Sky.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Celestial sphere, stars, planets, space dust...
13 */
14 
15 #include "MemDebug.h"
16 #include "Sky.h"
17 #include "StarSystem.h"
18 
19 #include "Game.h"
20 #include "Bitmap.h"
21 #include "DataLoader.h"
22 #include "Light.h"
23 #include "Random.h"
24 
25 void Print(const char* ftm, ...);
26 
27 
28 // +====================================================================+
29 
30 Stars::Stars(int nstars)
31 {
32  infinite = true;
33  luminous = true;
34  shadow = false;
35 
36  vset = new(__FILE__,__LINE__) VertexSet(nstars);
37  colors = new(__FILE__,__LINE__) Color[nstars];
38 
39  for (int i = 0; i < nstars; i++) {
40  vset->loc[i] = RandomVector(1000);
41 
42  ColorValue val = ColorValue((float) Random(0.7, 0.8),
43  (float) Random(0.7, 0.8),
44  (float) Random(0.7, 0.8));
45  Color c = val.ToColor();
46 
47  colors[i] = c;
48  vset->diffuse[i] = c.Value();
49  vset->specular[i] = 0;
50  }
51 
52  strcpy_s(name, "Stars");
53 }
54 
56 {
57  delete [] colors;
58  delete vset;
59 }
60 
61 // +--------------------------------------------------------------------+
62 
63 void
64 Stars::Illuminate(double scale)
65 {
66  if (!vset)
67  return;
68 
69  for (int i = 0; i < vset->nverts; i++) {
70  Color c = colors[i] * scale;
71  vset->diffuse[i] = c.Value();
72  }
73 }
74 
75 // +--------------------------------------------------------------------+
76 
77 void
78 Stars::Render(Video* video, DWORD flags)
79 {
80  if (!vset || !video || (flags & Graphic::RENDER_ADDITIVE) == 0)
81  return;
82 
84  video->DrawPoints(vset);
85 }
86 
87 // +====================================================================+
88 
89 static const double BOUNDARY = 3000;
90 static const double BOUNDARYx2 = BOUNDARY * 2;
91 
92 Dust::Dust(int ndust, bool b)
93 : really_hidden(false), bright(b)
94 {
95  radius = (float) BOUNDARYx2;
96  luminous = true;
97  vset = new(__FILE__,__LINE__) VertexSet(ndust);
98 
99  Reset(Point(0, 0, 0));
100  strcpy_s(name, "Dust");
101 }
102 
103 // +--------------------------------------------------------------------+
104 
106 {
107  delete vset;
108 }
109 
110 // +--------------------------------------------------------------------+
111 
112 void
113 Dust::Reset(const Point& ref)
114 {
115  BYTE c = 0;
116 
117  for (int i = 0; i < vset->nverts; i++) {
118  vset->loc[i] = Vec3( Random(-BOUNDARY, BOUNDARY),
119  Random(-BOUNDARY, BOUNDARY),
120  Random(-BOUNDARY, BOUNDARY) );
121 
122  if (bright)
123  c = (BYTE) Random(96,200);
124  else
125  c = (BYTE) Random(64,156);
126 
127  vset->diffuse[i] = Color(c,c,c).Value();
128  vset->specular[i] = 0;
129  }
130 }
131 
132 // +--------------------------------------------------------------------+
133 
134 void
135 Dust::ExecFrame(double factor, const Point& ref)
136 {
137  if (Game::TimeCompression() > 4) {
138  Hide();
139  return;
140  }
141 
142  Show();
143 
144  Point delta = ref - loc;
145  double dlen = delta.length();
146 
147  if (dlen < 0.0001)
148  return;
149 
150  if (dlen > BOUNDARY) {
151  Reset(ref);
152  }
153  else {
154  // wrap around if necessary to keep in view
155  for (int i = 0; i < vset->nverts; i++) {
156  Vec3 v = vset->loc[i];
157 
158  v -= delta;
159 
160  if (v.x > BOUNDARY) v.x -= (float) BOUNDARYx2;
161  if (v.x < -BOUNDARY) v.x += (float) BOUNDARYx2;
162  if (v.y > BOUNDARY) v.y -= (float) BOUNDARYx2;
163  if (v.y < -BOUNDARY) v.y += (float) BOUNDARYx2;
164  if (v.z > BOUNDARY) v.z -= (float) BOUNDARYx2;
165  if (v.z < -BOUNDARY) v.z += (float) BOUNDARYx2;
166 
167  vset->loc[i] = v;
168  }
169  }
170 
171  MoveTo(ref);
172 }
173 
174 // +--------------------------------------------------------------------+
175 
176 void
177 Dust::Render(Video* video, DWORD flags)
178 {
179  if (hidden || really_hidden)
180  return;
181 
182  if (!vset || !video || (flags & Graphic::RENDER_SOLID) == 0 || (flags & Graphic::RENDER_ADD_LIGHT) != 0)
183  return;
184 
186  video->SetRenderState(Video::Z_ENABLE, false);
187  video->SetRenderState(Video::Z_WRITE_ENABLE, false);
188 
189  video->DrawPoints(vset);
190 
191  video->SetRenderState(Video::Z_ENABLE, true);
193 }
194 
195 // +--------------------------------------------------------------------+
196 
197 void
199 {
200  hidden = true;
201  really_hidden = true;
202 }
203 
204 void
206 {
207  hidden = false;
208  really_hidden = false;
209 }
210 
211 // +====================================================================+
212 
213 PlanetRep::PlanetRep(const char* surface_name, const char* glow_name,
214 double rad, const Vec3& pos, double tscale,
215 const char* rngname, double minrad, double maxrad,
216 Color atmos, const char* gloss_name)
217 : mtl_surf(0), mtl_limb(0), mtl_ring(0), star_system(0)
218 {
219  loc = pos;
220 
221  radius = (float) rad;
222  has_ring = 0;
223  ring_verts = -1;
224  ring_polys = -1;
225  ring_rad = 0;
226  body_rad = rad;
227  daytime = false;
228  atmosphere = atmos;
229  star_system = 0;
230 
231  if (!surface_name || !*surface_name) {
232  Print(" invalid Planet patch - no surface texture specified\n");
233  return;
234  }
235 
236  Print(" constructing Planet patch %s\n", surface_name);
237  strncpy(name, surface_name, 31);
238  name[31] = 0;
239 
240  Bitmap* bmp_surf = 0;
241  Bitmap* bmp_spec = 0;
242  Bitmap* bmp_glow = 0;
243  Bitmap* bmp_ring = 0;
244  Bitmap* bmp_limb = 0;
245 
246  DataLoader* loader = DataLoader::GetLoader();
247  loader->LoadTexture(surface_name, bmp_surf, Bitmap::BMP_SOLID, true);
248 
249  if (glow_name && *glow_name) {
250  Print(" loading glow texture %s\n", glow_name);
251  loader->LoadTexture(glow_name, bmp_glow, Bitmap::BMP_SOLID, true);
252  }
253 
254  if (gloss_name && *gloss_name) {
255  Print(" loading gloss texture %s\n", gloss_name);
256  loader->LoadTexture(gloss_name, bmp_spec, Bitmap::BMP_SOLID, true);
257  }
258 
259  mtl_surf = new(__FILE__,__LINE__) Material;
260 
263  mtl_surf->Ke = bmp_glow ? Color::White : Color::Black;
264  mtl_surf->Ks = bmp_spec ? Color::LightGray : Color::Black;
265  mtl_surf->power = 25.0f;
266  mtl_surf->tex_diffuse = bmp_surf;
267  mtl_surf->tex_specular = bmp_spec;
268  mtl_surf->tex_emissive = bmp_glow;
270 
271  if (bmp_spec && Video::GetInstance()->IsSpecMapEnabled()) {
272  if (glow_name && strstr(glow_name, "light"))
273  strcpy_s(mtl_surf->shader, "SimplePix/PlanetSurfNightLight");
274 
275  else if (glow_name)
276  strcpy_s(mtl_surf->shader, "SimplePix/PlanetSurf");
277  }
278 
279  if (atmosphere != Color::Black) {
280  mtl_limb = new(__FILE__,__LINE__) Material;
281 
283 
284  strcpy_s(mtl_limb->shader, "PlanetLimb");
285 
286  Print(" loading atmospheric limb texture PlanetLimb.pcx\n");
287  loader->LoadTexture("PlanetLimb.pcx", bmp_limb, Bitmap::BMP_TRANSLUCENT, true);
288  mtl_limb->tex_diffuse = bmp_limb;
290  }
291 
292  if (maxrad > 0 && minrad > 0) {
293  has_ring = 1;
294  radius = (float) maxrad;
295  ring_rad = (maxrad + minrad)/2;
296  loader->LoadTexture(rngname, bmp_ring, Bitmap::BMP_SOLID, true);
297 
298  mtl_ring = new(__FILE__,__LINE__) Material;
299 
304  mtl_ring->power = 30.0f;
305  mtl_ring->tex_diffuse = bmp_ring;
307  }
308 
309  if (rad > 2e6 && rad < 1e8)
310  CreateSphere(rad, 24, 32, minrad, maxrad, 48, tscale);
311  else
312  CreateSphere(rad, 16, 24, minrad, maxrad, 48, tscale);
313 }
314 
315 // +--------------------------------------------------------------------+
316 
318 {
319 }
320 
321 // +--------------------------------------------------------------------+
322 
323 void
324 PlanetRep::CreateSphere(double radius, int nrings, int nsections,
325 double minrad, double maxrad, int rsections,
326 double tscale)
327 {
328  const int sect_verts = nsections + 1;
329 
330  model = new(__FILE__,__LINE__) Model;
331  own_model = 1;
332 
333  Surface* surface = new(__FILE__,__LINE__) Surface;
334 
335  int i, j, m, n;
336 
337  int npolys = (nrings + 2) * nsections;
338  int nverts = (nrings + 3) * sect_verts;
339 
340  int ppolys = npolys;
341  int pverts = nverts;
342 
343  int apolys = 0;
344  int averts = 0;
345 
346  if (atmosphere != Color::Black) {
347  apolys = npolys;
348  averts = nverts;
349 
350  npolys *= 2;
351  nverts *= 2;
352  }
353 
354  if (has_ring) {
355  ring_verts = nverts;
356  ring_polys = npolys;
357 
358  npolys += rsections * 3; // top, bottom, edge
359  nverts += rsections * 6;
360  }
361 
362  surface->SetName(name);
363  surface->CreateVerts(nverts);
364  surface->CreatePolys(npolys);
365 
366  VertexSet* vset = surface->GetVertexSet();
367 
368  if (!vset || vset->nverts < nverts) {
369  ::Print("WARNING: insufficient memory for planet '%s'\n", name);
370  return;
371  }
372 
373  Poly* polys = surface->GetPolys();
374 
375  if (!polys) {
376  ::Print("WARNING: insufficient memory for planet '%s'\n", name);
377  return;
378  }
379 
380  ZeroMemory(polys, sizeof(Poly) * npolys);
381 
382  // Generate vertex points for planetary rings:
383  double dtheta = PI / (nrings + 2);
384  double dphi = 2 * PI / nsections;
385  double theta = 0;
386  n = 0; // vertex being generated
387 
388  for (i = 0; i < nrings+3; i++) {
389  double y = radius * cos(theta); // y is the same for entire ring
390  double v = theta / PI; // v is the same for entire ring
391  double rsintheta = radius * sin(theta);
392  double phi = 0;
393 
394  for (j = 0; j < sect_verts; j++) {
395  double x = rsintheta * sin(phi);
396  double z = rsintheta * cos(phi);
397 
398  vset->loc[n] = Vec3(x, y, z);
399  vset->nrm[n] = Vec3(x, y, z);
400  vset->tu[n] = (float) (tscale * (1 - (phi/(2.0*PI))));
401  vset->tv[n] = (float) (tscale * v);
402 
403  vset->nrm[n].Normalize();
404 
405  phi += dphi;
406  n++;
407  }
408 
409  theta += dtheta;
410  }
411 
412  // Generate vertex points for rings:
413  if (has_ring) {
414  n = ring_verts;
415 
416  double dphi = 2.0 * PI / rsections;
417  double y = 0; // y is the same for entire ring
418 
419  // top of ring:
420  double phi = 0;
421  for (j = 0; j < rsections; j++) {
422  double x = minrad * sin(phi);
423  double z = minrad * cos(phi);
424 
425  vset->loc[n] = Vec3(x, y, z);
426  vset->nrm[n] = Vec3(0, 1, 0);
427  vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
428  vset->tv[n] = 0.0f;
429  n++;
430 
431  x = maxrad * sin(phi);
432  z = maxrad * cos(phi);
433 
434  vset->loc[n] = Vec3(x, y, z);
435  vset->nrm[n] = Vec3(0, 1, 0);
436  vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
437  vset->tv[n] = 1.0f;
438  n++;
439 
440  phi += dphi;
441  }
442 
443  // bottom of ring:
444  phi = 0;
445  for (j = 0; j < rsections; j++) {
446  double x = minrad * sin(phi);
447  double z = minrad * cos(phi);
448 
449  vset->loc[n] = Vec3(x, y, z);
450  vset->nrm[n] = Vec3(0, -1, 0);
451  vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
452  vset->tv[n] = 0.0f;
453  n++;
454 
455  x = maxrad * sin(phi);
456  z = maxrad * cos(phi);
457 
458  vset->loc[n] = Vec3(x, y, z);
459  vset->nrm[n] = Vec3(0, -1, 0);
460  vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
461  vset->tv[n] = 1.0f;
462  n++;
463 
464  phi += dphi;
465  }
466 
467  // edge of ring:
468  phi = 0;
469  for (j = 0; j < rsections; j++) {
470  double x = maxrad * sin(phi);
471  double z = maxrad * cos(phi);
472 
473  Point normal = Point(x,0,z);
474  normal.Normalize();
475 
476  double thickness = maxrad/333;
477 
478  vset->loc[n] = Vec3(x, y+thickness, z);
479  vset->nrm[n] = normal;
480  vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
481  vset->tv[n] = 1.0f;
482  n++;
483 
484  vset->loc[n] = Vec3(x, y-thickness, z);
485  vset->nrm[n] = normal;
486  vset->tu[n] = (j & 1) ? 1.0f : 0.0f;
487  vset->tv[n] = 1.0f;
488  n++;
489 
490  phi += dphi;
491  }
492  }
493 
494  for (i = 0; i < npolys; i++) {
495  polys[i].nverts = 3;
496  polys[i].vertex_set = vset;
497  polys[i].material = mtl_surf;
498  }
499 
500  // Generate triangles for top and bottom caps.
501  for (i = 0; i < nsections; i++) {
502  Poly& p0 = polys[i];
503  p0.verts[2] = i;
504  p0.verts[1] = sect_verts + i;
505  p0.verts[0] = sect_verts + ((i+1) % sect_verts);
506 
507  Poly& p1 = polys[ppolys - nsections + i];
508  p1.verts[2] = pverts - 1 - i;
509  p1.verts[1] = pverts - 1 - sect_verts - i;
510  p1.verts[0] = pverts - 2 - sect_verts - i;
511 
512  surface->AddIndices(6);
513  }
514 
515  // Generate triangles for the planetary rings
516  m = sect_verts; // first vertex in current ring
517  n = nsections; // triangle being generated, skip the top cap
518 
519  for (i = 0; i < nrings; i++) {
520  for (j = 0; j < nsections; j++) {
521  Poly& p0 = polys[n];
522  p0.nverts = 4;
523  p0.verts[3] = m + j;
524  p0.verts[2] = m + (sect_verts) + j;
525  p0.verts[1] = m + (sect_verts) + ((j + 1) % (sect_verts));
526  p0.verts[0] = m + ((j + 1) % (sect_verts));
527  n++;
528 
529  surface->AddIndices(6);
530  }
531 
532  m += sect_verts;
533  }
534 
535  if (averts && apolys && mtl_limb) {
536  for (i = 0; i < pverts; i++) {
537  vset->loc[averts + i] = vset->loc[i];
538  vset->nrm[averts + i] = vset->nrm[i];
539  }
540 
541  for (i = 0; i < ppolys; i++) {
542  Poly& p0 = polys[i];
543  Poly& p1 = polys[apolys + i];
544 
545  p1.vertex_set = vset;
546  p1.material = mtl_limb;
547 
548  p1.nverts = p0.nverts;
549  p1.verts[0] = p0.verts[0];
550  p1.verts[1] = p0.verts[1];
551  p1.verts[2] = p0.verts[2];
552  p1.verts[3] = p0.verts[3];
553 
554  surface->AddIndices(p1.nverts == 3 ? 3 : 6);
555  }
556  }
557 
558  if (has_ring) {
559  // Generate quads for the rings
560  m = ring_verts; // first vertex in top of ring, after planet verts
561  n = ring_polys; // quad being generated, after planet polys
562 
563  // top of ring:
564  for (j = 0; j < rsections; j++) {
565  Poly& p0 = polys[n];
566  p0.nverts = 4;
567  p0.material = mtl_ring;
568 
569  p0.verts[3] = m + 2*j;
570  p0.verts[2] = m + 2*j + 1;
571  p0.verts[1] = m + ((2*j + 3) % (rsections*2));
572  p0.verts[0] = m + ((2*j + 2) % (rsections*2));
573 
574  surface->AddIndices(6);
575 
576  n++;
577  }
578 
579  // bottom of ring:
580  // first vertex in bottom of ring, after top ring verts
581  m = ring_verts + 2*rsections;
582 
583  for (j = 0; j < rsections; j++) {
584  Poly& p0 = polys[n];
585  p0.nverts = 4;
586  p0.material = mtl_ring;
587 
588  p0.verts[0] = m + 2*j;
589  p0.verts[1] = m + 2*j + 1;
590  p0.verts[2] = m + ((2*j + 3) % (rsections*2));
591  p0.verts[3] = m + ((2*j + 2) % (rsections*2));
592 
593  surface->AddIndices(6);
594 
595  n++;
596  }
597 
598  // edge of ring:
599  // first vertex in edge of ring, after bottom ring verts
600  m = ring_verts + 4*rsections;
601 
602  for (j = 0; j < rsections; j++) {
603  Poly& p0 = polys[n];
604  p0.nverts = 4;
605  p0.material = mtl_ring;
606 
607  p0.verts[3] = m + 2*j;
608  p0.verts[2] = m + 2*j + 1;
609  p0.verts[1] = m + ((2*j + 3) % (rsections*2));
610  p0.verts[0] = m + ((2*j + 2) % (rsections*2));
611 
612  surface->AddIndices(6);
613 
614  n++;
615  }
616  }
617 
618  // then assign them to cohesive segments:
619  Segment* segment = 0;
620 
621  for (n = 0; n < npolys; n++) {
622  Poly& poly = polys[n];
623  poly.plane = Plane(vset->loc[poly.verts[0]],
624  vset->loc[poly.verts[2]],
625  vset->loc[poly.verts[1]]);
626 
627  if (segment && segment->material == polys[n].material) {
628  segment->npolys++;
629  }
630  else {
631  segment = new(__FILE__,__LINE__) Segment;
632 
633  segment->npolys = 1;
634  segment->polys = &polys[n];
635  segment->material = segment->polys->material;
636 
637  surface->GetSegments().append(segment);
638  }
639  }
640 
641  model->AddSurface(surface);
642 }
643 
644 
645 int
647 bool treat_translucent_polys_as_solid)
648 {
649  // compute leading edge of ray:
650  Point dst = Q + w*len;
651 
652  // check right angle spherical distance:
653  Point d0 = loc - Q;
654  Point d1 = d0.cross(w);
655  double dlen = d1.length(); // distance of point from line
656 
657  if (dlen > body_rad) // clean miss
658  return 0; // (no impact)
659 
660  // possible collision course...
661  Point d2 = Q + w * (d0 * w);
662 
663  // so check the leading edge:
664  Point delta0 = dst - loc;
665 
666  if (delta0.length() > radius) {
667  // and the endpoints:
668  Point delta1 = d2 - Q;
669  Point delta2 = dst - Q;
670 
671  // if d2 is not between Q and dst, we missed:
672  if (delta1 * delta2 < 0 ||
673  delta1.length() > delta2.length()) {
674  return 0;
675  }
676  }
677 
678  return 1;
679 }
680 
681 void
683 {
684  daytime = d;
685 
686  if (daytime) {
689  }
690 
691  else {
694  }
695 }
696 
697 void
699 {
700  star_system = system;
701 }
702 
703 // +--------------------------------------------------------------------+
704 
705 void
706 PlanetRep::Render(Video* video, DWORD flags)
707 {
708  Solid::Render(video, flags);
709 
710  /***
711  *** DEBUG
712  ***
713 
714 Matrix orient = Orientation();
715 orient.Transpose();
716 
717 video->SetObjTransform(orient, Location());
718 
719 Surface* surf = model->GetSurfaces().first();
720 Poly* polys = surf->GetPolys();
721 
722 for (int i = 0; i < 5; i++)
723  video->DrawPolyOutline(polys + i);
724 /***/
725 }
726