space-game001/PlanetObject.cpp
Vladislav Khorev f197dfcf02 Clean up some
2025-12-29 20:40:13 +03:00

570 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "PlanetObject.h"
#include <random>
#include <cmath>
#include "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