578 lines
22 KiB
C++
578 lines
22 KiB
C++
#include "PlanetObject.h"
|
||
#include <random>
|
||
#include <cmath>
|
||
#include "render/OpenGlExtensions.h"
|
||
#include "Environment.h"
|
||
#include "StoneObject.h"
|
||
#include "utils/TaskManager.h"
|
||
|
||
namespace ZL {
|
||
|
||
#if defined EMSCRIPTEN || defined __ANDROID__
|
||
using std::min;
|
||
using std::max;
|
||
#endif
|
||
|
||
extern const char* CONST_ZIP_FILE;
|
||
|
||
Matrix3f GetRotationForTriangle(const Triangle& tri) {
|
||
|
||
|
||
Vector3f vA = tri.data[0];
|
||
Vector3f vB = tri.data[1];
|
||
Vector3f vC = tri.data[2];
|
||
|
||
// 1. Вычисляем ось X (горизонталь).
|
||
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.data()[0] = x_axis.data()[0];
|
||
rot.data()[3] = x_axis.data()[1];
|
||
rot.data()[6] = x_axis.data()[2];
|
||
|
||
// Столбец 1: Ось Y
|
||
rot.data()[1] = y_axis.data()[0];
|
||
rot.data()[4] = y_axis.data()[1];
|
||
rot.data()[7] = y_axis.data()[2];
|
||
|
||
// Столбец 2: Ось Z
|
||
rot.data()[2] = z_axis.data()[0];
|
||
rot.data()[5] = z_axis.data()[1];
|
||
rot.data()[8] = z_axis.data()[2];
|
||
|
||
return rot;
|
||
}
|
||
|
||
PlanetObject::PlanetObject(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler)
|
||
: renderer(iRenderer),
|
||
taskManager(iTaskManager),
|
||
mainThreadHandler(iMainThreadHandler)
|
||
{
|
||
}
|
||
|
||
void PlanetObject::init() {
|
||
// 1. Инициализируем данные (генерация мешей)
|
||
planetData.init();
|
||
|
||
// 2. Забираем данные для VBO
|
||
// Берем максимальный LOD для начальной отрисовки
|
||
int lodIndex = planetData.getMaxLodIndex();
|
||
planetRenderStruct.data = planetData.getLodLevel(lodIndex).vertexData;
|
||
|
||
//planetRenderStruct.data.PositionData.resize(9);
|
||
planetRenderStruct.RefreshVBO();
|
||
|
||
|
||
sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/sand2.png", CONST_ZIP_FILE));
|
||
stoneTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/rockdark3.png", CONST_ZIP_FILE));
|
||
|
||
// Атмосфера
|
||
planetAtmosphereRenderStruct.data = planetData.getAtmosphereLod().vertexData;
|
||
if (planetAtmosphereRenderStruct.data.PositionData.size() > 0)
|
||
{
|
||
planetAtmosphereRenderStruct.RefreshVBO();
|
||
}
|
||
|
||
planetStones = CreateStoneGroupData(778, planetData.getLodLevel(lodIndex));
|
||
|
||
//stonesToRender = planetStones.inflate(planetStones.allInstances.size());
|
||
stonesToRender.resize(planetStones.allInstances.size());
|
||
planetStones.initStatuses();
|
||
|
||
stoneToBake = planetStones.inflateOne(0, 0.75);
|
||
}
|
||
|
||
|
||
void PlanetObject::update(float deltaTimeMs) {
|
||
|
||
// 1. Проверка порога движения (оптимизация из текущего кода)
|
||
float movementThreshold = 1.0f;
|
||
if ((Environment::shipPosition - lastUpdatePos).squaredNorm() < movementThreshold * movementThreshold
|
||
&& !triangleIndicesToDraw.empty()) {
|
||
//processMainThreadTasks(); // Все равно обрабатываем очередь OpenGL задач
|
||
return;
|
||
}
|
||
lastUpdatePos = Environment::shipPosition;
|
||
|
||
// 2. Получаем список видимых треугольников
|
||
auto newIndices = planetData.getTrianglesUnderCameraNew2(Environment::shipPosition);
|
||
std::sort(newIndices.begin(), newIndices.end());
|
||
|
||
// 3. Анализируем, что нужно загрузить
|
||
for (int triIdx : newIndices) {
|
||
if (planetStones.statuses[triIdx] == ChunkStatus::Empty) {
|
||
// Помечаем, чтобы не спамить задачами
|
||
planetStones.statuses[triIdx] = ChunkStatus::Generating;
|
||
|
||
// Отправляем тяжелую математику в TaskManager
|
||
taskManager.EnqueueBackgroundTask([this, triIdx]() {
|
||
// Выполняется в фоновом потоке: только генерация геометрии в RAM
|
||
float scaleModifier = 1.0f;
|
||
VertexDataStruct generatedData = planetStones.inflateOneDataOnly(triIdx, scaleModifier);
|
||
|
||
// Передаем задачу на загрузку в GPU в очередь главного потока
|
||
this->mainThreadHandler.EnqueueMainThreadTask([this, triIdx, data = std::move(generatedData)]() mutable {
|
||
// Проверяем актуальность: если треугольник всё еще в списке видимых
|
||
auto it = std::find(triangleIndicesToDraw.begin(), triangleIndicesToDraw.end(), triIdx);
|
||
if (it != triangleIndicesToDraw.end()) {
|
||
stonesToRender[triIdx].data = std::move(data);
|
||
stonesToRender[triIdx].RefreshVBO(); // OpenGL вызов
|
||
planetStones.statuses[triIdx] = ChunkStatus::Live;
|
||
}
|
||
else {
|
||
// Если уже не нужен — просто сбрасываем статус
|
||
planetStones.statuses[triIdx] = ChunkStatus::Empty;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
// 4. Очистка (Unload)
|
||
// Если статус Live, но треугольника нет в новых индексах — освобождаем GPU память
|
||
for (size_t i = 0; i < planetStones.statuses.size(); ++i) {
|
||
if (planetStones.statuses[i] == ChunkStatus::Live) {
|
||
bool isVisible = std::binary_search(newIndices.begin(), newIndices.end(), (int)i);
|
||
if (!isVisible) {
|
||
// Очищаем данные и VBO (деструкторы shared_ptr в VertexRenderStruct сделают glDeleteBuffers)
|
||
stonesToRender[i].data.PositionData.clear();
|
||
stonesToRender[i].vao.reset();
|
||
stonesToRender[i].positionVBO.reset();
|
||
stonesToRender[i].normalVBO.reset();
|
||
stonesToRender[i].tangentVBO.reset();
|
||
stonesToRender[i].binormalVBO.reset();
|
||
stonesToRender[i].colorVBO.reset();
|
||
stonesToRender[i].texCoordVBO.reset();
|
||
|
||
planetStones.statuses[i] = ChunkStatus::Empty;
|
||
}
|
||
}
|
||
}
|
||
|
||
triangleIndicesToDraw = std::move(newIndices);
|
||
|
||
// 5. Выполняем одну задачу из очереди OpenGL (RefreshVBO и т.д.)
|
||
//processMainThreadTasks();
|
||
}
|
||
|
||
|
||
void PlanetObject::bakeStoneTexture(Renderer& renderer) {
|
||
glViewport(0, 0, 512, 512);
|
||
glClearColor(0,0,0, 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);
|
||
|
||
Triangle tr = planetData.getLodLevel(planetData.getCurrentLodIndex()).triangles[0];
|
||
|
||
// 1. Получаем матрицу вращения (оси в столбцах)
|
||
Matrix3f mr = GetRotationForTriangle(tr);
|
||
|
||
// 2. Трансформируем вершины в локальное пространство экрана, чтобы найти габариты
|
||
// Используем MultMatrixVector(Matrix, Vector).
|
||
// Если ваша функция считает V * M, то передайте Inverse(mr).
|
||
Vector3f rA = mr * tr.data[0];
|
||
Vector3f rB = mr * tr.data[1];
|
||
Vector3f rC = mr * tr.data[2];
|
||
|
||
// 3. Вычисляем реальные границы треугольника после поворота
|
||
float minX = min(rA(0), min(rB(0), rC(0)));
|
||
float maxX = max(rA(0), max(rB(0), rC(0)));
|
||
float minY = min(rA(1), min(rB(1), rC(1)));
|
||
float maxY = max(rA(1), max(rB(1), rC(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.col(2);
|
||
|
||
renderer.RenderUniform3fv("uPlanePoint", tr.data[0].data());
|
||
renderer.RenderUniform3fv("uPlaneNormal", planeNormal.data());
|
||
renderer.RenderUniform1f("uMaxHeight", StoneParams::BASE_SCALE * 1.1f); // Соответствует BASE_SCALE + perturbation
|
||
|
||
|
||
glActiveTexture(GL_TEXTURE0);
|
||
|
||
glEnable(GL_CULL_FACE);
|
||
glCullFace(GL_BACK); // Отсекаем задние грани
|
||
if (stoneToBake.data.PositionData.size() > 0)
|
||
{
|
||
|
||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||
renderer.DrawVertexRenderStruct(stoneToBake);
|
||
CheckGlError();
|
||
}
|
||
glDisable(GL_CULL_FACE); // Не забываем выключить, чтобы не сломать остальной рендер
|
||
renderer.PopMatrix();
|
||
renderer.PopProjectionMatrix();
|
||
renderer.DisableVertexAttribArray(vTexCoordName);
|
||
renderer.DisableVertexAttribArray(vPositionName);
|
||
renderer.shaderManager.PopShader();
|
||
CheckGlError();
|
||
}
|
||
|
||
|
||
void PlanetObject::draw(Renderer& renderer) {
|
||
|
||
{
|
||
if (stoneMapFB == nullptr)
|
||
{
|
||
//stoneMapFB = std::make_unique<FrameBuffer>(512, 512, true);
|
||
stoneMapFB = std::make_unique<FrameBuffer>(512, 512);
|
||
}
|
||
stoneMapFB->Bind();
|
||
|
||
bakeStoneTexture(renderer);
|
||
|
||
stoneMapFB->Unbind();
|
||
|
||
stoneMapFB->GenerateMipmaps();
|
||
|
||
}
|
||
|
||
glViewport(0, 0, Environment::width, Environment::height);
|
||
//--------------------------
|
||
|
||
|
||
drawPlanet(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.distanceToPlanetSurfaceFast(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.data());
|
||
|
||
//renderer.RenderUniform1f("uHeightScale", 0.08f);
|
||
renderer.RenderUniform1f("uHeightScale", 0.0f);
|
||
|
||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
|
||
|
||
// Направление на солнце в мировом пространстве
|
||
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
|
||
|
||
// Направление от центра планеты к игроку для расчета дня/ночи
|
||
Vector3f playerDirWorld = Environment::shipPosition.normalized();
|
||
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
|
||
|
||
// Тот же фактор освещенности игрока
|
||
float playerLightFactor = playerDirWorld.dot(-sunDirWorld);
|
||
playerLightFactor = max(0.0f, (playerLightFactor + 0.2f) / 1.2f);
|
||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||
|
||
|
||
glActiveTexture(GL_TEXTURE1);
|
||
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
||
glActiveTexture(GL_TEXTURE0);
|
||
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
|
||
//glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
|
||
|
||
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.distanceToPlanetSurfaceFast(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.data());
|
||
//std::cout << "uViewPos" << Environment::shipPosition << std::endl;
|
||
// PlanetObject.cpp, метод drawStones
|
||
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
|
||
|
||
Vector3f playerDirWorld = Environment::shipPosition.normalized();
|
||
float playerLightFactor = max(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
|
||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||
|
||
glEnable(GL_CULL_FACE);
|
||
glCullFace(GL_BACK);
|
||
glEnable(GL_BLEND);
|
||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||
glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID());
|
||
|
||
/*
|
||
for (int i : triangleIndicesToDraw)
|
||
//for (int i = 0; i < stonesToRender.size(); i++)
|
||
{
|
||
if (stonesToRender[i].data.PositionData.size() > 0)
|
||
{
|
||
renderer.DrawVertexRenderStruct(stonesToRender[i]);
|
||
}
|
||
}*/
|
||
for (int i : triangleIndicesToDraw) {
|
||
// КРИТИЧЕСКОЕ ИЗМЕНЕНИЕ:
|
||
// Проверяем, что данные не просто существуют, а загружены в GPU
|
||
if (planetStones.statuses[i] == ChunkStatus::Live) {
|
||
// Дополнительная проверка на наличие данных (на всякий случай)
|
||
if (stonesToRender[i].data.PositionData.size() > 0) {
|
||
renderer.DrawVertexRenderStruct(stonesToRender[i]);
|
||
}
|
||
}
|
||
// Если статус Generating или Empty — мы просто пропускаем этот индекс.
|
||
// Камни появятся на экране плавно, как только отработает TaskManager и RefreshVBO.
|
||
}
|
||
|
||
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::drawAtmosphere(Renderer& renderer) {
|
||
static const std::string defaultShaderName = "defaultAtmosphere";
|
||
//static const std::string defaultShaderName = "defaultColor";
|
||
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.distanceToPlanetSurfaceFast(Environment::shipPosition);
|
||
auto zRange = planetData.calculateZRange(dist);
|
||
float currentZNear = zRange.first;
|
||
float currentZFar = zRange.second;
|
||
|
||
if (currentZNear < 200)
|
||
{
|
||
currentZNear = 200;
|
||
currentZFar = currentZNear * 100;
|
||
}
|
||
|
||
// 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.data());
|
||
|
||
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, viewMatrix.data());
|
||
|
||
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
|
||
|
||
// В начале drawAtmosphere или как uniform
|
||
//float time = SDL_GetTicks() / 1000.0f;
|
||
//renderer.RenderUniform1f("uTime", time);
|
||
|
||
// В методе PlanetObject::drawAtmosphere
|
||
Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||
|
||
// Получаем текущую матрицу вида (ModelView без трансляции объекта)
|
||
// В вашем движке это Environment::inverseShipMatrix
|
||
Matrix3f viewMatrix2 = Environment::inverseShipMatrix;
|
||
|
||
// Трансформируем вектор света в пространство вида
|
||
Vector3f viewLightDir = viewMatrix2 * worldLightDir;
|
||
|
||
// Передаем инвертированный вектор (направление НА источник)
|
||
Vector3f lightToSource = -viewLightDir.normalized();
|
||
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
|
||
|
||
// В методе Game::drawCubemap
|
||
//renderer.RenderUniform1f("uTime", SDL_GetTicks() / 1000.0f);
|
||
// Направление света в мировом пространстве для освещения облаков
|
||
//Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||
renderer.RenderUniform3fv("uWorldLightDir", worldLightDir.data());
|
||
|
||
// 1. Рассчитываем uPlayerLightFactor (как в Game.cpp)
|
||
Vector3f playerDirWorld = Environment::shipPosition.normalized();
|
||
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
|
||
|
||
// Насколько игрок на свету
|
||
float playerLightFactor = playerDirWorld.dot(-sunDirWorld);
|
||
playerLightFactor = max(0.0f, (playerLightFactor + 0.2f) / 1.2f); // Мягкий порог
|
||
|
||
// 2. ОБЯЗАТЕЛЬНО передаем в шейдер
|
||
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
|
||
|
||
// 3. Убедитесь, что uSkyColor тоже передан (в коде выше его не было)
|
||
renderer.RenderUniform3fv("uSkyColor", color.data());
|
||
|
||
//Vector3f playerDirWorld = Environment::shipPosition.normalized();
|
||
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
|
||
|
||
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.distanceToPlanetSurfaceFast(viewerPosition);
|
||
}
|
||
|
||
|
||
|
||
} // namespace ZL
|