921 lines
30 KiB
C++
921 lines
30 KiB
C++
// rendcontext.cpp
|
|
//
|
|
// Copyright (C) 2004-2009, the Celestia Development Team
|
|
// Original version by Chris Laurel <claurel@gmail.com>
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
|
|
#include <celutil/color.h>
|
|
#include "body.h"
|
|
#include "lightenv.h"
|
|
#include "rendcontext.h"
|
|
#include "render.h"
|
|
#include "texmanager.h"
|
|
#include "texture.h"
|
|
|
|
|
|
namespace
|
|
{
|
|
|
|
const cmod::Material defaultMaterial;
|
|
|
|
constexpr GLenum GLPrimitiveModes[static_cast<std::size_t>(cmod::PrimitiveGroupType::PrimitiveTypeMax)] =
|
|
{
|
|
GL_TRIANGLES,
|
|
GL_TRIANGLE_STRIP,
|
|
GL_TRIANGLE_FAN,
|
|
GL_LINES,
|
|
GL_LINE_STRIP,
|
|
GL_POINTS,
|
|
GL_POINTS,
|
|
};
|
|
|
|
constexpr GLenum GLComponentTypes[static_cast<std::size_t>(cmod::VertexAttributeFormat::FormatMax)] =
|
|
{
|
|
GL_FLOAT, // Float1
|
|
GL_FLOAT, // Float2
|
|
GL_FLOAT, // Float3
|
|
GL_FLOAT, // Float4,
|
|
GL_UNSIGNED_BYTE, // UByte4
|
|
};
|
|
|
|
constexpr int GLComponentCounts[static_cast<std::size_t>(cmod::VertexAttributeFormat::FormatMax)] =
|
|
{
|
|
1, // Float1
|
|
2, // Float2
|
|
3, // Float3
|
|
4, // Float4,
|
|
4, // UByte4
|
|
};
|
|
|
|
|
|
void
|
|
setStandardVertexArrays(const cmod::VertexDescription& desc,
|
|
const void* vertexData)
|
|
{
|
|
const cmod::VertexAttribute& position = desc.getAttribute(cmod::VertexAttributeSemantic::Position);
|
|
const cmod::VertexAttribute& normal = desc.getAttribute(cmod::VertexAttributeSemantic::Normal);
|
|
const cmod::VertexAttribute& color0 = desc.getAttribute(cmod::VertexAttributeSemantic::Color0);
|
|
const cmod::VertexAttribute& texCoord0 = desc.getAttribute(cmod::VertexAttributeSemantic::Texture0);
|
|
|
|
// Can't render anything unless we have positions
|
|
if (position.format != cmod::VertexAttributeFormat::Float3)
|
|
return;
|
|
|
|
// Set up the vertex arrays
|
|
glEnableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::VertexCoordAttributeIndex,
|
|
3, GL_FLOAT, GL_FALSE, desc.stride,
|
|
reinterpret_cast<const char*>(vertexData) + position.offset);
|
|
|
|
// Set up the normal array
|
|
switch (normal.format)
|
|
{
|
|
case cmod::VertexAttributeFormat::Float3:
|
|
glEnableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::NormalAttributeIndex,
|
|
3, GLComponentTypes[static_cast<std::size_t>(normal.format)],
|
|
GL_FALSE, desc.stride,
|
|
reinterpret_cast<const char*>(vertexData) + normal.offset);
|
|
break;
|
|
default:
|
|
glDisableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
|
|
break;
|
|
}
|
|
|
|
GLint normalized = GL_TRUE;
|
|
// Set up the color array
|
|
switch (color0.format)
|
|
{
|
|
case cmod::VertexAttributeFormat::Float3:
|
|
case cmod::VertexAttributeFormat::Float4:
|
|
normalized = GL_FALSE;
|
|
case cmod::VertexAttributeFormat::UByte4:
|
|
glEnableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::ColorAttributeIndex,
|
|
GLComponentCounts[static_cast<std::size_t>(color0.format)],
|
|
GLComponentTypes[static_cast<std::size_t>(color0.format)],
|
|
normalized, desc.stride,
|
|
reinterpret_cast<const char*>(vertexData) + color0.offset);
|
|
break;
|
|
default:
|
|
glDisableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
|
|
break;
|
|
}
|
|
|
|
// Set up the texture coordinate array
|
|
switch (texCoord0.format)
|
|
{
|
|
case cmod::VertexAttributeFormat::Float1:
|
|
case cmod::VertexAttributeFormat::Float2:
|
|
case cmod::VertexAttributeFormat::Float3:
|
|
case cmod::VertexAttributeFormat::Float4:
|
|
glEnableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::TextureCoord0AttributeIndex,
|
|
GLComponentCounts[static_cast<std::size_t>(texCoord0.format)],
|
|
GLComponentTypes[static_cast<std::size_t>(texCoord0.format)],
|
|
GL_FALSE,
|
|
desc.stride,
|
|
reinterpret_cast<const char*>(vertexData) + texCoord0.offset);
|
|
break;
|
|
default:
|
|
glDisableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
setExtendedVertexArrays(const cmod::VertexDescription& desc,
|
|
const void* vertexData)
|
|
{
|
|
const cmod::VertexAttribute& tangent = desc.getAttribute(cmod::VertexAttributeSemantic::Tangent);
|
|
const auto* vertices = reinterpret_cast<const char*>(vertexData);
|
|
|
|
switch (tangent.format)
|
|
{
|
|
case cmod::VertexAttributeFormat::Float3:
|
|
glEnableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::TangentAttributeIndex,
|
|
GLComponentCounts[static_cast<std::size_t>(tangent.format)],
|
|
GLComponentTypes[static_cast<std::size_t>(tangent.format)],
|
|
GL_FALSE,
|
|
desc.stride,
|
|
vertices + tangent.offset);
|
|
break;
|
|
default:
|
|
glDisableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
|
|
break;
|
|
}
|
|
|
|
const cmod::VertexAttribute& pointsize = desc.getAttribute(cmod::VertexAttributeSemantic::PointSize);
|
|
switch (pointsize.format)
|
|
{
|
|
case cmod::VertexAttributeFormat::Float1:
|
|
glEnableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::PointSizeAttributeIndex,
|
|
GLComponentCounts[static_cast<std::size_t>(pointsize.format)],
|
|
GLComponentTypes[static_cast<std::size_t>(pointsize.format)],
|
|
GL_FALSE,
|
|
desc.stride,
|
|
vertices + pointsize.offset);
|
|
break;
|
|
default:
|
|
glDisableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
|
|
break;
|
|
}
|
|
|
|
const cmod::VertexAttribute& nextPos = desc.getAttribute(cmod::VertexAttributeSemantic::NextPosition);
|
|
switch (nextPos.format)
|
|
{
|
|
case cmod::VertexAttributeFormat::Float3:
|
|
glEnableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::NextVCoordAttributeIndex,
|
|
GLComponentCounts[static_cast<std::size_t>(nextPos.format)],
|
|
GLComponentTypes[static_cast<std::size_t>(nextPos.format)],
|
|
GL_FALSE,
|
|
desc.stride,
|
|
vertices + nextPos.offset);
|
|
break;
|
|
default:
|
|
glDisableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
|
|
break;
|
|
}
|
|
|
|
const cmod::VertexAttribute& scaleFac = desc.getAttribute(cmod::VertexAttributeSemantic::ScaleFactor);
|
|
switch (scaleFac.format)
|
|
{
|
|
case cmod::VertexAttributeFormat::Float1:
|
|
glEnableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
|
|
glVertexAttribPointer(CelestiaGLProgram::ScaleFactorAttributeIndex,
|
|
GLComponentCounts[static_cast<std::size_t>(scaleFac.format)],
|
|
GLComponentTypes[static_cast<std::size_t>(scaleFac.format)],
|
|
GL_FALSE,
|
|
desc.stride,
|
|
vertices + scaleFac.offset);
|
|
break;
|
|
default:
|
|
glDisableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // end unnamed namespace
|
|
|
|
|
|
|
|
|
|
RenderContext::RenderContext(Renderer* _renderer) :
|
|
material(&defaultMaterial),
|
|
renderer(_renderer)
|
|
{
|
|
}
|
|
|
|
|
|
RenderContext::RenderContext(const cmod::Material* _material)
|
|
{
|
|
if (_material == nullptr)
|
|
material = &defaultMaterial;
|
|
else
|
|
material = _material;
|
|
}
|
|
|
|
|
|
const cmod::Material*
|
|
RenderContext::getMaterial() const
|
|
{
|
|
return material;
|
|
}
|
|
|
|
|
|
void
|
|
RenderContext::setMaterial(const cmod::Material* newMaterial)
|
|
{
|
|
if (!locked)
|
|
{
|
|
if (newMaterial == nullptr)
|
|
newMaterial = &defaultMaterial;
|
|
|
|
if (renderPass == PrimaryPass)
|
|
{
|
|
if (newMaterial != material)
|
|
{
|
|
material = newMaterial;
|
|
makeCurrent(*material);
|
|
}
|
|
}
|
|
else if (renderPass == EmissivePass)
|
|
{
|
|
if (material->getMap(cmod::TextureSemantic::EmissiveMap) !=
|
|
newMaterial->getMap(cmod::TextureSemantic::EmissiveMap))
|
|
{
|
|
material = newMaterial;
|
|
makeCurrent(*material);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
RenderContext::shouldDrawLineAsTriangles() const
|
|
{
|
|
return renderer->shouldDrawLineAsTriangles();
|
|
}
|
|
|
|
|
|
void
|
|
RenderContext::setPointScale(float _pointScale)
|
|
{
|
|
pointScale = _pointScale;
|
|
}
|
|
|
|
|
|
float
|
|
RenderContext::getPointScale() const
|
|
{
|
|
return pointScale;
|
|
}
|
|
|
|
|
|
void
|
|
RenderContext::setCameraOrientation(const Eigen::Quaternionf& q)
|
|
{
|
|
cameraOrientation = q;
|
|
}
|
|
|
|
|
|
Eigen::Quaternionf
|
|
RenderContext::getCameraOrientation() const
|
|
{
|
|
return cameraOrientation;
|
|
}
|
|
|
|
|
|
void
|
|
RenderContext::drawGroup(const cmod::PrimitiveGroup& group, bool useOverride)
|
|
{
|
|
// Skip rendering if this is the emissive pass but there's no
|
|
// emissive texture.
|
|
ResourceHandle emissiveMap = material->getMap(cmod::TextureSemantic::EmissiveMap);
|
|
|
|
if (renderPass == EmissivePass && emissiveMap == InvalidResource)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool drawPoints = false;
|
|
if (group.prim == cmod::PrimitiveGroupType::SpriteList || group.prim == cmod::PrimitiveGroupType::PointList)
|
|
{
|
|
drawPoints = true;
|
|
if (group.prim == cmod::PrimitiveGroupType::PointList)
|
|
glVertexAttrib1f(CelestiaGLProgram::PointSizeAttributeIndex, 1.0f);
|
|
#ifndef GL_ES
|
|
glEnable(GL_POINT_SPRITE);
|
|
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
#endif
|
|
glActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
glDrawElements(GLPrimitiveModes[(int)(useOverride ? group.primOverride : group.prim)],
|
|
useOverride ? group.nIndicesOverride : group.nIndices,
|
|
GL_UNSIGNED_INT,
|
|
useOverride ? group.indicesOverride : group.indices);
|
|
#ifndef GL_ES
|
|
if (drawPoints)
|
|
{
|
|
glDisable(GL_POINT_SPRITE);
|
|
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
RenderContext::setVertexArrays(const cmod::VertexDescription& desc,
|
|
const void* vertexData)
|
|
{
|
|
setStandardVertexArrays(desc, vertexData);
|
|
setExtendedVertexArrays(desc, vertexData);
|
|
}
|
|
|
|
void
|
|
RenderContext::updateShader(const cmod::VertexDescription& desc, cmod::PrimitiveGroupType primType)
|
|
{
|
|
// Normally, the shader that will be used depends only on the material.
|
|
// But the presence of point size and normals can also affect the
|
|
// shader, so force an update of the material if those attributes appear
|
|
// or disappear in the new set of vertex arrays.
|
|
bool usePointSizeNow = (desc.getAttribute(cmod::VertexAttributeSemantic::PointSize).format
|
|
== cmod::VertexAttributeFormat::Float1);
|
|
bool useNormalsNow = (desc.getAttribute(cmod::VertexAttributeSemantic::Normal).format
|
|
== cmod::VertexAttributeFormat::Float3);
|
|
bool useColorsNow = (desc.getAttribute(cmod::VertexAttributeSemantic::Color0).format
|
|
!= cmod::VertexAttributeFormat::InvalidFormat);
|
|
bool useTexCoordsNow = (desc.getAttribute(cmod::VertexAttributeSemantic::Texture0).format
|
|
!= cmod::VertexAttributeFormat::InvalidFormat);
|
|
bool drawLineNow = (desc.getAttribute(cmod::VertexAttributeSemantic::NextPosition).format
|
|
== cmod::VertexAttributeFormat::Float3);
|
|
bool useStaticPointSizeNow = primType == cmod::PrimitiveGroupType::PointList;
|
|
|
|
if (usePointSizeNow != usePointSize ||
|
|
useStaticPointSizeNow != useStaticPointSize ||
|
|
useNormalsNow != useNormals ||
|
|
useColorsNow != useColors ||
|
|
useTexCoordsNow != useTexCoords ||
|
|
drawLineNow != drawLine)
|
|
{
|
|
usePointSize = usePointSizeNow;
|
|
useStaticPointSize = useStaticPointSizeNow;
|
|
useNormals = useNormalsNow;
|
|
useColors = useColorsNow;
|
|
useTexCoords = useTexCoordsNow;
|
|
drawLine = drawLineNow;
|
|
if (getMaterial() != nullptr)
|
|
makeCurrent(*getMaterial());
|
|
}
|
|
}
|
|
|
|
void
|
|
RenderContext::setProjectionMatrix(const Eigen::Matrix4f *m)
|
|
{
|
|
projectionMatrix = m;
|
|
}
|
|
|
|
void
|
|
RenderContext::setModelViewMatrix(const Eigen::Matrix4f *m)
|
|
{
|
|
modelViewMatrix = m;
|
|
}
|
|
|
|
|
|
/***** GLSL render context ******/
|
|
|
|
GLSL_RenderContext::GLSL_RenderContext(Renderer* renderer,
|
|
const LightingState& ls,
|
|
float _objRadius,
|
|
const Eigen::Quaternionf& orientation) :
|
|
RenderContext(renderer),
|
|
lightingState(ls),
|
|
objRadius(_objRadius),
|
|
objScale(Eigen::Vector3f::Constant(_objRadius)),
|
|
objOrientation(orientation)
|
|
{
|
|
initLightingEnvironment();
|
|
}
|
|
|
|
|
|
GLSL_RenderContext::GLSL_RenderContext(Renderer* renderer,
|
|
const LightingState& ls,
|
|
const Eigen::Vector3f& _objScale,
|
|
const Eigen::Quaternionf& orientation) :
|
|
RenderContext(renderer),
|
|
lightingState(ls),
|
|
objRadius(_objScale.maxCoeff()),
|
|
objScale(_objScale),
|
|
objOrientation(orientation)
|
|
{
|
|
initLightingEnvironment();
|
|
}
|
|
|
|
|
|
GLSL_RenderContext::~GLSL_RenderContext()
|
|
{
|
|
glDisableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
|
|
}
|
|
|
|
|
|
void
|
|
GLSL_RenderContext::initLightingEnvironment()
|
|
{
|
|
// Set the light and shadow environment, which is constant for the entire model.
|
|
// The material properties will be set per mesh.
|
|
shaderProps.nLights = std::min(lightingState.nLights, MaxShaderLights);
|
|
|
|
// Set the shadow information.
|
|
// Track the total number of shadows; if there are too many, we'll have
|
|
// to fall back to multipass.
|
|
unsigned int totalShadows = 0;
|
|
for (unsigned int li = 0; li < lightingState.nLights; li++)
|
|
{
|
|
if (lightingState.shadows[li] && !lightingState.shadows[li]->empty())
|
|
{
|
|
unsigned int nShadows = static_cast<unsigned int>(std::min(static_cast<std::size_t>(MaxShaderEclipseShadows),
|
|
lightingState.shadows[li]->size()));
|
|
shaderProps.setEclipseShadowCountForLight(li, nShadows);
|
|
totalShadows += nShadows;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
GLSL_RenderContext::makeCurrent(const cmod::Material& m)
|
|
{
|
|
Texture* textures[4] = { nullptr, nullptr, nullptr, nullptr };
|
|
unsigned int nTextures = 0;
|
|
|
|
// Set up the textures used by this object
|
|
Texture* baseTex = nullptr;
|
|
Texture* bumpTex = nullptr;
|
|
Texture* specTex = nullptr;
|
|
Texture* emissiveTex = nullptr;
|
|
|
|
shaderProps.texUsage = ShaderProperties::SharedTextureCoords;
|
|
|
|
if (useNormals)
|
|
{
|
|
if (lunarLambert == 0.0f)
|
|
shaderProps.lightModel = ShaderProperties::DiffuseModel;
|
|
else
|
|
shaderProps.lightModel = ShaderProperties::LunarLambertModel;
|
|
}
|
|
else
|
|
{
|
|
// "particle" lighting is the only type that doesn't
|
|
// depend on having a surface normal.
|
|
// Enable alternate particle model when vertex colors are present;
|
|
// eventually, a render context method will enable the particle
|
|
// model.
|
|
if (useColors)
|
|
shaderProps.lightModel = ShaderProperties::ParticleModel;
|
|
else
|
|
shaderProps.lightModel = ShaderProperties::ParticleDiffuseModel;
|
|
}
|
|
|
|
ResourceHandle diffuseMap = m.getMap(cmod::TextureSemantic::DiffuseMap);
|
|
ResourceHandle normalMap = m.getMap(cmod::TextureSemantic::NormalMap);
|
|
ResourceHandle specularMap = m.getMap(cmod::TextureSemantic::SpecularMap);
|
|
ResourceHandle emissiveMap = m.getMap(cmod::TextureSemantic::EmissiveMap);
|
|
|
|
if (diffuseMap != InvalidResource && (useTexCoords || usePointSize))
|
|
{
|
|
baseTex = GetTextureManager()->find(diffuseMap);
|
|
if (baseTex != nullptr)
|
|
{
|
|
shaderProps.texUsage |= ShaderProperties::DiffuseTexture;
|
|
textures[nTextures++] = baseTex;
|
|
}
|
|
}
|
|
|
|
if (normalMap != InvalidResource)
|
|
{
|
|
bumpTex = GetTextureManager()->find(normalMap);
|
|
if (bumpTex != nullptr)
|
|
{
|
|
shaderProps.texUsage |= ShaderProperties::NormalTexture;
|
|
if (bumpTex->getFormatOptions() & Texture::DXT5NormalMap)
|
|
{
|
|
shaderProps.texUsage |= ShaderProperties::CompressedNormalTexture;
|
|
}
|
|
textures[nTextures++] = bumpTex;
|
|
}
|
|
}
|
|
|
|
if (m.specular != cmod::Color(0.0f, 0.0f, 0.0f) && useNormals)
|
|
{
|
|
shaderProps.lightModel = ShaderProperties::PerPixelSpecularModel;
|
|
specTex = GetTextureManager()->find(specularMap);
|
|
if (specTex == nullptr)
|
|
{
|
|
if (baseTex != nullptr)
|
|
shaderProps.texUsage |= ShaderProperties::SpecularInDiffuseAlpha;
|
|
}
|
|
else
|
|
{
|
|
shaderProps.texUsage |= ShaderProperties::SpecularTexture;
|
|
textures[nTextures++] = specTex;
|
|
}
|
|
}
|
|
|
|
if (emissiveMap != InvalidResource)
|
|
{
|
|
emissiveTex = GetTextureManager()->find(emissiveMap);
|
|
if (emissiveTex != nullptr)
|
|
{
|
|
shaderProps.texUsage |= ShaderProperties::EmissiveTexture;
|
|
textures[nTextures++] = emissiveTex;
|
|
}
|
|
}
|
|
|
|
if (lightingState.shadowingRingSystem)
|
|
{
|
|
Texture* ringsTex = lightingState.shadowingRingSystem->texture.find(medres);
|
|
if (ringsTex != nullptr)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + nTextures);
|
|
ringsTex->bind();
|
|
textures[nTextures++] = ringsTex;
|
|
|
|
#ifdef GL_ES
|
|
if (celestia::gl::OES_texture_border_clamp)
|
|
{
|
|
#endif
|
|
// Tweak the texture--set clamp to border and a border color with
|
|
// a zero alpha.
|
|
float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
#ifndef GL_ES
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
#else
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR_OES, bc);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER_OES);
|
|
#endif
|
|
|
|
#ifdef GL_ES
|
|
}
|
|
#endif
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
shaderProps.texUsage |= ShaderProperties::RingShadowTexture;
|
|
for (unsigned int lightIndex = 0; lightIndex < lightingState.nLights; lightIndex++)
|
|
{
|
|
if (lightingState.lights[lightIndex].castsShadows &&
|
|
lightingState.shadowingRingSystem == lightingState.ringShadows[lightIndex].ringSystem)
|
|
{
|
|
shaderProps.setRingShadowForLight(lightIndex, true);
|
|
}
|
|
else
|
|
{
|
|
shaderProps.setRingShadowForLight(lightIndex, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (usePointSize)
|
|
shaderProps.texUsage |= ShaderProperties::PointSprite;
|
|
else if (useStaticPointSize)
|
|
shaderProps.texUsage |= ShaderProperties::StaticPointSize;
|
|
|
|
if (drawLine)
|
|
shaderProps.texUsage |= ShaderProperties::LineAsTriangles;
|
|
|
|
if (useColors)
|
|
shaderProps.texUsage |= ShaderProperties::VertexColors;
|
|
|
|
if (atmosphere != nullptr)
|
|
{
|
|
// Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
|
|
if (atmosphere->mieScaleHeight > 0.0f)
|
|
shaderProps.texUsage |= ShaderProperties::Scattering;
|
|
}
|
|
|
|
bool hasShadowMap = shadowMap != 0 && shadowMapWidth != 0 && lightMatrix != nullptr;
|
|
if (hasShadowMap)
|
|
shaderProps.texUsage |= ShaderProperties::ShadowMapTexture;
|
|
|
|
// Get a shader for the current rendering configuration
|
|
assert(renderer != nullptr);
|
|
CelestiaGLProgram* prog = renderer->getShaderManager().getShader(shaderProps);
|
|
if (prog == nullptr)
|
|
return;
|
|
|
|
prog->use();
|
|
prog->setMVPMatrices(*projectionMatrix, *modelViewMatrix);
|
|
|
|
for (unsigned int i = 0; i < nTextures; i++)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
textures[i]->bind();
|
|
}
|
|
|
|
if (hasShadowMap)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + nTextures);
|
|
glBindTexture(GL_TEXTURE_2D, shadowMap);
|
|
#if GL_ONLY_SHADOWS
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
|
#endif
|
|
Eigen::Matrix4f shadowBias(Eigen::Matrix4f::Zero());
|
|
shadowBias.diagonal() = Eigen::Vector4f(0.5f, 0.5f, 0.5f, 1.0f);
|
|
shadowBias.col(3) = Eigen::Vector4f(0.5f, 0.5f, 0.5f, 1.0f);
|
|
prog->ShadowMatrix0 = shadowBias * (*lightMatrix);
|
|
prog->floatParam("shadowMapSize") = static_cast<float>(shadowMapWidth);
|
|
}
|
|
|
|
// setLightParameters() expects opacity in the alpha channel of the diffuse color
|
|
#ifdef HDR_COMPRESS
|
|
Color diffuse(m.diffuse.red() * 0.5f, m.diffuse.green() * 0.5f, m.diffuse.blue() * 0.5f, m.opacity);
|
|
#else
|
|
Color diffuse(m.diffuse.red(), m.diffuse.green(), m.diffuse.blue(), m.opacity);
|
|
#endif
|
|
Color specular(m.specular.red(), m.specular.green(), m.specular.blue());
|
|
Color emissive(m.emissive.red(), m.emissive.green(), m.emissive.blue());
|
|
|
|
prog->setLightParameters(lightingState, diffuse, specular, emissive);
|
|
|
|
if (shaderProps.hasEclipseShadows() != 0)
|
|
prog->setEclipseShadowParameters(lightingState, objScale, objOrientation);
|
|
|
|
// TODO: handle emissive color
|
|
prog->shininess = m.specularPower;
|
|
if (shaderProps.lightModel == ShaderProperties::LunarLambertModel)
|
|
{
|
|
prog->lunarLambert = lunarLambert;
|
|
}
|
|
|
|
// Generally, we want to disable depth writes for blend because it
|
|
// makes translucent objects look a bit better (though there are
|
|
// still problems when rendering them without sorting.) However,
|
|
// when scattering atmospheres are enabled, we need to render with
|
|
// depth writes on, otherwise the atmosphere will be drawn over
|
|
// a planet mesh. See SourceForge bug #1855894 for more details.
|
|
bool disableDepthWriteOnBlend = true;
|
|
|
|
if (atmosphere != nullptr && shaderProps.hasScattering())
|
|
{
|
|
prog->setAtmosphereParameters(*atmosphere, objRadius, objRadius);
|
|
disableDepthWriteOnBlend = false;
|
|
}
|
|
|
|
if (usePointSize)
|
|
{
|
|
prog->pointScale = getPointScale();
|
|
}
|
|
else if (useStaticPointSize)
|
|
{
|
|
prog->pointScale = renderer->getScreenDpi() / 96.0f;
|
|
}
|
|
|
|
if (drawLine)
|
|
{
|
|
prog->lineWidthX = renderer->getLineWidthX();
|
|
prog->lineWidthY = renderer->getLineWidthY();
|
|
}
|
|
|
|
// Ring shadow parameters
|
|
if ((shaderProps.texUsage & ShaderProperties::RingShadowTexture) != 0)
|
|
{
|
|
const RingSystem* rings = lightingState.shadowingRingSystem;
|
|
float ringWidth = rings->outerRadius - rings->innerRadius;
|
|
prog->ringRadius = rings->innerRadius / objRadius;
|
|
prog->ringWidth = objRadius / ringWidth;
|
|
prog->ringPlane = Eigen::Hyperplane<float, 3>(lightingState.ringPlaneNormal, lightingState.ringCenter / objRadius).coeffs();
|
|
prog->ringCenter = lightingState.ringCenter / objRadius;
|
|
|
|
for (unsigned int lightIndex = 0; lightIndex < lightingState.nLights; ++lightIndex)
|
|
{
|
|
if (shaderProps.hasRingShadowForLight(lightIndex))
|
|
{
|
|
prog->ringShadowLOD[lightIndex] = lightingState.ringShadows[lightIndex].texLod;
|
|
}
|
|
}
|
|
}
|
|
|
|
cmod::BlendMode newBlendMode = cmod::BlendMode::InvalidBlend;
|
|
if (m.opacity != 1.0f ||
|
|
m.blend == cmod::BlendMode::AdditiveBlend ||
|
|
(baseTex != nullptr && baseTex->hasAlpha()))
|
|
{
|
|
newBlendMode = m.blend;
|
|
}
|
|
|
|
if (newBlendMode != blendMode)
|
|
{
|
|
blendMode = newBlendMode;
|
|
switch (blendMode)
|
|
{
|
|
case cmod::BlendMode::NormalBlend:
|
|
renderer->enableBlending();
|
|
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
if (disableDepthWriteOnBlend)
|
|
renderer->disableDepthMask();
|
|
else
|
|
renderer->enableDepthMask();
|
|
break;
|
|
case cmod::BlendMode::AdditiveBlend:
|
|
renderer->enableBlending();
|
|
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE);
|
|
if (disableDepthWriteOnBlend)
|
|
renderer->disableDepthMask();
|
|
else
|
|
renderer->enableDepthMask();
|
|
break;
|
|
case cmod::BlendMode::PremultipliedAlphaBlend:
|
|
renderer->enableBlending();
|
|
renderer->setBlendingFactors(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
if (disableDepthWriteOnBlend)
|
|
renderer->disableDepthMask();
|
|
else
|
|
renderer->enableDepthMask();
|
|
break;
|
|
default:
|
|
renderer->disableBlending();
|
|
renderer->enableDepthMask();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
GLSL_RenderContext::setAtmosphere(const Atmosphere* _atmosphere)
|
|
{
|
|
atmosphere = _atmosphere;
|
|
}
|
|
|
|
// Extended material properties -- currently just lunarLambert term
|
|
void
|
|
GLSL_RenderContext::setLunarLambert(float l)
|
|
{
|
|
lunarLambert = l;
|
|
}
|
|
|
|
void
|
|
GLSL_RenderContext::setShadowMap(GLuint _shadowMap, GLuint _width, const Eigen::Matrix4f *_lightMatrix)
|
|
{
|
|
shadowMap = _shadowMap;
|
|
shadowMapWidth = _width;
|
|
lightMatrix = _lightMatrix;
|
|
}
|
|
|
|
/***** GLSL-Unlit render context ******/
|
|
|
|
GLSLUnlit_RenderContext::GLSLUnlit_RenderContext(Renderer* renderer, float _objRadius) :
|
|
RenderContext(renderer),
|
|
blendMode(cmod::BlendMode::InvalidBlend),
|
|
objRadius(_objRadius)
|
|
{
|
|
initLightingEnvironment();
|
|
}
|
|
|
|
|
|
GLSLUnlit_RenderContext::~GLSLUnlit_RenderContext()
|
|
{
|
|
glDisableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
|
|
glDisableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
|
|
}
|
|
|
|
|
|
void
|
|
GLSLUnlit_RenderContext::initLightingEnvironment()
|
|
{
|
|
// Set the light and shadow environment, which is constant for the entire model.
|
|
// The material properties will be set per mesh.
|
|
shaderProps.nLights = 1;
|
|
}
|
|
|
|
|
|
void
|
|
GLSLUnlit_RenderContext::makeCurrent(const cmod::Material& m)
|
|
{
|
|
Texture* textures[4] = { nullptr, nullptr, nullptr, nullptr };
|
|
unsigned int nTextures = 0;
|
|
|
|
// Set up the textures used by this object
|
|
Texture* baseTex = nullptr;
|
|
|
|
shaderProps.lightModel = ShaderProperties::EmissiveModel;
|
|
shaderProps.texUsage = ShaderProperties::SharedTextureCoords;
|
|
|
|
ResourceHandle diffuseMap = m.getMap(cmod::TextureSemantic::DiffuseMap);
|
|
if (diffuseMap != InvalidResource && (useTexCoords || usePointSize))
|
|
{
|
|
baseTex = GetTextureManager()->find(diffuseMap);
|
|
if (baseTex != nullptr)
|
|
{
|
|
shaderProps.texUsage |= ShaderProperties::DiffuseTexture;
|
|
textures[nTextures++] = baseTex;
|
|
}
|
|
}
|
|
|
|
if (usePointSize)
|
|
shaderProps.texUsage |= ShaderProperties::PointSprite;
|
|
else if (useStaticPointSize)
|
|
shaderProps.texUsage |= ShaderProperties::StaticPointSize;
|
|
|
|
if (drawLine)
|
|
shaderProps.texUsage |= ShaderProperties::LineAsTriangles;
|
|
|
|
if (useColors)
|
|
shaderProps.texUsage |= ShaderProperties::VertexColors;
|
|
|
|
// Get a shader for the current rendering configuration
|
|
assert(renderer != nullptr);
|
|
CelestiaGLProgram* prog = renderer->getShaderManager().getShader(shaderProps);
|
|
if (prog == nullptr)
|
|
return;
|
|
|
|
prog->use();
|
|
prog->setMVPMatrices(*projectionMatrix, *modelViewMatrix);
|
|
|
|
for (unsigned int i = 0; i < nTextures; i++)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
textures[i]->bind();
|
|
}
|
|
|
|
#ifdef HDR_COMPRESS
|
|
prog->lights[0].diffuse = m.diffuse.toVector3() * 0.5f;
|
|
#else
|
|
prog->lights[0].diffuse = m.diffuse.toVector3();
|
|
#endif
|
|
prog->opacity = m.opacity;
|
|
|
|
if (usePointSize)
|
|
{
|
|
prog->pointScale = getPointScale();
|
|
}
|
|
else if (useStaticPointSize)
|
|
{
|
|
prog->pointScale = renderer->getScreenDpi() / 96.0f;
|
|
}
|
|
|
|
if (drawLine)
|
|
{
|
|
prog->lineWidthX = renderer->getLineWidthX();
|
|
prog->lineWidthY = renderer->getLineWidthY();
|
|
}
|
|
|
|
cmod::BlendMode newBlendMode = cmod::BlendMode::InvalidBlend;
|
|
if (m.opacity != 1.0f ||
|
|
m.blend == cmod::BlendMode::AdditiveBlend ||
|
|
(baseTex != nullptr && baseTex->hasAlpha()))
|
|
{
|
|
newBlendMode = m.blend;
|
|
}
|
|
|
|
if (newBlendMode != blendMode)
|
|
{
|
|
blendMode = newBlendMode;
|
|
switch (blendMode)
|
|
{
|
|
case cmod::BlendMode::NormalBlend:
|
|
renderer->enableBlending();
|
|
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
renderer->disableDepthMask();
|
|
break;
|
|
case cmod::BlendMode::AdditiveBlend:
|
|
renderer->enableBlending();
|
|
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE);
|
|
renderer->disableDepthMask();
|
|
break;
|
|
case cmod::BlendMode::PremultipliedAlphaBlend:
|
|
renderer->enableBlending();
|
|
renderer->setBlendingFactors(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
renderer->disableDepthMask();
|
|
break;
|
|
default:
|
|
renderer->disableBlending();
|
|
renderer->enableDepthMask();
|
|
break;
|
|
}
|
|
}
|
|
}
|