Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TerrainPatch.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2005. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: TerrainPatch.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12 */
13 
14 #include "MemDebug.h"
15 #include "Terrain.h"
16 #include "TerrainLayer.h"
17 #include "TerrainPatch.h"
18 #include "TerrainRegion.h"
19 #include "Sim.h"
20 
21 #include "Light.h"
22 #include "CameraView.h"
23 #include "Projector.h"
24 #include "Bitmap.h"
25 #include "DataLoader.h"
26 #include "Game.h"
27 #include "Scene.h"
28 #include "Water.h"
29 
30 // +====================================================================+
31 
32 // #define DEBUG_DETAIL 1
33 
34 // +====================================================================+
35 
36 const int MAX_DETAIL = 4;
37 const int PATCH_SIZE = 17;
38 const int HALF_PATCH_SIZE = 8;
40 
41 static bool illuminating = false;
42 
43 // +--------------------------------------------------------------------+
44 
45 TerrainPatch::TerrainPatch(Terrain* terr,const Bitmap* patch, const Rect& r,
46 const Point& p1, const Point& p2)
47 : ndetail(0), terrain(terr), rect(r), water(0), min_height(1e9), max_height(-1e9)
48 {
49  luminous = true; // we will do our own lighting
50  own_model = false; // manage the model lifetimes in this derived class
51 
53  scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1);
54  mtnscale = p2.y - p1.y;
55  base = p1.y;
56  size = p2.x - p1.x;
57 
58  ZeroMemory(detail_levels, sizeof(detail_levels));
59 
60  terrain_width = patch->Width();
61 
62  loc = (p1 + p2) * 0.5;
63  loc.y = base;
64 
65  radius = (float) (size * 0.75);
66  heights = new(__FILE__,__LINE__) float[MAX_VERTS];
67 
68  float* pHeight = heights;
69  int tscale = rect.w / (PATCH_SIZE-1);
70  int i, j;
71 
72  for (i = 0; i < PATCH_SIZE; i++) {
73  int ty = rect.y + i * tscale;
74 
75  if (ty < 0)
76  ty = 0;
77 
78  if (ty > patch->Height()-1)
79  ty = patch->Height()-1;
80 
81  for (j = 0; j < PATCH_SIZE; j++) {
82  int tx = rect.x + (PATCH_SIZE-1 - j) * tscale;
83 
84  if (tx < 0)
85  tx = 0;
86 
87  if (tx > patch->Width()-1)
88  tx = patch->Width()-1;
89 
90  int red = patch->GetColor(tx,ty).Red();
91  float alt = (float) (red * mtnscale);
92 
93  if (alt < min_height)
94  min_height = alt;
95  if (alt > max_height)
96  max_height = alt;
97 
98  if (terrain->WaterTexture() && red < 2)
99  alt = -5000.0f;
100 
101  *pHeight++ = alt;
102  }
103  }
104 
105  Material* mtl = new(__FILE__,__LINE__) Material;
106  mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f);
107  mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f);
108  mtl->Ks = Color::Black;
109 
110  mtl->tex_diffuse = terrain->Texture();
111 
112  materials.append(mtl);
113 
115  for (i = 0; i < layers.size(); i++) {
116  Bitmap* tex0 = layers.at(i)->GetTileTexture();
117  Bitmap* tex1 = 0;
118  Bitmap* texd = layers.at(i)->GetDetailTexture();
119 
120  if (i < layers.size()-1)
121  tex1 = layers.at(i+1)->GetTileTexture();
122 
123  if (!texd)
124  texd = terrain->DetailTexture(0);
125 
126  mtl = new(__FILE__,__LINE__) Material;
127  mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f);
128  mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f);
129  mtl->Ks = Color::Black;
130 
131  if ((i & 1) != 0) {
132  mtl->tex_diffuse = tex1;
133  mtl->tex_alternate = tex0;
134  }
135  else {
136  mtl->tex_diffuse = tex0;
137  mtl->tex_alternate = tex1;
138  }
139 
140  mtl->tex_detail = texd;
141 
142  materials.append(mtl);
143  }
144 
145  for (i = 0; i <= max_detail; i++)
146  BuildDetailLevel(i);
147 
148  model = detail_levels[1];
149 }
150 
151 // +--------------------------------------------------------------------+
152 
154 const Rect& r,
155 const Point& p1,
156 const Point& p2,
157 double sea_level)
158 : ndetail(0), terrain(terr), rect(r), water(0)
159 {
160  luminous = true; // water is lit by the graphics engine
161  own_model = false; // manage the model lifetimes in this derived class
162 
164  scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1);
165  mtnscale = 0;
166  base = sea_level;
167  size = p2.x - p1.x;
168 
169  ZeroMemory(detail_levels, sizeof(detail_levels));
170 
171  terrain_width = 0;
172 
173  loc = (p1 + p2) * 0.5;
174  loc.y = base;
175 
176  radius = (float) (size * 0.75);
177  heights = new(__FILE__,__LINE__) float[MAX_VERTS];
178 
179  float* pHeight = heights;
180  int i, j;
181 
182  for (i = 0; i < PATCH_SIZE; i++) {
183  for (j = 0; j < PATCH_SIZE; j++) {
184  *pHeight++ = (float) sea_level;
185  }
186  }
187 
188  Material* mtl = new(__FILE__,__LINE__) Material;
189  mtl->Ka = Color::Gray;
190  mtl->Kd = Color::White;
191  mtl->Ks = Color::White;
192  mtl->power = 30.0f;
193  mtl->shadow = false;
194  mtl->tex_diffuse = terrain->WaterTexture();
195  //strcpy_s(mtl->shader, "WaterReflections");
196  materials.append(mtl);
197 
198  water = terrain->GetWater(1);
199 
200  for (i = 0; i <= max_detail; i++)
201  BuildDetailLevel(i);
202 
203  model = detail_levels[1];
204 }
205 
206 // +--------------------------------------------------------------------+
207 
209 {
210  delete [] heights;
211 
212  for (int i = 0; i < MAX_LOD; i++) {
213  Model* m = detail_levels[i];
214 
215  if (m) {
216  m->GetMaterials().clear();
217  delete m;
218  }
219  }
220 
221  materials.destroy();
222 }
223 
224 // +--------------------------------------------------------------------+
225 
226 void
227 TerrainPatch::SetScales(double s, double m, double b)
228 {
229  scale = s;
230  mtnscale = m;
231  base = b;
232 }
233 
234 // +--------------------------------------------------------------------+
235 
236 static int mcomp(const void* a, const void* b)
237 {
238  Poly* pa = (Poly*) a;
239  Poly* pb = (Poly*) b;
240 
241  if (pa->sortval == pb->sortval)
242  return 0;
243 
244  if (pa->sortval < pb->sortval)
245  return 1;
246 
247  return -1;
248 }
249 
250 
251 static void bisect(VertexSet* vset, int v[4])
252 {
253  double d1 = fabs(vset->loc[ v[0] ].y -
254  vset->loc[ v[3] ].y);
255 
256  double d2 = fabs(vset->loc[ v[1] ].y -
257  vset->loc[ v[2] ].y);
258 
259  if (d2 < 0.7*d1) {
260  int odd[4] = { v[1], v[3], v[0], v[2] };
261  for (int i = 0; i < 4; i++)
262  v[i] = odd[i];
263  }
264 }
265 
266 bool
268 {
269  int i, j;
270 
271  int detail_size = 1 << level;
272  int ds1 = detail_size+1;
273 
274  if (detail_size > PATCH_SIZE)
275  return false;
276 
277  Model* model = new(__FILE__,__LINE__) Model;
278  detail_levels[level] = model;
279 
280  model->SetLuminous(luminous);
281  model->SetDynamic(true);
282 
283  const int NUM_STRIPS = 4;
284  const int NUM_INDICES_TRI = 3;
285  const int NUM_INDICES_QUAD = 6;
286 
287  int nverts = ds1*ds1 + ds1*2*NUM_STRIPS;
288  int npolys = detail_size*detail_size*2;
289  int strip_len = detail_size;
290  int total = npolys + strip_len*NUM_STRIPS;
291 
292  if (water) {
293  nverts = ds1*ds1;
294  strip_len = 0;
295  total = npolys;
296  }
297 
298  Surface* s = new(__FILE__,__LINE__) Surface;
299  VertexSet* vset = 0;
300 
301  if (s) {
302  s->SetName("default");
303  s->CreateVerts(nverts);
304  s->CreatePolys(total);
305  s->AddIndices(npolys*NUM_INDICES_TRI + strip_len*NUM_STRIPS*NUM_INDICES_QUAD);
306 
307  vset = s->GetVertexSet();
308  if (!water)
310 
311  ZeroMemory(vset->loc, nverts * sizeof(Vec3));
312  ZeroMemory(vset->diffuse, nverts * sizeof(DWORD));
313  ZeroMemory(vset->specular, nverts * sizeof(DWORD));
314  ZeroMemory(vset->tu, nverts * sizeof(float));
315  ZeroMemory(vset->tv, nverts * sizeof(float));
316  if (!water) {
317  ZeroMemory(vset->tu1, nverts * sizeof(float));
318  ZeroMemory(vset->tv1, nverts * sizeof(float));
319  }
320  ZeroMemory(vset->rw, nverts * sizeof(float));
321 
322  // initialize vertices
323  Vec3* pVert = vset->loc;
324  float* pTu = vset->tu;
325  float* pTv = vset->tv;
326  float* pTu1 = vset->tu1;
327  float* pTv1 = vset->tv1;
328  DWORD* pSpec = vset->specular;
329 
330  int dscale = (PATCH_SIZE-1)/detail_size;
331  double dt = 0.0625 / (ds1-1); // terrain texture scale
332  double dtt = 2.0000 / (ds1-1); // tile texture scale
333  double tu0 = (double) rect.x / rect.w / 16.0 + 1.0/16.0;
334  double tv0 = (double) rect.y / rect.h / 16.0;
335 
336  // surface verts
337  for (i = 0; i < ds1; i++) {
338  for (j = 0; j < ds1; j++) {
339  *pVert = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
340  (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
341  (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
342 
343  if (level >= 2) {
344  *pTu++ = (float) (-j*dtt);
345  *pTv++ = (float) ( i*dtt);
346 
347  if (level >= 4 && !water) {
348  *pTu1++ = (float) (-j*dtt*3);
349  *pTv1++ = (float) ( i*dtt*3);
350  }
351 
352  *pSpec++ = BlendValue(pVert->y);
353  }
354 
355  else {
356  *pTu++ = (float) (tu0 - j*dt);
357  *pTv++ = (float) (tv0 + i*dt);
358  }
359 
360  pVert++;
361  }
362  }
363 
364  if (!water) {
365  // strip 1 & 2 verts
366  for (i = 0; i < ds1; i += detail_size) {
367  for (j = 0; j < ds1; j++) {
368  Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
369  (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
370  (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
371 
372  *pVert++ = vl;
373 
374  DWORD blend = 0;
375 
376  if (level >= 2) {
377  blend = BlendValue(vl.y);
378 
379  *pSpec++ = blend;
380  *pTu++ = (float) (-j*dtt);
381  *pTv++ = (float) ( i*dtt);
382  }
383 
384  else {
385  *pTu++ = (float) (tu0 - j*dt);
386  *pTv++ = (float) (tv0 + i*dt);
387  }
388 
389  vl.y = -5000.0f;
390 
391  *pVert++ = vl;
392 
393  if (level >= 2) {
394  *pSpec++ = blend;
395  *pTu++ = (float) (-j*dtt);
396  *pTv++ = (float) ( i*dtt);
397  }
398 
399  else {
400  *pTu++ = (float) (tu0 - j*dt);
401  *pTv++ = (float) (tv0 + i*dt);
402  }
403  }
404  }
405 
406  // strip 3 & 4 verts
407  for (j = 0; j < ds1; j += detail_size) {
408  for (i = 0; i < ds1; i++) {
409  Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
410  (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
411  (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
412 
413  *pVert++ = vl;
414 
415  DWORD blend = 0;
416 
417  if (level >= 2) {
418  blend = BlendValue(vl.y);
419 
420  *pSpec++ = blend;
421  *pTu++ = (float) (-j*dtt);
422  *pTv++ = (float) ( i*dtt);
423  }
424 
425  else {
426  *pTu++ = (float) (tu0 - j*dt);
427  *pTv++ = (float) (tv0 + i*dt);
428  }
429 
430  vl.y = -5000.0f;
431 
432  *pVert++ = vl;
433 
434  if (level >= 2) {
435  *pSpec++ = blend;
436  *pTu++ = (float) (-j*dtt);
437  *pTv++ = (float) ( i*dtt);
438  }
439 
440  else {
441  *pTu++ = (float) (tu0 - j*dt);
442  *pTv++ = (float) (tv0 + i*dt);
443  }
444  }
445  }
446  }
447 
448  Material* m = materials.first();
449 
450  // initialize the polys
451  for (i = 0; i < npolys; i++) {
452  Poly* p = s->GetPolys() + i;
453  p->nverts = 3;
454  p->vertex_set = vset;
455  p->visible = 1;
456  p->sortval = 0;
457  p->material = m;
458 
459  if (level >= 2 && !water) {
460  p->material = materials.at(1);
461  p->sortval = 1;
462  }
463  }
464 
465  for (i = npolys; i < total; i++) {
466  Poly* p = s->GetPolys() + i;
467  p->nverts = 4;
468  p->vertex_set = vset;
469  p->visible = 1;
470  p->sortval = 0;
471  p->material = m;
472  }
473 
474  int index = 0;
475 
476  // build main patch polys:
477  for (i = 0; i < detail_size; i++) {
478  for (j = 0; j < detail_size; j++) {
479  int v[4] = {
480  (ds1 * (i ) + (j )),
481  (ds1 * (i ) + (j+1)),
482  (ds1 * (i+1) + (j )),
483  (ds1 * (i+1) + (j+1)) };
484 
485  bisect(vset, v);
486 
487  // first triangle
488  Poly* p = s->GetPolys() + index++;
489  p->verts[0] = v[0];
490  p->verts[1] = v[1];
491  p->verts[2] = v[3];
492 
493  if (level >= 2 && !water) {
494  int layer = CalcLayer(p) + 1;
495  p->material = materials.at(layer);
496  p->sortval = layer;
497  }
498 
499  // second triangle
500  p = s->GetPolys() + index++;
501  p->verts[0] = v[0];
502  p->verts[1] = v[3];
503  p->verts[2] = v[2];
504 
505  if (level >= 2 && !water) {
506  int layer = CalcLayer(p) + 1;
507  p->material = materials.at(layer);
508  p->sortval = layer;
509  }
510  }
511  }
512 
513  // build vertical edge strip polys:
514 
515  if (!water) {
516  for (i = 0; i < NUM_STRIPS; i++) {
517  Poly* p = s->GetPolys() + npolys + i*strip_len;
518  int base_index = ds1*ds1 + ds1*2*i;
519 
520  for (j = 0; j < strip_len; j++) {
521  int v = base_index + j * 2;
522  p->nverts = 4;
523 
524  if (i == 1 || i == 2) {
525  p->verts[0] = v;
526  p->verts[1] = v+2;
527  p->verts[2] = v+3;
528  p->verts[3] = v+1;
529  }
530 
531  else {
532  p->verts[0] = v;
533  p->verts[1] = v+1;
534  p->verts[2] = v+3;
535  p->verts[3] = v+2;
536  }
537 
538  if (level >= 2) {
539  int layer = CalcLayer(p) + 1;
540  p->material = materials.at(layer);
541  p->sortval = layer;
542  }
543 
544  p++;
545  }
546  }
547  }
548 
549  // update the poly planes:
550  for (i = 0; i < total; i++) {
551  Poly* p = s->GetPolys() + i;
552  Plane& plane = p->plane;
553  WORD* v = p->verts;
554 
555  plane = Plane(vset->loc[v[0]] + loc,
556  vset->loc[v[1]] + loc,
557  vset->loc[v[2]] + loc);
558  }
559 
560  s->Normalize();
561 
562  // create continguous segments for each material:
563  // sort the polys by material index:
564  qsort((void*) s->GetPolys(), s->NumPolys(), sizeof(Poly), mcomp);
565 
566  // then assign them to cohesive segments:
567  Segment* segment = 0;
568  Poly* spolys = s->GetPolys();
569 
570  for (int n = 0; n < s->NumPolys(); n++) {
571  if (segment && segment->material == spolys[n].material) {
572  segment->npolys++;
573  }
574  else {
575  segment = 0;
576  }
577 
578  if (!segment) {
579  segment = new(__FILE__,__LINE__) Segment;
580 
581  segment->npolys = 1;
582  segment->polys = &spolys[n];
583  segment->material = segment->polys->material;
584  segment->model = model;
585 
586  s->GetSegments().append(segment);
587  }
588  }
589 
590  Solid::EnableCollision(false);
591  model->AddSurface(s);
593 
594  // copy vertex normals:
595  const Vec3B* tnorms = terrain->Normals();
596 
597  for (i = 0; i < ds1; i++) {
598  for (j = 0; j < ds1; j++) {
599 
600  if (water) {
601  vset->nrm[i*ds1+j] = Point(0,1,0);
602  }
603 
604  // blend adjacent normals:
605  else if (dscale > 1) {
606  Point normal;
607 
608  // but don't blend more than 16 normals per vertex:
609  int step = 1;
610  if (dscale > 4)
611  step = dscale / 4;
612 
613  for (int dy = -dscale/2; dy < dscale/2; dy += step) {
614  for (int dx = -dscale/2; dx < dscale/2; dx += step) {
615  int ix = rect.x + (ds1-1-j)*dscale + dx;
616  int iy = rect.y + i*dscale + dy;
617 
618  if (ix < 0) ix = 0;
619  if (ix > terrain_width-1) ix = terrain_width-1;
620  if (iy < 0) iy = 0;
621  if (iy > terrain_width-1) iy = terrain_width-1;
622 
623  Vec3B vbn = tnorms[iy*terrain_width + ix];
624  normal += Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0);
625  }
626  }
627 
628  normal.Normalize();
629  vset->nrm[i*ds1+j] = normal;
630  }
631 
632  // just copy the one normal:
633  else {
634  Vec3B vbn = tnorms[(rect.y + i*dscale)*terrain_width + (rect.x + (ds1-1-j) * dscale)];
635  Point normal = Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0);
636  vset->nrm[i*ds1+j] = normal;
637  }
638  }
639  }
640 
641  if (!water) {
642  pVert = &vset->nrm[ds1*ds1];
643 
644  // strip 1 & 2 verts
645  for (i = 0; i < ds1; i += detail_size) {
646  for (j = 0; j < ds1; j++) {
647  Vec3 vn = vset->nrm[i*ds1 + j];
648 
649  *pVert++ = vn;
650  *pVert++ = vn;
651  }
652  }
653 
654  // strip 3 & 4 verts
655  for (j = 0; j < ds1; j += detail_size) {
656  for (i = 0; i < ds1; i++) {
657  Vec3 vn = vset->nrm[i*ds1 + j];
658 
659  *pVert++ = vn;
660  *pVert++ = vn;
661  }
662  }
663  }
664  }
665 
666  if (level > max_detail)
667  max_detail = level;
668 
669  return true;
670 }
671 
672 // +--------------------------------------------------------------------+
673 
674 DWORD
676 {
677  if (terrain && y >= 0 && !water) {
678  // find the proper layer:
679  for (int i = 0; i < terrain->GetLayers().size(); i++) {
680  TerrainLayer* layer = terrain->GetLayers().at(i);
681 
682  if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) {
683  double scale = (y-layer->GetMinHeight()) / (layer->GetMaxHeight()-layer->GetMinHeight());
684 
685  if (scale < 0.2)
686  scale = 0;
687  else if (scale > 0.8)
688  scale = 1;
689  else
690  scale = (scale - 0.2) / 0.6;
691 
692  if ((i & 1) == 0) {
693  scale = 1 - scale;
694  }
695 
696  DWORD val = (DWORD) (scale*255);
697 
698  return val << 24;
699  }
700  }
701  }
702 
703  return 0;
704 }
705 
706 int
708 {
709  if (terrain && poly) {
710  if (water)
711  return 0;
712 
713  double y = 1e6;
714 
715  for (int i = 0; i < poly->nverts; i++) {
716  double h = poly->vertex_set->loc[ poly->verts[i] ].y;
717 
718  if (h >= 0 && h < y) {
719  y = h;
720  }
721  }
722 
723  if (y <= terrain->GetLayers().first()->GetMinHeight())
724  return 0;
725 
726  for (int i = 0; i < terrain->GetLayers().size(); i++) {
727  TerrainLayer* layer = terrain->GetLayers().at(i);
728 
729  if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) {
730  return i;
731  }
732  }
733  }
734 
735  return -1;
736 }
737 
738 // +--------------------------------------------------------------------+
739 
740 void
742 {
743  if (water && model && model->NumVerts() > 1) {
744  Surface* s = model->GetSurfaces().first();
745  if (s) {
746  VertexSet* vset = s->GetVertexSet();
747  for (int i = 0; i < vset->nverts; i++)
748  vset->loc[i].y = 0.0f;
749 
750  water->UpdateSurface(eyePos, vset);
751  }
752  }
753 }
754 
755 // +--------------------------------------------------------------------+
756 
757 int
759 {
760  return 0;
761 }
762 
763 // +--------------------------------------------------------------------+
764 
765 void
767 {
768  // This is where all the work is done,
769  // Delegate to the overall terrain to
770  // compute a detail level for every patch:
771 
772  if (terrain) {
773  terrain->SelectDetail(projector);
774  }
775 
776  // Build detail levels on demand:
777 
778  if (detail_levels[ndetail] == 0)
780 }
781 
782 void
784 {
785  if (nd >= 0 && nd <= max_detail) {
786  if (ndetail != nd)
788 
789  ndetail = nd;
790 
791  // build detail levels on demand:
792  if (detail_levels[ndetail] == 0)
794 
796 
797  if (water)
799  }
800 }
801 
802 // +--------------------------------------------------------------------+
803 
804 void
806 {
807  if (!model || model->NumVerts() < 1) return;
808  Surface* s = model->GetSurfaces().first();
809  if (!s) return;
810 
811  illuminating = true;
812 
813  // clear the solid lights to ambient:
814  VertexSet* vset = s->GetVertexSet();
815  int nverts = vset->nverts;
816  DWORD aval = ambient.Value();
817 
818  for (int i = 0; i < nverts; i++) {
819  vset->diffuse[i] = aval;
820  }
821 
822  TerrainRegion* trgn = terrain->GetRegion();
823  bool eclipsed = false;
824  bool first = terrain->IsFirstPatch(this);
825 
826  if (trgn && !first) {
827  eclipsed = trgn->IsEclipsed();
828  }
829 
830  // for sun and back lights:
831  ListIter<Light> iter = lights;
832  while (++iter) {
833  Light* light = iter.value();
834 
835  if (!light->IsDirectional()) // only do sun and
836  continue; // back lights
837 
838  if (light->CastsShadow() && first) {
839  eclipsed = light->Location().y < -100 || // has sun set, or
840 
841  scene->IsLightObscured(vset->loc[0], // is sun in eclipse
842  light->Location(), // by orbital body
843  radius); // such as a moon?
844  }
845 
846  if (!light->CastsShadow() || !eclipsed) {
847  Vec3 vl = light->Location();
848  vl.Normalize();
849 
850  for (int i = 0; i < nverts; i++) {
851  Vec3& nrm = vset->nrm[i];
852  double val = 0;
853  double gain = vl * nrm;
854 
855  if (gain > 0) {
856  val = light->Intensity() * (0.85 * gain);
857 
858  if (val > 1)
859  val = 1;
860  }
861 
862  if (val > 0.01)
863  vset->diffuse[i] = ((light->GetColor().dim(val)) + vset->diffuse[i]).Value();
864  }
865  }
866  }
867 
868  // combine blend weights:
869  if (ndetail >= 2) {
870  for (int i = 0; i < nverts; i++) {
871  vset->diffuse[i] = vset->specular[i] | (vset->diffuse[i] & 0x00ffffff);
872  }
873  }
874 
875  if (trgn && first) {
876  trgn->SetEclipsed(eclipsed);
877  }
878 
880  illuminating = false;
881 }
882 
883 // +--------------------------------------------------------------------+
884 
885 void
886 TerrainPatch::Render(Video* video, DWORD flags)
887 {
888  if (max_height < 0)
889  return;
890 
891  if (flags & RENDER_ADDITIVE)
892  return;
893 
894  if (!model)
895  model = detail_levels[0];
896 
897  if (scene) {
898  /***
899  *** TWO PASS LIGHTING FOR TERRAIN SHADOWS DOESN'T
900  *** WORK - IT MESSES UP THE HARDWARE FOG CALCS
901  ***
902  *** PLUS, IT'S INCREDIBLY SLOW!!!
903  ***/
904 
905  if ((flags & RENDER_ADD_LIGHT) != 0)
906  return;
907 
908  if (water) {
909  UpdateSurfaceWaves(Vec3(0,0,0));
911  }
912  else {
914  }
915  }
916  else {
917  if ((flags & RENDER_ADD_LIGHT) != 0)
918  return;
919  }
920 
921  Bitmap* details[16];
922  ZeroMemory(details, sizeof(details));
923 
924  if (ndetail < 3) {
925  for (int i = 0; i < 16 && i < materials.size(); i++) {
926  Material* mtl = materials[i];
927 
928  if (mtl->tex_detail) {
929  details[i] = mtl->tex_detail;
930  mtl->tex_detail = 0;
931  }
932  }
933  }
934 
935  double visibility = terrain->GetRegion()->GetWeather().Visibility();
936  FLOAT fog_density = (FLOAT) (terrain->GetRegion()->FogDensity() * 2.5e-5 * 1/visibility);
937 
938  video->SetRenderState(Video::LIGHTING_ENABLE, false);
939  video->SetRenderState(Video::SPECULAR_ENABLE, false); //water != 0);
940  video->SetRenderState(Video::FOG_ENABLE, true);
942  video->SetRenderState(Video::FOG_DENSITY, *((DWORD*) &fog_density));
943 
944  // Too bad this doesn't work right. But it makes the
945  // ground mostly disappear. :-(
946  //
947  //video->SetRenderState(Video::Z_BIAS, -2);
948 
949  Solid::Render(video, flags);
950 
953  video->SetRenderState(Video::FOG_ENABLE, false);
954  //video->SetRenderState(Video::Z_BIAS, 0);
955 
956  if (ndetail < 3) {
957  for (int i = 0; i < 16 && i < materials.size(); i++) {
958  Material* mtl = materials[i];
959 
960  if (details[i] && !mtl->tex_detail) {
961  mtl->tex_detail = details[i];
962  }
963  }
964  }
965 }
966 
967 // +--------------------------------------------------------------------+
968 
969 int
970 TerrainPatch::CheckRayIntersection(Point obj_pos, Point dir, double len, Point& ipt, bool ttpas)
971 {
972  Point light_pos = obj_pos + dir * len;
973  int impact = light_pos.y < -100;
974 
975  // Special case for illumination -
976  // just check if sun is above or below the horizon:
977 
978  if (illuminating || impact) {
979  return impact;
980  }
981 
982  if (obj_pos.x != 0 || obj_pos.y != 0 || obj_pos.z != 0) {
983  return impact;
984  }
985 
986  // the rest of this code is only used for eclipsing
987  // the solar lens flare:
988 
989  // check right angle spherical distance:
990  Point d0 = loc;
991  Point d1 = d0.cross(dir);
992  double dlen = d1.length(); // distance of point from line
993 
994  if (dlen > radius) // clean miss
995  return 0; // (no impact)
996 
997  // make sure some part of this patch falls between
998  // the camera and the sun:
999 
1000  Point closest = loc + dir * radius;
1001 
1002  if (closest * dir < 0)
1003  return 0;
1004 
1005  // probable hit at this point...
1006  // test for polygon intersection:
1007  if (!model)
1008  return 0;
1009 
1010  Surface* s = model->GetSurfaces().first();
1011 
1012  if (!s)
1013  return 0;
1014 
1015 
1016  // transform ray into object space:
1017  Matrix xform(Orientation());
1018 
1019  Vec3 tmp = dir;
1020 
1021  dir.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
1022  dir.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
1023  dir.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
1024 
1025  tmp = obj_pos-loc;
1026 
1027  obj_pos.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
1028  obj_pos.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
1029  obj_pos.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
1030 
1031  double min = 2 * len;
1032 
1033  // check each polygon:
1034  Poly* p = s->GetPolys();
1035 
1036  for (int i = 0; i < s->NumPolys(); i++) {
1037  Point v = p->plane.normal;
1038  double d = p->plane.distance;
1039 
1040  double denom = dir*v;
1041 
1042  if (denom < -1.0e-5) {
1043  Point P = v * d;
1044  double ilen = ((P-obj_pos)*v)/denom;
1045 
1046  if (ilen > 0 && ilen < min) {
1047  Point intersect = obj_pos + dir * ilen;
1048 
1049  if (p->Contains(intersect)) {
1050  ipt = intersect;
1051  min = ilen;
1052  impact = 1;
1053  }
1054  }
1055  }
1056 
1057  p++;
1058  }
1059 
1060  // xform impact point back into world coordinates:
1061 
1062  if (impact) {
1063  ipt = (ipt * Orientation()) + loc;
1064  }
1065 
1066  return impact;
1067 }
1068 
1069 // +--------------------------------------------------------------------+
1070 
1071 double
1072 TerrainPatch::Height(double x, double z) const
1073 {
1074  if (water) return base;
1075 
1076  double height = 0;
1077 
1078  x /= scale;
1079  z /= scale;
1080 
1081  int x0 = (int) x;
1082  int z0 = (int) z;
1083 
1084  if (x0 >= 0 && x0 < PATCH_SIZE && z0 >= 0 && z0 < PATCH_SIZE) {
1085  double dx = x-x0;
1086  double dz = z-z0;
1087 
1088  double h[4];
1089 
1090  h[0] = heights[(z0*PATCH_SIZE + x0)];
1091  h[1] = heights[((z0+1)*PATCH_SIZE + x0)];
1092  h[2] = heights[(z0*PATCH_SIZE + x0+1)];
1093  h[3] = heights[((z0+1)*PATCH_SIZE + x0+1)];
1094 
1095  // if level, just return height of one vertex:
1096  if (h[0] == h[1] && h[1] == h[2] && h[2] == h[3]) {
1097  height = h[0];
1098  }
1099 
1100  // if sloped, interpolate between vertex heights:
1101  else {
1102  double hl = h[0]*(1-dz) + h[1]*dz;
1103  double hr = h[2]*(1-dz) + h[3]*dz;
1104 
1105  height = hl*(1-dx) + hr*dx + 5; // fudge
1106  }
1107  }
1108 
1109  if (height < 0)
1110  height = 0;
1111 
1112  return height;
1113 }