#include "PlanetObject.h" #include #include #include "OpenGlExtensions.h" #include "Environment.h" #include "StoneObject.h" namespace ZL { 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.resize(3); 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) { // 1. Создаем FB (размер 512x512 для четкости) glViewport(0, 0, 1024, 1024); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* static const std::string stoneShader = "defaultColor2"; renderer.shaderManager.PushShader(stoneShader); 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.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vColorName); renderer.EnableVertexAttribArray(vNormalName); renderer.EnableVertexAttribArray(vTexCoordName); // 2. Очищаем (черный фон - это "нет камня") glClearColor(0.0f, 0.5f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 3. Настраиваем камеру (Ортографическая проекция над треугольником) // Смотрим на плоскость, где лежит эталонный треугольник renderer.PushProjectionMatrix(0.666667f, 1.777779f, 2000, 200000); renderer.PushMatrix(); renderer.LoadIdentity(); renderer.TranslateMatrix({ 0, 0, -45000.0f }); // Отодвигаемся, чтобы видеть камни // 4. Рендерим камни // Берем семена и параметры из первого треугольника (индекс 0) // Используем упрощенный вызов отрисовки геометрии камней // Рисуем меш камней для ОДНОГО эталонного треугольника // Здесь используем уже созданную нами функцию inflate для индекса 0 planetStones.inflate({ 0 }); VertexRenderStruct tempStoneRender; tempStoneRender.AssignFrom(planetStones.mesh); renderer.DrawVertexRenderStruct(tempStoneRender); renderer.PopMatrix(); renderer.PopProjectionMatrix(); //stoneMapFB->Unbind(); renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vColorName); renderer.DisableVertexAttribArray(vNormalName); renderer.DisableVertexAttribArray(vTexCoordName); renderer.shaderManager.PopShader(); // Восстанавливаем вьюпорт под экран glViewport(0, 0, Environment::width, Environment::height);*/ //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"; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 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, 1.0, currentZNear, currentZFar);*/ renderer.PushProjectionMatrix( -PlanetData::PLANET_RADIUS*sqrt(2)*(1-0/100.f)*0.5, PlanetData::PLANET_RADIUS * sqrt(2) * (1 - 0 / 100.f) * 0.5, -PlanetData::PLANET_RADIUS * sqrt(2) * (1 - 0 / 100.f) * 0.5, PlanetData::PLANET_RADIUS * sqrt(2) * (1 - 0 / 100.f) * 0.5, currentZNear, currentZFar); renderer.PushMatrix(); renderer.LoadIdentity(); //renderer.RotateMatrix(Environment::inverseShipMatrix); renderer.TranslateMatrix(Vector3f{ 0,0,-45000 }); 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 invr = InverseMatrix( MultMatrixMatrix(r2, r1)); renderer.RotateMatrix(invr); 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);// Аддитивное смешивание для эффекта свечения glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); //planetRenderStructCut.AssignFrom(planetRenderStruct.data); //planetRenderStructCut.data.PositionData.resize(3); //planetRenderStructCut.RefreshVBO(); renderer.DrawVertexRenderStruct(planetRenderStructCut); //planetStones.inflate({0/*,1,2,3,4,5,6,7*/}); //planetStonesRenderStruct.AssignFrom(planetStones.mesh); //planetStonesRenderStruct.RefreshVBO(); glClear(GL_DEPTH_BUFFER_BIT); 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(); //glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } void PlanetObject::draw(Renderer& renderer) { prepareDrawData(); { if (stoneMapFB == nullptr) { stoneMapFB = std::make_unique(1024, 1024); } 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