diff options
author | milo24x7@gmail.com <milo24x7@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544> | 2013-07-07 22:08:49 +0000 |
---|---|---|
committer | milo24x7@gmail.com <milo24x7@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544> | 2013-07-07 22:08:49 +0000 |
commit | d17521c8b9085a91d08fecfd0b51bbbf7b1dccac (patch) | |
tree | 4673104b47dc68a079cac9f94deefd48dfcb66fa /Stars45/TerrainPatch.cpp | |
parent | 1de4b2bdbb019be6f1b7262c3eba5568d7682edd (diff) | |
download | starshatter-d17521c8b9085a91d08fecfd0b51bbbf7b1dccac.zip starshatter-d17521c8b9085a91d08fecfd0b51bbbf7b1dccac.tar.gz starshatter-d17521c8b9085a91d08fecfd0b51bbbf7b1dccac.tar.bz2 |
Updated open source license declaration and fixed some formatting issues.
Diffstat (limited to 'Stars45/TerrainPatch.cpp')
-rw-r--r-- | Stars45/TerrainPatch.cpp | 2250 |
1 files changed, 1137 insertions, 1113 deletions
diff --git a/Stars45/TerrainPatch.cpp b/Stars45/TerrainPatch.cpp index a03ccd6..c9108f6 100644 --- a/Stars45/TerrainPatch.cpp +++ b/Stars45/TerrainPatch.cpp @@ -1,1113 +1,1137 @@ -/* Project Starshatter 4.5
- Destroyer Studios LLC
- Copyright © 1997-2005. All Rights Reserved.
-
- SUBSYSTEM: Stars.exe
- FILE: TerrainPatch.cpp
- AUTHOR: John DiCamillo
-
-
- OVERVIEW
- ========
-*/
-
-#include "MemDebug.h"
-#include "Terrain.h"
-#include "TerrainLayer.h"
-#include "TerrainPatch.h"
-#include "TerrainRegion.h"
-#include "Sim.h"
-
-#include "Light.h"
-#include "CameraView.h"
-#include "Projector.h"
-#include "Bitmap.h"
-#include "DataLoader.h"
-#include "Game.h"
-#include "Scene.h"
-#include "Water.h"
-
-// +====================================================================+
-
-// #define DEBUG_DETAIL 1
-
-// +====================================================================+
-
-const int MAX_DETAIL = 4;
-const int PATCH_SIZE = 17;
-const int HALF_PATCH_SIZE = 8;
-const int MAX_VERTS = PATCH_SIZE * PATCH_SIZE;
-
-static bool illuminating = false;
-
-// +--------------------------------------------------------------------+
-
-TerrainPatch::TerrainPatch(Terrain* terr,const Bitmap* patch, const Rect& r,
-const Point& p1, const Point& p2)
-: ndetail(0), terrain(terr), rect(r), water(0), min_height(1e9), max_height(-1e9)
-{
- luminous = true; // we will do our own lighting
- own_model = false; // manage the model lifetimes in this derived class
-
- max_detail = Terrain::DetailLevel();
- scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1);
- mtnscale = p2.y - p1.y;
- base = p1.y;
- size = p2.x - p1.x;
-
- ZeroMemory(detail_levels, sizeof(detail_levels));
-
- terrain_width = patch->Width();
-
- loc = (p1 + p2) * 0.5;
- loc.y = base;
-
- radius = (float) (size * 0.75);
- heights = new(__FILE__,__LINE__) float[MAX_VERTS];
-
- float* pHeight = heights;
- int tscale = rect.w / (PATCH_SIZE-1);
- int i, j;
-
- for (i = 0; i < PATCH_SIZE; i++) {
- int ty = rect.y + i * tscale;
-
- if (ty < 0)
- ty = 0;
-
- if (ty > patch->Height()-1)
- ty = patch->Height()-1;
-
- for (j = 0; j < PATCH_SIZE; j++) {
- int tx = rect.x + (PATCH_SIZE-1 - j) * tscale;
-
- if (tx < 0)
- tx = 0;
-
- if (tx > patch->Width()-1)
- tx = patch->Width()-1;
-
- int red = patch->GetColor(tx,ty).Red();
- float alt = (float) (red * mtnscale);
-
- if (alt < min_height)
- min_height = alt;
- if (alt > max_height)
- max_height = alt;
-
- if (terrain->WaterTexture() && red < 2)
- alt = -5000.0f;
-
- *pHeight++ = alt;
- }
- }
-
- Material* mtl = new(__FILE__,__LINE__) Material;
- mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f);
- mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f);
- mtl->Ks = Color::Black;
-
- mtl->tex_diffuse = terrain->Texture();
-
- materials.append(mtl);
-
- List<TerrainLayer>& layers = terrain->GetLayers();
- for (i = 0; i < layers.size(); i++) {
- Bitmap* tex0 = layers.at(i)->GetTileTexture();
- Bitmap* tex1 = 0;
- Bitmap* texd = layers.at(i)->GetDetailTexture();
-
- if (i < layers.size()-1)
- tex1 = layers.at(i+1)->GetTileTexture();
-
- if (!texd)
- texd = terrain->DetailTexture(0);
-
- mtl = new(__FILE__,__LINE__) Material;
- mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f);
- mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f);
- mtl->Ks = Color::Black;
-
- if ((i & 1) != 0) {
- mtl->tex_diffuse = tex1;
- mtl->tex_alternate = tex0;
- }
- else {
- mtl->tex_diffuse = tex0;
- mtl->tex_alternate = tex1;
- }
-
- mtl->tex_detail = texd;
-
- materials.append(mtl);
- }
-
- for (i = 0; i <= max_detail; i++)
- BuildDetailLevel(i);
-
- model = detail_levels[1];
-}
-
-// +--------------------------------------------------------------------+
-
-TerrainPatch::TerrainPatch(Terrain* terr,
-const Rect& r,
-const Point& p1,
-const Point& p2,
-double sea_level)
-: ndetail(0), terrain(terr), rect(r), water(0)
-{
- luminous = true; // water is lit by the graphics engine
- own_model = false; // manage the model lifetimes in this derived class
-
- max_detail = Terrain::DetailLevel();
- scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1);
- mtnscale = 0;
- base = sea_level;
- size = p2.x - p1.x;
-
- ZeroMemory(detail_levels, sizeof(detail_levels));
-
- terrain_width = 0;
-
- loc = (p1 + p2) * 0.5;
- loc.y = base;
-
- radius = (float) (size * 0.75);
- heights = new(__FILE__,__LINE__) float[MAX_VERTS];
-
- float* pHeight = heights;
- int i, j;
-
- for (i = 0; i < PATCH_SIZE; i++) {
- for (j = 0; j < PATCH_SIZE; j++) {
- *pHeight++ = (float) sea_level;
- }
- }
-
- Material* mtl = new(__FILE__,__LINE__) Material;
- mtl->Ka = Color::Gray;
- mtl->Kd = Color::White;
- mtl->Ks = Color::White;
- mtl->power = 30.0f;
- mtl->shadow = false;
- mtl->tex_diffuse = terrain->WaterTexture();
- //strcpy_s(mtl->shader, "WaterReflections");
- materials.append(mtl);
-
- water = terrain->GetWater(1);
-
- for (i = 0; i <= max_detail; i++)
- BuildDetailLevel(i);
-
- model = detail_levels[1];
-}
-
-// +--------------------------------------------------------------------+
-
-TerrainPatch::~TerrainPatch()
-{
- delete [] heights;
-
- for (int i = 0; i < MAX_LOD; i++) {
- Model* m = detail_levels[i];
-
- if (m) {
- m->GetMaterials().clear();
- delete m;
- }
- }
-
- materials.destroy();
-}
-
-// +--------------------------------------------------------------------+
-
-void
-TerrainPatch::SetScales(double s, double m, double b)
-{
- scale = s;
- mtnscale = m;
- base = b;
-}
-
-// +--------------------------------------------------------------------+
-
-static int mcomp(const void* a, const void* b)
-{
- Poly* pa = (Poly*) a;
- Poly* pb = (Poly*) b;
-
- if (pa->sortval == pb->sortval)
- return 0;
-
- if (pa->sortval < pb->sortval)
- return 1;
-
- return -1;
-}
-
-
-static void bisect(VertexSet* vset, int v[4])
-{
- double d1 = fabs(vset->loc[ v[0] ].y -
- vset->loc[ v[3] ].y);
-
- double d2 = fabs(vset->loc[ v[1] ].y -
- vset->loc[ v[2] ].y);
-
- if (d2 < 0.7*d1) {
- int odd[4] = { v[1], v[3], v[0], v[2] };
- for (int i = 0; i < 4; i++)
- v[i] = odd[i];
- }
-}
-
-bool
-TerrainPatch::BuildDetailLevel(int level)
-{
- int i, j;
-
- int detail_size = 1 << level;
- int ds1 = detail_size+1;
-
- if (detail_size > PATCH_SIZE)
- return false;
-
- Model* model = new(__FILE__,__LINE__) Model;
- detail_levels[level] = model;
-
- model->SetLuminous(luminous);
- model->SetDynamic(true);
-
- const int NUM_STRIPS = 4;
- const int NUM_INDICES_TRI = 3;
- const int NUM_INDICES_QUAD = 6;
-
- int nverts = ds1*ds1 + ds1*2*NUM_STRIPS;
- int npolys = detail_size*detail_size*2;
- int strip_len = detail_size;
- int total = npolys + strip_len*NUM_STRIPS;
-
- if (water) {
- nverts = ds1*ds1;
- strip_len = 0;
- total = npolys;
- }
-
- Surface* s = new(__FILE__,__LINE__) Surface;
- VertexSet* vset = 0;
-
- if (s) {
- s->SetName("default");
- s->CreateVerts(nverts);
- s->CreatePolys(total);
- s->AddIndices(npolys*NUM_INDICES_TRI + strip_len*NUM_STRIPS*NUM_INDICES_QUAD);
-
- vset = s->GetVertexSet();
- if (!water)
- vset->CreateAdditionalTexCoords();
-
- ZeroMemory(vset->loc, nverts * sizeof(Vec3));
- ZeroMemory(vset->diffuse, nverts * sizeof(DWORD));
- ZeroMemory(vset->specular, nverts * sizeof(DWORD));
- ZeroMemory(vset->tu, nverts * sizeof(float));
- ZeroMemory(vset->tv, nverts * sizeof(float));
- if (!water) {
- ZeroMemory(vset->tu1, nverts * sizeof(float));
- ZeroMemory(vset->tv1, nverts * sizeof(float));
- }
- ZeroMemory(vset->rw, nverts * sizeof(float));
-
- // initialize vertices
- Vec3* pVert = vset->loc;
- float* pTu = vset->tu;
- float* pTv = vset->tv;
- float* pTu1 = vset->tu1;
- float* pTv1 = vset->tv1;
- DWORD* pSpec = vset->specular;
-
- int dscale = (PATCH_SIZE-1)/detail_size;
- double dt = 0.0625 / (ds1-1); // terrain texture scale
- double dtt = 2.0000 / (ds1-1); // tile texture scale
- double tu0 = (double) rect.x / rect.w / 16.0 + 1.0/16.0;
- double tv0 = (double) rect.y / rect.h / 16.0;
-
- // surface verts
- for (i = 0; i < ds1; i++) {
- for (j = 0; j < ds1; j++) {
- *pVert = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
- (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
- (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
-
- if (level >= 2) {
- *pTu++ = (float) (-j*dtt);
- *pTv++ = (float) ( i*dtt);
-
- if (level >= 4 && !water) {
- *pTu1++ = (float) (-j*dtt*3);
- *pTv1++ = (float) ( i*dtt*3);
- }
-
- *pSpec++ = BlendValue(pVert->y);
- }
-
- else {
- *pTu++ = (float) (tu0 - j*dt);
- *pTv++ = (float) (tv0 + i*dt);
- }
-
- pVert++;
- }
- }
-
- if (!water) {
- // strip 1 & 2 verts
- for (i = 0; i < ds1; i += detail_size) {
- for (j = 0; j < ds1; j++) {
- Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
- (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
- (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
-
- *pVert++ = vl;
-
- DWORD blend = 0;
-
- if (level >= 2) {
- blend = BlendValue(vl.y);
-
- *pSpec++ = blend;
- *pTu++ = (float) (-j*dtt);
- *pTv++ = (float) ( i*dtt);
- }
-
- else {
- *pTu++ = (float) (tu0 - j*dt);
- *pTv++ = (float) (tv0 + i*dt);
- }
-
- vl.y = -5000.0f;
-
- *pVert++ = vl;
-
- if (level >= 2) {
- *pSpec++ = blend;
- *pTu++ = (float) (-j*dtt);
- *pTv++ = (float) ( i*dtt);
- }
-
- else {
- *pTu++ = (float) (tu0 - j*dt);
- *pTv++ = (float) (tv0 + i*dt);
- }
- }
- }
-
- // strip 3 & 4 verts
- for (j = 0; j < ds1; j += detail_size) {
- for (i = 0; i < ds1; i++) {
- Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)),
- (float) (heights[i*dscale*PATCH_SIZE + j*dscale]),
- (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale)));
-
- *pVert++ = vl;
-
- DWORD blend = 0;
-
- if (level >= 2) {
- blend = BlendValue(vl.y);
-
- *pSpec++ = blend;
- *pTu++ = (float) (-j*dtt);
- *pTv++ = (float) ( i*dtt);
- }
-
- else {
- *pTu++ = (float) (tu0 - j*dt);
- *pTv++ = (float) (tv0 + i*dt);
- }
-
- vl.y = -5000.0f;
-
- *pVert++ = vl;
-
- if (level >= 2) {
- *pSpec++ = blend;
- *pTu++ = (float) (-j*dtt);
- *pTv++ = (float) ( i*dtt);
- }
-
- else {
- *pTu++ = (float) (tu0 - j*dt);
- *pTv++ = (float) (tv0 + i*dt);
- }
- }
- }
- }
-
- Material* m = materials.first();
-
- // initialize the polys
- for (i = 0; i < npolys; i++) {
- Poly* p = s->GetPolys() + i;
- p->nverts = 3;
- p->vertex_set = vset;
- p->visible = 1;
- p->sortval = 0;
- p->material = m;
-
- if (level >= 2 && !water) {
- p->material = materials.at(1);
- p->sortval = 1;
- }
- }
-
- for (i = npolys; i < total; i++) {
- Poly* p = s->GetPolys() + i;
- p->nverts = 4;
- p->vertex_set = vset;
- p->visible = 1;
- p->sortval = 0;
- p->material = m;
- }
-
- int index = 0;
-
- // build main patch polys:
- for (i = 0; i < detail_size; i++) {
- for (j = 0; j < detail_size; j++) {
- int v[4] = {
- (ds1 * (i ) + (j )),
- (ds1 * (i ) + (j+1)),
- (ds1 * (i+1) + (j )),
- (ds1 * (i+1) + (j+1)) };
-
- bisect(vset, v);
-
- // first triangle
- Poly* p = s->GetPolys() + index++;
- p->verts[0] = v[0];
- p->verts[1] = v[1];
- p->verts[2] = v[3];
-
- if (level >= 2 && !water) {
- int layer = CalcLayer(p) + 1;
- p->material = materials.at(layer);
- p->sortval = layer;
- }
-
- // second triangle
- p = s->GetPolys() + index++;
- p->verts[0] = v[0];
- p->verts[1] = v[3];
- p->verts[2] = v[2];
-
- if (level >= 2 && !water) {
- int layer = CalcLayer(p) + 1;
- p->material = materials.at(layer);
- p->sortval = layer;
- }
- }
- }
-
- // build vertical edge strip polys:
-
- if (!water) {
- for (i = 0; i < NUM_STRIPS; i++) {
- Poly* p = s->GetPolys() + npolys + i*strip_len;
- int base_index = ds1*ds1 + ds1*2*i;
-
- for (j = 0; j < strip_len; j++) {
- int v = base_index + j * 2;
- p->nverts = 4;
-
- if (i == 1 || i == 2) {
- p->verts[0] = v;
- p->verts[1] = v+2;
- p->verts[2] = v+3;
- p->verts[3] = v+1;
- }
-
- else {
- p->verts[0] = v;
- p->verts[1] = v+1;
- p->verts[2] = v+3;
- p->verts[3] = v+2;
- }
-
- if (level >= 2) {
- int layer = CalcLayer(p) + 1;
- p->material = materials.at(layer);
- p->sortval = layer;
- }
-
- p++;
- }
- }
- }
-
- // update the poly planes:
- for (i = 0; i < total; i++) {
- Poly* p = s->GetPolys() + i;
- Plane& plane = p->plane;
- WORD* v = p->verts;
-
- plane = Plane(vset->loc[v[0]] + loc,
- vset->loc[v[1]] + loc,
- vset->loc[v[2]] + loc);
- }
-
- s->Normalize();
-
- // create continguous segments for each material:
- // sort the polys by material index:
- qsort((void*) s->GetPolys(), s->NumPolys(), sizeof(Poly), mcomp);
-
- // then assign them to cohesive segments:
- Segment* segment = 0;
- Poly* spolys = s->GetPolys();
-
- for (int n = 0; n < s->NumPolys(); n++) {
- if (segment && segment->material == spolys[n].material) {
- segment->npolys++;
- }
- else {
- segment = 0;
- }
-
- if (!segment) {
- segment = new(__FILE__,__LINE__) Segment;
-
- segment->npolys = 1;
- segment->polys = &spolys[n];
- segment->material = segment->polys->material;
- segment->model = model;
-
- s->GetSegments().append(segment);
- }
- }
-
- Solid::EnableCollision(false);
- model->AddSurface(s);
- Solid::EnableCollision(true);
-
- // copy vertex normals:
- const Vec3B* tnorms = terrain->Normals();
-
- for (i = 0; i < ds1; i++) {
- for (j = 0; j < ds1; j++) {
-
- if (water) {
- vset->nrm[i*ds1+j] = Point(0,1,0);
- }
-
- // blend adjacent normals:
- else if (dscale > 1) {
- Point normal;
-
- // but don't blend more than 16 normals per vertex:
- int step = 1;
- if (dscale > 4)
- step = dscale / 4;
-
- for (int dy = -dscale/2; dy < dscale/2; dy += step) {
- for (int dx = -dscale/2; dx < dscale/2; dx += step) {
- int ix = rect.x + (ds1-1-j)*dscale + dx;
- int iy = rect.y + i*dscale + dy;
-
- if (ix < 0) ix = 0;
- if (ix > terrain_width-1) ix = terrain_width-1;
- if (iy < 0) iy = 0;
- if (iy > terrain_width-1) iy = terrain_width-1;
-
- Vec3B vbn = tnorms[iy*terrain_width + ix];
- normal += Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0);
- }
- }
-
- normal.Normalize();
- vset->nrm[i*ds1+j] = normal;
- }
-
- // just copy the one normal:
- else {
- Vec3B vbn = tnorms[(rect.y + i*dscale)*terrain_width + (rect.x + (ds1-1-j) * dscale)];
- Point normal = Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0);
- vset->nrm[i*ds1+j] = normal;
- }
- }
- }
-
- if (!water) {
- pVert = &vset->nrm[ds1*ds1];
-
- // strip 1 & 2 verts
- for (i = 0; i < ds1; i += detail_size) {
- for (j = 0; j < ds1; j++) {
- Vec3 vn = vset->nrm[i*ds1 + j];
-
- *pVert++ = vn;
- *pVert++ = vn;
- }
- }
-
- // strip 3 & 4 verts
- for (j = 0; j < ds1; j += detail_size) {
- for (i = 0; i < ds1; i++) {
- Vec3 vn = vset->nrm[i*ds1 + j];
-
- *pVert++ = vn;
- *pVert++ = vn;
- }
- }
- }
- }
-
- if (level > max_detail)
- max_detail = level;
-
- return true;
-}
-
-// +--------------------------------------------------------------------+
-
-DWORD
-TerrainPatch::BlendValue(double y)
-{
- if (terrain && y >= 0 && !water) {
- // find the proper layer:
- for (int i = 0; i < terrain->GetLayers().size(); i++) {
- TerrainLayer* layer = terrain->GetLayers().at(i);
-
- if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) {
- double scale = (y-layer->GetMinHeight()) / (layer->GetMaxHeight()-layer->GetMinHeight());
-
- if (scale < 0.2)
- scale = 0;
- else if (scale > 0.8)
- scale = 1;
- else
- scale = (scale - 0.2) / 0.6;
-
- if ((i & 1) == 0) {
- scale = 1 - scale;
- }
-
- DWORD val = (DWORD) (scale*255);
-
- return val << 24;
- }
- }
- }
-
- return 0;
-}
-
-int
-TerrainPatch::CalcLayer(Poly* poly)
-{
- if (terrain && poly) {
- if (water)
- return 0;
-
- double y = 1e6;
-
- for (int i = 0; i < poly->nverts; i++) {
- double h = poly->vertex_set->loc[ poly->verts[i] ].y;
-
- if (h >= 0 && h < y) {
- y = h;
- }
- }
-
- if (y <= terrain->GetLayers().first()->GetMinHeight())
- return 0;
-
- for (int i = 0; i < terrain->GetLayers().size(); i++) {
- TerrainLayer* layer = terrain->GetLayers().at(i);
-
- if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) {
- return i;
- }
- }
- }
-
- return -1;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-TerrainPatch::UpdateSurfaceWaves(Vec3& eyePos)
-{
- if (water && model && model->NumVerts() > 1) {
- Surface* s = model->GetSurfaces().first();
- if (s) {
- VertexSet* vset = s->GetVertexSet();
- for (int i = 0; i < vset->nverts; i++)
- vset->loc[i].y = 0.0f;
-
- water->UpdateSurface(eyePos, vset);
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-int
-TerrainPatch::CollidesWith(Graphic& o)
-{
- return 0;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-TerrainPatch::SelectDetail(Projector* projector)
-{
- // This is where all the work is done,
- // Delegate to the overall terrain to
- // compute a detail level for every patch:
-
- if (terrain) {
- terrain->SelectDetail(projector);
- }
-
- // Build detail levels on demand:
-
- if (detail_levels[ndetail] == 0)
- BuildDetailLevel(ndetail);
-}
-
-void
-TerrainPatch::SetDetailLevel(int nd)
-{
- if (nd >= 0 && nd <= max_detail) {
- if (ndetail != nd)
- DeletePrivateData();
-
- ndetail = nd;
-
- // build detail levels on demand:
- if (detail_levels[ndetail] == 0)
- BuildDetailLevel(ndetail);
-
- model = detail_levels[ndetail];
-
- if (water)
- water = terrain->GetWater(ndetail);
- }
-}
-
-// +--------------------------------------------------------------------+
-
-void
-TerrainPatch::Illuminate(Color ambient, List<Light>& lights)
-{
- if (!model || model->NumVerts() < 1) return;
- Surface* s = model->GetSurfaces().first();
- if (!s) return;
-
- illuminating = true;
-
- // clear the solid lights to ambient:
- VertexSet* vset = s->GetVertexSet();
- int nverts = vset->nverts;
- DWORD aval = ambient.Value();
-
- for (int i = 0; i < nverts; i++) {
- vset->diffuse[i] = aval;
- }
-
- TerrainRegion* trgn = terrain->GetRegion();
- bool eclipsed = false;
- bool first = terrain->IsFirstPatch(this);
-
- if (trgn && !first) {
- eclipsed = trgn->IsEclipsed();
- }
-
- // for sun and back lights:
- ListIter<Light> iter = lights;
- while (++iter) {
- Light* light = iter.value();
-
- if (!light->IsDirectional()) // only do sun and
- continue; // back lights
-
- if (light->CastsShadow() && first) {
- eclipsed = light->Location().y < -100 || // has sun set, or
-
- scene->IsLightObscured(vset->loc[0], // is sun in eclipse
- light->Location(), // by orbital body
- radius); // such as a moon?
- }
-
- if (!light->CastsShadow() || !eclipsed) {
- Vec3 vl = light->Location();
- vl.Normalize();
-
- for (int i = 0; i < nverts; i++) {
- Vec3& nrm = vset->nrm[i];
- double val = 0;
- double gain = vl * nrm;
-
- if (gain > 0) {
- val = light->Intensity() * (0.85 * gain);
-
- if (val > 1)
- val = 1;
- }
-
- if (val > 0.01)
- vset->diffuse[i] = ((light->GetColor().dim(val)) + vset->diffuse[i]).Value();
- }
- }
- }
-
- // combine blend weights:
- if (ndetail >= 2) {
- for (int i = 0; i < nverts; i++) {
- vset->diffuse[i] = vset->specular[i] | (vset->diffuse[i] & 0x00ffffff);
- }
- }
-
- if (trgn && first) {
- trgn->SetEclipsed(eclipsed);
- }
-
- InvalidateSurfaceData();
- illuminating = false;
-}
-
-// +--------------------------------------------------------------------+
-
-void
-TerrainPatch::Render(Video* video, DWORD flags)
-{
- if (max_height < 0)
- return;
-
- if (flags & RENDER_ADDITIVE)
- return;
-
- if (!model)
- model = detail_levels[0];
-
- if (scene) {
- /***
- *** TWO PASS LIGHTING FOR TERRAIN SHADOWS DOESN'T
- *** WORK - IT MESSES UP THE HARDWARE FOG CALCS
- ***
- *** PLUS, IT'S INCREDIBLY SLOW!!!
- ***/
-
- if ((flags & RENDER_ADD_LIGHT) != 0)
- return;
-
- if (water) {
- UpdateSurfaceWaves(Vec3(0,0,0));
- Illuminate(scene->Ambient(), scene->Lights());
- }
- else {
- Illuminate(scene->Ambient(), scene->Lights());
- }
- }
- else {
- if ((flags & RENDER_ADD_LIGHT) != 0)
- return;
- }
-
- Bitmap* details[16];
- ZeroMemory(details, sizeof(details));
-
- if (ndetail < 3) {
- for (int i = 0; i < 16 && i < materials.size(); i++) {
- Material* mtl = materials[i];
-
- if (mtl->tex_detail) {
- details[i] = mtl->tex_detail;
- mtl->tex_detail = 0;
- }
- }
- }
-
- double visibility = terrain->GetRegion()->GetWeather().Visibility();
- FLOAT fog_density = (FLOAT) (terrain->GetRegion()->FogDensity() * 2.5e-5 * 1/visibility);
-
- video->SetRenderState(Video::LIGHTING_ENABLE, false);
- video->SetRenderState(Video::SPECULAR_ENABLE, false); //water != 0);
- video->SetRenderState(Video::FOG_ENABLE, true);
- video->SetRenderState(Video::FOG_COLOR, terrain->GetRegion()->FogColor().Value());
- video->SetRenderState(Video::FOG_DENSITY, *((DWORD*) &fog_density));
-
- // Too bad this doesn't work right. But it makes the
- // ground mostly disappear. :-(
- //
- //video->SetRenderState(Video::Z_BIAS, -2);
-
- Solid::Render(video, flags);
-
- video->SetRenderState(Video::LIGHTING_ENABLE, true);
- video->SetRenderState(Video::SPECULAR_ENABLE, true);
- video->SetRenderState(Video::FOG_ENABLE, false);
- //video->SetRenderState(Video::Z_BIAS, 0);
-
- if (ndetail < 3) {
- for (int i = 0; i < 16 && i < materials.size(); i++) {
- Material* mtl = materials[i];
-
- if (details[i] && !mtl->tex_detail) {
- mtl->tex_detail = details[i];
- }
- }
- }
-}
-
-// +--------------------------------------------------------------------+
-
-int
-TerrainPatch::CheckRayIntersection(Point obj_pos, Point dir, double len, Point& ipt, bool ttpas)
-{
- Point light_pos = obj_pos + dir * len;
- int impact = light_pos.y < -100;
-
- // Special case for illumination -
- // just check if sun is above or below the horizon:
-
- if (illuminating || impact) {
- return impact;
- }
-
- if (obj_pos.x != 0 || obj_pos.y != 0 || obj_pos.z != 0) {
- return impact;
- }
-
- // the rest of this code is only used for eclipsing
- // the solar lens flare:
-
- // check right angle spherical distance:
- Point d0 = loc;
- Point d1 = d0.cross(dir);
- double dlen = d1.length(); // distance of point from line
-
- if (dlen > radius) // clean miss
- return 0; // (no impact)
-
- // make sure some part of this patch falls between
- // the camera and the sun:
-
- Point closest = loc + dir * radius;
-
- if (closest * dir < 0)
- return 0;
-
- // probable hit at this point...
- // test for polygon intersection:
- if (!model)
- return 0;
-
- Surface* s = model->GetSurfaces().first();
-
- if (!s)
- return 0;
-
-
- // transform ray into object space:
- Matrix xform(Orientation());
-
- Vec3 tmp = dir;
-
- dir.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
- dir.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
- dir.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
-
- tmp = obj_pos-loc;
-
- obj_pos.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
- obj_pos.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
- obj_pos.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
-
- double min = 2 * len;
-
- // check each polygon:
- Poly* p = s->GetPolys();
-
- for (int i = 0; i < s->NumPolys(); i++) {
- Point v = p->plane.normal;
- double d = p->plane.distance;
-
- double denom = dir*v;
-
- if (denom < -1.0e-5) {
- Point P = v * d;
- double ilen = ((P-obj_pos)*v)/denom;
-
- if (ilen > 0 && ilen < min) {
- Point intersect = obj_pos + dir * ilen;
-
- if (p->Contains(intersect)) {
- ipt = intersect;
- min = ilen;
- impact = 1;
- }
- }
- }
-
- p++;
- }
-
- // xform impact point back into world coordinates:
-
- if (impact) {
- ipt = (ipt * Orientation()) + loc;
- }
-
- return impact;
-}
-
-// +--------------------------------------------------------------------+
-
-double
-TerrainPatch::Height(double x, double z) const
-{
- if (water) return base;
-
- double height = 0;
-
- x /= scale;
- z /= scale;
-
- int x0 = (int) x;
- int z0 = (int) z;
-
- if (x0 >= 0 && x0 < PATCH_SIZE && z0 >= 0 && z0 < PATCH_SIZE) {
- double dx = x-x0;
- double dz = z-z0;
-
- double h[4];
-
- h[0] = heights[(z0*PATCH_SIZE + x0)];
- h[1] = heights[((z0+1)*PATCH_SIZE + x0)];
- h[2] = heights[(z0*PATCH_SIZE + x0+1)];
- h[3] = heights[((z0+1)*PATCH_SIZE + x0+1)];
-
- // if level, just return height of one vertex:
- if (h[0] == h[1] && h[1] == h[2] && h[2] == h[3]) {
- height = h[0];
- }
-
- // if sloped, interpolate between vertex heights:
- else {
- double hl = h[0]*(1-dz) + h[1]*dz;
- double hr = h[2]*(1-dz) + h[3]*dz;
-
- height = hl*(1-dx) + hr*dx + 5; // fudge
- }
- }
-
- if (height < 0)
- height = 0;
-
- return height;
-}
+/* Starshatter OpenSource Distribution + Copyright (c) 1997-2004, Destroyer Studios LLC. + All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name "Destroyer Studios" nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + SUBSYSTEM: Stars.exe + FILE: TerrainPatch.cpp + AUTHOR: John DiCamillo + + + OVERVIEW + ======== +*/ + +#include "MemDebug.h" +#include "Terrain.h" +#include "TerrainLayer.h" +#include "TerrainPatch.h" +#include "TerrainRegion.h" +#include "Sim.h" + +#include "Light.h" +#include "CameraView.h" +#include "Projector.h" +#include "Bitmap.h" +#include "DataLoader.h" +#include "Game.h" +#include "Scene.h" +#include "Water.h" + +// +====================================================================+ + +// #define DEBUG_DETAIL 1 + +// +====================================================================+ + +const int MAX_DETAIL = 4; +const int PATCH_SIZE = 17; +const int HALF_PATCH_SIZE = 8; +const int MAX_VERTS = PATCH_SIZE * PATCH_SIZE; + +static bool illuminating = false; + +// +--------------------------------------------------------------------+ + +TerrainPatch::TerrainPatch(Terrain* terr,const Bitmap* patch, const Rect& r, +const Point& p1, const Point& p2) +: ndetail(0), terrain(terr), rect(r), water(0), min_height(1e9), max_height(-1e9) +{ + luminous = true; // we will do our own lighting + own_model = false; // manage the model lifetimes in this derived class + + max_detail = Terrain::DetailLevel(); + scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1); + mtnscale = p2.y - p1.y; + base = p1.y; + size = p2.x - p1.x; + + ZeroMemory(detail_levels, sizeof(detail_levels)); + + terrain_width = patch->Width(); + + loc = (p1 + p2) * 0.5; + loc.y = base; + + radius = (float) (size * 0.75); + heights = new(__FILE__,__LINE__) float[MAX_VERTS]; + + float* pHeight = heights; + int tscale = rect.w / (PATCH_SIZE-1); + int i, j; + + for (i = 0; i < PATCH_SIZE; i++) { + int ty = rect.y + i * tscale; + + if (ty < 0) + ty = 0; + + if (ty > patch->Height()-1) + ty = patch->Height()-1; + + for (j = 0; j < PATCH_SIZE; j++) { + int tx = rect.x + (PATCH_SIZE-1 - j) * tscale; + + if (tx < 0) + tx = 0; + + if (tx > patch->Width()-1) + tx = patch->Width()-1; + + int red = patch->GetColor(tx,ty).Red(); + float alt = (float) (red * mtnscale); + + if (alt < min_height) + min_height = alt; + if (alt > max_height) + max_height = alt; + + if (terrain->WaterTexture() && red < 2) + alt = -5000.0f; + + *pHeight++ = alt; + } + } + + Material* mtl = new(__FILE__,__LINE__) Material; + mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f); + mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f); + mtl->Ks = Color::Black; + + mtl->tex_diffuse = terrain->Texture(); + + materials.append(mtl); + + List<TerrainLayer>& layers = terrain->GetLayers(); + for (i = 0; i < layers.size(); i++) { + Bitmap* tex0 = layers.at(i)->GetTileTexture(); + Bitmap* tex1 = 0; + Bitmap* texd = layers.at(i)->GetDetailTexture(); + + if (i < layers.size()-1) + tex1 = layers.at(i+1)->GetTileTexture(); + + if (!texd) + texd = terrain->DetailTexture(0); + + mtl = new(__FILE__,__LINE__) Material; + mtl->Ka = ColorValue(0.5f, 0.5f, 0.5f); + mtl->Kd = ColorValue(0.3f, 0.6f, 0.2f); + mtl->Ks = Color::Black; + + if ((i & 1) != 0) { + mtl->tex_diffuse = tex1; + mtl->tex_alternate = tex0; + } + else { + mtl->tex_diffuse = tex0; + mtl->tex_alternate = tex1; + } + + mtl->tex_detail = texd; + + materials.append(mtl); + } + + for (i = 0; i <= max_detail; i++) + BuildDetailLevel(i); + + model = detail_levels[1]; +} + +// +--------------------------------------------------------------------+ + +TerrainPatch::TerrainPatch(Terrain* terr, +const Rect& r, +const Point& p1, +const Point& p2, +double sea_level) +: ndetail(0), terrain(terr), rect(r), water(0) +{ + luminous = true; // water is lit by the graphics engine + own_model = false; // manage the model lifetimes in this derived class + + max_detail = Terrain::DetailLevel(); + scale = fabs(p1.x - p2.x) / (PATCH_SIZE-1); + mtnscale = 0; + base = sea_level; + size = p2.x - p1.x; + + ZeroMemory(detail_levels, sizeof(detail_levels)); + + terrain_width = 0; + + loc = (p1 + p2) * 0.5; + loc.y = base; + + radius = (float) (size * 0.75); + heights = new(__FILE__,__LINE__) float[MAX_VERTS]; + + float* pHeight = heights; + int i, j; + + for (i = 0; i < PATCH_SIZE; i++) { + for (j = 0; j < PATCH_SIZE; j++) { + *pHeight++ = (float) sea_level; + } + } + + Material* mtl = new(__FILE__,__LINE__) Material; + mtl->Ka = Color::Gray; + mtl->Kd = Color::White; + mtl->Ks = Color::White; + mtl->power = 30.0f; + mtl->shadow = false; + mtl->tex_diffuse = terrain->WaterTexture(); + //strcpy_s(mtl->shader, "WaterReflections"); + materials.append(mtl); + + water = terrain->GetWater(1); + + for (i = 0; i <= max_detail; i++) + BuildDetailLevel(i); + + model = detail_levels[1]; +} + +// +--------------------------------------------------------------------+ + +TerrainPatch::~TerrainPatch() +{ + delete [] heights; + + for (int i = 0; i < MAX_LOD; i++) { + Model* m = detail_levels[i]; + + if (m) { + m->GetMaterials().clear(); + delete m; + } + } + + materials.destroy(); +} + +// +--------------------------------------------------------------------+ + +void +TerrainPatch::SetScales(double s, double m, double b) +{ + scale = s; + mtnscale = m; + base = b; +} + +// +--------------------------------------------------------------------+ + +static int mcomp(const void* a, const void* b) +{ + Poly* pa = (Poly*) a; + Poly* pb = (Poly*) b; + + if (pa->sortval == pb->sortval) + return 0; + + if (pa->sortval < pb->sortval) + return 1; + + return -1; +} + + +static void bisect(VertexSet* vset, int v[4]) +{ + double d1 = fabs(vset->loc[ v[0] ].y - + vset->loc[ v[3] ].y); + + double d2 = fabs(vset->loc[ v[1] ].y - + vset->loc[ v[2] ].y); + + if (d2 < 0.7*d1) { + int odd[4] = { v[1], v[3], v[0], v[2] }; + for (int i = 0; i < 4; i++) + v[i] = odd[i]; + } +} + +bool +TerrainPatch::BuildDetailLevel(int level) +{ + int i, j; + + int detail_size = 1 << level; + int ds1 = detail_size+1; + + if (detail_size > PATCH_SIZE) + return false; + + Model* model = new(__FILE__,__LINE__) Model; + detail_levels[level] = model; + + model->SetLuminous(luminous); + model->SetDynamic(true); + + const int NUM_STRIPS = 4; + const int NUM_INDICES_TRI = 3; + const int NUM_INDICES_QUAD = 6; + + int nverts = ds1*ds1 + ds1*2*NUM_STRIPS; + int npolys = detail_size*detail_size*2; + int strip_len = detail_size; + int total = npolys + strip_len*NUM_STRIPS; + + if (water) { + nverts = ds1*ds1; + strip_len = 0; + total = npolys; + } + + Surface* s = new(__FILE__,__LINE__) Surface; + VertexSet* vset = 0; + + if (s) { + s->SetName("default"); + s->CreateVerts(nverts); + s->CreatePolys(total); + s->AddIndices(npolys*NUM_INDICES_TRI + strip_len*NUM_STRIPS*NUM_INDICES_QUAD); + + vset = s->GetVertexSet(); + if (!water) + vset->CreateAdditionalTexCoords(); + + ZeroMemory(vset->loc, nverts * sizeof(Vec3)); + ZeroMemory(vset->diffuse, nverts * sizeof(DWORD)); + ZeroMemory(vset->specular, nverts * sizeof(DWORD)); + ZeroMemory(vset->tu, nverts * sizeof(float)); + ZeroMemory(vset->tv, nverts * sizeof(float)); + if (!water) { + ZeroMemory(vset->tu1, nverts * sizeof(float)); + ZeroMemory(vset->tv1, nverts * sizeof(float)); + } + ZeroMemory(vset->rw, nverts * sizeof(float)); + + // initialize vertices + Vec3* pVert = vset->loc; + float* pTu = vset->tu; + float* pTv = vset->tv; + float* pTu1 = vset->tu1; + float* pTv1 = vset->tv1; + DWORD* pSpec = vset->specular; + + int dscale = (PATCH_SIZE-1)/detail_size; + double dt = 0.0625 / (ds1-1); // terrain texture scale + double dtt = 2.0000 / (ds1-1); // tile texture scale + double tu0 = (double) rect.x / rect.w / 16.0 + 1.0/16.0; + double tv0 = (double) rect.y / rect.h / 16.0; + + // surface verts + for (i = 0; i < ds1; i++) { + for (j = 0; j < ds1; j++) { + *pVert = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)), + (float) (heights[i*dscale*PATCH_SIZE + j*dscale]), + (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale))); + + if (level >= 2) { + *pTu++ = (float) (-j*dtt); + *pTv++ = (float) ( i*dtt); + + if (level >= 4 && !water) { + *pTu1++ = (float) (-j*dtt*3); + *pTv1++ = (float) ( i*dtt*3); + } + + *pSpec++ = BlendValue(pVert->y); + } + + else { + *pTu++ = (float) (tu0 - j*dt); + *pTv++ = (float) (tv0 + i*dt); + } + + pVert++; + } + } + + if (!water) { + // strip 1 & 2 verts + for (i = 0; i < ds1; i += detail_size) { + for (j = 0; j < ds1; j++) { + Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)), + (float) (heights[i*dscale*PATCH_SIZE + j*dscale]), + (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale))); + + *pVert++ = vl; + + DWORD blend = 0; + + if (level >= 2) { + blend = BlendValue(vl.y); + + *pSpec++ = blend; + *pTu++ = (float) (-j*dtt); + *pTv++ = (float) ( i*dtt); + } + + else { + *pTu++ = (float) (tu0 - j*dt); + *pTv++ = (float) (tv0 + i*dt); + } + + vl.y = -5000.0f; + + *pVert++ = vl; + + if (level >= 2) { + *pSpec++ = blend; + *pTu++ = (float) (-j*dtt); + *pTv++ = (float) ( i*dtt); + } + + else { + *pTu++ = (float) (tu0 - j*dt); + *pTv++ = (float) (tv0 + i*dt); + } + } + } + + // strip 3 & 4 verts + for (j = 0; j < ds1; j += detail_size) { + for (i = 0; i < ds1; i++) { + Vec3 vl = Vec3((float) (j* scale * dscale - (HALF_PATCH_SIZE*scale)), + (float) (heights[i*dscale*PATCH_SIZE + j*dscale]), + (float) (i* scale * dscale - (HALF_PATCH_SIZE*scale))); + + *pVert++ = vl; + + DWORD blend = 0; + + if (level >= 2) { + blend = BlendValue(vl.y); + + *pSpec++ = blend; + *pTu++ = (float) (-j*dtt); + *pTv++ = (float) ( i*dtt); + } + + else { + *pTu++ = (float) (tu0 - j*dt); + *pTv++ = (float) (tv0 + i*dt); + } + + vl.y = -5000.0f; + + *pVert++ = vl; + + if (level >= 2) { + *pSpec++ = blend; + *pTu++ = (float) (-j*dtt); + *pTv++ = (float) ( i*dtt); + } + + else { + *pTu++ = (float) (tu0 - j*dt); + *pTv++ = (float) (tv0 + i*dt); + } + } + } + } + + Material* m = materials.first(); + + // initialize the polys + for (i = 0; i < npolys; i++) { + Poly* p = s->GetPolys() + i; + p->nverts = 3; + p->vertex_set = vset; + p->visible = 1; + p->sortval = 0; + p->material = m; + + if (level >= 2 && !water) { + p->material = materials.at(1); + p->sortval = 1; + } + } + + for (i = npolys; i < total; i++) { + Poly* p = s->GetPolys() + i; + p->nverts = 4; + p->vertex_set = vset; + p->visible = 1; + p->sortval = 0; + p->material = m; + } + + int index = 0; + + // build main patch polys: + for (i = 0; i < detail_size; i++) { + for (j = 0; j < detail_size; j++) { + int v[4] = { + (ds1 * (i ) + (j )), + (ds1 * (i ) + (j+1)), + (ds1 * (i+1) + (j )), + (ds1 * (i+1) + (j+1)) }; + + bisect(vset, v); + + // first triangle + Poly* p = s->GetPolys() + index++; + p->verts[0] = v[0]; + p->verts[1] = v[1]; + p->verts[2] = v[3]; + + if (level >= 2 && !water) { + int layer = CalcLayer(p) + 1; + p->material = materials.at(layer); + p->sortval = layer; + } + + // second triangle + p = s->GetPolys() + index++; + p->verts[0] = v[0]; + p->verts[1] = v[3]; + p->verts[2] = v[2]; + + if (level >= 2 && !water) { + int layer = CalcLayer(p) + 1; + p->material = materials.at(layer); + p->sortval = layer; + } + } + } + + // build vertical edge strip polys: + + if (!water) { + for (i = 0; i < NUM_STRIPS; i++) { + Poly* p = s->GetPolys() + npolys + i*strip_len; + int base_index = ds1*ds1 + ds1*2*i; + + for (j = 0; j < strip_len; j++) { + int v = base_index + j * 2; + p->nverts = 4; + + if (i == 1 || i == 2) { + p->verts[0] = v; + p->verts[1] = v+2; + p->verts[2] = v+3; + p->verts[3] = v+1; + } + + else { + p->verts[0] = v; + p->verts[1] = v+1; + p->verts[2] = v+3; + p->verts[3] = v+2; + } + + if (level >= 2) { + int layer = CalcLayer(p) + 1; + p->material = materials.at(layer); + p->sortval = layer; + } + + p++; + } + } + } + + // update the poly planes: + for (i = 0; i < total; i++) { + Poly* p = s->GetPolys() + i; + Plane& plane = p->plane; + WORD* v = p->verts; + + plane = Plane(vset->loc[v[0]] + loc, + vset->loc[v[1]] + loc, + vset->loc[v[2]] + loc); + } + + s->Normalize(); + + // create continguous segments for each material: + // sort the polys by material index: + qsort((void*) s->GetPolys(), s->NumPolys(), sizeof(Poly), mcomp); + + // then assign them to cohesive segments: + Segment* segment = 0; + Poly* spolys = s->GetPolys(); + + for (int n = 0; n < s->NumPolys(); n++) { + if (segment && segment->material == spolys[n].material) { + segment->npolys++; + } + else { + segment = 0; + } + + if (!segment) { + segment = new(__FILE__,__LINE__) Segment; + + segment->npolys = 1; + segment->polys = &spolys[n]; + segment->material = segment->polys->material; + segment->model = model; + + s->GetSegments().append(segment); + } + } + + Solid::EnableCollision(false); + model->AddSurface(s); + Solid::EnableCollision(true); + + // copy vertex normals: + const Vec3B* tnorms = terrain->Normals(); + + for (i = 0; i < ds1; i++) { + for (j = 0; j < ds1; j++) { + + if (water) { + vset->nrm[i*ds1+j] = Point(0,1,0); + } + + // blend adjacent normals: + else if (dscale > 1) { + Point normal; + + // but don't blend more than 16 normals per vertex: + int step = 1; + if (dscale > 4) + step = dscale / 4; + + for (int dy = -dscale/2; dy < dscale/2; dy += step) { + for (int dx = -dscale/2; dx < dscale/2; dx += step) { + int ix = rect.x + (ds1-1-j)*dscale + dx; + int iy = rect.y + i*dscale + dy; + + if (ix < 0) ix = 0; + if (ix > terrain_width-1) ix = terrain_width-1; + if (iy < 0) iy = 0; + if (iy > terrain_width-1) iy = terrain_width-1; + + Vec3B vbn = tnorms[iy*terrain_width + ix]; + normal += Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0); + } + } + + normal.Normalize(); + vset->nrm[i*ds1+j] = normal; + } + + // just copy the one normal: + else { + Vec3B vbn = tnorms[(rect.y + i*dscale)*terrain_width + (rect.x + (ds1-1-j) * dscale)]; + Point normal = Point((128-vbn.x)/127.0, (vbn.z-128)/127.0, (vbn.y-128)/127.0); + vset->nrm[i*ds1+j] = normal; + } + } + } + + if (!water) { + pVert = &vset->nrm[ds1*ds1]; + + // strip 1 & 2 verts + for (i = 0; i < ds1; i += detail_size) { + for (j = 0; j < ds1; j++) { + Vec3 vn = vset->nrm[i*ds1 + j]; + + *pVert++ = vn; + *pVert++ = vn; + } + } + + // strip 3 & 4 verts + for (j = 0; j < ds1; j += detail_size) { + for (i = 0; i < ds1; i++) { + Vec3 vn = vset->nrm[i*ds1 + j]; + + *pVert++ = vn; + *pVert++ = vn; + } + } + } + } + + if (level > max_detail) + max_detail = level; + + return true; +} + +// +--------------------------------------------------------------------+ + +DWORD +TerrainPatch::BlendValue(double y) +{ + if (terrain && y >= 0 && !water) { + // find the proper layer: + for (int i = 0; i < terrain->GetLayers().size(); i++) { + TerrainLayer* layer = terrain->GetLayers().at(i); + + if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) { + double scale = (y-layer->GetMinHeight()) / (layer->GetMaxHeight()-layer->GetMinHeight()); + + if (scale < 0.2) + scale = 0; + else if (scale > 0.8) + scale = 1; + else + scale = (scale - 0.2) / 0.6; + + if ((i & 1) == 0) { + scale = 1 - scale; + } + + DWORD val = (DWORD) (scale*255); + + return val << 24; + } + } + } + + return 0; +} + +int +TerrainPatch::CalcLayer(Poly* poly) +{ + if (terrain && poly) { + if (water) + return 0; + + double y = 1e6; + + for (int i = 0; i < poly->nverts; i++) { + double h = poly->vertex_set->loc[ poly->verts[i] ].y; + + if (h >= 0 && h < y) { + y = h; + } + } + + if (y <= terrain->GetLayers().first()->GetMinHeight()) + return 0; + + for (int i = 0; i < terrain->GetLayers().size(); i++) { + TerrainLayer* layer = terrain->GetLayers().at(i); + + if (y >= layer->GetMinHeight() && y < layer->GetMaxHeight()) { + return i; + } + } + } + + return -1; +} + +// +--------------------------------------------------------------------+ + +void +TerrainPatch::UpdateSurfaceWaves(Vec3& eyePos) +{ + if (water && model && model->NumVerts() > 1) { + Surface* s = model->GetSurfaces().first(); + if (s) { + VertexSet* vset = s->GetVertexSet(); + for (int i = 0; i < vset->nverts; i++) + vset->loc[i].y = 0.0f; + + water->UpdateSurface(eyePos, vset); + } + } +} + +// +--------------------------------------------------------------------+ + +int +TerrainPatch::CollidesWith(Graphic& o) +{ + return 0; +} + +// +--------------------------------------------------------------------+ + +void +TerrainPatch::SelectDetail(Projector* projector) +{ + // This is where all the work is done, + // Delegate to the overall terrain to + // compute a detail level for every patch: + + if (terrain) { + terrain->SelectDetail(projector); + } + + // Build detail levels on demand: + + if (detail_levels[ndetail] == 0) + BuildDetailLevel(ndetail); +} + +void +TerrainPatch::SetDetailLevel(int nd) +{ + if (nd >= 0 && nd <= max_detail) { + if (ndetail != nd) + DeletePrivateData(); + + ndetail = nd; + + // build detail levels on demand: + if (detail_levels[ndetail] == 0) + BuildDetailLevel(ndetail); + + model = detail_levels[ndetail]; + + if (water) + water = terrain->GetWater(ndetail); + } +} + +// +--------------------------------------------------------------------+ + +void +TerrainPatch::Illuminate(Color ambient, List<Light>& lights) +{ + if (!model || model->NumVerts() < 1) return; + Surface* s = model->GetSurfaces().first(); + if (!s) return; + + illuminating = true; + + // clear the solid lights to ambient: + VertexSet* vset = s->GetVertexSet(); + int nverts = vset->nverts; + DWORD aval = ambient.Value(); + + for (int i = 0; i < nverts; i++) { + vset->diffuse[i] = aval; + } + + TerrainRegion* trgn = terrain->GetRegion(); + bool eclipsed = false; + bool first = terrain->IsFirstPatch(this); + + if (trgn && !first) { + eclipsed = trgn->IsEclipsed(); + } + + // for sun and back lights: + ListIter<Light> iter = lights; + while (++iter) { + Light* light = iter.value(); + + if (!light->IsDirectional()) // only do sun and + continue; // back lights + + if (light->CastsShadow() && first) { + eclipsed = light->Location().y < -100 || // has sun set, or + + scene->IsLightObscured(vset->loc[0], // is sun in eclipse + light->Location(), // by orbital body + radius); // such as a moon? + } + + if (!light->CastsShadow() || !eclipsed) { + Vec3 vl = light->Location(); + vl.Normalize(); + + for (int i = 0; i < nverts; i++) { + Vec3& nrm = vset->nrm[i]; + double val = 0; + double gain = vl * nrm; + + if (gain > 0) { + val = light->Intensity() * (0.85 * gain); + + if (val > 1) + val = 1; + } + + if (val > 0.01) + vset->diffuse[i] = ((light->GetColor().dim(val)) + vset->diffuse[i]).Value(); + } + } + } + + // combine blend weights: + if (ndetail >= 2) { + for (int i = 0; i < nverts; i++) { + vset->diffuse[i] = vset->specular[i] | (vset->diffuse[i] & 0x00ffffff); + } + } + + if (trgn && first) { + trgn->SetEclipsed(eclipsed); + } + + InvalidateSurfaceData(); + illuminating = false; +} + +// +--------------------------------------------------------------------+ + +void +TerrainPatch::Render(Video* video, DWORD flags) +{ + if (max_height < 0) + return; + + if (flags & RENDER_ADDITIVE) + return; + + if (!model) + model = detail_levels[0]; + + if (scene) { + /*** + *** TWO PASS LIGHTING FOR TERRAIN SHADOWS DOESN'T + *** WORK - IT MESSES UP THE HARDWARE FOG CALCS + *** + *** PLUS, IT'S INCREDIBLY SLOW!!! + ***/ + + if ((flags & RENDER_ADD_LIGHT) != 0) + return; + + if (water) { + UpdateSurfaceWaves(Vec3(0,0,0)); + Illuminate(scene->Ambient(), scene->Lights()); + } + else { + Illuminate(scene->Ambient(), scene->Lights()); + } + } + else { + if ((flags & RENDER_ADD_LIGHT) != 0) + return; + } + + Bitmap* details[16]; + ZeroMemory(details, sizeof(details)); + + if (ndetail < 3) { + for (int i = 0; i < 16 && i < materials.size(); i++) { + Material* mtl = materials[i]; + + if (mtl->tex_detail) { + details[i] = mtl->tex_detail; + mtl->tex_detail = 0; + } + } + } + + double visibility = terrain->GetRegion()->GetWeather().Visibility(); + FLOAT fog_density = (FLOAT) (terrain->GetRegion()->FogDensity() * 2.5e-5 * 1/visibility); + + video->SetRenderState(Video::LIGHTING_ENABLE, false); + video->SetRenderState(Video::SPECULAR_ENABLE, false); //water != 0); + video->SetRenderState(Video::FOG_ENABLE, true); + video->SetRenderState(Video::FOG_COLOR, terrain->GetRegion()->FogColor().Value()); + video->SetRenderState(Video::FOG_DENSITY, *((DWORD*) &fog_density)); + + // Too bad this doesn't work right. But it makes the + // ground mostly disappear. :-( + // + //video->SetRenderState(Video::Z_BIAS, -2); + + Solid::Render(video, flags); + + video->SetRenderState(Video::LIGHTING_ENABLE, true); + video->SetRenderState(Video::SPECULAR_ENABLE, true); + video->SetRenderState(Video::FOG_ENABLE, false); + //video->SetRenderState(Video::Z_BIAS, 0); + + if (ndetail < 3) { + for (int i = 0; i < 16 && i < materials.size(); i++) { + Material* mtl = materials[i]; + + if (details[i] && !mtl->tex_detail) { + mtl->tex_detail = details[i]; + } + } + } +} + +// +--------------------------------------------------------------------+ + +int +TerrainPatch::CheckRayIntersection(Point obj_pos, Point dir, double len, Point& ipt, bool ttpas) +{ + Point light_pos = obj_pos + dir * len; + int impact = light_pos.y < -100; + + // Special case for illumination - + // just check if sun is above or below the horizon: + + if (illuminating || impact) { + return impact; + } + + if (obj_pos.x != 0 || obj_pos.y != 0 || obj_pos.z != 0) { + return impact; + } + + // the rest of this code is only used for eclipsing + // the solar lens flare: + + // check right angle spherical distance: + Point d0 = loc; + Point d1 = d0.cross(dir); + double dlen = d1.length(); // distance of point from line + + if (dlen > radius) // clean miss + return 0; // (no impact) + + // make sure some part of this patch falls between + // the camera and the sun: + + Point closest = loc + dir * radius; + + if (closest * dir < 0) + return 0; + + // probable hit at this point... + // test for polygon intersection: + if (!model) + return 0; + + Surface* s = model->GetSurfaces().first(); + + if (!s) + return 0; + + + // transform ray into object space: + Matrix xform(Orientation()); + + Vec3 tmp = dir; + + dir.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2)); + dir.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2)); + dir.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2)); + + tmp = obj_pos-loc; + + obj_pos.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2)); + obj_pos.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2)); + obj_pos.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2)); + + double min = 2 * len; + + // check each polygon: + Poly* p = s->GetPolys(); + + for (int i = 0; i < s->NumPolys(); i++) { + Point v = p->plane.normal; + double d = p->plane.distance; + + double denom = dir*v; + + if (denom < -1.0e-5) { + Point P = v * d; + double ilen = ((P-obj_pos)*v)/denom; + + if (ilen > 0 && ilen < min) { + Point intersect = obj_pos + dir * ilen; + + if (p->Contains(intersect)) { + ipt = intersect; + min = ilen; + impact = 1; + } + } + } + + p++; + } + + // xform impact point back into world coordinates: + + if (impact) { + ipt = (ipt * Orientation()) + loc; + } + + return impact; +} + +// +--------------------------------------------------------------------+ + +double +TerrainPatch::Height(double x, double z) const +{ + if (water) return base; + + double height = 0; + + x /= scale; + z /= scale; + + int x0 = (int) x; + int z0 = (int) z; + + if (x0 >= 0 && x0 < PATCH_SIZE && z0 >= 0 && z0 < PATCH_SIZE) { + double dx = x-x0; + double dz = z-z0; + + double h[4]; + + h[0] = heights[(z0*PATCH_SIZE + x0)]; + h[1] = heights[((z0+1)*PATCH_SIZE + x0)]; + h[2] = heights[(z0*PATCH_SIZE + x0+1)]; + h[3] = heights[((z0+1)*PATCH_SIZE + x0+1)]; + + // if level, just return height of one vertex: + if (h[0] == h[1] && h[1] == h[2] && h[2] == h[3]) { + height = h[0]; + } + + // if sloped, interpolate between vertex heights: + else { + double hl = h[0]*(1-dz) + h[1]*dz; + double hr = h[2]*(1-dz) + h[3]*dz; + + height = hl*(1-dx) + hr*dx + 5; // fudge + } + } + + if (height < 0) + height = 0; + + return height; +} |