Refactoring

This commit is contained in:
Vladislav Khorev 2025-12-15 21:47:45 +03:00
parent db25abc305
commit 7c272a2ee6
9 changed files with 765 additions and 823 deletions

View File

@ -440,6 +440,10 @@ add_executable(space-game001
SparkEmitter.h
PlanetObject.cpp
PlanetObject.h
PlanetData.cpp
PlanetData.h
Perlin.cpp
Perlin.h
)
# Установка проекта по умолчанию для Visual Studio

View File

@ -31,7 +31,7 @@ bool Environment::tapDownHold = false;
Vector2f Environment::tapDownStartPos = { 0, 0 };
Vector2f Environment::tapDownCurrentPos = { 0, 0 };
Vector3f Environment::shipPosition = {100,100,45000.f};
Vector3f Environment::shipPosition = {0,0,45000.f};
float Environment::shipVelocity = 0.f;

View File

@ -366,6 +366,7 @@ namespace ZL
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
renderer.TranslateMatrix({ 0.f, 0.f, 45000.f });
renderer.TranslateMatrix(boxCoordsArr[i].pos);
renderer.RotateMatrix(boxCoordsArr[i].m);
@ -468,7 +469,7 @@ namespace ZL
CheckGlError();
float skyPercent = 0.0;
float distance = planetObject.distanceToPlanetSurface();
float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition);
if (distance > 1900.f)
{
skyPercent = 0.0f;
@ -484,12 +485,12 @@ namespace ZL
drawCubemap(skyPercent);
planetObject.draw(renderer);
if (planetObject.planetIsFar())
if (planetObject.distanceToPlanetSurface(Environment::shipPosition) > 100.f)
{
glClear(GL_DEPTH_BUFFER_BIT);
}
drawShip();
//drawBoxes();
drawBoxes();
drawUI();

73
Perlin.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "Perlin.h"
#include <cmath>
#include <numeric>
#include <random>
#include <algorithm>
namespace ZL {
PerlinNoise::PerlinNoise() {
p.resize(256);
std::iota(p.begin(), p.end(), 0);
// Ïåðåìåøèâàåì äëÿ ñëó÷àéíîñòè (ìîæíî çàäàòü seed)
std::default_random_engine engine(77777);
std::shuffle(p.begin(), p.end(), engine);
p.insert(p.end(), p.begin(), p.end()); // Äóáëèðóåì äëÿ ïåðåïîëíåíèÿ
}
PerlinNoise::PerlinNoise(uint64_t seed) {
p.resize(256);
std::iota(p.begin(), p.end(), 0);
// Ïåðåìåøèâàåì äëÿ ñëó÷àéíîñòè (èñïîëüçóåì ïåðåäàííûé seed)
std::default_random_engine engine(static_cast<unsigned int>(seed));
std::shuffle(p.begin(), p.end(), engine);
p.insert(p.end(), p.begin(), p.end()); // Äóáëèðóåì äëÿ ïåðåïîëíåíèÿ
}
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); }
float PerlinNoise::grad(int hash, float x, float y, float z) {
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
float PerlinNoise::noise(float x, float y, float z) {
int X = (int)floor(x) & 255;
int Y = (int)floor(y) & 255;
int Z = (int)floor(z) & 255;
x -= floor(x);
y -= floor(y);
z -= floor(z);
float u = fade(x);
float v = fade(y);
float w = fade(z);
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z;
int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;
return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)),
lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))),
lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)),
lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1))));
}
float PerlinNoise::getSurfaceHeight(Vector3f pos, float noiseCoeff) {
// ×àñòîòà øóìà (÷åì áîëüøå, òåì áîëüøå "õîëìîâ")
float frequency = 7.0f;
// Ïîëó÷àåì çíà÷åíèå øóìà (îáû÷íî îò -1 äî 1)
float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency);
// Ìàñøòàáèðóåì: õîòèì îòêëîíåíèå îò 1.0 äî 1.1 (ïðèìåðíî)
float height = 1.0f + (noiseValue * noiseCoeff);
return height;
}
} // namespace ZL

24
Perlin.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <vector>
#include <cstdint>
#include "ZLMath.h"
namespace ZL {
class PerlinNoise {
std::vector<int> p;
public:
PerlinNoise();
PerlinNoise(uint64_t seed);
float fade(float t);
float lerp(float t, float a, float b);
float grad(int hash, float x, float y, float z);
float noise(float x, float y, float z);
float getSurfaceHeight(Vector3f pos, float noiseCoeff);
};
} // namespace ZL

483
PlanetData.cpp Normal file
View File

@ -0,0 +1,483 @@
#include "PlanetData.h"
#include <iostream>
#include <numeric>
#include <cmath>
#include <algorithm>
namespace ZL {
const float PlanetData::PLANET_RADIUS = 20000.f;
const Vector3f PlanetData::PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, 0.0f };
// --- Êîíñòàíòû äèàïàçîíîâ (ïåðåíåñåíû èç PlanetObject.cpp) ---
static constexpr float FAR_Z_NEAR = 2000.0f;
static constexpr float FAR_Z_FAR = 200000.0f;
static constexpr float TRANSITION_FAR_START = 3000.0f;
static constexpr float MIDDLE_Z_NEAR = 500.f;
static constexpr float MIDDLE_Z_FAR = 100000.f;
static constexpr float TRANSITION_MIDDLE_START = 500.f;
static constexpr float NEAR_Z_NEAR = 100.0f;
static constexpr float NEAR_Z_FAR = 20000.0f;
static constexpr float TRANSITION_NEAR_END = 100.f;
static constexpr float SUPER_NEAR_Z_NEAR = 30.0f;
static constexpr float SUPER_NEAR_Z_FAR = 6000.0f;
static constexpr float TRANSITION_SUPER_NEAR_END = 30.f;
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2) {
return id1 < id2 ? id1 + "_" + id2 : id2 + "_" + id1;
}
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ äëÿ ïðîåêöèè (ëîêàëüíàÿ)
static Vector3f projectPointOnPlane(const Vector3f& P, const Vector3f& A, const Vector3f& B, const Vector3f& C) {
Vector3f AB = B + A * (-1.0f);
Vector3f AC = C + A * (-1.0f);
Vector3f N = AB.cross(AC).normalized();
Vector3f AP = P + A * (-1.0f);
float distance = N.dot(AP);
return P + N * (-distance);
}
PlanetData::PlanetData()
: perlin(77777)
, colorPerlin(123123)
, currentLod(0)
{
currentLod = planetMeshLods.size() - 1; // Start with max LOD
initialVertexMap = {
{{ 0.0f, 1.0f, 0.0f}, "A"},
{{ 0.0f, -1.0f, 0.0f}, "B"},
{{ 1.0f, 0.0f, 0.0f}, "C"},
{{-1.0f, 0.0f, 0.0f}, "D"},
{{ 0.0f, 0.0f, 1.0f}, "E"},
{{ 0.0f, 0.0f, -1.0f}, "F"}
};
}
void PlanetData::init() {
for (int i = 0; i < planetMeshLods.size(); i++) {
planetMeshLods[i] = generateSphere(i, 0.02f);
planetMeshLods[i].vertexData.Scale(PLANET_RADIUS);
planetMeshLods[i].vertexData.Move(PLANET_CENTER_OFFSET);
}
planetAtmosphereLod = generateSphere(5, 0);
planetAtmosphereLod.vertexData.Scale(PLANET_RADIUS * 1.03);
planetAtmosphereLod.vertexData.Move(PLANET_CENTER_OFFSET);
}
const LodLevel& PlanetData::getLodLevel(int level) const {
return planetMeshLods.at(level);
}
const LodLevel& PlanetData::getAtmosphereLod() const {
return planetAtmosphereLod;
}
int PlanetData::getCurrentLodIndex() const {
return currentLod;
}
int PlanetData::getMaxLodIndex() const {
return static_cast<int>(planetMeshLods.size() - 1);
}
std::pair<float, float> PlanetData::calculateZRange(float dToPlanetSurface) {
float currentZNear;
float currentZFar;
float alpha;
if (dToPlanetSurface >= TRANSITION_FAR_START) {
currentZNear = FAR_Z_NEAR;
currentZFar = FAR_Z_FAR;
}
else if (dToPlanetSurface > TRANSITION_MIDDLE_START) {
const float transitionLength = TRANSITION_FAR_START - TRANSITION_MIDDLE_START;
float normalizedDist = (dToPlanetSurface - TRANSITION_MIDDLE_START) / transitionLength;
alpha = 1.0f - normalizedDist;
currentZNear = FAR_Z_NEAR * (1.0f - alpha) + MIDDLE_Z_NEAR * alpha;
currentZFar = FAR_Z_FAR * (1.0f - alpha) + MIDDLE_Z_FAR * alpha;
}
else if (dToPlanetSurface > TRANSITION_NEAR_END) {
const float transitionLength = TRANSITION_MIDDLE_START - TRANSITION_NEAR_END;
float normalizedDist = (dToPlanetSurface - TRANSITION_NEAR_END) / transitionLength;
alpha = 1.0f - normalizedDist;
currentZNear = MIDDLE_Z_NEAR * (1.0f - alpha) + NEAR_Z_NEAR * alpha;
currentZFar = MIDDLE_Z_FAR * (1.0f - alpha) + NEAR_Z_FAR * alpha;
}
else if (dToPlanetSurface > TRANSITION_SUPER_NEAR_END) {
const float transitionLength = TRANSITION_NEAR_END - TRANSITION_SUPER_NEAR_END;
float normalizedDist = (dToPlanetSurface - TRANSITION_SUPER_NEAR_END) / transitionLength;
alpha = 1.0f - normalizedDist;
currentZNear = NEAR_Z_NEAR * (1.0f - alpha) + SUPER_NEAR_Z_NEAR * alpha;
currentZFar = NEAR_Z_FAR * (1.0f - alpha) + SUPER_NEAR_Z_FAR * alpha;
}
else {
currentZNear = SUPER_NEAR_Z_NEAR;
currentZFar = SUPER_NEAR_Z_FAR;
}
return { currentZNear, currentZFar };
}
float PlanetData::distanceToPlanetSurface(const Vector3f& viewerPosition) {
Vector3f shipLocalPosition = viewerPosition - PLANET_CENTER_OFFSET;
// Èñïîëüçóåì getTrianglesUnderCamera äëÿ ïîèñêà
// ÂÍÈÌÀÍÈÅ: Çäåñü ðåêóðñèÿ ëîãèêè. Ìåòîä getTrianglesUnderCamera èñïîëüçóåò âíóòðè viewerPosition.
// ×òîáû íå äóáëèðîâàòü êîä, ïåðåïèøåì ëîãèêó ïîèñêà çäåñü èëè áóäåì èñïîëüçîâàòü óæå íàéäåííûé ðàíåå.
// Äëÿ ïðîñòîòû îñòàâèì ëîãèêó êàê â îðèãèíàëå, íî àäàïòèðîâàííóþ.
// ÂÀÆÍÎ: getTrianglesUnderCamera - ýòî "òÿæåëàÿ" ôóíêöèÿ. Â îðèãèíàëå îíà âûçûâàëàñü âíóòðè distanceToPlanetSurface.
// Íî îíà òðåáóåò LOD. Èñïîëüçóåì MAX LOD äëÿ òî÷íîñòè.
// Ðåêóðñèâíûé ïîèñê òðåóãîëüíèêîâ ïîä êàìåðîé.
// Çäåñü íàì íóæíî ðåàëèçîâàòü àíàëîã triangleUnderCamera, íî íå çàâèñÿùèé îò ãëîáàëüíîãî ñîñòîÿíèÿ.
// Â îðèãèíàëå ôóíêöèÿ triangleUnderCamera áûëà ðåêóðñèâíîé.
// Íèæå ÿ ðåàëèçóþ èòåðàòèâíûé èëè ðåêóðñèâíûé ïîäõîä âíóòðè getTrianglesUnderCamera.
// Âðåìåííîå ðåøåíèå: ïîëíàÿ êîïèÿ ëîãèêè ïîèñêà òðåóãîëüíèêà
// Íî íàì íóæåí äîñòóï ê ìåòîäó.
std::vector<int> targetTriangles = getTrianglesUnderCamera(viewerPosition);
if (targetTriangles.empty()) {
return (shipLocalPosition.length() - PLANET_RADIUS);
}
int tri_index = targetTriangles[0];
const auto& posData = planetMeshLods[currentLod].vertexData.PositionData;
size_t data_index = tri_index * 3;
if (data_index + 2 >= posData.size()) {
return (shipLocalPosition.length() - PLANET_RADIUS);
}
const Vector3f& A = posData[data_index];
const Vector3f& B = posData[data_index + 1];
const Vector3f& C = posData[data_index + 2];
Vector3f P_proj = projectPointOnPlane(shipLocalPosition, A, B, C);
Vector3f P_closest = P_proj;
return (shipLocalPosition - P_closest).length();
}
// --- Ðåàëèçàöèÿ getTrianglesUnderCamera (áûâøàÿ triangleUnderCamera) ---
// Âñïîìîãàòåëüíàÿ ðåêóðñèâíàÿ ôóíêöèÿ, ñêðûòàÿ îò ïóáëè÷íîãî API
static std::vector<int> recursiveTriangleSearch(int lod, const Vector3f& pos, const std::array<LodLevel, 6>& meshes) {
std::vector<int> 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);
}
}
else {
// Ðåêóðñèâíûé øàã
std::vector<int> 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<int> result = PlanetData::find_sub_triangle_spherical(a, b, c, pos);
for (int trix : result) {
r.push_back(tri0 * 4 + trix);
}
}
}
return r;
}
std::vector<int> 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<int> 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<int> 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;
}
// ... Ðåàëèçàöèè subdivideTriangles, calculateSurfaceNormal, trianglesToVertices, generateSphere, findNeighbors
// Ïåðåíåñèòå èõ èç PlanetObject.cpp "êàê åñòü", äîáàâèâ êëàññ PlanetData:: ïåðåä èìåíåì.
// Óáåäèòåñü, ÷òî èñïîëüçóåòå perlin è planetMeshLods èç this.
std::vector<Triangle> PlanetData::subdivideTriangles(const std::vector<Triangle>& input) {
std::vector<Triangle> output;
for (const auto& t : input) {
// Âåðøèíû è èõ ID
const Vector3f& a = t.data[0];
const Vector3f& b = t.data[1];
const Vector3f& c = t.data[2];
const VertexID& id_a = t.ids[0];
const VertexID& id_b = t.ids[1];
const VertexID& id_c = t.ids[2];
// 1. Âû÷èñëÿåì ñåðåäèíû (êîîðäèíàòû)
Vector3f m_ab = ((a + b) * 0.5f).normalized();
Vector3f m_bc = ((b + c) * 0.5f).normalized();
Vector3f m_ac = ((a + c) * 0.5f).normalized();
// 2. Âû÷èñëÿåì ID íîâûõ âåðøèí
VertexID id_mab = generateEdgeID(id_a, id_b);
VertexID id_mbc = generateEdgeID(id_b, id_c);
VertexID id_mac = generateEdgeID(id_a, id_c);
// 3. Ôîðìèðóåì 4 íîâûõ òðåóãîëüíèêà
output.emplace_back(Triangle{ {a, m_ab, m_ac}, {id_a, id_mab, id_mac} }); // 0
output.emplace_back(Triangle{ {m_ab, b, m_bc}, {id_mab, id_b, id_mbc} }); // 1
output.emplace_back(Triangle{ {m_ac, m_bc, c}, {id_mac, id_mbc, id_c} }); // 2
output.emplace_back(Triangle{ {m_ab, m_bc, m_ac}, {id_mab, id_mbc, id_mac} }); // 3
}
return output;
}
Vector3f PlanetData::calculateSurfaceNormal(Vector3f p_sphere, float noiseCoeff) {
// p_sphere - ýòî íîðìàëèçîâàííûé âåêòîð (òî÷êà íà èäåàëüíîé ñôåðå)
float theta = 0.01f; // Øàã äëÿ "ùóïàíüÿ" ñîñåäåé (epsilon)
// Íàì íóæíî íàéòè äâà âåêòîðà, êàñàòåëüíûõ ê ñôåðå â òî÷êå p_sphere.
// Äëÿ ýòîãî áåðåì ëþáîé âåêòîð (íàïðèìåð UP), äåëàåì Cross Product, ÷òîáû ïîëó÷èòü êàñàòåëüíóþ.
// Åñëè p_sphere ñîâïàäàåò ñ UP, áåðåì RIGHT.
Vector3f up = Vector3f(0.0f, 1.0f, 0.0f);
if (abs(p_sphere.dot(up)) > 0.99f) {
up = Vector3f(1.0f, 0.0f, 0.0f);
}
Vector3f tangentX = (up.cross(p_sphere)).normalized();
Vector3f tangentY = (p_sphere.cross(tangentX)).normalized();
// Òî÷êè íà èäåàëüíîé ñôåðå ñî ñìåùåíèåì
Vector3f p0_dir = p_sphere;
Vector3f p1_dir = (p_sphere + tangentX * theta).normalized();
Vector3f p2_dir = (p_sphere + tangentY * theta).normalized();
// Ðåàëüíûå òî÷êè íà èñêàæåííîé ïîâåðõíîñòè
// p = dir * height(dir)
Vector3f p0 = p0_dir * perlin.getSurfaceHeight(p0_dir, noiseCoeff);
Vector3f p1 = p1_dir * perlin.getSurfaceHeight(p1_dir, noiseCoeff);
Vector3f p2 = p2_dir * perlin.getSurfaceHeight(p2_dir, noiseCoeff);
// Âåêòîðà îò öåíòðàëüíîé òî÷êè ê ñîñåäÿì
Vector3f v1 = p1 - p0;
Vector3f v2 = p2 - p0;
// Íîðìàëü - ýòî ïåðïåíäèêóëÿð ê ýòèì äâóì âåêòîðàì
// Ïîðÿäîê (v2, v1) èëè (v1, v2) çàâèñèò îò ñèñòåìû êîîðäèíàò,
// çäåñü ïîäáèðàåì òàê, ÷òîáû íîðìàëü ñìîòðåëà íàðóæó.
return (-v2.cross(v1)).normalized();
}
LodLevel PlanetData::trianglesToVertices(const std::vector<Triangle>& geometry) {
LodLevel result;
result.vertexData.PositionData.reserve(geometry.size() * 3);
result.vertexData.NormalData.reserve(geometry.size() * 3);
result.vertexData.TexCoordData.reserve(geometry.size() * 3); // <-- ÐÅÇÅÐÂÈÐÓÅÌ
//buffer.TexCoord3Data.reserve(triangles.size() * 3); // <-- ÐÅÇÅÐÂÈÐÓÅÌ
// Ñòàíäàðòíûå UV-êîîðäèíàòû äëÿ ïîêðûòèÿ îäíîãî òðåóãîëüíèêà
// Ïîêðûâàåò òåêñòóðîé âñþ ãðàíü.
const std::array<Vector2f, 3> triangleUVs = {
Vector2f(0.0f, 0.0f),
Vector2f(1.0f, 0.0f),
Vector2f(0.0f, 1.0f)
};
result.VertexIDs.reserve(geometry.size() * 3); // Çàïîëíÿåì ID çäåñü
for (const auto& t : geometry) {
for (int i = 0; i < 3; ++i) {
// Çàïîëíÿåì PositionData
result.vertexData.PositionData.push_back(t.data[i]);
// Çàïîëíÿåì NormalData (íîðìàëü = íîðìàëèçîâàííàÿ ïîçèöèÿ íà ñôåðå)
result.vertexData.NormalData.push_back(t.data[i].normalized());
result.vertexData.TexCoordData.push_back(triangleUVs[i]);
// Çàïîëíÿåì VertexIDs
result.VertexIDs.push_back(t.ids[i]);
}
}
return result;
}
LodLevel PlanetData::generateSphere(int subdivisions, float noiseCoeff) {
// 1. Èñõîäíûé îêòàýäð è ïðèñâîåíèå ID
std::vector<Triangle> geometry = {
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // 0
{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 1
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // 2
{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // 3
{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // 4
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // 5
{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 6
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}} // 7
};
// Ïðèñâîåíèå ID èñõîäíûì âåðøèíàì
for (auto& t : geometry) {
for (int i = 0; i < 3; i++) {
// Èñïîëüçóåì map äëÿ ïîëó÷åíèÿ ID ïî ÷èñòûì êîîðäèíàòàì (íîðì. == ÷èñòûå)
t.ids[i] = initialVertexMap[t.data[i].normalized()];
}
}
// 2. ÏÐÈÌÅÍßÅÌ ØÓÌ Ê ÈÑÕÎÄÍÛÌ ÂÅÐØÈÍÀÌ
// ÂÀÆÍÎ: Ìû ïðèìåíÿåì øóì ÏÎÑËÅ íîðìàëèçàöèè, íî ïåðåä ðàçáèåíèåì.
// Åñëè âû õîòèòå, ÷òîáû øóì áûë ïðèìåíåí òîëüêî ê êîíå÷íûì âåðøèíàì,
// ïåðåìåñòèòå ýòîò áëîê ïîñëå øàãà 3. Îñòàâèì, êàê â âàøåì êîäå.
// **ÏÐÈÌÅ×ÀÍÈÅ:** Åñëè øóì ïðèìåíåí ñåé÷àñ, òî âåðøèíû íà L>0 áóäóò èìåòü
// êîîðäèíàòû (m_ab = (a_noisy + b_noisy)*0.5).
// Åñëè âû õîòèòå, ÷òîáû òîëüêî ôèíàëüíûå âåðøèíû èìåëè øóì, ïðîïóñòèòå ýòîò áëîê.
//  òåêóùåé çàäà÷å ýòî íå êðèòè÷íî, òàê êàê ìû èñïîëüçóåì ÒÎËÜÊÎ VertexID.
// 3. Ðàçáèâàåì N ðàç (â subdivideTriangles ãåíåðèðóþòñÿ ID íîâûõ âåðøèí)
for (int i = 0; i < subdivisions; i++) {
geometry = subdivideTriangles(geometry);
}
// 4. Ãåíåðèðóåì PositionData, NormalData è VertexIDs
LodLevel lodLevel = trianglesToVertices(geometry);
// 5. Ñîçäàíèå V2T-Map íà îñíîâå VertexIDs (ÒÎÏÎËÎÃÈ×ÅÑÊÈÉ ÊËÞ×)
V2TMap v2tMap;
const auto& finalVertexIDs = lodLevel.VertexIDs;
size_t num_triangles = geometry.size();
for (size_t i = 0; i < num_triangles; ++i) {
for (int j = 0; j < 3; ++j) {
VertexID v_id = finalVertexIDs[i * 3 + j];
// Åñëè êëþ÷ óæå åñòü, ïðîñòî äîáàâëÿåì èíäåêñ
v2tMap[v_id].push_back((int)i);
}
}
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);
// Îáðàòèòå âíèìàíèå: NormalData îñòàåòñÿ (dir), êàê â âàøåì êîäå
lodLevel.vertexData.NormalData[i] = dir;
}
// 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);
}
}
return lodLevel;
}
// Ïðèìåð findNeighbors:
std::vector<int> PlanetData::findNeighbors(int index, int lod) {
const V2TMap& v2tMap = planetMeshLods[lod].v2tMap;
const auto& vertexIDs = planetMeshLods[lod].VertexIDs;
if ((index * 3 + 2) >= vertexIDs.size()) return {};
std::set<int> neighbors;
for (int i = 0; i < 3; ++i) {
VertexID v_id = vertexIDs[index * 3 + i];
auto it = v2tMap.find(v_id);
if (it != v2tMap.end()) {
for (int tri_index : it->second) {
if (tri_index != index) {
neighbors.insert(tri_index);
}
}
}
}
return std::vector<int>(neighbors.begin(), neighbors.end());
}
// ... È îñòàëüíûå ìåòîäû ãåíåðàöèè ...
}

93
PlanetData.h Normal file
View File

@ -0,0 +1,93 @@
#pragma once
#include "ZLMath.h"
#include "Perlin.h"
#include "Renderer.h" // Äëÿ VertexDataStruct
#include <vector>
#include <array>
#include <string>
#include <map>
#include <set>
namespace ZL {
using VertexID = std::string;
using V2TMap = std::map<VertexID, std::vector<int>>;
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2);
struct Triangle
{
std::array<Vector3f, 3> data;
std::array<VertexID, 3> ids;
Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
: data{ p1, p2, p3 }
{
}
Triangle(std::array<Vector3f, 3> idata, std::array<VertexID, 3> iids)
: data{ idata }
, ids{ iids }
{
}
};
struct LodLevel
{
VertexDataStruct vertexData;
std::vector<VertexID> VertexIDs;
V2TMap v2tMap;
};
class PlanetData {
public:
static const float PLANET_RADIUS;
static const Vector3f PLANET_CENTER_OFFSET;
private:
PerlinNoise perlin;
PerlinNoise colorPerlin;
std::array<LodLevel, 6> planetMeshLods;
LodLevel planetAtmosphereLod;
int currentLod; // Ëîãè÷åñêèé òåêóùèé óðîâåíü äåòàëèçàöèè
std::map<Vector3f, VertexID> initialVertexMap;
// Âíóòðåííèå ìåòîäû ãåíåðàöèè
std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles);
Vector3f calculateSurfaceNormal(Vector3f p_sphere, float noiseCoeff);
LodLevel trianglesToVertices(const std::vector<Triangle>& triangles);
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();
void init();
// Ìåòîäû äîñòóïà ê äàííûì (äëÿ ðåíäåðåðà)
const LodLevel& getLodLevel(int level) const;
const LodLevel& getAtmosphereLod() const;
int getCurrentLodIndex() const;
int getMaxLodIndex() const;
// Ëîãèêà
std::pair<float, float> calculateZRange(float distanceToSurface);
float distanceToPlanetSurface(const Vector3f& viewerPosition);
// Âîçâðàùàåò èíäåêñû òðåóãîëüíèêîâ, âèäèìûõ êàìåðîé
std::vector<int> getTrianglesUnderCamera(const Vector3f& viewerPosition);
std::vector<int> findNeighbors(int index, int lod);
static std::vector<int> find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig);
};
} // namespace ZL

View File

@ -6,302 +6,81 @@
namespace ZL {
static constexpr float PLANET_RADIUS = 20000.f;
static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, 0.0f };
//static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -45000.f };
// --- 1. Дальний диапазон (FAR) ---
static constexpr float FAR_Z_NEAR = 2000.0f;
static constexpr float FAR_Z_FAR = 200000.0f;
// Дистанция, где НАЧИНАЕТСЯ переход FAR -> MIDDLE
static constexpr float TRANSITION_FAR_START = 3000.0f;
// --- 2. Средний диапазон (MIDDLE) ---
static constexpr float MIDDLE_Z_NEAR = 500.f;
static constexpr float MIDDLE_Z_FAR = 100000.f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход FAR -> MIDDLE и НАЧИНАЕТСЯ MIDDLE -> NEAR
static constexpr float TRANSITION_MIDDLE_START = 500.f;
// --- 3. Ближний диапазон (NEAR) ---
// Новые константы для максимальной точности
static constexpr float NEAR_Z_NEAR = 100.0f;
static constexpr float NEAR_Z_FAR = 20000.0f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR
static constexpr float TRANSITION_NEAR_END = 100.f;
// --- 3. Ближний диапазон (NEAR) ---
// Новые константы для максимальной точности
static constexpr float SUPER_NEAR_Z_NEAR = 30.0f;
static constexpr float SUPER_NEAR_Z_FAR = 6000.0f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR
static constexpr float TRANSITION_SUPER_NEAR_END = 30.f;
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2) {
// Канонический вид для ребра V1-V2: (min(ID1, ID2) + "_" + max(ID1, ID2))
return id1 < id2 ? id1 + "_" + id2 : id2 + "_" + id1;
}
std::pair<float, float> PlanetObject::calculateZRange(const Vector3f& shipPosition) {
// 1. Вычисление расстояния до поверхности планеты
const float dToPlanetSurface = distanceToPlanetSurface();
std::cout << "distanceToPlanetSurface " << dToPlanetSurface << std::endl;
float currentZNear;
float currentZFar;
float alpha; // Коэффициент интерполяции для текущего сегмента
// Диапазон I: Далеко (FAR) -> Средне (MIDDLE)
if (dToPlanetSurface >= TRANSITION_FAR_START) {
// Полностью дальний диапазон
currentZNear = FAR_Z_NEAR;
currentZFar = FAR_Z_FAR;
}
else if (dToPlanetSurface > TRANSITION_MIDDLE_START) {
// Плавный переход от FAR к MIDDLE
const float transitionLength = TRANSITION_FAR_START - TRANSITION_MIDDLE_START;
float normalizedDist = (dToPlanetSurface - TRANSITION_MIDDLE_START) / transitionLength;
alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко)
currentZNear = FAR_Z_NEAR * (1.0f - alpha) + MIDDLE_Z_NEAR * alpha;
currentZFar = FAR_Z_FAR * (1.0f - alpha) + MIDDLE_Z_FAR * alpha;
}
else if (dToPlanetSurface > TRANSITION_NEAR_END) {
const float transitionLength = TRANSITION_MIDDLE_START - TRANSITION_NEAR_END;
float normalizedDist = (dToPlanetSurface - TRANSITION_NEAR_END) / transitionLength;
alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко)
currentZNear = MIDDLE_Z_NEAR * (1.0f - alpha) + NEAR_Z_NEAR * alpha;
currentZFar = MIDDLE_Z_FAR * (1.0f - alpha) + NEAR_Z_FAR * alpha;
}
else if (dToPlanetSurface > TRANSITION_SUPER_NEAR_END) {
const float transitionLength = TRANSITION_NEAR_END - TRANSITION_SUPER_NEAR_END;
float normalizedDist = (dToPlanetSurface - TRANSITION_SUPER_NEAR_END) / transitionLength;
alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко)
currentZNear = NEAR_Z_NEAR * (1.0f - alpha) + SUPER_NEAR_Z_NEAR * alpha;
currentZFar = NEAR_Z_FAR * (1.0f - alpha) + SUPER_NEAR_Z_FAR * alpha;
}
else {
currentZNear = SUPER_NEAR_Z_NEAR;
currentZFar = SUPER_NEAR_Z_FAR;
}
return { currentZNear, currentZFar };
}
PerlinNoise::PerlinNoise() {
p.resize(256);
std::iota(p.begin(), p.end(), 0);
// Перемешиваем для случайности (можно задать seed)
std::default_random_engine engine(77777);
std::shuffle(p.begin(), p.end(), engine);
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
}
PerlinNoise::PerlinNoise(uint64_t seed) {
p.resize(256);
std::iota(p.begin(), p.end(), 0);
// Перемешиваем для случайности (используем переданный seed)
std::default_random_engine engine(seed); // <-- Использование сида
std::shuffle(p.begin(), p.end(), engine);
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
}
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); }
float PerlinNoise::grad(int hash, float x, float y, float z) {
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
float PerlinNoise::noise(float x, float y, float z) {
int X = (int)floor(x) & 255;
int Y = (int)floor(y) & 255;
int Z = (int)floor(z) & 255;
x -= floor(x);
y -= floor(y);
z -= floor(z);
float u = fade(x);
float v = fade(y);
float w = fade(z);
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z;
int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;
return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)),
lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))),
lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)),
lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1))));
}
float PerlinNoise::getSurfaceHeight(Vector3f pos, float noiseCoeff) {
// Частота шума (чем больше, тем больше "холмов")
float frequency = 7.0f;
// Получаем значение шума (обычно от -1 до 1)
float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency);
// Переводим из диапазона [-1, 1] в [0, 1]
//noiseValue = (noiseValue + 1.0f) * 0.5f;
// Масштабируем: хотим отклонение от 1.0 до 1.1
// Значит амплитуда = 0.1
//float height = 1.0f; // * 0.2 даст вариацию высоты
float height = 1.0f + (noiseValue * noiseCoeff); // * 0.2 даст вариацию высоты
return height;
}
bool PlanetObject::planetIsFar()
{
const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET;
const float distanceToPlanetCenter = (planetWorldPosition - Environment::shipPosition).length();
const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS;
return distanceToPlanetSurface > 0.1*TRANSITION_MIDDLE_START;
}
/**
* @brief Находит ближайшую точку на плоскости, содержащей треугольник (A, B, C).
* Это проекция точки P на плоскость.
* @return Точка на плоскости (но не обязательно внутри треугольника).
*/
Vector3f projectPointOnPlane(const Vector3f& P, const Vector3f& A, const Vector3f& B, const Vector3f& C) {
Vector3f AB = B + A * (-1.0f);
Vector3f AC = C + A * (-1.0f);
Vector3f N = AB.cross(AC).normalized(); // Нормаль к плоскости треугольника
Vector3f AP = P + A * (-1.0f);
// Расстояние (со знаком) от P до плоскости: d = N . AP
float distance = N.dot(AP);
// Проекция: P_proj = P - d * N
Vector3f P_proj = P + N * (-distance);
return P_proj;
}
float PlanetObject::distanceToPlanetSurface()
{
// 1. Смещение позиции корабля в локальные координаты планеты
const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET;
Vector3f shipLocalPosition = Environment::shipPosition - planetWorldPosition;
// 2. Находим индекс треугольника, над которым находится корабль
// Используем максимальный LOD для наивысшей точности
std::vector<int> targetTriangles = triangleUnderCamera(currentLod);
if (targetTriangles.empty()) {
// Если не удалось найти треугольник (например, ошибка в triangleUnderCamera
// или корабль очень далеко от сетки), возвращаем старую оценку.
return (shipLocalPosition.length() - PLANET_RADIUS);
}
// Берем первый найденный треугольник (в пограничных случаях будет выбрана одна сторона)
int tri_index = targetTriangles[0];
// 3. Извлекаем вершины
const auto& posData = planetMeshLods[currentLod].vertexData.PositionData;
// Индексы в плоском массиве PositionData
size_t data_index = tri_index * 3;
if (data_index + 2 >= posData.size()) {
// Ошибка данных
return (shipLocalPosition.length() - PLANET_RADIUS);
}
const Vector3f& A = posData[data_index];
const Vector3f& B = posData[data_index + 1];
const Vector3f& C = posData[data_index + 2];
// 4. Находим ближайшую точку на плоскости треугольника
Vector3f P_proj = projectPointOnPlane(shipLocalPosition, A, B, C);
// 5. Проверяем, находится ли P_proj внутри треугольника (используя барицентрические координаты)
// ВАЖНОЕ УПРОЩЕНИЕ: В контексте геосферы, если корабль находится "над" треугольником
// (что гарантируется triangleUnderCamera), ближайшая точка на плоскости (P_proj)
// почти всегда будет лежать внутри треугольника A, B, C.
// Если P_ship находится очень далеко от поверхности (что обычно так),
// P_proj почти точно совпадет с P_closest.
// Для высокой точности, нам бы понадобилась полная функция closestPointOnTriangle,
// но для оценки расстояния в LOD-системах, где P_ship находится далеко от граней,
// P_closest = P_proj — это приемлемая аппроксимация.
// P_closest = closestPointOnTriangle(shipLocalPosition, A, B, C);
Vector3f P_closest = P_proj; // Используем аппроксимацию P_proj
// 6. Вычисляем точное расстояние
// Расстояние = |shipLocalPosition - P_closest|
float exactDistance = (shipLocalPosition - P_closest).length();
return exactDistance;
}
PlanetObject::PlanetObject()
: perlin(77777)
, colorPerlin(123123)
{
}
void PlanetObject::init() {
// 1. Инициализируем данные (генерация мешей)
planetData.init();
for (int i = 0; i < planetMeshLods.size(); i++) {
planetMeshLods[i] = generateSphere(i, 0.02f);
planetMeshLods[i].vertexData.Scale(PLANET_RADIUS);
planetMeshLods[i].vertexData.Move(PLANET_CENTER_OFFSET);
}
planetRenderStruct.data = planetMeshLods[currentLod].vertexData;
// 2. Забираем данные для VBO
// Берем максимальный LOD для начальной отрисовки
int lodIndex = planetData.getMaxLodIndex();
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
planetRenderStruct.RefreshVBO();
sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand.png", ""));
//sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
planetAtmosphereLod = generateSphere(5, 0);
planetAtmosphere.data = planetAtmosphereLod.vertexData;
planetAtmosphere.data.Scale(PLANET_RADIUS*1.03);
planetAtmosphere.data.Move(PLANET_CENTER_OFFSET);
planetAtmosphere.RefreshVBO();
// Атмосфера
planetAtmosphereRenderStruct.data = planetData.getAtmosphereLod().vertexData;
planetAtmosphereRenderStruct.RefreshVBO();
}
void PlanetObject::prepareDrawData() {
if (!drawDataDirty) return;
drawDataDirty = false;
}
void PlanetObject::update(float deltaTimeMs) {
// Получаем видимые треугольники, передавая позицию корабля
auto lr = planetData.getTrianglesUnderCamera(Environment::shipPosition);
int currentLod = planetData.getCurrentLodIndex();
const auto& fullMesh = planetData.getLodLevel(currentLod).vertexData;
planetRenderRedStruct.data.PositionData.clear();
planetRenderRedStruct.data.TexCoordData.clear();
// ... очистка остальных буферов ...
planetRenderYellowStruct.data.PositionData.clear();
planetRenderYellowStruct.data.TexCoordData.clear();
std::set<int> usedYellow;
// Заполняем красный (текущий треугольник под камерой)
for (int i : lr) {
planetRenderRedStruct.data.PositionData.push_back(fullMesh.PositionData[i * 3]);
planetRenderRedStruct.data.PositionData.push_back(fullMesh.PositionData[i * 3 + 1]);
planetRenderRedStruct.data.PositionData.push_back(fullMesh.PositionData[i * 3 + 2]);
planetRenderRedStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[i * 3]);
planetRenderRedStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[i * 3 + 1]);
planetRenderRedStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[i * 3 + 2]);
usedYellow.insert(i);
}
planetRenderRedStruct.RefreshVBO();
// Заполняем желтый (соседи)
for (int i : lr) {
auto neighbors = planetData.findNeighbors(i, currentLod);
for (int n : neighbors) {
if (usedYellow.count(n) == 0) {
usedYellow.insert(n);
planetRenderYellowStruct.data.PositionData.push_back(fullMesh.PositionData[n * 3]);
planetRenderYellowStruct.data.PositionData.push_back(fullMesh.PositionData[n * 3 + 1]);
planetRenderYellowStruct.data.PositionData.push_back(fullMesh.PositionData[n * 3 + 2]);
planetRenderYellowStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[n * 3]);
planetRenderYellowStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[n * 3 + 1]);
planetRenderYellowStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[n * 3 + 2]);
}
}
}
planetRenderYellowStruct.RefreshVBO();
}
void PlanetObject::draw(Renderer& renderer) {
prepareDrawData();
@ -323,7 +102,8 @@ namespace ZL {
renderer.EnableVertexAttribArray(vTexCoordName);
//renderer.EnableVertexAttribArray(vTexCoord3Name);
const auto zRange = calculateZRange(Environment::shipPosition);
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
@ -356,9 +136,6 @@ namespace ZL {
renderer.RenderUniform3fv("uLightDirection", &lightDirection_View.v[0]);
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]);
float dist = distanceToPlanetSurface();
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
//glEnable(GL_BLEND);
@ -383,7 +160,7 @@ namespace ZL {
glClear(GL_DEPTH_BUFFER_BIT);
//--------------------------
/*
if (planetRenderRedStruct.data.PositionData.size() > 0)
{
@ -433,7 +210,7 @@ namespace ZL {
renderer.shaderManager.PopShader();
CheckGlError();
}*/
}
drawAtmosphere(renderer);
}
@ -450,7 +227,8 @@ namespace ZL {
renderer.EnableVertexAttribArray(vNormalName);
const auto zRange = calculateZRange(Environment::shipPosition);
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
@ -473,13 +251,12 @@ namespace ZL {
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]);
float dist = distanceToPlanetSurface();
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
renderer.DrawVertexRenderStruct(planetAtmosphere);
renderer.DrawVertexRenderStruct(planetAtmosphereRenderStruct);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
renderer.PopMatrix();
@ -492,451 +269,9 @@ namespace ZL {
}
void PlanetObject::update(float deltaTimeMs) {
auto lr = triangleUnderCamera(currentLod);
planetRenderRedStruct.data.PositionData.clear();
planetRenderYellowStruct.data.PositionData.clear();
std::set<int> usedYellow;
for (int i : lr)
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
{
planetRenderRedStruct.data.PositionData.push_back(planetMeshLods[currentLod].vertexData.PositionData[i * 3]);
planetRenderRedStruct.data.PositionData.push_back(planetMeshLods[currentLod].vertexData.PositionData[i * 3 + 1]);
planetRenderRedStruct.data.PositionData.push_back(planetMeshLods[currentLod].vertexData.PositionData[i * 3 + 2]);
planetRenderRedStruct.data.TexCoordData.push_back(planetMeshLods[currentLod].vertexData.TexCoordData[i * 3]);
planetRenderRedStruct.data.TexCoordData.push_back(planetMeshLods[currentLod].vertexData.TexCoordData[i * 3 + 1]);
planetRenderRedStruct.data.TexCoordData.push_back(planetMeshLods[currentLod].vertexData.TexCoordData[i * 3 + 2]);
usedYellow.insert(i);
}
planetRenderRedStruct.RefreshVBO();
for (int i : lr)
{
auto neighbors = findNeighbors(i, currentLod);
for (int n : neighbors)
{
if (usedYellow.count(n) == 0)
{
usedYellow.insert(n);
planetRenderYellowStruct.data.PositionData.push_back(planetMeshLods[currentLod].vertexData.PositionData[n * 3]);
planetRenderYellowStruct.data.PositionData.push_back(planetMeshLods[currentLod].vertexData.PositionData[n * 3 + 1]);
planetRenderYellowStruct.data.PositionData.push_back(planetMeshLods[currentLod].vertexData.PositionData[n * 3 + 2]);
planetRenderYellowStruct.data.TexCoordData.push_back(planetMeshLods[currentLod].vertexData.TexCoordData[n * 3]);
planetRenderYellowStruct.data.TexCoordData.push_back(planetMeshLods[currentLod].vertexData.TexCoordData[n * 3 + 1]);
planetRenderYellowStruct.data.TexCoordData.push_back(planetMeshLods[currentLod].vertexData.TexCoordData[n * 3 + 2]);
}
}
}
planetRenderYellowStruct.RefreshVBO();
}
std::vector<Triangle> PlanetObject::subdivideTriangles(const std::vector<Triangle>& input) {
std::vector<Triangle> output;
for (const auto& t : input) {
// Вершины и их ID
const Vector3f& a = t.data[0];
const Vector3f& b = t.data[1];
const Vector3f& c = t.data[2];
const VertexID& id_a = t.ids[0];
const VertexID& id_b = t.ids[1];
const VertexID& id_c = t.ids[2];
// 1. Вычисляем середины (координаты)
Vector3f m_ab = ((a + b) * 0.5f).normalized();
Vector3f m_bc = ((b + c) * 0.5f).normalized();
Vector3f m_ac = ((a + c) * 0.5f).normalized();
// 2. Вычисляем ID новых вершин
VertexID id_mab = generateEdgeID(id_a, id_b);
VertexID id_mbc = generateEdgeID(id_b, id_c);
VertexID id_mac = generateEdgeID(id_a, id_c);
// 3. Формируем 4 новых треугольника
output.emplace_back(Triangle{ {a, m_ab, m_ac}, {id_a, id_mab, id_mac} }); // 0
output.emplace_back(Triangle{ {m_ab, b, m_bc}, {id_mab, id_b, id_mbc} }); // 1
output.emplace_back(Triangle{ {m_ac, m_bc, c}, {id_mac, id_mbc, id_c} }); // 2
output.emplace_back(Triangle{ {m_ab, m_bc, m_ac}, {id_mab, id_mbc, id_mac} }); // 3
}
return output;
}
Vector3f PlanetObject::calculateSurfaceNormal(Vector3f p_sphere, float noiseCoeff) {
// p_sphere - это нормализованный вектор (точка на идеальной сфере)
float theta = 0.01f; // Шаг для "щупанья" соседей (epsilon)
// Нам нужно найти два вектора, касательных к сфере в точке p_sphere.
// Для этого берем любой вектор (например UP), делаем Cross Product, чтобы получить касательную.
// Если p_sphere совпадает с UP, берем RIGHT.
Vector3f up = Vector3f(0.0f, 1.0f, 0.0f);
if (abs(p_sphere.dot(up)) > 0.99f) {
up = Vector3f(1.0f, 0.0f, 0.0f);
}
Vector3f tangentX = (up.cross(p_sphere)).normalized();
Vector3f tangentY = (p_sphere.cross(tangentX)).normalized();
// Точки на идеальной сфере со смещением
Vector3f p0_dir = p_sphere;
Vector3f p1_dir = (p_sphere + tangentX * theta).normalized();
Vector3f p2_dir = (p_sphere + tangentY * theta).normalized();
// Реальные точки на искаженной поверхности
// p = dir * height(dir)
Vector3f p0 = p0_dir * perlin.getSurfaceHeight(p0_dir, noiseCoeff);
Vector3f p1 = p1_dir * perlin.getSurfaceHeight(p1_dir, noiseCoeff);
Vector3f p2 = p2_dir * perlin.getSurfaceHeight(p2_dir, noiseCoeff);
// Вектора от центральной точки к соседям
Vector3f v1 = p1 - p0;
Vector3f v2 = p2 - p0;
// Нормаль - это перпендикуляр к этим двум векторам
// Порядок (v2, v1) или (v1, v2) зависит от системы координат,
// здесь подбираем так, чтобы нормаль смотрела наружу.
return (-v2.cross(v1)).normalized();
}
LodLevel PlanetObject::trianglesToVertices(const std::vector<Triangle>& geometry) {
LodLevel result;
result.vertexData.PositionData.reserve(geometry.size() * 3);
result.vertexData.NormalData.reserve(geometry.size() * 3);
result.vertexData.TexCoordData.reserve(geometry.size() * 3); // <-- РЕЗЕРВИРУЕМ
//buffer.TexCoord3Data.reserve(triangles.size() * 3); // <-- РЕЗЕРВИРУЕМ
// Стандартные UV-координаты для покрытия одного треугольника
// Покрывает текстурой всю грань.
const std::array<Vector2f, 3> triangleUVs = {
Vector2f(0.0f, 0.0f),
Vector2f(1.0f, 0.0f),
Vector2f(0.0f, 1.0f)
};
result.VertexIDs.reserve(geometry.size() * 3); // Заполняем ID здесь
for (const auto& t : geometry) {
for (int i = 0; i < 3; ++i) {
// Заполняем PositionData
result.vertexData.PositionData.push_back(t.data[i]);
// Заполняем NormalData (нормаль = нормализованная позиция на сфере)
result.vertexData.NormalData.push_back(t.data[i].normalized());
result.vertexData.TexCoordData.push_back(triangleUVs[i]);
// Заполняем VertexIDs
result.VertexIDs.push_back(t.ids[i]);
}
}
return result;
}
LodLevel PlanetObject::generateSphere(int subdivisions, float noiseCoeff) {
// 1. Исходный октаэдр и присвоение ID
std::vector<Triangle> geometry = {
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // 0
{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 1
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // 2
{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // 3
{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // 4
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // 5
{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 6
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}} // 7
};
// Присвоение ID исходным вершинам
for (auto& t : geometry) {
for (int i = 0; i < 3; i++) {
// Используем map для получения ID по чистым координатам (норм. == чистые)
t.ids[i] = initialVertexMap[t.data[i].normalized()];
}
}
// 2. ПРИМЕНЯЕМ ШУМ К ИСХОДНЫМ ВЕРШИНАМ
// ВАЖНО: Мы применяем шум ПОСЛЕ нормализации, но перед разбиением.
// Если вы хотите, чтобы шум был применен только к конечным вершинам,
// переместите этот блок после шага 3. Оставим, как в вашем коде.
// **ПРИМЕЧАНИЕ:** Если шум применен сейчас, то вершины на L>0 будут иметь
// координаты (m_ab = (a_noisy + b_noisy)*0.5).
// Если вы хотите, чтобы только финальные вершины имели шум, пропустите этот блок.
// В текущей задаче это не критично, так как мы используем ТОЛЬКО VertexID.
// 3. Разбиваем N раз (в subdivideTriangles генерируются ID новых вершин)
for (int i = 0; i < subdivisions; i++) {
geometry = subdivideTriangles(geometry);
}
// 4. Генерируем PositionData, NormalData и VertexIDs
LodLevel lodLevel = trianglesToVertices(geometry);
// 5. Создание V2T-Map на основе VertexIDs (ТОПОЛОГИЧЕСКИЙ КЛЮЧ)
V2TMap v2tMap;
const auto& finalVertexIDs = lodLevel.VertexIDs;
size_t num_triangles = geometry.size();
for (size_t i = 0; i < num_triangles; ++i) {
for (int j = 0; j < 3; ++j) {
VertexID v_id = finalVertexIDs[i * 3 + j];
// Если ключ уже есть, просто добавляем индекс
v2tMap[v_id].push_back((int)i);
}
}
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);
// Обратите внимание: NormalData остается (dir), как в вашем коде
lodLevel.vertexData.NormalData[i] = dir;
}
// 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);
}
}
return lodLevel;
}
std::vector<int> PlanetObject::findNeighbors(int index, int lod) {
// Проверка lod опущена для краткости...
// Получаем V2T-Map и VertexIDs для текущего LOD
const V2TMap& v2tMap = planetMeshLods[lod].v2tMap;
const auto& vertexIDs = planetMeshLods[lod].VertexIDs;
if ((index * 3 + 2) >= vertexIDs.size()) return {};
std::set<int> neighbors;
// 1. Проходим по 3 топологическим ID вершин искомого треугольника
for (int i = 0; i < 3; ++i) {
VertexID v_id = vertexIDs[index * 3 + i];
// 2. Ищем этот ID в Map
auto it = v2tMap.find(v_id);
if (it != v2tMap.end()) {
// 3. Добавляем все треугольники, связанные с v_id
for (int tri_index : it->second) {
if (tri_index != index) {
neighbors.insert(tri_index);
}
}
}
}
// Иерархические соседи (родители/дети) можно добавить по желанию
// ...
return std::vector<int>(neighbors.begin(), neighbors.end());
}
float check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon = 1e-6f) {
// Нормаль к плоскости, проходящей через O, V1 и V2
Vector3f N_plane = V1.cross(V2);
// Расстояние P до плоскости V1V2 (со знаком)
float sign_P = P.dot(N_plane);
// Расстояние V3 до плоскости V1V2 (со знаком)
float sign_V3 = V3.dot(N_plane);
// Если знаки совпадают, P находится на той же стороне, что и V3.
// Возвращаем произведение знаков.
return sign_P * sign_V3;
}
/**
* @brief Проверяет, находится ли точка P внутри сферического треугольника (V1, V2, V3).
* @param P Проверяемая точка (нормализованная).
* @param V1, V2, V3 Нормализованные вершины треугольника.
* @return true, если внутри или на границе.
*/
bool is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon) {
// Точка P должна быть на одной стороне от всех трех граней относительно третьей вершины.
// 1. Проверка относительно V1V2 (эталон V3)
if (check_spherical_side(V1, V2, P, V3, epsilon) < -epsilon) return false;
// 2. Проверка относительно V2V3 (эталон V1)
if (check_spherical_side(V2, V3, P, V1, epsilon) < -epsilon) return false;
// 3. Проверка относительно V3V1 (эталон V2)
if (check_spherical_side(V3, V1, P, V2, epsilon) < -epsilon) return false;
return true;
}
std::vector<int> 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<int> result;
// 1. Нормализация всех вершин и проверяемой точки для работы на сфере (радиус 1)
const Vector3f a = a_orig.normalized();
const Vector3f b = b_orig.normalized();
const Vector3f c = c_orig.normalized();
// Точка pxx должна быть спроецирована на сферу.
const Vector3f pxx_normalized = px_orig.normalized();
// 2. Вычисляем нормализованные середины (вершины внутренних треугольников)
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();
// 3. Определяем 4 подтреугольника
// Delta_0: (a, m_ab, m_ac)
if (is_inside_spherical_triangle(pxx_normalized, a, m_ab, m_ac, EPSILON)) {
result.push_back(0);
}
// Delta_1: (m_ab, b, m_bc)
if (is_inside_spherical_triangle(pxx_normalized, m_ab, b, m_bc, EPSILON)) {
result.push_back(1);
}
// Delta_2: (m_ac, m_bc, c)
if (is_inside_spherical_triangle(pxx_normalized, m_ac, m_bc, c, EPSILON)) {
result.push_back(2);
}
// Delta_3: (m_ab, m_bc, m_ac) - Внутренний
if (is_inside_spherical_triangle(pxx_normalized, m_ab, m_bc, m_ac, EPSILON)) {
result.push_back(3);
}
// Если исходный pxx точно лежит на луче из O(0,0,0) и внутри исходного треугольника,
// то pxx_normalized гарантированно попадет в один или несколько (на границе)
// из 4х подтреугольников.
return result;
}
std::vector<int> PlanetObject::triangleUnderCamera(int lod)
{
std::vector<int> r;
if (lod == 0)
{
if (Environment::shipPosition.v[1] >= 0)
{
if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] >= 0)
{
r.push_back(0);
}
if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] <= 0)
{
r.push_back(1);
}
if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] <= 0)
{
r.push_back(2);
}
if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] >= 0)
{
r.push_back(3);
}
}
if (Environment::shipPosition.v[1] <= 0)
{
if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] >= 0)
{
r.push_back(4);
}
if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] >= 0)
{
r.push_back(5);
}
if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] <= 0)
{
r.push_back(6);
}
if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] <= 0)
{
r.push_back(7);
}
}
}
else if (lod >= 1)
{
std::vector<int> r0 = triangleUnderCamera(lod-1);
for (int tri0 : r0)
{
Vector3f a = planetMeshLods[lod - 1].vertexData.PositionData[tri0 * 3];
Vector3f b = planetMeshLods[lod - 1].vertexData.PositionData[tri0 * 3+1];
Vector3f c = planetMeshLods[lod - 1].vertexData.PositionData[tri0 * 3+2];
std::vector<int> result = find_sub_triangle_spherical(a, b, c, Environment::shipPosition);
if (result.size() == 0)
{
std::cout << "Error!" << std::endl;
}
for (int trix : result)
{
r.push_back(tri0 * 4 + trix);
}
}
}
return r;
return planetData.distanceToPlanetSurface(viewerPosition);
}

View File

@ -13,107 +13,36 @@
#include <algorithm>
#include <map>
#include <set>
#include "Perlin.h"
#include "PlanetData.h"
namespace ZL {
using VertexID = std::string;
using V2TMap = std::map<VertexID, std::vector<int>>;
VertexID generateEdgeID(const VertexID& id1, const VertexID& id2);
struct Triangle
{
std::array<Vector3f, 3> data;
std::array<VertexID, 3> ids;
Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
: data{ p1, p2, p3 }
{
}
Triangle(std::array<Vector3f, 3> idata, std::array<VertexID, 3> iids)
: data{ idata }
, ids{ iids }
{
}
};
struct LodLevel
{
VertexDataStruct vertexData;
std::vector<VertexID> VertexIDs;
V2TMap v2tMap;
};
class PerlinNoise {
std::vector<int> p;
public:
PerlinNoise();
PerlinNoise(uint64_t seed);
float fade(float t);
float lerp(float t, float a, float b);
float grad(int hash, float x, float y, float z);
float noise(float x, float y, float z);
float getSurfaceHeight(Vector3f pos, float noiseCoeff);
};
class PlanetObject {
private:
PerlinNoise perlin;
PerlinNoise colorPerlin;
void prepareDrawData();
std::array<LodLevel, 6> planetMeshLods;
// Агрегация: логика и данные теперь здесь
PlanetData planetData;
// Данные только для рендеринга (OpenGL specific)
VertexRenderStruct planetRenderStruct;
VertexRenderStruct planetRenderRedStruct;
VertexRenderStruct planetRenderYellowStruct;
LodLevel planetAtmosphereLod;
VertexRenderStruct planetAtmosphere;
VertexRenderStruct planetAtmosphereRenderStruct;
std::shared_ptr<Texture> sandTexture;
std::map<Vector3f, VertexID> initialVertexMap = {
{{ 0.0f, 1.0f, 0.0f}, "A"},
{{ 0.0f, -1.0f, 0.0f}, "B"},
{{ 1.0f, 0.0f, 0.0f}, "C"},
{{-1.0f, 0.0f, 0.0f}, "D"},
{{ 0.0f, 0.0f, 1.0f}, "E"},
{{ 0.0f, 0.0f, -1.0f}, "F"}
};
bool drawDataDirty = true;
void prepareDrawData();
public:
PlanetObject();
void init();
void update(float deltaTimeMs);
void draw(Renderer& renderer);
void drawAtmosphere(Renderer& renderer);
bool planetIsFar();
float distanceToPlanetSurface();
private:
bool drawDataDirty = true;
int currentLod = planetMeshLods.size()-1;
std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles);
Vector3f calculateSurfaceNormal(Vector3f p_sphere, float noiseCoeff);
LodLevel trianglesToVertices(const std::vector<Triangle>& triangles);
LodLevel generateSphere(int subdivisions, float noiseCoeff);
std::pair<float, float> calculateZRange(const Vector3f& shipPosition);
std::vector<int> triangleUnderCamera(int lod);
std::vector<int> findNeighbors(int index, int lod);
float distanceToPlanetSurface(const Vector3f& viewerPosition);
};
} // namespace ZL