434 lines
15 KiB
C++
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]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |