diff options
Diffstat (limited to 'src/graphics/engine')
-rw-r--r-- | src/graphics/engine/camera.cpp | 29 | ||||
-rw-r--r-- | src/graphics/engine/cloud.cpp | 47 | ||||
-rw-r--r-- | src/graphics/engine/cloud.h | 31 | ||||
-rw-r--r-- | src/graphics/engine/engine.cpp | 49 | ||||
-rw-r--r-- | src/graphics/engine/engine.h | 7 | ||||
-rw-r--r-- | src/graphics/engine/lightman.cpp | 3 | ||||
-rw-r--r-- | src/graphics/engine/modelfile.cpp | 1205 | ||||
-rw-r--r-- | src/graphics/engine/modelfile.h | 62 | ||||
-rw-r--r-- | src/graphics/engine/terrain.cpp | 969 | ||||
-rw-r--r-- | src/graphics/engine/terrain.h | 286 | ||||
-rw-r--r-- | src/graphics/engine/test/CMakeLists.txt | 25 | ||||
-rw-r--r-- | src/graphics/engine/test/modelfile_test.cpp | 305 | ||||
-rw-r--r-- | src/graphics/engine/water.cpp | 32 | ||||
-rw-r--r-- | src/graphics/engine/water.h | 6 |
14 files changed, 1918 insertions, 1138 deletions
diff --git a/src/graphics/engine/camera.cpp b/src/graphics/engine/camera.cpp index 2db6398..034c5ea 100644 --- a/src/graphics/engine/camera.cpp +++ b/src/graphics/engine/camera.cpp @@ -1272,14 +1272,14 @@ bool Gfx::CCamera::EventFrameFree(const Event &event) m_heightEye -= event.rTime * factor * m_speed; } - m_terrain->ValidPosition(m_eyePt, 10.0f); + m_terrain->AdjustToBounds(m_eyePt, 10.0f); - if (m_terrain->MoveOnFloor(m_eyePt, true)) + if (m_terrain->AdjustToFloor(m_eyePt, true)) { m_eyePt.y += m_heightEye; Math::Vector pos = m_eyePt; - if (m_terrain->MoveOnFloor(pos, true)) + if (m_terrain->AdjustToFloor(pos, true)) { pos.y -= 2.0f; if (m_eyePt.y < pos.y) @@ -1290,7 +1290,7 @@ bool Gfx::CCamera::EventFrameFree(const Event &event) Math::Vector lookatPt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); - if (m_terrain->MoveOnFloor(lookatPt, true)) + if (m_terrain->AdjustToFloor(lookatPt, true)) lookatPt.y += m_heightLookat; SetViewTime(m_eyePt, lookatPt, event.rTime); @@ -1314,14 +1314,14 @@ bool Gfx::CCamera::EventFrameEdit(const Event &event) m_fixDirectionH = Math::NormAngle(m_fixDirectionH); } - m_terrain->ValidPosition(m_eyePt, 10.0f); + m_terrain->AdjustToBounds(m_eyePt, 10.0f); - if (m_terrain->MoveOnFloor(m_eyePt, false)) + if (m_terrain->AdjustToFloor(m_eyePt, false)) { m_eyePt.y += m_editHeight; Math::Vector pos = m_eyePt; - if (m_terrain->MoveOnFloor(pos, false)) + if (m_terrain->AdjustToFloor(pos, false)) { pos.y += 2.0f; if (m_eyePt.y < pos.y) @@ -1332,7 +1332,7 @@ bool Gfx::CCamera::EventFrameEdit(const Event &event) Math::Vector lookatPt = Math::LookatPoint( m_eyePt, m_directionH, m_directionV, 50.0f ); - if ( m_terrain->MoveOnFloor(lookatPt, true)) + if ( m_terrain->AdjustToFloor(lookatPt, true)) lookatPt.y += m_heightLookat; SetViewTime(m_eyePt, lookatPt, event.rTime); @@ -1483,7 +1483,7 @@ bool Gfx::CCamera::EventFrameBack(const Event &event) if ( (physics != NULL) && physics->GetLand() ) // ground? { Math::Vector pos = lookatPt + (lookatPt - m_eyePt); - float floor = m_terrain->GetFloorHeight(pos) - 4.0f; + float floor = m_terrain->GetHeightToFloor(pos) - 4.0f; if (floor > 0.0f) m_eyePt.y += floor; // shows the descent in front } @@ -1551,14 +1551,14 @@ bool Gfx::CCamera::EventFrameExplo(const Event &event) if (m_mouseDirH != 0.0f) m_directionH -= m_mouseDirH * event.rTime * 0.7f * m_speed; - m_terrain->ValidPosition(m_eyePt, 10.0f); + m_terrain->AdjustToBounds(m_eyePt, 10.0f); - if ( m_terrain->MoveOnFloor(m_eyePt, false) ) + if ( m_terrain->AdjustToFloor(m_eyePt, false) ) { m_eyePt.y += m_heightEye; Math::Vector pos = m_eyePt; - if ( m_terrain->MoveOnFloor(pos, false) ) + if ( m_terrain->AdjustToFloor(pos, false) ) { pos.y += 2.0f; if ( m_eyePt.y < pos.y ) @@ -1569,7 +1569,7 @@ bool Gfx::CCamera::EventFrameExplo(const Event &event) Math::Vector lookatPt = Math::LookatPoint(m_eyePt, m_directionH, m_directionV, 50.0f); - if (m_terrain->MoveOnFloor(lookatPt, true)) + if (m_terrain->AdjustToFloor(lookatPt, true)) lookatPt.y += m_heightLookat; SetViewTime(m_eyePt, lookatPt, event.rTime); @@ -1680,7 +1680,7 @@ Math::Vector Gfx::CCamera::ExcludeTerrain(Math::Vector eye, Math::Vector lookat, float &angleH, float &angleV) { Math::Vector pos = eye; - if (m_terrain->MoveOnFloor(pos)) + if (m_terrain->AdjustToFloor(pos)) { float dist = Math::DistanceProjected(lookat, pos); pos.y += 2.0f+dist*0.1f; @@ -1719,3 +1719,4 @@ Math::Vector Gfx::CCamera::ExcludeObject(Math::Vector eye, Math::Vector lookat, return eye;*/ } + diff --git a/src/graphics/engine/cloud.cpp b/src/graphics/engine/cloud.cpp index 6f6bc57..26a75ab 100644 --- a/src/graphics/engine/cloud.cpp +++ b/src/graphics/engine/cloud.cpp @@ -28,7 +28,8 @@ const int CLOUD_LINE_PREALLOCATE_COUNT = 100; -const int DIMEXPAND = 4; // extension of the dimensions +//! Extension of the bricks dimensions +const int CLOUD_SIZE_EXPAND = 4; Gfx::CCloud::CCloud(CInstanceManager* iMan, Gfx::CEngine* engine) @@ -42,7 +43,7 @@ Gfx::CCloud::CCloud(CInstanceManager* iMan, Gfx::CEngine* engine) m_level = 0.0f; m_wind = Math::Vector(0.0f, 0.0f, 0.0f); m_subdiv = 8; - m_enable = true; + m_enabled = true; m_lines.reserve(CLOUD_LINE_PREALLOCATE_COUNT); } @@ -77,8 +78,8 @@ bool Gfx::CCloud::EventFrame(const Event &event) return true; } -void Gfx::CCloud::AdjustLevel(Math::Vector &pos, Math::Vector &eye, float deep, - Math::Point &uv1, Math::Point &uv2) +void Gfx::CCloud::AdjustLevel(Math::Vector& pos, Math::Vector& eye, float deep, + Math::Point& uv1, Math::Point& uv2) { uv1.x = (pos.x+20000.0f)/1280.0f; uv1.y = (pos.z+20000.0f)/1280.0f; @@ -95,14 +96,14 @@ void Gfx::CCloud::AdjustLevel(Math::Vector &pos, Math::Vector &eye, float deep, void Gfx::CCloud::Draw() { - if (! m_enable) return; + if (! m_enabled) return; if (m_level == 0.0f) return; if (m_lines.empty()) return; - std::vector<Gfx::VertexTex2> vertices((m_brick+2)*2, Gfx::VertexTex2()); + std::vector<Gfx::VertexTex2> vertices((m_brickCount+2)*2, Gfx::VertexTex2()); float iDeep = m_engine->GetDeepView(); - float deep = (m_brick*m_size)/2.0f; + float deep = (m_brickCount*m_brickSize)/2.0f; m_engine->SetDeepView(deep); m_engine->SetFocus(m_engine->GetFocus()); m_engine->UpdateMatProj(); // increases the depth of view @@ -132,7 +133,7 @@ void Gfx::CCloud::Draw() matrix.LoadIdentity(); device->SetTransform(Gfx::TRANSFORM_WORLD, matrix); - float size = m_size/2.0f; + float size = m_brickSize/2.0f; Math::Vector eye = m_engine->GetEyePt(); Math::Vector n = Math::Vector(0.0f, -1.0f, 0.0f); @@ -195,17 +196,17 @@ void Gfx::CCloud::CreateLine(int x, int y, int len) line.y = y; line.len = len; - float offset = m_brick*m_size/2.0f - m_size/2.0f; + float offset = m_brickCount*m_brickSize/2.0f - m_brickSize/2.0f; - line.px1 = m_size* line.x - offset; - line.px2 = m_size*(line.x+line.len) - offset; - line.pz = m_size* line.y - offset; + line.px1 = m_brickSize* line.x - offset; + line.px2 = m_brickSize*(line.x+line.len) - offset; + line.pz = m_brickSize* line.y - offset; m_lines.push_back(line); } void Gfx::CCloud::Create(const std::string& fileName, - Gfx::Color diffuse, Gfx::Color ambient, + const Gfx::Color& diffuse, const Gfx::Color& ambient, float level) { m_diffuse = diffuse; @@ -223,18 +224,18 @@ void Gfx::CCloud::Create(const std::string& fileName, m_wind = m_terrain->GetWind(); - m_brick = m_terrain->GetBrick()*m_terrain->GetMosaic()*DIMEXPAND; - m_size = m_terrain->GetSize(); + m_brickCount = m_terrain->GetBrickCount()*m_terrain->GetMosaicCount()*CLOUD_SIZE_EXPAND; + m_brickSize = m_terrain->GetBrickSize(); - m_brick /= m_subdiv*DIMEXPAND; - m_size *= m_subdiv*DIMEXPAND; + m_brickCount /= m_subdiv*CLOUD_SIZE_EXPAND; + m_brickSize *= m_subdiv*CLOUD_SIZE_EXPAND; if (m_level == 0.0f) return; m_lines.clear(); - for (int y = 0; y < m_brick; y++) - CreateLine(0, y, m_brick); + for (int y = 0; y < m_brickCount; y++) + CreateLine(0, y, m_brickCount); return; } @@ -257,12 +258,12 @@ float Gfx::CCloud::GetLevel() return m_level; } -void Gfx::CCloud::SetEnable(bool enable) +void Gfx::CCloud::SetEnabled(bool enabled) { - m_enable = enable; + m_enabled = enabled; } -bool Gfx::CCloud::GetEnable() +bool Gfx::CCloud::GetEnabled() { - return m_enable; + return m_enabled; } diff --git a/src/graphics/engine/cloud.h b/src/graphics/engine/cloud.h index abfaf26..a9109fa 100644 --- a/src/graphics/engine/cloud.h +++ b/src/graphics/engine/cloud.h @@ -79,29 +79,32 @@ public: CCloud(CInstanceManager* iMan, CEngine* engine); ~CCloud(); - bool EventProcess(const Event &event); + bool EventProcess(const Event& event); //! Removes all the clouds void Flush(); //! Creates all areas of cloud - void Create(const std::string& fileName, Gfx::Color diffuse, Gfx::Color ambient, float level); + void Create(const std::string& fileName, const Gfx::Color& diffuse, const Gfx::Color& ambient, float level); //! Draw the clouds void Draw(); - //! Modifies the cloud level + //! Management of cloud level + //@{ void SetLevel(float level); - //! Returns the current level of clouds float GetLevel(); + //@} - //! Activate management of clouds - void SetEnable(bool enable); - bool GetEnable(); + //! Management of clouds + //@{ + void SetEnabled(bool enable); + bool GetEnabled(); + //@} protected: //! Makes the clouds evolve bool EventFrame(const Event &event); //! Adjusts the position to normal, to imitate the clouds at movement - void AdjustLevel(Math::Vector &pos, Math::Vector &eye, float deep, - Math::Point &uv1, Math::Point &uv2); + void AdjustLevel(Math::Vector& pos, Math::Vector& eye, float deep, + Math::Point& uv1, Math::Point& uv2); //! Updates the positions, relative to the ground void CreateLine(int x, int y, int len); @@ -110,9 +113,11 @@ protected: Gfx::CEngine* m_engine; Gfx::CTerrain* m_terrain; - std::string m_fileName; + bool m_enabled; //! Overall level float m_level; + //! Texture + std::string m_fileName; //! Feedrate (wind) Math::Point m_speed; //! Diffuse color @@ -126,13 +131,11 @@ protected: //! Wind speed Math::Vector m_wind; //! Brick mosaic - int m_brick; + int m_brickCount; //! Size of a brick element - float m_size; + float m_brickSize; std::vector<Gfx::CloudLine> m_lines; - - bool m_enable; }; diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 3187dde..068687a 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -142,14 +142,14 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app) m_eyeDirH = 0.0f; m_eyeDirV = 0.0f; m_backgroundName = ""; // no background image - m_backgroundColorUp = 0; - m_backgroundColorDown = 0; - m_backgroundCloudUp = 0; - m_backgroundCloudDown = 0; + m_backgroundColorUp = Gfx::Color(); + m_backgroundColorDown = Gfx::Color(); + m_backgroundCloudUp = Gfx::Color(); + m_backgroundCloudDown = Gfx::Color(); m_backgroundFull = false; m_backgroundQuarter = false; m_overFront = true; - m_overColor = 0; + m_overColor = Gfx::Color(); m_overMode = ENG_RSTATE_TCOLOR_BLACK; m_highlightRank[0] = -1; // empty list m_highlightTime = 0.0f; @@ -208,7 +208,7 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app) m_mouseVisible = false; m_texPath = "textures/"; - m_defaultTexParams.format = Gfx::TEX_IMG_RGB; + m_defaultTexParams.format = Gfx::TEX_IMG_AUTO; m_defaultTexParams.mipmap = true; m_defaultTexParams.minFilter = Gfx::TEX_MIN_FILTER_LINEAR_MIPMAP_LINEAR; m_defaultTexParams.magFilter = Gfx::TEX_MAG_FILTER_LINEAR; @@ -260,6 +260,8 @@ bool Gfx::CEngine::Create() m_lightning = new Gfx::CLightning(m_iMan, this); m_planet = new Gfx::CPlanet(m_iMan, this); + m_lightMan->SetDevice(m_device); + m_text->SetDevice(m_device); if (! m_text->Create()) { @@ -979,7 +981,7 @@ Gfx::EngineObjLevel4* Gfx::CEngine::FindTriangles(int objRank, const Gfx::Materi for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if (p3.min != min || p3.max != max) continue; @@ -1031,7 +1033,7 @@ int Gfx::CEngine::GetPartialTriangles(int objRank, float min, float max, float p for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if (p3.min != min || p3.max != max) continue; @@ -1126,7 +1128,7 @@ void Gfx::CEngine::ChangeLOD() for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if ( Math::IsEqual(p3.min, 0.0f ) && @@ -1620,7 +1622,7 @@ void Gfx::CEngine::UpdateGeometry() for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; for (int l4 = 0; l4 < static_cast<int>( p3.next.size() ); l4++) @@ -1712,7 +1714,7 @@ int Gfx::CEngine::DetectObject(Math::Point mouse) for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if (p3.min != 0.0f) continue; // LOD B or C? @@ -2099,7 +2101,8 @@ void Gfx::CEngine::SetViewParams(const Math::Vector& eyePt, const Math::Vector& if (m_sound == nullptr) m_sound = static_cast<CSoundInterface*>( m_iMan->SearchInstance(CLASS_SOUND) ); - m_sound->SetListener(eyePt, lookatPt); + if (m_sound != nullptr) + m_sound->SetListener(eyePt, lookatPt); } Gfx::Texture Gfx::CEngine::CreateTexture(const std::string& texName, const Gfx::TextureCreateParams& params) @@ -2315,7 +2318,7 @@ void Gfx::CEngine::SetFocus(float focus) m_focus = focus; m_size = m_app->GetVideoConfig().size; - float aspect = (static_cast<float>(m_size.y)) / m_size.x; + float aspect = (static_cast<float>(m_size.x)) / m_size.y; Math::LoadProjectionMatrix(m_matProj, m_focus, aspect, 0.5f, m_deepView[0]); } @@ -2837,7 +2840,7 @@ void Gfx::CEngine::Render() m_statisticTriangle = 0; m_lastState = -1; - m_lastColor = 999; + m_lastColor = Gfx::Color(-1.0f); m_lastMaterial = Gfx::Material(); m_lightMan->UpdateLights(); @@ -2889,6 +2892,8 @@ void Gfx::CEngine::Draw3DScene() if (m_shadowVisible) { + m_lightMan->UpdateLightsEnableState(Gfx::ENG_OBJTYPE_TERRAIN); + // Draw the terrain for (int l1 = 0; l1 < static_cast<int>( m_objectTree.size() ); l1++) @@ -2911,13 +2916,19 @@ void Gfx::CEngine::Draw3DScene() if (! m_objects[objRank].drawWorld) continue; + m_device->SetTransform(Gfx::TRANSFORM_WORLD, m_objects[objRank].transform); + + if (! IsVisible(objRank)) + continue; + for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if ( m_objects[objRank].distance < p3.min || - m_objects[objRank].distance >= p3.max ) continue; + m_objects[objRank].distance >= p3.max ) + continue; for (int l4 = 0; l4 < static_cast<int>( p3.next.size() ); l4++) { @@ -2985,7 +2996,7 @@ void Gfx::CEngine::Draw3DScene() for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if ( m_objects[objRank].distance < p3.min || @@ -3074,7 +3085,7 @@ void Gfx::CEngine::Draw3DScene() for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if ( m_objects[objRank].distance < p3.min || @@ -3189,7 +3200,7 @@ void Gfx::CEngine::DrawInterface() for (int l3 = 0; l3 < static_cast<int>( p2.next.size() ); l3++) { - Gfx::EngineObjLevel3& p3 = p2.next[l1]; + Gfx::EngineObjLevel3& p3 = p2.next[l3]; if (! p3.used) continue; if ( m_objects[objRank].distance < p3.min || diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index b3a576a..705c83a 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -807,6 +807,9 @@ public: float min, float max, Gfx::EngineTextureMapping mode, float pos, float factor, float tl, float ts, float tt); + //! Detects the target object that is selected with the mouse + /** Returns the rank of the object or -1. */ + int DetectObject(Math::Point mouse); //! Creates a shadow for the given object bool CreateShadow(int objRank); @@ -1197,10 +1200,6 @@ protected: //! Compute and return the 2D box on screen of any object bool GetBBox2D(int objRank, Math::Point& min, Math::Point& max); - //! Detects the target object that is selected with the mouse - /** Returns the rank of the object or -1. */ - int DetectObject(Math::Point mouse); - //! Detects whether the mouse is in a triangle. bool DetectTriangle(Math::Point mouse, Gfx::VertexTex2* triangle, int objRank, float& dist); diff --git a/src/graphics/engine/lightman.cpp b/src/graphics/engine/lightman.cpp index 9e15b5a..46000d7 100644 --- a/src/graphics/engine/lightman.cpp +++ b/src/graphics/engine/lightman.cpp @@ -65,6 +65,7 @@ void Gfx::LightProgression::SetTarget(float value) Gfx::DynamicLight::DynamicLight() { used = enabled = false; + includeType = excludeType = Gfx::ENG_OBJTYPE_NULL; } @@ -147,8 +148,6 @@ bool Gfx::CLightManager::DeleteLight(int lightRank) return true; } -// Specifies a light. - bool Gfx::CLightManager::SetLight(int lightRank, const Gfx::Light &light) { if ( (lightRank < 0) || (lightRank >= static_cast<int>( m_dynLights.size() )) ) diff --git a/src/graphics/engine/modelfile.cpp b/src/graphics/engine/modelfile.cpp index 9d7a389..2049749 100644 --- a/src/graphics/engine/modelfile.cpp +++ b/src/graphics/engine/modelfile.cpp @@ -23,120 +23,42 @@ #include "common/ioutils.h" #include "common/logger.h" #include "common/stringutils.h" +#include "graphics/engine/engine.h" #include "math/geometry.h" #include <string.h> #include <fstream> +#include <sstream> -//! How big the triangle vector is by default -const int TRIANGLE_PREALLOCATE_COUNT = 2000; - -/** - \struct ModelHeader - \brief Header info for model file +/* + * NOTE: #ifndef checking for MODELFILE_NO_ENGINE + * is provided in this module to conditionally + * disable dependence on CEngine. */ -struct ModelHeader -{ - //! Revision number - int revision; - //! Version number - int version; - //! Total number of vertices - int totalVertices; - //! Reserved area - int reserved[10]; - - ModelHeader() - { - memset(this, 0, sizeof(*this)); - } -}; -struct OldModelTriangle1 -{ - char used; - char selected; - Gfx::Vertex p1; - Gfx::Vertex p2; - Gfx::Vertex p3; - Gfx::Material material; - char texName[20]; - float min; - float max; - - OldModelTriangle1() - { - memset(this, 0, sizeof(*this)); - } -}; - -struct OldModelTriangle2 -{ - char used; - char selected; - Gfx::Vertex p1; - Gfx::Vertex p2; - Gfx::Vertex p3; - Gfx::Material material; - char texName[20]; - float min; - float max; - long state; - short reserved1; - short reserved2; - short reserved3; - short reserved4; - OldModelTriangle2() - { - memset(this, 0, sizeof(*this)); - } -}; - - -struct NewModelTriangle -{ - char used; - char selected; - Gfx::VertexTex2 p1; - Gfx::VertexTex2 p2; - Gfx::VertexTex2 p3; - Gfx::Material material; - char texName[20]; - float min; - float max; - long state; - short texNum2; - short reserved2; - short reserved3; - short reserved4; +//! How big the triangle vector is by default +const int TRIANGLE_PREALLOCATE_COUNT = 2000; - NewModelTriangle() - { - memset(this, 0, sizeof(*this)); - } -}; -Gfx::Vertex ReadBinaryVertex(std::istream &stream) +bool ReadBinaryVertex(std::istream& stream, Gfx::Vertex& vertex) { - Gfx::Vertex result; - - result.coord.x = IOUtils::ReadBinaryFloat(stream); - result.coord.y = IOUtils::ReadBinaryFloat(stream); - result.coord.z = IOUtils::ReadBinaryFloat(stream); - result.normal.x = IOUtils::ReadBinaryFloat(stream); - result.normal.y = IOUtils::ReadBinaryFloat(stream); - result.normal.z = IOUtils::ReadBinaryFloat(stream); - result.texCoord.x = IOUtils::ReadBinaryFloat(stream); - result.texCoord.y = IOUtils::ReadBinaryFloat(stream); - - return result; + vertex.coord.x = IOUtils::ReadBinaryFloat(stream); + vertex.coord.y = IOUtils::ReadBinaryFloat(stream); + vertex.coord.z = IOUtils::ReadBinaryFloat(stream); + vertex.normal.x = IOUtils::ReadBinaryFloat(stream); + vertex.normal.y = IOUtils::ReadBinaryFloat(stream); + vertex.normal.z = IOUtils::ReadBinaryFloat(stream); + vertex.texCoord.x = IOUtils::ReadBinaryFloat(stream); + vertex.texCoord.y = IOUtils::ReadBinaryFloat(stream); + + return !stream.fail(); } -void WriteBinaryVertex(Gfx::Vertex vertex, std::ostream &stream) +bool WriteBinaryVertex(Gfx::Vertex vertex, std::ostream& stream) { IOUtils::WriteBinaryFloat(vertex.coord.x, stream); IOUtils::WriteBinaryFloat(vertex.coord.y, stream); @@ -146,27 +68,27 @@ void WriteBinaryVertex(Gfx::Vertex vertex, std::ostream &stream) IOUtils::WriteBinaryFloat(vertex.normal.z, stream); IOUtils::WriteBinaryFloat(vertex.texCoord.x, stream); IOUtils::WriteBinaryFloat(vertex.texCoord.y, stream); + + return !stream.fail(); } -Gfx::VertexTex2 ReadBinaryVertexTex2(std::istream &stream) +bool ReadBinaryVertexTex2(std::istream& stream, Gfx::VertexTex2& vertex) { - Gfx::VertexTex2 result; - - result.coord.x = IOUtils::ReadBinaryFloat(stream); - result.coord.y = IOUtils::ReadBinaryFloat(stream); - result.coord.z = IOUtils::ReadBinaryFloat(stream); - result.normal.x = IOUtils::ReadBinaryFloat(stream); - result.normal.y = IOUtils::ReadBinaryFloat(stream); - result.normal.z = IOUtils::ReadBinaryFloat(stream); - result.texCoord.x = IOUtils::ReadBinaryFloat(stream); - result.texCoord.y = IOUtils::ReadBinaryFloat(stream); - result.texCoord2.x = IOUtils::ReadBinaryFloat(stream); - result.texCoord2.y = IOUtils::ReadBinaryFloat(stream); - - return result; + vertex.coord.x = IOUtils::ReadBinaryFloat(stream); + vertex.coord.y = IOUtils::ReadBinaryFloat(stream); + vertex.coord.z = IOUtils::ReadBinaryFloat(stream); + vertex.normal.x = IOUtils::ReadBinaryFloat(stream); + vertex.normal.y = IOUtils::ReadBinaryFloat(stream); + vertex.normal.z = IOUtils::ReadBinaryFloat(stream); + vertex.texCoord.x = IOUtils::ReadBinaryFloat(stream); + vertex.texCoord.y = IOUtils::ReadBinaryFloat(stream); + vertex.texCoord2.x = IOUtils::ReadBinaryFloat(stream); + vertex.texCoord2.y = IOUtils::ReadBinaryFloat(stream); + + return !stream.fail(); } -void WriteBinaryVertexTex2(Gfx::VertexTex2 vertex, std::ostream &stream) +bool WriteBinaryVertexTex2(Gfx::VertexTex2 vertex, std::ostream& stream) { IOUtils::WriteBinaryFloat(vertex.coord.x, stream); IOUtils::WriteBinaryFloat(vertex.coord.y, stream); @@ -178,38 +100,83 @@ void WriteBinaryVertexTex2(Gfx::VertexTex2 vertex, std::ostream &stream) IOUtils::WriteBinaryFloat(vertex.texCoord.y, stream); IOUtils::WriteBinaryFloat(vertex.texCoord2.x, stream); IOUtils::WriteBinaryFloat(vertex.texCoord2.y, stream); + + return !stream.fail(); } -Gfx::Material ReadBinaryMaterial(std::istream &stream) +bool ReadTextVertexTex2(const std::string& text, Gfx::VertexTex2& vertex) { - Gfx::Material result; + std::stringstream stream; + stream.str(text); - result.diffuse.r = IOUtils::ReadBinaryFloat(stream); - result.diffuse.g = IOUtils::ReadBinaryFloat(stream); - result.diffuse.b = IOUtils::ReadBinaryFloat(stream); - result.diffuse.a = IOUtils::ReadBinaryFloat(stream); + std::string what; - result.ambient.r = IOUtils::ReadBinaryFloat(stream); - result.ambient.g = IOUtils::ReadBinaryFloat(stream); - result.ambient.b = IOUtils::ReadBinaryFloat(stream); - result.ambient.a = IOUtils::ReadBinaryFloat(stream); + stream >> what; + if (what != "c") + return false; - result.specular.r = IOUtils::ReadBinaryFloat(stream); - result.specular.g = IOUtils::ReadBinaryFloat(stream); - result.specular.b = IOUtils::ReadBinaryFloat(stream); - result.specular.a = IOUtils::ReadBinaryFloat(stream); + stream >> vertex.coord.x >> vertex.coord.y >> vertex.coord.z; - /* emissive.r = */ IOUtils::ReadBinaryFloat(stream); - /* emissive.g = */ IOUtils::ReadBinaryFloat(stream); - /* emissive.b = */ IOUtils::ReadBinaryFloat(stream); - /* emissive.a = */ IOUtils::ReadBinaryFloat(stream); + stream >> what; + if (what != "n") + return false; + + stream >> vertex.normal.x >> vertex.normal.y >> vertex.normal.z; + + stream >> what; + if (what != "t1") + return false; - /* power = */ IOUtils::ReadBinaryFloat(stream); + stream >> vertex.texCoord.x >> vertex.texCoord.y; - return result; + stream >> what; + if (what != "t2") + return false; + + stream >> vertex.texCoord2.x >> vertex.texCoord2.y; + + return !stream.fail(); +} + +bool WriteTextVertexTex2(const Gfx::VertexTex2& vertex, std::ostream& stream) +{ + stream << "c " << vertex.coord.x << " " << vertex.coord.y << " " << vertex.coord.z; + stream << " n " << vertex.normal.x << " " << vertex.normal.y << " " << vertex.normal.z; + stream << " t1 " << vertex.texCoord.x << " " << vertex.texCoord.y; + stream << " t2 " << vertex.texCoord2.x << " " << vertex.texCoord2.y; + stream << std::endl; + + return !stream.fail(); +} + +bool ReadBinaryMaterial(std::istream& stream, Gfx::Material& material) +{ + material.diffuse.r = IOUtils::ReadBinaryFloat(stream); + material.diffuse.g = IOUtils::ReadBinaryFloat(stream); + material.diffuse.b = IOUtils::ReadBinaryFloat(stream); + material.diffuse.a = IOUtils::ReadBinaryFloat(stream); + + material.ambient.r = IOUtils::ReadBinaryFloat(stream); + material.ambient.g = IOUtils::ReadBinaryFloat(stream); + material.ambient.b = IOUtils::ReadBinaryFloat(stream); + material.ambient.a = IOUtils::ReadBinaryFloat(stream); + + material.specular.r = IOUtils::ReadBinaryFloat(stream); + material.specular.g = IOUtils::ReadBinaryFloat(stream); + material.specular.b = IOUtils::ReadBinaryFloat(stream); + material.specular.a = IOUtils::ReadBinaryFloat(stream); + + /* emissive.r = */ IOUtils::ReadBinaryFloat(stream); + /* emissive.g = */ IOUtils::ReadBinaryFloat(stream); + /* emissive.b = */ IOUtils::ReadBinaryFloat(stream); + /* emissive.a = */ IOUtils::ReadBinaryFloat(stream); + + /* power = */ IOUtils::ReadBinaryFloat(stream); + + return !stream.fail(); } -void WriteBinaryMaterial(Gfx::Material material, std::ostream &stream) +bool WriteBinaryMaterial(const Gfx::Material& material, std::ostream& stream) { IOUtils::WriteBinaryFloat(material.diffuse.r, stream); IOUtils::WriteBinaryFloat(material.diffuse.g, stream); @@ -232,13 +199,120 @@ void WriteBinaryMaterial(Gfx::Material material, std::ostream &stream) /* emissive.a */ IOUtils::WriteBinaryFloat(0.0f, stream); /* power */ IOUtils::WriteBinaryFloat(0.0f, stream); + + return !stream.fail(); +} + +bool ReadTextMaterial(const std::string& text, Gfx::Material& material) +{ + std::stringstream stream; + stream.str(text); + + std::string what; + + stream >> what; + if (what != "dif") + return false; + + stream >> material.diffuse.r + >> material.diffuse.g + >> material.diffuse.b + >> material.diffuse.a; + + stream >> what; + if (what != "amb") + return false; + + stream >> material.ambient.r + >> material.ambient.g + >> material.ambient.b + >> material.ambient.a; + + stream >> what; + if (what != "spc") + return false; + + stream >> material.specular.r + >> material.specular.g + >> material.specular.b + >> material.specular.a; + + return !stream.fail(); +} + +bool WriteTextMaterial(const Gfx::Material& material, std::ostream& stream) +{ + stream << "dif " << material.diffuse.r + << " " << material.diffuse.g + << " " << material.diffuse.b + << " " << material.diffuse.a; + + stream << " amb " << material.ambient.r + << " " << material.ambient.g + << " " << material.ambient.b + << " " << material.ambient.a; + + stream << " spc " << material.specular.r + << " " << material.specular.g << " " + << material.specular.b << " " + << material.specular.a; + + stream << std::endl; + + return !stream.fail(); } -Gfx::ModelTriangle::ModelTriangle() +template<typename T> +bool ReadLineValue(std::istream& stream, const std::string& prefix, T& value) { - min = 0.0f; - max = 0.0f; - state = 0L; + std::string line; + while (true) + { + if (stream.eof() || stream.fail()) + return false; + + std::getline(stream, line); + if (!line.empty() && line[0] != '#') + break; + } + + std::stringstream s; + s.str(line); + + std::string what; + s >> what; + if (what != prefix) + return false; + + s >> value; + + return true; +} + +bool ReadLineString(std::istream& stream, const std::string& prefix, std::string& value) +{ + std::string line; + while (true) + { + if (stream.eof() || stream.fail()) + return false; + + std::getline(stream, line); + if (!line.empty() && line[0] != '#') + break; + } + + std::stringstream s; + s.str(line); + + std::string what; + s >> what; + if (what != prefix) + return false; + + std::getline(s, value); + + return true; } @@ -246,7 +320,9 @@ Gfx::CModelFile::CModelFile(CInstanceManager* iMan) { m_iMan = iMan; +#ifndef MODELFILE_NO_ENGINE m_engine = static_cast<CEngine*>(m_iMan->SearchInstance(CLASS_ENGINE)); +#endif m_triangles.reserve(TRIANGLE_PREALLOCATE_COUNT); } @@ -255,71 +331,171 @@ Gfx::CModelFile::~CModelFile() { } -std::string Gfx::CModelFile::GetError() + +/******************************************************* + Deprecated formats + *******************************************************/ + +/** + * \struct OldModelHeader + * \brief Colobot binary model header info + * + * @deprecated + */ +struct OldModelHeader { - return m_error; -} + //! Revision number + int revision; + //! Version number + int version; + //! Total number of triangles + int totalTriangles; + //! Reserved area + int reserved[10]; + + OldModelHeader() + { + memset(this, 0, sizeof(*this)); + } +}; + + +/** + * \struct OldModelTriangle1 + * \brief Colobot binary model file version 1 + * + * @deprecated + */ +struct OldModelTriangle1 +{ + char used; + char selected; + Gfx::Vertex p1; + Gfx::Vertex p2; + Gfx::Vertex p3; + Gfx::Material material; + char texName[20]; + float min; + float max; + OldModelTriangle1() + { + memset(this, 0, sizeof(*this)); + } +}; + +/** + * \struct OldModelTriangle2 + * \brief Colobot binary model file version 2 + * + * @deprecated + */ +struct OldModelTriangle2 +{ + char used; + char selected; + Gfx::Vertex p1; + Gfx::Vertex p2; + Gfx::Vertex p3; + Gfx::Material material; + char texName[20]; + float min; + float max; + long state; + short reserved1; + short reserved2; + short reserved3; + short reserved4; + OldModelTriangle2() + { + memset(this, 0, sizeof(*this)); + } +}; + +/** + * \struct OldModelTriangle3 + * \brief Colobot binary model file version 3 + * + * @deprecated + */ +struct OldModelTriangle3 +{ + char used; + char selected; + Gfx::VertexTex2 p1; + Gfx::VertexTex2 p2; + Gfx::VertexTex2 p3; + Gfx::Material material; + char texName[20]; + float min; + float max; + long state; + short texNum2; + short reserved2; + short reserved3; + short reserved4; + + OldModelTriangle3() + { + memset(this, 0, sizeof(*this)); + } +}; -bool Gfx::CModelFile::ReadModel(const std::string &filename, bool edit, bool meta) +bool Gfx::CModelFile::ReadModel(const std::string& fileName) { m_triangles.clear(); - m_error = ""; std::ifstream stream; - stream.open(filename.c_str(), std::ios_base::in | std::ios_base::binary); - if (! stream.good()) + stream.open(fileName.c_str(), std::ios_base::in | std::ios_base::binary); + if (!stream.good()) { - m_error = std::string("Could not open file '") + filename + std::string("'"); + GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); return false; } - return ReadModel(stream, edit, meta); + return ReadModel(stream); } -bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta) +bool Gfx::CModelFile::ReadModel(std::istream& stream) { m_triangles.clear(); - m_error = ""; - // FIXME: for now, reading models only from files, not metafile - - ModelHeader header; + OldModelHeader header; header.revision = IOUtils::ReadBinary<4, int>(stream); header.version = IOUtils::ReadBinary<4, int>(stream); - header.totalVertices = IOUtils::ReadBinary<4, int>(stream); + header.totalTriangles = IOUtils::ReadBinary<4, int>(stream); for (int i = 0; i < 10; ++i) header.reserved[i] = IOUtils::ReadBinary<4, int>(stream); - if (! stream.good()) + if (!stream.good()) { - m_error = "Error reading model file header"; + GetLogger()->Error("Error reading model file header\n"); return false; } // Old model version #1 if ( (header.revision == 1) && (header.version == 0) ) { - for (int i = 0; i < header.totalVertices; ++i) + for (int i = 0; i < header.totalTriangles; ++i) { OldModelTriangle1 t; t.used = IOUtils::ReadBinary<1, char>(stream); t.selected = IOUtils::ReadBinary<1, char>(stream); - t.p1 = ReadBinaryVertex(stream); - t.p2 = ReadBinaryVertex(stream); - t.p3 = ReadBinaryVertex(stream); + ReadBinaryVertex(stream, t.p1); + ReadBinaryVertex(stream, t.p2); + ReadBinaryVertex(stream, t.p3); - t.material = ReadBinaryMaterial(stream); + ReadBinaryMaterial(stream, t.material); stream.read(t.texName, 20); t.min = IOUtils::ReadBinaryFloat(stream); t.max = IOUtils::ReadBinaryFloat(stream); - if (! stream.good()) + if (stream.fail()) { - m_error = "Error reading model data"; + GetLogger()->Error("Error reading model data\n"); return false; } @@ -338,17 +514,17 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta) } else if ( header.revision == 1 && header.version == 1 ) { - for (int i = 0; i < header.totalVertices; ++i) + for (int i = 0; i < header.totalTriangles; ++i) { OldModelTriangle2 t; t.used = IOUtils::ReadBinary<1, char>(stream); t.selected = IOUtils::ReadBinary<1, char>(stream); - t.p1 = ReadBinaryVertex(stream); - t.p2 = ReadBinaryVertex(stream); - t.p3 = ReadBinaryVertex(stream); + ReadBinaryVertex(stream, t.p1); + ReadBinaryVertex(stream, t.p2); + ReadBinaryVertex(stream, t.p3); - t.material = ReadBinaryMaterial(stream); + ReadBinaryMaterial(stream, t.material); stream.read(t.texName, 20); t.min = IOUtils::ReadBinaryFloat(stream); t.max = IOUtils::ReadBinaryFloat(stream); @@ -359,9 +535,9 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta) t.reserved3 = IOUtils::ReadBinary<2, short>(stream); t.reserved4 = IOUtils::ReadBinary<2, short>(stream); - if (! stream.good()) + if (stream.fail()) { - m_error = "Error reading model data"; + GetLogger()->Error("Error reading model data\n"); return false; } @@ -381,19 +557,19 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta) } else { - for (int i = 0; i < header.totalVertices; ++i) + for (int i = 0; i < header.totalTriangles; ++i) { - NewModelTriangle t; + OldModelTriangle3 t; t.used = IOUtils::ReadBinary<1, char>(stream); t.selected = IOUtils::ReadBinary<1, char>(stream); /* padding */ IOUtils::ReadBinary<2, unsigned int>(stream); - t.p1 = ReadBinaryVertexTex2(stream); - t.p2 = ReadBinaryVertexTex2(stream); - t.p3 = ReadBinaryVertexTex2(stream); + ReadBinaryVertexTex2(stream, t.p1); + ReadBinaryVertexTex2(stream, t.p2); + ReadBinaryVertexTex2(stream, t.p3); - t.material = ReadBinaryMaterial(stream); + ReadBinaryMaterial(stream, t.material); stream.read(t.texName, 20); t.min = IOUtils::ReadBinaryFloat(stream); t.max = IOUtils::ReadBinaryFloat(stream); @@ -404,9 +580,9 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta) t.reserved3 = IOUtils::ReadBinary<2, short>(stream); t.reserved4 = IOUtils::ReadBinary<2, short>(stream); - if (! stream.good()) + if (stream.fail()) { - m_error = "Error reading model data"; + GetLogger()->Error("Error reading model data\n"); return false; } @@ -417,15 +593,26 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta) triangle.material = t.material; triangle.tex1Name = std::string(t.texName); - char tex2Name[20] = { 0 }; triangle.min = t.min; triangle.max = t.max; triangle.state = t.state; + triangle.variableTex2 = t.texNum2 == 1; + + if (triangle.tex1Name == "plant.png") + triangle.state |= Gfx::ENG_RSTATE_ALPHA; + + if (!triangle.variableTex2 && t.texNum2 != 0) + { + if (t.texNum2 >= 1 && t.texNum2 <= 10) + triangle.state |= Gfx::ENG_RSTATE_DUAL_BLACK; - if (t.texNum2 != 0) - sprintf(tex2Name, "dirty%.2d.tga", t.texNum2); // hardcoded as in the original code + if (t.texNum2 >= 11 && t.texNum2 <= 20) + triangle.state |= Gfx::ENG_RSTATE_DUAL_WHITE; - triangle.tex2Name = std::string(tex2Name); + char tex2Name[20] = { 0 }; + sprintf(tex2Name, "dirty%.2d.png", t.texNum2); // hardcoded as in original code + triangle.tex2Name = tex2Name; + } m_triangles.push_back(triangle); } @@ -433,97 +620,70 @@ bool Gfx::CModelFile::ReadModel(std::istream &stream, bool edit, bool meta) for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i) { - m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "bmp", "tga"); + // All extensions are now png + m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "bmp", "png"); + m_triangles[i].tex1Name = StrUtils::Replace(m_triangles[i].tex1Name, "tga", "png"); + + m_triangles[i].tex2Name = StrUtils::Replace(m_triangles[i].tex2Name, "bmp", "png"); + m_triangles[i].tex2Name = StrUtils::Replace(m_triangles[i].tex2Name, "tga", "png"); - GetLogger()->Info("ModelTriangle %d\n", i+1); + GetLogger()->Trace("ModelTriangle %d\n", i+1); std::string s1 = m_triangles[i].p1.ToString(); - GetLogger()->Info(" p1: %s\n", s1.c_str()); + GetLogger()->Trace(" p1: %s\n", s1.c_str()); std::string s2 = m_triangles[i].p2.ToString(); - GetLogger()->Info(" p2: %s\n", s2.c_str()); + GetLogger()->Trace(" p2: %s\n", s2.c_str()); std::string s3 = m_triangles[i].p3.ToString(); - GetLogger()->Info(" p3: %s\n", s3.c_str()); + GetLogger()->Trace(" p3: %s\n", s3.c_str()); std::string d = m_triangles[i].material.diffuse.ToString(); std::string a = m_triangles[i].material.ambient.ToString(); std::string s = m_triangles[i].material.specular.ToString(); - GetLogger()->Info(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str()); + GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str()); - GetLogger()->Info(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str()); - GetLogger()->Info(" min: %.2f max: %.2f\n", m_triangles[i].min, m_triangles[i].max); - GetLogger()->Info(" state: %ld\n", m_triangles[i].state); + GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), + m_triangles[i].variableTex2 ? "(variable)" : m_triangles[i].tex2Name.c_str()); + GetLogger()->Trace(" min: %.2f max: %.2f\n", m_triangles[i].min, m_triangles[i].max); + GetLogger()->Trace(" state: %ld\n", m_triangles[i].state); } - /* - if (! edit) - { - float limit[2]; - limit[0] = m_engine->RetLimitLOD(0); // frontier AB as config - limit[1] = m_engine->RetLimitLOD(1); // frontier BC as config - - // Standard frontiers -> config. - for (int i = 0; i < m_triangles.size(); ++i) - { - if ( m_triangles[i].min == 0.0f && - m_triangles[i].max == 100.0f ) // resolution A ? - { - m_triangles[i].max = limit[0]; - } - else if ( m_triangles[i].min == 100.0f && - m_triangles[i].max == 200.0f ) // resolution B ? - { - m_triangles[i].min = limit[0]; - m_triangles[i].max = limit[1]; - } - else if ( m_triangles[i].min == 200.0f && - m_triangles[i].max == 1000000.0f ) // resolution C ? - { - m_triangles[i].min = limit[1]; - } - } - }*/ - return true; } -bool Gfx::CModelFile::WriteModel(const std::string &filename) +bool Gfx::CModelFile::WriteModel(const std::string& fileName) { - m_error = ""; - std::ofstream stream; - stream.open(filename.c_str(), std::ios_base::out | std::ios_base::binary); - if (! stream.good()) + stream.open(fileName.c_str(), std::ios_base::out | std::ios_base::binary); + if (!stream.good()) { - m_error = std::string("Could not open file '") + filename + std::string("'"); + GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); return false; } return WriteModel(stream); } -bool Gfx::CModelFile::WriteModel(std::ostream &stream) +bool Gfx::CModelFile::WriteModel(std::ostream& stream) { - m_error = ""; - if (m_triangles.size() == 0) { - m_error = "Empty model"; + GetLogger()->Error("Empty model\n"); return false; } - ModelHeader header; + OldModelHeader header; header.revision = 1; header.version = 2; - header.totalVertices = m_triangles.size(); + header.totalTriangles = m_triangles.size(); IOUtils::WriteBinary<4, int>(header.revision, stream); IOUtils::WriteBinary<4, int>(header.version, stream); - IOUtils::WriteBinary<4, int>(header.totalVertices, stream); + IOUtils::WriteBinary<4, int>(header.totalTriangles, stream); for (int i = 0; i < 10; ++i) IOUtils::WriteBinary<4, int>(header.reserved[i], stream); for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i) { - NewModelTriangle t; + OldModelTriangle3 t; t.used = true; @@ -536,14 +696,21 @@ bool Gfx::CModelFile::WriteModel(std::ostream &stream) t.min = m_triangles[i].min; t.max = m_triangles[i].max; t.state = m_triangles[i].state; + int no = 0; - sscanf(m_triangles[i].tex2Name.c_str(), "dirty%d.tga", &no); // hardcoded as in the original code + if (m_triangles[i].variableTex2) + no = 1; + else + sscanf(m_triangles[i].tex2Name.c_str(), "dirty%d.png", &no); // hardcoded as in the original code + t.texNum2 = no; IOUtils::WriteBinary<1, char>(t.used, stream); IOUtils::WriteBinary<1, char>(t.selected, stream); + /* padding */ IOUtils::WriteBinary<2, unsigned int>(0, stream); + WriteBinaryVertexTex2(t.p1, stream); WriteBinaryVertexTex2(t.p2, stream); WriteBinaryVertexTex2(t.p3, stream); @@ -563,215 +730,489 @@ bool Gfx::CModelFile::WriteModel(std::ostream &stream) return true; } -bool Gfx::CModelFile::ReadDXF(const std::string &filename, float min, float max) + +/******************************************************* + New formats + *******************************************************/ + +/** + * \struct NewModelHeader + * \brief Header for new binary model file + */ +struct NewModelHeader { - m_triangles.clear(); - m_error = ""; + //! File version (1, 2, ...) + int version; + //! Total number of triangles + int totalTriangles; + NewModelHeader() + { + version = 0; + totalTriangles = 0; + } +}; + +/** + * \struct NewModelTriangle1 + * \brief Triangle of new binary model file + * + * NOTE: at this time, it is identical to ModelTriangle struct, but it may change + * independently in the future. + */ +struct NewModelTriangle1 +{ + //! 1st vertex + Gfx::VertexTex2 p1; + //! 2nd vertex + Gfx::VertexTex2 p2; + //! 3rd vertex + Gfx::VertexTex2 p3; + //! Material + Gfx::Material material; + //! Name of 1st texture + std::string tex1Name; + //! Name of 2nd texture + std::string tex2Name; + //! If true, 2nd texture will be taken from current engine setting + bool variableTex2; + //! Min LOD threshold + float min; + //! Max LOD threshold + float max; + //! Rendering state to be set + int state; + + NewModelTriangle1() + { + variableTex2 = true; + min = max = 0.0f; + state = 0; + } +}; + + +bool Gfx::CModelFile::ReadTextModel(const std::string& fileName) +{ std::ifstream stream; - stream.open(filename.c_str(), std::ios_base::in); - if (! stream.good()) + stream.open(fileName.c_str(), std::ios_base::in); + if (!stream.good()) { - m_error = std::string("Couldn't open file '") + filename + std::string("'"); + GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); return false; } - return ReadDXF(stream, min, max); + return ReadTextModel(stream); } -bool Gfx::CModelFile::ReadDXF(std::istream &stream, float min, float max) +bool Gfx::CModelFile::ReadTextModel(std::istream& stream) { m_triangles.clear(); - m_error = ""; - if (! stream.good()) + NewModelHeader header; + + bool headOk = ReadLineValue<int>(stream, "version", header.version) && + ReadLineValue<int>(stream, "total_triangles", header.totalTriangles); + + if (!headOk || !stream.good()) { - m_error = "Invalid stream"; + GetLogger()->Error("Error reading model file header\n"); return false; } - // Input state - bool waitNumVertex = false; - bool waitNumFace = false; - bool waitVertexX = false; - bool waitVertexY = false; - bool waitVertexZ = false; - bool waitFaceX = false; - bool waitFaceY = false; - bool waitFaceZ = false; - - // Vertex array - std::vector<Math::Vector> vertices; - vertices.reserve(TRIANGLE_PREALLOCATE_COUNT); - - // Number of vertices & faces of the primitive to be read - int vertexNum = 0, faceNum = 0; - // Vertex coords - Math::Vector coords; - // Indexes of face (triangle) points - int p1 = 0, p2 = 0, p3 = 0; - - // Input line - std::string line; - while (! stream.eof() ) + // New model version 1 + if (header.version == 1) { - // Read line with command - std::getline(stream, line); - int command = StrUtils::FromString<int>(line); + for (int i = 0; i < header.totalTriangles; ++i) + { + NewModelTriangle1 t; + + std::string p1Text, p2Text, p3Text; + std::string matText; + char varTex2Ch = 0; + + bool triOk = ReadLineString(stream, "p1", p1Text) && + ReadTextVertexTex2(p1Text, t.p1) && + ReadLineString(stream, "p2", p2Text) && + ReadTextVertexTex2(p2Text, t.p2) && + ReadLineString(stream, "p3", p3Text) && + ReadTextVertexTex2(p3Text, t.p3) && + ReadLineString(stream, "mat", matText) && + ReadTextMaterial(matText, t.material) && + ReadLineValue<std::string>(stream, "tex1", t.tex1Name) && + ReadLineValue<std::string>(stream, "tex2", t.tex2Name) && + ReadLineValue<char>(stream, "var_tex2", varTex2Ch) && + ReadLineValue<float>(stream, "min", t.min) && + ReadLineValue<float>(stream, "max", t.max) && + ReadLineValue<int>(stream, "state", t.state); + + if (!triOk || stream.fail()) + { + GetLogger()->Error("Error reading model file header\n"); + return false; + } - // Read line with param - std::getline(stream, line); + t.variableTex2 = varTex2Ch == 'Y'; - bool ok = true; + Gfx::ModelTriangle triangle; + triangle.p1 = t.p1; + triangle.p2 = t.p2; + triangle.p3 = t.p3; + triangle.material = t.material; + triangle.tex1Name = t.tex1Name; + triangle.tex2Name = t.tex2Name; + triangle.variableTex2 = t.variableTex2; + triangle.min = t.min; + triangle.max = t.max; + triangle.state = t.state; - if (command == 66) - { - waitNumVertex = true; - } + m_triangles.push_back(triangle); - if ( command == 71 && waitNumVertex ) - { - waitNumVertex = false; - vertexNum = StrUtils::FromString<int>(line, &ok); - waitNumFace = true; + continue; } + } + else + { + GetLogger()->Error("Unknown model file version\n"); + return false; + } - if ( command == 72 && waitNumFace ) - { - waitNumFace = false; - faceNum = StrUtils::FromString<int>(line, &ok); - waitVertexX = true; - } + for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i) + { + GetLogger()->Trace("ModelTriangle %d\n", i+1); + std::string s1 = m_triangles[i].p1.ToString(); + GetLogger()->Trace(" p1: %s\n", s1.c_str()); + std::string s2 = m_triangles[i].p2.ToString(); + GetLogger()->Trace(" p2: %s\n", s2.c_str()); + std::string s3 = m_triangles[i].p3.ToString(); + GetLogger()->Trace(" p3: %s\n", s3.c_str()); - if ( command == 10 && waitVertexX ) - { - waitVertexX = false; - coords.x = StrUtils::FromString<float>(line, &ok); - waitVertexY = true; - } + std::string d = m_triangles[i].material.diffuse.ToString(); + std::string a = m_triangles[i].material.ambient.ToString(); + std::string s = m_triangles[i].material.specular.ToString(); + GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str()); + + GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str()); + GetLogger()->Trace(" min: %.2f max: %.2f\n", m_triangles[i].min, m_triangles[i].max); + GetLogger()->Trace(" state: %ld\n", m_triangles[i].state); + } + + return true; +} + +bool Gfx::CModelFile::WriteTextModel(const std::string &fileName) +{ + std::ofstream stream; + stream.open(fileName.c_str(), std::ios_base::out); + if (!stream.good()) + { + GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); + return false; + } - if ( command == 20 && waitVertexY ) + return WriteTextModel(stream); +} + +bool Gfx::CModelFile::WriteTextModel(std::ostream& stream) +{ + if (m_triangles.size() == 0) + { + GetLogger()->Error("Empty model\n"); + return false; + } + + NewModelHeader header; + + header.version = 1; + header.totalTriangles = m_triangles.size(); + + stream << "# Colobot text model" << std::endl; + stream << std::endl; + stream << "### HEAD" << std::endl; + stream << "version " << header.version << std::endl; + stream << "total_triangles " << header.totalTriangles << std::endl; + stream << std::endl; + stream << "### TRIANGLES" << std::endl; + + for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i) + { + NewModelTriangle1 t; + + t.p1 = m_triangles[i].p1; + t.p2 = m_triangles[i].p2; + t.p3 = m_triangles[i].p3; + t.material = m_triangles[i].material; + t.tex1Name = m_triangles[i].tex1Name; + t.tex2Name = m_triangles[i].tex2Name; + t.variableTex2 = m_triangles[i].variableTex2; + t.min = m_triangles[i].min; + t.max = m_triangles[i].max; + t.state = m_triangles[i].state; + + stream << "p1 "; + WriteTextVertexTex2(t.p1, stream); + stream << "p2 "; + WriteTextVertexTex2(t.p2, stream); + stream << "p3 "; + WriteTextVertexTex2(t.p3, stream); + stream << "mat "; + WriteTextMaterial(t.material, stream); + + stream << "tex1 " << t.tex1Name << std::endl; + stream << "tex2 " << t.tex2Name << std::endl; + stream << "var_tex2 " << (t.variableTex2 ? 'Y' : 'N') << std::endl; + stream << "min " << t.min << std::endl; + stream << "max " << t.max << std::endl; + stream << "state " << t.state << std::endl; + + stream << std::endl; + + if (stream.fail()) { - waitVertexY = false; - coords.y = StrUtils::FromString<float>(line, &ok); - waitVertexZ = true; + GetLogger()->Error("Error writing model file\n"); + return false; } + } - if ( command == 30 && waitVertexZ ) + return true; +} + +bool Gfx::CModelFile::ReadBinaryModel(const std::string& fileName) +{ + std::ifstream stream; + stream.open(fileName.c_str(), std::ios_base::in | std::ios_base::binary); + if (!stream.good()) + { + GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); + return false; + } + + return ReadBinaryModel(stream); +} + +bool Gfx::CModelFile::ReadBinaryModel(std::istream& stream) +{ + m_triangles.clear(); + + NewModelHeader header; + + header.version = IOUtils::ReadBinary<4, int>(stream); + header.totalTriangles = IOUtils::ReadBinary<4, int>(stream); + + if (!stream.good()) + { + GetLogger()->Error("Error reading model file header\n"); + return false; + } + + // New model version 1 + if (header.version == 1) + { + for (int i = 0; i < header.totalTriangles; ++i) { - waitVertexZ = false; - coords.z = StrUtils::FromString<float>(line, &ok); + NewModelTriangle1 t; + + ReadBinaryVertexTex2(stream, t.p1); + ReadBinaryVertexTex2(stream, t.p2); + ReadBinaryVertexTex2(stream, t.p3); + ReadBinaryMaterial(stream, t.material); + t.tex1Name = IOUtils::ReadBinaryString<1>(stream); + t.tex2Name = IOUtils::ReadBinaryString<1>(stream); + t.variableTex2 = IOUtils::ReadBinaryBool(stream); + t.min = IOUtils::ReadBinaryFloat(stream); + t.max = IOUtils::ReadBinaryFloat(stream); + t.state = IOUtils::ReadBinary<4, unsigned int>(stream); - vertexNum --; - if ( vertexNum >= 0 ) - { - Math::Vector p(coords.x, coords.z, coords.y); // permutation of Y and Z! - vertices.push_back(p); - waitVertexX = true; - } - else + if (stream.fail()) { - waitFaceX = true; + GetLogger()->Error("Error reading model data\n"); + return false; } - } - if ( command == 71 && waitFaceX ) - { - waitFaceX = false; - p1 = StrUtils::FromString<int>(line, &ok); - if ( p1 < 0 ) p1 = -p1; - waitFaceY = true; - } + Gfx::ModelTriangle triangle; + triangle.p1 = t.p1; + triangle.p2 = t.p2; + triangle.p3 = t.p3; + triangle.material = t.material; + triangle.tex1Name = t.tex1Name; + triangle.tex2Name = t.tex2Name; + triangle.variableTex2 = t.variableTex2; + triangle.min = t.min; + triangle.max = t.max; + triangle.state = t.state; - if ( command == 72 && waitFaceY ) - { - waitFaceY = false; - p2 = StrUtils::FromString<int>(line, &ok); - if ( p2 < 0 ) p2 = -p2; - waitFaceZ = true; + m_triangles.push_back(triangle); } + } + else + { + GetLogger()->Error("Unknown model file version\n"); + return false; + } - if ( command == 73 && waitFaceZ ) - { - waitFaceZ = false; - p3 = StrUtils::FromString<int>(line, &ok); - if ( p3 < 0 ) p3 = -p3; + for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i) + { + GetLogger()->Trace("ModelTriangle %d\n", i+1); + std::string s1 = m_triangles[i].p1.ToString(); + GetLogger()->Trace(" p1: %s\n", s1.c_str()); + std::string s2 = m_triangles[i].p2.ToString(); + GetLogger()->Trace(" p2: %s\n", s2.c_str()); + std::string s3 = m_triangles[i].p3.ToString(); + GetLogger()->Trace(" p3: %s\n", s3.c_str()); - faceNum --; - if ( faceNum >= 0 ) - { - assert( (p1-1 >= 0) && (p1-1 < static_cast<int>(vertices.size())) ); - assert( (p2-1 >= 0) && (p2-1 < static_cast<int>(vertices.size())) ); - assert( (p3-1 >= 0) && (p3-1 < static_cast<int>(vertices.size())) ); + std::string d = m_triangles[i].material.diffuse.ToString(); + std::string a = m_triangles[i].material.ambient.ToString(); + std::string s = m_triangles[i].material.specular.ToString(); + GetLogger()->Trace(" mat: d: %s a: %s s: %s\n", d.c_str(), a.c_str(), s.c_str()); - CreateTriangle(vertices[p3-1], vertices[p2-1], vertices[p1-1], min, max); - waitFaceX = true; - } - } + GetLogger()->Trace(" tex1: %s tex2: %s\n", m_triangles[i].tex1Name.c_str(), m_triangles[i].tex2Name.c_str()); + GetLogger()->Trace(" min: %.2f max: %.2f\n", m_triangles[i].min, m_triangles[i].max); + GetLogger()->Trace(" state: %ld\n", m_triangles[i].state); + } - if (! ok) + return true; +} + +bool Gfx::CModelFile::WriteBinaryModel(const std::string& fileName) +{ + std::ofstream stream; + stream.open(fileName.c_str(), std::ios_base::out | std::ios_base::binary); + if (!stream.good()) + { + GetLogger()->Error("Could not open file '%s'\n", fileName.c_str()); + return false; + } + + return WriteBinaryModel(stream); +} + +bool Gfx::CModelFile::WriteBinaryModel(std::ostream& stream) +{ + if (m_triangles.size() == 0) + { + GetLogger()->Error("Empty model\n"); + return false; + } + + NewModelHeader header; + + header.version = 1; + header.totalTriangles = m_triangles.size(); + + IOUtils::WriteBinary<4, int>(header.version, stream); + IOUtils::WriteBinary<4, int>(header.totalTriangles, stream); + + for (int i = 0; i < static_cast<int>( m_triangles.size() ); ++i) + { + NewModelTriangle1 t; + + t.p1 = m_triangles[i].p1; + t.p2 = m_triangles[i].p2; + t.p3 = m_triangles[i].p3; + t.material = m_triangles[i].material; + t.tex1Name = m_triangles[i].tex1Name; + t.tex2Name = m_triangles[i].tex2Name; + t.variableTex2 = m_triangles[i].variableTex2; + t.min = m_triangles[i].min; + t.max = m_triangles[i].max; + t.state = m_triangles[i].state; + + WriteBinaryVertexTex2(t.p1, stream); + WriteBinaryVertexTex2(t.p2, stream); + WriteBinaryVertexTex2(t.p3, stream); + WriteBinaryMaterial(t.material, stream); + IOUtils::WriteBinaryString<1>(t.tex1Name, stream); + IOUtils::WriteBinaryString<1>(t.tex2Name, stream); + IOUtils::WriteBinaryBool(t.variableTex2, stream); + IOUtils::WriteBinaryFloat(t.min, stream); + IOUtils::WriteBinaryFloat(t.max, stream); + IOUtils::WriteBinary<4, unsigned int>(t.state, stream); + + if (stream.fail()) { - m_error = "Error reading data"; + GetLogger()->Error("Error writing model file\n"); return false; } - } return true; } -bool Gfx::CModelFile::CreateEngineObject(int objRank, int addState) + +/******************************************************* + Other stuff + *******************************************************/ + +#ifndef MODELFILE_NO_ENGINE + +bool Gfx::CModelFile::CreateEngineObject(int objRank) { + std::vector<Gfx::VertexTex2> vs(3, Gfx::VertexTex2()); + + float limit[2]; + limit[0] = m_engine->GetLimitLOD(0); // frontier AB as config + limit[1] = m_engine->GetLimitLOD(1); // frontier BC as config + for (int i = 0; i < static_cast<int>( m_triangles.size() ); i++) { - int state = m_triangles[i].state; + // TODO move this to CEngine - /* TODO ??? - if (texName1 == "plant.png") - state |= Gfx::ENG_RSTATE_ALPHA; + float min = m_triangles[i].min; + float max = m_triangles[i].max; - if (m_triangles[i].tex2Name.empty()) + // Standard frontiers -> config + if (min == 0.0f && max == 100.0f) // resolution A ? + { + max = limit[0]; + } + else if (min == 100.0f && max == 200.0f) // resolution B ? { - int texNum = 0; + min = limit[0]; + max = limit[1]; + } + else if (min == 200.0f && max == 1000000.0f) // resolution C ? + { + min = limit[1]; + } - if ( m_triangles[i].texNum2 == 1 ) - { - texNum = m_engine->RetSecondTexture(); - } - else - { - texNum = m_triangles[i].texNum2; - } + int state = m_triangles[i].state; + std::string tex2Name = m_triangles[i].tex2Name; - if ( texNum >= 1 && texNum <= 10 ) - { - state |= D3DSTATEDUALb; - } - if ( texNum >= 11 && texNum <= 20 ) - { - state |= D3DSTATEDUALw; - } - sprintf(texName2, "dirty%.2d.tga", texNum); // ??? - }*/ + if (m_triangles[i].variableTex2) + { + int texNum = m_engine->GetSecondTexture(); - std::vector<Gfx::VertexTex2> vs; - vs.push_back(m_triangles[i].p1); - vs.push_back(m_triangles[i].p2); - vs.push_back(m_triangles[i].p3); + if (texNum >= 1 && texNum <= 10) + state |= Gfx::ENG_RSTATE_DUAL_BLACK; - m_engine->AddTriangles(objRank, vs, - m_triangles[i].material, - state + addState, - m_triangles[i].tex1Name, - m_triangles[i].tex2Name, - m_triangles[i].min, - m_triangles[i].max, false); + if (texNum >= 11 && texNum <= 20) + state |= Gfx::ENG_RSTATE_DUAL_WHITE; + + char name[20] = { 0 }; + sprintf(name, "dirty%.2d.png", texNum); + tex2Name = name; + } + + vs[0] = m_triangles[i].p1; + vs[1] = m_triangles[i].p2; + vs[2] = m_triangles[i].p3; + + bool ok = m_engine->AddTriangles(objRank, vs, + m_triangles[i].material, + state, + m_triangles[i].tex1Name, + tex2Name, + min, max, false); + if (!ok) + return false; } return true; } +#endif + void Gfx::CModelFile::Mirror() { for (int i = 0; i < static_cast<int>( m_triangles.size() ); i++) @@ -790,7 +1231,7 @@ void Gfx::CModelFile::Mirror() } } -std::vector<Gfx::ModelTriangle>& Gfx::CModelFile::GetTriangles() +const std::vector<Gfx::ModelTriangle>& Gfx::CModelFile::GetTriangles() { return m_triangles; } diff --git a/src/graphics/engine/modelfile.h b/src/graphics/engine/modelfile.h index fab190f..833cdf6 100644 --- a/src/graphics/engine/modelfile.h +++ b/src/graphics/engine/modelfile.h @@ -20,7 +20,6 @@ * \brief Model loading - Gfx::CModelFile class (aka modfile) */ -#include "graphics/engine/engine.h" #include "graphics/core/vertex.h" #include "graphics/core/material.h" #include "math/vector.h" @@ -35,6 +34,9 @@ class CInstanceManager; namespace Gfx { +class CEngine; + + /** \struct ModelTriangle \brief Triangle of a 3D model @@ -53,14 +55,21 @@ struct ModelTriangle std::string tex1Name; //! Name of 2nd texture std::string tex2Name; + //! If true, 2nd texture will be taken from current engine setting + bool variableTex2; //! Min LOD threshold float min; //! Max LOD threshold float max; //! Rendering state to be set - long state; - - ModelTriangle(); + int state; + + ModelTriangle() + { + variableTex2 = true; + min = max = 0.0f; + state = 0; + } }; @@ -75,27 +84,45 @@ public: CModelFile(CInstanceManager* iMan); ~CModelFile(); - //! Returns the last error encountered - std::string GetError(); + //! Reads a model in text format from file + bool ReadTextModel(const std::string &fileName); + //! Reads a model in text format from stream + bool ReadTextModel(std::istream &stream); + + //! Writes the model in text format to a file + bool WriteTextModel(const std::string &fileName); + //! Writes the model in text format to a stream + bool WriteTextModel(std::ostream &stream); + + //! Reads a model in new binary format from file + bool ReadBinaryModel(const std::string &fileName); + //! Reads a model in new binary format from stream + bool ReadBinaryModel(std::istream &stream); + + //! Writes the model in binary format to a file + bool WriteBinaryModel(const std::string &fileName); + //! Writes the model in binary format to a stream + bool WriteBinaryModel(std::ostream &stream); //! Reads a binary Colobot model from file - bool ReadModel(const std::string &filename, bool edit = false, bool meta = true); + //! @deprecated + bool ReadModel(const std::string &fileName); //! Reads a binary Colobot model from stream - bool ReadModel(std::istream &stream, bool edit = false, bool meta = true); + //! @deprecated + bool ReadModel(std::istream &stream); //! Writes the model to Colobot binary model file - bool WriteModel(const std::string &filename); + //! @deprecated + bool WriteModel(const std::string &fileName); //! Writes the model to Colobot binary model file + //! @deprecated bool WriteModel(std::ostream &stream); - //! Reads a DXF model from file - bool ReadDXF(const std::string &filename, float min, float max); - //! Reads a DXF model from stream - bool ReadDXF(std::istream &stream, float min, float max); - //! Returns the number of triangles in model int GetTriangleCount(); + //! Returns the triangle vector - std::vector<Gfx::ModelTriangle>& GetTriangles(); + const std::vector<Gfx::ModelTriangle>& GetTriangles(); + //! Returns the height of model -- closest point to X and Z coords of \a pos float GetHeight(Math::Vector pos); @@ -103,7 +130,7 @@ public: void Mirror(); //! Creates an object in the graphics engine from the model - bool CreateEngineObject(int objRank, int addState = 0); + bool CreateEngineObject(int objRank); protected: //! Adds a triangle to the list @@ -113,9 +140,6 @@ protected: CInstanceManager* m_iMan; Gfx::CEngine* m_engine; - //! Last error - std::string m_error; - //! Model triangles std::vector<Gfx::ModelTriangle> m_triangles; }; diff --git a/src/graphics/engine/terrain.cpp b/src/graphics/engine/terrain.cpp index 6b26281..3e70719 100644 --- a/src/graphics/engine/terrain.cpp +++ b/src/graphics/engine/terrain.cpp @@ -44,111 +44,88 @@ Gfx::CTerrain::CTerrain(CInstanceManager* iMan) m_engine = static_cast<Gfx::CEngine*>( m_iMan->SearchInstance(CLASS_ENGINE) ); m_water = static_cast<Gfx::CWater*>( 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_levelMats.reserve(LEVEL_MAT_PREALLOCATE_COUNT); + m_mosaicCount = 20; + m_brickCount = 1 << 4; + m_brickSize = 10.0f; + m_vision = 200.0f; + m_textureScale = 0.01f; + m_scaleRelief = 1.0f; + m_textureSubdivCount = 1; + m_depth = 2; + m_maxMaterialID = 0; + m_wind = Math::Vector(0.0f, 0.0f, 0.0f); + m_defaultHardness = 0.5f; + m_useMaterials = false; + + m_materials.reserve(LEVEL_MAT_PREALLOCATE_COUNT); m_flyingLimits.reserve(FLYING_LIMIT_PREALLOCATE_COUNT); m_buildingLevels.reserve(BUILDING_LEVEL_PREALLOCATE_COUNT); + + FlushBuildingLevel(); + FlushFlyingLimit(); + FlushMaterials(); } 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) +bool Gfx::CTerrain::Generate(int mosaicCount, int brickCountPow2, float brickSize, + float vision, int depth, float hardness) { - m_mosaic = mosaic; - m_brick = 1 << brickPow2; - m_size = size; + m_mosaicCount = mosaicCount; + m_brickCount = 1 << brickCountPow2; + m_brickSize = brickSize; m_vision = vision; m_depth = depth; - m_defHardness = hardness; + m_defaultHardness = hardness; m_engine->SetTerrainVision(vision); - m_multiText = true; - m_levelText = false; - m_scaleMapping = 1.0f / (m_brick*m_size); - m_subdivMapping = 1; + m_textureScale = 1.0f / (m_brickCount*m_brickSize); + m_textureSubdivCount = 1; + + m_useMaterials = false; int dim = 0; - dim = (m_mosaic*m_brick+1)*(m_mosaic*m_brick+1); + dim = (m_mosaicCount*m_brickCount+1)*(m_mosaicCount*m_brickCount+1); std::vector<float>(dim).swap(m_relief); - dim = m_mosaic*m_subdivMapping*m_mosaic*m_subdivMapping; - std::vector<int>(dim).swap(m_texture); + dim = m_mosaicCount*m_textureSubdivCount*m_mosaicCount*m_textureSubdivCount; + std::vector<int>(dim).swap(m_textures); - dim = m_mosaic*m_mosaic; - std::vector<int>(dim).swap(m_objRank); + dim = m_mosaicCount*m_mosaicCount; + std::vector<int>(dim).swap(m_objRanks); return true; } -int Gfx::CTerrain::GetMosaic() +int Gfx::CTerrain::GetMosaicCount() { - return m_mosaic; + return m_mosaicCount; } -int Gfx::CTerrain::GetBrick() +int Gfx::CTerrain::GetBrickCount() { - return m_brick; + return m_brickCount; } -float Gfx::CTerrain::GetSize() +float Gfx::CTerrain::GetBrickSize() { - return m_size; + return m_brickSize; } -float Gfx::CTerrain::GetScaleRelief() +float Gfx::CTerrain::GetReliefScale() { return m_scaleRelief; } bool Gfx::CTerrain::InitTextures(const std::string& baseName, int* table, int dx, int dy) { - m_levelText = false; + m_useMaterials = false; + m_texBaseName = baseName; size_t pos = baseName.find('.'); if (pos == baseName.npos) @@ -161,69 +138,60 @@ bool Gfx::CTerrain::InitTextures(const std::string& baseName, int* table, int dx m_texBaseExt = m_texBaseName.substr(pos); } - for (int y = 0; y < m_mosaic*m_subdivMapping; y++) + for (int y = 0; y < m_mosaicCount*m_textureSubdivCount; y++) { - for (int x = 0; x < m_mosaic*m_subdivMapping; x++) + for (int x = 0; x < m_mosaicCount*m_textureSubdivCount; x++) { - m_texture[x+y*m_mosaic] = table[(x%dx)+(y%dy)*dx]; + m_textures[x+y*m_mosaicCount] = table[(x%dx)+(y%dy)*dx]; } } return true; } -void Gfx::CTerrain::LevelFlush() +void Gfx::CTerrain::FlushMaterials() { - m_levelMats.clear(); - m_levelMatMax = 0; - m_levelID = 1000; - LevelCloseTable(); + m_materials.clear(); + m_maxMaterialID = 0; + m_materialAutoID = 1000; + FlushMaterialPoints(); } -void Gfx::CTerrain::LevelMaterial(int id, std::string& baseName, float u, float v, - int up, int right, int down, int left, - float hardness) +void Gfx::CTerrain::AddMaterial(int id, const std::string& texName, const Math::Point &uv, + int up, int right, int down, int left, + float hardness) { - LevelOpenTable(); + InitMaterialPoints(); if (id == 0) - id = m_levelID++; // puts an ID internal standard + id = m_materialAutoID++; Gfx::TerrainMaterial tm; - tm.texName = baseName; + tm.texName = texName; tm.id = id; - tm.u = u; - tm.v = v; + tm.uv = uv; tm.mat[0] = up; tm.mat[1] = right; tm.mat[2] = down; tm.mat[3] = left; tm.hardness = hardness; - m_levelMats.push_back(tm); + m_materials.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; + if (m_maxMaterialID < up+1 ) m_maxMaterialID = up+1; + if (m_maxMaterialID < right+1) m_maxMaterialID = right+1; + if (m_maxMaterialID < down+1 ) m_maxMaterialID = down+1; + if (m_maxMaterialID < left+1 ) m_maxMaterialID = left+1; - m_levelText = true; - m_subdivMapping = 4; + m_useMaterials = true; + m_textureSubdivCount = 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) + * The image must be 24 bits/pixel and grayscale and dx x dy in size + * with dx = dy = (mosaic*brick)+1 */ +bool Gfx::CTerrain::LoadResources(const std::string& fileName) { CImage img; if (! img.Load(CApplication::GetInstance().GetDataFilePath(m_engine->GetTextureDir(), fileName))) @@ -231,7 +199,7 @@ bool Gfx::CTerrain::ResFromPNG(const std::string& fileName) ImageData *data = img.GetData(); - int size = (m_mosaic*m_brick)+1; + int size = (m_mosaicCount*m_brickCount)+1; m_resources.clear(); @@ -241,8 +209,13 @@ bool Gfx::CTerrain::ResFromPNG(const std::string& fileName) (data->surface->format->BytesPerPixel != 3) ) return false; - // Assuming the data format is compatible - memcpy(&m_resources[0], data->surface->pixels, 3*size*size); + unsigned char* pixels = static_cast<unsigned char*>(data->surface->pixels); + int pitch = data->surface->pitch; + + for (int y = 0; y < size; y++) + { + memcpy(&m_resources[3*size*y], &pixels[pitch*y], 3*size); + } return true; } @@ -252,14 +225,14 @@ Gfx::TerrainRes Gfx::CTerrain::GetResource(const Math::Vector &p) if (m_resources.empty()) return Gfx::TR_NULL; - int x = static_cast<int>((p.x + (m_mosaic*m_brick*m_size)/2.0f)/m_size); - int y = static_cast<int>((p.z + (m_mosaic*m_brick*m_size)/2.0f)/m_size); + int x = static_cast<int>((p.x + (m_mosaicCount*m_brickCount*m_brickSize)/2.0f)/m_brickSize); + int y = static_cast<int>((p.z + (m_mosaicCount*m_brickCount*m_brickSize)/2.0f)/m_brickSize); - if ( x < 0 || x > m_mosaic*m_brick || - y < 0 || y > m_mosaic*m_brick ) + if ( x < 0 || x > m_mosaicCount*m_brickCount || + y < 0 || y > m_mosaicCount*m_brickCount ) return Gfx::TR_NULL; - int size = (m_mosaic*m_brick)+1; + int size = (m_mosaicCount*m_brickCount)+1; int resR = m_resources[3*x+3*size*(size-y-1)]; int resG = m_resources[3*x+3*size*(size-y-1)+1]; @@ -284,20 +257,10 @@ void Gfx::CTerrain::FlushRelief() } /** - 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) + * The image must be 24 bits/pixel and dx x dy in size + * with dx = dy = (mosaic*brick)+1 */ +bool Gfx::CTerrain::LoadRelief(const std::string &fileName, float scaleRelief, + bool adjustBorder) { m_scaleRelief = scaleRelief; @@ -307,20 +270,21 @@ bool Gfx::CTerrain::ReliefFromPNG(const std::string &fileName, float scaleRelief ImageData *data = img.GetData(); - int size = (m_mosaic*m_brick)+1; + int size = (m_mosaicCount*m_brickCount)+1; if ( (data->surface->w != size) || (data->surface->h != size) || (data->surface->format->BytesPerPixel != 3) ) return false; unsigned char* pixels = static_cast<unsigned char*>(data->surface->pixels); + int pitch = data->surface->pitch; 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 level = (255 - pixels[3*x+pitch*(size-y-1)]) * scaleRelief; float dist = Math::Max(fabs(static_cast<float>(x-size/2)), fabs(static_cast<float>(y-size/2))); @@ -340,13 +304,13 @@ bool Gfx::CTerrain::ReliefFromPNG(const std::string &fileName, float scaleRelief return true; } -bool Gfx::CTerrain::ReliefAddDot(Math::Vector pos, float scaleRelief) +bool Gfx::CTerrain::AddReliefPoint(Math::Vector pos, float scaleRelief) { - float dim = (m_mosaic*m_brick*m_size)/2.0f; - int size = (m_mosaic*m_brick)+1; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; + int size = (m_mosaicCount*m_brickCount)+1; - pos.x = (pos.x+dim)/m_size; - pos.z = (pos.z+dim)/m_size; + pos.x = (pos.x+dim)/m_brickSize; + pos.z = (pos.z+dim)/m_brickSize; int x = static_cast<int>(pos.x); int y = static_cast<int>(pos.z); @@ -360,33 +324,20 @@ bool Gfx::CTerrain::ReliefAddDot(Math::Vector pos, float 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; + if (m_depth == 1) return; - int ii = m_mosaic*m_brick+1; + int ii = m_mosaicCount*m_brickCount+1; int b = 1 << (m_depth-1); - for (int y = 0; y < m_mosaic*m_brick; y += b) + for (int y = 0; y < m_mosaicCount*m_brickCount; y += b) { - for (int x = 0; x < m_mosaic*m_brick; x += b) + for (int x = 0; x < m_mosaicCount*m_brickCount; x += b) { int xx = 0; int yy = 0; - if ((y+yy)%m_brick == 0) + if ((y+yy)%m_brickCount == 0) { float level1 = m_relief[(x+0)+(y+yy)*ii]; float level2 = m_relief[(x+b)+(y+yy)*ii]; @@ -397,7 +348,7 @@ void Gfx::CTerrain::AdjustRelief() } yy = b; - if ((y+yy)%m_brick == 0) + if ((y+yy)%m_brickCount == 0) { float level1 = m_relief[(x+0)+(y+yy)*ii]; float level2 = m_relief[(x+b)+(y+yy)*ii]; @@ -408,7 +359,7 @@ void Gfx::CTerrain::AdjustRelief() } xx = 0; - if ((x+xx)%m_brick == 0) + if ((x+xx)%m_brickCount == 0) { float level1 = m_relief[(x+xx)+(y+0)*ii]; float level2 = m_relief[(x+xx)+(y+b)*ii]; @@ -419,7 +370,7 @@ void Gfx::CTerrain::AdjustRelief() } xx = b; - if ((x+xx)%m_brick == 0) + if ((x+xx)%m_brickCount == 0) { float level1 = m_relief[(x+xx)+(y+0)*ii]; float level2 = m_relief[(x+xx)+(y+b)*ii]; @@ -435,14 +386,14 @@ void Gfx::CTerrain::AdjustRelief() 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; + p.x = x*m_brickSize - (m_mosaicCount*m_brickCount*m_brickSize) / 2.0; + p.z = y*m_brickSize - (m_mosaicCount*m_brickCount*m_brickSize) / 2.0; - if ( !m_relief.empty() && - x >= 0 && x <= m_mosaic*m_brick && - y >= 0 && y <= m_mosaic*m_brick ) + if ( !m_relief.empty() && + x >= 0 && x <= m_mosaicCount*m_brickCount && + y >= 0 && y <= m_mosaicCount*m_brickCount ) { - p.y = m_relief[x+y*(m_mosaic*m_brick+1)]; + p.y = m_relief[x+y*(m_mosaicCount*m_brickCount+1)]; } else { @@ -452,7 +403,7 @@ Math::Vector Gfx::CTerrain::GetVector(int x, int y) return p; } -/** Calculates a normal soft, taking into account the six adjacent triangles: +/** Calculates an averaged normal, taking into account the six adjacent triangles: \verbatim ^ y @@ -481,16 +432,16 @@ Gfx::VertexTex2 Gfx::CTerrain::GetVertex(int x, int y, int step) Math::Vector s(0.0f, 0.0f, 0.0f); - if (x-step >= 0 && y+step <= m_mosaic*m_brick+1) + if (x-step >= 0 && y+step <= m_mosaicCount*m_brickCount+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) + if (x+step <= m_mosaicCount*m_brickCount+1 && y+step <= m_mosaicCount*m_brickCount+1) s += Math::NormalToPlane(d,c,o); - if (x+step <= m_mosaic*m_brick+1 && y-step >= 0) + if (x+step <= m_mosaicCount*m_brickCount+1 && y-step >= 0) { s += Math::NormalToPlane(e,d,o); s += Math::NormalToPlane(f,e,o); @@ -502,19 +453,11 @@ Gfx::VertexTex2 Gfx::CTerrain::GetVertex(int x, int y, int step) 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; - } + int brick = m_brickCount/m_textureSubdivCount; + Math::Vector oo = GetVector((x/brick)*brick, (y/brick)*brick); + o = GetVector(x, y); + v.texCoord.x = (o.x-oo.x)*m_textureScale*m_textureSubdivCount; + v.texCoord.y = 1.0f - (o.z-oo.z)*m_textureScale*m_textureSubdivCount; return v; } @@ -539,7 +482,7 @@ bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, if ( step == 1 && m_engine->GetGroundSpot() ) { - int i = (ox/5) + (oy/5)*(m_mosaic/5); + int i = (ox/5) + (oy/5)*(m_mosaicCount/5); std::stringstream s; s << "shadow"; s.width(2); @@ -549,9 +492,9 @@ bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, texName2 = s.str(); } - int brick = m_brick/m_subdivMapping; + int brick = m_brickCount/m_textureSubdivCount; - Gfx::VertexTex2 o = GetVertex(ox*m_brick+m_brick/2, oy*m_brick+m_brick/2, step); + Gfx::VertexTex2 o = GetVertex(ox*m_brickCount+m_brickCount/2, oy*m_brickCount+m_brickCount/2, step); int total = ((brick/step)+1)*2; float pixel = 1.0f/256.0f; // 1 pixel cover (*) @@ -559,24 +502,24 @@ bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, Math::Point uv; - for (int my = 0; my < m_subdivMapping; my++) + for (int my = 0; my < m_textureSubdivCount; my++) { - for (int mx = 0; mx < m_subdivMapping; mx++) + for (int mx = 0; mx < m_textureSubdivCount; mx++) { - if (m_levelText) + if (m_useMaterials) { - 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); + int xx = ox*m_brickCount + mx*(m_brickCount/m_textureSubdivCount); + int yy = oy*m_brickCount + my*(m_brickCount/m_textureSubdivCount); + GetTexture(xx, yy, texName1, uv); } else { - int i = (ox*m_subdivMapping+mx)+(oy*m_subdivMapping+my)*m_mosaic; + int i = (ox*m_textureSubdivCount+mx)+(oy*m_textureSubdivCount+my)*m_mosaicCount; std::stringstream s; s << m_texBaseName; s.width(3); s.fill('0'); - s << m_texture[i]; + s << m_textures[i]; s << m_texBaseExt; texName1 = s.str(); } @@ -597,36 +540,33 @@ bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, 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); + Gfx::VertexTex2 p1 = GetVertex(ox*m_brickCount+mx*brick+x, oy*m_brickCount+my*brick+y+0 , step); + Gfx::VertexTex2 p2 = GetVertex(ox*m_brickCount+mx*brick+x, oy*m_brickCount+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) { - 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); + 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) + if (m_useMaterials) { - 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; + p1.texCoord.x /= m_textureSubdivCount; // 0..1 -> 0..0.25 + p1.texCoord.y /= m_textureSubdivCount; + p2.texCoord.x /= m_textureSubdivCount; + p2.texCoord.y /= m_textureSubdivCount; if (x == 0) { @@ -635,11 +575,11 @@ bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, } if (x == brick) { - p1.texCoord.x = (1.0f/m_subdivMapping)-dp; - p2.texCoord.x = (1.0f/m_subdivMapping)-dp; + p1.texCoord.x = (1.0f/m_textureSubdivCount)-dp; + p2.texCoord.x = (1.0f/m_textureSubdivCount)-dp; } if (y == 0) - p1.texCoord.y = (1.0f/m_subdivMapping)-dp; + p1.texCoord.y = (1.0f/m_textureSubdivCount)-dp; if (y == brick - step) p2.texCoord.y = 0.0f+dp; @@ -650,12 +590,12 @@ bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, 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<float>(ox%5)*m_brick+xx+0.0f)/(m_brick*5); - p1.texCoord2.y = (static_cast<float>(oy%5)*m_brick+yy+0.0f)/(m_brick*5); - p2.texCoord2.x = (static_cast<float>(ox%5)*m_brick+xx+0.0f)/(m_brick*5); - p2.texCoord2.y = (static_cast<float>(oy%5)*m_brick+yy+1.0f)/(m_brick*5); + int xx = mx*(m_brickCount/m_textureSubdivCount) + x; + int yy = my*(m_brickCount/m_textureSubdivCount) + y; + p1.texCoord2.x = (static_cast<float>(ox%5)*m_brickCount+xx+0.0f)/(m_brickCount*5); + p1.texCoord2.y = (static_cast<float>(oy%5)*m_brickCount+yy+0.0f)/(m_brickCount*5); + p2.texCoord2.x = (static_cast<float>(ox%5)*m_brickCount+xx+0.0f)/(m_brickCount*5); + p2.texCoord2.y = (static_cast<float>(oy%5)*m_brickCount+yy+1.0f)/(m_brickCount*5); // Correction for 1 pixel cover // There is 1 pixel cover around each of the 16 surfaces: @@ -693,40 +633,38 @@ bool Gfx::CTerrain::CreateMosaic(int ox, int oy, int step, int objRank, return true; } -Gfx::TerrainMaterial* Gfx::CTerrain::LevelSearchMat(int id) +Gfx::TerrainMaterial* Gfx::CTerrain::FindMaterial(int id) { - for (int i = 0; i < static_cast<int>( m_levelMats.size() ); i++) + for (int i = 0; i < static_cast<int>( m_materials.size() ); i++) { - if (id == m_levelMats[i].id) - return &m_levelMats[i]; + if (id == m_materials[i].id) + return &m_materials[i]; } return nullptr; } -void Gfx::CTerrain::LevelTextureName(int x, int y, std::string& name, Math::Point &uv) +void Gfx::CTerrain::GetTexture(int x, int y, std::string& name, Math::Point &uv) { - x /= m_brick/m_subdivMapping; - y /= m_brick/m_subdivMapping; + x /= m_brickCount/m_textureSubdivCount; + y /= m_brickCount/m_textureSubdivCount; - TerrainMaterial* tm = LevelSearchMat(m_levelDots[x+y*m_levelDotSize].id); + TerrainMaterial* tm = FindMaterial(m_materialPoints[x+y*m_materialPointCount].id); if (tm == nullptr) { name = "xxx.png"; - uv.x = 0.0f; - uv.y = 0.0f; + uv = Math::Point(0.0f, 0.0f); } else { name = tm->texName; - uv.x = tm->u; - uv.y = tm->v; + uv = tm->uv; } } -float Gfx::CTerrain::LevelGetHeight(int x, int y) +float Gfx::CTerrain::GetHeight(int x, int y) { - int size = (m_mosaic*m_brick+1); + int size = (m_mosaicCount*m_brickCount+1); if (x < 0 ) x = 0; if (x >= size) x = size-1; @@ -736,15 +674,15 @@ float Gfx::CTerrain::LevelGetHeight(int x, int y) return m_relief[x+y*size]; } -bool Gfx::CTerrain::LevelGetDot(int x, int y, float min, float max, float slope) +bool Gfx::CTerrain::CheckMaterialPoint(int x, int y, float min, float max, float slope) { - float hc = LevelGetHeight(x, y); + float hc = GetHeight(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) + GetHeight(x+0, y+1), + GetHeight(x+1, y+0), + GetHeight(x+0, y-1), + GetHeight(x-1, y+0) }; if (hc < min || hc > max) @@ -776,25 +714,22 @@ bool Gfx::CTerrain::LevelGetDot(int x, int y, float min, float max, float slope) return false; } - -/** Returns the index within m_levelMats or -1 if there is not. - m_levelMats[i].id gives the identifier. */ -int Gfx::CTerrain::LevelTestMat(char *mat) +int Gfx::CTerrain::FindMaterialByNeighbors(char *mat) { - for (int i = 0; i < static_cast<int>( m_levelMats.size() ); i++) + for (int i = 0; i < static_cast<int>( m_materials.size() ); i++) { - if ( m_levelMats[i].mat[0] == mat[0] && - m_levelMats[i].mat[1] == mat[1] && - m_levelMats[i].mat[2] == mat[2] && - m_levelMats[i].mat[3] == mat[3] ) return i; + if ( m_materials[i].mat[0] == mat[0] && + m_materials[i].mat[1] == mat[1] && + m_materials[i].mat[2] == mat[2] && + m_materials[i].mat[3] == mat[3] ) return i; } return -1; } -void Gfx::CTerrain::LevelSetDot(int x, int y, int id, char *mat) +void Gfx::CTerrain::SetMaterialPoint(int x, int y, int id, char *mat) { - TerrainMaterial* tm = LevelSearchMat(id); + TerrainMaterial* tm = FindMaterial(id); if (tm == nullptr) return; if ( tm->mat[0] != mat[0] || @@ -802,310 +737,310 @@ void Gfx::CTerrain::LevelSetDot(int x, int y, int id, char *mat) tm->mat[2] != mat[2] || tm->mat[3] != mat[3] ) // id incompatible with mat? { - int ii = LevelTestMat(mat); + int ii = FindMaterialByNeighbors(mat); if (ii == -1) return; - id = m_levelMats[ii].id; // looking for a id compatible with mat + id = m_materials[ii].id; // looking for a id compatible with mat } // Changes the point - m_levelDots[x+y*m_levelDotSize].id = id; - m_levelDots[x+y*m_levelDotSize].mat[0] = mat[0]; - m_levelDots[x+y*m_levelDotSize].mat[1] = mat[1]; - m_levelDots[x+y*m_levelDotSize].mat[2] = mat[2]; - m_levelDots[x+y*m_levelDotSize].mat[3] = mat[3]; + m_materialPoints[x+y*m_materialPointCount].id = id; + m_materialPoints[x+y*m_materialPointCount].mat[0] = mat[0]; + m_materialPoints[x+y*m_materialPointCount].mat[1] = mat[1]; + m_materialPoints[x+y*m_materialPointCount].mat[2] = mat[2]; + m_materialPoints[x+y*m_materialPointCount].mat[3] = mat[3]; // Changes the lower neighbor - if ( (x+0) >= 0 && (x+0) < m_levelDotSize && - (y-1) >= 0 && (y-1) < m_levelDotSize ) + if ( (x+0) >= 0 && (x+0) < m_materialPointCount && + (y-1) >= 0 && (y-1) < m_materialPointCount ) { - int i = (x+0)+(y-1)*m_levelDotSize; - if (m_levelDots[i].mat[0] != mat[2]) + int i = (x+0)+(y-1)*m_materialPointCount; + if (m_materialPoints[i].mat[0] != mat[2]) { - m_levelDots[i].mat[0] = mat[2]; - int ii = LevelTestMat(m_levelDots[i].mat); + m_materialPoints[i].mat[0] = mat[2]; + int ii = FindMaterialByNeighbors(m_materialPoints[i].mat); if (ii != -1) - m_levelDots[i].id = m_levelMats[ii].id; + m_materialPoints[i].id = m_materials[ii].id; } } // Modifies the left neighbor - if ( (x-1) >= 0 && (x-1) < m_levelDotSize && - (y+0) >= 0 && (y+0) < m_levelDotSize ) + if ( (x-1) >= 0 && (x-1) < m_materialPointCount && + (y+0) >= 0 && (y+0) < m_materialPointCount ) { - int i = (x-1)+(y+0)*m_levelDotSize; - if (m_levelDots[i].mat[1] != mat[3]) + int i = (x-1)+(y+0)*m_materialPointCount; + if (m_materialPoints[i].mat[1] != mat[3]) { - m_levelDots[i].mat[1] = mat[3]; - int ii = LevelTestMat(m_levelDots[i].mat); + m_materialPoints[i].mat[1] = mat[3]; + int ii = FindMaterialByNeighbors(m_materialPoints[i].mat); if (ii != -1) - m_levelDots[i].id = m_levelMats[ii].id; + m_materialPoints[i].id = m_materials[ii].id; } } // Changes the upper neighbor - if ( (x+0) >= 0 && (x+0) < m_levelDotSize && - (y+1) >= 0 && (y+1) < m_levelDotSize ) + if ( (x+0) >= 0 && (x+0) < m_materialPointCount && + (y+1) >= 0 && (y+1) < m_materialPointCount ) { - int i = (x+0)+(y+1)*m_levelDotSize; - if (m_levelDots[i].mat[2] != mat[0]) + int i = (x+0)+(y+1)*m_materialPointCount; + if (m_materialPoints[i].mat[2] != mat[0]) { - m_levelDots[i].mat[2] = mat[0]; - int ii = LevelTestMat(m_levelDots[i].mat); + m_materialPoints[i].mat[2] = mat[0]; + int ii = FindMaterialByNeighbors(m_materialPoints[i].mat); if (ii != -1) - m_levelDots[i].id = m_levelMats[ii].id; + m_materialPoints[i].id = m_materials[ii].id; } } // Changes the right neighbor - if ( (x+1) >= 0 && (x+1) < m_levelDotSize && - (y+0) >= 0 && (y+0) < m_levelDotSize ) + if ( (x+1) >= 0 && (x+1) < m_materialPointCount && + (y+0) >= 0 && (y+0) < m_materialPointCount ) { - int i = (x+1)+(y+0)*m_levelDotSize; - if ( m_levelDots[i].mat[3] != mat[1] ) + int i = (x+1)+(y+0)*m_materialPointCount; + if ( m_materialPoints[i].mat[3] != mat[1] ) { - m_levelDots[i].mat[3] = mat[1]; - int ii = LevelTestMat(m_levelDots[i].mat); + m_materialPoints[i].mat[3] = mat[1]; + int ii = FindMaterialByNeighbors(m_materialPoints[i].mat); if (ii != -1) - m_levelDots[i].id = m_levelMats[ii].id; + m_materialPoints[i].id = m_materials[ii].id; } } } -bool Gfx::CTerrain::LevelIfDot(int x, int y, int id, char *mat) +bool Gfx::CTerrain::CondChangeMaterialPoint(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 ) + if ( x+0 >= 0 && x+0 < m_materialPointCount && + y-1 >= 0 && y-1 < m_materialPointCount ) { test[0] = mat[2]; - test[1] = m_levelDots[(x+0)+(y-1)*m_levelDotSize].mat[1]; - test[2] = m_levelDots[(x+0)+(y-1)*m_levelDotSize].mat[2]; - test[3] = m_levelDots[(x+0)+(y-1)*m_levelDotSize].mat[3]; + test[1] = m_materialPoints[(x+0)+(y-1)*m_materialPointCount].mat[1]; + test[2] = m_materialPoints[(x+0)+(y-1)*m_materialPointCount].mat[2]; + test[3] = m_materialPoints[(x+0)+(y-1)*m_materialPointCount].mat[3]; - if ( LevelTestMat(test) == -1 ) return false; + if ( FindMaterialByNeighbors(test) == -1 ) return false; } // Compatible with left neighbor? - if ( x-1 >= 0 && x-1 < m_levelDotSize && - y+0 >= 0 && y+0 < m_levelDotSize ) + if ( x-1 >= 0 && x-1 < m_materialPointCount && + y+0 >= 0 && y+0 < m_materialPointCount ) { - test[0] = m_levelDots[(x-1)+(y+0)*m_levelDotSize].mat[0]; + test[0] = m_materialPoints[(x-1)+(y+0)*m_materialPointCount].mat[0]; test[1] = mat[3]; - test[2] = m_levelDots[(x-1)+(y+0)*m_levelDotSize].mat[2]; - test[3] = m_levelDots[(x-1)+(y+0)*m_levelDotSize].mat[3]; + test[2] = m_materialPoints[(x-1)+(y+0)*m_materialPointCount].mat[2]; + test[3] = m_materialPoints[(x-1)+(y+0)*m_materialPointCount].mat[3]; - if ( LevelTestMat(test) == -1 ) return false; + if ( FindMaterialByNeighbors(test) == -1 ) return false; } // Compatible with upper neighbor? - if ( x+0 >= 0 && x+0 < m_levelDotSize && - y+1 >= 0 && y+1 < m_levelDotSize ) + if ( x+0 >= 0 && x+0 < m_materialPointCount && + y+1 >= 0 && y+1 < m_materialPointCount ) { - test[0] = m_levelDots[(x+0)+(y+1)*m_levelDotSize].mat[0]; - test[1] = m_levelDots[(x+0)+(y+1)*m_levelDotSize].mat[1]; + test[0] = m_materialPoints[(x+0)+(y+1)*m_materialPointCount].mat[0]; + test[1] = m_materialPoints[(x+0)+(y+1)*m_materialPointCount].mat[1]; test[2] = mat[0]; - test[3] = m_levelDots[(x+0)+(y+1)*m_levelDotSize].mat[3]; + test[3] = m_materialPoints[(x+0)+(y+1)*m_materialPointCount].mat[3]; - if ( LevelTestMat(test) == -1 ) return false; + if ( FindMaterialByNeighbors(test) == -1 ) return false; } // Compatible with right neighbor? - if ( x+1 >= 0 && x+1 < m_levelDotSize && - y+0 >= 0 && y+0 < m_levelDotSize ) + if ( x+1 >= 0 && x+1 < m_materialPointCount && + y+0 >= 0 && y+0 < m_materialPointCount ) { - test[0] = m_levelDots[(x+1)+(y+0)*m_levelDotSize].mat[0]; - test[1] = m_levelDots[(x+1)+(y+0)*m_levelDotSize].mat[1]; - test[2] = m_levelDots[(x+1)+(y+0)*m_levelDotSize].mat[2]; + test[0] = m_materialPoints[(x+1)+(y+0)*m_materialPointCount].mat[0]; + test[1] = m_materialPoints[(x+1)+(y+0)*m_materialPointCount].mat[1]; + test[2] = m_materialPoints[(x+1)+(y+0)*m_materialPointCount].mat[2]; test[3] = mat[1]; - if ( LevelTestMat(test) == -1 ) return false; + if ( FindMaterialByNeighbors(test) == -1 ) return false; } - LevelSetDot(x, y, id, mat); // puts the point + SetMaterialPoint(x, y, id, mat); // puts the point return true; } -bool Gfx::CTerrain::LevelPutDot(int x, int y, int id) +bool Gfx::CTerrain::ChangeMaterialPoint(int x, int y, int id) { char mat[4]; - x /= m_brick/m_subdivMapping; - y /= m_brick/m_subdivMapping; + x /= m_brickCount/m_textureSubdivCount; + y /= m_brickCount/m_textureSubdivCount; - if ( x < 0 || x >= m_levelDotSize || - y < 0 || y >= m_levelDotSize ) return false; + if ( x < 0 || x >= m_materialPointCount || + y < 0 || y >= m_materialPointCount ) return false; - TerrainMaterial* tm = LevelSearchMat(id); + TerrainMaterial* tm = FindMaterial(id); if (tm == nullptr) return false; // Tries without changing neighbors. - if ( LevelIfDot(x, y, id, tm->mat) ) return true; + if ( CondChangeMaterialPoint(x, y, id, tm->mat) ) return true; // Tries changing a single neighbor (4x). - for (int up = 0; up < m_levelMatMax; up++) + for (int up = 0; up < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } - for (int right = 0; right < m_levelMatMax; right++) + for (int right = 0; right < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } - for (int down = 0; down < m_levelMatMax; down++) + for (int down = 0; down < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } - for (int left = 0; left < m_levelMatMax; left++) + for (int left = 0; left < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } // Tries changing two neighbors (6x). - for (int up = 0; up < m_levelMatMax; up++) + for (int up = 0; up < m_maxMaterialID; up++) { - for (int down = 0; down < m_levelMatMax; down++) + for (int down = 0; down < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } } - for (int right = 0; right < m_levelMatMax; right++) + for (int right = 0; right < m_maxMaterialID; right++) { - for (int left = 0; left < m_levelMatMax; left++) + for (int left = 0; left < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } } - for (int up = 0; up < m_levelMatMax; up++) + for (int up = 0; up < m_maxMaterialID; up++) { - for (int right = 0; right < m_levelMatMax; right++) + for (int right = 0; right < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } } - for (int right = 0; right < m_levelMatMax; right++) + for (int right = 0; right < m_maxMaterialID; right++) { - for (int down = 0; down < m_levelMatMax; down++) + for (int down = 0; down < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } } - for (int down = 0; down < m_levelMatMax; down++) + for (int down = 0; down < m_maxMaterialID; down++) { - for (int left = 0; left < m_levelMatMax; left++) + for (int left = 0; left < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } } - for (int up = 0; up < m_levelMatMax; up++) + for (int up = 0; up < m_maxMaterialID; up++) { - for (int left = 0; left < m_levelMatMax; left++) + for (int left = 0; left < m_maxMaterialID; 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; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } } // Tries changing all the neighbors. - for (int up = 0; up < m_levelMatMax; up++) + for (int up = 0; up < m_maxMaterialID; up++) { - for (int right = 0; right < m_levelMatMax; right++) + for (int right = 0; right < m_maxMaterialID; right++) { - for (int down = 0; down < m_levelMatMax; down++) + for (int down = 0; down < m_maxMaterialID; down++) { - for (int left = 0; left < m_levelMatMax; left++) + for (int left = 0; left < m_maxMaterialID; left++) { mat[0] = up; mat[1] = right; mat[2] = down; mat[3] = left; - if (LevelIfDot(x, y, id, mat)) return true; + if (CondChangeMaterialPoint(x, y, id, mat)) return true; } } } } - GetLogger()->Error("LevelPutDot error\n"); + GetLogger()->Error("AddMaterialPoint error\n"); return false; } -bool Gfx::CTerrain::LevelInit(int id) +bool Gfx::CTerrain::InitMaterials(int id) { - TerrainMaterial* tm = LevelSearchMat(id); + TerrainMaterial* tm = FindMaterial(id); if (tm == nullptr) return false; - for (int i = 0; i < m_levelDotSize*m_levelDotSize; i++) + for (int i = 0; i < m_materialPointCount*m_materialPointCount; i++) { - m_levelDots[i].id = id; + m_materialPoints[i].id = id; for (int j = 0; j < 4; j++) - m_levelDots[i].mat[j] = tm->mat[j]; + m_materialPoints[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) +bool Gfx::CTerrain::GenerateMaterials(int *id, float min, float max, + float slope, float freq, + Math::Vector center, float radius) { static char random[100] = { @@ -1124,40 +1059,40 @@ bool Gfx::CTerrain::LevelGenerate(int *id, float min, float max, TerrainMaterial* tm = nullptr; int i = 0; - while ( id[i] != 0 ) + while (id[i] != 0) { - tm = LevelSearchMat(id[i++]); - if (tm == nullptr) return false; + tm = FindMaterial(id[i++]); + if (tm == nullptr) return false; } int numID = i; - int group = m_brick / m_subdivMapping; + int group = m_brickCount / m_textureSubdivCount; if (radius > 0.0f && radius < 5.0f) // just a square? { - float dim = (m_mosaic*m_brick*m_size)/2.0f; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; - int xx = static_cast<int>((center.x+dim)/m_size); - int yy = static_cast<int>((center.z+dim)/m_size); + int xx = static_cast<int>((center.x+dim)/m_brickSize); + int yy = static_cast<int>((center.z+dim)/m_brickSize); int x = xx/group; int y = yy/group; - tm = LevelSearchMat(id[0]); + tm = FindMaterial(id[0]); if (tm != nullptr) - LevelSetDot(x, y, id[0], tm->mat); // puts the point + SetMaterialPoint(x, y, id[0], tm->mat); // puts the point } else { - for (int y = 0; y < m_levelDotSize; y++) + for (int y = 0; y < m_materialPointCount; y++) { - for (int x = 0; x < m_levelDotSize; x++) + for (int x = 0; x < m_materialPointCount; x++) { if (radius != 0.0f) { Math::Vector pos; - pos.x = (static_cast<float>(x)-m_levelDotSize/2.0f)*group*m_size; - pos.z = (static_cast<float>(y)-m_levelDotSize/2.0f)*group*m_size; + pos.x = (static_cast<float>(x)-m_materialPointCount/2.0f)*group*m_brickSize; + pos.z = (static_cast<float>(y)-m_materialPointCount/2.0f)*group*m_brickSize; if (Math::DistanceProjected(pos, center) > radius) continue; } @@ -1170,11 +1105,11 @@ bool Gfx::CTerrain::LevelGenerate(int *id, float min, float max, int xx = x*group + group/2; int yy = y*group + group/2; - if (LevelGetDot(xx, yy, min, max, slope)) + if (CheckMaterialPoint(xx, yy, min, max, slope)) { int rnd = random[(x%10)+(y%10)*10]; int ii = rnd % numID; - LevelPutDot(xx, yy, id[ii]); + ChangeMaterialPoint(xx, yy, id[ii]); } } } @@ -1183,66 +1118,59 @@ bool Gfx::CTerrain::LevelGenerate(int *id, float min, float max, return true; } -void Gfx::CTerrain::LevelOpenTable() +void Gfx::CTerrain::InitMaterialPoints() { - if (! m_levelText) return; - if (! m_levelDots.empty()) return; // already allocated + if (! m_useMaterials) return; + if (! m_materialPoints.empty()) return; // already allocated - m_levelDotSize = (m_mosaic*m_brick)/(m_brick/m_subdivMapping)+1; - std::vector<Gfx::DotLevel>(m_levelDotSize*m_levelDotSize).swap(m_levelDots); + m_materialPointCount = (m_mosaicCount*m_brickCount)/(m_brickCount/m_textureSubdivCount)+1; + std::vector<Gfx::TerrainMaterialPoint>(m_materialPointCount*m_materialPointCount).swap(m_materialPoints); - for (int i = 0; i < m_levelDotSize * m_levelDotSize; i++) + for (int i = 0; i < m_materialPointCount * m_materialPointCount; i++) { for (int j = 0; j < 4; j++) - m_levelDots[i].mat[j] = 0; + m_materialPoints[i].mat[j] = 0; } } -void Gfx::CTerrain::LevelCloseTable() +void Gfx::CTerrain::FlushMaterialPoints() { - m_levelDots.clear(); + m_materialPoints.clear(); } -bool Gfx::CTerrain::CreateSquare(bool multiRes, int x, int y) +bool Gfx::CTerrain::CreateSquare(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_engine->SetObjectType(objRank, Gfx::ENG_OBJTYPE_TERRAIN); - m_objRank[x+y*m_mosaic] = objRank; + m_objRanks[x+y*m_mosaicCount] = objRank; - if (multiRes) + float min = 0.0f; + float max = m_vision; + max *= m_engine->GetClippingDistance(); + for (int step = 0; step < m_depth; step++) { - 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); + CreateMosaic(x, y, 1 << step, objRank, mat, min, max); + min = max; + max *= 2; + if (step == m_depth-1) max = Math::HUGE_NUM; } return true; } -bool Gfx::CTerrain::CreateObjects(bool multiRes) +bool Gfx::CTerrain::CreateObjects() { AdjustRelief(); - for (int y = 0; y < m_mosaic; y++) + for (int y = 0; y < m_mosaicCount; y++) { - for (int x = 0; x < m_mosaic; x++) - CreateSquare(multiRes, x, y); + for (int x = 0; x < m_mosaicCount; x++) + CreateSquare(x, y); } return true; @@ -1251,29 +1179,29 @@ bool Gfx::CTerrain::CreateObjects(bool multiRes) /** 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; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; Math::IntPoint tp1, tp2; - tp1.x = static_cast<int>((p1.x+dim+m_size/2.0f)/m_size); - tp1.y = static_cast<int>((p1.z+dim+m_size/2.0f)/m_size); - tp2.x = static_cast<int>((p2.x+dim+m_size/2.0f)/m_size); - tp2.y = static_cast<int>((p2.z+dim+m_size/2.0f)/m_size); + tp1.x = static_cast<int>((p1.x+dim+m_brickSize/2.0f)/m_brickSize); + tp1.y = static_cast<int>((p1.z+dim+m_brickSize/2.0f)/m_brickSize); + tp2.x = static_cast<int>((p2.x+dim+m_brickSize/2.0f)/m_brickSize); + tp2.y = static_cast<int>((p2.z+dim+m_brickSize/2.0f)/m_brickSize); if (tp1.x > tp2.x) { - int x = tp1.x; + int x = tp1.x; tp1.x = tp2.x; tp2.x = x; } if (tp1.y > tp2.y) { - int y = tp1.y; + int y = tp1.y; tp1.y = tp2.y; tp2.y = y; } - int size = (m_mosaic*m_brick)+1; + int size = (m_mosaicCount*m_brickCount)+1; // Calculates the current average height float avg = 0.0f; @@ -1295,13 +1223,13 @@ bool Gfx::CTerrain::Terraform(const Math::Vector &p1, const Math::Vector &p2, fl { m_relief[x+y*size] = avg+height; - if (x % m_brick == 0 && y % m_depth != 0) + if (x % m_brickCount == 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) + if (y % m_brickCount == 0 && x % m_depth != 0) { m_relief[(x-1)+(y+0)*size] = avg+height; m_relief[(x+1)+(y+0)*size] = avg+height; @@ -1311,22 +1239,22 @@ bool Gfx::CTerrain::Terraform(const Math::Vector &p1, const Math::Vector &p2, fl 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; + pp1.x = (tp1.x-2)/m_brickCount; + pp1.y = (tp1.y-2)/m_brickCount; + pp2.x = (tp2.x+1)/m_brickCount; + pp2.y = (tp2.y+1)/m_brickCount; - 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; + if (pp1.x < 0 ) pp1.x = 0; + if (pp1.x >= m_mosaicCount) pp1.x = m_mosaicCount-1; + if (pp1.y < 0 ) pp1.y = 0; + if (pp1.y >= m_mosaicCount) pp1.y = m_mosaicCount-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->DeleteObject(m_objRanks[x+y*m_mosaicCount]); + CreateSquare(x, y); // recreates the square } } m_engine->Update(); @@ -1355,37 +1283,37 @@ 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; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; - int x = static_cast<int>((pos.x+dim)/m_size); - int y = static_cast<int>((pos.z+dim)/m_size); + int x = static_cast<int>((pos.x+dim)/m_brickSize); + int y = static_cast<int>((pos.z+dim)/m_brickSize); - if ( x < 0 || x >= m_mosaic*m_brick || - y < 0 || y >= m_mosaic*m_brick ) return 0.0f; + if ( x < 0 || x >= m_mosaicCount*m_brickCount || + y < 0 || y >= m_mosaicCount*m_brickCount ) 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)], + m_relief[(x+0)+(y+0)*(m_mosaicCount*m_brickCount+1)], + m_relief[(x+1)+(y+0)*(m_mosaicCount*m_brickCount+1)], + m_relief[(x+0)+(y+1)*(m_mosaicCount*m_brickCount+1)], + m_relief[(x+1)+(y+1)*(m_mosaicCount*m_brickCount+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); + return atanf((max-min)/m_brickSize); } bool Gfx::CTerrain::GetNormal(Math::Vector &n, const Math::Vector &p) { - float dim = (m_mosaic*m_brick*m_size)/2.0f; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; - int x = static_cast<int>((p.x+dim)/m_size); - int y = static_cast<int>((p.z+dim)/m_size); + int x = static_cast<int>((p.x+dim)/m_brickSize); + int y = static_cast<int>((p.z+dim)/m_brickSize); - if ( x < 0 || x > m_mosaic*m_brick || - y < 0 || y > m_mosaic*m_brick ) return false; + if ( x < 0 || x > m_mosaicCount*m_brickCount || + y < 0 || y > m_mosaicCount*m_brickCount ) return false; Math::Vector p1 = GetVector(x+0, y+0); Math::Vector p2 = GetVector(x+1, y+0); @@ -1400,23 +1328,23 @@ bool Gfx::CTerrain::GetNormal(Math::Vector &n, const Math::Vector &p) return true; } -float Gfx::CTerrain::GetFloorLevel(const Math::Vector &p, bool brut, bool water) +float Gfx::CTerrain::GetFloorLevel(const Math::Vector &pos, bool brut, bool water) { - float dim = (m_mosaic*m_brick*m_size)/2.0f; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; - int x = static_cast<int>((p.x+dim)/m_size); - int y = static_cast<int>((p.z+dim)/m_size); + int x = static_cast<int>((pos.x+dim)/m_brickSize); + int y = static_cast<int>((pos.z+dim)/m_brickSize); - if ( x < 0 || x > m_mosaic*m_brick || - y < 0 || y > m_mosaic*m_brick ) return false; + if ( x < 0 || x > m_mosaicCount*m_brickCount || + y < 0 || y > m_mosaicCount*m_brickCount ) 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) ) + Math::Vector ps = pos; + if ( fabs(pos.z-p2.z) < fabs(pos.x-p2.x) ) { if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f; } @@ -1436,25 +1364,23 @@ float Gfx::CTerrain::GetFloorLevel(const Math::Vector &p, bool brut, bool 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 Gfx::CTerrain::GetHeightToFloor(const Math::Vector &pos, bool brut, bool water) { - float dim = (m_mosaic*m_brick*m_size)/2.0f; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; - int x = static_cast<int>((p.x+dim)/m_size); - int y = static_cast<int>((p.z+dim)/m_size); + int x = static_cast<int>((pos.x+dim)/m_brickSize); + int y = static_cast<int>((pos.z+dim)/m_brickSize); - if ( x < 0 || x > m_mosaic*m_brick || - y < 0 || y > m_mosaic*m_brick ) return false; + if ( x < 0 || x > m_mosaicCount*m_brickCount || + y < 0 || y > m_mosaicCount*m_brickCount ) 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) ) + Math::Vector ps = pos; + if ( fabs(pos.z-p2.z) < fabs(pos.x-p2.x) ) { if ( !IntersectY(p1, p2, p3, ps) ) return 0.0f; } @@ -1468,76 +1394,114 @@ float Gfx::CTerrain::GetFloorHeight(const Math::Vector &p, bool brut, bool water if (water) // not going underwater? { float level = m_water->GetLevel(); - if ( ps.y < level ) ps.y = level; // not under water + if (ps.y < level ) ps.y = level; // not under water } - return p.y-ps.y; + return pos.y-ps.y; } -bool Gfx::CTerrain::MoveOnFloor(Math::Vector &p, bool brut, bool water) +bool Gfx::CTerrain::AdjustToFloor(Math::Vector &pos, bool brut, bool water) { - float dim = (m_mosaic*m_brick*m_size)/2.0f; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; - int x = static_cast<int>((p.x + dim) / m_size); - int y = static_cast<int>((p.z + dim) / m_size); + int x = static_cast<int>((pos.x + dim) / m_brickSize); + int y = static_cast<int>((pos.z + dim) / m_brickSize); - if ( x < 0 || x > m_mosaic*m_brick || - y < 0 || y > m_mosaic*m_brick ) return false; + if ( x < 0 || x > m_mosaicCount*m_brickCount || + y < 0 || y > m_mosaicCount*m_brickCount ) 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 (fabs(pos.z - p2.z) < fabs(pos.x - p2.x)) { - if (! Math::IntersectY(p1, p2, p3, p)) return false; + if (! Math::IntersectY(p1, p2, p3, pos)) return false; } else { - if (! Math::IntersectY(p2, p4, p3, p)) return false; + if (! Math::IntersectY(p2, p4, p3, pos)) return false; } - if (! brut) AdjustBuildingLevel(p); + if (! brut) AdjustBuildingLevel(pos); if (water) // not going underwater? { float level = m_water->GetLevel(); - if (p.y < level) p.y = level; // not under water + if (pos.y < level) pos.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) +/** + * @returns \c false if the initial coordinate was outside terrain area; \c true otherwise + */ +bool Gfx::CTerrain::AdjustToStandardBounds(Math::Vector& pos) { bool ok = true; - float limit = m_mosaic*m_brick*m_size/2.0f - marging; + // TODO: _TEEN ... * 0.98f; + float limit = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f*0.92f; - if (p.x < -limit) + if (pos.x < -limit) { - p.x = -limit; + pos.x = -limit; ok = false; } - if (p.z < -limit) + if (pos.z < -limit) { - p.z = -limit; + pos.z = -limit; ok = false; } - if (p.x > limit) + if (pos.x > limit) { - p.x = limit; + pos.x = limit; ok = false; } - if (p.z > limit) + if (pos.z > limit) { - p.z = limit; + pos.z = limit; + ok = false; + } + + return ok; +} + +/** + * @param margin margin to the terrain border + * @returns \c false if the initial coordinate was outside terrain area; \c true otherwise + */ +bool Gfx::CTerrain::AdjustToBounds(Math::Vector& pos, float margin) +{ + bool ok = true; + float limit = m_mosaicCount*m_brickCount*m_brickSize/2.0f - margin; + + if (pos.x < -limit) + { + pos.x = -limit; + ok = false; + } + + if (pos.z < -limit) + { + pos.z = -limit; + ok = false; + } + + if (pos.x > limit) + { + pos.x = limit; + ok = false; + } + + if (pos.z > limit) + { + pos.z = limit; ok = false; } @@ -1664,46 +1628,41 @@ void Gfx::CTerrain::AdjustBuildingLevel(Math::Vector &p) } } - -// 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 (factor != 1.0f) return 1.0f; // on building level - if (m_levelDots.empty()) return m_defHardness; + if (m_materialPoints.empty()) return m_defaultHardness; - float dim = (m_mosaic*m_brick*m_size)/2.0f; + float dim = (m_mosaicCount*m_brickCount*m_brickSize)/2.0f; int x, y; - x = static_cast<int>((p.x+dim)/m_size); - y = static_cast<int>((p.z+dim)/m_size); + x = static_cast<int>((p.x+dim)/m_brickSize); + y = static_cast<int>((p.z+dim)/m_brickSize); - if ( x < 0 || x > m_mosaic*m_brick || - y < 0 || y > m_mosaic*m_brick ) return m_defHardness; + if ( x < 0 || x > m_mosaicCount*m_brickCount || + y < 0 || y > m_mosaicCount*m_brickCount ) return m_defaultHardness; - x /= m_brick/m_subdivMapping; - y /= m_brick/m_subdivMapping; + x /= m_brickCount/m_textureSubdivCount; + y /= m_brickCount/m_textureSubdivCount; - if ( x < 0 || x >= m_levelDotSize || - y < 0 || y >= m_levelDotSize ) return m_defHardness; + if ( x < 0 || x >= m_materialPointCount || + y < 0 || y >= m_materialPointCount ) return m_defaultHardness; - int id = m_levelDots[x+y*m_levelDotSize].id; - TerrainMaterial* tm = LevelSearchMat(id); - if (tm == nullptr) return m_defHardness; + int id = m_materialPoints[x+y*m_materialPointCount].id; + TerrainMaterial* tm = FindMaterial(id); + if (tm == nullptr) return m_defaultHardness; return tm->hardness; } -void Gfx::CTerrain::GroundFlat(Math::Vector pos) +void Gfx::CTerrain::ShowFlatGround(Math::Vector pos) { - static char table[41*41]; - + static char table[41*41] = { 1 }; - float rapport = 3200.0f/1024.0f; + float radius = 3200.0f/1024.0f; for (int y = 0; y <= 40; y++) { @@ -1713,16 +1672,16 @@ void Gfx::CTerrain::GroundFlat(Math::Vector pos) table[i] = 0; Math::Vector p; - p.x = (x-20)*rapport; - p.z = (y-20)*rapport; + p.x = (x-20)*radius; + p.z = (y-20)*radius; p.y = 0.0f; - if (Math::Point(p.x, p.y).Length() > 20.0f*rapport) + if (Math::Point(p.x, p.y).Length() > 20.0f*radius) continue; float angle = GetFineSlope(pos+p); - if (angle < FLATLIMIT) + if (angle < Gfx::TERRAIN_FLATLIMIT) table[i] = 1; else table[i] = 2; @@ -1735,7 +1694,7 @@ void Gfx::CTerrain::GroundFlat(Math::Vector pos) float Gfx::CTerrain::GetFlatZoneRadius(Math::Vector center, float max) { float angle = GetFineSlope(center); - if (angle >= Gfx::FLATLIMIT) + if (angle >= Gfx::TERRAIN_FLATLIMIT) return 0.0f; float ref = GetFloorLevel(center, true); diff --git a/src/graphics/engine/terrain.h b/src/graphics/engine/terrain.h index 24bd1f9..80eed07 100644 --- a/src/graphics/engine/terrain.h +++ b/src/graphics/engine/terrain.h @@ -35,7 +35,7 @@ class CWater; //! Limit of slope considered a flat piece of land -const short FLATLIMIT = (5.0f*Math::PI/180.0f); +const short TERRAIN_FLATLIMIT = (5.0f*Math::PI/180.0f); /** @@ -61,6 +61,10 @@ enum TerrainRes //@} }; +/** + * \struct BuildingLevel + * \brief Flat level for building + */ struct BuildingLevel { Math::Vector center; @@ -81,29 +85,43 @@ struct BuildingLevel } }; +/** + * \struct TerrainMaterial + * \brief Material for ground surface + */ struct TerrainMaterial { + //! Unique ID short id; + //! Texture std::string texName; - float u,v; + //! UV texture coordinates + Math::Point uv; + //! Terrain hardness (defines e.g. sound of walking) float hardness; - char mat[4]; // up, right, down, left + //! IDs of neighbor materials: up, right, down, left + char mat[4]; TerrainMaterial() { id = 0; - u = v = 0.0f; hardness = 0.0f; mat[0] = mat[1] = mat[2] = mat[3] = 0; } }; -struct DotLevel +/** + * \struct TerrainMaterialPoint + * \brief Material used for terrain point + */ +struct TerrainMaterialPoint { + //! ID of material short id; - char mat[4]; // up, right, down, left + //! IDs of neighbor materials: up, right, down, left + char mat[4]; - DotLevel() + TerrainMaterialPoint() { id = 0; mat[0] = mat[1] = mat[2] = mat[3] = 0; @@ -132,17 +150,73 @@ struct FlyingLimit * \class CTerrain * \brief Terrain loader/generator and manager * + * \section Mapping Terrain mapping + * * Terrain is created from relief textures specifying a XY plane with height * values which are then scaled and translated into XZ surface forming the * terrain of game level. * - * The class also facilitates creating and searching for flat space expanses - * for construction of buildings. + * The basic unit of terrain is called "brick", which is two triangles + * forming a quad. Bricks have constant size (brick size) + * in X and Z direction. + * Points forming the bricks correspond one-to-one to relief data points + * (pixels in relief image). + * + * Bricks are grouped into "mosaics". Mosaic is a square containing + * brickCount x brickCount bricks where brickCount is an even power of 2. + * Each mosaic corresponds to one created engine object. + * + * The whole terrain is also a square formed by mosaicCount * mosaicCount + * of mosaics. + * + * Image coordinates are converted in the following way to world coordinates + * of brick points (Wx, Wy, Wz - world coordinates, Ix, Iy - image coordinates, + * Pxy - pixel value at Ix,Iy): + * + * Wx = (Ix - brickCount*mosaicCount / 2.0f) * brickSize \n + * Wz = (Iy - brickCount*mosaicCount / 2.0f) * brickSize \n + * Wy = (Pxy / 255.0f) * reliefScale + * + * To create and initialize a terrain, you must call Generate() as the first function, + * setting the number of bricks, mosaics etc. + * + * \section Materials Materials and textures + * + * The terrain can be textured in two ways: + * - by applying texture index table + * - by specifying one or more "materials" that cover "material points" + * + * Textures are applied to subdivisions of mosaics (groups of bricks of size + * brickCount / textureSubdivCount). + * + * \subsection TextureIndexes Texture indexes + * + * Texture indexes specify the texture for each textured point by concatenating + * base name of texture, the index number and texture extension. + * + * Texture indexes are specified directly in InitTextures(). + * + * \subsection TerrainMaterials Terrain materials + * + * Terrain materials are more sophisticated system. Each material is a texture, + * applied to one area, but specifying also the textures to use on surrounding areas: + * left, right, bottom and top. * - * The terrain also specifies underground resources loaded from texture - * and flying limits for the player. + * You specify one or more terrain materials in AddMaterial() function. + * The function will add a material for given circle on the ground, with some + * randomized matching of supplied materials and sophisticated logic for ensuring + * that borders between neighboring materials follow the specified rules. * - * ... + * \subsection BuildingLevels Other features + * + * Terrain can have specified building levels - flat space expanses, + * where relief data is specifically adjusted to level space to allow + * construction of buildings. + * + * Undergound resources can be supplied by loading them from image like relief data. + * + * Terrain also specifies flying limits for player: one global level and possible + * additional spherical restrictions. */ class CTerrain { @@ -151,25 +225,32 @@ public: ~CTerrain(); //! Generates a new flat terrain - bool Generate(int mosaic, int brickPow2, float size, float vision, int depth, float hardness); + bool Generate(int mosaicCount, int brickCountPow2, float brickSize, 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(); - //! 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); + + //! Clears all terrain materials + void FlushMaterials(); + //! Adds a terrain material the names of textures to use for the land + void AddMaterial(int id, const std::string& baseName, const Math::Point& uv, + int up, int right, int down, int left, float hardness); + //! Initializes all the ground with one material + bool InitMaterials(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 + bool GenerateMaterials(int *id, float min, float max, float slope, float freq, Math::Vector center, float radius); + + //! Clears the relief data to zero void FlushRelief(); - //! 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); + //! Load relief from image + bool LoadRelief(const std::string& fileName, float scaleRelief, bool adjustBorder); + + //! Load resources from image + bool LoadResources(const std::string& fileName); + //! Creates all objects of the terrain within the 3D engine - bool CreateObjects(bool multiRes); + bool CreateObjects(); + //! Modifies the terrain's relief bool Terraform(const Math::Vector& p1, const Math::Vector& p2, float height); @@ -179,24 +260,24 @@ public: Math::Vector GetWind(); //@} - //! Gives the exact slope of the terrain of a place given + //! Gives the exact slope of the terrain at 2D (XZ) position float GetFineSlope(const Math::Vector& pos); - //! Gives the approximate slope of the terrain of a specific location + //! Gives the approximate slope of the terrain at 2D (XZ) position float GetCoarseSlope(const Math::Vector& pos); - //! Gives the normal vector at the position p (x,-,z) of the ground + //! Gives the normal vector at 2D (XZ) position 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); + //! Returns the height of the ground level at 2D (XZ) position + float GetFloorLevel(const Math::Vector& pos, bool brut=false, bool water=false); + //! Returns the distance to the ground level from 3D position + float GetHeightToFloor(const Math::Vector& pos, bool brut=false, bool water=false); + //! Modifies the Y coordinate of 3D position to rest on the ground floor + bool AdjustToFloor(Math::Vector& pos, bool brut=false, bool water=false); + //! Adjusts 3D position so that it is within standard terrain boundaries + bool AdjustToStandardBounds(Math::Vector &pos); + //! Adjusts 3D position so that it is within terrain boundaries and the given margin + bool AdjustToBounds(Math::Vector& pos, float margin); + //! Returns the resource type available underground at 2D (XZ) position + Gfx::TerrainRes GetResource(const Math::Vector& pos); //! Empty the table of elevations void FlushBuildingLevel(); @@ -207,16 +288,21 @@ public: //! Removes the elevation for a building when it was destroyed bool DeleteBuildingLevel(Math::Vector center); //! 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 GetMosaic(); - int GetBrick(); - float GetSize(); - float GetScaleRelief(); + float GetBuildingFactor(const Math::Vector& pos); + //! Returns the hardness of the ground in a given place + float GetHardness(const Math::Vector& pos); + + //! Returns number of mosaics + int GetMosaicCount(); + //! Returns number of bricks in mosaic + int GetBrickCount(); + //! Returns brick size + float GetBrickSize(); + //! Returns the vertical scale of relief + float GetReliefScale(); //! Shows the flat areas on the ground - void GroundFlat(Math::Vector pos); + void ShowFlatGround(Math::Vector pos); //! Calculates the radius of the largest flat area available float GetFlatZoneRadius(Math::Vector center, float max); @@ -234,7 +320,7 @@ public: protected: //! Adds a point of elevation in the buffer of relief - bool ReliefAddDot(Math::Vector pos, float scaleRelief); + bool AddReliefPoint(Math::Vector pos, float scaleRelief); //! Adjust the edges of each mosaic to be compatible with all lower resolutions void AdjustRelief(); //! Calculates a vector of the terrain @@ -244,28 +330,28 @@ protected: //! 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); + bool CreateSquare(int x, int y); + + //! Seeks a material based on its ID + Gfx::TerrainMaterial* FindMaterial(int id); + //! Seeks a material based on neighbor values + int FindMaterialByNeighbors(char *mat); + //! Returns the texture name and UV coords to use for a given square + void GetTexture(int x, int y, std::string& name, Math::Point& uv); //! Returns the height of the terrain - float LevelGetHeight(int x, int y); + float GetHeight(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); + bool CheckMaterialPoint(int x, int y, float min, float max, float slope); //! 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); + void SetMaterialPoint(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(); + bool ChangeMaterialPoint(int x, int y, int id); + //! Tests if a material can give a place, according to its four neighbors. If yes, puts the point. + bool CondChangeMaterialPoint(int x, int y, int id, char *mat); + //! Initializes material points array + void InitMaterialPoints(); + //! Clears the material points + void FlushMaterialPoints(); //! Adjusts a position according to a possible rise void AdjustBuildingLevel(Math::Vector &p); @@ -275,39 +361,51 @@ protected: CEngine* m_engine; CWater* m_water; - //! 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 + //! Relief data points std::vector<float> m_relief; - //! Table of textures - std::vector<int> m_texture; - //! Table of rows of objects - std::vector<int> m_objRank; - //! Table of resources + //! Resources data std::vector<unsigned char> m_resources; - bool m_multiText; - bool m_levelText; - //! Scale of the mapping - float m_scaleMapping; + //! Texture indices + std::vector<int> m_textures; + //! Object ranks for mosaic objects + std::vector<int> m_objRanks; + + //! Number of mosaics (along one dimension) + int m_mosaicCount; + //! Number of bricks per mosaic (along one dimension) + int m_brickCount; + //! Number of terrain material dots (along one dimension) + int m_materialPointCount; + //! Size of single brick (along X and Z axis) + float m_brickSize; + //! Vertical (relief) scale float m_scaleRelief; - int m_subdivMapping; + //! Subdivision of material points in mosaic + int m_textureSubdivCount; //! Number of different resolutions (1,2,3,4) int m_depth; + //! Scale of texture mapping + float m_textureScale; + //! Vision before a change of resolution + float m_vision; + + //! Base name for single texture std::string m_texBaseName; + //! Extension for single texture std::string m_texBaseExt; - float m_defHardness; - - std::vector<TerrainMaterial> m_levelMats; - std::vector<Gfx::DotLevel> m_levelDots; - int m_levelMatMax; - int m_levelID; + //! Default hardness for level material + float m_defaultHardness; + + //! True if using terrain material mapping + bool m_useMaterials; + //! Terrain materials + std::vector<Gfx::TerrainMaterial> m_materials; + //! Material for terrain points + std::vector<Gfx::TerrainMaterialPoint> m_materialPoints; + //! Maximum level ID (no ID is >= to this) + int m_maxMaterialID; + //! Internal counter for auto generation of material IDs + int m_materialAutoID; std::vector<Gfx::BuildingLevel> m_buildingLevels; diff --git a/src/graphics/engine/test/CMakeLists.txt b/src/graphics/engine/test/CMakeLists.txt index bd83773..4e3263b 100644 --- a/src/graphics/engine/test/CMakeLists.txt +++ b/src/graphics/engine/test/CMakeLists.txt @@ -1,7 +1,26 @@ cmake_minimum_required(VERSION 2.8) set(CMAKE_BUILD_TYPE debug) -set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0") +set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wold-style-cast -std=gnu++0x") -include_directories(. ../../..) -add_executable(modelfile_test modelfile_test.cpp ../modelfile.cpp ../../../common/logger.cpp ../../../common/stringutils.cpp ../../../common/iman.cpp) +set(MODELFILE_TEST_SOURCES +modelfile_test.cpp +../modelfile.cpp +../../../common/logger.cpp +../../../common/stringutils.cpp +../../../common/iman.cpp +) + +add_definitions(-DMODELFILE_NO_ENGINE) + +include_directories( +. +../../.. +${GTEST_DIR}/include +) + +add_executable(modelfile_test ${MODELFILE_TEST_SOURCES}) + +target_link_libraries(modelfile_test gtest) + +add_test(modelfile_test modelfile_test) diff --git a/src/graphics/engine/test/modelfile_test.cpp b/src/graphics/engine/test/modelfile_test.cpp index f7ed87f..6879a1b 100644 --- a/src/graphics/engine/test/modelfile_test.cpp +++ b/src/graphics/engine/test/modelfile_test.cpp @@ -1,48 +1,273 @@ -#include "graphics/engine/modelfile.h" +// * This file is part of the COLOBOT source code +// * Copyright (C) 2012, Polish Portal of Colobot (PPC) +// * +// * 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/. + + #include "common/iman.h" +#include "common/logger.h" +#include "graphics/engine/modelfile.h" +#include "math/func.h" + +#include "gtest/gtest.h" + +#include <cassert> +#include <sstream> + +/* Test model file (new text format) */ +const char* const TEXT_MODEL = +"# Colobot text model\n" +"\n" +"### HEAD\n" +"version 1\n" +"total_triangles 2\n" +"\n" +"### TRIANGLES\n" +"p1 c -12.4099 10.0016 -2.54558 n 1 -0 1.87319e-07 t1 0.970703 0.751953 t2 0 0\n" +"p2 c -12.4099 10.0016 2.54558 n 1 -0 1.87319e-07 t1 0.998047 0.751953 t2 0 0\n" +"p3 c -12.4099 4.00165 -2.54558 n 1 -0 1.87319e-07 t1 0.970703 0.998047 t2 0 0\n" +"mat dif 1 1 1 0 amb 0.5 0.5 0.5 0 spc 0 0 0 0\n" +"tex1 lemt.png\n" +"tex2\n" +"var_tex2 N\n" +"min 200\n" +"max 1e+06\n" +"state 1024\n" +"\n" +"p1 c -19 -1 4 n -1 0 0 t1 0.248047 0.123047 t2 0.905224 0.52067\n" +"p2 c -19 4 4 n -1 0 0 t1 0.248047 0.00195312 t2 0.905224 0.614223\n" +"p3 c -19 4 -4 n -1 0 0 t1 0.00195312 0.00195312 t2 0.0947756 0.614223\n" +"mat dif 1 1 1 0 amb 0.5 0.5 0.5 0 spc 0 0 0 0\n" +"tex1 derrick.png\n" +"tex2\n" +"var_tex2 Y\n" +"min 200\n" +"max 1e+06\n" +"state 0\n" +""; + +// Triangles as defined in model file +Gfx::ModelTriangle TRIANGLE_1; +Gfx::ModelTriangle TRIANGLE_2; + +// Sets triangle data +void Init() +{ + + TRIANGLE_1.p1 = Gfx::VertexTex2(Math::Vector(-12.4099, 10.0016, -2.54558), + Math::Vector(1, 0, 1.87319e-07), + Math::Point(0.970703, 0.751953), + Math::Point(0, 0)); + TRIANGLE_1.p2 = Gfx::VertexTex2(Math::Vector(-12.4099, 10.0016, 2.54558), + Math::Vector(1, 0, 1.87319e-07), + Math::Point(0.998047, 0.751953), + Math::Point(0, 0)); + TRIANGLE_1.p3 = Gfx::VertexTex2(Math::Vector(-12.4099, 4.00165, -2.54558), + Math::Vector(1, 0, 1.87319e-07), + Math::Point(0.970703, 0.998047), + Math::Point(0, 0)); + TRIANGLE_1.material.diffuse = Gfx::Color(1, 1, 1, 0); + TRIANGLE_1.material.ambient = Gfx::Color(0.5, 0.5, 0.5, 0); + TRIANGLE_1.material.specular = Gfx::Color(0, 0, 0, 0); + TRIANGLE_1.tex1Name = "lemt.png"; + TRIANGLE_1.variableTex2 = false; + TRIANGLE_1.min = 200.0f; + TRIANGLE_1.max = 1e+06f; + TRIANGLE_1.state = 1024; + + TRIANGLE_2.p1 = Gfx::VertexTex2(Math::Vector(-19, -1, 4), + Math::Vector(-1, 0, 0), + Math::Point(0.248047, 0.123047), + Math::Point(0.905224, 0.52067)); + TRIANGLE_2.p2 = Gfx::VertexTex2(Math::Vector(-19, 4, 4), + Math::Vector(-1, 0, 0), + Math::Point(0.248047, 0.00195312), + Math::Point(0.905224, 0.614223)); + TRIANGLE_2.p3 = Gfx::VertexTex2(Math::Vector(-19, 4, -4), + Math::Vector(-1, 0, 0), + Math::Point(0.00195312, 0.00195312), + Math::Point(0.0947756, 0.614223)); + TRIANGLE_2.material.diffuse = Gfx::Color(1, 1, 1, 0); + TRIANGLE_2.material.ambient = Gfx::Color(0.5, 0.5, 0.5, 0); + TRIANGLE_2.material.specular = Gfx::Color(0, 0, 0, 0); + TRIANGLE_2.tex1Name = "derrick.png"; + TRIANGLE_2.variableTex2 = true; + TRIANGLE_2.min = 200.0f; + TRIANGLE_2.max = 1e+06f; + TRIANGLE_2.state = 0; +} + + +// Compares vertices (within Math::TOLERANCE) +bool CompareVertices(const Gfx::VertexTex2& v1, const Gfx::VertexTex2& v2) +{ + if ( !( Math::IsEqual(v1.coord.x, v2.coord.x) && + Math::IsEqual(v1.coord.y, v2.coord.y) && + Math::IsEqual(v1.coord.z, v2.coord.z) ) ) + return false; + + if ( !( Math::IsEqual(v1.normal.x, v2.normal.x) && + Math::IsEqual(v1.normal.y, v2.normal.y) && + Math::IsEqual(v1.normal.z, v2.normal.z) ) ) + return false; -#include <iostream> + if ( !( Math::IsEqual(v1.texCoord.x, v2.texCoord.x) && + Math::IsEqual(v1.texCoord.y, v2.texCoord.y) ) ) + return false; + if ( !( Math::IsEqual(v1.texCoord2.x, v2.texCoord2.x) && + Math::IsEqual(v1.texCoord2.y, v2.texCoord2.y) ) ) + return false; -int main(int argc, char *argv[]) + return true; +} + +// Compares colors (within Math::TOLERANCE) +bool CompareColors(const Gfx::Color& c1, const Gfx::Color& c2) +{ + return Math::IsEqual(c1.r, c2.r) && + Math::IsEqual(c1.g, c2.g) && + Math::IsEqual(c1.b, c2.b) && + Math::IsEqual(c1.a, c2.a); +} + +// Compares model triangles (within Math::TOLERANCE) +bool CompareTriangles(const Gfx::ModelTriangle& t1, const Gfx::ModelTriangle& t2) { - if (argc != 4) - { - std::cerr << "Usage: " << argv[0] << " {mod|dxf} in_file out_file" << std::endl; - return 1; - } + if (! CompareVertices(t1.p1, t2.p1)) + return false; + + if (! CompareVertices(t1.p2, t2.p2)) + return false; + + if (! CompareVertices(t1.p3, t2.p3)) + return false; + + if (! CompareColors(t1.material.diffuse, t2.material.diffuse)) + return false; + + if (! CompareColors(t1.material.ambient, t2.material.ambient)) + return false; + + if (! CompareColors(t1.material.specular, t2.material.specular)) + return false; + + if (t1.tex1Name != t2.tex1Name) + return false; + + if (t1.tex2Name != t2.tex2Name) + return false; + + if (t1.variableTex2 != t2.variableTex2) + return false; + + if (!Math::IsEqual(t1.min, t2.min)) + return false; + + if (!Math::IsEqual(t1.max, t2.max)) + return false; + + if (t1.state != t2.state) + return false; + + return true; +} + +// Tests reading/writing new text model file +TEST(ModelFileTest, RWTxtModel) +{ + std::stringstream str; + str.str(TEXT_MODEL); CInstanceManager iMan; - Gfx::CModelFile modfile(&iMan); - - std::string mode(argv[1]); - if (mode == "mod") - { - if (! modfile.ReadModel(argv[2], false, false) ) - { - std::cerr << "Read error: " << modfile.GetError() << std::endl; - return 2; - } - } - else if (mode == "dxf") - { - if (! modfile.ReadDXF(argv[2], false, false) ) - { - std::cerr << "Read error: " << modfile.GetError() << std::endl; - return 2; - } - } - else - { - std::cerr << "Usage: " << argv[0] << " {mod|dxf} in_file out_file" << std::endl; - return 1; - } - - if (! modfile.WriteModel(argv[3]) ) - { - std::cerr << "Write error: " << modfile.GetError() << std::endl; - return 3; - } - - return 0; + Gfx::CModelFile modelFile(&iMan); + + EXPECT_TRUE(modelFile.ReadTextModel(str)); + + EXPECT_EQ(modelFile.GetTriangleCount(), 2); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[0], TRIANGLE_1)); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[1], TRIANGLE_2)); + + str.str(""); + + EXPECT_TRUE(modelFile.WriteTextModel(str)); + str.seekg(0); + EXPECT_TRUE(modelFile.ReadTextModel(str)); + + EXPECT_EQ(modelFile.GetTriangleCount(), 2); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[0], TRIANGLE_1)); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[1], TRIANGLE_2)); +} + +// Tests reading/writing new binary model +TEST(ModelFileTest, RWBinModel) +{ + std::stringstream str; + str.str(TEXT_MODEL); + + CInstanceManager iMan; + Gfx::CModelFile modelFile(&iMan); + + EXPECT_TRUE(modelFile.ReadTextModel(str)); + + EXPECT_EQ(modelFile.GetTriangleCount(), 2); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[0], TRIANGLE_1)); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[1], TRIANGLE_2)); + + str.str(""); + + EXPECT_TRUE(modelFile.WriteBinaryModel(str)); + str.seekg(0); + EXPECT_TRUE(modelFile.ReadBinaryModel(str)); + + EXPECT_EQ(modelFile.GetTriangleCount(), 2); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[0], TRIANGLE_1)); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[1], TRIANGLE_2)); +} + +// Tests reading/writing old model file +TEST(ModelFileTest, RWOldModel) +{ + std::stringstream str; + str.str(TEXT_MODEL); + + CInstanceManager iMan; + Gfx::CModelFile modelFile(&iMan); + + EXPECT_TRUE(modelFile.ReadTextModel(str)); + + EXPECT_EQ(modelFile.GetTriangleCount(), 2); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[0], TRIANGLE_1)); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[1], TRIANGLE_2)); + + str.str(""); + + EXPECT_TRUE(modelFile.WriteModel(str)); + str.seekg(0); + EXPECT_TRUE(modelFile.ReadModel(str)); + + EXPECT_EQ(modelFile.GetTriangleCount(), 2); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[0], TRIANGLE_1)); + EXPECT_TRUE(CompareTriangles(modelFile.GetTriangles()[1], TRIANGLE_2)); +} + +int main(int argc, char **argv) +{ + CLogger logger; + + Init(); + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/src/graphics/engine/water.cpp b/src/graphics/engine/water.cpp index bf2f64d..a2ff081 100644 --- a/src/graphics/engine/water.cpp +++ b/src/graphics/engine/water.cpp @@ -50,7 +50,7 @@ Gfx::CWater::CWater(CInstanceManager* iMan, Gfx::CEngine* engine) m_level = 0.0f; m_draw = true; m_lava = false; - m_color = 0xffffffff; + m_color = Gfx::Color(1.0f, 1.0f, 1.0f, 1.0f); m_subdiv = 4; m_lines.reserve(WATERLINE_PREALLOCATE_COUNT); @@ -330,7 +330,7 @@ void Gfx::CWater::DrawSurf() if (m_type[0] == Gfx::WATER_NULL) return; if (m_lines.empty()) return; - std::vector<Gfx::VertexTex2> vertices((m_brick+2)*2, Gfx::VertexTex2()); + std::vector<Gfx::VertexTex2> vertices((m_brickCount+2)*2, Gfx::VertexTex2()); Math::Vector eye = m_engine->GetEyePt(); @@ -365,7 +365,7 @@ void Gfx::CWater::DrawSurf() device->SetRenderState(Gfx::RENDER_STATE_FOG, true); - float size = m_size/2.0f; + float size = m_brickSize/2.0f; float sizez = 0.0f; if (under) sizez = -size; else sizez = size; @@ -436,8 +436,8 @@ bool Gfx::CWater::GetWater(int x, int y) x *= m_subdiv; y *= m_subdiv; - float size = m_size/m_subdiv; - float offset = m_brick*m_size/2.0f; + float size = m_brickSize/m_subdiv; + float offset = m_brickCount*m_brickSize/2.0f; for (int dy = 0; dy <= m_subdiv; dy++) { @@ -463,11 +463,11 @@ void Gfx::CWater::CreateLine(int x, int y, int len) line.y = y; line.len = len; - float offset = m_brick*m_size/2.0f - m_size/2.0f; + float offset = m_brickCount*m_brickSize/2.0f - m_brickSize/2.0f; - line.px1 = m_size* line.x - offset; - line.px2 = m_size*(line.x+line.len) - offset; - line.pz = m_size* line.y - offset; + line.px1 = m_brickSize* line.x - offset; + line.px2 = m_brickSize*(line.x+line.len) - offset; + line.pz = m_brickSize* line.y - offset; m_lines.push_back(line); } @@ -495,21 +495,21 @@ void Gfx::CWater::Create(Gfx::WaterType type1, Gfx::WaterType type2, const std:: if (m_terrain == nullptr) m_terrain = static_cast<CTerrain*>(m_iMan->SearchInstance(CLASS_TERRAIN)); - m_brick = m_terrain->GetBrick()*m_terrain->GetMosaic(); - m_size = m_terrain->GetSize(); + m_brickCount = m_terrain->GetBrickCount()*m_terrain->GetMosaicCount(); + m_brickSize = m_terrain->GetBrickSize(); - m_brick /= m_subdiv; - m_size *= m_subdiv; + m_brickCount /= m_subdiv; + m_brickSize *= m_subdiv; if (m_type[0] == WATER_NULL) return; m_lines.clear(); - for (int y = 0; y < m_brick; y++) + for (int y = 0; y < m_brickCount; y++) { int len = 0; - for (int x = 0; x < m_brick; x++) + for (int x = 0; x < m_brickCount; x++) { if (GetWater(x,y)) // water here? { @@ -530,7 +530,7 @@ void Gfx::CWater::Create(Gfx::WaterType type1, Gfx::WaterType type2, const std:: } } if (len != 0) - CreateLine(m_brick - len, y, len); + CreateLine(m_brickCount - len, y, len); } } diff --git a/src/graphics/engine/water.h b/src/graphics/engine/water.h index 371a91f..9b31045 100644 --- a/src/graphics/engine/water.h +++ b/src/graphics/engine/water.h @@ -188,16 +188,16 @@ protected: int m_subdiv; //! Number of brick*mosaics - int m_brick; + int m_brickCount; //! Size of a item in an brick - float m_size; + float m_brickSize; std::vector<WaterLine> m_lines; std::vector<WaterVapor> m_vapors; bool m_draw; bool m_lava; - long m_color; + Gfx::Color m_color; }; }; // namespace Gfx |