#include "PlanetObject.h" #include #include #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 m (std::array): // m[0]=X.x, m[1]=Y.x, m[2]=Z.x // m[3]=X.y, m[4]=Y.y, m[5]=Z.y // m[6]=X.z, m[7]=Y.z, m[8]=Z.z 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.getLodLevel(lodIndex).vertexData; /*planetRenderStructCut.data.PositionData[0] = planetRenderStructCut.data.PositionData[3 * 1 + 0]; planetRenderStructCut.data.PositionData[1] = planetRenderStructCut.data.PositionData[3 * 1 + 1]; planetRenderStructCut.data.PositionData[2] = planetRenderStructCut.data.PositionData[3 * 1 + 2]; */ planetRenderStructCut.data.PositionData.resize(3); /* Vector4f q1 = QuatFromRotateAroundX(-M_PI * 45.0 / 180.0); Vector4f q2 = QuatFromRotateAroundY(M_PI * 45.0 / 180.0); //Vector4f q3 = {-cos(0.5*M_PI * x / 180.0), -cos(0.5 * M_PI * x / 180.0), -cos(0.5 * M_PI * x / 180.0),sin(0.5 * M_PI * x / 180.0) }; Matrix3f r1 = QuatToMatrix(q1); Matrix3f r2 = QuatToMatrix(q2); //Matrix3f r3 = QuatToMatrix(q3); Matrix3f m = MultMatrixMatrix(r2, r1); Matrix3f invr = InverseMatrix(m); planetRenderStructCut.data.RotateByMatrix(invr);*/ planetRenderStructCut.RefreshVBO(); //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand2.png", "")); sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sandx.png", "")); stoneTexture = std::make_unique(CreateTextureDataFromPng("./resources/rockx.png", "")); // Атмосфера planetAtmosphereRenderStruct.data = planetData.getAtmosphereLod().vertexData; planetAtmosphereRenderStruct.RefreshVBO(); planetStones = CreateStoneGroupData(777, planetData.getLodLevel(lodIndex)); planetStones.inflate({ 0/*,1,2,3,4,5,6,7*/ }); planetStonesRenderStruct.AssignFrom(planetStones.mesh); planetStonesRenderStruct.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 newIndices; std::set 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); } } } // 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]); } } planetRenderYellowStruct.RefreshVBO(); // --- ОБНОВЛЯЕМ КАМНИ (через новую структуру StoneGroup) --- //planetStones.inflate(triangleIndicesToDraw); // Используем AssignFrom, он внутри сам вызывает RefreshVBO //planetStonesRenderStruct.AssignFrom(planetStones.mesh); } } void PlanetObject::bakeStoneTexture(Renderer& renderer) { glViewport(0, 0, 512, 512); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); static const std::string defaultShaderName2 = "defaultColor2"; 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.getLodLevel(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; //float size = max(width, height); //float hSize = size * 0.5f; renderer.PushProjectionMatrix( centerX - width*0.5, centerX + width * 0.5, centerY - height * 0.5, centerY + height * 0.5, currentZNear, currentZFar); renderer.PushMatrix(); renderer.LoadIdentity(); // Сдвигаем камеру по Z renderer.TranslateMatrix(Vector3f{ 0, 0, -45000 }); // Применяем вращение renderer.RotateMatrix(mr); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); renderer.DrawVertexRenderStruct(planetRenderStructCut); glClear(GL_DEPTH_BUFFER_BIT); if (planetStonesRenderStruct.data.PositionData.size() > 0) { glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID()); renderer.DrawVertexRenderStruct(planetStonesRenderStruct); CheckGlError(); } 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(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 = "defaultColorStones"; 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 vTexCoord2Name = "vTexCoord2"; //static const std::string vTexCoord3Name = "vTexCoord3"; static const std::string textureUniformName = "Texture"; renderer.shaderManager.PushShader(defaultShaderName); renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vColorName); renderer.EnableVertexAttribArray(vNormalName); renderer.EnableVertexAttribArray(vTexCoordName); //renderer.EnableVertexAttribArray(vTexCoord3Name); 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(Environment::width) / static_cast(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 lightDir_World = Vector3f(1.0f, 0.0f, -1.0f).normalized(); // В OpenGL/шейдерах удобнее работать с вектором, указывающим ОТ источника к поверхности. Vector3f lightDirection_World = -lightDir_World; // Вектор, направленный от источника Vector3f lightDirection_View; lightDirection_View.v[0] = viewMatrix.m[0] * lightDirection_World.v[0] + viewMatrix.m[4] * lightDirection_World.v[1] + viewMatrix.m[8] * lightDirection_World.v[2]; lightDirection_View.v[1] = viewMatrix.m[1] * lightDirection_World.v[0] + viewMatrix.m[5] * lightDirection_World.v[1] + viewMatrix.m[9] * lightDirection_World.v[2]; lightDirection_View.v[2] = viewMatrix.m[2] * lightDirection_World.v[0] + viewMatrix.m[6] * lightDirection_World.v[1] + viewMatrix.m[10] * lightDirection_World.v[2]; lightDirection_View = lightDirection_View.normalized(); // Нормализуем на всякий случай // Установка uniform-переменной // Предполагается, что RenderUniform3fv определена в Renderer.h renderer.RenderUniform3fv("uLightDirection", &lightDirection_View.v[0]); renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]); renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); renderer.RenderUniform1f("uCurrentZFar", currentZFar); renderer.RenderUniform1f("testShift1", x/500.f); renderer.RenderUniform1f("testShift2", y / 5000.f); renderer.RenderUniform1i("Texture", 0); renderer.RenderUniform1i("StoneMap", 1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID()); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); renderer.DrawVertexRenderStruct(planetRenderStruct); //glDisable(GL_BLEND); CheckGlError(); renderer.PopMatrix(); renderer.PopProjectionMatrix(); //renderer.DisableVertexAttribArray(vTexCoord3Name); renderer.DisableVertexAttribArray(vTexCoord2Name); renderer.DisableVertexAttribArray(vTexCoordName); renderer.DisableVertexAttribArray(vNormalName); renderer.DisableVertexAttribArray(vColorName); renderer.DisableVertexAttribArray(vPositionName); renderer.shaderManager.PopShader(); CheckGlError(); } void PlanetObject::drawStones(Renderer& renderer) { //static const std::string defaultShaderName = "defaultColor"; static const std::string defaultShaderName2 = "defaultColor2"; 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 vTexCoord3Name = "vTexCoord3"; 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); //renderer.EnableVertexAttribArray(vTexCoord3Name); 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(Environment::width) / static_cast(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 lightDir_World = Vector3f(1.0f, 0.0f, -1.0f).normalized(); // В OpenGL/шейдерах удобнее работать с вектором, указывающим ОТ источника к поверхности. Vector3f lightDirection_World = -lightDir_World; // Вектор, направленный от источника Vector3f lightDirection_View; lightDirection_View.v[0] = viewMatrix.m[0] * lightDirection_World.v[0] + viewMatrix.m[4] * lightDirection_World.v[1] + viewMatrix.m[8] * lightDirection_World.v[2]; lightDirection_View.v[1] = viewMatrix.m[1] * lightDirection_World.v[0] + viewMatrix.m[5] * lightDirection_World.v[1] + viewMatrix.m[9] * lightDirection_World.v[2]; lightDirection_View.v[2] = viewMatrix.m[2] * lightDirection_World.v[0] + viewMatrix.m[6] * lightDirection_World.v[1] + viewMatrix.m[10] * lightDirection_World.v[2]; lightDirection_View = lightDirection_View.normalized(); // Нормализуем на всякий случай // Установка uniform-переменной // Предполагается, что RenderUniform3fv определена в Renderer.h renderer.RenderUniform3fv("uLightDirection", &lightDirection_View.v[0]); renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]); renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); renderer.RenderUniform1f("uCurrentZFar", currentZFar); Vector3f color2 = { 1.0, 1.0, 1.0 }; renderer.RenderUniform3fv("uColor", &color2.v[0]); //glEnable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения if (planetStonesRenderStruct.data.PositionData.size() > 0) { //glBindTexture(GL_TEXTURE_2D, fb->getTextureID()); glBindTexture(GL_TEXTURE_2D, stoneTexture->getTexID()); renderer.DrawVertexRenderStruct(planetStonesRenderStruct); //glDisable(GL_BLEND); CheckGlError(); } renderer.PopMatrix(); renderer.PopProjectionMatrix(); //renderer.DisableVertexAttribArray(vTexCoord3Name); 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 = "defaultColor"; static const std::string defaultShaderName2 = "defaultColor2"; 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 vTexCoord3Name = "vTexCoord3"; 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(defaultShaderName2); renderer.RenderUniform1i(textureUniformName, 0); renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vTexCoordName); // 2. Применяем динамическую матрицу проекции renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, static_cast(Environment::width) / static_cast(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); 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(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(Environment::width) / static_cast(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