From 2b40c13c9d5ad5e7b11f51b1baa1b96e047a64d0 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Wed, 31 Dec 2025 23:02:13 +0300 Subject: [PATCH] Finally somehow planet is done --- shaders/planet_land_desktop.fragment | 7 + shaders/planet_stone.vertex | 9 - shaders/planet_stone_desktop.fragment | 4 +- src/planet/PlanetData.cpp | 304 +++++++++++++------------- src/planet/PlanetData.h | 22 +- src/planet/PlanetObject.cpp | 101 +++------ src/planet/PlanetObject.h | 4 +- src/planet/StoneObject.cpp | 186 +++++++++------- src/planet/StoneObject.h | 8 +- 9 files changed, 315 insertions(+), 330 deletions(-) diff --git a/shaders/planet_land_desktop.fragment b/shaders/planet_land_desktop.fragment index 117e5bd..e1379c0 100644 --- a/shaders/planet_land_desktop.fragment +++ b/shaders/planet_land_desktop.fragment @@ -32,6 +32,11 @@ void main() { vec4 bakedTextureColor = texture2D(BakedTexture, finalTexCoord); vec4 textureColor = texture2D(Texture, TexCoord); + if (bakedTextureColor.x < 0.01 && bakedTextureColor.y < 0.01 && bakedTextureColor.y < 0.01) + { + textureMixFactor = 1.0; + } + vec4 finalColor = textureMixFactor*textureColor + (1 - textureMixFactor) * bakedTextureColor; float fogFactor; @@ -65,6 +70,8 @@ void main() { fogFactor = clamp((realDist - 1000) / (800.0), 0.0, 1.0); gl_FragColor = mix(vec4(finalColor.rgb, 1.0), FOG_COLOR, fogFactor); + } + //gl_FragColor = vec4(finalColor.rgb, 1.0); } diff --git a/shaders/planet_stone.vertex b/shaders/planet_stone.vertex index baaf37a..7ee5836 100644 --- a/shaders/planet_stone.vertex +++ b/shaders/planet_stone.vertex @@ -4,8 +4,6 @@ attribute vec3 vPosition; attribute vec2 vTexCoord; varying vec2 TexCoord; -varying float viewZ; -varying vec3 pos; varying vec3 worldPosition; @@ -17,13 +15,6 @@ void main() // Преобразование позиции в пространство вида (View Space) vec4 viewPosition = ModelViewMatrix * vec4(vPosition.xyz, 1.0); - // Сохраняем отрицательную Z-координату. В OpenGL Z-координата (глубина) - // в пространстве вида обычно отрицательна, но для расчета тумана - // удобнее использовать положительное значение. - viewZ = -viewPosition.z; - - pos = vPosition.xyz; - gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); TexCoord = vTexCoord; diff --git a/shaders/planet_stone_desktop.fragment b/shaders/planet_stone_desktop.fragment index 4c3db9b..51d81e0 100644 --- a/shaders/planet_stone_desktop.fragment +++ b/shaders/planet_stone_desktop.fragment @@ -1,8 +1,6 @@ // ---Фрагментный шейдер (Fragment Shader) varying vec2 TexCoord; -varying float viewZ; -varying vec3 pos; uniform sampler2D Texture; uniform float uDistanceToPlanetSurface; @@ -55,5 +53,5 @@ void main() gl_FragColor = mix(vec4(finalColor.rgb, alphaFactor), FOG_COLOR, fogFactor); } - + //gl_FragColor = vec4(finalColor.rgb, 1.0); } \ No newline at end of file diff --git a/src/planet/PlanetData.cpp b/src/planet/PlanetData.cpp index 768f370..343d902 100644 --- a/src/planet/PlanetData.cpp +++ b/src/planet/PlanetData.cpp @@ -44,17 +44,12 @@ namespace ZL { void PlanetData::init() { for (int i = 0; i < planetMeshLods.size(); i++) { - planetMeshLods[i] = generateSphere(i, 0.025f); + //planetMeshLods[i] = generateSphere(i, 0.01f); + planetMeshLods[i] = generateSphere(i, 0.f); planetMeshLods[i].Scale(PLANET_RADIUS); planetMeshLods[i].Move(PLANET_CENTER_OFFSET); } - for (int i = 0; i < planetMeshLodsNoDist.size(); i++) { - planetMeshLodsNoDist[i] = generateSphere(i, 0); - planetMeshLodsNoDist[i].Scale(PLANET_RADIUS); - planetMeshLodsNoDist[i].Move(PLANET_CENTER_OFFSET); - } - planetAtmosphereLod = generateSphere(5, 0); planetAtmosphereLod.Scale(PLANET_RADIUS * 1.03); planetAtmosphereLod.Move(PLANET_CENTER_OFFSET); @@ -64,9 +59,6 @@ namespace ZL { return planetMeshLods.at(level); } - const LodLevel& PlanetData::getLodLevelNoDist(int level) const { - return planetMeshLodsNoDist.at(level); - } const LodLevel& PlanetData::getAtmosphereLod() const { return planetAtmosphereLod; @@ -145,7 +137,7 @@ namespace ZL { // : // . - std::vector targetTriangles = getTrianglesUnderCamera(viewerPosition); + std::vector targetTriangles = getTrianglesUnderCameraNew(viewerPosition); if (targetTriangles.empty()) { return (shipLocalPosition.length() - PLANET_RADIUS); @@ -202,87 +194,52 @@ namespace ZL { return lowestDistance; } } + std::vector PlanetData::getTrianglesUnderCameraNew(const Vector3f& viewerPosition) { + const LodLevel& finalLod = planetMeshLods[currentLod]; // LOD + Vector3f targetDir = (viewerPosition - PLANET_CENTER_OFFSET).normalized(); - // --- getTrianglesUnderCamera ( triangleUnderCamera) --- - // , API - static std::vector recursiveTriangleSearch(int lod, const Vector3f& pos, const std::array& meshes) { - std::vector r; - // 0 ( ) - if (lod == 0) { - if (pos.v[1] >= 0) { - if (pos.v[0] >= 0 && pos.v[2] >= 0) r.push_back(0); - if (pos.v[0] >= 0 && pos.v[2] <= 0) r.push_back(1); - if (pos.v[0] <= 0 && pos.v[2] <= 0) r.push_back(2); - if (pos.v[0] <= 0 && pos.v[2] >= 0) r.push_back(3); - } - if (pos.v[1] <= 0) { - if (pos.v[0] >= 0 && pos.v[2] >= 0) r.push_back(4); - if (pos.v[0] <= 0 && pos.v[2] >= 0) r.push_back(5); - if (pos.v[0] <= 0 && pos.v[2] <= 0) r.push_back(6); - if (pos.v[0] >= 0 && pos.v[2] <= 0) r.push_back(7); + int bestTriangle = -1; + float maxDot = -1.0f; + + // 1: "" + // , N- + // LOD0, . + // ( 5-6 ) + for (int i = 0; i < (int)finalLod.triangles.size(); ++i) { + // + Vector3f triDir = (finalLod.triangles[i].data[0] + + finalLod.triangles[i].data[1] + + finalLod.triangles[i].data[2]).normalized(); + + float dot = targetDir.dot(triDir); + if (dot > maxDot) { + maxDot = dot; + bestTriangle = i; } } - else { - // - std::vector r0 = recursiveTriangleSearch(lod - 1, pos, meshes); - for (int tri0 : r0) { - Vector3f a = meshes[lod - 1].vertexData.PositionData[tri0 * 3]; - Vector3f b = meshes[lod - 1].vertexData.PositionData[tri0 * 3 + 1]; - Vector3f c = meshes[lod - 1].vertexData.PositionData[tri0 * 3 + 2]; - std::vector result = PlanetData::find_sub_triangle_spherical(a, b, c, pos); - for (int trix : result) { - r.push_back(tri0 * 4 + trix); - } + if (bestTriangle == -1) return {}; + + // 2: "" + // findNeighbors + std::set resultSet; + resultSet.insert(bestTriangle); + + // + std::vector neighbors = findNeighbors(bestTriangle, currentLod); + for (int n : neighbors) { + resultSet.insert(n); + + // : ( ) + std::vector secondCircle = findNeighbors(n, currentLod); + for (int nn : secondCircle) { + resultSet.insert(nn); } } - return r; + + return std::vector(resultSet.begin(), resultSet.end()); } - - - std::vector PlanetData::getTrianglesUnderCamera(const Vector3f& viewerPosition) { - // LOD - return recursiveTriangleSearch(currentLod, viewerPosition, planetMeshLods); - } - - // --- (check_spherical_side, generateSphere ..) --- - // ( PlanetObject.cpp, - // Environment::shipPosition , , - // this->planetMeshLods) - - float PlanetData::check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon) { - Vector3f N_plane = V1.cross(V2); - float sign_P = P.dot(N_plane); - float sign_V3 = V3.dot(N_plane); - return sign_P * sign_V3; - } - - bool PlanetData::is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon) { - if (check_spherical_side(V1, V2, P, V3, epsilon) < -epsilon) return false; - if (check_spherical_side(V2, V3, P, V1, epsilon) < -epsilon) return false; - if (check_spherical_side(V3, V1, P, V2, epsilon) < -epsilon) return false; - return true; - } - - std::vector PlanetData::find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig) { - const float EPSILON = 1e-6f; - std::vector result; - const Vector3f a = a_orig.normalized(); - const Vector3f b = b_orig.normalized(); - const Vector3f c = c_orig.normalized(); - const Vector3f pxx_normalized = px_orig.normalized(); - const Vector3f m_ab = ((a + b) * 0.5f).normalized(); - const Vector3f m_bc = ((b + c) * 0.5f).normalized(); - const Vector3f m_ac = ((a + c) * 0.5f).normalized(); - - if (is_inside_spherical_triangle(pxx_normalized, a, m_ab, m_ac, EPSILON)) result.push_back(0); - if (is_inside_spherical_triangle(pxx_normalized, m_ab, b, m_bc, EPSILON)) result.push_back(1); - if (is_inside_spherical_triangle(pxx_normalized, m_ac, m_bc, c, EPSILON)) result.push_back(2); - if (is_inside_spherical_triangle(pxx_normalized, m_ab, m_bc, m_ac, EPSILON)) result.push_back(3); - - return result; - } - + std::vector PlanetData::subdivideTriangles(const std::vector& input, float noiseCoeff) { std::vector output; @@ -300,9 +257,13 @@ namespace ZL { Vector3f m_bc = ((b + c) * 0.5f).normalized(); Vector3f m_ac = ((a + c) * 0.5f).normalized(); - Vector3f pm_ab = m_ab * perlin.getSurfaceHeight(m_ab, noiseCoeff); - Vector3f pm_bc = m_bc * perlin.getSurfaceHeight(m_bc, noiseCoeff); - Vector3f pm_ac = m_ac * perlin.getSurfaceHeight(m_ac, noiseCoeff); + //Vector3f pm_ab = m_ab * perlin.getSurfaceHeight(m_ab, noiseCoeff); + //Vector3f pm_bc = m_bc * perlin.getSurfaceHeight(m_bc, noiseCoeff); + //Vector3f pm_ac = m_ac * perlin.getSurfaceHeight(m_ac, noiseCoeff); + + Vector3f pm_ab = m_ab; + Vector3f pm_bc = m_bc; + Vector3f pm_ac = m_ac; // 2. ID VertexID id_mab = generateEdgeID(id_a, id_b); @@ -318,25 +279,46 @@ namespace ZL { return output; } - LodLevel PlanetData::trianglesToVertices(const std::vector& geometry) { + LodLevel PlanetData::createLodLevel(const std::vector& geometry) { LodLevel result; result.triangles = geometry; size_t vertexCount = geometry.size() * 3; - result.vertexData.PositionData.reserve(vertexCount); - result.vertexData.NormalData.reserve(vertexCount); - result.vertexData.TexCoordData.reserve(vertexCount); - result.vertexData.TangentData.reserve(vertexCount); // - result.vertexData.BinormalData.reserve(vertexCount); + result.VertexIDs.reserve(vertexCount); + for (const auto& t : geometry) { + for (int i = 0; i < 3; ++i) { + result.VertexIDs.push_back(t.ids[i]); + } + } + return result; + } + + void PlanetData::recalculateMeshAttributes(LodLevel& lod) + { + size_t vertexCount = lod.triangles.size() * 3; + + lod.vertexData.PositionData.clear(); + lod.vertexData.NormalData.clear(); + lod.vertexData.TexCoordData.clear(); + lod.vertexData.TangentData.clear(); + lod.vertexData.BinormalData.clear(); + + lod.vertexData.PositionData.reserve(vertexCount); + lod.vertexData.NormalData.reserve(vertexCount); + lod.vertexData.TexCoordData.reserve(vertexCount); + lod.vertexData.TangentData.reserve(vertexCount); + lod.vertexData.BinormalData.reserve(vertexCount); + + const std::array triangleUVs = { Vector2f(0.5f, 1.0f), Vector2f(0.0f, 0.0f), Vector2f(1.0f, 0.0f) }; - for (const auto& t : geometry) { + for (const auto& t : lod.triangles) { // --- ( GetRotationForTriangle) --- Vector3f vA = t.data[0]; Vector3f vB = t.data[1]; @@ -357,18 +339,13 @@ namespace ZL { Vector3f y_axis = z_axis.cross(x_axis).normalized(); // V for (int i = 0; i < 3; ++i) { - result.vertexData.PositionData.push_back(t.data[i]); - result.vertexData.NormalData.push_back(z_axis); // Parallax - result.vertexData.TexCoordData.push_back(triangleUVs[i]); - - // - result.vertexData.TangentData.push_back(x_axis); - result.vertexData.BinormalData.push_back(y_axis); - - result.VertexIDs.push_back(t.ids[i]); + lod.vertexData.PositionData.push_back(t.data[i]); + lod.vertexData.NormalData.push_back(z_axis); + lod.vertexData.TexCoordData.push_back(triangleUVs[i]); + lod.vertexData.TangentData.push_back(x_axis); + lod.vertexData.BinormalData.push_back(y_axis); } } - return result; } @@ -400,7 +377,7 @@ namespace ZL { } // 4. PositionData, NormalData VertexIDs - LodLevel lodLevel = trianglesToVertices(geometry); + LodLevel lodLevel = createLodLevel(geometry); // 5. V2T-Map VertexIDs ( ) V2TMap v2tMap; @@ -417,55 +394,26 @@ namespace ZL { } lodLevel.v2tMap = v2tMap; - // 6. ( , ) - // buffer.PositionData, - // . - // , ( v2tMap ). - // 2, : - for (size_t i = 0; i < lodLevel.vertexData.PositionData.size(); i++) { - Vector3f dir = lodLevel.vertexData.PositionData[i].normalized(); - lodLevel.vertexData.PositionData[i] = dir * perlin.getSurfaceHeight(dir, noiseCoeff); - } + applySphericalLaplacianSmoothing(lodLevel, 5, 0.5f); - - // 7. ColorData - lodLevel.vertexData.ColorData.reserve(geometry.size() * 3); - const Vector3f baseColor = { 0.498f, 0.416f, 0.0f }; - const float colorFrequency = 5.0f; - const float colorAmplitude = 0.2f; - const Vector3f offsetR = { 0.1f, 0.2f, 0.3f }; - const Vector3f offsetG = { 0.5f, 0.4f, 0.6f }; - const Vector3f offsetB = { 0.9f, 0.8f, 0.7f }; - - - for (size_t i = 0; i < geometry.size(); i++) { - for (int j = 0; j < 3; j++) { - // PositionData ( NormalData) - Vector3f dir = lodLevel.vertexData.NormalData[i * 3 + j]; - - // - float noiseR = colorPerlin.noise( - (dir.v[0] + offsetR.v[0]) * colorFrequency, - (dir.v[1] + offsetR.v[1]) * colorFrequency, - (dir.v[2] + offsetR.v[2]) * colorFrequency - ); - // ... ( noiseG noiseB) - - // , PerlinNoise - float noiseG = 0.0f; - float noiseB = 0.0f; - - Vector3f colorOffset = { - noiseR * colorAmplitude, - noiseG * colorAmplitude, - noiseB * colorAmplitude - }; - - Vector3f finalColor = baseColor + colorOffset; - - lodLevel.vertexData.ColorData.push_back(finalColor); + // ID + std::map displacedPositions; + for (const auto& tri : lodLevel.triangles) { + for (int i = 0; i < 3; ++i) { + if (displacedPositions.find(tri.ids[i]) == displacedPositions.end()) { + Vector3f dir = tri.data[i].normalized(); + displacedPositions[tri.ids[i]] = dir * perlin.getSurfaceHeight(dir, noiseCoeff); + } } } + // + for (auto& tri : lodLevel.triangles) { + for (int i = 0; i < 3; ++i) { + tri.data[i] = displacedPositions[tri.ids[i]]; + } + } + + recalculateMeshAttributes(lodLevel); return lodLevel; } @@ -490,4 +438,56 @@ namespace ZL { } return std::vector(neighbors.begin(), neighbors.end()); } + + + void PlanetData::applySphericalLaplacianSmoothing(LodLevel& lod, int iterations, float relaxationFactor) { + for (int iter = 0; iter < iterations; ++iter) { + std::map newPositions; + + // 1. triangles + for (auto const& [vID, connectedTriangles] : lod.v2tMap) { + Vector3f currentPos; + std::set neighborIDs; + + for (int triIdx : connectedTriangles) { + const auto& tri = lod.triangles[triIdx]; + for (int i = 0; i < 3; ++i) { + if (tri.ids[i] == vID) { + currentPos = tri.data[i]; // triangles + } + else { + neighborIDs.insert(tri.ids[i]); + } + } + } + + if (neighborIDs.empty()) continue; + + // 2. + Vector3f centroid(0, 0, 0); + for (const auto& nID : neighborIDs) { + int firstTriIdx = lod.v2tMap[nID][0]; + const auto& tri = lod.triangles[firstTriIdx]; + for (int k = 0; k < 3; ++k) { + if (tri.ids[k] == nID) { + centroid = centroid + tri.data[k]; + break; + } + } + } + centroid = centroid * (1.0f / (float)neighborIDs.size()); + + // 3. + Vector3f relaxedPos = currentPos + (centroid - currentPos) * relaxationFactor; + newPositions[vID] = relaxedPos.normalized() * currentPos.length(); + } + + // 4. Triangle + for (auto& tri : lod.triangles) { + for (int i = 0; i < 3; ++i) { + tri.data[i] = newPositions[tri.ids[i]]; + } + } + } + } } \ No newline at end of file diff --git a/src/planet/PlanetData.h b/src/planet/PlanetData.h index 26aa8b5..0a56779 100644 --- a/src/planet/PlanetData.h +++ b/src/planet/PlanetData.h @@ -17,13 +17,17 @@ namespace ZL { VertexID generateEdgeID(const VertexID& id1, const VertexID& id2); constexpr static int MAX_LOD_LEVELS = 6; - //constexpr static int MAX_LOD_LEVELS = 3; + //constexpr static int MAX_LOD_LEVELS = 2; struct Triangle { std::array data; std::array ids; + Triangle() + { + } + Triangle(Vector3f p1, Vector3f p2, Vector3f p3) : data{ p1, p2, p3 } { @@ -73,7 +77,6 @@ namespace ZL { PerlinNoise colorPerlin; std::array planetMeshLods; - std::array planetMeshLodsNoDist; LodLevel planetAtmosphereLod; int currentLod; // @@ -82,13 +85,9 @@ namespace ZL { // std::vector subdivideTriangles(const std::vector& inputTriangles, float noiseCoeff); - LodLevel trianglesToVertices(const std::vector& triangles); + LodLevel createLodLevel(const std::vector& triangles); + void recalculateMeshAttributes(LodLevel& lod); LodLevel generateSphere(int subdivisions, float noiseCoeff); - - // - static float check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon = 1e-6f); - static bool is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon); - public: PlanetData(); @@ -96,7 +95,6 @@ namespace ZL { // ( ) const LodLevel& getLodLevel(int level) const; - const LodLevel& getLodLevelNoDist(int level) const; const LodLevel& getAtmosphereLod() const; int getCurrentLodIndex() const; int getMaxLodIndex() const; @@ -106,12 +104,10 @@ namespace ZL { float distanceToPlanetSurface(const Vector3f& viewerPosition); // , - std::vector getTrianglesUnderCamera(const Vector3f& viewerPosition); - + std::vector getTrianglesUnderCameraNew(const Vector3f& viewerPosition); std::vector findNeighbors(int index, int lod); - static std::vector find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig); - + void applySphericalLaplacianSmoothing(LodLevel& lod, int iterations, float relaxationFactor); }; } // namespace ZL \ No newline at end of file diff --git a/src/planet/PlanetObject.cpp b/src/planet/PlanetObject.cpp index 847ab59..98f900b 100644 --- a/src/planet/PlanetObject.cpp +++ b/src/planet/PlanetObject.cpp @@ -7,19 +7,13 @@ namespace ZL { - Matrix3f GetRotationForTriangle(const Triangle& tri) { - // Для треугольника №0: - // tri.data[0] = {0, 20000, 0} (A) - // tri.data[1] = {0, 0, 20000} (B) - // tri.data[2] = {20000, 0, 0} (C) Vector3f vA = tri.data[0]; Vector3f vB = tri.data[1]; Vector3f vC = tri.data[2]; // 1. Вычисляем ось X (горизонталь). - // Нам нужна грань BC: от (0, 0, 20000) до (20000, 0, 0) Vector3f x_axis = (vC - vB).normalized(); @@ -67,28 +61,24 @@ namespace ZL { // Берем максимальный LOD для начальной отрисовки int lodIndex = planetData.getMaxLodIndex(); planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData; + + //planetRenderStruct.data.PositionData.resize(9); planetRenderStruct.RefreshVBO(); - planetRenderStructCut.data = planetData.getLodLevelNoDist(lodIndex).vertexData; - - planetRenderStructCut.data.PositionData.resize(3); - - planetRenderStructCut.RefreshVBO(); - //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand2.png", "")); sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand2.png", "")); stoneTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); // Атмосфера planetAtmosphereRenderStruct.data = planetData.getAtmosphereLod().vertexData; - planetAtmosphereRenderStruct.RefreshVBO(); + if (planetAtmosphereRenderStruct.data.PositionData.size() > 0) + { + planetAtmosphereRenderStruct.RefreshVBO(); + } planetStones = CreateStoneGroupData(778, planetData.getLodLevel(lodIndex)); - planetStones.inflate({ 0/*,1,2,3,4,5,6,7*/ }); - planetStonesToBakeRenderStruct.AssignFrom(planetStones.mesh); - planetStonesToBakeRenderStruct.RefreshVBO(); - + stonesToRender = planetStones.inflate(planetStones.allInstances.size()); } void PlanetObject::prepareDrawData() { @@ -99,33 +89,14 @@ namespace ZL { void PlanetObject::update(float deltaTimeMs) { // 1. Получаем базовые треугольники под камерой - auto lr = planetData.getTrianglesUnderCamera(Environment::shipPosition); + auto lr = planetData.getTrianglesUnderCameraNew(Environment::shipPosition); int currentLod = planetData.getCurrentLodIndex(); + // Временный вектор для сбора новых индексов std::vector newIndices; - std::set used; - // Рекурсивно (или итеративно, как у тебя) собираем индексы видимых зон - for (int i : lr) { - if (used.insert(i).second) newIndices.push_back(i); - - auto neighbors = planetData.findNeighbors(i, currentLod); - for (int n : neighbors) { - if (used.insert(n).second) newIndices.push_back(n); - - auto neighbors2 = planetData.findNeighbors(n, currentLod); - for (int n2 : neighbors2) { - if (used.insert(n2).second) newIndices.push_back(n2); - - auto neighbors3 = planetData.findNeighbors(n, currentLod); - - for (int n3 : neighbors3) { - if (used.insert(n3).second) newIndices.push_back(n3); - } - } - } - } + newIndices = lr; // 2. Сортируем новый список, чтобы порядок не влиял на сравнение std::sort(newIndices.begin(), newIndices.end()); @@ -134,7 +105,7 @@ namespace ZL { if (newIndices != triangleIndicesToDraw) { // Обновляем список индексов (используем move для эффективности) triangleIndicesToDraw = std::move(newIndices); - + /* // --- ОБНОВЛЯЕМ ЖЕЛТУЮ ЗОНУ (только когда изменился состав треугольников) --- const auto& fullMesh = planetData.getLodLevel(currentLod).vertexData; planetRenderYellowStruct.data.PositionData.clear(); @@ -150,15 +121,7 @@ namespace ZL { if (planetRenderYellowStruct.data.PositionData.size() > 0) { planetRenderYellowStruct.RefreshVBO(); - } - - // --- ОБНОВЛЯЕМ КАМНИ (через новую структуру StoneGroup) --- - if (triangleIndicesToDraw.size() > 0) - { - planetStones.inflate(triangleIndicesToDraw); - // Используем AssignFrom, он внутри сам вызывает RefreshVBO - planetStonesRenderStruct.AssignFrom(planetStones.mesh); - } + }*/ } } @@ -166,7 +129,7 @@ namespace ZL { void PlanetObject::bakeStoneTexture(Renderer& renderer) { glViewport(0, 0, 512, 512); - glClearColor(224 / 255.f, 201 / 255.f, 167 / 255.f, 1.0f); + glClearColor(0,0,0, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -188,7 +151,7 @@ namespace ZL { - Triangle tr = planetData.getLodLevelNoDist(planetData.getCurrentLodIndex()).triangles[0]; + Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0]; // 1. Получаем матрицу вращения (оси в столбцах) Matrix3f mr = GetRotationForTriangle(tr); @@ -213,8 +176,8 @@ namespace ZL { float centerX = (minX + maxX) * 0.5f; float centerY = (minY + maxY) * 0.5f; - width = width * 0.995; - height = height * 0.995; + //width = width * 0.995; + //height = height * 0.995; renderer.PushProjectionMatrix( centerX - width*0.5, centerX + width * 0.5, @@ -237,21 +200,16 @@ namespace ZL { renderer.RenderUniform3fv("uPlaneNormal", &planeNormal.v[0]); renderer.RenderUniform1f("uMaxHeight", StoneParams::BASE_SCALE * 1.1f); // Соответствует BASE_SCALE + perturbation - + glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); - renderer.PushMatrix(); - - renderer.DrawVertexRenderStruct(planetRenderStructCut); - renderer.PopMatrix(); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // Отсекаем задние грани - if (planetStonesToBakeRenderStruct.data.PositionData.size() > 0) + if (stonesToRender[0].data.PositionData.size() > 0) { glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID()); - renderer.DrawVertexRenderStruct(planetStonesToBakeRenderStruct); + renderer.DrawVertexRenderStruct(stonesToRender[0]); CheckGlError(); } glDisable(GL_CULL_FACE); // Не забываем выключить, чтобы не сломать остальной рендер @@ -342,8 +300,8 @@ namespace ZL { // Позиция камеры (корабля) в мире renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]); - //renderer.RenderUniform1f("uHeightScale", 0.03f); - renderer.RenderUniform1f("uHeightScale", 0.0f); + renderer.RenderUniform1f("uHeightScale", 0.03f); + //renderer.RenderUniform1f("uHeightScale", 0.0f); renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); renderer.RenderUniform1f("uCurrentZFar", currentZFar); @@ -371,6 +329,7 @@ namespace ZL { } + void PlanetObject::drawStones(Renderer& renderer) { static const std::string defaultShaderName2 = "planetStone"; @@ -403,7 +362,7 @@ namespace ZL { renderer.RotateMatrix(Environment::inverseShipMatrix); renderer.TranslateMatrix(-Environment::shipPosition); - + renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); renderer.RenderUniform1f("uCurrentZFar", currentZFar); renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]); @@ -412,13 +371,16 @@ namespace ZL { glCullFace(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID()); - if (planetStonesRenderStruct.data.PositionData.size() > 0) + for (int i : triangleIndicesToDraw) { - glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID()); - renderer.DrawVertexRenderStruct(planetStonesRenderStruct); - CheckGlError(); + if (stonesToRender[i].data.PositionData.size() > 0) + { + renderer.DrawVertexRenderStruct(stonesToRender[i]); + } } + CheckGlError(); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); @@ -436,6 +398,7 @@ namespace ZL { void PlanetObject::drawYellowZone(Renderer& renderer) { + /* static const std::string defaultShaderName = "planetLand"; static const std::string vPositionName = "vPosition"; @@ -503,7 +466,7 @@ namespace ZL { renderer.shaderManager.PopShader(); CheckGlError(); - } + }*/ } void PlanetObject::drawAtmosphere(Renderer& renderer) { diff --git a/src/planet/PlanetObject.h b/src/planet/PlanetObject.h index 0c3ad6c..f30fa3d 100644 --- a/src/planet/PlanetObject.h +++ b/src/planet/PlanetObject.h @@ -27,12 +27,10 @@ namespace ZL { // Данные только для рендеринга (OpenGL specific) VertexRenderStruct planetRenderStruct; - VertexRenderStruct planetRenderStructCut; VertexRenderStruct planetRenderYellowStruct; VertexRenderStruct planetAtmosphereRenderStruct; - VertexRenderStruct planetStonesRenderStruct; - VertexRenderStruct planetStonesToBakeRenderStruct; StoneGroup planetStones; + std::vector stonesToRender; std::vector triangleIndicesToDraw; diff --git a/src/planet/StoneObject.cpp b/src/planet/StoneObject.cpp index e336e94..a0cb5b5 100644 --- a/src/planet/StoneObject.cpp +++ b/src/planet/StoneObject.cpp @@ -1,4 +1,4 @@ -#include "StoneObject.h" +#include "StoneObject.h" #include "utils/Utils.h" #include @@ -10,39 +10,40 @@ namespace ZL { - // --- ( ) --- - const float StoneParams::BASE_SCALE = 17.0f; // - //const float StoneParams::BASE_SCALE = 5000.0f; // - //const float StoneParams::MIN_AXIS_SCALE = 1.0f; // / - //const float StoneParams::MAX_AXIS_SCALE = 1.0f; // / - //const float StoneParams::MIN_PERTURBATION = 0.0f; // - //const float StoneParams::MAX_PERTURBATION = 0.0f; // - const float StoneParams::MIN_AXIS_SCALE = 0.5f; // / - const float StoneParams::MAX_AXIS_SCALE = 1.5f; // / - const float StoneParams::MIN_PERTURBATION = 0.05f; // - const float StoneParams::MAX_PERTURBATION = 0.25f; // - const int StoneParams::STONES_PER_TRIANGLE = 100; - // [min, max] + const float StoneParams::BASE_SCALE = 15.0f; // Общий размер камня + /*const float StoneParams::BASE_SCALE = 150.0f; // Общий размер камня + const float StoneParams::MIN_AXIS_SCALE = 1.0f; // Минимальное растяжение/сжатие по оси + const float StoneParams::MAX_AXIS_SCALE = 1.0f; // Максимальное растяжение/сжатие по оси + const float StoneParams::MIN_PERTURBATION = 0.0f; // Минимальное радиальное возмущение вершины + const float StoneParams::MAX_PERTURBATION = 0.0f;*/ // Максимальное радиальное возмущение вершины + const float StoneParams::MIN_AXIS_SCALE = 0.5f; // Минимальное растяжение/сжатие по оси + const float StoneParams::MAX_AXIS_SCALE = 1.5f; // Максимальное растяжение/сжатие по оси + const float StoneParams::MIN_PERTURBATION = 0.05f; // Минимальное радиальное возмущение вершины + const float StoneParams::MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины + + const int StoneParams::STONES_PER_TRIANGLE = 100; + + // Вспомогательная функция для получения случайного числа в диапазоне [min, max] float getRandomFloat(std::mt19937& gen, float min, float max) { std::uniform_real_distribution<> distrib(min, max); return static_cast(distrib(gen)); } - // - // + // Вспомогательная функция для генерации случайной точки на треугольнике + // Использует барицентрические координаты Vector3f GetRandomPointOnTriangle(const Triangle& t, std::mt19937& engine) { std::uniform_real_distribution<> distrib(0.0f, 1.0f); float r1 = getRandomFloat(engine, 0.0f, 1.0f); float r2 = getRandomFloat(engine, 0.0f, 1.0f); - // r1, r2 + // Преобразование r1, r2 для получения равномерного распределения float a = 1.0f - std::sqrt(r1); float b = std::sqrt(r1) * r2; float c = 1.0f - a - b; // c = sqrt(r1) * (1 - r2) - // + // Барицентрические координаты // P = a*p1 + b*p2 + c*p3 Vector3f p1_term = t.data[0] * a; Vector3f p2_term = t.data[1] * b; @@ -52,68 +53,68 @@ namespace ZL { } - // ( phi) - // . - // , . + // Икосаэдр (на основе золотого сечения phi) + // Координаты могут быть вычислены заранее для константного икосаэдра. + // Здесь только объявление, чтобы показать идею. VertexDataStruct CreateBaseConvexPolyhedron(uint64_t seed) { - // const size_t SUBDIVISION_LEVEL = 1; // ( , ) + // const size_t SUBDIVISION_LEVEL = 1; // Уровень подразделения (для более круглого камня, пока опустим) std::mt19937 engine(static_cast(seed)); - // + // Золотое сечение const float t = (1.0f + std::sqrt(5.0f)) / 2.0f; - // 12 + // 12 вершин икосаэдра std::vector initialVertices = { { -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 }, { 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t }, { t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 } }; - // 20 ( ) + // 20 треугольных граней (индексы вершин) std::vector> faces = { - // 5 0 + // 5 треугольников вокруг вершины 0 {0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11}, - // 5 + // 5 смежных полос {1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8}, - // 5 3 + // 5 треугольников вокруг вершины 3 {3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9}, - // 5 + // 5 смежных полос {4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1} }; - // 1. (Perturbation) + // 1. Нормализация и Возмущение (Perturbation) for (Vector3f& v : initialVertices) { - v = v.normalized() * StoneParams::BASE_SCALE; // BASE_SCALE + v = v.normalized() * StoneParams::BASE_SCALE; // Нормализация к сфере радиуса BASE_SCALE - // : + // Радиальное возмущение: float perturbation = getRandomFloat(engine, StoneParams::MIN_PERTURBATION, StoneParams::MAX_PERTURBATION); v = v * (1.0f + perturbation); } - // 2. ( ) + // 2. Трансформация (Масштабирование и Поворот) - // + // Случайные масштабы по осям Vector3f scaleFactors = { getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE), getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE), getRandomFloat(engine, StoneParams::MIN_AXIS_SCALE, StoneParams::MAX_AXIS_SCALE) }; - // + // Применяем масштабирование for (Vector3f& v : initialVertices) { v.v[0] *= scaleFactors.v[0]; v.v[1] *= scaleFactors.v[1]; v.v[2] *= scaleFactors.v[2]; } - // (, ) + // Случайный поворот (например, вокруг трех осей) Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f)); - Vector4f qFinal = slerp(qx, qy, 0.5f); // + Vector4f qFinal = slerp(qx, qy, 0.5f); // Простой пример комбинирования qFinal = slerp(qFinal, qz, 0.5f).normalized(); Matrix3f rotationMatrix = QuatToMatrix(qFinal); @@ -121,19 +122,19 @@ namespace ZL { v = MultMatrixVector(rotationMatrix, v); } - // 3. Mesh + // 3. Сглаженные Нормали и Формирование Mesh VertexDataStruct result; - // - // ( < Vector3f ZLMath.h, ) + // Карта для накопления нормалей по уникальным позициям вершин + // (Требует определенного оператора < для Vector3f в ZLMath.h, который у вас есть) std::map smoothNormalsMap; - // + // Предварительное заполнение карты нормалями for (const auto& face : faces) { Vector3f p1 = initialVertices[face[0]]; Vector3f p2 = initialVertices[face[1]]; Vector3f p3 = initialVertices[face[2]]; - // : (p2 - p1) x (p3 - p1) + // Нормаль грани: (p2 - p1) x (p3 - p1) Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized(); smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal; @@ -141,63 +142,63 @@ namespace ZL { smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal; } - // + // Нормализация накопленных нормалей for (auto& pair : smoothNormalsMap) { pair.second = pair.second.normalized(); } - // 4. VertexDataStruct + // 4. Финальное заполнение VertexDataStruct и Текстурные Координаты for (const auto& face : faces) { Vector3f p1 = initialVertices[face[0]]; Vector3f p2 = initialVertices[face[1]]; Vector3f p3 = initialVertices[face[2]]; - // + // Позиции result.PositionData.push_back(p1); result.PositionData.push_back(p2); result.PositionData.push_back(p3); - // ( ) + // Сглаженные Нормали (из карты) result.NormalData.push_back(smoothNormalsMap[p1]); result.NormalData.push_back(smoothNormalsMap[p2]); result.NormalData.push_back(smoothNormalsMap[p3]); - // ( ) + // Текстурные Координаты (Планарная проекция на плоскость грани) // p1 -> (0, 0), p2 -> (L_12, 0), p3 -> (L_13 * cos(angle), L_13 * sin(angle)) - // L_xy - , angle - p2-p1 p3-p1 + // Где L_xy - длина вектора, angle - угол между p2-p1 и p3-p1 Vector3f uAxis = (p2 - p1).normalized(); Vector3f vRaw = p3 - p1; - // vRaw uAxis + // Проекция vRaw на uAxis float uProjLen = vRaw.dot(uAxis); - // V U: vRaw - uProj + // Вектор V перпендикулярный U: vRaw - uProj Vector3f vAxisRaw = vRaw - (uAxis * uProjLen); - // V + // Длина оси V float vLen = vAxisRaw.length(); - // V + // Нормализованная ось V Vector3f vAxis = vAxisRaw.normalized(); - // (u, v) p1, p2, p3 p1 + // Координаты (u, v) для p1, p2, p3 относительно p1 Vector2f uv1 = { 0.0f, 0.0f }; - Vector2f uv2 = { (p2 - p1).length(), 0.0f }; // p2-p1 U - Vector2f uv3 = { uProjLen, vLen }; // p3-p1: u- = uProjLen, v- = vLen + Vector2f uv2 = { (p2 - p1).length(), 0.0f }; // p2-p1 вдоль оси U + Vector2f uv3 = { uProjLen, vLen }; // p3-p1: u-компонента = uProjLen, v-компонента = vLen - // , [0, 1] + // Находим максимальный размер, чтобы масштабировать в [0, 1] float maxUV = max(uv2.v[0], max(uv3.v[0], uv3.v[1])); if (maxUV > 0.000001f) { - // : + // Масштабируем: result.TexCoordData.push_back(uv1); result.TexCoordData.push_back(uv2 * (1.f / maxUV)); result.TexCoordData.push_back(uv3 * (1.f / maxUV)); } else { - // + // Предотвращение деления на ноль для вырожденных граней result.TexCoordData.push_back({ 0.0f, 0.0f }); result.TexCoordData.push_back({ 0.0f, 0.0f }); result.TexCoordData.push_back({ 0.0f, 0.0f }); @@ -207,10 +208,36 @@ namespace ZL { return result; } + Triangle createLocalTriangle(const Triangle& sampleTri) + { + // Находим центр в 3D + Vector3f center = (sampleTri.data[0] + sampleTri.data[1] + sampleTri.data[2]) * (1.0f / 3.0f); + + // Строим базис самого треугольника + // vY направляем на 0-ю вершину (как в вашем Special расчете) + Vector3f vY = (sampleTri.data[0] - center).normalized(); + // Временный X для расчета нормали + Vector3f vX_temp = (sampleTri.data[1] - sampleTri.data[2]).normalized(); + // Чистая нормаль + Vector3f vZ = vX_temp.cross(vY).normalized(); + // Чистый X, перпендикулярный Y и Z + Vector3f vX = vY.cross(vZ).normalized(); + + // Переводим 3D точки в этот 2D базис (Z зануляется сам собой) + auto toLocal = [&](const Vector3f& p) { + Vector3f d = p - center; + return Vector3f{ d.dot(vX), d.dot(vY), 0.0f }; + }; + + Triangle local; + local.data[0] = toLocal(sampleTri.data[0]); + local.data[1] = toLocal(sampleTri.data[1]); + local.data[2] = toLocal(sampleTri.data[2]); + return local; + } + StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel) { StoneGroup group; - - // LOD group.allInstances.resize(planetLodLevel.triangles.size()); for (size_t tIdx = 0; tIdx < planetLodLevel.triangles.size(); ++tIdx) { @@ -219,18 +246,21 @@ namespace ZL { for (int i = 0; i < StoneParams::STONES_PER_TRIANGLE; ++i) { StoneInstance instance; - instance.seed = globalSeed;// + tIdx * 1000 + i; // - + instance.seed = globalSeed;// + tIdx * 1000 + i; instance.position = GetRandomPointOnTriangle(tri, engine); - //instance.position = Vector3f(5000.0f, 5000.0f, 5000.0f); - // instance.scale = { getRandomFloat(engine, 0.5f, 1.5f), getRandomFloat(engine, 0.5f, 1.5f), getRandomFloat(engine, 0.5f, 1.5f) }; + //!!! Hack - to fix it! -- Vladislav Khorev + if (tIdx == 0) + { + instance.scale = instance.scale * 0.6f; + } + Vector4f qx = QuatFromRotateAroundX(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qy = QuatFromRotateAroundY(getRandomFloat(engine, 0.0f, 360.0f)); Vector4f qz = QuatFromRotateAroundZ(getRandomFloat(engine, 0.0f, 360.0f)); @@ -242,18 +272,15 @@ namespace ZL { return group; } - void StoneGroup::inflate(const std::vector& triangleIndices) { - // 1. - mesh.PositionData.clear(); - mesh.NormalData.clear(); - mesh.TexCoordData.clear(); - + std::vector StoneGroup::inflate(int count) + { static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337); - // 2. - for (int tIdx : triangleIndices) { - if (tIdx >= allInstances.size()) continue; + std::vector result; + result.resize(count); + for (int tIdx = 0; tIdx < count; tIdx++) + { for (const auto& inst : allInstances[tIdx]) { Matrix3f rotMat = QuatToMatrix(inst.rotation); @@ -261,16 +288,21 @@ namespace ZL { Vector3f p = baseStone.PositionData[j]; Vector3f n = baseStone.NormalData[j]; - // -> -> p.v[0] *= inst.scale.v[0]; p.v[1] *= inst.scale.v[1]; p.v[2] *= inst.scale.v[2]; - mesh.PositionData.push_back(MultMatrixVector(rotMat, p) + inst.position); - mesh.NormalData.push_back(MultMatrixVector(rotMat, n)); - mesh.TexCoordData.push_back(baseStone.TexCoordData[j]); + result[tIdx].data.PositionData.push_back(MultMatrixVector(rotMat, p) + inst.position); + result[tIdx].data.NormalData.push_back(MultMatrixVector(rotMat, n)); + result[tIdx].data.TexCoordData.push_back(baseStone.TexCoordData[j]); + } + + result[tIdx].RefreshVBO(); } } + + return result; } + } // namespace ZL diff --git a/src/planet/StoneObject.h b/src/planet/StoneObject.h index 5a1664c..f2d301d 100644 --- a/src/planet/StoneObject.h +++ b/src/planet/StoneObject.h @@ -27,15 +27,15 @@ namespace ZL { // mesh.PositionData inflate() VertexDataStruct mesh; - // , - // std::vector> allInstances; // - void inflate(const std::vector& triangleIndices); + std::vector inflate(int count); }; // , - StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel); + StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& lodLevel); + + Triangle createLocalTriangle(const Triangle& sampleTri); } // namespace ZL