2271 lines
50 KiB
C++
2271 lines
50 KiB
C++
// * This file is part of the COLOBOT source code
|
|
// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
|
|
// *
|
|
// * This program is free software: you can redistribute it and/or modify
|
|
// * it under the terms of the GNU General Public License as published by
|
|
// * the Free Software Foundation, either version 3 of the License, or
|
|
// * (at your option) any later version.
|
|
// *
|
|
// * This program is distributed in the hope that it will be useful,
|
|
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// * GNU General Public License for more details.
|
|
// *
|
|
// * You should have received a copy of the GNU General Public License
|
|
// * along with this program. If not, see http://www.gnu.org/licenses/.
|
|
|
|
// terrain.cpp
|
|
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <d3d.h>
|
|
|
|
#include "common/struct.h"
|
|
#include "math/const.h"
|
|
#include "math/geometry.h"
|
|
#include "graphics/d3d/d3dengine.h"
|
|
#include "math/old/d3dmath.h"
|
|
#include "graphics/d3d/d3dutil.h"
|
|
#include "common/language.h"
|
|
#include "common/event.h"
|
|
#include "common/misc.h"
|
|
#include "common/iman.h"
|
|
#include "math/old/math3d.h"
|
|
#include "common/modfile.h"
|
|
#include "graphics/common/water.h"
|
|
#include "graphics/common/terrain.h"
|
|
|
|
|
|
const int BMPHEAD = 1078;
|
|
|
|
|
|
|
|
// Constructor of the terrain.
|
|
|
|
CTerrain::CTerrain(CInstanceManager* iMan)
|
|
{
|
|
m_iMan = iMan;
|
|
m_iMan->AddInstance(CLASS_TERRAIN, this);
|
|
|
|
m_engine = (CD3DEngine*)m_iMan->SearchInstance(CLASS_ENGINE);
|
|
m_water = (CWater*)m_iMan->SearchInstance(CLASS_WATER);
|
|
|
|
m_mosaic = 20;
|
|
m_brick = 1<<4;
|
|
m_size = 10.0f;
|
|
m_vision = 200.0f;
|
|
m_relief = 0;
|
|
m_texture = 0;
|
|
m_objRank = 0;
|
|
m_scaleMapping = 0.01f;
|
|
m_scaleRelief = 1.0f;
|
|
m_subdivMapping = 1;
|
|
m_depth = 2;
|
|
m_texBaseName[0]= 0;
|
|
m_texBaseExt[0] = 0;
|
|
m_bMultiText = true;
|
|
m_bLevelText = false;
|
|
m_resources = 0;
|
|
m_levelMatTotal = 0;
|
|
m_levelMatMax = 0;
|
|
m_levelDot = 0;
|
|
m_wind = Math::Vector(0.0f, 0.0f, 0.0f);
|
|
m_defHardness = 0.5f;
|
|
|
|
FlushBuildingLevel();
|
|
FlushFlyingLimit();
|
|
}
|
|
|
|
// Destructor of the terrain.
|
|
|
|
CTerrain::~CTerrain()
|
|
{
|
|
free(m_relief);
|
|
free(m_texture);
|
|
free(m_objRank);
|
|
free(m_resources);
|
|
}
|
|
|
|
|
|
// Generates a new flat terrain.
|
|
// The terrain is composed of mosaics, themselves composed of bricks.
|
|
// Each brick is composed of two triangles.
|
|
// mosaic: number of mosaics along the axes X and Z
|
|
// brick: number of bricks (power of 2)
|
|
// size: size of a brick along the axes X and Z
|
|
// vision: vision before a change of resolution
|
|
// scaleMapping: scale textures for mapping
|
|
//
|
|
// ^ z
|
|
// | <---> brick*size
|
|
// +---+---+---+---+
|
|
// | | | |_|_| mosaic = 4
|
|
// | | | | | | brick = 2 (brickP2=1)
|
|
// +---+---+---+---+
|
|
// |\ \| | | |
|
|
// |\ \| | | |
|
|
// +---+---o---+---+---> x
|
|
// | | | | |
|
|
// | | | | |
|
|
// +---+---+---+---+
|
|
// | | | | | The land is viewed from above here.
|
|
// | | | | |
|
|
// +---+---+---+---+
|
|
// <---------------> mosaic*brick*size
|
|
|
|
bool CTerrain::Generate(int mosaic, int brickP2, float size, float vision,
|
|
int depth, float hardness)
|
|
{
|
|
int dim;
|
|
|
|
m_mosaic = mosaic;
|
|
m_brick = 1<<brickP2;
|
|
m_size = size;
|
|
m_vision = vision;
|
|
m_depth = depth;
|
|
m_defHardness = hardness;
|
|
|
|
m_engine->SetTerrainVision(vision);
|
|
|
|
m_bMultiText = true;
|
|
m_bLevelText = false;
|
|
m_scaleMapping = 1.0f/(m_brick*m_size);
|
|
m_subdivMapping = 1;
|
|
|
|
dim = (m_mosaic*m_brick+1)*(m_mosaic*m_brick+1);
|
|
m_relief = (float*)malloc(sizeof(float)*dim);
|
|
ZeroMemory(m_relief, sizeof(float)*dim);
|
|
|
|
dim = m_mosaic*m_subdivMapping*m_mosaic*m_subdivMapping;
|
|
m_texture = (int*)malloc(sizeof(int)*dim);
|
|
ZeroMemory(m_texture, sizeof(int)*dim);
|
|
|
|
dim = m_mosaic*m_mosaic;
|
|
m_objRank = (int*)malloc(sizeof(int)*dim);
|
|
ZeroMemory(m_objRank, sizeof(int)*dim);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int CTerrain::RetMosaic()
|
|
{
|
|
return m_mosaic;
|
|
}
|
|
|
|
int CTerrain::RetBrick()
|
|
{
|
|
return m_brick;
|
|
}
|
|
|
|
float CTerrain::RetSize()
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
float CTerrain::RetScaleRelief()
|
|
{
|
|
return m_scaleRelief;
|
|
}
|
|
|
|
|
|
// Initializes the names of textures to use for the land.
|
|
|
|
bool CTerrain::InitTextures(char* baseName, int* table, int dx, int dy)
|
|
{
|
|
int x, y;
|
|
char* p;
|
|
|
|
m_bLevelText = false;
|
|
|
|
strcpy(m_texBaseName, baseName);
|
|
p = strchr(m_texBaseName, '.'); // p <- ^beginning of the extension
|
|
if ( p == 0 )
|
|
{
|
|
strcpy(m_texBaseExt, ".tga");
|
|
}
|
|
else
|
|
{
|
|
strcpy(m_texBaseExt, p); // m_texBaseExt <- ".tga" or ".bmp"
|
|
*p = 0; // m_texBaseName <- name without extension
|
|
}
|
|
|
|
for ( y=0 ; y<m_mosaic*m_subdivMapping ; y++ )
|
|
{
|
|
for ( x=0 ; x<m_mosaic*m_subdivMapping ; x++ )
|
|
{
|
|
m_texture[x+y*m_mosaic] = table[(x%dx)+(y%dy)*dx];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// Empties level.
|
|
|
|
void CTerrain::LevelFlush()
|
|
{
|
|
m_levelMatTotal = 0;
|
|
m_levelMatMax = 0;
|
|
m_levelID = 1000;
|
|
LevelCloseTable();
|
|
}
|
|
|
|
// Initializes the names of textures to use for the land.
|
|
|
|
bool CTerrain::LevelMaterial(int id, char* baseName, float u, float v,
|
|
int up, int right, int down, int left,
|
|
float hardness)
|
|
{
|
|
int i;
|
|
|
|
i = m_levelMatTotal;
|
|
if ( i >= MAXMATTERRAIN-1 ) return false;
|
|
|
|
LevelOpenTable();
|
|
|
|
if ( id == 0 )
|
|
{
|
|
id = m_levelID++; // puts an ID internal standard
|
|
}
|
|
|
|
strcpy(m_levelMat[i].texName, baseName);
|
|
m_levelMat[i].id = id;
|
|
m_levelMat[i].u = u;
|
|
m_levelMat[i].v = v;
|
|
m_levelMat[i].mat[0] = up;
|
|
m_levelMat[i].mat[1] = right;
|
|
m_levelMat[i].mat[2] = down;
|
|
m_levelMat[i].mat[3] = left;
|
|
m_levelMat[i].hardness = hardness;
|
|
|
|
if ( m_levelMatMax < up+1 ) m_levelMatMax = up+1;
|
|
if ( m_levelMatMax < right+1 ) m_levelMatMax = right+1;
|
|
if ( m_levelMatMax < down+1 ) m_levelMatMax = down+1;
|
|
if ( m_levelMatMax < left+1 ) m_levelMatMax = left+1;
|
|
|
|
m_bLevelText = true;
|
|
m_subdivMapping = 4;
|
|
|
|
m_levelMatTotal ++;
|
|
return true;
|
|
}
|
|
|
|
|
|
// Load relief from a BMP file.
|
|
// The size of the image must be dimension dx and dy with dx=dy=(mosaic*brick)+1.
|
|
// The image must be 8 bits/pixel, 256 colors with a standard pallet.
|
|
|
|
// Converts coordinated image (x;y) -> world (x;-;z) :
|
|
// Wx = 5*Ix-400
|
|
// Wz = -(5*Iy-400)
|
|
|
|
// Converts coordinated world (x;-;z) -> image (x;y) :
|
|
// Ix = (400+Wx)/5
|
|
// Iy = (400-Wz)/5
|
|
|
|
bool CTerrain::ResFromBMP(const char* filename)
|
|
{
|
|
FILE* file;
|
|
int size, sizem;
|
|
|
|
file = fopen(filename, "rb");
|
|
if ( file == NULL ) return false;
|
|
|
|
size = (m_mosaic*m_brick)+1;
|
|
sizem = ((size+4-1)/4)*4; // upper size multiple of 4
|
|
|
|
if ( m_resources != 0 )
|
|
{
|
|
free(m_resources);
|
|
}
|
|
|
|
m_resources = (unsigned char*)malloc(BMPHEAD+sizem*size);
|
|
fread(m_resources, BMPHEAD+sizem*size, 1, file);
|
|
|
|
if ( m_resources[18] != (size&0xff) || m_resources[19] != (size>>8) ||
|
|
m_resources[22] != (size&0xff) || m_resources[23] != (size>>8) )
|
|
{
|
|
free(m_resources);
|
|
m_resources = 0;
|
|
fclose(file);
|
|
return false;
|
|
}
|
|
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
|
|
// Returns the resource type available underground.
|
|
|
|
TerrainRes CTerrain::RetResource(const Math::Vector &p)
|
|
{
|
|
int x, y, size, sizem, ress;
|
|
|
|
if ( m_resources == 0 ) return TR_NULL;
|
|
|
|
x = (int)((p.x + (m_mosaic*m_brick*m_size)/2.0f)/m_size);
|
|
y = (int)((p.z + (m_mosaic*m_brick*m_size)/2.0f)/m_size);
|
|
|
|
if ( x < 0 || x > m_mosaic*m_brick ||
|
|
y < 0 || y > m_mosaic*m_brick ) return TR_NULL;
|
|
|
|
size = (m_mosaic*m_brick)+1;
|
|
sizem = ((size+4-1)/4)*4; // upper size multiple of 4
|
|
|
|
ress = m_resources[BMPHEAD+x+sizem*y];
|
|
if ( ress == 5 ) return TR_STONE; // red?
|
|
if ( ress == 35 ) return TR_URANIUM; // yellow?
|
|
if ( ress == 30 ) return TR_POWER; // green?
|
|
if ( ress == 24 ) return TR_KEYa; // ~green?
|
|
if ( ress == 25 ) return TR_KEYb; // ~green?
|
|
if ( ress == 26 ) return TR_KEYc; // ~green?
|
|
if ( ress == 27 ) return TR_KEYd; // ~green?
|
|
|
|
return TR_NULL;
|
|
}
|
|
|
|
|
|
// Initializes a completely flat terrain.
|
|
|
|
void CTerrain::FlushRelief()
|
|
{
|
|
free(m_relief);
|
|
m_relief = 0;
|
|
}
|
|
|
|
// Load relief from a BMP file.
|
|
// The size of the image must be dimension dx and dy with dx=dy=(mosaic*brick)+1.
|
|
// The image must be 8 bits/pixel, 256 gray scale:
|
|
// white = ground (y=0)
|
|
// black = mountain (y=255*scaleRelief)
|
|
|
|
// Converts coordinated image(x;y) -> world (x;-;z) :
|
|
// Wx = 5*Ix-400
|
|
// Wz = -(5*Iy-400)
|
|
|
|
// Converts coordinated world (x;-;z) -> image (x;y) :
|
|
// Ix = (400+Wx)/5
|
|
// Iy = (400-Wz)/5
|
|
|
|
bool CTerrain::ReliefFromBMP(const char* filename, float scaleRelief,
|
|
bool adjustBorder)
|
|
{
|
|
FILE* file;
|
|
unsigned char* buffer;
|
|
int size, sizem, x, y;
|
|
float level, limit, dist, border;
|
|
|
|
m_scaleRelief = scaleRelief;
|
|
|
|
file = fopen(filename, "rb");
|
|
if ( file == NULL ) return false;
|
|
|
|
size = (m_mosaic*m_brick)+1;
|
|
sizem = ((size+4-1)/4)*4; // upper size multiple of 4
|
|
|
|
buffer = (unsigned char*)malloc(BMPHEAD+sizem*size);
|
|
fread(buffer, BMPHEAD+sizem*size, 1, file);
|
|
|
|
if ( buffer[18] != (size&0xff) || buffer[19] != (size>>8) ||
|
|
buffer[22] != (size&0xff) || buffer[23] != (size>>8) )
|
|
{
|
|
free(buffer);
|
|
fclose(file);
|
|
return false;
|
|
}
|
|
|
|
limit = 0.9f;
|
|
for ( y=0 ; y<size ; y++ )
|
|
{
|
|
for ( x=0 ; x<size ; x++ )
|
|
{
|
|
level = (255-buffer[BMPHEAD+x+sizem*y])*scaleRelief;
|
|
|
|
//? dist = Length((float)(x-size/2), (float)(y-size/2));
|
|
dist = Math::Max(fabs((float)(x-size/2)), fabs((float)(y-size/2)));
|
|
dist = dist/(float)(size/2);
|
|
if ( dist > limit && adjustBorder )
|
|
{
|
|
dist = (dist-limit)/(1.0f-limit); // 0..1
|
|
if ( dist > 1.0f ) dist = 1.0f;
|
|
border = 300.0f+Math::Rand()*20.0f;
|
|
level = level+dist*(border-level);
|
|
}
|
|
|
|
m_relief[x+y*size] = level;
|
|
}
|
|
}
|
|
|
|
free(buffer);
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
|
|
// Adds a point of elevation in the buffer of relief.
|
|
|
|
bool CTerrain::ReliefAddDot(Math::Vector pos, float scaleRelief)
|
|
{
|
|
float dim;
|
|
int size, x, y;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
size = (m_mosaic*m_brick)+1;
|
|
|
|
pos.x = (pos.x+dim)/m_size;
|
|
pos.z = (pos.z+dim)/m_size;
|
|
|
|
x = (int)pos.x;
|
|
y = (int)pos.z;
|
|
|
|
if ( x < 0 || x >= size ||
|
|
y < 0 || y >= size ) return false;
|
|
|
|
if ( m_relief[x+y*size] < pos.y*scaleRelief )
|
|
{
|
|
m_relief[x+y*size] = pos.y*scaleRelief;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Load relief from a DXF file.
|
|
|
|
bool CTerrain::ReliefFromDXF(const char* filename, float scaleRelief)
|
|
{
|
|
FILE* file = NULL;
|
|
char line[100];
|
|
int command, rankSommet, nbSommet, nbFace, size;
|
|
Math::Vector* table;
|
|
bool bWaitNbSommet;
|
|
bool bWaitNbFace;
|
|
bool bWaitSommetX;
|
|
bool bWaitSommetY;
|
|
bool bWaitSommetZ;
|
|
bool bWaitFaceX;
|
|
bool bWaitFaceY;
|
|
bool bWaitFaceZ;
|
|
float x,y,z;
|
|
int p1,p2,p3;
|
|
|
|
ZeroMemory(m_relief, sizeof(float)*(m_mosaic*m_brick+1)*(m_mosaic*m_brick+1));
|
|
|
|
file = fopen(filename, "r");
|
|
if ( file == NULL ) return false;
|
|
|
|
size = (m_mosaic*m_brick)+1;
|
|
table = (Math::Vector*)malloc(sizeof(Math::Vector)*size*size);
|
|
|
|
rankSommet = 0;
|
|
bWaitNbSommet = false;
|
|
bWaitNbFace = false;
|
|
bWaitSommetX = false;
|
|
bWaitSommetY = false;
|
|
bWaitSommetZ = false;
|
|
bWaitFaceX = false;
|
|
bWaitFaceY = false;
|
|
bWaitFaceZ = false;
|
|
|
|
while ( fgets(line, 100, file) != NULL )
|
|
{
|
|
sscanf(line, "%d", &command);
|
|
if ( fgets(line, 100, file) == NULL ) break;
|
|
|
|
if ( command == 66 )
|
|
{
|
|
bWaitNbSommet = true;
|
|
}
|
|
|
|
if ( command == 71 && bWaitNbSommet )
|
|
{
|
|
bWaitNbSommet = false;
|
|
sscanf(line, "%d", &nbSommet);
|
|
if ( nbSommet > size*size ) nbSommet = size*size;
|
|
rankSommet = 0;
|
|
bWaitNbFace = true;
|
|
}
|
|
|
|
if ( command == 72 && bWaitNbFace )
|
|
{
|
|
bWaitNbFace = false;
|
|
sscanf(line, "%d", &nbFace);
|
|
bWaitSommetX = true;
|
|
}
|
|
|
|
if ( command == 10 && bWaitSommetX )
|
|
{
|
|
bWaitSommetX = false;
|
|
sscanf(line, "%f", &x);
|
|
bWaitSommetY = true;
|
|
}
|
|
|
|
if ( command == 20 && bWaitSommetY )
|
|
{
|
|
bWaitSommetY = false;
|
|
sscanf(line, "%f", &y);
|
|
bWaitSommetZ = true;
|
|
}
|
|
|
|
if ( command == 30 && bWaitSommetZ )
|
|
{
|
|
bWaitSommetZ = false;
|
|
sscanf(line, "%f", &z);
|
|
|
|
nbSommet --;
|
|
if ( nbSommet >= 0 )
|
|
{
|
|
Math::Vector p(x,z,y); // permutation of Y and Z!
|
|
table[rankSommet++] = p;
|
|
bWaitSommetX = true;
|
|
}
|
|
else
|
|
{
|
|
bWaitFaceX = true;
|
|
}
|
|
}
|
|
|
|
if ( command == 71 && bWaitFaceX )
|
|
{
|
|
bWaitFaceX = false;
|
|
sscanf(line, "%d", &p1);
|
|
if ( p1 < 0 ) p1 = -p1;
|
|
bWaitFaceY = true;
|
|
}
|
|
|
|
if ( command == 72 && bWaitFaceY )
|
|
{
|
|
bWaitFaceY = false;
|
|
sscanf(line, "%d", &p2);
|
|
if ( p2 < 0 ) p2 = -p2;
|
|
bWaitFaceZ = true;
|
|
}
|
|
|
|
if ( command == 73 && bWaitFaceZ )
|
|
{
|
|
bWaitFaceZ = false;
|
|
sscanf(line, "%d", &p3);
|
|
if ( p3 < 0 ) p3 = -p3;
|
|
|
|
nbFace --;
|
|
if ( nbFace >= 0 )
|
|
{
|
|
ReliefAddDot(table[p3-1], scaleRelief);
|
|
ReliefAddDot(table[p2-1], scaleRelief);
|
|
ReliefAddDot(table[p1-1], scaleRelief);
|
|
bWaitFaceX = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
free(table);
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
|
|
|
|
// Adjusts a position so that it does not exceed the boundaries.
|
|
|
|
void CTerrain::LimitPos(Math::Vector &pos)
|
|
{
|
|
float dim;
|
|
|
|
#if _TEEN
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f*0.98f;
|
|
#else
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f*0.92f;
|
|
#endif
|
|
|
|
if ( pos.x < -dim ) pos.x = -dim;
|
|
if ( pos.x > dim ) pos.x = dim;
|
|
if ( pos.z < -dim ) pos.z = -dim;
|
|
if ( pos.z > dim ) pos.z = dim;
|
|
}
|
|
|
|
|
|
// Adjust the edges of each mosaic to be compatible with all lower resolutions.
|
|
|
|
void CTerrain::AdjustRelief()
|
|
{
|
|
int x, y, xx, yy, ii, b;
|
|
float level1, level2;
|
|
|
|
if ( m_depth == 1 ) return;
|
|
|
|
ii = m_mosaic*m_brick+1;
|
|
b = 1<<(m_depth-1);
|
|
|
|
for ( y=0 ; y<m_mosaic*m_brick ; y+=b )
|
|
{
|
|
for ( x=0 ; x<m_mosaic*m_brick ; x+=b )
|
|
{
|
|
yy = 0;
|
|
if ( (y+yy)%m_brick == 0 )
|
|
{
|
|
level1 = m_relief[(x+0)+(y+yy)*ii];
|
|
level2 = m_relief[(x+b)+(y+yy)*ii];
|
|
for ( xx=1 ; xx<b ; xx++ )
|
|
{
|
|
m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*xx+level1;
|
|
}
|
|
}
|
|
|
|
yy = b;
|
|
if ( (y+yy)%m_brick == 0 )
|
|
{
|
|
level1 = m_relief[(x+0)+(y+yy)*ii];
|
|
level2 = m_relief[(x+b)+(y+yy)*ii];
|
|
for ( xx=1 ; xx<b ; xx++ )
|
|
{
|
|
m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*xx+level1;
|
|
}
|
|
}
|
|
|
|
xx = 0;
|
|
if ( (x+xx)%m_brick == 0 )
|
|
{
|
|
level1 = m_relief[(x+xx)+(y+0)*ii];
|
|
level2 = m_relief[(x+xx)+(y+b)*ii];
|
|
for ( yy=1 ; yy<b ; yy++ )
|
|
{
|
|
m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*yy+level1;
|
|
}
|
|
}
|
|
|
|
xx = b;
|
|
if ( (x+xx)%m_brick == 0 )
|
|
{
|
|
level1 = m_relief[(x+xx)+(y+0)*ii];
|
|
level2 = m_relief[(x+xx)+(y+b)*ii];
|
|
for ( yy=1 ; yy<b ; yy++ )
|
|
{
|
|
m_relief[(x+xx)+(y+yy)*ii] = ((level2-level1)/b)*yy+level1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Calculates a vector of the terrain.
|
|
|
|
Math::Vector CTerrain::RetVector(int x, int y)
|
|
{
|
|
Math::Vector p;
|
|
|
|
p.x = x*m_size - (m_mosaic*m_brick*m_size)/2;
|
|
p.z = y*m_size - (m_mosaic*m_brick*m_size)/2;
|
|
|
|
if ( m_relief != 0 &&
|
|
x >= 0 && x <= m_mosaic*m_brick &&
|
|
y >= 0 && y <= m_mosaic*m_brick )
|
|
{
|
|
p.y = m_relief[x+y*(m_mosaic*m_brick+1)];
|
|
}
|
|
else
|
|
{
|
|
p.y = 0.0f;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
// Calculates a vertex of the terrain.
|
|
// Calculates a normal soft, taking into account the six adjacent triangles:
|
|
//
|
|
// ^ y
|
|
// |
|
|
// b---c---+
|
|
// |\ |\ |
|
|
// | \| \|
|
|
// a---o---d
|
|
// |\ |\ |
|
|
// | \| \|
|
|
// +---f---e--> x
|
|
|
|
D3DVERTEX2 CTerrain::RetVertex(int x, int y, int step)
|
|
{
|
|
D3DVERTEX2 v;
|
|
Math::Vector o, oo, a,b,c,d,e,f, n, s;
|
|
int brick;
|
|
|
|
o = RetVector(x, y);
|
|
v.x = o.x;
|
|
v.y = o.y;
|
|
v.z = o.z;
|
|
|
|
a = RetVector(x-step, y );
|
|
b = RetVector(x-step, y+step);
|
|
c = RetVector(x, y+step);
|
|
d = RetVector(x+step, y );
|
|
e = RetVector(x+step, y-step);
|
|
f = RetVector(x, y-step);
|
|
|
|
s = Math::Vector(0.0f, 0.0f, 0.0f);
|
|
|
|
if ( x-step >= 0 && y+step <= m_mosaic*m_brick+1 )
|
|
{
|
|
s += Math::NormalToPlane(b,a,o);
|
|
s += Math::NormalToPlane(c,b,o);
|
|
}
|
|
|
|
if ( x+step <= m_mosaic*m_brick+1 && y+step <= m_mosaic*m_brick+1 )
|
|
{
|
|
s += Math::NormalToPlane(d,c,o);
|
|
}
|
|
|
|
if ( x+step <= m_mosaic*m_brick+1 && y-step >= 0 )
|
|
{
|
|
s += Math::NormalToPlane(e,d,o);
|
|
s += Math::NormalToPlane(f,e,o);
|
|
}
|
|
|
|
if ( x-step >= 0 && y-step >= 0 )
|
|
{
|
|
s += Math::NormalToPlane(a,f,o);
|
|
}
|
|
|
|
s = Normalize(s);
|
|
v.nx = s.x;
|
|
v.ny = s.y;
|
|
v.nz = s.z;
|
|
|
|
if ( m_bMultiText )
|
|
{
|
|
brick = m_brick/m_subdivMapping;
|
|
oo = RetVector((x/brick)*brick, (y/brick)*brick);
|
|
o = RetVector(x, y);
|
|
v.tu = (o.x-oo.x)*m_scaleMapping*m_subdivMapping;
|
|
v.tv = 1.0f - (o.z-oo.z)*m_scaleMapping*m_subdivMapping;
|
|
}
|
|
else
|
|
{
|
|
v.tu = o.x*m_scaleMapping;
|
|
v.tv = o.z*m_scaleMapping;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
// Creates all objects of a mosaic.
|
|
// The origin of mosaic is his center.
|
|
//
|
|
// ^ z
|
|
// |
|
|
// | 2---4---6--
|
|
// | |\ |\ |\
|
|
// | | \| \|
|
|
// | 1---3---5--- ...
|
|
// |
|
|
// +-------------------> x
|
|
|
|
bool CTerrain::CreateMosaic(int ox, int oy, int step, int objRank,
|
|
const D3DMATERIAL7 &mat,
|
|
float min, float max)
|
|
{
|
|
Math::Matrix transform;
|
|
D3DVERTEX2 o, p1, p2;
|
|
D3DObjLevel6* buffer;
|
|
Math::Point uv;
|
|
int brick, total, size, mx, my, x, y, xx, yy, i;
|
|
char texName1[20];
|
|
char texName2[20];
|
|
float pixel, dp;
|
|
|
|
if ( step == 1 && m_engine->RetGroundSpot() )
|
|
{
|
|
i = (ox/5) + (oy/5)*(m_mosaic/5);
|
|
sprintf(texName2, "shadow%.2d.tga", i);
|
|
}
|
|
else
|
|
{
|
|
texName2[0] = 0;
|
|
}
|
|
|
|
brick = m_brick/m_subdivMapping;
|
|
|
|
o = RetVertex(ox*m_brick+m_brick/2, oy*m_brick+m_brick/2, step);
|
|
total = ((brick/step)+1)*2;
|
|
size = sizeof(D3DObjLevel6)+sizeof(D3DVERTEX2)*(total-1);
|
|
|
|
pixel = 1.0f/256.0f; // 1 pixel cover (*)
|
|
//? dp = 0.5f/512.0f;
|
|
dp = 1.0f/512.0f;
|
|
|
|
for ( my=0 ; my<m_subdivMapping ; my++ )
|
|
{
|
|
for ( mx=0 ; mx<m_subdivMapping ; mx++ )
|
|
{
|
|
if ( m_bLevelText )
|
|
{
|
|
xx = ox*m_brick + mx*(m_brick/m_subdivMapping);
|
|
yy = oy*m_brick + my*(m_brick/m_subdivMapping);
|
|
LevelTextureName(xx, yy, texName1, uv);
|
|
}
|
|
else
|
|
{
|
|
i = (ox*m_subdivMapping+mx)+(oy*m_subdivMapping+my)*m_mosaic;
|
|
sprintf(texName1, "%s%.3d%s", m_texBaseName, m_texture[i], m_texBaseExt);
|
|
}
|
|
|
|
for ( y=0 ; y<brick ; y+=step )
|
|
{
|
|
buffer = (D3DObjLevel6*)malloc(size);
|
|
ZeroMemory(buffer, sizeof(D3DObjLevel6));
|
|
buffer->totalPossible = total;
|
|
buffer->totalUsed = total;
|
|
buffer->type = D3DTYPE6S;
|
|
buffer->material = mat;
|
|
if ( m_bMultiText )
|
|
{
|
|
//? buffer->state = D3DSTATENORMAL;
|
|
buffer->state = D3DSTATEWRAP;
|
|
}
|
|
else
|
|
{
|
|
buffer->state = D3DSTATEWRAP;
|
|
}
|
|
buffer->state |= D3DSTATESECOND;
|
|
if ( step == 1 )
|
|
{
|
|
buffer->state |= D3DSTATEDUALb;
|
|
}
|
|
i = 0;
|
|
for ( x=0 ; x<=brick ; x+=step )
|
|
{
|
|
p1 = RetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+0 , step);
|
|
p2 = RetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+step, step);
|
|
p1.x -= o.x; p1.z -= o.z;
|
|
p2.x -= o.x; p2.z -= o.z;
|
|
|
|
if ( m_bMultiText )
|
|
{
|
|
if ( x == 0 )
|
|
{
|
|
p1.tu = 0.0f+(0.5f/256.0f);
|
|
p2.tu = 0.0f+(0.5f/256.0f);
|
|
}
|
|
if ( x == brick )
|
|
{
|
|
p1.tu = 1.0f-(0.5f/256.0f);
|
|
p2.tu = 1.0f-(0.5f/256.0f);
|
|
}
|
|
if ( y == 0 )
|
|
{
|
|
p1.tv = 1.0f-(0.5f/256.0f);
|
|
}
|
|
if ( y == brick-step )
|
|
{
|
|
p2.tv = 0.0f+(0.5f/256.0f);
|
|
}
|
|
}
|
|
|
|
if ( m_bLevelText )
|
|
{
|
|
p1.tu /= m_subdivMapping; // 0..1 -> 0..0.25
|
|
p1.tv /= m_subdivMapping;
|
|
p2.tu /= m_subdivMapping;
|
|
p2.tv /= m_subdivMapping;
|
|
|
|
if ( x == 0 )
|
|
{
|
|
p1.tu = 0.0f+dp;
|
|
p2.tu = 0.0f+dp;
|
|
}
|
|
if ( x == brick )
|
|
{
|
|
p1.tu = (1.0f/m_subdivMapping)-dp;
|
|
p2.tu = (1.0f/m_subdivMapping)-dp;
|
|
}
|
|
if ( y == 0 )
|
|
{
|
|
p1.tv = (1.0f/m_subdivMapping)-dp;
|
|
}
|
|
if ( y == brick-step )
|
|
{
|
|
p2.tv = 0.0f+dp;
|
|
}
|
|
|
|
p1.tu += uv.x;
|
|
p1.tv += uv.y;
|
|
p2.tu += uv.x;
|
|
p2.tv += uv.y;
|
|
}
|
|
|
|
#if 1
|
|
xx = mx*(m_brick/m_subdivMapping) + x;
|
|
yy = my*(m_brick/m_subdivMapping) + y;
|
|
p1.tu2 = ((float)(ox%5)*m_brick+xx+0.0f)/(m_brick*5);
|
|
p1.tv2 = ((float)(oy%5)*m_brick+yy+0.0f)/(m_brick*5);
|
|
p2.tu2 = ((float)(ox%5)*m_brick+xx+0.0f)/(m_brick*5);
|
|
p2.tv2 = ((float)(oy%5)*m_brick+yy+1.0f)/(m_brick*5);
|
|
|
|
// Correction for 1 pixel cover (*).
|
|
p1.tu2 = (p1.tu2+pixel)*(1.0f-pixel)/(1.0f+pixel);
|
|
p1.tv2 = (p1.tv2+pixel)*(1.0f-pixel)/(1.0f+pixel);
|
|
p2.tu2 = (p2.tu2+pixel)*(1.0f-pixel)/(1.0f+pixel);
|
|
p2.tv2 = (p2.tv2+pixel)*(1.0f-pixel)/(1.0f+pixel);
|
|
#endif
|
|
|
|
buffer->vertex[i++] = p1;
|
|
buffer->vertex[i++] = p2;
|
|
}
|
|
m_engine->AddQuick(objRank, buffer, texName1, texName2, min, max, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
transform.LoadIdentity();
|
|
transform.Set(1, 4, o.x);
|
|
transform.Set(3, 4, o.z);
|
|
m_engine->SetObjectTransform(objRank, transform);
|
|
|
|
return true;
|
|
}
|
|
|
|
// (*) There is 1 pixel cover around each of the 16 surfaces:
|
|
//
|
|
// |<--------------256-------------->|
|
|
// | |<----------254---------->| |
|
|
// |---|---|---|-- ... --|---|---|---|
|
|
// | 0.0 1.0 |
|
|
// | | | |
|
|
// 0.0 min max 1.0
|
|
//
|
|
// The uv coordinates used for texturing are between min and max (instead of 0 and 1).
|
|
// This allows to exclude the pixels situated in a margin of a pixel around the surface.
|
|
|
|
|
|
// Seeks a materials based on theirs identifier.
|
|
|
|
TerrainMaterial* CTerrain::LevelSearchMat(int id)
|
|
{
|
|
int i;
|
|
|
|
for ( i=0 ; i<m_levelMatTotal ; i++ )
|
|
{
|
|
if ( id == m_levelMat[i].id )
|
|
{
|
|
return &m_levelMat[i];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Chooses texture to use for a given square.
|
|
|
|
void CTerrain::LevelTextureName(int x, int y, char *name, Math::Point &uv)
|
|
{
|
|
TerrainMaterial* tm;
|
|
|
|
x /= m_brick/m_subdivMapping;
|
|
y /= m_brick/m_subdivMapping;
|
|
|
|
tm = LevelSearchMat(m_levelDot[x+y*m_levelDotSize].id);
|
|
if ( tm == 0 )
|
|
{
|
|
strcpy(name, "xxx.tga");
|
|
uv.x = 0.0f;
|
|
uv.y = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
//? sprintf(name, "%s.tga", tm->texName);
|
|
strcpy(name, tm->texName);
|
|
uv.x = tm->u;
|
|
uv.y = tm->v;
|
|
}
|
|
}
|
|
|
|
// Returns the height of the terrain.
|
|
|
|
float CTerrain::LevelRetHeight(int x, int y)
|
|
{
|
|
int size;
|
|
|
|
size = (m_mosaic*m_brick+1);
|
|
|
|
if ( x < 0 ) x = 0;
|
|
if ( x >= size ) x = size-1;
|
|
if ( y < 0 ) y = 0;
|
|
if ( y >= size ) y = size-1;
|
|
|
|
return m_relief[x+y*size];
|
|
}
|
|
|
|
// Decide whether a point is using the materials.
|
|
|
|
bool CTerrain::LevelGetDot(int x, int y, float min, float max, float slope)
|
|
{
|
|
float hc, h[4];
|
|
int i;
|
|
|
|
hc = LevelRetHeight(x, y);
|
|
h[0] = LevelRetHeight(x+0, y+1);
|
|
h[1] = LevelRetHeight(x+1, y+0);
|
|
h[2] = LevelRetHeight(x+0, y-1);
|
|
h[3] = LevelRetHeight(x-1, y+0);
|
|
|
|
if ( hc < min ||
|
|
hc > max ) return false;
|
|
|
|
if ( slope == 0.0f )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( slope > 0.0f )
|
|
{
|
|
for ( i=0 ; i<4 ; i++ )
|
|
{
|
|
if ( fabs(hc-h[i]) >= slope )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if ( slope < 0.0f )
|
|
{
|
|
for ( i=0 ; i<4 ; i++ )
|
|
{
|
|
if ( fabs(hc-h[i]) < -slope )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Seeks if material exists.
|
|
// Returns the index within m_levelMat or -1 if there is not.
|
|
// m_levelMat[i].id gives the identifier.
|
|
|
|
int CTerrain::LevelTestMat(char *mat)
|
|
{
|
|
int i;
|
|
|
|
for ( i=0 ; i<m_levelMatTotal ; i++ )
|
|
{
|
|
if ( m_levelMat[i].mat[0] == mat[0] &&
|
|
m_levelMat[i].mat[1] == mat[1] &&
|
|
m_levelMat[i].mat[2] == mat[2] &&
|
|
m_levelMat[i].mat[3] == mat[3] ) return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Modifies the state of a point and its four neighbors, without testing if possible.
|
|
|
|
void CTerrain::LevelSetDot(int x, int y, int id, char *mat)
|
|
{
|
|
TerrainMaterial* tm;
|
|
int i, ii;
|
|
|
|
tm = LevelSearchMat(id);
|
|
if ( tm == 0 ) return;
|
|
|
|
if ( tm->mat[0] != mat[0] ||
|
|
tm->mat[1] != mat[1] ||
|
|
tm->mat[2] != mat[2] ||
|
|
tm->mat[3] != mat[3] ) // id incompatible with mat?
|
|
{
|
|
ii = LevelTestMat(mat);
|
|
if ( ii == -1 ) return;
|
|
id = m_levelMat[ii].id; // looking for a id compatible with mat
|
|
}
|
|
|
|
// Changes the point.
|
|
m_levelDot[x+y*m_levelDotSize].id = id;
|
|
m_levelDot[x+y*m_levelDotSize].mat[0] = mat[0];
|
|
m_levelDot[x+y*m_levelDotSize].mat[1] = mat[1];
|
|
m_levelDot[x+y*m_levelDotSize].mat[2] = mat[2];
|
|
m_levelDot[x+y*m_levelDotSize].mat[3] = mat[3];
|
|
|
|
// Changes the lower neighbor.
|
|
if ( (x+0) >= 0 && (x+0) < m_levelDotSize &&
|
|
(y-1) >= 0 && (y-1) < m_levelDotSize )
|
|
{
|
|
i = (x+0)+(y-1)*m_levelDotSize;
|
|
if ( m_levelDot[i].mat[0] != mat[2] )
|
|
{
|
|
m_levelDot[i].mat[0] = mat[2];
|
|
ii = LevelTestMat(m_levelDot[i].mat);
|
|
if ( ii != -1 )
|
|
{
|
|
m_levelDot[i].id = m_levelMat[ii].id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Modifies the left neighbor.
|
|
if ( (x-1) >= 0 && (x-1) < m_levelDotSize &&
|
|
(y+0) >= 0 && (y+0) < m_levelDotSize )
|
|
{
|
|
i = (x-1)+(y+0)*m_levelDotSize;
|
|
if ( m_levelDot[i].mat[1] != mat[3] )
|
|
{
|
|
m_levelDot[i].mat[1] = mat[3];
|
|
ii = LevelTestMat(m_levelDot[i].mat);
|
|
if ( ii != -1 )
|
|
{
|
|
m_levelDot[i].id = m_levelMat[ii].id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Changes the upper neighbor.
|
|
if ( (x+0) >= 0 && (x+0) < m_levelDotSize &&
|
|
(y+1) >= 0 && (y+1) < m_levelDotSize )
|
|
{
|
|
i = (x+0)+(y+1)*m_levelDotSize;
|
|
if ( m_levelDot[i].mat[2] != mat[0] )
|
|
{
|
|
m_levelDot[i].mat[2] = mat[0];
|
|
ii = LevelTestMat(m_levelDot[i].mat);
|
|
if ( ii != -1 )
|
|
{
|
|
m_levelDot[i].id = m_levelMat[ii].id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Changes the right neighbor.
|
|
if ( (x+1) >= 0 && (x+1) < m_levelDotSize &&
|
|
(y+0) >= 0 && (y+0) < m_levelDotSize )
|
|
{
|
|
i = (x+1)+(y+0)*m_levelDotSize;
|
|
if ( m_levelDot[i].mat[3] != mat[1] )
|
|
{
|
|
m_levelDot[i].mat[3] = mat[1];
|
|
ii = LevelTestMat(m_levelDot[i].mat);
|
|
if ( ii != -1 )
|
|
{
|
|
m_levelDot[i].id = m_levelMat[ii].id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests if a material can give a place, according to its four neighbors.
|
|
// If yes, puts the point.
|
|
|
|
bool CTerrain::LevelIfDot(int x, int y, int id, char *mat)
|
|
{
|
|
char test[4];
|
|
|
|
// Compatible with lower neighbor?
|
|
if ( x+0 >= 0 && x+0 < m_levelDotSize &&
|
|
y-1 >= 0 && y-1 < m_levelDotSize )
|
|
{
|
|
test[0] = mat[2];
|
|
test[1] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[1];
|
|
test[2] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[2];
|
|
test[3] = m_levelDot[(x+0)+(y-1)*m_levelDotSize].mat[3];
|
|
|
|
if ( LevelTestMat(test) == -1 ) return false;
|
|
}
|
|
|
|
// Compatible with left neighbor?
|
|
if ( x-1 >= 0 && x-1 < m_levelDotSize &&
|
|
y+0 >= 0 && y+0 < m_levelDotSize )
|
|
{
|
|
test[0] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[0];
|
|
test[1] = mat[3];
|
|
test[2] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[2];
|
|
test[3] = m_levelDot[(x-1)+(y+0)*m_levelDotSize].mat[3];
|
|
|
|
if ( LevelTestMat(test) == -1 ) return false;
|
|
}
|
|
|
|
// Compatible with upper neighbor?
|
|
if ( x+0 >= 0 && x+0 < m_levelDotSize &&
|
|
y+1 >= 0 && y+1 < m_levelDotSize )
|
|
{
|
|
test[0] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[0];
|
|
test[1] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[1];
|
|
test[2] = mat[0];
|
|
test[3] = m_levelDot[(x+0)+(y+1)*m_levelDotSize].mat[3];
|
|
|
|
if ( LevelTestMat(test) == -1 ) return false;
|
|
}
|
|
|
|
// Compatible with right neighbor?
|
|
if ( x+1 >= 0 && x+1 < m_levelDotSize &&
|
|
y+0 >= 0 && y+0 < m_levelDotSize )
|
|
{
|
|
test[0] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[0];
|
|
test[1] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[1];
|
|
test[2] = m_levelDot[(x+1)+(y+0)*m_levelDotSize].mat[2];
|
|
test[3] = mat[1];
|
|
|
|
if ( LevelTestMat(test) == -1 ) return false;
|
|
}
|
|
|
|
LevelSetDot(x, y, id, mat); // puts the point
|
|
return true;
|
|
}
|
|
|
|
// Modifies the state of a point.
|
|
|
|
bool CTerrain::LevelPutDot(int x, int y, int id)
|
|
{
|
|
TerrainMaterial *tm;
|
|
char mat[4];
|
|
int up, right, down, left;
|
|
|
|
x /= m_brick/m_subdivMapping;
|
|
y /= m_brick/m_subdivMapping;
|
|
|
|
if ( x < 0 || x >= m_levelDotSize ||
|
|
y < 0 || y >= m_levelDotSize ) return false;
|
|
|
|
tm = LevelSearchMat(id);
|
|
if ( tm == 0 ) return false;
|
|
|
|
// Tries without changing neighbors.
|
|
if ( LevelIfDot(x, y, id, tm->mat) ) return true;
|
|
|
|
// Tries changing a single neighbor (4x).
|
|
for ( up=0 ; up<m_levelMatMax ; up++ )
|
|
{
|
|
mat[0] = up;
|
|
mat[1] = tm->mat[1];
|
|
mat[2] = tm->mat[2];
|
|
mat[3] = tm->mat[3];
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
|
|
for ( right=0 ; right<m_levelMatMax ; right++ )
|
|
{
|
|
mat[0] = tm->mat[0];
|
|
mat[1] = right;
|
|
mat[2] = tm->mat[2];
|
|
mat[3] = tm->mat[3];
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
|
|
for ( down=0 ; down<m_levelMatMax ; down++ )
|
|
{
|
|
mat[0] = tm->mat[0];
|
|
mat[1] = tm->mat[1];
|
|
mat[2] = down;
|
|
mat[3] = tm->mat[3];
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
|
|
for ( left=0 ; left<m_levelMatMax ; left++ )
|
|
{
|
|
mat[0] = tm->mat[0];
|
|
mat[1] = tm->mat[1];
|
|
mat[2] = tm->mat[2];
|
|
mat[3] = left;
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
|
|
// Tries changing two neighbors (6x).
|
|
for ( up=0 ; up<m_levelMatMax ; up++ )
|
|
{
|
|
for ( down=0 ; down<m_levelMatMax ; down++ )
|
|
{
|
|
mat[0] = up;
|
|
mat[1] = tm->mat[1];
|
|
mat[2] = down;
|
|
mat[3] = tm->mat[3];
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
}
|
|
|
|
for ( right=0 ; right<m_levelMatMax ; right++ )
|
|
{
|
|
for ( left=0 ; left<m_levelMatMax ; left++ )
|
|
{
|
|
mat[0] = tm->mat[0];
|
|
mat[1] = right;
|
|
mat[2] = tm->mat[2];
|
|
mat[3] = left;
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
}
|
|
|
|
for ( up=0 ; up<m_levelMatMax ; up++ )
|
|
{
|
|
for ( right=0 ; right<m_levelMatMax ; right++ )
|
|
{
|
|
mat[0] = up;
|
|
mat[1] = right;
|
|
mat[2] = tm->mat[2];
|
|
mat[3] = tm->mat[3];
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
}
|
|
|
|
for ( right=0 ; right<m_levelMatMax ; right++ )
|
|
{
|
|
for ( down=0 ; down<m_levelMatMax ; down++ )
|
|
{
|
|
mat[0] = tm->mat[0];
|
|
mat[1] = right;
|
|
mat[2] = down;
|
|
mat[3] = tm->mat[3];
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
}
|
|
|
|
for ( down=0 ; down<m_levelMatMax ; down++ )
|
|
{
|
|
for ( left=0 ; left<m_levelMatMax ; left++ )
|
|
{
|
|
mat[0] = tm->mat[0];
|
|
mat[1] = tm->mat[1];
|
|
mat[2] = down;
|
|
mat[3] = left;
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
}
|
|
|
|
for ( up=0 ; up<m_levelMatMax ; up++ )
|
|
{
|
|
for ( left=0 ; left<m_levelMatMax ; left++ )
|
|
{
|
|
mat[0] = up;
|
|
mat[1] = tm->mat[1];
|
|
mat[2] = tm->mat[2];
|
|
mat[3] = left;
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
}
|
|
|
|
// Tries changing all the neighbors.
|
|
for ( up=0 ; up<m_levelMatMax ; up++ )
|
|
{
|
|
for ( right=0 ; right<m_levelMatMax ; right++ )
|
|
{
|
|
for ( down=0 ; down<m_levelMatMax ; down++ )
|
|
{
|
|
for ( left=0 ; left<m_levelMatMax ; left++ )
|
|
{
|
|
mat[0] = up;
|
|
mat[1] = right;
|
|
mat[2] = down;
|
|
mat[3] = left;
|
|
|
|
if ( LevelIfDot(x, y, id, mat) ) return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OutputDebugString("LevelPutDot error\n");
|
|
return false;
|
|
}
|
|
|
|
// Initializes all the ground with a material.
|
|
|
|
bool CTerrain::LevelInit(int id)
|
|
{
|
|
TerrainMaterial* tm;
|
|
int i, j;
|
|
|
|
tm = LevelSearchMat(id);
|
|
if ( tm == 0 ) return false;
|
|
|
|
for ( i=0 ; i<m_levelDotSize*m_levelDotSize ; i++ )
|
|
{
|
|
m_levelDot[i].id = id;
|
|
|
|
for ( j=0 ; j<4 ; j++ )
|
|
{
|
|
m_levelDot[i].mat[j] = tm->mat[j];
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Generates a level in the terrain.
|
|
|
|
bool CTerrain::LevelGenerate(int *id, float min, float max,
|
|
float slope, float freq,
|
|
Math::Vector center, float radius)
|
|
{
|
|
TerrainMaterial *tm;
|
|
Math::Vector pos;
|
|
int i, numID, x, y, xx, yy, group, rnd;
|
|
float dim;
|
|
|
|
static char random[100] =
|
|
{
|
|
84,25,12, 6,34,52,85,38,97,16,
|
|
21,31,65,19,62,40,72,22,48,61,
|
|
56,47, 8,53,73,77, 4,91,26,88,
|
|
76, 1,44,93,39,11,71,17,98,95,
|
|
88,83,18,30, 3,57,28,49,74, 9,
|
|
32,13,96,66,15,70,36,10,59,94,
|
|
45,86, 2,29,63,42,51, 0,79,27,
|
|
54, 7,20,69,89,23,64,43,81,92,
|
|
90,33,46,14,67,35,50, 5,87,60,
|
|
68,55,24,78,41,75,58,80,37,82,
|
|
};
|
|
|
|
i = 0;
|
|
while ( id[i] != 0 )
|
|
{
|
|
tm = LevelSearchMat(id[i++]);
|
|
if ( tm == 0 ) return false;
|
|
}
|
|
numID = i;
|
|
|
|
group = m_brick/m_subdivMapping;
|
|
|
|
if ( radius > 0.0f && radius < 5.0f ) // just a square?
|
|
{
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
|
|
xx = (int)((center.x+dim)/m_size);
|
|
yy = (int)((center.z+dim)/m_size);
|
|
|
|
x = xx/group;
|
|
y = yy/group;
|
|
|
|
tm = LevelSearchMat(id[0]);
|
|
if ( tm != 0 )
|
|
{
|
|
LevelSetDot(x, y, id[0], tm->mat); // puts the point
|
|
}
|
|
//? LevelPutDot(xx,yy, id[0]);
|
|
}
|
|
else
|
|
{
|
|
for ( y=0 ; y<m_levelDotSize ; y++ )
|
|
{
|
|
for ( x=0 ; x<m_levelDotSize ; x++ )
|
|
{
|
|
if ( radius != 0.0f )
|
|
{
|
|
pos.x = ((float)x-m_levelDotSize/2.0f)*group*m_size;
|
|
pos.z = ((float)y-m_levelDotSize/2.0f)*group*m_size;
|
|
if ( Math::DistanceProjected(pos, center) > radius ) continue;
|
|
}
|
|
|
|
if ( freq < 100.0f )
|
|
{
|
|
rnd = random[(x%10)+(y%10)*10];
|
|
if ( (float)rnd > freq ) continue;
|
|
}
|
|
|
|
xx = x*group + group/2;
|
|
yy = y*group + group/2;
|
|
|
|
if ( LevelGetDot(xx,yy, min, max, slope) )
|
|
{
|
|
rnd = random[(x%10)+(y%10)*10];
|
|
i = rnd%numID;
|
|
LevelPutDot(xx,yy, id[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Initializes an table with empty levels.
|
|
|
|
void CTerrain::LevelOpenTable()
|
|
{
|
|
int i, j;
|
|
|
|
if ( !m_bLevelText ) return;
|
|
if ( m_levelDot != 0 ) return; // already allocated
|
|
|
|
m_levelDotSize = (m_mosaic*m_brick)/(m_brick/m_subdivMapping)+1;
|
|
m_levelDot = (DotLevel*)malloc(m_levelDotSize*m_levelDotSize*sizeof(DotLevel));
|
|
|
|
for ( i=0 ; i<m_levelDotSize*m_levelDotSize ; i++ )
|
|
{
|
|
for ( j=0 ; j<4 ; j++ )
|
|
{
|
|
m_levelDot[i].mat[j] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Closes the level table.
|
|
|
|
void CTerrain::LevelCloseTable()
|
|
{
|
|
free(m_levelDot);
|
|
m_levelDot = 0;
|
|
}
|
|
|
|
|
|
|
|
// Creates all objects in a mesh square ground.
|
|
|
|
bool CTerrain::CreateSquare(bool bMultiRes, int x, int y)
|
|
{
|
|
D3DMATERIAL7 mat;
|
|
float min, max;
|
|
int step, objRank;
|
|
|
|
ZeroMemory( &mat, sizeof(D3DMATERIAL7) );
|
|
mat.diffuse.r = 1.0f;
|
|
mat.diffuse.g = 1.0f;
|
|
mat.diffuse.b = 1.0f;
|
|
mat.ambient.r = 0.0f;
|
|
mat.ambient.g = 0.0f;
|
|
mat.ambient.b = 0.0f;
|
|
|
|
objRank = m_engine->CreateObject();
|
|
m_engine->SetObjectType(objRank, TYPETERRAIN); // it is a terrain
|
|
|
|
m_objRank[x+y*m_mosaic] = objRank;
|
|
|
|
if ( bMultiRes )
|
|
{
|
|
min = 0.0f;
|
|
max = m_vision;
|
|
max *= m_engine->RetClippingDistance();
|
|
for ( step=0 ; step<m_depth ; step++ )
|
|
{
|
|
CreateMosaic(x, y, 1<<step, objRank, mat, min, max);
|
|
min = max;
|
|
max *= 2;
|
|
if ( step == m_depth-1 ) max = g_HUGE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreateMosaic(x, y, 1, objRank, mat, 0.0f, g_HUGE);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Creates all objects of the terrain within the 3D engine.
|
|
|
|
bool CTerrain::CreateObjects(bool bMultiRes)
|
|
{
|
|
int x, y;
|
|
|
|
AdjustRelief();
|
|
|
|
for ( y=0 ; y<m_mosaic ; y++ )
|
|
{
|
|
for ( x=0 ; x<m_mosaic ; x++ )
|
|
{
|
|
CreateSquare(bMultiRes, x, y);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Modifies the terrain's relief.
|
|
// ATTENTION: ok only with m_depth = 2!
|
|
|
|
bool CTerrain::Terraform(const Math::Vector &p1, const Math::Vector &p2, float height)
|
|
{
|
|
POINT tp1, tp2, pp1, pp2;
|
|
float dim, avg;
|
|
int x, y, size, nb;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
tp1.x = (int)((p1.x+dim+m_size/2.0f)/m_size);
|
|
tp1.y = (int)((p1.z+dim+m_size/2.0f)/m_size);
|
|
tp2.x = (int)((p2.x+dim+m_size/2.0f)/m_size);
|
|
tp2.y = (int)((p2.z+dim+m_size/2.0f)/m_size);
|
|
|
|
if ( tp1.x > tp2.x )
|
|
{
|
|
x = tp1.x;
|
|
tp1.x = tp2.x;
|
|
tp2.x = x;
|
|
}
|
|
|
|
if ( tp1.y > tp2.y )
|
|
{
|
|
y = tp1.y;
|
|
tp1.y = tp2.y;
|
|
tp2.y = y;
|
|
}
|
|
|
|
size = (m_mosaic*m_brick)+1;
|
|
|
|
// Calculates the current average height.
|
|
avg = 0.0f;
|
|
nb = 0;
|
|
for ( y=tp1.y ; y<=tp2.y ; y++ )
|
|
{
|
|
for ( x=tp1.x ; x<=tp2.x ; x++ )
|
|
{
|
|
avg += m_relief[x+y*size];
|
|
nb ++;
|
|
}
|
|
}
|
|
avg /= (float)nb;
|
|
|
|
// Changes the description of the relief.
|
|
for ( y=tp1.y ; y<=tp2.y ; y++ )
|
|
{
|
|
for ( x=tp1.x ; x<=tp2.x ; x++ )
|
|
{
|
|
m_relief[x+y*size] = avg+height;
|
|
|
|
if ( x%m_brick == 0 && y%m_depth != 0 )
|
|
{
|
|
m_relief[(x+0)+(y-1)*size] = avg+height;
|
|
m_relief[(x+0)+(y+1)*size] = avg+height;
|
|
}
|
|
|
|
if ( y%m_brick == 0 && x%m_depth != 0 )
|
|
{
|
|
m_relief[(x-1)+(y+0)*size] = avg+height;
|
|
m_relief[(x+1)+(y+0)*size] = avg+height;
|
|
}
|
|
}
|
|
}
|
|
AdjustRelief();
|
|
|
|
pp1.x = (tp1.x-2)/m_brick;
|
|
pp1.y = (tp1.y-2)/m_brick;
|
|
pp2.x = (tp2.x+1)/m_brick;
|
|
pp2.y = (tp2.y+1)/m_brick;
|
|
|
|
if ( pp1.x < 0 ) pp1.x = 0;
|
|
if ( pp1.x >= m_mosaic ) pp1.x = m_mosaic-1;
|
|
if ( pp1.y < 0 ) pp1.y = 0;
|
|
if ( pp1.y >= m_mosaic ) pp1.y = m_mosaic-1;
|
|
|
|
for ( y=pp1.y ; y<=pp2.y ; y++ )
|
|
{
|
|
for ( x=pp1.x ; x<=pp2.x ; x++ )
|
|
{
|
|
m_engine->DeleteObject(m_objRank[x+y*m_mosaic]);
|
|
CreateSquare(m_bMultiText, x, y); // recreates the square
|
|
}
|
|
}
|
|
m_engine->Update();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Management of the wind.
|
|
|
|
void CTerrain::SetWind(Math::Vector speed)
|
|
{
|
|
m_wind = speed;
|
|
}
|
|
|
|
Math::Vector CTerrain::RetWind()
|
|
{
|
|
return m_wind;
|
|
}
|
|
|
|
|
|
// Gives the exact slope of the terrain of a place given.
|
|
|
|
float CTerrain::RetFineSlope(const Math::Vector &pos)
|
|
{
|
|
Math::Vector n;
|
|
|
|
if ( !GetNormal(n, pos) ) return 0.0f;
|
|
return fabs(Math::RotateAngle(Math::Point(n.x, n.z).Length(), n.y)-Math::PI/2.0f);
|
|
}
|
|
|
|
// Gives the approximate slope of the terrain of a specific location.
|
|
|
|
float CTerrain::RetCoarseSlope(const Math::Vector &pos)
|
|
{
|
|
float dim, level[4], min, max;
|
|
int x, y;
|
|
|
|
if ( m_relief == 0 ) return 0.0f;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
|
|
x = (int)((pos.x+dim)/m_size);
|
|
y = (int)((pos.z+dim)/m_size);
|
|
|
|
if ( x < 0 || x >= m_mosaic*m_brick ||
|
|
y < 0 || y >= m_mosaic*m_brick ) return 0.0f;
|
|
|
|
level[0] = m_relief[(x+0)+(y+0)*(m_mosaic*m_brick+1)];
|
|
level[1] = m_relief[(x+1)+(y+0)*(m_mosaic*m_brick+1)];
|
|
level[2] = m_relief[(x+0)+(y+1)*(m_mosaic*m_brick+1)];
|
|
level[3] = m_relief[(x+1)+(y+1)*(m_mosaic*m_brick+1)];
|
|
|
|
min = Math::Min(level[0], level[1], level[2], level[3]);
|
|
max = Math::Max(level[0], level[1], level[2], level[3]);
|
|
|
|
return atanf((max-min)/m_size);
|
|
}
|
|
|
|
// Gives the normal vector at the position p (x,-,z) of the ground.
|
|
|
|
bool CTerrain::GetNormal(Math::Vector &n, const Math::Vector &p)
|
|
{
|
|
Math::Vector p1, p2, p3, p4;
|
|
float dim;
|
|
int x, y;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
|
|
x = (int)((p.x+dim)/m_size);
|
|
y = (int)((p.z+dim)/m_size);
|
|
|
|
if ( x < 0 || x > m_mosaic*m_brick ||
|
|
y < 0 || y > m_mosaic*m_brick ) return false;
|
|
|
|
p1 = RetVector(x+0, y+0);
|
|
p2 = RetVector(x+1, y+0);
|
|
p3 = RetVector(x+0, y+1);
|
|
p4 = RetVector(x+1, y+1);
|
|
|
|
if ( fabs(p.z-p2.z) < fabs(p.x-p2.x) )
|
|
{
|
|
n = Math::NormalToPlane(p1,p2,p3);
|
|
}
|
|
else
|
|
{
|
|
n = Math::NormalToPlane(p2,p4,p3);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Returns the height of the ground.
|
|
|
|
float CTerrain::RetFloorLevel(const Math::Vector &p, bool bBrut, bool bWater)
|
|
{
|
|
Math::Vector p1, p2, p3, p4, ps;
|
|
float dim, level;
|
|
int x, y;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
|
|
x = (int)((p.x+dim)/m_size);
|
|
y = (int)((p.z+dim)/m_size);
|
|
|
|
if ( x < 0 || x > m_mosaic*m_brick ||
|
|
y < 0 || y > m_mosaic*m_brick ) return false;
|
|
|
|
p1 = RetVector(x+0, y+0);
|
|
p2 = RetVector(x+1, y+0);
|
|
p3 = RetVector(x+0, y+1);
|
|
p4 = RetVector(x+1, y+1);
|
|
|
|
ps = p;
|
|
if ( fabs(p.z-p2.z) < fabs(p.x-p2.x) )
|
|
{
|
|
if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f;
|
|
}
|
|
else
|
|
{
|
|
if ( !IntersectY(p2, p4, p3, ps) ) return 0.0f;
|
|
}
|
|
|
|
if ( !bBrut ) AdjustBuildingLevel(ps);
|
|
|
|
if ( bWater ) // not going underwater?
|
|
{
|
|
level = m_water->RetLevel();
|
|
if ( ps.y < level ) ps.y = level; // not under water
|
|
}
|
|
|
|
return ps.y;
|
|
}
|
|
|
|
// Returns the height to the ground.
|
|
// This height is positive when you are above the ground.
|
|
|
|
float CTerrain::RetFloorHeight(const Math::Vector &p, bool bBrut, bool bWater)
|
|
{
|
|
Math::Vector p1, p2, p3, p4, ps;
|
|
float dim, level;
|
|
int x, y;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
|
|
x = (int)((p.x+dim)/m_size);
|
|
y = (int)((p.z+dim)/m_size);
|
|
|
|
if ( x < 0 || x > m_mosaic*m_brick ||
|
|
y < 0 || y > m_mosaic*m_brick ) return false;
|
|
|
|
p1 = RetVector(x+0, y+0);
|
|
p2 = RetVector(x+1, y+0);
|
|
p3 = RetVector(x+0, y+1);
|
|
p4 = RetVector(x+1, y+1);
|
|
|
|
ps = p;
|
|
if ( fabs(p.z-p2.z) < fabs(p.x-p2.x) )
|
|
{
|
|
if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f;
|
|
}
|
|
else
|
|
{
|
|
if ( !IntersectY(p2, p4, p3, ps) ) return 0.0f;
|
|
}
|
|
|
|
if ( !bBrut ) AdjustBuildingLevel(ps);
|
|
|
|
if ( bWater ) // not going underwater?
|
|
{
|
|
level = m_water->RetLevel();
|
|
if ( ps.y < level ) ps.y = level; // not under water
|
|
}
|
|
|
|
return p.y-ps.y;
|
|
}
|
|
|
|
// Modifies the coordinate "y" of point "p" to rest on the ground floor.
|
|
|
|
bool CTerrain::MoveOnFloor(Math::Vector &p, bool bBrut, bool bWater)
|
|
{
|
|
Math::Vector p1, p2, p3, p4;
|
|
float dim, level;
|
|
int x, y;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
|
|
x = (int)((p.x+dim)/m_size);
|
|
y = (int)((p.z+dim)/m_size);
|
|
|
|
if ( x < 0 || x > m_mosaic*m_brick ||
|
|
y < 0 || y > m_mosaic*m_brick ) return false;
|
|
|
|
p1 = RetVector(x+0, y+0);
|
|
p2 = RetVector(x+1, y+0);
|
|
p3 = RetVector(x+0, y+1);
|
|
p4 = RetVector(x+1, y+1);
|
|
|
|
if ( fabs(p.z-p2.z) < fabs(p.x-p2.x) )
|
|
{
|
|
if ( !IntersectY(p1, p2, p3, p) ) return false;
|
|
}
|
|
else
|
|
{
|
|
if ( !IntersectY(p2, p4, p3, p) ) return false;
|
|
}
|
|
|
|
if ( !bBrut ) AdjustBuildingLevel(p);
|
|
|
|
if ( bWater ) // not going underwater?
|
|
{
|
|
level = m_water->RetLevel();
|
|
if ( p.y < level ) p.y = level; // not under water
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Modifies a coordinate so that it is on the ground.
|
|
// Returns false if the initial coordinate was too far.
|
|
|
|
bool CTerrain::ValidPosition(Math::Vector &p, float marging)
|
|
{
|
|
bool bOK = true;
|
|
float limit;
|
|
|
|
limit = m_mosaic*m_brick*m_size/2.0f - marging;
|
|
|
|
if ( p.x < -limit )
|
|
{
|
|
p.x = -limit;
|
|
bOK = false;
|
|
}
|
|
|
|
if ( p.z < -limit )
|
|
{
|
|
p.z = -limit;
|
|
bOK = false;
|
|
}
|
|
|
|
if ( p.x > limit )
|
|
{
|
|
p.x = limit;
|
|
bOK = false;
|
|
}
|
|
|
|
if ( p.z > limit )
|
|
{
|
|
p.z = limit;
|
|
bOK = false;
|
|
}
|
|
|
|
return bOK;
|
|
}
|
|
|
|
|
|
|
|
// Empty the table of elevations.
|
|
|
|
void CTerrain::FlushBuildingLevel()
|
|
{
|
|
m_buildingUsed = 0;
|
|
}
|
|
|
|
// Adds a new elevation for a building.
|
|
|
|
bool CTerrain::AddBuildingLevel(Math::Vector center, float min, float max,
|
|
float height, float factor)
|
|
{
|
|
int i;
|
|
|
|
for ( i=0 ; i<m_buildingUsed ; i++ )
|
|
{
|
|
if ( center.x == m_buildingTable[i].center.x &&
|
|
center.z == m_buildingTable[i].center.z )
|
|
{
|
|
goto update;
|
|
}
|
|
}
|
|
|
|
if ( m_buildingUsed >= MAXBUILDINGLEVEL ) return false;
|
|
i = m_buildingUsed++;
|
|
|
|
update:
|
|
m_buildingTable[i].center = center;
|
|
m_buildingTable[i].min = min;
|
|
m_buildingTable[i].max = max;
|
|
m_buildingTable[i].level = RetFloorLevel(center, true);
|
|
m_buildingTable[i].height = height;
|
|
m_buildingTable[i].factor = factor;
|
|
m_buildingTable[i].bboxMinX = center.x-max;
|
|
m_buildingTable[i].bboxMaxX = center.x+max;
|
|
m_buildingTable[i].bboxMinZ = center.z-max;
|
|
m_buildingTable[i].bboxMaxZ = center.z+max;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Updates the elevation for a building when it was moved up (after a terraforming).
|
|
|
|
bool CTerrain::UpdateBuildingLevel(Math::Vector center)
|
|
{
|
|
int i;
|
|
|
|
for ( i=0 ; i<m_buildingUsed ; i++ )
|
|
{
|
|
if ( center.x == m_buildingTable[i].center.x &&
|
|
center.z == m_buildingTable[i].center.z )
|
|
{
|
|
m_buildingTable[i].center = center;
|
|
m_buildingTable[i].level = RetFloorLevel(center, true);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Removes the elevation for a building when it was destroyed.
|
|
|
|
bool CTerrain::DeleteBuildingLevel(Math::Vector center)
|
|
{
|
|
int i, j;
|
|
|
|
for ( i=0 ; i<m_buildingUsed ; i++ )
|
|
{
|
|
if ( center.x == m_buildingTable[i].center.x &&
|
|
center.z == m_buildingTable[i].center.z )
|
|
{
|
|
for ( j=i+1 ; j<m_buildingUsed ; j++ )
|
|
{
|
|
m_buildingTable[j-1] = m_buildingTable[j];
|
|
}
|
|
m_buildingUsed --;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Returns the influence factor whether a position is on a possible rise.
|
|
|
|
float CTerrain::RetBuildingFactor(const Math::Vector &p)
|
|
{
|
|
float dist;
|
|
int i;
|
|
|
|
for ( i=0 ; i<m_buildingUsed ; i++ )
|
|
{
|
|
if ( p.x < m_buildingTable[i].bboxMinX ||
|
|
p.x > m_buildingTable[i].bboxMaxX ||
|
|
p.z < m_buildingTable[i].bboxMinZ ||
|
|
p.z > m_buildingTable[i].bboxMaxZ ) continue;
|
|
|
|
dist = Math::DistanceProjected(p, m_buildingTable[i].center);
|
|
|
|
if ( dist <= m_buildingTable[i].max )
|
|
{
|
|
return m_buildingTable[i].factor;
|
|
}
|
|
}
|
|
return 1.0f; // it is normal on the ground
|
|
}
|
|
|
|
// Adjusts a position according to a possible rise.
|
|
|
|
void CTerrain::AdjustBuildingLevel(Math::Vector &p)
|
|
{
|
|
Math::Vector border;
|
|
float dist, base;
|
|
int i;
|
|
|
|
for ( i=0 ; i<m_buildingUsed ; i++ )
|
|
{
|
|
if ( p.x < m_buildingTable[i].bboxMinX ||
|
|
p.x > m_buildingTable[i].bboxMaxX ||
|
|
p.z < m_buildingTable[i].bboxMinZ ||
|
|
p.z > m_buildingTable[i].bboxMaxZ ) continue;
|
|
|
|
dist = Math::DistanceProjected(p, m_buildingTable[i].center);
|
|
|
|
if ( dist > m_buildingTable[i].max ) continue;
|
|
|
|
if ( dist < m_buildingTable[i].min )
|
|
{
|
|
p.y = m_buildingTable[i].level+m_buildingTable[i].height;
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
p.y = m_buildingTable[i].level;
|
|
p.y += (m_buildingTable[i].max-dist)/
|
|
(m_buildingTable[i].max-m_buildingTable[i].min)*
|
|
m_buildingTable[i].height;
|
|
|
|
base = RetFloorLevel(p, true);
|
|
if ( p.y < base ) p.y = base;
|
|
#else
|
|
border.x = ((p.x-m_buildingTable[i].center.x)*m_buildingTable[i].max)/
|
|
dist+m_buildingTable[i].center.x;
|
|
border.z = ((p.z-m_buildingTable[i].center.z)*m_buildingTable[i].max)/
|
|
dist+m_buildingTable[i].center.z;
|
|
|
|
base = RetFloorLevel(border, true);
|
|
|
|
p.y = (m_buildingTable[i].max-dist)/
|
|
(m_buildingTable[i].max-m_buildingTable[i].min)*
|
|
(m_buildingTable[i].level+m_buildingTable[i].height-base)+
|
|
base;
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// Returns the hardness of the ground in a given place.
|
|
// The hardness determines the noise (SOUND_STEP and SOUND_BOUM).
|
|
|
|
float CTerrain::RetHardness(const Math::Vector &p)
|
|
{
|
|
TerrainMaterial* tm;
|
|
float factor, dim;
|
|
int x, y, id;
|
|
|
|
factor = RetBuildingFactor(p);
|
|
if ( factor != 1.0f ) return 1.0f; // on building
|
|
|
|
if ( m_levelDot == 0 ) return m_defHardness;
|
|
|
|
dim = (m_mosaic*m_brick*m_size)/2.0f;
|
|
|
|
x = (int)((p.x+dim)/m_size);
|
|
y = (int)((p.z+dim)/m_size);
|
|
|
|
if ( x < 0 || x > m_mosaic*m_brick ||
|
|
y < 0 || y > m_mosaic*m_brick ) return m_defHardness;
|
|
|
|
x /= m_brick/m_subdivMapping;
|
|
y /= m_brick/m_subdivMapping;
|
|
|
|
if ( x < 0 || x >= m_levelDotSize ||
|
|
y < 0 || y >= m_levelDotSize ) return m_defHardness;
|
|
|
|
id = m_levelDot[x+y*m_levelDotSize].id;
|
|
tm = LevelSearchMat(id);
|
|
if ( tm == 0 ) return m_defHardness;
|
|
|
|
return tm->hardness;
|
|
}
|
|
|
|
|
|
// Shows the flat areas on the ground.
|
|
|
|
void CTerrain::GroundFlat(Math::Vector pos)
|
|
{
|
|
Math::Vector p;
|
|
float rapport, angle;
|
|
int x, y, i;
|
|
static char table[41*41];
|
|
|
|
|
|
rapport = 3200.0f/1024.0f;
|
|
|
|
for ( y=0 ; y<=40 ; y++ )
|
|
{
|
|
for ( x=0 ; x<=40 ; x++ )
|
|
{
|
|
i = x + y*41;
|
|
table[i] = 0;
|
|
|
|
p.x = (x-20)*rapport;
|
|
p.z = (y-20)*rapport;
|
|
p.y = 0.0f;
|
|
if ( Math::Point(p.x, p.y).Length() > 20.0f*rapport ) continue;
|
|
|
|
angle = RetFineSlope(pos+p);
|
|
|
|
if ( angle < FLATLIMIT )
|
|
{
|
|
table[i] = 1;
|
|
}
|
|
else
|
|
{
|
|
table[i] = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_engine->GroundMarkCreate(pos, 40.0f, 0.001f, 15.0f, 0.001f, 41, 41, table);
|
|
}
|
|
|
|
|
|
// Calculates the radius of the largest flat area available.
|
|
// This calculation is not optimized!
|
|
|
|
float CTerrain::RetFlatZoneRadius(Math::Vector center, float max)
|
|
{
|
|
Math::Vector pos;
|
|
Math::Point c, p;
|
|
float ref, radius, angle, h;
|
|
int i, nb;
|
|
|
|
angle = RetFineSlope(center);
|
|
if ( angle >= FLATLIMIT ) return 0.0f;
|
|
|
|
ref = RetFloorLevel(center, true);
|
|
|
|
radius = 1.0f;
|
|
while ( radius <= max )
|
|
{
|
|
angle = 0.0f;
|
|
nb = (int)(2.0f*Math::PI*radius);
|
|
if ( nb < 8 ) nb = 8;
|
|
for ( i=0 ; i<nb ; i++ )
|
|
{
|
|
c.x = center.x;
|
|
c.y = center.z;
|
|
p.x = center.x+radius;
|
|
p.y = center.z;
|
|
p = Math::RotatePoint(c, angle, p);
|
|
pos.x = p.x;
|
|
pos.z = p.y;
|
|
h = RetFloorLevel(pos, true);
|
|
if ( fabs(h-ref) > 1.0f ) return radius;
|
|
|
|
angle += Math::PI*2.0f/8.0f;
|
|
}
|
|
radius += 1.0f;
|
|
}
|
|
return max;
|
|
}
|
|
|
|
|
|
|
|
// Specifies the maximum height of flight.
|
|
|
|
void CTerrain::SetFlyingMaxHeight(float height)
|
|
{
|
|
m_flyingMaxHeight = height;
|
|
}
|
|
|
|
// Returns the maximum height of flight.
|
|
|
|
float CTerrain::RetFlyingMaxHeight()
|
|
{
|
|
return m_flyingMaxHeight;
|
|
}
|
|
|
|
|
|
// Empty the limits table of flight.
|
|
|
|
void CTerrain::FlushFlyingLimit()
|
|
{
|
|
m_flyingMaxHeight = 280.0f;
|
|
m_flyingLimitTotal = 0;
|
|
}
|
|
|
|
// Empty the limits table of flight.
|
|
|
|
bool CTerrain::AddFlyingLimit(Math::Vector center,
|
|
float extRadius, float intRadius,
|
|
float maxHeight)
|
|
{
|
|
int i;
|
|
|
|
if ( m_flyingLimitTotal >= MAXFLYINGLIMIT ) return false;
|
|
|
|
i = m_flyingLimitTotal;
|
|
m_flyingLimit[i].center = center;
|
|
m_flyingLimit[i].extRadius = extRadius;
|
|
m_flyingLimit[i].intRadius = intRadius;
|
|
m_flyingLimit[i].maxHeight = maxHeight;
|
|
m_flyingLimitTotal = i+1;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Returns the maximum height of flight.
|
|
|
|
float CTerrain::RetFlyingLimit(Math::Vector pos, bool bNoLimit)
|
|
{
|
|
float dist, h;
|
|
int i;
|
|
|
|
if ( bNoLimit ) return 280.0f;
|
|
if ( m_flyingLimitTotal == 0 ) return m_flyingMaxHeight;
|
|
|
|
for ( i=0 ; i<m_flyingLimitTotal ; i++ )
|
|
{
|
|
dist = Math::DistanceProjected(pos, m_flyingLimit[i].center);
|
|
|
|
if ( dist >= m_flyingLimit[i].extRadius ) continue;
|
|
|
|
if ( dist <= m_flyingLimit[i].intRadius )
|
|
{
|
|
return m_flyingLimit[i].maxHeight;
|
|
}
|
|
|
|
dist -= m_flyingLimit[i].intRadius;
|
|
|
|
h = dist*(m_flyingMaxHeight-m_flyingLimit[i].maxHeight)/
|
|
(m_flyingLimit[i].extRadius-m_flyingLimit[i].intRadius);
|
|
|
|
return h + m_flyingLimit[i].maxHeight;
|
|
}
|
|
|
|
return m_flyingMaxHeight;
|
|
}
|
|
|