space-game001/src/planet/PlanetData.cpp
2026-01-09 20:49:03 +03:00

434 lines
15 KiB
C++

#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) ---
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.01f);
planetMeshLods[i] = generateSphere(i, 0.f);
planetMeshLods[i].Scale(PLANET_RADIUS);
planetMeshLods[i].Move(PLANET_CENTER_OFFSET);
}
planetAtmosphereLod = generateSphere(5, 0);
planetAtmosphereLod.Scale(PLANET_RADIUS * 1.03);
planetAtmosphereLod.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 > 2000)
{
currentZNear = 1000;
currentZFar = currentZNear * 100;
}
else if (dToPlanetSurface > 1200)
{
currentZNear = 500;
currentZFar = currentZNear * 100;
}
else if (dToPlanetSurface > 650)
{
currentZNear = 250;
currentZFar = currentZNear * 100;
}
else if (dToPlanetSurface > 160)
{
currentZNear = 125;
currentZFar = currentZNear * 150;
}
else if (dToPlanetSurface > 100)
{
currentZNear = 65;
currentZFar = currentZNear * 170;
}
else if (dToPlanetSurface > 40)
{
currentZNear = 32;
currentZFar = 10000.f;
}
else if (dToPlanetSurface > 20)
{
currentZNear = 16;
currentZFar = 5000.f;
}
else if (dToPlanetSurface > 5)
{
currentZNear = 8;
currentZFar = 2500.f;
}
else
{
currentZNear = 4;
currentZFar = 1250.f;
}
return { currentZNear, currentZFar };
}
float PlanetData::distanceToPlanetSurfaceFast(const Vector3f& viewerPosition)
{
Vector3f shipLocalPosition = viewerPosition - PLANET_CENTER_OFFSET;
return sqrt(shipLocalPosition.squaredNorm()) - PLANET_RADIUS;
}
std::vector<int> PlanetData::getBestTriangleUnderCamera(const Vector3f& viewerPosition) {
const LodLevel& finalLod = planetMeshLods[currentLod]; // Ðàáîòàåì ñ òåêóùèì àêòèâíûì LOD
Vector3f targetDir = (viewerPosition - PLANET_CENTER_OFFSET).normalized();
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;
}
}
if (bestTriangle == -1) return {};
return { bestTriangle };
}
std::vector<int> PlanetData::getTrianglesUnderCameraNew2(const Vector3f& viewerPosition) {
const LodLevel& finalLod = planetMeshLods[currentLod];
Vector3f shipLocal = viewerPosition - PLANET_CENTER_OFFSET;
float currentDist = shipLocal.norm();
Vector3f targetDir = shipLocal.normalized();
// Æåëàåìûé ðàäèóñ ïîêðûòèÿ íà ïîâåðõíîñòè ïëàíåòû (â ìåòðàõ/åäèíèöàõ äâèæêà)
// Ïîäáåðè ýòî çíà÷åíèå òàê, ÷òîáû êàìíè âîêðóã êîðàáëÿ âñåãäà áûëè âèäíû.
const float desiredCoverageRadius = 3000.0f;
// Âû÷èñëÿåì ïîðîã êîñèíóñà íà îñíîâå æåëàåìîãî ðàäèóñà è òåêóùåãî ðàññòîÿíèÿ.
// ×åì ìû äàëüøå (currentDist áîëüøå), òåì ìåíüøå äîëæåí áûòü óãîë îòêëîíåíèÿ
// îò íîðìàëè, ÷òîáû çàõâàòèòü òó æå ïëîùàäü.
float angle = atan2(desiredCoverageRadius, currentDist);
float searchThreshold = cos(angle);
// Îãðàíè÷èòåëü, ÷òîáû íå çàõâàòèòü âñþ ïëàíåòó èëè âîîáùå íè÷åãî
searchThreshold = std::clamp(searchThreshold, 0.90f, 0.9999f);
std::vector<int> result;
for (int i = 0; i < (int)finalLod.triangles.size(); ++i) {
// Èñïîëüçóåì öåíòðîèä (ìîæíî êýøèðîâàòü â LodLevel äëÿ ñêîðîñòè)
Vector3f triDir = (finalLod.triangles[i].data[0] +
finalLod.triangles[i].data[1] +
finalLod.triangles[i].data[2]).normalized();
if (targetDir.dot(triDir) > searchThreshold) {
result.push_back(i);
}
}
if (result.empty()) return getBestTriangleUnderCamera(viewerPosition);
return result;
}
std::vector<Triangle> PlanetData::subdivideTriangles(const std::vector<Triangle>& input, float noiseCoeff) {
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();
//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);
VertexID id_mbc = generateEdgeID(id_b, id_c);
VertexID id_mac = generateEdgeID(id_a, id_c);
// 3. Ôîðìèðóåì 4 íîâûõ òðåóãîëüíèêà
output.emplace_back(Triangle{ {a, pm_ab, pm_ac}, {id_a, id_mab, id_mac} }); // 0
output.emplace_back(Triangle{ {pm_ab, b, pm_bc}, {id_mab, id_b, id_mbc} }); // 1
output.emplace_back(Triangle{ {pm_ac, pm_bc, c}, {id_mac, id_mbc, id_c} }); // 2
output.emplace_back(Triangle{ {pm_ab, pm_bc, pm_ac}, {id_mab, id_mbc, id_mac} }); // 3
}
return output;
}
LodLevel PlanetData::createLodLevel(const std::vector<Triangle>& geometry) {
LodLevel result;
result.triangles = geometry;
size_t vertexCount = geometry.size() * 3;
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.ColorData.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);
lod.vertexData.ColorData.reserve(vertexCount);
const std::array<Vector2f, 3> triangleUVs = {
Vector2f(0.5f, 1.0f),
Vector2f(0.0f, 0.0f),
Vector2f(1.0f, 0.0f)
};
const Vector3f colorPinkish = { 1.0f, 0.8f, 0.82f }; // Ñëåãêà ðîçîâàòûé
const Vector3f colorYellowish = { 1.0f, 1.0f, 0.75f }; // Ñëåãêà æåëòîâàòûé
const float colorFrequency = 4.0f; // Ìàñøòàá ïÿòåí
for (const auto& t : lod.triangles) {
// --- Âû÷èñëÿåì ëîêàëüíûé áàçèñ òðåóãîëüíèêà (êàê â GetRotationForTriangle) ---
Vector3f vA = t.data[0];
Vector3f vB = t.data[1];
Vector3f vC = t.data[2];
Vector3f x_axis = (vC - vB).normalized(); // Íàïðàâëåíèå U
Vector3f edge1 = vB - vA;
Vector3f edge2 = vC - vA;
Vector3f z_axis = edge1.cross(edge2).normalized(); // Íîðìàëü ïëîñêîñòè
// Ïðîâåðêà íàïðàâëåíèÿ íîðìàëè íàðóæó (îò öåíòðà ïëàíåòû)
Vector3f centerToTri = (vA + vB + vC).normalized();
if (z_axis.dot(centerToTri) < 0) {
z_axis = z_axis * -1.0f;
}
Vector3f y_axis = z_axis.cross(x_axis).normalized(); // Íàïðàâëåíèå V
for (int i = 0; i < 3; ++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);
// Èñïîëüçóåì îäèí øóì äëÿ âûáîðà ìåæäó ðîçîâûì è æåëòûì
Vector3f dir = t.data[i].normalized();
float blendFactor = colorPerlin.noise(
dir(0) * colorFrequency,
dir(1) * colorFrequency,
dir(2) * colorFrequency
);
// Ïðèâîäèì øóì èç äèàïàçîíà [-1, 1] â [0, 1]
blendFactor = blendFactor * 0.5f + 0.5f;
// Ëèíåéíàÿ èíòåðïîëÿöèÿ ìåæäó äâóìÿ öâåòàìè
Vector3f finalColor;
finalColor = colorPinkish + blendFactor * (colorYellowish - colorPinkish);
lod.vertexData.ColorData.push_back(finalColor);
}
}
}
LodLevel PlanetData::generateSphere(int subdivisions, float noiseCoeff) {
const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;
// 12 áàçîâûõ âåðøèí èêîñàýäðà
std::vector<Vector3f> icosaVertices = {
{-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}
};
// Íîðìàëèçóåì âåðøèíû
for (auto& v : icosaVertices) v = v.normalized();
// 20 ãðàíåé èêîñàýäðà
struct IndexedTri { int v1, v2, v3; };
std::vector<IndexedTri> faces = {
{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
{1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
{3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
{4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
};
std::vector<Triangle> geometry;
for (auto& f : faces) {
Triangle tri;
tri.data[0] = icosaVertices[f.v1];
tri.data[1] = icosaVertices[f.v2];
tri.data[2] = icosaVertices[f.v3];
// Ãåíåðèðóåì ID äëÿ áàçîâûõ âåðøèí (ìîæíî èñïîëüçîâàòü èõ êîîðäèíàòû)
for (int i = 0; i < 3; ++i) {
tri.ids[i] = std::to_string(tri.data[i](0)) + "_" +
std::to_string(tri.data[i](1)) + "_" +
std::to_string(tri.data[i](2));
}
geometry.push_back(tri);
}
// 3. Ðàçáèâàåì N ðàç
for (int i = 0; i < subdivisions; i++) {
geometry = subdivideTriangles(geometry, 0.0f); // Øóì ïîêà èãíîðèðóåì
}
// 4. Ñîçäàåì LodLevel è çàïîëíÿåì òîïîëîãèþ (v2tMap)
LodLevel lodLevel = createLodLevel(geometry);
// Ïåðåñîáèðàåì v2tMap (îíà êðèòè÷íà äëÿ ðåëàêñàöèè)
lodLevel.v2tMap.clear();
for (size_t i = 0; i < geometry.size(); ++i) {
for (int j = 0; j < 3; ++j) {
lodLevel.v2tMap[geometry[i].ids[j]].push_back((int)i);
}
}
// 5. Ïðèìåíÿåì èòåðàòèâíóþ ðåëàêñàöèþ (Lloyd-like)
// 5-10 èòåðàöèé äîñòàòî÷íî äëÿ îòëè÷íîé ñåòêè
applySphericalRelaxation(lodLevel, 8);
// 6. Íàêëàäûâàåì øóì è îáíîâëÿåì àòðèáóòû
// ... (òâîé êîä íàëîæåíèÿ øóìà ÷åðåç Perlin)
recalculateMeshAttributes(lodLevel);
return lodLevel;
}
void PlanetData::applySphericalRelaxation(LodLevel& lod, int iterations) {
for (int iter = 0; iter < iterations; ++iter) {
std::map<VertexID, Vector3f> newPositions;
for (auto const& [vID, connectedTris] : lod.v2tMap) {
Vector3f centroid(0, 0, 0);
// Íàõîäèì ñðåäíþþ òî÷êó ñðåäè öåíòðîâ âñåõ ñîñåäíèõ òðåóãîëüíèêîâ
for (int triIdx : connectedTris) {
const auto& tri = lod.triangles[triIdx];
Vector3f faceCenter = (tri.data[0] + tri.data[1] + tri.data[2]) * (1.0f / 3.0f);
centroid = centroid + faceCenter;
}
centroid = centroid * (1.0f / (float)connectedTris.size());
// Ïðîåöèðóåì îáðàòíî íà åäèíè÷íóþ ñôåðó
newPositions[vID] = centroid.normalized();
}
// Ñèíõðîíèçèðóåì äàííûå â òðåóãîëüíèêàõ
for (auto& tri : lod.triangles) {
for (int i = 0; i < 3; ++i) {
tri.data[i] = newPositions[tri.ids[i]];
}
}
}
}
}