mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-01 09:10:18 -06:00
525 lines
18 KiB
C++
525 lines
18 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2021 *
|
|
* *
|
|
* 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/renderablemodel.h>
|
|
|
|
#include <modules/base/basemodule.h>
|
|
#include <modules/base/rendering/modelgeometry.h>
|
|
#include <openspace/documentation/documentation.h>
|
|
#include <openspace/documentation/verifier.h>
|
|
#include <openspace/engine/globals.h>
|
|
#include <openspace/rendering/renderengine.h>
|
|
#include <openspace/util/time.h>
|
|
#include <openspace/util/updatestructures.h>
|
|
#include <openspace/scene/scene.h>
|
|
#include <openspace/scene/lightsource.h>
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/invariants.h>
|
|
#include <ghoul/misc/profiling.h>
|
|
#include <ghoul/opengl/openglstatecache.h>
|
|
#include <ghoul/opengl/programobject.h>
|
|
#include <ghoul/opengl/textureunit.h>
|
|
|
|
namespace {
|
|
constexpr const char* ProgramName = "ModelProgram";
|
|
constexpr const char* KeyGeometry = "Geometry";
|
|
|
|
constexpr const int DefaultBlending = 0;
|
|
constexpr const int AdditiveBlending = 1;
|
|
constexpr const int PointsAndLinesBlending = 2;
|
|
constexpr const int PolygonBlending = 3;
|
|
constexpr const int ColorAddingBlending = 4;
|
|
|
|
std::map<std::string, int> BlendingMapping = {
|
|
{ "Default", DefaultBlending },
|
|
{ "Additive", AdditiveBlending },
|
|
{ "Points and Lines", PointsAndLinesBlending },
|
|
{ "Polygon", PolygonBlending },
|
|
{ "Color Adding", ColorAddingBlending }
|
|
};
|
|
|
|
constexpr const std::array<const char*, 13> UniformNames = {
|
|
"opacity", "nLightSources", "lightDirectionsViewSpace", "lightIntensities",
|
|
"modelViewTransform", "normalTransform", "projectionTransform",
|
|
"performShading", "texture1", "ambientIntensity", "diffuseIntensity",
|
|
"specularIntensity", "opacityBlending"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo AmbientIntensityInfo = {
|
|
"AmbientIntensity",
|
|
"Ambient Intensity",
|
|
"A multiplier for ambient lighting."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo DiffuseIntensityInfo = {
|
|
"DiffuseIntensity",
|
|
"Diffuse Intensity",
|
|
"A multiplier for diffuse lighting."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo SpecularIntensityInfo = {
|
|
"SpecularIntensity",
|
|
"Specular Intensity",
|
|
"A multiplier for specular lighting."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShadingInfo = {
|
|
"PerformShading",
|
|
"Perform Shading",
|
|
"This value determines whether this model should be shaded by using the position "
|
|
"of the Sun."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo DisableFaceCullingInfo = {
|
|
"DisableFaceCulling",
|
|
"Disable Face Culling",
|
|
"Disable OpenGL automatic face culling optimization."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ModelTransformInfo = {
|
|
"ModelTransform",
|
|
"Model Transform",
|
|
"This value specifies the model transform that is applied to the model before "
|
|
"all other transformations are applied."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo RotationVecInfo = {
|
|
"RotationVector",
|
|
"Rotation Vector",
|
|
"Rotation Vector using degrees"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo LightSourcesInfo = {
|
|
"LightSources",
|
|
"Light Sources",
|
|
"A list of light sources that this model should accept light from."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo DisableDepthTestInfo = {
|
|
"DisableDepthTest",
|
|
"Disable Depth Test",
|
|
"Disable Depth Testing for the Model."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo BlendingOptionInfo = {
|
|
"BledingOption",
|
|
"Blending Options",
|
|
"Debug option for blending colors."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo EnableOpacityBlendingInfo = {
|
|
"EnableOpacityBlending",
|
|
"Enable Opacity Blending",
|
|
"Enable Opacity Blending."
|
|
};
|
|
} // namespace
|
|
|
|
namespace openspace {
|
|
|
|
documentation::Documentation RenderableModel::Documentation() {
|
|
using namespace documentation;
|
|
return {
|
|
"RenderableModel",
|
|
"base_renderable_model",
|
|
{
|
|
{
|
|
KeyGeometry,
|
|
new TableVerifier({
|
|
{
|
|
"*",
|
|
new ReferencingVerifier("base_geometry_model"),
|
|
Optional::Yes
|
|
}
|
|
}),
|
|
Optional::No,
|
|
"This specifies the model that is rendered by the Renderable."
|
|
},
|
|
{
|
|
AmbientIntensityInfo.identifier,
|
|
new DoubleVerifier,
|
|
Optional::Yes,
|
|
AmbientIntensityInfo.description
|
|
},
|
|
{
|
|
DiffuseIntensityInfo.identifier,
|
|
new DoubleVerifier,
|
|
Optional::Yes,
|
|
DiffuseIntensityInfo.description
|
|
},
|
|
{
|
|
SpecularIntensityInfo.identifier,
|
|
new DoubleVerifier,
|
|
Optional::Yes,
|
|
SpecularIntensityInfo.description
|
|
},
|
|
{
|
|
ShadingInfo.identifier,
|
|
new BoolVerifier,
|
|
Optional::Yes,
|
|
ShadingInfo.description
|
|
},
|
|
{
|
|
DisableFaceCullingInfo.identifier,
|
|
new BoolVerifier,
|
|
Optional::Yes,
|
|
DisableFaceCullingInfo.description
|
|
},
|
|
{
|
|
ModelTransformInfo.identifier,
|
|
new DoubleMatrix3Verifier,
|
|
Optional::Yes,
|
|
ModelTransformInfo.description
|
|
},
|
|
{
|
|
RotationVecInfo.identifier,
|
|
new DoubleVector3Verifier,
|
|
Optional::Yes,
|
|
RotationVecInfo.description
|
|
},
|
|
{
|
|
LightSourcesInfo.identifier,
|
|
new TableVerifier({
|
|
{
|
|
"*",
|
|
new ReferencingVerifier("core_light_source"),
|
|
Optional::Yes
|
|
}
|
|
}),
|
|
Optional::Yes,
|
|
LightSourcesInfo.description
|
|
},
|
|
{
|
|
DisableDepthTestInfo.identifier,
|
|
new BoolVerifier,
|
|
Optional::Yes,
|
|
DisableDepthTestInfo.description
|
|
},
|
|
{
|
|
BlendingOptionInfo.identifier,
|
|
new StringVerifier,
|
|
Optional::Yes,
|
|
BlendingOptionInfo.description
|
|
},
|
|
{
|
|
EnableOpacityBlendingInfo.identifier,
|
|
new BoolVerifier,
|
|
Optional::Yes,
|
|
EnableOpacityBlendingInfo.description
|
|
},
|
|
}
|
|
};
|
|
}
|
|
|
|
RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
|
|
: Renderable(dictionary)
|
|
, _ambientIntensity(AmbientIntensityInfo, 0.2f, 0.f, 1.f)
|
|
, _diffuseIntensity(DiffuseIntensityInfo, 1.f, 0.f, 1.f)
|
|
, _specularIntensity(SpecularIntensityInfo, 1.f, 0.f, 1.f)
|
|
, _performShading(ShadingInfo, true)
|
|
, _disableFaceCulling(DisableFaceCullingInfo, false)
|
|
, _modelTransform(
|
|
ModelTransformInfo,
|
|
glm::dmat3(1.0),
|
|
glm::dmat3(-1.0),
|
|
glm::dmat3(1.0)
|
|
)
|
|
, _rotationVec(RotationVecInfo, glm::dvec3(0.0), glm::dvec3(0.0), glm::dvec3(360.0))
|
|
, _enableOpacityBlending(EnableOpacityBlendingInfo, false)
|
|
, _disableDepthTest(DisableDepthTestInfo, false)
|
|
, _blendingFuncOption(
|
|
BlendingOptionInfo,
|
|
properties::OptionProperty::DisplayType::Dropdown
|
|
)
|
|
, _lightSourcePropertyOwner({ "LightSources", "Light Sources" })
|
|
{
|
|
documentation::testSpecificationAndThrow(
|
|
Documentation(),
|
|
dictionary,
|
|
"RenderableModel"
|
|
);
|
|
|
|
addProperty(_opacity);
|
|
registerUpdateRenderBinFromOpacity();
|
|
|
|
|
|
if (dictionary.hasKey(KeyGeometry)) {
|
|
ghoul::Dictionary dict = dictionary.value<ghoul::Dictionary>(KeyGeometry);
|
|
for (int i = 1; i <= dict.size(); ++i) {
|
|
std::string key = std::to_string(i);
|
|
ghoul::Dictionary geom = dict.value<ghoul::Dictionary>(key);
|
|
_geometry.push_back(modelgeometry::ModelGeometry::createFromDictionary(geom));
|
|
}
|
|
}
|
|
|
|
if (dictionary.hasKey(ModelTransformInfo.identifier)) {
|
|
_modelTransform = dictionary.value<glm::dmat3>(ModelTransformInfo.identifier);
|
|
}
|
|
|
|
if (dictionary.hasKey(AmbientIntensityInfo.identifier)) {
|
|
_ambientIntensity = dictionary.value<double>(AmbientIntensityInfo.identifier);
|
|
}
|
|
if (dictionary.hasKey(DiffuseIntensityInfo.identifier)) {
|
|
_diffuseIntensity = dictionary.value<double>(DiffuseIntensityInfo.identifier);
|
|
}
|
|
if (dictionary.hasKey(SpecularIntensityInfo.identifier)) {
|
|
_specularIntensity = dictionary.value<double>(SpecularIntensityInfo.identifier);
|
|
}
|
|
|
|
if (dictionary.hasKey(ShadingInfo.identifier)) {
|
|
_performShading = dictionary.value<bool>(ShadingInfo.identifier);
|
|
}
|
|
|
|
if (dictionary.hasKey(DisableDepthTestInfo.identifier)) {
|
|
_disableDepthTest = dictionary.value<bool>(DisableDepthTestInfo.identifier);
|
|
}
|
|
|
|
if (dictionary.hasKey(DisableFaceCullingInfo.identifier)) {
|
|
_disableFaceCulling = dictionary.value<bool>(DisableFaceCullingInfo.identifier);
|
|
}
|
|
|
|
if (dictionary.hasKey(LightSourcesInfo.identifier)) {
|
|
const ghoul::Dictionary& lsDictionary =
|
|
dictionary.value<ghoul::Dictionary>(LightSourcesInfo.identifier);
|
|
|
|
for (std::string_view k : lsDictionary.keys()) {
|
|
std::unique_ptr<LightSource> lightSource = LightSource::createFromDictionary(
|
|
lsDictionary.value<ghoul::Dictionary>(k)
|
|
);
|
|
_lightSourcePropertyOwner.addPropertySubOwner(lightSource.get());
|
|
_lightSources.push_back(std::move(lightSource));
|
|
}
|
|
}
|
|
|
|
addPropertySubOwner(_lightSourcePropertyOwner);
|
|
addProperty(_ambientIntensity);
|
|
addProperty(_diffuseIntensity);
|
|
addProperty(_specularIntensity);
|
|
addProperty(_performShading);
|
|
addProperty(_disableFaceCulling);
|
|
addProperty(_disableDepthTest);
|
|
addProperty(_modelTransform);
|
|
addProperty(_rotationVec);
|
|
|
|
_rotationVec.onChange([this]() {
|
|
_modelTransform = glm::mat4_cast(glm::quat(glm::radians(_rotationVec.value())));
|
|
});
|
|
|
|
|
|
if (dictionary.hasKey(RotationVecInfo.identifier)) {
|
|
_rotationVec = dictionary.value<glm::dvec3>(RotationVecInfo.identifier);
|
|
}
|
|
|
|
_blendingFuncOption.addOption(DefaultBlending, "Default");
|
|
_blendingFuncOption.addOption(AdditiveBlending, "Additive");
|
|
_blendingFuncOption.addOption(PointsAndLinesBlending, "Points and Lines");
|
|
_blendingFuncOption.addOption(PolygonBlending, "Polygon");
|
|
_blendingFuncOption.addOption(ColorAddingBlending, "Color Adding");
|
|
|
|
addProperty(_blendingFuncOption);
|
|
|
|
if (dictionary.hasKey(BlendingOptionInfo.identifier)) {
|
|
const std::string blendingOpt = dictionary.value<std::string>(
|
|
BlendingOptionInfo.identifier
|
|
);
|
|
_blendingFuncOption.set(BlendingMapping[blendingOpt]);
|
|
}
|
|
|
|
if (dictionary.hasKey(DisableDepthTestInfo.identifier)) {
|
|
_enableOpacityBlending = dictionary.value<bool>(
|
|
EnableOpacityBlendingInfo.identifier
|
|
);
|
|
}
|
|
|
|
addProperty(_enableOpacityBlending);
|
|
}
|
|
|
|
bool RenderableModel::isReady() const {
|
|
return _program;
|
|
}
|
|
|
|
void RenderableModel::initialize() {
|
|
ZoneScoped
|
|
|
|
for (const std::unique_ptr<LightSource>& ls : _lightSources) {
|
|
ls->initialize();
|
|
}
|
|
}
|
|
|
|
void RenderableModel::initializeGL() {
|
|
ZoneScoped
|
|
|
|
_program = BaseModule::ProgramObjectManager.request(
|
|
ProgramName,
|
|
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
|
|
return global::renderEngine->buildRenderProgram(
|
|
ProgramName,
|
|
absPath("${MODULE_BASE}/shaders/model_vs.glsl"),
|
|
absPath("${MODULE_BASE}/shaders/model_fs.glsl")
|
|
);
|
|
}
|
|
);
|
|
|
|
ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames);
|
|
|
|
for (const ghoul::mm_unique_ptr<modelgeometry::ModelGeometry>& geom : _geometry) {
|
|
geom->initialize(this);
|
|
}
|
|
}
|
|
|
|
void RenderableModel::deinitializeGL() {
|
|
for (const ghoul::mm_unique_ptr<modelgeometry::ModelGeometry>& geom : _geometry) {
|
|
geom->deinitialize();
|
|
}
|
|
_geometry.clear();
|
|
|
|
BaseModule::ProgramObjectManager.release(
|
|
ProgramName,
|
|
[](ghoul::opengl::ProgramObject* p) {
|
|
global::renderEngine->removeRenderProgram(p);
|
|
}
|
|
);
|
|
_program = nullptr;
|
|
}
|
|
|
|
void RenderableModel::render(const RenderData& data, RendererTasks&) {
|
|
_program->activate();
|
|
|
|
_program->setUniform(_uniformCache.opacity, _opacity);
|
|
|
|
// Model transform and view transform needs to be in double precision
|
|
const glm::dmat4 modelTransform =
|
|
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation
|
|
glm::dmat4(data.modelTransform.rotation) * // Spice rotation
|
|
glm::scale(
|
|
glm::dmat4(_modelTransform.value()), glm::dvec3(data.modelTransform.scale)
|
|
);
|
|
const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() *
|
|
modelTransform;
|
|
|
|
int nLightSources = 0;
|
|
_lightIntensitiesBuffer.resize(_lightSources.size());
|
|
_lightDirectionsViewSpaceBuffer.resize(_lightSources.size());
|
|
for (const std::unique_ptr<LightSource>& lightSource : _lightSources) {
|
|
if (!lightSource->isEnabled()) {
|
|
continue;
|
|
}
|
|
_lightIntensitiesBuffer[nLightSources] = lightSource->intensity();
|
|
_lightDirectionsViewSpaceBuffer[nLightSources] =
|
|
lightSource->directionViewSpace(data);
|
|
|
|
++nLightSources;
|
|
}
|
|
|
|
_program->setUniform(
|
|
_uniformCache.nLightSources,
|
|
nLightSources
|
|
);
|
|
_program->setUniform(
|
|
_uniformCache.lightIntensities,
|
|
_lightIntensitiesBuffer
|
|
);
|
|
_program->setUniform(
|
|
_uniformCache.lightDirectionsViewSpace,
|
|
_lightDirectionsViewSpaceBuffer
|
|
);
|
|
_program->setUniform(
|
|
_uniformCache.modelViewTransform,
|
|
glm::mat4(modelViewTransform)
|
|
);
|
|
|
|
glm::dmat4 normalTransform = glm::transpose(glm::inverse(modelViewTransform));
|
|
|
|
_program->setUniform(
|
|
_uniformCache.normalTransform,
|
|
glm::mat4(normalTransform)
|
|
);
|
|
|
|
_program->setUniform(
|
|
_uniformCache.projectionTransform,
|
|
data.camera.projectionMatrix()
|
|
);
|
|
_program->setUniform(_uniformCache.ambientIntensity, _ambientIntensity);
|
|
_program->setUniform(_uniformCache.diffuseIntensity, _diffuseIntensity);
|
|
_program->setUniform(_uniformCache.specularIntensity, _specularIntensity);
|
|
_program->setUniform(_uniformCache.performShading, _performShading);
|
|
_program->setUniform(_uniformCache.opacityBlending, _enableOpacityBlending);
|
|
|
|
if (_disableFaceCulling) {
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
|
|
glEnablei(GL_BLEND, 0);
|
|
switch (_blendingFuncOption) {
|
|
case DefaultBlending:
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case AdditiveBlending:
|
|
glBlendFunc(GL_ONE, GL_ONE);
|
|
break;
|
|
case PointsAndLinesBlending:
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case PolygonBlending:
|
|
glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);
|
|
break;
|
|
case ColorAddingBlending:
|
|
glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);
|
|
break;
|
|
};
|
|
|
|
if (_disableDepthTest) {
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
ghoul::opengl::TextureUnit unit;
|
|
unit.activate();
|
|
_program->setUniform(_uniformCache.texture, unit);
|
|
for (const ghoul::mm_unique_ptr<modelgeometry::ModelGeometry>& geom : _geometry) {
|
|
geom->setUniforms(*_program);
|
|
geom->bindTexture();
|
|
geom->render();
|
|
}
|
|
if (_disableFaceCulling) {
|
|
glEnable(GL_CULL_FACE);
|
|
}
|
|
|
|
global::renderEngine->openglStateCache().resetBlendState();
|
|
|
|
if (_disableDepthTest) {
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
_program->deactivate();
|
|
}
|
|
|
|
void RenderableModel::update(const UpdateData&) {
|
|
if (_program->isDirty()) {
|
|
_program->rebuildFromFile();
|
|
ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames);
|
|
}
|
|
}
|
|
|
|
} // namespace openspace
|