From 878eec8eea26deedf3b1346df71bff391424c325 Mon Sep 17 00:00:00 2001 From: Piotr Dziwinski Date: Wed, 8 Aug 2012 21:32:44 +0200 Subject: CTerrain implementation Added rewritten CTerrain implementation Compiles OK, but functions are missing from other classes Also needs testing --- src/graphics/engine/cloud.h | 4 +- src/graphics/engine/engine.cpp | 5 + src/graphics/engine/engine.h | 3 +- src/graphics/engine/particle.h | 6 +- src/graphics/engine/terrain.cpp | 1802 ++++++++++++++++++++++++++++++++++++++- src/graphics/engine/terrain.h | 258 ++++-- src/graphics/engine/water.h | 14 +- 7 files changed, 1988 insertions(+), 104 deletions(-) (limited to 'src/graphics/engine') diff --git a/src/graphics/engine/cloud.h b/src/graphics/engine/cloud.h index d2d29d7..562f651 100644 --- a/src/graphics/engine/cloud.h +++ b/src/graphics/engine/cloud.h @@ -56,10 +56,10 @@ public: void Draw(); bool SetLevel(float level); - float RetLevel(); + float GetLevel(); void SetEnable(bool bEnable); - bool RetEnable(); + bool GetEnable(); protected: bool EventFrame(const Event &event); diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index c5d2a1d..4bf80d2 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -674,6 +674,11 @@ Math::IntSize Gfx::CEngine::InterfaceToWindowSize(Math::Size size) static_cast(size.h * m_size.h)); } +std::string Gfx::CEngine::GetTextureDir() +{ + return m_texPath; +} + void Gfx::CEngine::DrawMouse() { if (! m_mouseVisible) diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index 3f7f4f2..cd89a1c 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -552,6 +552,7 @@ public: //! Converts interface size to window size Math::IntSize InterfaceToWindowSize(Math::Size size); + std::string GetTextureDir(); bool WriteProfile(); @@ -606,7 +607,7 @@ public: bool AddSurface(int objRank, Gfx::VertexTex2* vertex, int nb, const Gfx::Material &mat, int state, std::string texName1, std::string texName2, float min, float max, bool globalUpdate); - bool AddQuick(int objRank, Gfx::EngineObjLevel5* buffer, + bool AddQuick(int objRank, const Gfx::EngineObjLevel5& buffer, std::string texName1, std::string texName2, float min, float max, bool globalUpdate); Gfx::EngineObjLevel5* SearchTriangle(int objRank, const Gfx::Material &mat, diff --git a/src/graphics/engine/particle.h b/src/graphics/engine/particle.h index bd9741f..94aacfc 100644 --- a/src/graphics/engine/particle.h +++ b/src/graphics/engine/particle.h @@ -260,7 +260,7 @@ public: CParticle(CInstanceManager* iMan, CEngine* engine); ~CParticle(); - void SetGLDevice(CDevice device); + void SetDevice(CDevice* device); void FlushParticle(); void FlushParticle(int sheet); @@ -283,7 +283,7 @@ public: void SetPhase(int channel, ParticlePhase phase, float duration); bool GetPosition(int channel, Math::Vector &pos); - Gfx::Color RetFogColor(Math::Vector pos); + Gfx::Color GetFogColor(Math::Vector pos); void SetFrameUpdate(int sheet, bool bUpdate); void FrameParticle(float rTime); @@ -311,7 +311,7 @@ protected: protected: CInstanceManager* m_iMan; CEngine* m_engine; - CDevice* m_pDevice; + CDevice* m_device; CRobotMain* m_main; CTerrain* m_terrain; CWater* m_water; diff --git a/src/graphics/engine/terrain.cpp b/src/graphics/engine/terrain.cpp index c489321..6d4fcd3 100644 --- a/src/graphics/engine/terrain.cpp +++ b/src/graphics/engine/terrain.cpp @@ -19,5 +19,1805 @@ #include "graphics/engine/terrain.h" +#include "app/app.h" +#include "common/iman.h" +#include "common/image.h" +#include "common/logger.h" +#include "graphics/engine/engine.h" +#include "graphics/engine/water.h" +#include "math/geometry.h" -// TODO implementation +#include + +#include + +const int LEVEL_MAT_PREALLOCATE_COUNT = 101; +const int FLYING_LIMIT_PREALLOCATE_COUNT = 10; +const int BUILDING_LEVEL_PREALLOCATE_COUNT = 101; + + +Gfx::CTerrain::CTerrain(CInstanceManager* iMan) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_TERRAIN, this); + + m_engine = static_cast( m_iMan->SearchInstance(CLASS_ENGINE) ); + m_water = static_cast( m_iMan->SearchInstance(CLASS_WATER) ); + + m_mosaic = 20; + m_brick = 1 << 4; + m_size = 10.0f; + m_vision = 200.0f; + m_scaleMapping = 0.01f; + m_scaleRelief = 1.0f; + m_subdivMapping = 1; + m_depth = 2; + m_levelMatMax = 0; + m_multiText = true; + m_levelText = false; + m_wind = Math::Vector(0.0f, 0.0f, 0.0f); + m_defHardness = 0.5f; + + m_levelMat.reserve(LEVEL_MAT_PREALLOCATE_COUNT); + m_flyingLimits.reserve(FLYING_LIMIT_PREALLOCATE_COUNT); + m_buildingLevels.reserve(BUILDING_LEVEL_PREALLOCATE_COUNT); +} + +Gfx::CTerrain::~CTerrain() +{ +} + +/** + 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 + +\verbatim + ^ z + | <---> brick*size + +---+---+---+---+ + | | | |_|_| mosaic = 4 + | | | | | | brick = 2 (brickP2=1) + +---+---+---+---+ + |\ \| | | | + |\ \| | | | + +---+---o---+---+---> x + | | | | | + | | | | | + +---+---+---+---+ + | | | | | The land is viewed from above here. + | | | | | + +---+---+---+---+ + <---------------> mosaic*brick*size +\endverbatim */ +bool Gfx::CTerrain::Generate(int mosaic, int brickPow2, float size, float vision, + int depth, float hardness) +{ + m_mosaic = mosaic; + m_brick = 1 << brickPow2; + m_size = size; + m_vision = vision; + m_depth = depth; + m_defHardness = hardness; + + m_engine->SetTerrainVision(vision); + + m_multiText = true; + m_levelText = false; + m_scaleMapping = 1.0f / (m_brick*m_size); + m_subdivMapping = 1; + + int dim = 0; + + dim = (m_mosaic*m_brick+1)*(m_mosaic*m_brick+1); + std::vector(dim).swap(m_relief); + + dim = m_mosaic*m_subdivMapping*m_mosaic*m_subdivMapping; + std::vector(dim).swap(m_texture); + + dim = m_mosaic*m_mosaic; + std::vector(dim).swap(m_objRank); + + return true; +} + + +int Gfx::CTerrain::GetMosaic() +{ + return m_mosaic; +} + +int Gfx::CTerrain::GetBrick() +{ + return m_brick; +} + +float Gfx::CTerrain::GetSize() +{ + return m_size; +} + +float Gfx::CTerrain::GetScaleRelief() +{ + return m_scaleRelief; +} + +bool Gfx::CTerrain::InitTextures(const std::string& baseName, int* table, int dx, int dy) +{ + m_levelText = false; + m_texBaseName = baseName; + size_t pos = baseName.find('.'); + if (pos == baseName.npos) + { + m_texBaseExt = ".png"; + } + else + { + m_texBaseName = m_texBaseName.substr(0, pos); + m_texBaseExt = m_texBaseName.substr(pos); + } + + for (int y = 0; y < m_mosaic*m_subdivMapping; y++) + { + for (int x = 0; x < m_mosaic*m_subdivMapping; x++) + { + m_texture[x+y*m_mosaic] = table[(x%dx)+(y%dy)*dx]; + } + } + return true; +} + + +void Gfx::CTerrain::LevelFlush() +{ + m_levelMat.clear(); + m_levelMatMax = 0; + m_levelID = 1000; + LevelCloseTable(); +} + +void Gfx::CTerrain::LevelMaterial(int id, std::string& baseName, float u, float v, + int up, int right, int down, int left, + float hardness) +{ + LevelOpenTable(); + + if (id == 0) + id = m_levelID++; // puts an ID internal standard + + Gfx::TerrainMaterial tm; + tm.texName = baseName; + tm.id = id; + tm.u = u; + tm.v = v; + tm.mat[0] = up; + tm.mat[1] = right; + tm.mat[2] = down; + tm.mat[3] = left; + tm.hardness = hardness; + + m_levelMat.push_back(tm); + + 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_levelText = true; + m_subdivMapping = 4; +} + + +/** + The size of the image must be dimension dx and dy with dx=dy=(mosaic*brick)+1. + The image must be 24 bits/pixel + + 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 Gfx::CTerrain::ResFromPNG(const std::string& fileName) +{ + CImage img; + if (! img.Load(CApplication::GetInstance().GetDataFilePath(m_engine->GetTextureDir(), fileName))) + return false; + + ImageData *data = img.GetData(); + + int size = (m_mosaic*m_brick)+1; + + m_resources.clear(); + + std::vector(3*size*size).swap(m_resources); + + if ( (data->surface->w != size) || (data->surface->h != size) || + (data->surface->format->BytesPerPixel != 3) ) + return false; + + // Assuming the data format is compatible + memcpy(&m_resources[0], data->surface->pixels, 3*size*size); + + return true; +} + +Gfx::TerrainRes Gfx::CTerrain::GetResource(const Math::Vector &p) +{ + if (m_resources.empty()) + return Gfx::TR_NULL; + + int x = static_cast((p.x + (m_mosaic*m_brick*m_size)/2.0f)/m_size); + int y = static_cast((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 Gfx::TR_NULL; + + int size = (m_mosaic*m_brick)+1; + + int resR = m_resources[3*x+3*size*(size-y-1)]; + int resG = m_resources[3*x+3*size*(size-y-1)+1]; + int resB = m_resources[3*x+3*size*(size-y-1)+2]; + + if (resR == 255 && resG == 0 && resB == 0) return Gfx::TR_STONE; + if (resR == 255 && resG == 255 && resB == 0) return Gfx::TR_URANIUM; + if (resR == 0 && resG == 255 && resB == 0) return Gfx::TR_POWER; + + // TODO key res values + //if (ress == 24) return Gfx::TR_KEY_A; // ~green? + //if (ress == 25) return Gfx::TR_KEY_B; // ~green? + //if (ress == 26) return Gfx::TR_KEY_C; // ~green? + //if (ress == 27) return Gfx::TR_KEY_D; // ~green? + + return TR_NULL; +} + +void Gfx::CTerrain::FlushRelief() +{ + m_relief.clear(); +} + +/** + The size of the image must be dimension dx and dy with dx=dy=(mosaic*brick)+1. + The image must be 24 bits/pixel, but 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 Gfx::CTerrain::ReliefFromPNG(const std::string &fileName, float scaleRelief, + bool adjustBorder) +{ + m_scaleRelief = scaleRelief; + + CImage img; + if (! img.Load(CApplication::GetInstance().GetDataFilePath(m_engine->GetTextureDir(), fileName))) + return false; + + ImageData *data = img.GetData(); + + int size = (m_mosaic*m_brick)+1; + + if ( (data->surface->w != size) || (data->surface->h != size) || + (data->surface->format->BytesPerPixel != 3) ) + return false; + + unsigned char* pixels = static_cast(data->surface->pixels); + + float limit = 0.9f; + for (int y = 0; y < size; y++) + { + for (int x = 0; x < size; x++) + { + float level = (255 - pixels[3*x+3*size*(size-y-1)]) * scaleRelief; + + float dist = Math::Max(fabs(static_cast(x-size/2)), + fabs(static_cast(y-size/2))); + dist = dist/ static_cast(size / 2); + if (dist > limit && adjustBorder) + { + dist = (dist-limit)/(1.0f-limit); // 0..1 + if (dist > 1.0f) dist = 1.0f; + float border = 300.0f+Math::Rand()*20.0f; + level = level+dist*(border-level); + } + + m_relief[x+y*size] = level; + } + } + + return true; +} + +bool Gfx::CTerrain::ReliefAddDot(Math::Vector pos, float scaleRelief) +{ + float dim = (m_mosaic*m_brick*m_size)/2.0f; + int size = (m_mosaic*m_brick)+1; + + pos.x = (pos.x+dim)/m_size; + pos.z = (pos.z+dim)/m_size; + + int x = static_cast(pos.x); + int y = static_cast(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; +} + +void Gfx::CTerrain::LimitPos(Math::Vector &pos) +{ +// TODO: #if _TEEN +// dim = (m_mosaic*m_brick*m_size)/2.0f*0.98f; + + float dim = (m_mosaic*m_brick*m_size)/2.0f*0.92f; + + 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; +} + +void Gfx::CTerrain::AdjustRelief() +{ + if (m_depth == 1) return; + + int ii = m_mosaic*m_brick+1; + int b = 1 << (m_depth-1); + + for (int y = 0; y < m_mosaic*m_brick; y += b) + { + for (int x = 0; x < m_mosaic*m_brick; x += b) + { + int xx = 0; + int yy = 0; + if ((y+yy)%m_brick == 0) + { + float level1 = m_relief[(x+0)+(y+yy)*ii]; + float 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) + { + float level1 = m_relief[(x+0)+(y+yy)*ii]; + float 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) + { + float level1 = m_relief[(x+xx)+(y+0)*ii]; + float 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) + { + float level1 = m_relief[(x+xx)+(y+0)*ii]; + float 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; + } + } + } + } +} + +Math::Vector Gfx::CTerrain::GetVector(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.empty() && + 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 normal soft, taking into account the six adjacent triangles: + +\verbatim + ^ y + | + b---c---+ + |\ |\ | + | \| \| + a---o---d + |\ |\ | + | \| \| + +---f---e--> x +\endverbatim */ +Gfx::VertexTex2 Gfx::CTerrain::GetVertex(int x, int y, int step) +{ + Gfx::VertexTex2 v; + + Math::Vector o = GetVector(x, y); + v.coord = o; + + Math::Vector a = GetVector(x-step, y ); + Math::Vector b = GetVector(x-step, y+step); + Math::Vector c = GetVector(x, y+step); + Math::Vector d = GetVector(x+step, y ); + Math::Vector e = GetVector(x+step, y-step); + Math::Vector f = GetVector(x, y-step); + + Math::Vector s(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.normal = s; + + if (m_multiText) + { + int brick = m_brick/m_subdivMapping; + Math::Vector oo = GetVector((x/brick)*brick, (y/brick)*brick); + o = GetVector(x, y); + v.texCoord.x = (o.x-oo.x)*m_scaleMapping*m_subdivMapping; + v.texCoord.y = 1.0f - (o.z-oo.z)*m_scaleMapping*m_subdivMapping; + } + else + { + v.texCoord.x = o.x*m_scaleMapping; + v.texCoord.y = o.z*m_scaleMapping; + } + + return v; +} + +/** The origin of mosaic is its center. +\verbatim + ^ z + | + | 2---4---6-- + | |\ |\ |\ + | | \| \| + | 1---3---5--- ... + | + +-------------------> x +\endverbatim */ +bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, + const Gfx::Material &mat, + float min, float max) +{ + std::string texName1; + std::string texName2; + + if ( step == 1 && m_engine->GetGroundSpot() ) + { + int i = (ox/5) + (oy/5)*(m_mosaic/5); + std::stringstream s; + s << "shadow"; + s.width(2); + s.fill('0'); + s << i; + s << ".png"; + texName2 = s.str(); + } + + int brick = m_brick/m_subdivMapping; + + Gfx::VertexTex2 o = GetVertex(ox*m_brick+m_brick/2, oy*m_brick+m_brick/2, step); + int total = ((brick/step)+1)*2; + + float pixel = 1.0f/256.0f; // 1 pixel cover (*) + float dp = 1.0f/512.0f; + + Math::Point uv; + + for (int my = 0; my < m_subdivMapping; my++) + { + for (int mx = 0; mx < m_subdivMapping; mx++) + { + if (m_levelText) + { + int xx = ox*m_brick + mx*(m_brick/m_subdivMapping); + int yy = oy*m_brick + my*(m_brick/m_subdivMapping); + LevelTextureName(xx, yy, texName1, uv); + } + else + { + int i = (ox*m_subdivMapping+mx)+(oy*m_subdivMapping+my)*m_mosaic; + std::stringstream s; + s << m_texBaseName; + s.width(3); + s.fill('0'); + s << m_texture[i]; + s << m_texBaseExt; + texName1 = s.str(); + } + + for (int y = 0; y < brick; y += step) + { + Gfx::EngineObjLevel5 buffer; + buffer.vertices.reserve(total); + + buffer.type = Gfx::ENG_TRIANGLE_TYPE_6S; + buffer.material = mat; + + buffer.state = Gfx::ENG_RSTATE_WRAP; + + buffer.state |= Gfx::ENG_RSTATE_SECOND; + if (step == 1) + buffer.state |= Gfx::ENG_RSTATE_DUAL_BLACK; + + for (int x = 0; x <= brick; x += step) + { + Gfx::VertexTex2 p1 = GetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+0 , step); + Gfx::VertexTex2 p2 = GetVertex(ox*m_brick+mx*brick+x, oy*m_brick+my*brick+y+step, step); + p1.coord.x -= o.coord.x; p1.coord.z -= o.coord.z; + p2.coord.x -= o.coord.x; p2.coord.z -= o.coord.z; + + if (m_multiText) + { + if (x == 0) + { + p1.texCoord.x = 0.0f+(0.5f/256.0f); + p2.texCoord.x = 0.0f+(0.5f/256.0f); + } + if (x == brick) + { + p1.texCoord.x = 1.0f-(0.5f/256.0f); + p2.texCoord.x = 1.0f-(0.5f/256.0f); + } + if (y == 0) + p1.texCoord.y = 1.0f-(0.5f/256.0f); + + if (y == brick - step) + p2.texCoord.y = 0.0f+(0.5f/256.0f); + } + + if (m_levelText) + { + p1.texCoord.x /= m_subdivMapping; // 0..1 -> 0..0.25 + p1.texCoord.y /= m_subdivMapping; + p2.texCoord.x /= m_subdivMapping; + p2.texCoord.y /= m_subdivMapping; + + if (x == 0) + { + p1.texCoord.x = 0.0f+dp; + p2.texCoord.x = 0.0f+dp; + } + if (x == brick) + { + p1.texCoord.x = (1.0f/m_subdivMapping)-dp; + p2.texCoord.x = (1.0f/m_subdivMapping)-dp; + } + if (y == 0) + p1.texCoord.y = (1.0f/m_subdivMapping)-dp; + + if (y == brick - step) + p2.texCoord.y = 0.0f+dp; + + p1.texCoord.x += uv.x; + p1.texCoord.y += uv.y; + p2.texCoord.x += uv.x; + p2.texCoord.y += uv.y; + } + + int xx = mx*(m_brick/m_subdivMapping) + x; + int yy = my*(m_brick/m_subdivMapping) + y; + p1.texCoord2.x = (static_cast(ox%5)*m_brick+xx+0.0f)/(m_brick*5); + p1.texCoord2.y = (static_cast(oy%5)*m_brick+yy+0.0f)/(m_brick*5); + p2.texCoord2.x = (static_cast(ox%5)*m_brick+xx+0.0f)/(m_brick*5); + p2.texCoord2.y = (static_cast(oy%5)*m_brick+yy+1.0f)/(m_brick*5); + +// Correction for 1 pixel cover +// 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 + + p1.texCoord2.x = (p1.texCoord2.x+pixel)*(1.0f-pixel)/(1.0f+pixel); + p1.texCoord2.y = (p1.texCoord2.y+pixel)*(1.0f-pixel)/(1.0f+pixel); + p2.texCoord2.x = (p2.texCoord2.x+pixel)*(1.0f-pixel)/(1.0f+pixel); + p2.texCoord2.y = (p2.texCoord2.y+pixel)*(1.0f-pixel)/(1.0f+pixel); + + + buffer.vertices.push_back(p1); + buffer.vertices.push_back(p2); + } + m_engine->AddQuick(objRank, buffer, texName1, texName2, min, max, true); + } + } + } + + Math::Matrix transform; + transform.LoadIdentity(); + transform.Set(1, 4, o.coord.x); + transform.Set(3, 4, o.coord.z); + m_engine->SetObjectTransform(objRank, transform); + + return true; +} + +Gfx::TerrainMaterial* Gfx::CTerrain::LevelSearchMat(int id) +{ + for (int i = 0; i < static_cast( m_levelMat.size() ); i++) + { + if (id == m_levelMat[i].id) + return &m_levelMat[i]; + } + + return nullptr; +} + +void Gfx::CTerrain::LevelTextureName(int x, int y, std::string& name, Math::Point &uv) +{ + x /= m_brick/m_subdivMapping; + y /= m_brick/m_subdivMapping; + + TerrainMaterial* tm = LevelSearchMat(m_levelDot[x+y*m_levelDotSize].id); + if (tm == nullptr) + { + name = "xxx.png"; + uv.x = 0.0f; + uv.y = 0.0f; + } + else + { + name = tm->texName; + uv.x = tm->u; + uv.y = tm->v; + } +} + +float Gfx::CTerrain::LevelGetHeight(int x, int y) +{ + int 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]; +} + +bool Gfx::CTerrain::LevelGetDot(int x, int y, float min, float max, float slope) +{ + float hc = LevelGetHeight(x, y); + float h[4] = + { + LevelGetHeight(x+0, y+1), + LevelGetHeight(x+1, y+0), + LevelGetHeight(x+0, y-1), + LevelGetHeight(x-1, y+0) + }; + + if (hc < min || hc > max) + return false; + + if (slope == 0.0f) + return true; + + if (slope > 0.0f) + { + for (int i = 0; i < 4; i++) + { + if (fabs(hc - h[i]) >= slope) + return false; + } + return true; + } + + if (slope < 0.0f) + { + for (int i = 0; i < 4; i++) + { + if (fabs(hc - h[i]) < -slope) + return false; + } + return true; + } + + return false; +} + + +/** Returns the index within m_levelMat or -1 if there is not. + m_levelMat[i].id gives the identifier. */ +int Gfx::CTerrain::LevelTestMat(char *mat) +{ + for (int i = 0; i < static_cast( m_levelMat.size() ); 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; +} + +void Gfx::CTerrain::LevelSetDot(int x, int y, int id, char *mat) +{ + TerrainMaterial* tm = LevelSearchMat(id); + if (tm == nullptr) 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? + { + int 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 ) + { + int i = (x+0)+(y-1)*m_levelDotSize; + if (m_levelDot[i].mat[0] != mat[2]) + { + m_levelDot[i].mat[0] = mat[2]; + int 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 ) + { + int i = (x-1)+(y+0)*m_levelDotSize; + if (m_levelDot[i].mat[1] != mat[3]) + { + m_levelDot[i].mat[1] = mat[3]; + int 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 ) + { + int i = (x+0)+(y+1)*m_levelDotSize; + if (m_levelDot[i].mat[2] != mat[0]) + { + m_levelDot[i].mat[2] = mat[0]; + int 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 ) + { + int i = (x+1)+(y+0)*m_levelDotSize; + if ( m_levelDot[i].mat[3] != mat[1] ) + { + m_levelDot[i].mat[3] = mat[1]; + int ii = LevelTestMat(m_levelDot[i].mat); + if (ii != -1) + m_levelDot[i].id = m_levelMat[ii].id; + } + } +} + +bool Gfx::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; +} + +bool Gfx::CTerrain::LevelPutDot(int x, int y, int id) +{ + char mat[4]; + + x /= m_brick/m_subdivMapping; + y /= m_brick/m_subdivMapping; + + if ( x < 0 || x >= m_levelDotSize || + y < 0 || y >= m_levelDotSize ) return false; + + TerrainMaterial* tm = LevelSearchMat(id); + if (tm == nullptr) return false; + + // Tries without changing neighbors. + if ( LevelIfDot(x, y, id, tm->mat) ) return true; + + // Tries changing a single neighbor (4x). + for (int 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 (int 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 (int 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 (int 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 (int up = 0; up < m_levelMatMax; up++) + { + for (int 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 (int right = 0; right < m_levelMatMax; right++) + { + for (int 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 (int up = 0; up < m_levelMatMax; up++) + { + for (int 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 (int right = 0; right < m_levelMatMax; right++) + { + for (int 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 (int down = 0; down < m_levelMatMax; down++) + { + for (int 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 (int up = 0; up < m_levelMatMax; up++) + { + for (int 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 (int up = 0; up < m_levelMatMax; up++) + { + for (int right = 0; right < m_levelMatMax; right++) + { + for (int down = 0; down < m_levelMatMax; down++) + { + for (int 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; + } + } + } + } + + GetLogger()->Error("LevelPutDot error\n"); + return false; +} + +bool Gfx::CTerrain::LevelInit(int id) +{ + TerrainMaterial* tm = LevelSearchMat(id); + if (tm == nullptr) return false; + + for (int i = 0; i < m_levelDotSize*m_levelDotSize; i++) + { + m_levelDot[i].id = id; + + for (int j = 0; j < 4; j++) + m_levelDot[i].mat[j] = tm->mat[j]; + } + + return true; +} + +bool Gfx::CTerrain::LevelGenerate(int *id, float min, float max, + float slope, float freq, + Math::Vector center, float radius) +{ + 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, + }; + + TerrainMaterial* tm = nullptr; + + int i = 0; + while ( id[i] != 0 ) + { + tm = LevelSearchMat(id[i++]); + if (tm == nullptr) return false; + } + int numID = i; + + int group = m_brick / m_subdivMapping; + + if (radius > 0.0f && radius < 5.0f) // just a square? + { + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + int xx = static_cast((center.x+dim)/m_size); + int yy = static_cast((center.z+dim)/m_size); + + int x = xx/group; + int y = yy/group; + + tm = LevelSearchMat(id[0]); + if (tm != nullptr) + LevelSetDot(x, y, id[0], tm->mat); // puts the point + } + else + { + for (int y = 0; y < m_levelDotSize; y++) + { + for (int x = 0; x < m_levelDotSize; x++) + { + if (radius != 0.0f) + { + Math::Vector pos; + pos.x = (static_cast(x)-m_levelDotSize/2.0f)*group*m_size; + pos.z = (static_cast(y)-m_levelDotSize/2.0f)*group*m_size; + if (Math::DistanceProjected(pos, center) > radius) continue; + } + + if (freq < 100.0f) + { + int rnd = random[(x%10)+(y%10)*10]; + if ( static_cast(rnd) > freq ) continue; + } + + int xx = x*group + group/2; + int yy = y*group + group/2; + + if (LevelGetDot(xx, yy, min, max, slope)) + { + int rnd = random[(x%10)+(y%10)*10]; + int ii = rnd % numID; + LevelPutDot(xx, yy, id[ii]); + } + } + } + } + + return true; +} + +void Gfx::CTerrain::LevelOpenTable() +{ + if (! m_levelText) return; + if (! m_levelDot.empty()) return; // already allocated + + m_levelDotSize = (m_mosaic*m_brick)/(m_brick/m_subdivMapping)+1; + std::vector(m_levelDotSize*m_levelDotSize).swap(m_levelDot); + + for (int i = 0; i < m_levelDotSize * m_levelDotSize; i++) + { + for (int j = 0; j < 4; j++) + m_levelDot[i].mat[j] = 0; + } +} + +void Gfx::CTerrain::LevelCloseTable() +{ + m_levelDot.clear(); +} + +bool Gfx::CTerrain::CreateSquare(bool multiRes, int x, int y) +{ + Gfx::Material mat; + mat.diffuse = Gfx::Color(1.0f, 1.0f, 1.0f); + mat.ambient = Gfx::Color(0.0f, 0.0f, 0.0f); + + int objRank = m_engine->CreateObject(); + m_engine->SetObjectType(objRank, Gfx::ENG_OBJTYPE_TERRAIN); // it is a terrain + + m_objRank[x+y*m_mosaic] = objRank; + + if (multiRes) + { + float min = 0.0f; + float max = m_vision; + max *= m_engine->GetClippingDistance(); + for (int 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 = Math::HUGE_NUM; + } + } + else + { + CreateMosaic(x, y, 1, objRank, mat, 0.0f, Math::HUGE_NUM); + } + + return true; +} + +bool Gfx::CTerrain::CreateObjects(bool multiRes) +{ + AdjustRelief(); + + for (int y = 0; y < m_mosaic; y++) + { + for (int x = 0; x < m_mosaic; x++) + CreateSquare(multiRes, x, y); + } + + return true; +} + +/** ATTENTION: ok only with m_depth = 2! */ +bool Gfx::CTerrain::Terraform(const Math::Vector &p1, const Math::Vector &p2, float height) +{ + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + Math::IntPoint tp1, tp2; + tp1.x = static_cast((p1.x+dim+m_size/2.0f)/m_size); + tp1.y = static_cast((p1.z+dim+m_size/2.0f)/m_size); + tp2.x = static_cast((p2.x+dim+m_size/2.0f)/m_size); + tp2.y = static_cast((p2.z+dim+m_size/2.0f)/m_size); + + if (tp1.x > tp2.x) + { + int x = tp1.x; + tp1.x = tp2.x; + tp2.x = x; + } + + if (tp1.y > tp2.y) + { + int y = tp1.y; + tp1.y = tp2.y; + tp2.y = y; + } + + int size = (m_mosaic*m_brick)+1; + + // Calculates the current average height + float avg = 0.0f; + int nb = 0; + for (int y = tp1.y; y <= tp2.y; y++) + { + for (int x = tp1.x; x <= tp2.x; x++) + { + avg += m_relief[x+y*size]; + nb ++; + } + } + avg /= static_cast(nb); + + // Changes the description of the relief + for (int y = tp1.y; y <= tp2.y; y++) + { + for (int 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(); + + Math::IntPoint pp1, pp2; + 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 (int y = pp1.y; y <= pp2.y; y++) + { + for (int x = pp1.x; x <= pp2.x; x++) + { + m_engine->DeleteObject(m_objRank[x+y*m_mosaic]); + CreateSquare(m_multiText, x, y); // recreates the square + } + } + m_engine->Update(); + + return true; +} + +void Gfx::CTerrain::SetWind(Math::Vector speed) +{ + m_wind = speed; +} + +Math::Vector Gfx::CTerrain::GetWind() +{ + return m_wind; +} + +float Gfx::CTerrain::GetFineSlope(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); +} + +float Gfx::CTerrain::GetCoarseSlope(const Math::Vector &pos) +{ + if (m_relief.empty()) return 0.0f; + + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + int x = static_cast((pos.x+dim)/m_size); + int y = static_cast((pos.z+dim)/m_size); + + if ( x < 0 || x >= m_mosaic*m_brick || + y < 0 || y >= m_mosaic*m_brick ) return 0.0f; + + float level[4] = + { + m_relief[(x+0)+(y+0)*(m_mosaic*m_brick+1)], + m_relief[(x+1)+(y+0)*(m_mosaic*m_brick+1)], + m_relief[(x+0)+(y+1)*(m_mosaic*m_brick+1)], + m_relief[(x+1)+(y+1)*(m_mosaic*m_brick+1)], + }; + + float min = Math::Min(level[0], level[1], level[2], level[3]); + float max = Math::Max(level[0], level[1], level[2], level[3]); + + return atanf((max-min)/m_size); +} + +bool Gfx::CTerrain::GetNormal(Math::Vector &n, const Math::Vector &p) +{ + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + int x = static_cast((p.x+dim)/m_size); + int y = static_cast((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return false; + + Math::Vector p1 = GetVector(x+0, y+0); + Math::Vector p2 = GetVector(x+1, y+0); + Math::Vector p3 = GetVector(x+0, y+1); + Math::Vector p4 = GetVector(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; +} + +float Gfx::CTerrain::GetFloorLevel(const Math::Vector &p, bool brut, bool water) +{ + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + int x = static_cast((p.x+dim)/m_size); + int y = static_cast((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return false; + + Math::Vector p1 = GetVector(x+0, y+0); + Math::Vector p2 = GetVector(x+1, y+0); + Math::Vector p3 = GetVector(x+0, y+1); + Math::Vector p4 = GetVector(x+1, y+1); + + Math::Vector 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 (! brut) AdjustBuildingLevel(ps); + + if (water) // not going underwater? + { + float level = m_water->GetLevel(); + if (ps.y < level) ps.y = level; // not under water + } + + return ps.y; +} + + +/** This height is positive when you are above the ground */ +float Gfx::CTerrain::GetFloorHeight(const Math::Vector &p, bool brut, bool water) +{ + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + int x = static_cast((p.x+dim)/m_size); + int y = static_cast((p.z+dim)/m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return false; + + Math::Vector p1 = GetVector(x+0, y+0); + Math::Vector p2 = GetVector(x+1, y+0); + Math::Vector p3 = GetVector(x+0, y+1); + Math::Vector p4 = GetVector(x+1, y+1); + + Math::Vector 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 (! brut) AdjustBuildingLevel(ps); + + if (water) // not going underwater? + { + float level = m_water->GetLevel(); + if ( ps.y < level ) ps.y = level; // not under water + } + + return p.y-ps.y; +} + +bool Gfx::CTerrain::MoveOnFloor(Math::Vector &p, bool brut, bool water) +{ + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + int x = static_cast((p.x + dim) / m_size); + int y = static_cast((p.z + dim) / m_size); + + if ( x < 0 || x > m_mosaic*m_brick || + y < 0 || y > m_mosaic*m_brick ) return false; + + Math::Vector p1 = GetVector(x+0, y+0); + Math::Vector p2 = GetVector(x+1, y+0); + Math::Vector p3 = GetVector(x+0, y+1); + Math::Vector p4 = GetVector(x+1, y+1); + + if (fabs(p.z - p2.z) < fabs(p.x - p2.x)) + { + if (! Math::IntersectY(p1, p2, p3, p)) return false; + } + else + { + if (! Math::IntersectY(p2, p4, p3, p)) return false; + } + + if (! brut) AdjustBuildingLevel(p); + + if (water) // not going underwater? + { + float level = m_water->GetLevel(); + if (p.y < level) p.y = level; // not under water + } + + return true; +} + + +/** Returns false if the initial coordinate was too far */ +bool Gfx::CTerrain::ValidPosition(Math::Vector &p, float marging) +{ + bool ok = true; + + float limit = m_mosaic*m_brick*m_size/2.0f - marging; + + if (p.x < -limit) + { + p.x = -limit; + ok = false; + } + + if (p.z < -limit) + { + p.z = -limit; + ok = false; + } + + if (p.x > limit) + { + p.x = limit; + ok = false; + } + + if (p.z > limit) + { + p.z = limit; + ok = false; + } + + return ok; +} + +void Gfx::CTerrain::FlushBuildingLevel() +{ + m_buildingLevels.clear(); +} + +bool Gfx::CTerrain::AddBuildingLevel(Math::Vector center, float min, float max, + float height, float factor) +{ + int i = 0; + for ( ; i < static_cast( m_buildingLevels.size() ); i++) + { + if ( center.x == m_buildingLevels[i].center.x && + center.z == m_buildingLevels[i].center.z ) + { + break; + } + } + + if (i == static_cast( m_buildingLevels.size() )) + m_buildingLevels.push_back(Gfx::BuildingLevel()); + + m_buildingLevels[i].center = center; + m_buildingLevels[i].min = min; + m_buildingLevels[i].max = max; + m_buildingLevels[i].level = GetFloorLevel(center, true); + m_buildingLevels[i].height = height; + m_buildingLevels[i].factor = factor; + m_buildingLevels[i].bboxMinX = center.x-max; + m_buildingLevels[i].bboxMaxX = center.x+max; + m_buildingLevels[i].bboxMinZ = center.z-max; + m_buildingLevels[i].bboxMaxZ = center.z+max; + + return true; +} + +bool Gfx::CTerrain::UpdateBuildingLevel(Math::Vector center) +{ + for (int i = 0; i < static_cast( m_buildingLevels.size() ); i++) + { + if ( center.x == m_buildingLevels[i].center.x && + center.z == m_buildingLevels[i].center.z ) + { + m_buildingLevels[i].center = center; + m_buildingLevels[i].level = GetFloorLevel(center, true); + return true; + } + } + return false; +} + +bool Gfx::CTerrain::DeleteBuildingLevel(Math::Vector center) +{ + for (int i = 0; i < static_cast( m_buildingLevels.size() ); i++) + { + if ( center.x == m_buildingLevels[i].center.x && + center.z == m_buildingLevels[i].center.z ) + { + for (int j = i+1; j < static_cast( m_buildingLevels.size() ); j++) + m_buildingLevels[j-1] = m_buildingLevels[j]; + + m_buildingLevels.pop_back(); + return true; + } + } + return false; +} + +float Gfx::CTerrain::GetBuildingFactor(const Math::Vector &p) +{ + for (int i = 0; i < static_cast( m_buildingLevels.size() ); i++) + { + if ( p.x < m_buildingLevels[i].bboxMinX || + p.x > m_buildingLevels[i].bboxMaxX || + p.z < m_buildingLevels[i].bboxMinZ || + p.z > m_buildingLevels[i].bboxMaxZ ) continue; + + float dist = Math::DistanceProjected(p, m_buildingLevels[i].center); + + if (dist <= m_buildingLevels[i].max) + return m_buildingLevels[i].factor; + } + return 1.0f; // it is normal on the ground +} + +void Gfx::CTerrain::AdjustBuildingLevel(Math::Vector &p) +{ + for (int i = 0; i < static_cast( m_buildingLevels.size() ); i++) + { + if ( p.x < m_buildingLevels[i].bboxMinX || + p.x > m_buildingLevels[i].bboxMaxX || + p.z < m_buildingLevels[i].bboxMinZ || + p.z > m_buildingLevels[i].bboxMaxZ ) continue; + + float dist = Math::DistanceProjected(p, m_buildingLevels[i].center); + + if (dist > m_buildingLevels[i].max) continue; + + if (dist < m_buildingLevels[i].min) + { + p.y = m_buildingLevels[i].level + m_buildingLevels[i].height; + return; + } + + Math::Vector border; + border.x = ((p.x - m_buildingLevels[i].center.x) * m_buildingLevels[i].max) / + dist + m_buildingLevels[i].center.x; + border.z = ((p.z - m_buildingLevels[i].center.z) * m_buildingLevels[i].max) / + dist + m_buildingLevels[i].center.z; + + float base = GetFloorLevel(border, true); + + p.y = (m_buildingLevels[i].max - dist) / + (m_buildingLevels[i].max - m_buildingLevels[i].min) * + (m_buildingLevels[i].level + m_buildingLevels[i].height-base) + + base; + + return; + } +} + + +// returns the hardness of the ground in a given place. +// The hardness determines the noise (SOUND_STEP and SOUND_BOUM). + +float Gfx::CTerrain::GetHardness(const Math::Vector &p) +{ + float factor = GetBuildingFactor(p); + if (factor != 1.0f) return 1.0f; // on building + + if (m_levelDot.empty()) return m_defHardness; + + float dim = (m_mosaic*m_brick*m_size)/2.0f; + + int x, y; + + x = static_cast((p.x+dim)/m_size); + y = static_cast((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; + + int id = m_levelDot[x+y*m_levelDotSize].id; + TerrainMaterial* tm = LevelSearchMat(id); + if (tm == nullptr) return m_defHardness; + + return tm->hardness; +} + +void Gfx::CTerrain::GroundFlat(Math::Vector pos) +{ + static char table[41*41]; + + + float rapport = 3200.0f/1024.0f; + + for (int y = 0; y <= 40; y++) + { + for (int x = 0; x <= 40; x++) + { + int i = x + y*41; + table[i] = 0; + + Math::Vector p; + 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; + + float angle = GetFineSlope(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); +} + +float Gfx::CTerrain::GetFlatZoneRadius(Math::Vector center, float max) +{ + float angle = GetFineSlope(center); + if (angle >= Gfx::FLATLIMIT) + return 0.0f; + + float ref = GetFloorLevel(center, true); + + float radius = 1.0f; + while (radius <= max) + { + angle = 0.0f; + int nb = static_cast(2.0f*Math::PI*radius); + if (nb < 8) nb = 8; + + for (int i = 0; i < nb; i++) + { + Math::Point c(center.x, center.z); + Math::Point p (center.x+radius, center.z); + p = Math::RotatePoint(c, angle, p); + Math::Vector pos; + pos.x = p.x; + pos.z = p.y; + float h = GetFloorLevel(pos, true); + if ( fabs(h-ref) > 1.0f ) return radius; + + angle += Math::PI*2.0f/8.0f; + } + radius += 1.0f; + } + return max; +} + +void Gfx::CTerrain::SetFlyingMaxHeight(float height) +{ + m_flyingMaxHeight = height; +} + +float Gfx::CTerrain::GetFlyingMaxHeight() +{ + return m_flyingMaxHeight; +} + +void Gfx::CTerrain::FlushFlyingLimit() +{ + m_flyingMaxHeight = 280.0f; + m_flyingLimits.clear(); +} + +void Gfx::CTerrain::AddFlyingLimit(Math::Vector center, + float extRadius, float intRadius, + float maxHeight) +{ + Gfx::FlyingLimit fl; + fl.center = center; + fl.extRadius = extRadius; + fl.intRadius = intRadius; + fl.maxHeight = maxHeight; + m_flyingLimits.push_back(fl); +} + +float Gfx::CTerrain::GetFlyingLimit(Math::Vector pos, bool noLimit) +{ + if (noLimit) + return 280.0f; + + if (m_flyingLimits.empty()) + return m_flyingMaxHeight; + + for (int i = 0; i < static_cast( m_flyingLimits.size() ); i++) + { + float dist = Math::DistanceProjected(pos, m_flyingLimits[i].center); + + if (dist >= m_flyingLimits[i].extRadius) + continue; + + if (dist <= m_flyingLimits[i].intRadius) + return m_flyingLimits[i].maxHeight; + + dist -= m_flyingLimits[i].intRadius; + + float h = dist * (m_flyingMaxHeight - m_flyingLimits[i].maxHeight) / + (m_flyingLimits[i].extRadius - m_flyingLimits[i].intRadius); + + return h + m_flyingLimits[i].maxHeight; + } + + return m_flyingMaxHeight; +} diff --git a/src/graphics/engine/terrain.h b/src/graphics/engine/terrain.h index 8d8b165..a198590 100644 --- a/src/graphics/engine/terrain.h +++ b/src/graphics/engine/terrain.h @@ -40,56 +40,72 @@ enum TerrainRes TR_STONE = 1, TR_URANIUM = 2, TR_POWER = 3, - TR_KEYa = 4, - TR_KEYb = 5, - TR_KEYc = 6, - TR_KEYd = 7, + TR_KEY_A = 4, + TR_KEY_B = 5, + TR_KEY_C = 6, + TR_KEY_D = 7, }; - -const short MAXBUILDINGLEVEL = 100; - struct BuildingLevel { - Math::Vector center; - float factor; - float min; - float max; - float level; - float height; - float bboxMinX; - float bboxMaxX; - float bboxMinZ; - float bboxMaxZ; + Math::Vector center; + float factor; + float min; + float max; + float level; + float height; + float bboxMinX; + float bboxMaxX; + float bboxMinZ; + float bboxMaxZ; + + BuildingLevel() + { + factor = min = max = level = height = 0.0f; + bboxMinX = bboxMaxX = bboxMinZ = bboxMaxZ = 0.0f; + } }; - -const short MAXMATTERRAIN = 100; - struct TerrainMaterial { short id; - char texName[20]; + std::string texName; float u,v; float hardness; char mat[4]; // up, right, down, left + + TerrainMaterial() + { + id = 0; + u = v = 0.0f; + hardness = 0.0f; + mat[0] = mat[1] = mat[2] = mat[3] = 0; + } }; struct DotLevel { short id; char mat[4]; // up, right, down, left -}; - -const short MAXFLYINGLIMIT = 10; + DotLevel() + { + id = 0; + mat[0] = mat[1] = mat[2] = mat[3] = 0; + } +}; struct FlyingLimit { - Math::Vector center; - float extRadius; - float intRadius; - float maxHeight; + Math::Vector center; + float extRadius; + float intRadius; + float maxHeight; + + FlyingLimit() + { + extRadius = intRadius = maxHeight = 0.0f; + } }; @@ -100,72 +116,124 @@ public: CTerrain(CInstanceManager* iMan); ~CTerrain(); - bool Generate(int mosaic, int brickP2, float size, float vision, int depth, float hardness); - bool InitTextures(char* baseName, int* table, int dx, int dy); + //! Generates a new flat terrain + bool Generate(int mosaic, int brickPow2, float size, float vision, int depth, float hardness); + //! Initializes the names of textures to use for the land + bool InitTextures(const std::string& baseName, int* table, int dx, int dy); + //! Empties level void LevelFlush(); - bool LevelMaterial(int id, char* baseName, float u, float v, int up, int right, int down, int left, float hardness); + //! Initializes the names of textures to use for the land + void LevelMaterial(int id, std::string& baseName, float u, float v, int up, int right, int down, int left, float hardness); + //! Initializes all the ground with a material bool LevelInit(int id); + //! Generates a level in the terrain bool LevelGenerate(int *id, float min, float max, float slope, float freq, Math::Vector center, float radius); + //! Initializes a completely flat terrain void FlushRelief(); - bool ReliefFromBMP(const char* filename, float scaleRelief, bool adjustBorder); - bool ReliefFromDXF(const char* filename, float scaleRelief); - bool ResFromBMP(const char* filename); - bool CreateObjects(bool bMultiRes); - bool Terraform(const Math::Vector &p1, const Math::Vector &p2, float height); - - void SetWind(Math::Vector speed); - Math::Vector RetWind(); - - float RetFineSlope(const Math::Vector &pos); - float RetCoarseSlope(const Math::Vector &pos); - bool GetNormal(Math::Vector &n, const Math::Vector &p); - float RetFloorLevel(const Math::Vector &p, bool bBrut=false, bool bWater=false); - float RetFloorHeight(const Math::Vector &p, bool bBrut=false, bool bWater=false); - bool MoveOnFloor(Math::Vector &p, bool bBrut=false, bool bWater=false); - bool ValidPosition(Math::Vector &p, float marging); - TerrainRes RetResource(const Math::Vector &p); + //! Load relief from a PNG file + bool ReliefFromPNG(const std::string& filename, float scaleRelief, bool adjustBorder); + //! Load resources from a PNG file + bool ResFromPNG(const std::string& filename); + //! Creates all objects of the terrain within the 3D engine + bool CreateObjects(bool multiRes); + //! Modifies the terrain's relief + bool Terraform(const Math::Vector& p1, const Math::Vector& p2, float height); + + //@{ + //! Management of the wind + void SetWind(Math::Vector speed); + Math::Vector GetWind(); + //@} + + //! Gives the exact slope of the terrain of a place given + float GetFineSlope(const Math::Vector& pos); + //! Gives the approximate slope of the terrain of a specific location + float GetCoarseSlope(const Math::Vector& pos); + //! Gives the normal vector at the position p (x,-,z) of the ground + bool GetNormal(Math::Vector& n, const Math::Vector &p); + //! returns the height of the ground + float GetFloorLevel(const Math::Vector& p, bool brut=false, bool water=false); + //! Returns the height to the ground + float GetFloorHeight(const Math::Vector& p, bool brut=false, bool water=false); + //! Modifies the coordinate "y" of point "p" to rest on the ground floor + bool MoveOnFloor(Math::Vector& p, bool brut=false, bool water=false); + //! Modifies a coordinate so that it is on the ground + bool ValidPosition(Math::Vector& p, float marging); + //! Returns the resource type available underground + Gfx::TerrainRes GetResource(const Math::Vector& p); + //! Adjusts a position so that it does not exceed the boundaries void LimitPos(Math::Vector &pos); + //! Empty the table of elevations void FlushBuildingLevel(); + //! Adds a new elevation for a building bool AddBuildingLevel(Math::Vector center, float min, float max, float height, float factor); + //! Updates the elevation for a building when it was moved up (after a terraforming) bool UpdateBuildingLevel(Math::Vector center); + //! Removes the elevation for a building when it was destroyed bool DeleteBuildingLevel(Math::Vector center); - float RetBuildingFactor(const Math::Vector &p); - float RetHardness(const Math::Vector &p); + //! Returns the influence factor whether a position is on a possible rise + float GetBuildingFactor(const Math::Vector& p); + float GetHardness(const Math::Vector& p); - int RetMosaic(); - int RetBrick(); - float RetSize(); - float RetScaleRelief(); + int GetMosaic(); + int GetBrick(); + float GetSize(); + float GetScaleRelief(); + //! Shows the flat areas on the ground void GroundFlat(Math::Vector pos); - float RetFlatZoneRadius(Math::Vector center, float max); + //! Calculates the radius of the largest flat area available + float GetFlatZoneRadius(Math::Vector center, float max); + //@{ + //! Management of the global max flying height void SetFlyingMaxHeight(float height); - float RetFlyingMaxHeight(); + float GetFlyingMaxHeight(); + //@} + //! Empty the table of flying limits void FlushFlyingLimit(); - bool AddFlyingLimit(Math::Vector center, float extRadius, float intRadius, float maxHeight); - float RetFlyingLimit(Math::Vector pos, bool bNoLimit); + //! Adds a new flying limit + void AddFlyingLimit(Math::Vector center, float extRadius, float intRadius, float maxHeight); + //! Returns the maximum height of flight + float GetFlyingLimit(Math::Vector pos, bool noLimit); protected: + //! Adds a point of elevation in the buffer of relief bool ReliefAddDot(Math::Vector pos, float scaleRelief); + //! Adjust the edges of each mosaic to be compatible with all lower resolutions void AdjustRelief(); - Math::Vector RetVector(int x, int y); - Gfx::VertexTex2 RetVertex(int x, int y, int step); - bool CreateMosaic(int ox, int oy, int step, int objRank, const Gfx::Material &mat, float min, float max); - bool CreateSquare(bool bMultiRes, int x, int y); - - TerrainMaterial* LevelSearchMat(int id); - void LevelTextureName(int x, int y, char *name, Math::Point &uv); - float LevelRetHeight(int x, int y); + //! Calculates a vector of the terrain + Math::Vector GetVector(int x, int y); + //! Calculates a vertex of the terrain + Gfx::VertexTex2 GetVertex(int x, int y, int step); + //! Creates all objects of a mosaic + bool CreateMosaic(int ox, int oy, int step, int objRank, const Gfx::Material& mat, float min, float max); + //! Creates all objects in a mesh square ground + bool CreateSquare(bool multiRes, int x, int y); + + //! Seeks a materials based on theirs identifier + Gfx::TerrainMaterial* LevelSearchMat(int id); + //! Chooses texture to use for a given square + void LevelTextureName(int x, int y, std::string& name, Math::Point &uv); + //! Returns the height of the terrain + float LevelGetHeight(int x, int y); + //! Decide whether a point is using the materials bool LevelGetDot(int x, int y, float min, float max, float slope); + //! Seeks if material exists int LevelTestMat(char *mat); + //! Modifies the state of a point and its four neighbors, without testing if possible void LevelSetDot(int x, int y, int id, char *mat); + //! Tests if a material can give a place, according to its four neighbors. If yes, puts the point. bool LevelIfDot(int x, int y, int id, char *mat); + //! Modifies the state of a point bool LevelPutDot(int x, int y, int id); + //! Initializes a table with empty levels void LevelOpenTable(); + //! Closes the level table void LevelCloseTable(); + //! Adjusts a position according to a possible rise void AdjustBuildingLevel(Math::Vector &p); protected: @@ -173,39 +241,49 @@ protected: CEngine* m_engine; CWater* m_water; - int m_mosaic; // number of mosaics - int m_brick; // number of bricks per mosaics - float m_size; // size of an item in an brick - float m_vision; // vision before a change of resolution - float* m_relief; // table of the relief - int* m_texture; // table of textures - int* m_objRank; // table of rows of objects - bool m_bMultiText; - bool m_bLevelText; - float m_scaleMapping; // scale of the mapping + //! Number of mosaics + int m_mosaic; + //! Number of bricks per mosaics + int m_brick; + int m_levelDotSize; + //! Size of an item in a brick + float m_size; + //! Vision before a change of resolution + float m_vision; + //! Table of the relief + std::vector m_relief; + //! Table of textures + std::vector m_texture; + //! Table of rows of objects + std::vector m_objRank; + //! Table of resources + std::vector m_resources; + bool m_multiText; + bool m_levelText; + //! Scale of the mapping + float m_scaleMapping; float m_scaleRelief; - int m_subdivMapping; - int m_depth; // number of different resolutions (1,2,3,4) - char m_texBaseName[20]; - char m_texBaseExt[10]; + int m_subdivMapping; + //! Number of different resolutions (1,2,3,4) + int m_depth; + std::string m_texBaseName; + std::string m_texBaseExt; float m_defHardness; - TerrainMaterial m_levelMat[MAXMATTERRAIN+1]; - int m_levelMatTotal; + std::vector m_levelMat; + std::vector m_levelDot; int m_levelMatMax; - int m_levelDotSize; - DotLevel* m_levelDot; int m_levelID; - int m_buildingUsed; - BuildingLevel m_buildingTable[MAXBUILDINGLEVEL]; + std::vector m_buildingLevels; - unsigned char* m_resources; - Math::Vector m_wind; // wind speed + //! Wind speed + Math::Vector m_wind; + //! Global flying height limit float m_flyingMaxHeight; - int m_flyingLimitTotal; - FlyingLimit m_flyingLimit[MAXFLYINGLIMIT]; + //! List of local flight limits + std::vector m_flyingLimits; }; }; // namespace Gfx diff --git a/src/graphics/engine/water.h b/src/graphics/engine/water.h index 67be9dc..6d3736e 100644 --- a/src/graphics/engine/water.h +++ b/src/graphics/engine/water.h @@ -72,7 +72,7 @@ public: CWater(CInstanceManager* iMan, Gfx::CEngine* engine); ~CWater(); - void SetGLDevice(Gfx::CDevice device); + void SetDevice(Gfx::CDevice* device); bool EventProcess(const Event &event); void Flush(); bool Create(WaterType type1, WaterType type2, const char *filename, Gfx::Color diffuse, Gfx::Color ambient, float level, float glint, Math::Vector eddy); @@ -80,11 +80,11 @@ public: void DrawSurf(); bool SetLevel(float level); - float RetLevel(); - float RetLevel(CObject* object); + float GetLevel(); + float GetLevel(CObject* object); void SetLava(bool bLava); - bool RetLava(); + bool GetLava(); void AdjustEye(Math::Vector &eye); @@ -92,7 +92,7 @@ protected: bool EventFrame(const Event &event); void LavaFrame(float rTime); void AdjustLevel(Math::Vector &pos, Math::Vector &norm, Math::Point &uv1, Math::Point &uv2); - bool RetWater(int x, int y); + bool GetWater(int x, int y); bool CreateLine(int x, int y, int len); void VaporFlush(); @@ -101,8 +101,8 @@ protected: protected: CInstanceManager* m_iMan; - CEngine* m_engine; - CDevice* m_pDevice; + CEngine* m_engine; + CDevice* m_device; CTerrain* m_terrain; CParticle* m_particule; CSound* m_sound; -- cgit v1.2.3-1-g7c22