Files
OpenSpace/modules/base/rendering/renderableplane.cpp

350 lines
12 KiB
C++

/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2023 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/base/rendering/renderableplane.h>
#include <modules/base/basemodule.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/misc/defer.h>
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/glm.h>
#include <glm/gtx/string_cast.hpp>
#include <optional>
#include <variant>
namespace {
enum BlendMode {
Normal = 0,
Additive
};
constexpr openspace::properties::Property::PropertyInfo BillboardInfo = {
"Billboard",
"Billboard mode",
"This value specifies whether the plane is a billboard, which means that it is "
"always facing the camera. If this is false, it can be oriented using other "
"transformations"
};
constexpr openspace::properties::Property::PropertyInfo MirrorBacksideInfo = {
"MirrorBackside",
"Mirror backside of image plane",
"If this value is set to false, the image plane will not be mirrored when "
"looking from the backside. This is usually desirable when the image shows "
"data at a specific location, but not if it is displaying text for example"
};
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
"Size",
"Size (in meters)",
"This value specifies the size of the plane in meters"
};
constexpr openspace::properties::Property::PropertyInfo AutoScaleInfo = {
"AutoScale",
"Auto Scale",
"When true, the plane will automatically adjust in size to match the aspect "
"ratio of the content. Otherwise it will remain in the given size."
};
constexpr openspace::properties::Property::PropertyInfo BlendModeInfo = {
"BlendMode",
"Blending Mode",
"This determines the blending mode that is applied to this plane"
};
constexpr openspace::properties::Property::PropertyInfo MultiplyColorInfo = {
"MultiplyColor",
"Multiply Color",
"If set, the plane's texture is multiplied with this color. Useful for applying "
"a color grayscale images"
};
struct [[codegen::Dictionary(RenderablePlane)]] Parameters {
// [[codegen::verbatim(BillboardInfo.description)]]
std::optional<bool> billboard;
// [[codegen::verbatim(MirrorBacksideInfo.description)]]
std::optional<bool> mirrorBackside;
// [[codegen::verbatim(SizeInfo.description)]]
std::variant<float, glm::vec2> size;
enum class [[codegen::map(BlendMode)]] BlendMode {
Normal,
Additive
};
// [[codegen::verbatim(BlendModeInfo.description)]]
std::optional<BlendMode> blendMode;
// [[codegen::verbatim(MultiplyColorInfo.description)]]
std::optional<glm::vec3> multiplyColor [[codegen::color()]];
};
#include "renderableplane_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderablePlane::Documentation() {
return codegen::doc<Parameters>("base_renderable_plane");
}
RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _blendMode(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _billboard(BillboardInfo, false)
, _mirrorBackside(MirrorBacksideInfo, false)
, _size(SizeInfo, glm::vec2(10.f), glm::vec2(0.f), glm::vec2(1e25f))
, _autoScale(AutoScaleInfo, false)
, _multiplyColor(MultiplyColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
{
Parameters p = codegen::bake<Parameters>(dictionary);
addProperty(_opacity);
registerUpdateRenderBinFromOpacity();
if (std::holds_alternative<float>(p.size)) {
_size = glm::vec2(std::get<float>(p.size));
}
else {
_size = std::get<glm::vec2>(p.size);
}
_billboard = p.billboard.value_or(_billboard);
_mirrorBackside = p.mirrorBackside.value_or(_mirrorBackside);
_blendMode.addOptions({
{ static_cast<int>(BlendMode::Normal), "Normal" },
{ static_cast<int>(BlendMode::Additive), "Additive"}
});
_blendMode.onChange([&]() {
switch (_blendMode) {
case static_cast<int>(BlendMode::Normal):
setRenderBinFromOpacity();
break;
case static_cast<int>(BlendMode::Additive):
setRenderBin(Renderable::RenderBin::PreDeferredTransparent);
break;
default:
throw ghoul::MissingCaseException();
}
});
_opacity.onChange([&]() {
if (_blendMode == static_cast<int>(BlendMode::Normal)) {
setRenderBinFromOpacity();
}
});
if (p.blendMode.has_value()) {
_blendMode = codegen::map<BlendMode>(*p.blendMode);
}
_multiplyColor = p.multiplyColor.value_or(_multiplyColor);
_multiplyColor.setViewOption(properties::Property::ViewOptions::Color);
addProperty(_billboard);
_size.setExponent(15.f);
addProperty(_size);
_size.onChange([this](){ _planeIsDirty = true; });
addProperty(_autoScale);
addProperty(_multiplyColor);
setBoundingSphere(glm::compMax(_size.value()));
}
bool RenderablePlane::isReady() const {
return _shader != nullptr;
}
void RenderablePlane::initializeGL() {
ZoneScoped;
glGenVertexArrays(1, &_quad); // generate array
glGenBuffers(1, &_vertexPositionBuffer); // generate buffer
createPlane();
_shader = BaseModule::ProgramObjectManager.request(
"Plane",
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
return global::renderEngine->buildRenderProgram(
"Plane",
absPath("${MODULE_BASE}/shaders/plane_vs.glsl"),
absPath("${MODULE_BASE}/shaders/plane_fs.glsl")
);
}
);
}
void RenderablePlane::deinitializeGL() {
ZoneScoped;
glDeleteVertexArrays(1, &_quad);
_quad = 0;
glDeleteBuffers(1, &_vertexPositionBuffer);
_vertexPositionBuffer = 0;
BaseModule::ProgramObjectManager.release(
"Plane",
[](ghoul::opengl::ProgramObject* p) {
global::renderEngine->removeRenderProgram(p);
}
);
_shader = nullptr;
}
void RenderablePlane::render(const RenderData& data, RendererTasks&) {
ZoneScoped;
_shader->activate();
_shader->setUniform("opacity", opacity());
_shader->setUniform("mirrorBackside", _mirrorBackside);
glm::dvec3 objectPositionWorld = glm::dvec3(
glm::translate(
glm::dmat4(1.0),
data.modelTransform.translation) * glm::dvec4(0.0, 0.0, 0.0, 1.0)
);
glm::dvec3 normal = glm::normalize(data.camera.positionVec3() - objectPositionWorld);
glm::dvec3 newRight = glm::normalize(
glm::cross(data.camera.lookUpVectorWorldSpace(), normal)
);
glm::dvec3 newUp = glm::cross(normal, newRight);
glm::dmat4 cameraOrientedRotation = glm::dmat4(1.0);
cameraOrientedRotation[0] = glm::dvec4(newRight, 0.0);
cameraOrientedRotation[1] = glm::dvec4(newUp, 0.0);
cameraOrientedRotation[2] = glm::dvec4(normal, 0.0);
const glm::dmat4 rotationTransform = _billboard ?
cameraOrientedRotation :
glm::dmat4(data.modelTransform.rotation);
const glm::dmat4 modelTransform =
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
rotationTransform *
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) *
glm::dmat4(1.0);
const glm::dmat4 modelViewTransform =
data.camera.combinedViewMatrix() * modelTransform;
_shader->setUniform("modelViewProjectionTransform",
data.camera.projectionMatrix() * glm::mat4(modelViewTransform));
_shader->setUniform("modelViewTransform",
glm::mat4(data.camera.combinedViewMatrix() * glm::dmat4(modelViewTransform)));
ghoul::opengl::TextureUnit unit;
unit.activate();
bindTexture();
defer { unbindTexture(); };
_shader->setUniform("texture1", unit);
_shader->setUniform("multiplyColor", _multiplyColor);
bool additiveBlending = (_blendMode == static_cast<int>(BlendMode::Additive));
if (additiveBlending) {
glDepthMask(false);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
glDisable(GL_CULL_FACE);
glBindVertexArray(_quad);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
if (additiveBlending) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(true);
}
_shader->deactivate();
}
void RenderablePlane::bindTexture() {}
void RenderablePlane::unbindTexture() {}
void RenderablePlane::update(const UpdateData&) {
ZoneScoped;
if (_shader->isDirty()) {
_shader->rebuildFromFile();
}
if (_planeIsDirty) {
createPlane();
}
}
void RenderablePlane::createPlane() {
const GLfloat sizeX = _size.value().x;
const GLfloat sizeY = _size.value().y;
const GLfloat vertexData[] = {
// x y z w s t
-sizeX, -sizeY, 0.f, 0.f, 0.f, 0.f,
sizeX, sizeY, 0.f, 0.f, 1.f, 1.f,
-sizeX, sizeY, 0.f, 0.f, 0.f, 1.f,
-sizeX, -sizeY, 0.f, 0.f, 0.f, 0.f,
sizeX, -sizeY, 0.f, 0.f, 1.f, 0.f,
sizeX, sizeY, 0.f, 0.f, 1.f, 1.f,
};
glBindVertexArray(_quad);
glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, nullptr);
glEnableVertexAttribArray(1);
glVertexAttribPointer(
1,
2,
GL_FLOAT,
GL_FALSE,
sizeof(GLfloat) * 6,
reinterpret_cast<void*>(sizeof(GLfloat) * 4)
);
glBindVertexArray(0);
}
} // namespace openspace