leocad/common/terrain.cpp

862 lines
22 KiB
C++

// Terrain: a Bezier surface.
//
#include "lc_global.h"
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "opengl.h"
#include "defines.h"
#include "terrain.h"
#include "file.h"
#include "camera.h"
#include "matrix.h"
#include "system.h"
#include "texture.h"
/////////////////////////////////////////////////////////////////////////////
// Static functions
// Cubic Bezier patch matrix:
// 1 0 0 0
// -3 3 0 0
// 3 -6 3 0
// -1 3 -3 1
static float SolveBase(int i, float t)
{
switch (i)
{
case 0: return (((-t)+3)*t-3)*t+1;
case 1: return (((3*t)-6)*t+3)*t;
case 2: return ((-3*t)+3)*t*t;
case 3: return t*t*t;
}
return 0;
}
static float SolveDiff(int i, float t)
{
switch (i)
{
case 0: return ((-3*t)+6)*t-3;
case 1: return ((9*t)-12)*t+3;
case 2: return ((-9*t)+6)*t;
case 3: return 3*t*t;
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// TerrainPatch functions
TerrainPatch::TerrainPatch ()
{
vertex = NULL;
normals = NULL;
coords = NULL;
index = NULL;
steps = 10;
visible = true;
}
TerrainPatch::~TerrainPatch ()
{
FreeMemory ();
}
void TerrainPatch::InitBox(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
{
// Now make each corner point for convenient culling.
corners[0][0] = minX;
corners[1][0] = minX;
corners[2][0] = minX;
corners[3][0] = minX;
corners[4][0] = maxX;
corners[5][0] = maxX;
corners[6][0] = maxX;
corners[7][0] = maxX;
corners[0][1] = minY;
corners[1][1] = minY;
corners[2][1] = maxY;
corners[3][1] = maxY;
corners[4][1] = minY;
corners[5][1] = minY;
corners[6][1] = maxY;
corners[7][1] = maxY;
corners[0][2] = minZ;
corners[1][2] = maxZ;
corners[2][2] = minZ;
corners[3][2] = maxZ;
corners[4][2] = minZ;
corners[5][2] = maxZ;
corners[6][2] = minZ;
corners[7][2] = maxZ;
}
bool TerrainPatch::BoxIsOutside(const float plane[4]) const
{
float planeEqVal;
for (int i = 0; i < 8; i++)
{
planeEqVal = plane[0] * corners[i][0] + plane[1] * corners[i][1] + plane[2] * corners[i][2] + plane[3];
if (planeEqVal > 0)
return false;
}
return true;
}
#define controlsX(b, a) control[(a*4+b)*3]
#define controlsY(b, a) control[(a*4+b)*3+1]
#define controlsZ(b, a) control[(a*4+b)*3+2]
void TerrainPatch::Tesselate(bool bNormals)
{
FreeMemory();
vertex = new float[steps*steps*3];
if (bNormals)
normals = new float[steps*steps*3];
coords = new float[steps*steps*2];
float invTotalSteps = 1.0f / (steps - 1);
for (int stepU = 0; stepU < steps; stepU++)
{
// Generate the parameter for this step of the curve.
float u = stepU * invTotalSteps;
for (int stepV = 0; stepV < steps; stepV++)
{
// Generate the parameter for this step of the curve.
float v = stepV * invTotalSteps;
// This holds the point we're working on as we add control points' contributions to it.
float curPt[3] = { 0, 0, 0 };
float curNorm[3] = { 0, 0, 0 };
float curUTan[3] = { 0, 0, 0 };
float curVTan[3] = { 0, 0, 0 };
// Generate a point on the curve for this step.
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
{
// Get a few basis function values and products thereof that we'll need.
float bu = SolveBase(i, u);
float bv = SolveBase(j, v);
float dbu = SolveDiff(i, u);
float dbv = SolveDiff(j, v);
float bu_bv = bu * bv;
float bu_dbv = bu * dbv;
float dbu_bv = dbu * bv;
// Add this control point's contribution onto the current point.
curPt[0] += controlsX(i, j) * bu_bv;
curPt[1] += controlsY(i, j) * bu_bv;
curPt[2] += controlsZ(i, j) * bu_bv;
// Add this point's contribution to our u-tangent.
curUTan[0] += controlsX(i, j) * dbu_bv;
curUTan[1] += controlsY(i, j) * dbu_bv;
curUTan[2] += controlsZ(i, j) * dbu_bv;
// Add this point's contribution to our v-tangent.
curVTan[0] += controlsX(i, j) * bu_dbv;
curVTan[1] += controlsY(i, j) * bu_dbv;
curVTan[2] += controlsZ(i, j) * bu_dbv;
}
// Now get our normal as the cross-product of the u and v tangents.
curNorm[0] = curVTan[1] * curUTan[2] - curVTan[2] * curUTan[1];
curNorm[1] = curVTan[2] * curUTan[0] - curVTan[0] * curUTan[2];
curNorm[2] = curVTan[0] * curUTan[1] - curVTan[1] * curUTan[0];
// Normalize our normal (ouch!)
float rInv = 1.0f / (float)sqrt(curNorm[0]*curNorm[0] + curNorm[1]*curNorm[1] + curNorm[2]*curNorm[2]);
curNorm[0] *= rInv;
curNorm[1] *= rInv;
curNorm[2] *= rInv;
// Store these.
memcpy(&vertex[(stepU+(stepV*steps))*3], curPt, 3*sizeof(float));
if (bNormals)
memcpy(&normals[(stepU+(stepV*steps))*3], curNorm, 3*sizeof(float));
coords[(stepU+(stepV*steps))*2] = u;
coords[(stepU+(stepV*steps))*2+1] = v;
}
}
index = new unsigned short[(steps-1)*(steps-1)*6];
for (unsigned short i = 0; i < steps-1; i++)
for (unsigned short j = 0; j < steps-1; j++)
{
index[(i*(steps-1)+j)*6] = i*steps+j;
index[(i*(steps-1)+j)*6+1] = (i+1)*steps+j;
index[(i*(steps-1)+j)*6+2] = i*steps+j+1;
index[(i*(steps-1)+j)*6+3] = (i+1)*steps+j;
index[(i*(steps-1)+j)*6+4] = (i+1)*steps+j+1;
index[(i*(steps-1)+j)*6+5] = i*steps+j+1;
}
}
#undef controlsX
#undef controlsY
#undef controlsZ
void TerrainPatch::FreeMemory()
{
if (vertex)
{
delete[] vertex;
vertex = NULL;
}
if (normals)
{
delete[] normals;
normals = NULL;
}
if (coords)
{
delete[] coords;
coords = NULL;
}
if (index)
{
delete[] index;
index = NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
// Terrain construction/destruction
Terrain::Terrain()
{
m_uPatches = 0;
m_vPatches = 0;
m_uSize = 50;
m_vSize = 50;
m_Patches = NULL;
m_pControl = NULL;
m_nOptions = 0;
m_pTexture = new Texture();
}
Terrain::~Terrain()
{
FreeMemory();
delete m_pTexture;
}
/////////////////////////////////////////////////////////////////////////////
// Terrain functions
void Terrain::FileLoad(File* file)
{
unsigned char ch;
unsigned short sh;
int i, j;
file->ReadByte (&ch, 1);
file->ReadLong (&i, 1);
file->ReadLong (&j, 1);
file->ReadFloat (&m_uSize, 1);
file->ReadFloat (&m_vSize, 1);
file->ReadLong (&m_nOptions, 1);
file->ReadFloat (&m_fColor, 3);
if (ch == 1)
{
file->Read(&ch, 1);
sh = ch;
}
else
file->ReadShort (&sh, 1);
if (sh > LC_MAXPATH)
file->Seek (sh, SEEK_CUR);
else
file->Read (&m_strTexture, sh);
SetPatchCount(i, j);
for (i = 0; i < GetCountU(); i++)
for (j = 0; j < GetCountV(); j++)
file->ReadFloat (&m_pControl[i][j*3+2], 1);
}
void Terrain::FileSave(File* file)
{
unsigned char version = 2; // LeoCAD 0.70
unsigned short sh;
file->WriteByte (&version, 1);
file->WriteLong (&m_uPatches, 1);
file->WriteLong (&m_vPatches, 1);
file->WriteFloat (&m_uSize, 1);
file->WriteFloat (&m_vSize, 1);
file->WriteLong (&m_nOptions, 1);
file->WriteFloat (&m_fColor, 3);
sh = strlen (m_strTexture);
file->WriteShort (&sh, 1);
file->Write (m_strTexture, sh);
for (int i = 0; i < GetCountU(); i++)
for (int j = 0; j < GetCountV(); j++)
file->WriteFloat (&m_pControl[i][j*3+2], 1);
}
void Terrain::FreeMemory()
{
int i;
if (m_Patches)
{
for (i = 0; i < m_uPatches; i++)
delete[] m_Patches[i];
delete[] m_Patches;
m_Patches = NULL;
}
if (m_pControl)
{
for (i = 0; i < (m_uPatches*3)+1; i++)
delete[] m_pControl[i];
delete[] m_pControl;
m_pControl = NULL;
}
}
// Copy terrain info
Terrain& Terrain::operator= (const Terrain& source)
{
FreeMemory();
int i;
m_nOptions = source.m_nOptions;
strcpy(m_strTexture, source.m_strTexture);
memcpy(m_fColor, source.m_fColor, sizeof(m_fColor));
m_uPatches = source.m_uPatches;
m_vPatches = source.m_vPatches;
m_Patches = new TerrainPatch*[m_uPatches];
for (i = 0; i < m_uPatches; i++)
m_Patches[i] = new TerrainPatch[m_vPatches];
int uCount = GetCountU(), vCount = GetCountV();
m_pControl = new float*[uCount];
for (i = 0; i < uCount; i++)
{
m_pControl[i] = new float[vCount*3];
memcpy(m_pControl[i], source.m_pControl[i], vCount*3*sizeof(float));
}
m_uSize = source.m_uSize;
m_vSize = source.m_vSize;
SetControlPoints();
Tesselate();
return *this;
}
void Terrain::GetSize(float *uSize, float *vSize)
{
*uSize = m_uSize;
*vSize = m_vSize;
}
void Terrain::SetSize(float uSize, float vSize)
{
m_uSize = uSize;
m_vSize = vSize;
int i, j, uCount = GetCountU(), vCount = GetCountV();
for (i = 0; i < uCount; i++)
for (j = 0; j < vCount; j++)
{
m_pControl[i][j*3] = m_uSize * ((float)i/(uCount-1)-0.5f);
m_pControl[i][j*3+1] = m_vSize * ((float)j/(vCount-1)-0.5f);
}
SetControlPoints();
Tesselate();
}
void Terrain::GetPatchCount(int *uCount, int *vCount)
{
*uCount = m_uPatches;
*vCount = m_vPatches;
}
void Terrain::SetPatchCount(int uPatches, int vPatches)
{
if (uPatches == m_uPatches && vPatches == m_vPatches)
return;
float** oldControl = m_pControl;
int i, j, uCount = uPatches*3+1, vCount = vPatches*3+1;
int uCountOld = m_uPatches != 0 ? m_uPatches*3+1 : 0, vCountOld = m_vPatches != 0 ? m_vPatches*3+1 : 0;
// allocate new arrays
// if (uPatches != m_uPatches)
m_pControl = new float*[uCount];
if (m_vPatches != vPatches)
{
for (i = 0; i < uCount; i++)
m_pControl[i] = new float[vCount*3];
}
else
{
for (i = 0; (i < uCount) && (i < uCountOld); i++)
m_pControl[i] = oldControl[i];
for (i = uCountOld; i < uCount; i++)
m_pControl[i] = new float[vCount*3];
}
// set the points
for (i = 0; i < uCount; i++)
{
for (j = 0; j < vCount; j++)
{
m_pControl[i][j*3] = m_uSize * ((float)i/(uCount-1)-0.5f);
m_pControl[i][j*3+1] = m_vSize * ((float)j/(vCount-1)-0.5f);
if (i < uCountOld && j < vCountOld)
m_pControl[i][j*3+2] = oldControl[i][j*3+2];
else
m_pControl[i][j*3+2] = 0;
}
}
if (m_vPatches != vPatches)
{
for (i = 0; i < uCountOld; i++)
delete[] oldControl[i];
}
else
{
for (i = uCount; i < uCountOld; i++)
delete[] oldControl[i];
}
// if ((uPatches != m_uPatches) && (oldControl != NULL))
delete[] oldControl;
if (m_Patches)
{
for (i = 0; i < m_uPatches; i++)
{
for (j = 0; j < m_vPatches; j++)
m_Patches[i][j].FreeMemory();
delete[] m_Patches[i];
}
delete[] m_Patches;
}
m_uPatches = uPatches;
m_vPatches = vPatches;
m_Patches = new TerrainPatch*[m_uPatches];
for (i = 0; i < m_uPatches; i++)
m_Patches[i] = new TerrainPatch[m_vPatches];
SetControlPoints();
Tesselate();
}
// Set the control points for each patch
void Terrain::SetControlPoints()
{
int i, j;
for (i = 0; i < m_uPatches; i++)
for (j = 0; j < m_vPatches; j++)
{
///////////
m_Patches[i][j].FreeMemory();
float minX = 9999999, maxX = -9999999, minY = 9999999, maxY = -9999999, minZ = 9999999, maxZ = -9999999;
for (int a = 0; a < 4; a++)
for (int b = 0; b < 4; b++)
{
m_Patches[i][j].control[(a*4+b)*3] = m_pControl[i*(4-1)+a][(j*(4-1)+b)*3];
m_Patches[i][j].control[(a*4+b)*3+1] = m_pControl[i*(4-1)+a][(j*(4-1)+b)*3+1];
m_Patches[i][j].control[(a*4+b)*3+2] = m_pControl[i*(4-1)+a][(j*(4-1)+b)*3+2];
minX = min(minX, m_Patches[i][j].control[(a*4+b)*3]);
maxX = max(maxX, m_Patches[i][j].control[(a*4+b)*3]);
minY = min(minY, m_Patches[i][j].control[(a*4+b)*3+1]);
maxY = max(maxY, m_Patches[i][j].control[(a*4+b)*3+1]);
minZ = min(minZ, m_Patches[i][j].control[(a*4+b)*3+2]);
maxZ = max(maxZ, m_Patches[i][j].control[(a*4+b)*3+2]);
}
m_Patches[i][j].InitBox(minX, maxX, minY, maxY, minZ, maxZ);
}
}
// Generate mesh and store for later use.
void Terrain::Tesselate()
{
int i, j, a, steps = 10;
float x, y, z, inv;
for (i = 0; i < m_uPatches; i++)
for (j = 0; j < m_vPatches; j++)
m_Patches[i][j].Tesselate((m_nOptions & LC_TERRAIN_SMOOTH) != 0);
if ((m_nOptions & LC_TERRAIN_SMOOTH) != 0)
{
// fix normals at +u
for (i = 0; i < m_uPatches-1; i++)
for (j = 0; j < m_vPatches; j++)
for (a = 0; a < steps; a++)
{
x = m_Patches[i][j].normals[((steps-1)*steps+a)*3] + m_Patches[i+1][j].normals[a*3];
y = m_Patches[i][j].normals[((steps-1)*steps+a)*3+1] + m_Patches[i+1][j].normals[a*3+1];
z = m_Patches[i][j].normals[((steps-1)*steps+a)*3+2] + m_Patches[i+1][j].normals[a*3+2];
inv = 1.0f / (float)sqrt(x*x + y*y + z*z);
x *= inv;
y *= inv;
z *= inv;
m_Patches[i][j].normals[((steps-1)*steps+a)*3] = x;
m_Patches[i][j].normals[((steps-1)*steps+a)*3+1] = y;
m_Patches[i][j].normals[((steps-1)*steps+a)*3+2] = z;
m_Patches[i+1][j].normals[a*3] = x;
m_Patches[i+1][j].normals[a*3+1] = y;
m_Patches[i+1][j].normals[a*3+2] = z;
}
// and at +v
for (i = 0; i < m_uPatches; i++)
for (j = 0; j < m_vPatches-1; j++)
for (a = 0; a < steps; a++)
{
x = m_Patches[i][j].normals[((steps-1)+a*steps)*3] + m_Patches[i][j+1].normals[(a*steps)*3];
y = m_Patches[i][j].normals[((steps-1)+a*steps)*3+1] + m_Patches[i][j+1].normals[(a*steps)*3+1];
z = m_Patches[i][j].normals[((steps-1)+a*steps)*3+2] + m_Patches[i][j+1].normals[(a*steps)*3+2];
inv = 1.0f / (float)sqrt(x*x + y*y + z*z);
x *= inv;
y *= inv;
z *= inv;
m_Patches[i][j].normals[((steps-1)+a*steps)*3] = x;
m_Patches[i][j].normals[((steps-1)+a*steps)*3+1] = y;
m_Patches[i][j].normals[((steps-1)+a*steps)*3+2] = z;
m_Patches[i][j+1].normals[(a*steps)*3] = x;
m_Patches[i][j+1].normals[(a*steps)*3+1] = y;
m_Patches[i][j+1].normals[(a*steps)*3+2] = z;
}
}
}
void Terrain::Render(Camera* pCam, float aspect)
{
if (m_nOptions & LC_TERRAIN_FLAT)
{
float eye[3];
pCam->GetEyePos(eye);
glPushMatrix();
glTranslatef(eye[0], eye[1], 0);
glScalef(pCam->m_zFar, pCam->m_zFar, 1);
if (m_nOptions & LC_TERRAIN_TEXTURE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
m_pTexture->MakeCurrent();
glEnable(GL_TEXTURE_2D);
float tw = 15.0f, th = 15.0f;
// tw = 2*pCam->m_zFar/m_nBackgroundSize;
// th = 2*pCam->m_zFar/m_nBackgroundSize;
float tx, ty;
tx = (tw*eye[0])/(2*pCam->m_zFar);
ty = (th*eye[1])/(2*pCam->m_zFar);
glBegin(GL_QUADS);
glTexCoord2f(tx, ty);
glVertex2f(-1, -1);
glTexCoord2f(tx+tw, ty);
glVertex2f(1, -1);
glTexCoord2f(tx+tw, ty+th);
glVertex2f(1, 1);
glTexCoord2f(tx, ty+th);
glVertex2f(-1, 1);
glEnd();
glDisable(GL_TEXTURE_2D);
}
else
{
glColor3fv(m_fColor);
glBegin(GL_QUADS);
glVertex2f(-1, -1);
glVertex2f(1, -1);
glVertex2f(1, 1);
glVertex2f(-1, 1);
glEnd();
}
glPopMatrix();
}
else
{
FindVisiblePatches(pCam, aspect);
int i, j;
glColor3fv(m_fColor);
glEnableClientState(GL_VERTEX_ARRAY);
if (m_nOptions & LC_TERRAIN_TEXTURE)
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
m_pTexture->MakeCurrent();
glEnable(GL_TEXTURE_2D);
}
if (m_nOptions & LC_TERRAIN_SMOOTH)
glEnableClientState(GL_NORMAL_ARRAY);
for (i = 0; i < m_uPatches; i++)
for (j = 0; j < m_vPatches; j++)
if (m_Patches[i][j].visible)
{
glVertexPointer(3, GL_FLOAT, 0, m_Patches[i][j].vertex);
if (m_nOptions & LC_TERRAIN_SMOOTH)
glNormalPointer(GL_FLOAT, 0, m_Patches[i][j].normals);
if (m_nOptions & LC_TERRAIN_TEXTURE)
glTexCoordPointer(2, GL_FLOAT, 0, m_Patches[i][j].coords);
glDrawElements(GL_TRIANGLES, (m_Patches[i][j].steps-1)*(m_Patches[i][j].steps-1)*6, GL_UNSIGNED_SHORT, m_Patches[i][j].index);
}
if (m_nOptions & LC_TERRAIN_SMOOTH)
glDisableClientState(GL_NORMAL_ARRAY);
if (m_nOptions & LC_TERRAIN_TEXTURE)
{
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
}
}
void Terrain::FindVisiblePatches(Camera* pCam, float aspect)
{
// Get camera position information.
float eye[3];
pCam->GetEyePos(eye);
// Get perspective information.
float alpha = pCam->m_fovy / 2.0f;
float halfFovY = pCam->m_fovy / 2.0f;
halfFovY = halfFovY * 3.1415f / 180.0f;
float halfFovX = (float)atan(tan(halfFovY) * aspect);
halfFovX = halfFovX * 180.0f / 3.1415f;
float beta = 2.0f * halfFovX;
// Get vector stuff from the position.
float nonOrthoTop[3], target[3];
pCam->GetUpVec(nonOrthoTop);
pCam->GetTargetPos(target);
float front[3] = { target[0] - eye[0], target[1] - eye[1], target[2] - eye[2]};
float side[3];
side[0] = nonOrthoTop[1]*front[2] - nonOrthoTop[2]*front[1];
side[1] = nonOrthoTop[2]*front[0] - nonOrthoTop[0]*front[2];
side[2] = nonOrthoTop[0]*front[1] - nonOrthoTop[1]*front[0];
// Make sure our up vector is orthogonal.
float top[3];
top[0] = front[1]*side[2] - front[2]*side[1];
top[1] = front[2]*side[0] - front[0]*side[2];
top[2] = front[0]*side[1] - front[1]*side[0];
// Get our plane normals.
Matrix mat;
float topNormal[3] = { -top[0], -top[1], -top[2] };
mat.FromAxisAngle(side, -alpha);
mat.TransformPoints(topNormal, 1);
float bottomNormal[3] = { top[0], top[1], top[2] };
mat.FromAxisAngle(side, alpha);
mat.TransformPoints(bottomNormal, 1);
float rightNormal[3] = { side[0], side[1], side[2] };
mat.FromAxisAngle(top, -beta);
mat.TransformPoints(rightNormal, 1);
float leftNormal[3] = { -side[0], -side[1], -side[2] };
mat.FromAxisAngle(top, beta);
mat.TransformPoints(leftNormal, 1);
float nearNormal[3] = { front[0], front[1], front[2] };
// Now calculate our plane offsets from the normals and the eye position.
float topD = eye[0]*-topNormal[0] + eye[1]*-topNormal[1] + eye[2]*-topNormal[2];
float bottomD = eye[0]*-bottomNormal[0] + eye[1]*-bottomNormal[1] + eye[2]*-bottomNormal[2];
float leftD = eye[0]*-leftNormal[0] + eye[1]*-leftNormal[1] + eye[2]*-leftNormal[2];
float rightD = eye[0]*-rightNormal[0] + eye[1]*-rightNormal[1] + eye[2]*-rightNormal[2];
float nearD = eye[0]*-nearNormal[0] + eye[1]*-nearNormal[1] + eye[2]*-nearNormal[2];
// For the far plane, find the point farDist away from the eye along the front vector.
float farDist = pCam->m_zFar;
float farPt[3] = { front[0], front[1], front[2] };
float invR = farDist/(float)sqrt(farPt[0]*farPt[0]+farPt[1]*farPt[1]+farPt[2]*farPt[2]);
farPt[0] = farPt[0]*invR;
farPt[1] = farPt[1]*invR;
farPt[2] = farPt[2]*invR;
farPt[0] += eye[0];
farPt[1] += eye[1];
farPt[2] += eye[2];
float farD = farPt[0]*nearNormal[0] + farPt[1]*nearNormal[1] + farPt[2]*nearNormal[2];
// Now generate the planes
invR = 1.0f/(float)sqrt(topNormal[0]*topNormal[0]+topNormal[1]*topNormal[1]+topNormal[2]*topNormal[2]);
float topPlane[4] = { topNormal[0]*invR, topNormal[1]*invR, topNormal[2]*invR, topD*invR };
invR = 1.0f/(float)sqrt(bottomNormal[0]*bottomNormal[0]+bottomNormal[1]*bottomNormal[1]+bottomNormal[2]*bottomNormal[2]);
float bottomPlane[4] = { bottomNormal[0]*invR, bottomNormal[1]*invR, bottomNormal[2]*invR, bottomD*invR };
invR = 1.0f/(float)sqrt(leftNormal[0]*leftNormal[0]+leftNormal[1]*leftNormal[1]+leftNormal[2]*leftNormal[2]);
float leftPlane[4] = { leftNormal[0]*invR, leftNormal[1]*invR, leftNormal[2]*invR, leftD*invR };
invR = 1.0f/(float)sqrt(rightNormal[0]*rightNormal[0]+rightNormal[1]*rightNormal[1]+rightNormal[2]*rightNormal[2]);
float rightPlane[4] = { rightNormal[0]*invR, rightNormal[1]*invR, rightNormal[2]*invR, rightD*invR };
invR = 1.0f/(float)sqrt(nearNormal[0]*nearNormal[0]+nearNormal[1]*nearNormal[1]+nearNormal[2]*nearNormal[2]);
float nearPlane[4] = { nearNormal[0]*invR, nearNormal[1]*invR, nearNormal[2]*invR, nearD*invR };
invR = 1.0f/(float)sqrt(-nearNormal[0]*-nearNormal[0]+-nearNormal[1]*-nearNormal[1]+-nearNormal[2]*-nearNormal[2]);
float farPlane[4] = { -nearNormal[0]*invR, -nearNormal[1]*invR, -nearNormal[2]*invR, farD*invR };
for (int i = 0; i < m_uPatches; i++)
{
for (int j = 0; j < m_vPatches; j++)
{
m_Patches[i][j].visible = true;
if (m_Patches[i][j].BoxIsOutside(leftPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(rightPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(nearPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(farPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(bottomPlane))
{
m_Patches[i][j].visible = false;
continue;
}
if (m_Patches[i][j].BoxIsOutside(topPlane))
{
m_Patches[i][j].visible = false;
continue;
}
}
}
}
void Terrain::LoadDefaults(bool bLinear)
{
unsigned long rgb = Sys_ProfileLoadInt ("Default", "Floor", RGB (0,191,0));
m_fColor[0] = (float)((unsigned char) (rgb))/255;
m_fColor[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
m_fColor[2] = (float)((unsigned char) ((rgb) >> 16))/255;
m_uSize = 50;
m_vSize = 50;
strcpy (m_strTexture, Sys_ProfileLoadString ("Default", "FloorBMP", ""));
m_pTexture->Unload();
m_nOptions = LC_TERRAIN_FLAT;
if (strlen(m_strTexture))
{
m_nOptions |= LC_TERRAIN_TEXTURE;
LoadTexture(bLinear);
}
SetPatchCount(4, 4);
for (int i = 0; i < 13; i++)
for (int j = 0; j < 13; j++)
{
m_pControl[i][j*3] = m_uSize * ((float)i/12-0.5f);
m_pControl[i][j*3+1] = m_vSize * ((float)j/12-0.5f);
m_pControl[i][j*3+2] = 0;
}
SetControlPoints();
Tesselate();
}
void Terrain::LoadTexture(bool bLinear)
{
m_pTexture->Unload();
if ((m_nOptions & LC_TERRAIN_TEXTURE) == 0)
return;
if (m_pTexture->LoadFromFile(m_strTexture, bLinear) == false)
{
// AfxMessageBox("Could not load terrain texture.", MB_OK|MB_ICONEXCLAMATION);
m_nOptions &= ~LC_TERRAIN_TEXTURE;
}
}
void Terrain::GenerateRandom()
{
srand((unsigned)time(NULL));
int uCount = (m_uPatches*3)+1, vCount = (m_vPatches*3)+1;
for (int i = 0; i < uCount; i++)
for (int j = 0; j < vCount; j++)
{
m_pControl[i][j*3] = m_uSize * ((float)i/(uCount-1)-0.5f);
m_pControl[i][j*3+1] = m_vSize * ((float)j/(vCount-1)-0.5f);
m_pControl[i][j*3+2] = (((float)rand()/(float)RAND_MAX) - 0.5f) * 8;
}
SetControlPoints();
Tesselate();
}