Minor refactoring
This commit is contained in:
1 parent
0a5a3b653d
commit
592e008914
43 files changed
+3589
-3594
No files matched your search
@@ -0,0 +1,493 @@
|
||||
#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.025f);
|
||||
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);
|
||||
}
|
||||
|
||||
const LodLevel& PlanetData::getLodLevel(int level) const {
|
||||
return planetMeshLods.at(level);
|
||||
}
|
||||
|
||||
const LodLevel& PlanetData::getLodLevelNoDist(int level) const {
|
||||
return planetMeshLodsNoDist.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
|
||||
{
|
||||
currentZNear = 16;
|
||||
currentZFar = 5000.f;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
float lowestDistance;
|
||||
|
||||
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;
|
||||
|
||||
lowestDistance = (shipLocalPosition - P_closest).length();
|
||||
|
||||
if (targetTriangles.size() <= 1)
|
||||
{
|
||||
return lowestDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < targetTriangles.size(); i++)
|
||||
{
|
||||
int tri_index = targetTriangles[i];
|
||||
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;
|
||||
|
||||
if (lowestDistance < (shipLocalPosition - P_closest).length())
|
||||
{
|
||||
lowestDistance = (shipLocalPosition - P_closest).length();
|
||||
}
|
||||
}
|
||||
|
||||
return lowestDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Ðåàëèçàöèÿ getTrianglesUnderCamera (áûâøàÿ triangleUnderCamera) ---
|
||||
// Âñïîìîãàòåëüíàÿ ðåêóðñèâíàÿ ôóíêöèÿ, ñêðûòàÿ îò ïóáëè÷íîãî API
|
||||
static std::vector<int> recursiveTriangleSearch(int lod, const Vector3f& pos, const std::array<LodLevel, MAX_LOD_LEVELS>& 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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::trianglesToVertices(const std::vector<Triangle>& 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);
|
||||
|
||||
const std::array<Vector2f, 3> triangleUVs = {
|
||||
Vector2f(0.5f, 1.0f),
|
||||
Vector2f(0.0f, 0.0f),
|
||||
Vector2f(1.0f, 0.0f)
|
||||
};
|
||||
|
||||
for (const auto& t : geometry) {
|
||||
// --- Âû÷èñëÿåì ëîêàëüíûé áàçèñ òðåóãîëüíèêà (êàê â 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) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
LodLevel PlanetData::generateSphere(int subdivisions, float noiseCoeff) {
|
||||
// 1. Èñõîäíûé îêòàýäð è ïðèñâîåíèå ID
|
||||
std::vector<Triangle> geometry = {
|
||||
// Âåðõíÿÿ ïîëóñôåðà (Y > 0)
|
||||
{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // 0
|
||||
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}}, // 1
|
||||
{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 2
|
||||
{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // 3
|
||||
// Íèæíÿÿ ïîëóñôåðà (Y < 0)
|
||||
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // 4
|
||||
{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // 5
|
||||
{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // 6
|
||||
{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}} // 7
|
||||
};
|
||||
|
||||
// Ïðèñâîåíèå ID èñõîäíûì âåðøèíàì
|
||||
for (auto& t : geometry) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Èñïîëüçóåì map äëÿ ïîëó÷åíèÿ ID ïî ÷èñòûì êîîðäèíàòàì (íîðì. == ÷èñòûå)
|
||||
t.ids[i] = initialVertexMap[t.data[i].normalized()];
|
||||
}
|
||||
}
|
||||
// 3. Ðàçáèâàåì N ðàç (â subdivideTriangles ãåíåðèðóþòñÿ ID íîâûõ âåðøèí)
|
||||
for (int i = 0; i < subdivisions; i++) {
|
||||
geometry = subdivideTriangles(geometry, noiseCoeff);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/ZLMath.h"
|
||||
#include "utils/Perlin.h"
|
||||
#include "render/Renderer.h"
|
||||
#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);
|
||||
|
||||
constexpr static int MAX_LOD_LEVELS = 6;
|
||||
//constexpr static int MAX_LOD_LEVELS = 3;
|
||||
|
||||
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
|
||||
{
|
||||
std::vector<Triangle> triangles;
|
||||
VertexDataStruct vertexData;
|
||||
std::vector<VertexID> VertexIDs;
|
||||
V2TMap v2tMap;
|
||||
|
||||
void Scale(float s)
|
||||
{
|
||||
vertexData.Scale(s);
|
||||
for (auto& t : triangles) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t.data[i] = t.data[i] * s;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Move(Vector3f pos)
|
||||
{
|
||||
vertexData.Move(pos);
|
||||
for (auto& t : triangles) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t.data[i] = t.data[i] + pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PlanetData {
|
||||
public:
|
||||
static const float PLANET_RADIUS;
|
||||
static const Vector3f PLANET_CENTER_OFFSET;
|
||||
|
||||
private:
|
||||
PerlinNoise perlin;
|
||||
PerlinNoise colorPerlin;
|
||||
|
||||
std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLods;
|
||||
std::array<LodLevel, MAX_LOD_LEVELS> planetMeshLodsNoDist;
|
||||
LodLevel planetAtmosphereLod;
|
||||
|
||||
int currentLod; // Ëîãè÷åñêèé òåêóùèé óðîâåíü äåòàëèçàöèè
|
||||
|
||||
std::map<Vector3f, VertexID> initialVertexMap;
|
||||
|
||||
// Âíóòðåííèå ìåòîäû ãåíåðàöèè
|
||||
std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles, 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& getLodLevelNoDist(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
|
||||
@@ -0,0 +1,570 @@
|
||||
#include "PlanetObject.h"
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include "render/OpenGlExtensions.h"
|
||||
#include "Environment.h"
|
||||
#include "StoneObject.h"
|
||||
|
||||
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();
|
||||
|
||||
|
||||
// 2. Вычисляем нормаль (ось Z).
|
||||
// Порядок cross product (AB x AC) определит "лицевую" сторону.
|
||||
Vector3f edge1 = vB - vA;
|
||||
Vector3f edge2 = vC - vA;
|
||||
Vector3f z_axis = edge1.cross(edge2).normalized();
|
||||
|
||||
// 3. Вычисляем ось Y (вертикаль).
|
||||
// В ортонормированном базисе Y всегда перпендикулярна Z и X.
|
||||
Vector3f y_axis = z_axis.cross(x_axis).normalized();
|
||||
|
||||
// 4. Формируем прямую матрицу поворота (Rotation/World Matrix).
|
||||
Matrix3f rot;
|
||||
|
||||
// Столбец 0: Ось X
|
||||
rot.m[0] = x_axis.v[0];
|
||||
rot.m[3] = x_axis.v[1];
|
||||
rot.m[6] = x_axis.v[2];
|
||||
|
||||
// Столбец 1: Ось Y
|
||||
rot.m[1] = y_axis.v[0];
|
||||
rot.m[4] = y_axis.v[1];
|
||||
rot.m[7] = y_axis.v[2];
|
||||
|
||||
// Столбец 2: Ось Z
|
||||
rot.m[2] = z_axis.v[0];
|
||||
rot.m[5] = z_axis.v[1];
|
||||
rot.m[8] = z_axis.v[2];
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
PlanetObject::PlanetObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PlanetObject::init() {
|
||||
// 1. Инициализируем данные (генерация мешей)
|
||||
planetData.init();
|
||||
|
||||
// 2. Забираем данные для VBO
|
||||
// Берем максимальный LOD для начальной отрисовки
|
||||
int lodIndex = planetData.getMaxLodIndex();
|
||||
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
|
||||
planetRenderStruct.RefreshVBO();
|
||||
|
||||
planetRenderStructCut.data = planetData.getLodLevelNoDist(lodIndex).vertexData;
|
||||
|
||||
planetRenderStructCut.data.PositionData.resize(3);
|
||||
|
||||
planetRenderStructCut.RefreshVBO();
|
||||
//sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand2.png", ""));
|
||||
sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand2.png", ""));
|
||||
stoneTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
|
||||
|
||||
// Атмосфера
|
||||
planetAtmosphereRenderStruct.data = planetData.getAtmosphereLod().vertexData;
|
||||
planetAtmosphereRenderStruct.RefreshVBO();
|
||||
|
||||
|
||||
planetStones = CreateStoneGroupData(778, planetData.getLodLevel(lodIndex));
|
||||
planetStones.inflate({ 0/*,1,2,3,4,5,6,7*/ });
|
||||
planetStonesToBakeRenderStruct.AssignFrom(planetStones.mesh);
|
||||
planetStonesToBakeRenderStruct.RefreshVBO();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PlanetObject::prepareDrawData() {
|
||||
if (!drawDataDirty) return;
|
||||
drawDataDirty = false;
|
||||
}
|
||||
|
||||
|
||||
void PlanetObject::update(float deltaTimeMs) {
|
||||
// 1. Получаем базовые треугольники под камерой
|
||||
auto lr = planetData.getTrianglesUnderCamera(Environment::shipPosition);
|
||||
int currentLod = planetData.getCurrentLodIndex();
|
||||
|
||||
// Временный вектор для сбора новых индексов
|
||||
std::vector<int> newIndices;
|
||||
std::set<int> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Сортируем новый список, чтобы порядок не влиял на сравнение
|
||||
std::sort(newIndices.begin(), newIndices.end());
|
||||
|
||||
// 3. Сравниваем с тем, что было нарисовано в прошлый раз
|
||||
if (newIndices != triangleIndicesToDraw) {
|
||||
// Обновляем список индексов (используем move для эффективности)
|
||||
triangleIndicesToDraw = std::move(newIndices);
|
||||
|
||||
// --- ОБНОВЛЯЕМ ЖЕЛТУЮ ЗОНУ (только когда изменился состав треугольников) ---
|
||||
const auto& fullMesh = planetData.getLodLevel(currentLod).vertexData;
|
||||
planetRenderYellowStruct.data.PositionData.clear();
|
||||
planetRenderYellowStruct.data.TexCoordData.clear();
|
||||
|
||||
for (int i : triangleIndicesToDraw) {
|
||||
// Копируем геометрию для подсветки
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
planetRenderYellowStruct.data.PositionData.push_back(fullMesh.PositionData[i * 3 + j]);
|
||||
planetRenderYellowStruct.data.TexCoordData.push_back(fullMesh.TexCoordData[i * 3 + j]);
|
||||
}
|
||||
}
|
||||
if (planetRenderYellowStruct.data.PositionData.size() > 0)
|
||||
{
|
||||
planetRenderYellowStruct.RefreshVBO();
|
||||
}
|
||||
|
||||
// --- ОБНОВЛЯЕМ КАМНИ (через новую структуру StoneGroup) ---
|
||||
if (triangleIndicesToDraw.size() > 0)
|
||||
{
|
||||
planetStones.inflate(triangleIndicesToDraw);
|
||||
// Используем AssignFrom, он внутри сам вызывает RefreshVBO
|
||||
planetStonesRenderStruct.AssignFrom(planetStones.mesh);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void PlanetObject::bakeStoneTexture(Renderer& renderer) {
|
||||
glViewport(0, 0, 512, 512);
|
||||
glClearColor(224 / 255.f, 201 / 255.f, 167 / 255.f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
static const std::string defaultShaderName2 = "planetBake";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
|
||||
|
||||
Triangle tr = planetData.getLodLevelNoDist(planetData.getCurrentLodIndex()).triangles[0];
|
||||
|
||||
// 1. Получаем матрицу вращения (оси в столбцах)
|
||||
Matrix3f mr = GetRotationForTriangle(tr);
|
||||
|
||||
// 2. Трансформируем вершины в локальное пространство экрана, чтобы найти габариты
|
||||
// Используем MultMatrixVector(Matrix, Vector).
|
||||
// Если ваша функция считает V * M, то передайте Inverse(mr).
|
||||
Vector3f rA = MultMatrixVector(mr, tr.data[0]);
|
||||
Vector3f rB = MultMatrixVector(mr, tr.data[1]);
|
||||
Vector3f rC = MultMatrixVector(mr, tr.data[2]);
|
||||
|
||||
// 3. Вычисляем реальные границы треугольника после поворота
|
||||
float minX = min(rA.v[0], min(rB.v[0], rC.v[0]));
|
||||
float maxX = max(rA.v[0], max(rB.v[0], rC.v[0]));
|
||||
float minY = min(rA.v[1], min(rB.v[1], rC.v[1]));
|
||||
float maxY = max(rA.v[1], max(rB.v[1], rC.v[1]));
|
||||
|
||||
|
||||
// Находим центр и размеры
|
||||
float width = maxX - minX;
|
||||
float height = maxY - minY;
|
||||
float centerX = (minX + maxX) * 0.5f;
|
||||
float centerY = (minY + maxY) * 0.5f;
|
||||
|
||||
width = width * 0.995;
|
||||
height = height * 0.995;
|
||||
|
||||
renderer.PushProjectionMatrix(
|
||||
centerX - width*0.5, centerX + width * 0.5,
|
||||
centerY - height * 0.5, centerY + height * 0.5,
|
||||
150, 200000);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
// Сдвигаем камеру по Z
|
||||
renderer.TranslateMatrix(Vector3f{ 0, 0, -45000 });
|
||||
|
||||
// Применяем вращение
|
||||
renderer.RotateMatrix(mr);
|
||||
|
||||
|
||||
// Извлекаем нормаль треугольника (это 3-й столбец нашей матрицы вращения)
|
||||
Vector3f planeNormal = { mr.m[2], mr.m[5], mr.m[8] };
|
||||
|
||||
renderer.RenderUniform3fv("uPlanePoint", &tr.data[0].v[0]);
|
||||
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)
|
||||
{
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(planetStonesToBakeRenderStruct);
|
||||
CheckGlError();
|
||||
}
|
||||
glDisable(GL_CULL_FACE); // Не забываем выключить, чтобы не сломать остальной рендер
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
|
||||
void PlanetObject::draw(Renderer& renderer) {
|
||||
|
||||
prepareDrawData();
|
||||
|
||||
{
|
||||
if (stoneMapFB == nullptr)
|
||||
{
|
||||
stoneMapFB = std::make_unique<FrameBuffer>(512, 512);
|
||||
}
|
||||
stoneMapFB->Bind();
|
||||
|
||||
bakeStoneTexture(renderer);
|
||||
|
||||
stoneMapFB->Unbind();
|
||||
|
||||
}
|
||||
|
||||
//bakeStoneTexture(renderer);
|
||||
|
||||
glViewport(0, 0, Environment::width, Environment::height);
|
||||
//--------------------------
|
||||
|
||||
|
||||
drawPlanet(renderer);
|
||||
//drawYellowZone(renderer);
|
||||
drawStones(renderer);
|
||||
|
||||
drawAtmosphere(renderer);
|
||||
}
|
||||
|
||||
void PlanetObject::drawPlanet(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName = "planetLand";
|
||||
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray("vTangent");
|
||||
renderer.EnableVertexAttribArray("vBinormal");
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
|
||||
renderer.RenderUniform1i("Texture", 0);
|
||||
renderer.RenderUniform1i("BakedTexture", 1);
|
||||
|
||||
|
||||
Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0]; // Берем базовый треугольник
|
||||
Matrix3f mr = GetRotationForTriangle(tr); // Та же матрица, что и при запекании
|
||||
|
||||
// Позиция камеры (корабля) в мире
|
||||
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
|
||||
|
||||
//renderer.RenderUniform1f("uHeightScale", 0.03f);
|
||||
renderer.RenderUniform1f("uHeightScale", 0.0f);
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||||
|
||||
renderer.DrawVertexRenderStruct(planetRenderStruct);
|
||||
CheckGlError();
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray("vTangent");
|
||||
renderer.DisableVertexAttribArray("vBinormal");
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PlanetObject::drawStones(Renderer& renderer)
|
||||
{
|
||||
static const std::string defaultShaderName2 = "planetStone";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName2);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
if (planetStonesRenderStruct.data.PositionData.size() > 0)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(planetStonesRenderStruct);
|
||||
CheckGlError();
|
||||
}
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void PlanetObject::drawYellowZone(Renderer& renderer)
|
||||
{
|
||||
|
||||
static const std::string defaultShaderName = "planetLand";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vColorName = "vColor";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
static const std::string vTexCoordName = "vTexCoord";
|
||||
static const std::string textureUniformName = "Texture";
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
if (planetRenderYellowStruct.data.PositionData.size() > 0)
|
||||
{
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vColorName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
renderer.EnableVertexAttribArray("vTangent");
|
||||
renderer.EnableVertexAttribArray("vBinormal");
|
||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||||
|
||||
renderer.RenderUniform3fv("uViewPos", &Environment::shipPosition.v[0]);
|
||||
|
||||
Vector3f color2 = { 1.0, 1.0, 0.0 };
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||||
|
||||
renderer.RenderUniform3fv("uColor", &color2.v[0]);
|
||||
renderer.DrawVertexRenderStruct(planetRenderYellowStruct);
|
||||
//glDisable(GL_BLEND);
|
||||
CheckGlError();
|
||||
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray("vTangent");
|
||||
renderer.DisableVertexAttribArray("vBinormal");
|
||||
renderer.DisableVertexAttribArray(vColorName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void PlanetObject::drawAtmosphere(Renderer& renderer) {
|
||||
static const std::string defaultShaderName = "defaultAtmosphere";
|
||||
static const std::string vPositionName = "vPosition";
|
||||
static const std::string vNormalName = "vNormal";
|
||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
renderer.shaderManager.PushShader(defaultShaderName);
|
||||
renderer.EnableVertexAttribArray(vPositionName);
|
||||
renderer.EnableVertexAttribArray(vNormalName);
|
||||
|
||||
|
||||
float dist = planetData.distanceToPlanetSurface(Environment::shipPosition);
|
||||
auto zRange = planetData.calculateZRange(dist);
|
||||
const float currentZNear = zRange.first;
|
||||
const float currentZFar = zRange.second;
|
||||
|
||||
// 2. Применяем динамическую матрицу проекции
|
||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
||||
currentZNear, currentZFar);
|
||||
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
||||
|
||||
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
|
||||
|
||||
Vector3f color = { 0.0, 0.5, 1.0 };
|
||||
|
||||
renderer.RenderUniform3fv("uColor", &color.v[0]);
|
||||
|
||||
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]);
|
||||
|
||||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
|
||||
|
||||
renderer.DrawVertexRenderStruct(planetAtmosphereRenderStruct);
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
|
||||
renderer.DisableVertexAttribArray(vNormalName);
|
||||
renderer.DisableVertexAttribArray(vPositionName);
|
||||
renderer.shaderManager.PopShader();
|
||||
CheckGlError();
|
||||
|
||||
}
|
||||
|
||||
float PlanetObject::distanceToPlanetSurface(const Vector3f& viewerPosition)
|
||||
{
|
||||
return planetData.distanceToPlanetSurface(viewerPosition);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "utils/Perlin.h"
|
||||
#include "PlanetData.h"
|
||||
#include "StoneObject.h"
|
||||
#include "render/FrameBuffer.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
class PlanetObject {
|
||||
public:
|
||||
// Агрегация: логика и данные теперь здесь
|
||||
PlanetData planetData;
|
||||
|
||||
// Данные только для рендеринга (OpenGL specific)
|
||||
VertexRenderStruct planetRenderStruct;
|
||||
VertexRenderStruct planetRenderStructCut;
|
||||
VertexRenderStruct planetRenderYellowStruct;
|
||||
VertexRenderStruct planetAtmosphereRenderStruct;
|
||||
VertexRenderStruct planetStonesRenderStruct;
|
||||
VertexRenderStruct planetStonesToBakeRenderStruct;
|
||||
StoneGroup planetStones;
|
||||
|
||||
std::vector<int> triangleIndicesToDraw;
|
||||
|
||||
std::shared_ptr<Texture> sandTexture;
|
||||
std::shared_ptr<Texture> stoneTexture;
|
||||
|
||||
bool drawDataDirty = true;
|
||||
void prepareDrawData();
|
||||
|
||||
std::unique_ptr<FrameBuffer> stoneMapFB;
|
||||
|
||||
public:
|
||||
PlanetObject();
|
||||
|
||||
void init();
|
||||
void update(float deltaTimeMs);
|
||||
void bakeStoneTexture(Renderer& renderer);
|
||||
void draw(Renderer& renderer);
|
||||
void drawStones(Renderer& renderer);
|
||||
void drawPlanet(Renderer& renderer);
|
||||
void drawYellowZone(Renderer& renderer);
|
||||
void drawAtmosphere(Renderer& renderer);
|
||||
|
||||
|
||||
float distanceToPlanetSurface(const Vector3f& viewerPosition);
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,276 @@
|
||||
#include "StoneObject.h"
|
||||
|
||||
#include "utils/Utils.h"
|
||||
#include <GL/gl.h>
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include "render/Renderer.h"
|
||||
#include "PlanetData.h"
|
||||
|
||||
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]
|
||||
float getRandomFloat(std::mt19937& gen, float min, float max) {
|
||||
std::uniform_real_distribution<> distrib(min, max);
|
||||
return static_cast<float>(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 äëÿ ïîëó÷åíèÿ ðàâíîìåðíîãî ðàñïðåäåëåíèÿ
|
||||
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;
|
||||
Vector3f p3_term = t.data[2] * c;
|
||||
|
||||
return p1_term + p2_term + p3_term;
|
||||
}
|
||||
|
||||
|
||||
// Èêîñàýäð (íà îñíîâå çîëîòîãî ñå÷åíèÿ phi)
|
||||
// Êîîðäèíàòû ìîãóò áûòü âû÷èñëåíû çàðàíåå äëÿ êîíñòàíòíîãî èêîñàýäðà.
|
||||
// Çäåñü òîëüêî îáúÿâëåíèå, ÷òîáû ïîêàçàòü èäåþ.
|
||||
|
||||
VertexDataStruct CreateBaseConvexPolyhedron(uint64_t seed) {
|
||||
|
||||
// const size_t SUBDIVISION_LEVEL = 1; // Óðîâåíü ïîäðàçäåëåíèÿ (äëÿ áîëåå êðóãëîãî êàìíÿ, ïîêà îïóñòèì)
|
||||
|
||||
std::mt19937 engine(static_cast<unsigned int>(seed));
|
||||
|
||||
// Çîëîòîå ñå÷åíèå
|
||||
const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;
|
||||
|
||||
// 12 âåðøèí èêîñàýäðà
|
||||
std::vector<Vector3f> 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 òðåóãîëüíûõ ãðàíåé (èíäåêñû âåðøèí)
|
||||
std::vector<std::array<int, 3>> faces = {
|
||||
// 5 òðåóãîëüíèêîâ âîêðóã âåðøèíû 0
|
||||
{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
|
||||
// 5 ñìåæíûõ ïîëîñ
|
||||
{1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
|
||||
// 5 òðåóãîëüíèêîâ âîêðóã âåðøèíû 3
|
||||
{3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
|
||||
// 5 ñìåæíûõ ïîëîñ
|
||||
{4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
|
||||
};
|
||||
|
||||
// 1. Íîðìàëèçàöèÿ è Âîçìóùåíèå (Perturbation)
|
||||
for (Vector3f& v : initialVertices) {
|
||||
v = v.normalized() * StoneParams::BASE_SCALE; // Íîðìàëèçàöèÿ ê ñôåðå ðàäèóñà BASE_SCALE
|
||||
|
||||
// Ðàäèàëüíîå âîçìóùåíèå:
|
||||
float perturbation = getRandomFloat(engine, StoneParams::MIN_PERTURBATION, StoneParams::MAX_PERTURBATION);
|
||||
v = v * (1.0f + perturbation);
|
||||
}
|
||||
|
||||
// 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); // Ïðîñòîé ïðèìåð êîìáèíèðîâàíèÿ
|
||||
qFinal = slerp(qFinal, qz, 0.5f).normalized();
|
||||
Matrix3f rotationMatrix = QuatToMatrix(qFinal);
|
||||
|
||||
for (Vector3f& v : initialVertices) {
|
||||
v = MultMatrixVector(rotationMatrix, v);
|
||||
}
|
||||
|
||||
// 3. Ñãëàæåííûå Íîðìàëè è Ôîðìèðîâàíèå Mesh
|
||||
VertexDataStruct result;
|
||||
// Êàðòà äëÿ íàêîïëåíèÿ íîðìàëåé ïî óíèêàëüíûì ïîçèöèÿì âåðøèí
|
||||
// (Òðåáóåò îïðåäåëåííîãî îïåðàòîðà < äëÿ Vector3f â ZLMath.h, êîòîðûé ó âàñ åñòü)
|
||||
std::map<Vector3f, Vector3f> 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)
|
||||
Vector3f faceNormal = (p2 - p1).cross(p3 - p1).normalized();
|
||||
|
||||
smoothNormalsMap[p1] = smoothNormalsMap[p1] + faceNormal;
|
||||
smoothNormalsMap[p2] = smoothNormalsMap[p2] + faceNormal;
|
||||
smoothNormalsMap[p3] = smoothNormalsMap[p3] + faceNormal;
|
||||
}
|
||||
|
||||
// Íîðìàëèçàöèÿ íàêîïëåííûõ íîðìàëåé
|
||||
for (auto& pair : smoothNormalsMap) {
|
||||
pair.second = pair.second.normalized();
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
Vector3f uAxis = (p2 - p1).normalized();
|
||||
Vector3f vRaw = p3 - p1;
|
||||
|
||||
// Ïðîåêöèÿ vRaw íà uAxis
|
||||
float uProjLen = vRaw.dot(uAxis);
|
||||
|
||||
// Âåêòîð V ïåðïåíäèêóëÿðíûé U: vRaw - uProj
|
||||
Vector3f vAxisRaw = vRaw - (uAxis * uProjLen);
|
||||
|
||||
// Äëèíà îñè V
|
||||
float vLen = vAxisRaw.length();
|
||||
|
||||
// Íîðìàëèçîâàííàÿ îñü V
|
||||
Vector3f vAxis = vAxisRaw.normalized();
|
||||
|
||||
// Êîîðäèíàòû (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
|
||||
|
||||
// Íàõîäèì ìàêñèìàëüíûé ðàçìåð, ÷òîáû ìàñøòàáèðîâàòü â [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 });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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) {
|
||||
const Triangle& tri = planetLodLevel.triangles[tIdx];
|
||||
std::mt19937 engine(static_cast<unsigned int>(globalSeed));
|
||||
|
||||
for (int i = 0; i < StoneParams::STONES_PER_TRIANGLE; ++i) {
|
||||
StoneInstance instance;
|
||||
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)
|
||||
};
|
||||
|
||||
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));
|
||||
instance.rotation = slerp(slerp(qx, qy, 0.5f), qz, 0.5f).normalized();
|
||||
|
||||
group.allInstances[tIdx].push_back(instance);
|
||||
}
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
void StoneGroup::inflate(const std::vector<int>& triangleIndices) {
|
||||
// 1. Î÷èùàåì òåêóùèé ìåø ïåðåä çàïîëíåíèåì
|
||||
mesh.PositionData.clear();
|
||||
mesh.NormalData.clear();
|
||||
mesh.TexCoordData.clear();
|
||||
|
||||
static VertexDataStruct baseStone = CreateBaseConvexPolyhedron(1337);
|
||||
|
||||
// 2. Íàïîëíÿåì ìåø òîëüêî äëÿ âèäèìûõ òðåóãîëüíèêîâ
|
||||
for (int tIdx : triangleIndices) {
|
||||
if (tIdx >= allInstances.size()) continue;
|
||||
|
||||
for (const auto& inst : allInstances[tIdx]) {
|
||||
Matrix3f rotMat = QuatToMatrix(inst.rotation);
|
||||
|
||||
for (size_t j = 0; j < baseStone.PositionData.size(); ++j) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ZL
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "utils/ZLMath.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "PlanetData.h"
|
||||
|
||||
namespace ZL {
|
||||
|
||||
struct StoneParams
|
||||
{
|
||||
static const float BASE_SCALE; // Îáùèé ðàçìåð êàìíÿ
|
||||
static const float MIN_AXIS_SCALE; // Ìèíèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
static const float MAX_AXIS_SCALE; // Ìàêñèìàëüíîå ðàñòÿæåíèå/ñæàòèå ïî îñè
|
||||
static const float MIN_PERTURBATION; // Ìèíèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
static const float MAX_PERTURBATION; // Ìàêñèìàëüíîå ðàäèàëüíîå âîçìóùåíèå âåðøèíû
|
||||
static const int STONES_PER_TRIANGLE;
|
||||
|
||||
};
|
||||
|
||||
struct StoneInstance {
|
||||
uint64_t seed;
|
||||
Vector3f position;
|
||||
Vector3f scale;
|
||||
Vector4f rotation;
|
||||
};
|
||||
|
||||
struct StoneGroup {
|
||||
// mesh.PositionData è ïðî÷èå áóäóò çàïîëíÿòüñÿ â inflate()
|
||||
VertexDataStruct mesh;
|
||||
|
||||
// Âíåøíèé âåêòîð — èíäåêñ òðåóãîëüíèêà ïëàíåòû,
|
||||
// âíóòðåííèé — ñïèñîê êàìíåé íà ýòîì òðåóãîëüíèêå
|
||||
std::vector<std::vector<StoneInstance>> allInstances;
|
||||
|
||||
// Î÷èùàåò ñòàðóþ ãåîìåòðèþ è ãåíåðèðóåò íîâóþ äëÿ óêàçàííûõ èíäåêñîâ
|
||||
void inflate(const std::vector<int>& triangleIndices);
|
||||
};
|
||||
|
||||
// Òåïåðü âîçâðàùàåò çàãîòîâêó ñî âñåìè ïàðàìåòðàìè, íî áåç òÿæåëîãî ìåøà
|
||||
StoneGroup CreateStoneGroupData(uint64_t globalSeed, const LodLevel& planetLodLevel);
|
||||
|
||||
} // namespace ZL
|
||||
Reference in new issue
Block a user