mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-06 03:29:44 -06:00
Merge branch 'master' into merge/2022softwareintegration
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2022 #
|
||||
# Copyright (c) 2014-2025 #
|
||||
# #
|
||||
# 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 #
|
||||
@@ -22,8 +22,8 @@
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
|
||||
##########################################################################################
|
||||
|
||||
include(${OPENSPACE_CMAKE_EXT_DIR}/global_variables.cmake)
|
||||
include(${GHOUL_BASE_DIR}/support/cmake/message_macros.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/support/cmake/global_variables.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/ext/ghoul/support/cmake/message_macros.cmake)
|
||||
|
||||
|
||||
# This function takes a list of module paths and returns the list of include paths that
|
||||
@@ -91,7 +91,7 @@ function (get_module_attribute_supported path result)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Returns the path for the 'module_name'. If the module has not been seen before by
|
||||
# Returns the path for the 'module_name'. If the module has not been seen before by
|
||||
# get_individual_modules, an empty string is returned
|
||||
function (find_path_for_module module_name module_names module_paths result)
|
||||
list(FIND module_names ${module_name} i)
|
||||
@@ -121,7 +121,7 @@ function (get_recursive_dependencies module_name module_path module_names module
|
||||
"${module_names}" "${module_paths}"
|
||||
res
|
||||
)
|
||||
# 1. We add "base" to the list of dependencies as we always want it
|
||||
# 1. We add "base" to the list of dependencies as we always want it
|
||||
# 2. We add dependencies in this order such that when we later traverse
|
||||
# this list, we automatically get them in the correct order (meaning
|
||||
# that we include a dependency first)
|
||||
@@ -162,9 +162,8 @@ endfunction ()
|
||||
|
||||
|
||||
|
||||
|
||||
set(OPENSPACE_EXTERNAL_MODULES_PATHS "" CACHE STRING "List of external modules")
|
||||
set(internal_module_path "${OPENSPACE_BASE_DIR}/modules")
|
||||
option(OPENSPACE_ENABLE_ALL_MODULES "If this is ON, all modules will be enabled by default")
|
||||
set(internal_module_path "${PROJECT_SOURCE_DIR}/modules")
|
||||
set(all_enabled_modules "")
|
||||
|
||||
|
||||
@@ -173,21 +172,6 @@ set(all_enabled_modules "")
|
||||
#
|
||||
# First get all the internal module
|
||||
get_individual_modules(${internal_module_path} all_module_names all_module_paths)
|
||||
|
||||
# Then get all external modules
|
||||
foreach (path ${OPENSPACE_EXTERNAL_MODULES_PATHS})
|
||||
get_individual_modules(${path} names paths)
|
||||
|
||||
foreach (n ${names})
|
||||
if (${n} IN_LIST all_module_names)
|
||||
message(FATAL_ERROR "Module name ${n} is not unique among the external directories")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
set(all_module_names ${all_module_names} ${names})
|
||||
set(all_module_paths ${all_module_paths} ${paths})
|
||||
endforeach ()
|
||||
|
||||
list(LENGTH all_module_names all_module_names_count)
|
||||
math(EXPR all_module_names_count "${all_module_names_count} - 1")
|
||||
|
||||
@@ -209,7 +193,11 @@ foreach(val RANGE ${all_module_names_count})
|
||||
|
||||
get_module_attribute_default(${path} is_default_module)
|
||||
create_option_name(${name} optionName)
|
||||
option(${optionName} "Build ${path} Module" ${is_default_module})
|
||||
if (${OPENSPACE_ENABLE_ALL_MODULES})
|
||||
option(${optionName} "Build ${path} Module" ON)
|
||||
else ()
|
||||
option(${optionName} "Build ${path} Module" ${is_default_module})
|
||||
endif ()
|
||||
|
||||
if (${optionName})
|
||||
list(APPEND enabled_module_names ${name})
|
||||
@@ -241,7 +229,7 @@ foreach (val RANGE ${enabled_module_count})
|
||||
set(dependencies ${dependencies} ${deps})
|
||||
endforeach()
|
||||
|
||||
# We can remove the duplicates here. We constructed the list such that nested
|
||||
# We can remove the duplicates here. We constructed the list such that nested
|
||||
# dependencies are order left to right. REMOVE_DUPLICATES will keep the left most
|
||||
# value in the case of duplicates, so that will still work
|
||||
list(REMOVE_DUPLICATES dependencies)
|
||||
@@ -332,7 +320,7 @@ list(REMOVE_DUPLICATES topologically_sorted)
|
||||
|
||||
set(MODULE_HEADERS "")
|
||||
set(MODULE_CLASSES "")
|
||||
set(MODULE_PATHS "")
|
||||
set(MODULE_DOCUMENTATION "")
|
||||
|
||||
foreach (key RANGE ${enabled_module_count})
|
||||
list(GET topologically_sorted ${key} name)
|
||||
@@ -348,6 +336,7 @@ foreach (key RANGE ${enabled_module_count})
|
||||
|
||||
list(APPEND MODULE_HEADERS "#include <${header_filepath}>\n")
|
||||
list(APPEND MODULE_CLASSES " new ${class_name},\n")
|
||||
list(APPEND MODULE_DOCUMENTATION " ${class_name}::Documentation(),\n")
|
||||
endforeach ()
|
||||
|
||||
get_unique_include_paths(
|
||||
@@ -361,7 +350,7 @@ foreach (path ${module_paths})
|
||||
# The module path should include the 'modules' directory, which is removed for the
|
||||
# include path to make all of the includes look the same
|
||||
list(APPEND MODULE_PATHS " \"${path}/modules\",\n")
|
||||
target_include_directories(openspace-module-collection PUBLIC ${path})
|
||||
target_include_directories(openspace-module-collection INTERFACE ${path})
|
||||
endforeach ()
|
||||
|
||||
if (NOT "${MODULE_HEADERS}" STREQUAL "")
|
||||
@@ -372,20 +361,14 @@ if (NOT "${MODULE_CLASSES}" STREQUAL "")
|
||||
string(REPLACE ";" "" MODULE_CLASSES ${MODULE_CLASSES})
|
||||
endif ()
|
||||
|
||||
if (NOT "${MODULE_PATHS}" STREQUAL "")
|
||||
string(REPLACE ";" "" MODULE_PATHS ${MODULE_PATHS})
|
||||
string(REPLACE "\\" "/" MODULE_PATHS ${MODULE_PATHS})
|
||||
if (NOT "${MODULE_DOCUMENTATION}" STREQUAL "")
|
||||
string(REPLACE ";" "" MODULE_DOCUMENTATION ${MODULE_DOCUMENTATION})
|
||||
endif ()
|
||||
|
||||
configure_file(
|
||||
${OPENSPACE_CMAKE_EXT_DIR}/module_registration.template
|
||||
${PROJECT_SOURCE_DIR}/support/cmake/module_registration.template
|
||||
${CMAKE_BINARY_DIR}/_generated/include/openspace/moduleregistration.h
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${OPENSPACE_CMAKE_EXT_DIR}/module_path.template
|
||||
${CMAKE_BINARY_DIR}/_generated/include/openspace/modulepath.h
|
||||
)
|
||||
|
||||
# Return the list of enabled modules to the caller
|
||||
set(all_enabled_modules ${all_enabled_modules} PARENT_SCOPE)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2022 #
|
||||
# Copyright (c) 2014-2025 #
|
||||
# #
|
||||
# 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 #
|
||||
@@ -22,7 +22,7 @@
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
|
||||
##########################################################################################
|
||||
|
||||
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/support/cmake/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
rendering/atmospheredeferredcaster.h
|
||||
@@ -38,7 +38,7 @@ source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
set(SHADER_FILES
|
||||
shaders/atmosphere_common.glsl
|
||||
shaders/atmosphere_deferred_vs.glsl
|
||||
shaders/atmosphere_deferred_vs.glsl
|
||||
shaders/atmosphere_deferred_fs.glsl
|
||||
shaders/calculation_gs.glsl
|
||||
shaders/calculation_vs.glsl
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -70,16 +70,6 @@
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "AtmosphereDeferredcaster";
|
||||
|
||||
constexpr std::array<const char*, 27> UniformNames = {
|
||||
"cullAtmosphere", "Rg", "Rt", "groundRadianceEmission", "HR", "betaRayleigh",
|
||||
"HM", "betaMieExtinction", "mieG", "sunRadiance", "ozoneLayerEnabled", "HO",
|
||||
"betaOzoneExtinction", "SAMPLES_R", "SAMPLES_MU", "SAMPLES_MU_S", "SAMPLES_NU",
|
||||
"inverseModelTransformMatrix", "modelTransformMatrix",
|
||||
"projectionToModelTransformMatrix", "viewToWorldMatrix", "camPosObj",
|
||||
"sunDirectionObj", "hardShadows", "transmittanceTexture", "irradianceTexture",
|
||||
"inscatterTexture"
|
||||
};
|
||||
|
||||
constexpr float ATM_EPS = 2000.f;
|
||||
constexpr float KM_TO_M = 1000.f;
|
||||
|
||||
@@ -112,14 +102,14 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
bool isAtmosphereInFrustum(const glm::dmat4& MVMatrix, const glm::dvec3& position,
|
||||
bool isAtmosphereInFrustum(const glm::dmat4& mv, const glm::dvec3& position,
|
||||
double radius)
|
||||
{
|
||||
// Frustum Planes
|
||||
glm::dvec3 col1 = glm::dvec3(MVMatrix[0][0], MVMatrix[1][0], MVMatrix[2][0]);
|
||||
glm::dvec3 col2 = glm::dvec3(MVMatrix[0][1], MVMatrix[1][1], MVMatrix[2][1]);
|
||||
glm::dvec3 col3 = glm::dvec3(MVMatrix[0][2], MVMatrix[1][2], MVMatrix[2][2]);
|
||||
glm::dvec3 col4 = glm::dvec3(MVMatrix[0][3], MVMatrix[1][3], MVMatrix[2][3]);
|
||||
const glm::dvec3 col1 = glm::dvec3(mv[0][0], mv[1][0], mv[2][0]);
|
||||
const glm::dvec3 col2 = glm::dvec3(mv[0][1], mv[1][1], mv[2][1]);
|
||||
const glm::dvec3 col3 = glm::dvec3(mv[0][2], mv[1][2], mv[2][2]);
|
||||
const glm::dvec3 col4 = glm::dvec3(mv[0][3], mv[1][3], mv[2][3]);
|
||||
|
||||
glm::dvec3 leftNormal = col4 + col1;
|
||||
glm::dvec3 rightNormal = col4 - col1;
|
||||
@@ -129,11 +119,11 @@ namespace {
|
||||
glm::dvec3 farNormal = col4 - col3;
|
||||
|
||||
// Plane Distances
|
||||
double leftDistance = MVMatrix[3][3] + MVMatrix[3][0];
|
||||
double rightDistance = MVMatrix[3][3] - MVMatrix[3][0];
|
||||
double bottomDistance = MVMatrix[3][3] + MVMatrix[3][1];
|
||||
double topDistance = MVMatrix[3][3] - MVMatrix[3][1];
|
||||
double nearDistance = MVMatrix[3][3] + MVMatrix[3][2];
|
||||
double leftDistance = mv[3][3] + mv[3][0];
|
||||
double rightDistance = mv[3][3] - mv[3][0];
|
||||
double bottomDistance = mv[3][3] + mv[3][1];
|
||||
double topDistance = mv[3][3] - mv[3][1];
|
||||
double nearDistance = mv[3][3] + mv[3][2];
|
||||
|
||||
// Normalize Planes
|
||||
const double invLeftMag = 1.0 / glm::length(leftNormal);
|
||||
@@ -159,20 +149,18 @@ namespace {
|
||||
const double invFarMag = 1.0 / glm::length(farNormal);
|
||||
farNormal *= invFarMag;
|
||||
|
||||
if (((glm::dot(leftNormal, position) + leftDistance) < -radius) ||
|
||||
const bool outsideFrustum =
|
||||
(((glm::dot(leftNormal, position) + leftDistance) < -radius) ||
|
||||
((glm::dot(rightNormal, position) + rightDistance) < -radius) ||
|
||||
((glm::dot(bottomNormal, position) + bottomDistance) < -radius) ||
|
||||
((glm::dot(topNormal, position) + topDistance) < -radius) ||
|
||||
((glm::dot(nearNormal, position) + nearDistance) < -radius))
|
||||
// The far plane testing is disabled because the atm has no depth.
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
((glm::dot(nearNormal, position) + nearDistance) < -radius));
|
||||
|
||||
return !outsideFrustum;
|
||||
}
|
||||
|
||||
GLuint createTexture(const glm::ivec2& size, std::string_view name) {
|
||||
GLuint t;
|
||||
GLuint t = 0;
|
||||
glGenTextures(1, &t);
|
||||
glBindTexture(GL_TEXTURE_2D, t);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
@@ -201,7 +189,7 @@ namespace {
|
||||
GLuint createTexture(const glm::ivec3& size, std::string_view name, int components) {
|
||||
ghoul_assert(components == 3 || components == 4, "Only 3-4 components supported");
|
||||
|
||||
GLuint t;
|
||||
GLuint t = 0;
|
||||
glGenTextures(1, &t);
|
||||
glBindTexture(GL_TEXTURE_3D, t);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
@@ -252,7 +240,7 @@ AtmosphereDeferredcaster::AtmosphereDeferredcaster(float textureScale,
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::initialize() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
_transmittanceTableTexture = createTexture(_transmittanceTableSize, "Transmittance");
|
||||
_irradianceTableTexture = createTexture(_irradianceTableSize, "Irradiance");
|
||||
@@ -261,7 +249,7 @@ void AtmosphereDeferredcaster::initialize() {
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::deinitialize() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glDeleteTextures(1, &_transmittanceTableTexture);
|
||||
glDeleteTextures(1, &_irradianceTableTexture);
|
||||
@@ -270,13 +258,60 @@ void AtmosphereDeferredcaster::deinitialize() {
|
||||
|
||||
void AtmosphereDeferredcaster::update(const UpdateData&) {}
|
||||
|
||||
float AtmosphereDeferredcaster::eclipseShadow(const glm::dvec3& position) {
|
||||
// This code is copied from the atmosphere deferred fragment shader
|
||||
// It is used to calculate the eclipse shadow
|
||||
if (_shadowDataArrayCache.empty() || !_shadowDataArrayCache.front().isShadowing) {
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
const ShadowRenderingStruct& shadow = _shadowDataArrayCache.front();
|
||||
const glm::dvec3 positionToCaster = shadow.casterPositionVec - position;
|
||||
const glm::dvec3 sourceToCaster = shadow.sourceCasterVec; // Normalized
|
||||
const glm::dvec3 casterShadow =
|
||||
dot(positionToCaster, sourceToCaster) * sourceToCaster;
|
||||
const glm::dvec3 positionToShadow = positionToCaster - casterShadow;
|
||||
|
||||
const float distanceToShadow = static_cast<float>(length(positionToShadow));
|
||||
const double shadowLength = length(casterShadow);
|
||||
|
||||
const float radiusPenumbra = static_cast<float>(
|
||||
shadow.radiusCaster * (shadowLength + shadow.penumbra) / shadow.penumbra
|
||||
);
|
||||
const float radiusUmbra = static_cast<float>(
|
||||
shadow.radiusCaster * (shadow.umbra - shadowLength) / shadow.umbra
|
||||
);
|
||||
|
||||
// Is the position in the umbra - the fully shaded part
|
||||
if (distanceToShadow < radiusUmbra) {
|
||||
if (_hardShadowsEnabled) {
|
||||
return 0.5f;
|
||||
}
|
||||
else {
|
||||
// Smooth the shadow with the butterworth function
|
||||
const float s = radiusUmbra / (radiusUmbra + std::pow(distanceToShadow, 4.f));
|
||||
return std::sqrt(s);
|
||||
}
|
||||
}
|
||||
else if (distanceToShadow < radiusPenumbra) { // In penumbra - partially shaded part
|
||||
return _hardShadowsEnabled ? 0.5f : distanceToShadow / radiusPenumbra;
|
||||
}
|
||||
else {
|
||||
return 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::preRaycast(const RenderData& data, const DeferredcastData&,
|
||||
ghoul::opengl::ProgramObject& prg)
|
||||
ghoul::opengl::ProgramObject& program)
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
TracyGpuZone("Atmosphere preRaycast");
|
||||
|
||||
|
||||
// Atmosphere Frustum Culling
|
||||
glm::dvec3 tPlanetPos = glm::dvec3(_modelTransform * glm::dvec4(0.0, 0.0, 0.0, 1.0));
|
||||
const glm::dvec3 tPlanetPos = glm::dvec3(
|
||||
_modelTransform * glm::dvec4(0.0, 0.0, 0.0, 1.0)
|
||||
);
|
||||
const double distance = glm::distance(tPlanetPos, data.camera.eyePositionVec3());
|
||||
|
||||
// Radius is in KM
|
||||
@@ -285,75 +320,83 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& data, const Deferred
|
||||
);
|
||||
|
||||
// Number of planet radii to use as distance threshold for culling
|
||||
prg.setUniform(_uniformCache.cullAtmosphere, 1);
|
||||
program.setUniform(_uniformCache.cullAtmosphere, 1);
|
||||
|
||||
constexpr double DistanceCullingRadii = 5000;
|
||||
glm::dmat4 MV = glm::dmat4(data.camera.sgctInternal.projectionMatrix()) *
|
||||
const glm::dmat4 MV = glm::dmat4(data.camera.sgctInternal.projectionMatrix()) *
|
||||
data.camera.combinedViewMatrix();
|
||||
if (distance <= scaledRadius * DistanceCullingRadii &&
|
||||
isAtmosphereInFrustum(MV, tPlanetPos, scaledRadius + ATM_EPS))
|
||||
{
|
||||
prg.setUniform(_uniformCache.cullAtmosphere, 0);
|
||||
prg.setUniform(_uniformCache.Rg, _atmospherePlanetRadius);
|
||||
prg.setUniform(_uniformCache.Rt, _atmosphereRadius);
|
||||
prg.setUniform(_uniformCache.groundRadianceEmission, _groundRadianceEmission);
|
||||
prg.setUniform(_uniformCache.HR, _rayleighHeightScale);
|
||||
prg.setUniform(_uniformCache.betaRayleigh, _rayleighScatteringCoeff);
|
||||
prg.setUniform(_uniformCache.HM, _mieHeightScale);
|
||||
prg.setUniform(_uniformCache.betaMieExtinction, _mieExtinctionCoeff);
|
||||
prg.setUniform(_uniformCache.mieG, _miePhaseConstant);
|
||||
prg.setUniform(_uniformCache.sunRadiance, _sunRadianceIntensity);
|
||||
prg.setUniform(_uniformCache.ozoneLayerEnabled, _ozoneEnabled);
|
||||
prg.setUniform(_uniformCache.HO, _ozoneHeightScale);
|
||||
prg.setUniform(_uniformCache.betaOzoneExtinction, _ozoneExtinctionCoeff);
|
||||
prg.setUniform(_uniformCache.SAMPLES_R, _rSamples);
|
||||
prg.setUniform(_uniformCache.SAMPLES_MU, _muSamples);
|
||||
prg.setUniform(_uniformCache.SAMPLES_MU_S, _muSSamples);
|
||||
prg.setUniform(_uniformCache.SAMPLES_NU, _nuSamples);
|
||||
program.setUniform(_uniformCache.cullAtmosphere, 0);
|
||||
program.setUniform(_uniformCache.opacity, _opacity);
|
||||
program.setUniform(_uniformCache.Rg, _atmospherePlanetRadius);
|
||||
program.setUniform(_uniformCache.Rt, _atmosphereRadius);
|
||||
program.setUniform(_uniformCache.groundRadianceEmission, _groundRadianceEmission);
|
||||
program.setUniform(_uniformCache.HR, _rayleighHeightScale);
|
||||
program.setUniform(_uniformCache.betaRayleigh, _rayleighScatteringCoeff);
|
||||
program.setUniform(_uniformCache.HM, _mieHeightScale);
|
||||
program.setUniform(_uniformCache.betaMieExtinction, _mieExtinctionCoeff);
|
||||
program.setUniform(_uniformCache.mieG, _miePhaseConstant);
|
||||
program.setUniform(_uniformCache.sunRadiance, _sunRadianceIntensity);
|
||||
program.setUniform(_uniformCache.ozoneLayerEnabled, _ozoneEnabled);
|
||||
program.setUniform(_uniformCache.HO, _ozoneHeightScale);
|
||||
program.setUniform(_uniformCache.betaOzoneExtinction, _ozoneExtinctionCoeff);
|
||||
program.setUniform(_uniformCache.SAMPLES_R, _rSamples);
|
||||
program.setUniform(_uniformCache.SAMPLES_MU, _muSamples);
|
||||
program.setUniform(_uniformCache.SAMPLES_MU_S, _muSSamples);
|
||||
program.setUniform(_uniformCache.SAMPLES_NU, _nuSamples);
|
||||
// We expose the value as degrees, but the shader wants radians
|
||||
program.setUniform(_uniformCache.sunAngularSize, glm::radians(_sunAngularSize));
|
||||
|
||||
// Object Space
|
||||
glm::dmat4 invModelMatrix = glm::inverse(_modelTransform);
|
||||
prg.setUniform(_uniformCache.inverseModelTransformMatrix, invModelMatrix);
|
||||
prg.setUniform(_uniformCache.modelTransformMatrix, _modelTransform);
|
||||
const glm::dmat4 invModelMatrix = glm::inverse(_modelTransform);
|
||||
program.setUniform(_uniformCache.inverseModelTransformMatrix, invModelMatrix);
|
||||
program.setUniform(_uniformCache.modelTransformMatrix, _modelTransform);
|
||||
|
||||
glm::dmat4 viewToWorldMatrix = glm::inverse(data.camera.combinedViewMatrix());
|
||||
const glm::dmat4 viewToWorld = glm::inverse(data.camera.combinedViewMatrix());
|
||||
|
||||
// Eye Space to World Space
|
||||
prg.setUniform(_uniformCache.viewToWorldMatrix, viewToWorldMatrix);
|
||||
program.setUniform(_uniformCache.viewToWorldMatrix, viewToWorld);
|
||||
|
||||
// Projection to Eye Space
|
||||
glm::dmat4 dInvProj = glm::inverse(glm::dmat4(data.camera.projectionMatrix()));
|
||||
const glm::dmat4 dInvProj = glm::inverse(
|
||||
glm::dmat4(data.camera.projectionMatrix())
|
||||
);
|
||||
|
||||
glm::dmat4 invWholePipeline = invModelMatrix * viewToWorldMatrix * dInvProj;
|
||||
const glm::dmat4 invWholePipeline = invModelMatrix * viewToWorld * dInvProj;
|
||||
|
||||
prg.setUniform(_uniformCache.projectionToModelTransform, invWholePipeline);
|
||||
program.setUniform(
|
||||
_uniformCache.projectionToModelTransformMatrix,
|
||||
invWholePipeline
|
||||
);
|
||||
|
||||
glm::dvec4 camPosObjCoords =
|
||||
const glm::dvec4 camPosObjCoords =
|
||||
invModelMatrix * glm::dvec4(data.camera.eyePositionVec3(), 1.0);
|
||||
prg.setUniform(_uniformCache.camPosObj, glm::dvec3(camPosObjCoords));
|
||||
program.setUniform(_uniformCache.camPosObj, glm::dvec3(camPosObjCoords));
|
||||
|
||||
SceneGraphNode* node = sceneGraph()->sceneGraphNode("Sun");
|
||||
glm::dvec3 sunPosWorld = node ? node->worldPosition() : glm::dvec3(0.0);
|
||||
// For the lighting we use the provided node, or the Sun
|
||||
SceneGraphNode* node =
|
||||
_lightSourceNode ? _lightSourceNode : sceneGraph()->sceneGraphNode("Sun");
|
||||
const glm::dvec3 sunPosWorld = node ? node->worldPosition() : glm::dvec3(0.0);
|
||||
|
||||
glm::dvec3 sunPosObj;
|
||||
// Sun following camera position
|
||||
if (_sunFollowingCameraEnabled) {
|
||||
sunPosObj = invModelMatrix * glm::dvec4(data.camera.eyePositionVec3(), 1.0);
|
||||
sunPosObj = camPosObjCoords;
|
||||
}
|
||||
else {
|
||||
sunPosObj = invModelMatrix *
|
||||
glm::dvec4((sunPosWorld - data.modelTransform.translation) * 1000.0, 1.0);
|
||||
sunPosObj = invModelMatrix * glm::dvec4(sunPosWorld, 1.0);
|
||||
}
|
||||
|
||||
// Sun Position in Object Space
|
||||
prg.setUniform(_uniformCache.sunDirectionObj, glm::normalize(sunPosObj));
|
||||
program.setUniform(_uniformCache.sunDirectionObj, glm::normalize(sunPosObj));
|
||||
|
||||
// Shadow calculations..
|
||||
_shadowDataArrayCache.clear();
|
||||
for (ShadowConfiguration& shadowConf : _shadowConfArray) {
|
||||
// TO REMEMBER: all distances and lengths in world coordinates are in
|
||||
// meters!!! We need to move this to view space...
|
||||
double lt;
|
||||
double lt = 0.0;
|
||||
glm::dvec3 sourcePos = SpiceManager::ref().targetPosition(
|
||||
shadowConf.source.first,
|
||||
"SSB",
|
||||
@@ -392,25 +435,25 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& data, const Deferred
|
||||
|
||||
const double sourceScale = std::max(glm::compMax(sourceNode->scale()), 1.0);
|
||||
const double casterScale = std::max(glm::compMax(casterNode->scale()), 1.0);
|
||||
|
||||
const double actualSourceRadius = shadowConf.source.second * sourceScale;
|
||||
const double actualCasterRadius = shadowConf.caster.second * casterScale;
|
||||
// First we determine if the caster is shadowing the current planet
|
||||
// (all calculations in World Coordinates):
|
||||
glm::dvec3 planetCasterVec = casterPos - data.modelTransform.translation;
|
||||
glm::dvec3 sourceCasterVec = casterPos - sourcePos;
|
||||
double scLength = glm::length(sourceCasterVec);
|
||||
glm::dvec3 planetCasterProj =
|
||||
const glm::dvec3 planetCasterVec =
|
||||
casterPos - data.modelTransform.translation;
|
||||
const glm::dvec3 sourceCasterVec = casterPos - sourcePos;
|
||||
const double scLength = glm::length(sourceCasterVec);
|
||||
const glm::dvec3 planetCasterProj =
|
||||
(glm::dot(planetCasterVec, sourceCasterVec) / (scLength * scLength)) *
|
||||
sourceCasterVec;
|
||||
double dTest = glm::length(planetCasterVec - planetCasterProj);
|
||||
double xpTest = shadowConf.caster.second * casterScale *
|
||||
scLength /
|
||||
(shadowConf.source.second * sourceScale +
|
||||
shadowConf.caster.second * casterScale);
|
||||
double rpTest = shadowConf.caster.second * casterScale *
|
||||
const double dTest = glm::length(planetCasterVec - planetCasterProj);
|
||||
const double xpTest = actualCasterRadius * scLength /
|
||||
(actualSourceRadius + actualCasterRadius);
|
||||
const double rpTest = actualCasterRadius *
|
||||
(glm::length(planetCasterProj) + xpTest) / xpTest;
|
||||
|
||||
double casterDistSun = glm::length(casterPos - sunPosWorld);
|
||||
double planetDistSun = glm::length(
|
||||
const double casterDistSun = glm::length(casterPos - sunPosWorld);
|
||||
const double planetDistSun = glm::length(
|
||||
data.modelTransform.translation - sunPosWorld
|
||||
);
|
||||
|
||||
@@ -422,11 +465,13 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& data, const Deferred
|
||||
{
|
||||
// The current caster is shadowing the current planet
|
||||
shadow.isShadowing = true;
|
||||
shadow.rs = shadowConf.source.second * sourceScale;
|
||||
shadow.rc = shadowConf.caster.second * casterScale;
|
||||
shadow.radiusSource = actualSourceRadius;
|
||||
shadow.radiusCaster = actualCasterRadius;
|
||||
shadow.sourceCasterVec = glm::normalize(sourceCasterVec);
|
||||
shadow.xp = xpTest;
|
||||
shadow.xu = shadow.rc * scLength / (shadow.rs - shadow.rc);
|
||||
shadow.penumbra = xpTest;
|
||||
shadow.umbra =
|
||||
shadow.radiusCaster * scLength /
|
||||
(shadow.radiusSource - shadow.radiusCaster);
|
||||
shadow.casterPositionVec = casterPos;
|
||||
}
|
||||
_shadowDataArrayCache.push_back(shadow);
|
||||
@@ -436,44 +481,49 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& data, const Deferred
|
||||
unsigned int counter = 0;
|
||||
for (const ShadowRenderingStruct& sd : _shadowDataArrayCache) {
|
||||
// Add the counter
|
||||
char* bf = fmt::format_to(_uniformNameBuffer + 16, "{}", counter);
|
||||
char* bf = std::format_to(_uniformNameBuffer + 16, "{}", counter);
|
||||
|
||||
std::strcpy(bf, "].isShadowing\0");
|
||||
prg.setUniform(_uniformNameBuffer, sd.isShadowing);
|
||||
program.setUniform(_uniformNameBuffer, sd.isShadowing);
|
||||
|
||||
if (sd.isShadowing) {
|
||||
std::strcpy(bf, "].xp\0");
|
||||
prg.setUniform(_uniformNameBuffer, sd.xp);
|
||||
program.setUniform(_uniformNameBuffer, sd.penumbra);
|
||||
std::strcpy(bf, "].xu\0");
|
||||
prg.setUniform(_uniformNameBuffer, sd.xu);
|
||||
program.setUniform(_uniformNameBuffer, sd.umbra);
|
||||
std::strcpy(bf, "].rc\0");
|
||||
prg.setUniform(_uniformNameBuffer, sd.rc);
|
||||
program.setUniform(_uniformNameBuffer, sd.radiusCaster);
|
||||
std::strcpy(bf, "].sourceCasterVec\0");
|
||||
prg.setUniform(_uniformNameBuffer, sd.sourceCasterVec);
|
||||
program.setUniform(_uniformNameBuffer, sd.sourceCasterVec);
|
||||
std::strcpy(bf, "].casterPositionVec\0");
|
||||
prg.setUniform(_uniformNameBuffer, sd.casterPositionVec);
|
||||
program.setUniform(_uniformNameBuffer, sd.casterPositionVec);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
prg.setUniform(_uniformCache.hardShadows, _hardShadowsEnabled);
|
||||
program.setUniform(_uniformCache.hardShadows, _hardShadowsEnabled);
|
||||
}
|
||||
_transmittanceTableTextureUnit.activate();
|
||||
glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture);
|
||||
prg.setUniform(_uniformCache.transmittanceTexture, _transmittanceTableTextureUnit);
|
||||
program.setUniform(
|
||||
_uniformCache.transmittanceTexture,
|
||||
_transmittanceTableTextureUnit
|
||||
);
|
||||
|
||||
_irradianceTableTextureUnit.activate();
|
||||
glBindTexture(GL_TEXTURE_2D, _irradianceTableTexture);
|
||||
prg.setUniform(_uniformCache.irradianceTexture, _irradianceTableTextureUnit);
|
||||
program.setUniform(_uniformCache.irradianceTexture, _irradianceTableTextureUnit);
|
||||
|
||||
_inScatteringTableTextureUnit.activate();
|
||||
glBindTexture(GL_TEXTURE_3D, _inScatteringTableTexture);
|
||||
prg.setUniform(_uniformCache.inscatterTexture, _inScatteringTableTextureUnit);
|
||||
program.setUniform(_uniformCache.inscatterTexture, _inScatteringTableTextureUnit);
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::postRaycast(const RenderData&, const DeferredcastData&,
|
||||
ghoul::opengl::ProgramObject&)
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
TracyGpuZone("Atmosphere postRaycast");
|
||||
|
||||
// Deactivate the texture units
|
||||
_transmittanceTableTextureUnit.deactivate();
|
||||
@@ -496,13 +546,17 @@ std::filesystem::path AtmosphereDeferredcaster::helperPath() const {
|
||||
void AtmosphereDeferredcaster::initializeCachedVariables(
|
||||
ghoul::opengl::ProgramObject& program)
|
||||
{
|
||||
ghoul::opengl::updateUniformLocations(program, _uniformCache, UniformNames);
|
||||
ghoul::opengl::updateUniformLocations(program, _uniformCache);
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::setModelTransform(glm::dmat4 transform) {
|
||||
_modelTransform = std::move(transform);
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::setOpacity(float opacity) {
|
||||
_opacity = opacity;
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::setParameters(float atmosphereRadius, float planetRadius,
|
||||
float averageGroundReflectance,
|
||||
float groundRadianceEmission,
|
||||
@@ -513,7 +567,8 @@ void AtmosphereDeferredcaster::setParameters(float atmosphereRadius, float plane
|
||||
glm::vec3 ozoneExtinctionCoefficients,
|
||||
glm::vec3 mieScatteringCoefficients,
|
||||
glm::vec3 mieExtinctionCoefficients,
|
||||
bool sunFollowing)
|
||||
bool sunFollowing, float sunAngularSize,
|
||||
SceneGraphNode* lightSourceNode)
|
||||
{
|
||||
_atmosphereRadius = atmosphereRadius;
|
||||
_atmospherePlanetRadius = planetRadius;
|
||||
@@ -530,6 +585,9 @@ void AtmosphereDeferredcaster::setParameters(float atmosphereRadius, float plane
|
||||
_mieScatteringCoeff = std::move(mieScatteringCoefficients);
|
||||
_mieExtinctionCoeff = std::move(mieExtinctionCoefficients);
|
||||
_sunFollowingCameraEnabled = sunFollowing;
|
||||
_sunAngularSize = sunAngularSize;
|
||||
// The light source may be nullptr which we interpret to mean a position of (0,0,0)
|
||||
_lightSourceNode = lightSourceNode;
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::setHardShadows(bool enabled) {
|
||||
@@ -537,7 +595,7 @@ void AtmosphereDeferredcaster::setHardShadows(bool enabled) {
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::calculateTransmittance() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(
|
||||
GL_FRAMEBUFFER,
|
||||
@@ -564,8 +622,8 @@ void AtmosphereDeferredcaster::calculateTransmittance() {
|
||||
program->setUniform("HO", _ozoneHeightScale);
|
||||
program->setUniform("betaOzoneExtinction", _ozoneExtinctionCoeff);
|
||||
|
||||
constexpr float Black[] = { 0.f, 0.f, 0.f, 0.f };
|
||||
glClearBufferfv(GL_COLOR, 0, Black);
|
||||
constexpr glm::vec4 Black = glm::vec4(0.f, 0.f, 0.f, 0.f);
|
||||
glClearBufferfv(GL_COLOR, 0, glm::value_ptr(Black));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
if (_saveCalculationTextures) {
|
||||
saveTextureFile("transmittance_texture.ppm", _transmittanceTableSize);
|
||||
@@ -574,9 +632,9 @@ void AtmosphereDeferredcaster::calculateTransmittance() {
|
||||
}
|
||||
|
||||
GLuint AtmosphereDeferredcaster::calculateDeltaE() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
GLuint deltaE = createTexture(_deltaETableSize, "DeltaE");
|
||||
const GLuint deltaE = createTexture(_deltaETableSize, "DeltaE");
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaE, 0);
|
||||
glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y);
|
||||
using ProgramObject = ghoul::opengl::ProgramObject;
|
||||
@@ -603,14 +661,14 @@ GLuint AtmosphereDeferredcaster::calculateDeltaE() {
|
||||
}
|
||||
|
||||
std::pair<GLuint, GLuint> AtmosphereDeferredcaster::calculateDeltaS() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
GLuint deltaSRayleigh = createTexture(_textureSize, "DeltaS Rayleigh", 3);
|
||||
const GLuint deltaSRayleigh = createTexture(_textureSize, "DeltaS Rayleigh", 3);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaSRayleigh, 0);
|
||||
GLuint deltaSMie = createTexture(_textureSize, "DeltaS Mie", 3);
|
||||
const GLuint deltaSMie = createTexture(_textureSize, "DeltaS Mie", 3);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, deltaSMie, 0);
|
||||
GLenum colorBuffers[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
|
||||
glDrawBuffers(2, colorBuffers);
|
||||
std::array<GLenum, 2> colorBuffers = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
|
||||
glDrawBuffers(2, colorBuffers.data());
|
||||
glViewport(0, 0, _textureSize.x, _textureSize.y);
|
||||
using ProgramObject = ghoul::opengl::ProgramObject;
|
||||
std::unique_ptr<ProgramObject> program = ProgramObject::Build(
|
||||
@@ -649,15 +707,15 @@ std::pair<GLuint, GLuint> AtmosphereDeferredcaster::calculateDeltaS() {
|
||||
);
|
||||
}
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0);
|
||||
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
|
||||
glDrawBuffers(1, drawBuffers);
|
||||
const std::array<GLenum, 1> drawBuffers = { GL_COLOR_ATTACHMENT0 };
|
||||
glDrawBuffers(1, drawBuffers.data());
|
||||
|
||||
program->deactivate();
|
||||
return { deltaSRayleigh, deltaSMie };
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::calculateIrradiance() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(
|
||||
GL_FRAMEBUFFER,
|
||||
@@ -686,7 +744,7 @@ void AtmosphereDeferredcaster::calculateIrradiance() {
|
||||
void AtmosphereDeferredcaster::calculateInscattering(GLuint deltaSRayleigh,
|
||||
GLuint deltaSMie)
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(
|
||||
GL_FRAMEBUFFER,
|
||||
@@ -734,7 +792,7 @@ void AtmosphereDeferredcaster::calculateDeltaJ(int scatteringOrder,
|
||||
GLuint deltaJ, GLuint deltaE,
|
||||
GLuint deltaSRayleigh, GLuint deltaSMie)
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaJ, 0);
|
||||
glViewport(0, 0, _textureSize.x, _textureSize.y);
|
||||
@@ -780,7 +838,7 @@ void AtmosphereDeferredcaster::calculateDeltaJ(int scatteringOrder,
|
||||
}
|
||||
if (_saveCalculationTextures) {
|
||||
saveTextureFile(
|
||||
fmt::format("deltaJ_texture-scattering_order-{}.ppm", scatteringOrder),
|
||||
std::format("deltaJ_texture-scattering_order-{}.ppm", scatteringOrder),
|
||||
glm::ivec2(_textureSize)
|
||||
);
|
||||
}
|
||||
@@ -792,7 +850,7 @@ void AtmosphereDeferredcaster::calculateDeltaE(int scatteringOrder,
|
||||
GLuint deltaE, GLuint deltaSRayleigh,
|
||||
GLuint deltaSMie)
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaE, 0);
|
||||
glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y);
|
||||
@@ -820,7 +878,7 @@ void AtmosphereDeferredcaster::calculateDeltaE(int scatteringOrder,
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
if (_saveCalculationTextures) {
|
||||
saveTextureFile(
|
||||
fmt::format("deltaE_texture-scattering_order-{}.ppm", scatteringOrder),
|
||||
std::format("deltaE_texture-scattering_order-{}.ppm", scatteringOrder),
|
||||
_deltaETableSize
|
||||
);
|
||||
}
|
||||
@@ -831,7 +889,7 @@ void AtmosphereDeferredcaster::calculateDeltaS(int scatteringOrder,
|
||||
ghoul::opengl::ProgramObject& program,
|
||||
GLuint deltaSRayleigh, GLuint deltaJ)
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaSRayleigh, 0);
|
||||
glViewport(0, 0, _textureSize.x, _textureSize.y);
|
||||
@@ -860,7 +918,7 @@ void AtmosphereDeferredcaster::calculateDeltaS(int scatteringOrder,
|
||||
}
|
||||
if (_saveCalculationTextures) {
|
||||
saveTextureFile(
|
||||
fmt::format("deltaS_texture-scattering_order-{}.ppm", scatteringOrder),
|
||||
std::format("deltaS_texture-scattering_order-{}.ppm", scatteringOrder),
|
||||
glm::ivec2(_textureSize)
|
||||
);
|
||||
}
|
||||
@@ -871,7 +929,7 @@ void AtmosphereDeferredcaster::calculateIrradiance(int scatteringOrder,
|
||||
ghoul::opengl::ProgramObject& program,
|
||||
GLuint deltaE)
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(
|
||||
GL_FRAMEBUFFER,
|
||||
@@ -891,7 +949,7 @@ void AtmosphereDeferredcaster::calculateIrradiance(int scatteringOrder,
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
if (_saveCalculationTextures) {
|
||||
saveTextureFile(
|
||||
fmt::format("irradianceTable_order-{}.ppm", scatteringOrder),
|
||||
std::format("irradianceTable_order-{}.ppm", scatteringOrder),
|
||||
_deltaETableSize
|
||||
);
|
||||
}
|
||||
@@ -899,11 +957,11 @@ void AtmosphereDeferredcaster::calculateIrradiance(int scatteringOrder,
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::calculateInscattering(int scatteringOrder,
|
||||
ghoul::opengl::ProgramObject& prg,
|
||||
GLuint deltaSRayleigh)
|
||||
ghoul::opengl::ProgramObject& program,
|
||||
GLuint deltaSRayleigh)
|
||||
|
||||
{
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glFramebufferTexture(
|
||||
GL_FRAMEBUFFER,
|
||||
@@ -912,31 +970,31 @@ void AtmosphereDeferredcaster::calculateInscattering(int scatteringOrder,
|
||||
0
|
||||
);
|
||||
glViewport(0, 0, _textureSize.x, _textureSize.y);
|
||||
prg.activate();
|
||||
program.activate();
|
||||
|
||||
ghoul::opengl::TextureUnit unit;
|
||||
unit.activate();
|
||||
glBindTexture(GL_TEXTURE_3D, deltaSRayleigh);
|
||||
prg.setUniform("deltaSTexture", unit);
|
||||
prg.setUniform("SAMPLES_MU_S", _muSSamples);
|
||||
prg.setUniform("SAMPLES_NU", _nuSamples);
|
||||
prg.setUniform("SAMPLES_MU", _muSamples);
|
||||
prg.setUniform("SAMPLES_R", _rSamples);
|
||||
program.setUniform("deltaSTexture", unit);
|
||||
program.setUniform("SAMPLES_MU_S", _muSSamples);
|
||||
program.setUniform("SAMPLES_NU", _nuSamples);
|
||||
program.setUniform("SAMPLES_MU", _muSamples);
|
||||
program.setUniform("SAMPLES_R", _rSamples);
|
||||
for (int layer = 0; layer < _rSamples; ++layer) {
|
||||
prg.setUniform("layer", layer);
|
||||
program.setUniform("layer", layer);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
if (_saveCalculationTextures) {
|
||||
saveTextureFile(
|
||||
fmt::format("inscatteringTable_order-{}.ppm", scatteringOrder),
|
||||
std::format("inscatteringTable_order-{}.ppm", scatteringOrder),
|
||||
glm::ivec2(_textureSize)
|
||||
);
|
||||
}
|
||||
prg.deactivate();
|
||||
program.deactivate();
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::calculateAtmosphereParameters() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
using ProgramObject = ghoul::opengl::ProgramObject;
|
||||
std::unique_ptr<ProgramObject> deltaJProgram = ProgramObject::Build(
|
||||
@@ -970,38 +1028,38 @@ void AtmosphereDeferredcaster::calculateAtmosphereParameters() {
|
||||
|
||||
|
||||
// Saves current FBO first
|
||||
GLint defaultFBO;
|
||||
GLint defaultFBO = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
||||
|
||||
GLint viewport[4];
|
||||
global::renderEngine->openglStateCache().viewport(viewport);
|
||||
std::array<GLint, 4> viewport;
|
||||
global::renderEngine->openglStateCache().viewport(viewport.data());
|
||||
|
||||
// Creates the FBO for the calculations
|
||||
GLuint calcFBO;
|
||||
GLuint calcFBO = 0;
|
||||
glGenFramebuffers(1, &calcFBO);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, calcFBO);
|
||||
GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
|
||||
glDrawBuffers(1, drawBuffers);
|
||||
std::array<GLenum, 1> drawBuffers = { GL_COLOR_ATTACHMENT0 };
|
||||
glDrawBuffers(1, drawBuffers.data());
|
||||
|
||||
// Prepare for rendering/calculations
|
||||
GLuint quadVao;
|
||||
GLuint quadVao = 0;
|
||||
glGenVertexArrays(1, &quadVao);
|
||||
glBindVertexArray(quadVao);
|
||||
GLuint quadVbo;
|
||||
GLuint quadVbo = 0;
|
||||
glGenBuffers(1, &quadVbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quadVbo);
|
||||
|
||||
const GLfloat VertexData[] = {
|
||||
// x y z
|
||||
constexpr std::array<GLfloat, 12> VertexData = {
|
||||
// x y
|
||||
-1.f, -1.f,
|
||||
1.f, 1.f,
|
||||
1.f, 1.f,
|
||||
-1.f, 1.f,
|
||||
-1.f, -1.f,
|
||||
1.f, -1.f,
|
||||
1.f, 1.f,
|
||||
1.f, -1.f,
|
||||
1.f, 1.f,
|
||||
};
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData.data(), GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr);
|
||||
|
||||
@@ -1013,7 +1071,7 @@ void AtmosphereDeferredcaster::calculateAtmosphereParameters() {
|
||||
calculateTransmittance();
|
||||
|
||||
// line 2 in algorithm 4.1
|
||||
GLuint deltaETable = calculateDeltaE();
|
||||
const GLuint deltaETable = calculateDeltaE();
|
||||
|
||||
// line 3 in algorithm 4.1
|
||||
auto [deltaSRayleighTable, deltaSMieTable] = calculateDeltaS();
|
||||
@@ -1024,7 +1082,7 @@ void AtmosphereDeferredcaster::calculateAtmosphereParameters() {
|
||||
// line 5 in algorithm 4.1
|
||||
calculateInscattering(deltaSRayleighTable, deltaSMieTable);
|
||||
|
||||
GLuint deltaJTable = createTexture(_textureSize, "DeltaJ", 3);
|
||||
const GLuint deltaJTable = createTexture(_textureSize, "DeltaJ", 3);
|
||||
|
||||
// loop in line 6 in algorithm 4.1
|
||||
for (int scatteringOrder = 2; scatteringOrder <= 4; ++scatteringOrder) {
|
||||
@@ -1086,7 +1144,7 @@ void AtmosphereDeferredcaster::calculateAtmosphereParameters() {
|
||||
|
||||
// Restores system state
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
||||
global::renderEngine->openglStateCache().setViewportState(viewport);
|
||||
global::renderEngine->openglStateCache().setViewportState(viewport.data());
|
||||
glDeleteBuffers(1, &quadVbo);
|
||||
glDeleteVertexArrays(1, &quadVao);
|
||||
glDeleteFramebuffers(1, &calcFBO);
|
||||
@@ -1095,7 +1153,8 @@ void AtmosphereDeferredcaster::calculateAtmosphereParameters() {
|
||||
LDEBUG("Ended precalculations for Atmosphere effects");
|
||||
}
|
||||
|
||||
void AtmosphereDeferredcaster::step3DTexture(ghoul::opengl::ProgramObject& prg, int layer)
|
||||
void AtmosphereDeferredcaster::step3DTexture(ghoul::opengl::ProgramObject& prg,
|
||||
int layer) const
|
||||
{
|
||||
// See OpenGL redbook 8th Edition page 556 for Layered Rendering
|
||||
const float planet2 = _atmospherePlanetRadius * _atmospherePlanetRadius;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -46,10 +46,10 @@ struct DeferredcastData;
|
||||
struct ShadowConfiguration;
|
||||
|
||||
struct ShadowRenderingStruct {
|
||||
double xu = 0.0;
|
||||
double xp = 0.0;
|
||||
double rs = 0.0;
|
||||
double rc = 0.0;
|
||||
double umbra = 0.0;
|
||||
double penumbra = 0.0;
|
||||
double radiusSource = 0.0;
|
||||
double radiusCaster = 0.0;
|
||||
glm::dvec3 sourceCasterVec = glm::dvec3(0.0);
|
||||
glm::dvec3 casterPositionVec = glm::dvec3(0.0);
|
||||
bool isShadowing = false;
|
||||
@@ -75,10 +75,12 @@ public:
|
||||
void initializeCachedVariables(ghoul::opengl::ProgramObject& program) override;
|
||||
|
||||
void update(const UpdateData&) override;
|
||||
float eclipseShadow(const glm::dvec3& position);
|
||||
|
||||
void calculateAtmosphereParameters();
|
||||
|
||||
void setModelTransform(glm::dmat4 transform);
|
||||
void setOpacity(float opacity);
|
||||
|
||||
void setParameters(float atmosphereRadius, float planetRadius,
|
||||
float averageGroundReflectance, float groundRadianceEmission,
|
||||
@@ -86,12 +88,12 @@ public:
|
||||
float mieHeightScale, float miePhaseConstant, float sunRadiance,
|
||||
glm::vec3 rayScatteringCoefficients, glm::vec3 ozoneExtinctionCoefficients,
|
||||
glm::vec3 mieScatteringCoefficients, glm::vec3 mieExtinctionCoefficients,
|
||||
bool sunFollowing);
|
||||
bool sunFollowing, float sunAngularSize, SceneGraphNode* lightSourceNode);
|
||||
|
||||
void setHardShadows(bool enabled);
|
||||
|
||||
private:
|
||||
void step3DTexture(ghoul::opengl::ProgramObject& prg, int layer);
|
||||
void step3DTexture(ghoul::opengl::ProgramObject& prg, int layer) const;
|
||||
|
||||
void calculateTransmittance();
|
||||
GLuint calculateDeltaE();
|
||||
@@ -112,12 +114,13 @@ private:
|
||||
ghoul::opengl::ProgramObject& program, GLuint deltaSRayleigh);
|
||||
|
||||
|
||||
UniformCache(cullAtmosphere, Rg, Rt, groundRadianceEmission, HR, betaRayleigh, HM,
|
||||
betaMieExtinction, mieG, sunRadiance, ozoneLayerEnabled, HO, betaOzoneExtinction,
|
||||
SAMPLES_R, SAMPLES_MU, SAMPLES_MU_S, SAMPLES_NU, inverseModelTransformMatrix,
|
||||
modelTransformMatrix, projectionToModelTransform, viewToWorldMatrix,
|
||||
camPosObj, sunDirectionObj, hardShadows, transmittanceTexture, irradianceTexture,
|
||||
inscatterTexture) _uniformCache;
|
||||
UniformCache(cullAtmosphere, opacity, Rg, Rt, groundRadianceEmission, HR,
|
||||
betaRayleigh, HM, betaMieExtinction, mieG, sunRadiance, ozoneLayerEnabled, HO,
|
||||
betaOzoneExtinction, SAMPLES_R, SAMPLES_MU, SAMPLES_MU_S, SAMPLES_NU,
|
||||
inverseModelTransformMatrix, modelTransformMatrix,
|
||||
projectionToModelTransformMatrix, viewToWorldMatrix, camPosObj, sunDirectionObj,
|
||||
hardShadows, transmittanceTexture, irradianceTexture, inscatterTexture,
|
||||
sunAngularSize) _uniformCache;
|
||||
|
||||
ghoul::opengl::TextureUnit _transmittanceTableTextureUnit;
|
||||
ghoul::opengl::TextureUnit _irradianceTableTextureUnit;
|
||||
@@ -139,6 +142,8 @@ private:
|
||||
float _mieHeightScale = 0.f;
|
||||
float _miePhaseConstant = 0.f;
|
||||
float _sunRadianceIntensity = 5.f;
|
||||
float _sunAngularSize = 0.3f;
|
||||
SceneGraphNode* _lightSourceNode = nullptr;
|
||||
|
||||
glm::vec3 _rayleighScatteringCoeff = glm::vec3(0.f);
|
||||
glm::vec3 _ozoneExtinctionCoeff = glm::vec3(0.f);
|
||||
@@ -156,6 +161,7 @@ private:
|
||||
const glm::ivec3 _textureSize;
|
||||
|
||||
glm::dmat4 _modelTransform;
|
||||
float _opacity = 1.f;
|
||||
|
||||
// Eclipse Shadows
|
||||
std::vector<ShadowConfiguration> _shadowConfArray;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -30,10 +30,12 @@
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <ghoul/misc/profiling.h>
|
||||
#include <openspace/properties/property.h>
|
||||
#include <openspace/rendering/deferredcastermanager.h>
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
constexpr float KM_TO_M = 1000.f;
|
||||
@@ -41,7 +43,8 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo AtmosphereHeightInfo = {
|
||||
"AtmosphereHeight",
|
||||
"Atmosphere Height (KM)",
|
||||
"The thickness of the atmosphere in km"
|
||||
"The thickness of the atmosphere in kilometers.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo AverageGroundReflectanceInfo =
|
||||
@@ -49,59 +52,68 @@ namespace {
|
||||
"AverageGroundReflectance",
|
||||
"Average Ground Reflectance (%)",
|
||||
"Average percentage of light reflected by the ground during the pre-calculation "
|
||||
"phase"
|
||||
"phase.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo GroundRadianceEmissionInfo = {
|
||||
"GroundRadianceEmission",
|
||||
"Percentage of initial radiance emitted from ground",
|
||||
"Multiplier of the ground radiance color during the rendering phase"
|
||||
"Multiplier of the ground radiance color during the rendering phase.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RayleighHeightScaleInfo = {
|
||||
"RayleighHeightScale",
|
||||
"Rayleigh Scale Height (KM)",
|
||||
"It is the vertical distance over which the density and pressure fall by a "
|
||||
"constant factor"
|
||||
"The vertical distance over which the density and pressure falls by a constant "
|
||||
"factor.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RayleighScatteringCoeffInfo =
|
||||
{
|
||||
"RayleighScatteringCoeff",
|
||||
"Rayleigh Scattering Coeff",
|
||||
"Rayleigh sea-level scattering coefficients in meters"
|
||||
"Rayleigh sea-level scattering coefficients in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo OzoneLayerInfo = {
|
||||
"Ozone",
|
||||
"Ozone Layer Enabled",
|
||||
"Enables/Disable Ozone Layer during pre-calculation phase"
|
||||
"Enables/Disable Ozone Layer during pre-calculation phase.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo OzoneHeightScaleInfo = {
|
||||
"OzoneLayerHeightScale",
|
||||
"Ozone Scale Height (KM)",
|
||||
"It is the vertical distance over which the density and pressure fall by a "
|
||||
"constant factor"
|
||||
"Ozone Scale Height (km)",
|
||||
"The vertical distance over which the density and pressure fall by a constant "
|
||||
"factor, given in kilometers.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo OzoneLayerCoeffInfo = {
|
||||
"OzoneLayerCoeff",
|
||||
"Ozone Layer Extinction Coeff",
|
||||
"Ozone scattering coefficients in meters"
|
||||
"Ozone Layer Extinction Coefficient",
|
||||
"Ozone scattering coefficients in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo MieHeightScaleInfo = {
|
||||
"MieHeightScale",
|
||||
"Mie Scale Height (KM)",
|
||||
"It is the vertical distance over which the density and pressure fall by a "
|
||||
"constant factor"
|
||||
"Mie Scale Height (km)",
|
||||
"The vertical distance over which the density and pressure fall by a constant "
|
||||
"factor, given in kilometers.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo MieScatteringCoeffInfo = {
|
||||
"MieScatteringCoeff",
|
||||
"Mie Scattering Coeff",
|
||||
"Mie sea-level scattering coefficients in meters"
|
||||
"Mie Scattering Coefficient",
|
||||
"Mie sea-level scattering coefficients in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo
|
||||
@@ -109,19 +121,22 @@ namespace {
|
||||
{
|
||||
"MieScatteringExtinctionPropCoefficient",
|
||||
"Mie Scattering/Extinction Proportion Coefficient (%)",
|
||||
"Mie Scattering/Extinction Proportion Coefficient (%)"
|
||||
"Mie Scattering/Extinction Proportion Coefficient.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo MieAsymmetricFactorGInfo = {
|
||||
"MieAsymmetricFactorG",
|
||||
"Mie Asymmetric Factor G",
|
||||
"Averaging of the scattering angle over a high number of scattering events"
|
||||
"Averaging of the scattering angle over a high number of scattering events.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SunIntensityInfo = {
|
||||
"SunIntensity",
|
||||
"Sun Intensity",
|
||||
"Unitless for now"
|
||||
"A unitless value that controls the intensity/brightness of the Sun.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo
|
||||
@@ -129,59 +144,78 @@ namespace {
|
||||
{
|
||||
"SunFollowingCamera",
|
||||
"Enable Sun On Camera Position",
|
||||
"When selected the Sun is artificially positioned behind the observer all times"
|
||||
"When selected the Sun is artificially positioned behind the observer all times.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo EclipseHardShadowsInfo = {
|
||||
"EclipseHardShadowsInfo",
|
||||
"EclipseHardShadows",
|
||||
"Enable Hard Shadows for Eclipses",
|
||||
"Enable/Disables hard shadows through the atmosphere"
|
||||
"Enables/Disables hard shadows through the atmosphere.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo AtmosphereDimmingHeightInfo ={
|
||||
"AtmosphereDimmingHeight",
|
||||
"Atmosphere Dimming Height",
|
||||
"Percentage of the atmosphere where other objects, such as the stars, are faded"
|
||||
"Percentage of the atmosphere where other objects, such as the stars, are faded.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SunsetAngleInfo = {
|
||||
"AtmosphereDimmingSunsetAngle",
|
||||
"Atmosphere Dimming Sunset Angle",
|
||||
"The angle (degrees) between the Camera and the Sun where the sunset starts, and "
|
||||
"the atmosphere starts to fade in objects such as the stars"
|
||||
"the atmosphere starts to fade in objects such as the stars.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SunAngularSize = {
|
||||
"SunAngularSize",
|
||||
"Angular Size of the Sun",
|
||||
"The angular size of the Sun in degrees.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LightSourceNodeInfo = {
|
||||
"LightSourceNode",
|
||||
"Light Source",
|
||||
"The name of a scene graph node to be used as the source of illumination "
|
||||
"for the atmosphere. If not specified, the solar system's Sun is used.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(RenderableAtmosphere)]] Parameters {
|
||||
struct ShadowGroup {
|
||||
// Individual light sources
|
||||
// Individual light sources.
|
||||
struct SourceElement {
|
||||
// The scene graph node name of the source
|
||||
// The scene graph node name of the source.
|
||||
std::string name;
|
||||
// The radius of the object in meters
|
||||
// The radius of the object in meters.
|
||||
double radius;
|
||||
};
|
||||
// A list of light sources
|
||||
// A list of light sources.
|
||||
std::vector<SourceElement> sources;
|
||||
|
||||
// Individual shadow casters
|
||||
// Individual shadow casters.
|
||||
struct CasterElement {
|
||||
// The scene graph node name of the source
|
||||
// The scene graph node name of the source.
|
||||
std::string name;
|
||||
// The radius of the object in meters
|
||||
// The radius of the object in meters.
|
||||
double radius;
|
||||
};
|
||||
|
||||
// A list of objects that cast light on this atmosphere
|
||||
// A list of objects that cast light on this atmosphere.
|
||||
std::vector<CasterElement> casters;
|
||||
};
|
||||
// Declares shadow groups, meaning which nodes are considered in shadow
|
||||
// calculations
|
||||
// calculations.
|
||||
std::optional<ShadowGroup> shadowGroup;
|
||||
|
||||
// [[codegen::verbatim(AtmosphereHeightInfo.description)]]
|
||||
float atmosphereHeight;
|
||||
|
||||
// The radius of the planet in meters
|
||||
// The radius of the planet in meters.
|
||||
float planetRadius;
|
||||
|
||||
float planetAverageGroundReflectance;
|
||||
@@ -236,6 +270,12 @@ namespace {
|
||||
|
||||
// [[codegen::verbatim(SunsetAngleInfo.description)]]
|
||||
std::optional<glm::vec2> sunsetAngle;
|
||||
|
||||
// [[codegen::verbatim(SunAngularSize.description)]]
|
||||
std::optional<float> sunAngularSize [[codegen::inrange(0.0, 180.0)]];
|
||||
|
||||
// [[codegen::verbatim(LightSourceNodeInfo.description)]]
|
||||
std::optional<std::string> lightSourceNode;
|
||||
};
|
||||
#include "renderableatmosphere_codegen.cpp"
|
||||
|
||||
@@ -249,7 +289,7 @@ documentation::Documentation RenderableAtmosphere::Documentation() {
|
||||
|
||||
RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary)
|
||||
, _atmosphereHeight(AtmosphereHeightInfo, 60.f, 0.1f, 99.0f)
|
||||
, _atmosphereHeight(AtmosphereHeightInfo, 60.f, 0.1f, 99.f)
|
||||
, _groundAverageReflectance(AverageGroundReflectanceInfo, 0.f, 0.f, 1.f)
|
||||
, _groundRadianceEmission(GroundRadianceEmissionInfo, 0.f, 0.f, 1.f)
|
||||
, _rayleighHeightScale(RayleighHeightScaleInfo, 0.f, 0.1f, 50.f)
|
||||
@@ -276,8 +316,11 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary)
|
||||
, _sunIntensity(SunIntensityInfo, 5.f, 0.1f, 1000.f)
|
||||
, _sunFollowingCameraEnabled(EnableSunOnCameraPositionInfo, false)
|
||||
, _hardShadowsEnabled(EclipseHardShadowsInfo, false)
|
||||
, _sunAngularSize(SunAngularSize, 0.3f, 0.f, 180.f)
|
||||
, _lightSourceNodeName(LightSourceNodeInfo)
|
||||
, _atmosphereDimmingHeight(AtmosphereDimmingHeightInfo, 0.7f, 0.f, 1.f)
|
||||
, _atmosphereDimmingSunsetAngle(SunsetAngleInfo,
|
||||
, _atmosphereDimmingSunsetAngle(
|
||||
SunsetAngleInfo,
|
||||
glm::vec2(95.f, 100.f), glm::vec2(0.f), glm::vec2(180.f)
|
||||
)
|
||||
{
|
||||
@@ -384,7 +427,8 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary)
|
||||
|
||||
setBoundingSphere(_planetRadius * 1000.0);
|
||||
|
||||
_atmosphereDimmingHeight = p.atmosphereDimmingHeight.value_or(_atmosphereDimmingHeight);
|
||||
_atmosphereDimmingHeight =
|
||||
p.atmosphereDimmingHeight.value_or(_atmosphereDimmingHeight);
|
||||
addProperty(_atmosphereDimmingHeight);
|
||||
|
||||
_atmosphereDimmingSunsetAngle = p.sunsetAngle.value_or(
|
||||
@@ -394,6 +438,34 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary)
|
||||
properties::Property::ViewOptions::MinMaxRange
|
||||
);
|
||||
addProperty(_atmosphereDimmingSunsetAngle);
|
||||
|
||||
_sunAngularSize = p.sunAngularSize.value_or(_sunAngularSize);
|
||||
_sunAngularSize.onChange(updateWithoutCalculation);
|
||||
addProperty(_sunAngularSize);
|
||||
|
||||
_lightSourceNodeName.onChange([this]() {
|
||||
if (_lightSourceNodeName.value().empty()) {
|
||||
_lightSourceNode = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
SceneGraphNode* n = sceneGraphNode(_lightSourceNodeName);
|
||||
if (!n) {
|
||||
LERRORC(
|
||||
"RenderabeAtmosphere",
|
||||
std::format(
|
||||
"Could not find node '{}' as illumination for '{}'",
|
||||
_lightSourceNodeName.value(), identifier()
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
_lightSourceNode = n;
|
||||
_deferredCasterNeedsUpdate = true;
|
||||
}
|
||||
});
|
||||
_lightSourceNodeName = p.lightSourceNode.value_or("");
|
||||
addProperty(_lightSourceNodeName);
|
||||
}
|
||||
|
||||
void RenderableAtmosphere::deinitializeGL() {
|
||||
@@ -425,11 +497,11 @@ glm::dmat4 RenderableAtmosphere::computeModelTransformMatrix(const TransformData
|
||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.scale));
|
||||
}
|
||||
|
||||
void RenderableAtmosphere::render(const RenderData& data, RendererTasks& renderTask) {
|
||||
ZoneScoped
|
||||
void RenderableAtmosphere::render(const RenderData& data, RendererTasks& rendererTask) {
|
||||
ZoneScoped;
|
||||
|
||||
DeferredcasterTask task{ _deferredcaster.get(), data };
|
||||
renderTask.deferredcasterTasks.push_back(task);
|
||||
DeferredcasterTask task = { _deferredcaster.get(), data };
|
||||
rendererTask.deferredcasterTasks.push_back(std::move(task));
|
||||
}
|
||||
|
||||
void RenderableAtmosphere::update(const UpdateData& data) {
|
||||
@@ -443,57 +515,10 @@ void RenderableAtmosphere::update(const UpdateData& data) {
|
||||
}
|
||||
|
||||
glm::dmat4 modelTransform = computeModelTransformMatrix(data.modelTransform);
|
||||
_deferredcaster->setModelTransform(modelTransform);
|
||||
_deferredcaster->setModelTransform(std::move(modelTransform));
|
||||
_deferredcaster->setOpacity(opacity());
|
||||
_deferredcaster->update(data);
|
||||
|
||||
// Calculate atmosphere dimming coefficient
|
||||
// Calculate if the camera is in the atmosphere and if it is in the fading region
|
||||
float atmosphereDimming = 1.f;
|
||||
glm::dvec3 cameraPos = global::navigationHandler->camera()->positionVec3();
|
||||
glm::dvec3 planetPos = glm::dvec3(modelTransform * glm::dvec4(0.0, 0.0, 0.0, 1.0));
|
||||
float cameraDistance = static_cast<float>(glm::distance(planetPos, cameraPos));
|
||||
// Atmosphere height is in KM
|
||||
float atmosphereEdge = KM_TO_M * (_planetRadius + _atmosphereHeight);
|
||||
// Height of the atmosphere where the objects will be faded
|
||||
float atmosphereFadingHeight = KM_TO_M * _atmosphereDimmingHeight * _atmosphereHeight;
|
||||
float atmosphereInnerEdge = atmosphereEdge - atmosphereFadingHeight;
|
||||
bool cameraIsInAtmosphere = cameraDistance < atmosphereEdge;
|
||||
bool cameraIsInFadingRegion = cameraDistance > atmosphereInnerEdge;
|
||||
|
||||
// Check if camera is in sunset
|
||||
glm::dvec3 normalUnderCamera = glm::normalize(cameraPos - planetPos);
|
||||
glm::dvec3 vecToSun = glm::normalize(-planetPos);
|
||||
float cameraSunAngle = glm::degrees(static_cast<float>(
|
||||
glm::acos(glm::dot(vecToSun, normalUnderCamera))
|
||||
));
|
||||
float sunsetStart = _atmosphereDimmingSunsetAngle.value().x;
|
||||
float sunsetEnd = _atmosphereDimmingSunsetAngle.value().y;
|
||||
// If cameraSunAngle is more than 90 degrees, we are in shaded part of globe
|
||||
bool cameraIsInSun = cameraSunAngle <= sunsetEnd;
|
||||
bool cameraIsInSunset = cameraSunAngle > sunsetStart && cameraIsInSun;
|
||||
|
||||
// Fade if camera is inside the atmosphere
|
||||
if (cameraIsInAtmosphere && cameraIsInSun) {
|
||||
// If camera is in fading part of the atmosphere
|
||||
// Fade with regards to altitude
|
||||
if (cameraIsInFadingRegion) {
|
||||
// Fading - linear interpolation
|
||||
atmosphereDimming = (cameraDistance - atmosphereInnerEdge) /
|
||||
atmosphereFadingHeight;
|
||||
}
|
||||
else {
|
||||
// Camera is below fading region - atmosphere dims objects completely
|
||||
atmosphereDimming = 0.0;
|
||||
}
|
||||
if (cameraIsInSunset) {
|
||||
// Fading - linear interpolation
|
||||
atmosphereDimming = (cameraSunAngle - sunsetStart) /
|
||||
(sunsetEnd - sunsetStart);
|
||||
}
|
||||
global::navigationHandler->camera()->setAtmosphereDimmingFactor(
|
||||
atmosphereDimming
|
||||
);
|
||||
}
|
||||
setDimmingCoefficient(computeModelTransformMatrix(data.modelTransform));
|
||||
}
|
||||
|
||||
void RenderableAtmosphere::updateAtmosphereParameters() {
|
||||
@@ -515,9 +540,87 @@ void RenderableAtmosphere::updateAtmosphereParameters() {
|
||||
_ozoneCoeff,
|
||||
_mieScatteringCoeff,
|
||||
_mieExtinctionCoeff,
|
||||
_sunFollowingCameraEnabled
|
||||
_sunFollowingCameraEnabled,
|
||||
_sunAngularSize,
|
||||
_lightSourceNode
|
||||
);
|
||||
_deferredcaster->setHardShadows(_hardShadowsEnabled);
|
||||
}
|
||||
|
||||
// Calculate atmosphere dimming coefficient
|
||||
void RenderableAtmosphere::setDimmingCoefficient(const glm::dmat4& modelTransform) {
|
||||
// Calculate if the camera is in the atmosphere and if it is in the sunny region
|
||||
const glm::dvec3 cameraPos = global::navigationHandler->camera()->positionVec3();
|
||||
// TODO: change the assumption that the Sun is placed in the origin
|
||||
const glm::dvec3 planetPos =
|
||||
glm::dvec3(modelTransform * glm::dvec4(0.0, 0.0, 0.0, 1.0));
|
||||
const glm::dvec3 normalUnderCamera = glm::normalize(cameraPos - planetPos);
|
||||
const glm::dvec3 vecToSun = glm::normalize(-planetPos);
|
||||
|
||||
const float cameraDistance = static_cast<float>(glm::distance(planetPos, cameraPos));
|
||||
const float cameraSunAngle = static_cast<float>(
|
||||
glm::degrees(glm::acos(glm::dot(vecToSun, normalUnderCamera))
|
||||
));
|
||||
const float sunsetEnd = _atmosphereDimmingSunsetAngle.value().y;
|
||||
|
||||
// If cameraSunAngle is more than 90 degrees, we are in shaded part of globe
|
||||
const bool cameraIsInSun = cameraSunAngle <= sunsetEnd;
|
||||
// Atmosphere height is in KM
|
||||
const float atmosphereEdge = KM_TO_M * (_planetRadius + _atmosphereHeight);
|
||||
const bool cameraIsInAtmosphere = cameraDistance < atmosphereEdge;
|
||||
|
||||
// Don't fade if camera is not in the sunny part of an atmosphere
|
||||
if (!cameraIsInAtmosphere || !cameraIsInSun) {
|
||||
return;
|
||||
}
|
||||
// Else we need to fade the objects
|
||||
// Height of the atmosphere where the objects will be faded
|
||||
const float atmosphereFadingHeight =
|
||||
KM_TO_M * _atmosphereDimmingHeight * _atmosphereHeight;
|
||||
const float atmosphereInnerEdge = atmosphereEdge - atmosphereFadingHeight;
|
||||
const bool cameraIsInFadingRegion = cameraDistance > atmosphereInnerEdge;
|
||||
|
||||
// Check if camera is in sunset
|
||||
const float sunsetStart = _atmosphereDimmingSunsetAngle.value().x;
|
||||
const bool cameraIsInSunset = cameraSunAngle > sunsetStart && cameraIsInSun;
|
||||
|
||||
// See if we are inside of an eclipse shadow
|
||||
float eclipseShadow = _deferredcaster->eclipseShadow(cameraPos);
|
||||
const bool cameraIsInEclipse = std::abs(eclipseShadow - 1.f) > glm::epsilon<float>();
|
||||
// Invert shadow and multiply with itself to make it more narrow
|
||||
eclipseShadow = std::pow(1.f - eclipseShadow, 2.f);
|
||||
float atmosphereDimming = 0.f;
|
||||
|
||||
if (cameraIsInSunset) {
|
||||
// Fading - linear interpolation
|
||||
atmosphereDimming = (cameraSunAngle - sunsetStart) /
|
||||
(sunsetEnd - sunsetStart);
|
||||
}
|
||||
else if (cameraIsInFadingRegion && cameraIsInEclipse) {
|
||||
// Fade with regards to altitude & eclipse shadow
|
||||
// Fading - linear interpolation
|
||||
const float fading =
|
||||
(cameraDistance - atmosphereInnerEdge) / atmosphereFadingHeight;
|
||||
atmosphereDimming = std::clamp(eclipseShadow + fading, 0.f, 1.f);
|
||||
}
|
||||
else if (cameraIsInFadingRegion) {
|
||||
// Fade with regards to altitude
|
||||
// Fading - linear interpolation
|
||||
atmosphereDimming = (cameraDistance - atmosphereInnerEdge) /
|
||||
atmosphereFadingHeight;
|
||||
}
|
||||
else if (cameraIsInEclipse) {
|
||||
atmosphereDimming = eclipseShadow;
|
||||
}
|
||||
else {
|
||||
// Camera is below fading region - atmosphere dims objects completely
|
||||
atmosphereDimming = 0.f;
|
||||
}
|
||||
// Calculate dimming coefficient for stars, labels etc that are dimmed in the
|
||||
// atmosphere
|
||||
global::navigationHandler->camera()->setAtmosphereDimmingFactor(
|
||||
atmosphereDimming
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
@@ -65,7 +65,7 @@ namespace planetgeometry { class PlanetGeometry; }
|
||||
|
||||
class RenderableAtmosphere : public Renderable {
|
||||
public:
|
||||
RenderableAtmosphere(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableAtmosphere(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initializeGL() override;
|
||||
void deinitializeGL() override;
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
private:
|
||||
glm::dmat4 computeModelTransformMatrix(const openspace::TransformData& data);
|
||||
void updateAtmosphereParameters();
|
||||
void setDimmingCoefficient(const glm::dmat4& modelTransform);
|
||||
|
||||
properties::FloatProperty _atmosphereHeight;
|
||||
properties::FloatProperty _groundAverageReflectance;
|
||||
@@ -95,6 +96,9 @@ private:
|
||||
properties::FloatProperty _sunIntensity;
|
||||
properties::BoolProperty _sunFollowingCameraEnabled;
|
||||
properties::BoolProperty _hardShadowsEnabled;
|
||||
properties::FloatProperty _sunAngularSize;
|
||||
SceneGraphNode* _lightSourceNode = nullptr;
|
||||
properties::StringProperty _lightSourceNodeName;
|
||||
|
||||
// Atmosphere dimming
|
||||
properties::FloatProperty _atmosphereDimmingHeight;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
/*****************************************************************************************
|
||||
* Modified parts of the code (4D texture mechanism, analytical transmittance etc) *
|
||||
* from Eric Bruneton is used in the following code. *
|
||||
* from Eric Bruneton is used in the following code. *
|
||||
****************************************************************************************/
|
||||
|
||||
/**
|
||||
@@ -67,17 +67,17 @@ const float ATM_EPSILON = 1.0;
|
||||
// Calculate the distance of the ray starting at x (height r) until the planet's ground
|
||||
// or top of atmosphere
|
||||
// r := || vec(x) || e [0, Rt]
|
||||
// mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r
|
||||
// mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r
|
||||
float rayDistance(float r, float mu, float Rt, float Rg) {
|
||||
// The light ray starting at the observer in/on the atmosphere can have to possible end
|
||||
// points: the top of the atmosphere or the planet ground. So the shortest path is the
|
||||
// one we are looking for, otherwise we may be passing through the ground
|
||||
|
||||
|
||||
// cosine law
|
||||
float atmRadiusEps2 = (Rt + ATM_EPSILON) * (Rt + ATM_EPSILON);
|
||||
float mu2 = mu * mu;
|
||||
float r2 = r * r;
|
||||
float rayDistanceAtmosphere = -r * mu + sqrt(r2 * (mu2 - 1.0) + atmRadiusEps2);
|
||||
float rayDistanceAtmosphere = -r * mu + sqrt(r2 * (mu2 - 1.0) + atmRadiusEps2);
|
||||
float delta = r2 * (mu2 - 1.0) + Rg*Rg;
|
||||
|
||||
// Ray may be hitting ground
|
||||
@@ -108,7 +108,7 @@ void unmappingMuMuSunNu(float r, vec4 dhdH, int SAMPLES_MU, float Rg, float Rt,
|
||||
// Pre-calculations
|
||||
float r2 = r * r;
|
||||
float Rg2 = Rg * Rg;
|
||||
|
||||
|
||||
float halfSAMPLE_MU = float(SAMPLES_MU) / 2.0;
|
||||
// If the (vec(x) dot vec(v))/r is negative, i.e., the light ray has great probability
|
||||
// to touch the ground, we obtain mu considering the geometry of the ground
|
||||
@@ -130,7 +130,7 @@ void unmappingMuMuSunNu(float r, vec4 dhdH, int SAMPLES_MU, float Rg, float Rt,
|
||||
// cosine law: Rt^2 = r^2 + d^2 - 2rdcos(pi-theta) where cosine(theta) = mu
|
||||
mu = (Rt*Rt - r2 - d * d) / (2.0 * r * d);
|
||||
}
|
||||
|
||||
|
||||
float modValueMuSun = mod(fragment.x, float(SAMPLES_MU_S)) / (float(SAMPLES_MU_S) - 1.0);
|
||||
// The following mapping is different from the paper. See Collienne for an details.
|
||||
muSun = tan((2.0 * modValueMuSun - 1.0 + 0.26) * 1.1) / tan(1.26 * 1.1);
|
||||
@@ -148,7 +148,7 @@ vec3 transmittance(sampler2D tex, float r, float mu, float Rg, float Rt) {
|
||||
float u_r = sqrt((r - Rg) / (Rt - Rg));
|
||||
// See Collienne to understand the mapping
|
||||
float u_mu = atan((mu + 0.15) / 1.15 * tan(1.5)) / 1.5;
|
||||
|
||||
|
||||
return texture(tex, vec2(u_mu, u_r)).rgb;
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ vec3 transmittance(sampler2D tex, float r, float mu, float d, float Rg, float Rt
|
||||
// Here we use the transmittance property: T(x,v) = T(x,d)*T(d,v) to, given a distance
|
||||
// d, calculates that transmittance along that distance starting in x (height r):
|
||||
// T(x,d) = T(x,v)/T(d,v).
|
||||
//
|
||||
//
|
||||
// From cosine law: c^2 = a^2 + b^2 - 2*a*b*cos(ab)
|
||||
float ri = sqrt(d * d + r * r + 2.0 * r * d * mu);
|
||||
// mu_i = (vec(d) dot vec(v)) / r_i
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
/*****************************************************************************************
|
||||
* Modified parts of the code (4D texture mechanism) from Eric Bruneton is used in the *
|
||||
* following code. *
|
||||
* following code. *
|
||||
****************************************************************************************/
|
||||
|
||||
/**
|
||||
@@ -64,6 +64,7 @@ in vec2 texCoord;
|
||||
out vec4 renderTarget;
|
||||
|
||||
uniform int cullAtmosphere;
|
||||
uniform float opacity;
|
||||
uniform float Rg;
|
||||
uniform float Rt;
|
||||
uniform float groundRadianceEmission;
|
||||
@@ -76,6 +77,7 @@ uniform vec3 betaMieExtinction;
|
||||
uniform float mieG;
|
||||
uniform float sunRadiance;
|
||||
uniform bool ozoneLayerEnabled;
|
||||
uniform float sunAngularSize;
|
||||
uniform int SAMPLES_R;
|
||||
uniform int SAMPLES_MU;
|
||||
uniform int SAMPLES_MU_S;
|
||||
@@ -86,7 +88,7 @@ uniform sampler3D inscatterTexture;
|
||||
uniform sampler2D mainPositionTexture;
|
||||
uniform sampler2D mainNormalTexture;
|
||||
uniform sampler2D mainColorTexture;
|
||||
uniform dmat4 inverseModelTransformMatrix;
|
||||
uniform dmat4 inverseModelTransformMatrix;
|
||||
uniform dmat4 modelTransformMatrix;
|
||||
uniform dmat4 viewToWorldMatrix;
|
||||
uniform dmat4 projectionToModelTransformMatrix;
|
||||
@@ -118,40 +120,42 @@ uniform ShadowRenderingStruct shadowDataArray[numberOfShadows];
|
||||
uniform int shadows;
|
||||
uniform bool hardShadows;
|
||||
|
||||
float calcShadow(ShadowRenderingStruct shadowInfoArray[numberOfShadows], dvec3 position,
|
||||
// Returns whether there is an eclipse in the x component and the strength of the
|
||||
// shadowing in the y component
|
||||
vec2 calcShadow(ShadowRenderingStruct shadowInfoArray[numberOfShadows], dvec3 position,
|
||||
bool ground)
|
||||
{
|
||||
if (!shadowInfoArray[0].isShadowing) {
|
||||
return 1.0;
|
||||
return vec2(0.0, 1.0);
|
||||
}
|
||||
|
||||
dvec3 pc = shadowInfoArray[0].casterPositionVec - position;
|
||||
dvec3 scNorm = shadowInfoArray[0].sourceCasterVec;
|
||||
dvec3 pcProj = dot(pc, scNorm) * scNorm;
|
||||
dvec3 d = pc - pcProj;
|
||||
|
||||
|
||||
float length_d = float(length(d));
|
||||
double lengthPcProj = length(pcProj);
|
||||
|
||||
|
||||
float r_p_pi = float(shadowInfoArray[0].rc * (lengthPcProj + shadowInfoArray[0].xp) / shadowInfoArray[0].xp);
|
||||
float r_u_pi = float(shadowInfoArray[0].rc * (shadowInfoArray[0].xu - lengthPcProj) / shadowInfoArray[0].xu);
|
||||
|
||||
|
||||
if (length_d < r_u_pi) {
|
||||
// umbra
|
||||
if (hardShadows) {
|
||||
return ground ? 0.2 : 0.5;
|
||||
return ground ? vec2(1.0, 0.2) : vec2(1.0, 0.5);
|
||||
}
|
||||
else {
|
||||
// butterworth function
|
||||
return sqrt(r_u_pi / (r_u_pi + pow(length_d, 4.0)));
|
||||
return vec2(1.0, sqrt(r_u_pi / (r_u_pi + pow(length_d, 4.0))));
|
||||
}
|
||||
}
|
||||
else if (length_d < r_p_pi) {
|
||||
// penumbra
|
||||
return hardShadows ? 0.5 : length_d / r_p_pi;
|
||||
return hardShadows ? vec2(1.0, 0.5) : vec2(1.0, length_d / r_p_pi);
|
||||
}
|
||||
else {
|
||||
return 1.0;
|
||||
return vec2(1.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +244,7 @@ bool atmosphereIntersection(Ray ray, double atmRadius, out double offset,
|
||||
offset = 0.0;
|
||||
maxLength = s + q;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -257,7 +261,7 @@ Ray calculateRayRenderableGlobe(vec2 st) {
|
||||
// Clip to Object Coords
|
||||
dvec4 objectCoords = projectionToModelTransformMatrix * clipCoords;
|
||||
objectCoords.xyz /= objectCoords.w;
|
||||
|
||||
|
||||
// Building Ray
|
||||
// Ray in object space (in KM)
|
||||
Ray ray;
|
||||
@@ -266,7 +270,7 @@ Ray calculateRayRenderableGlobe(vec2 st) {
|
||||
return ray;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Calculates the light scattering in the view direction comming from other light rays
|
||||
* scattered in the atmosphere.
|
||||
* Following the paper: S[L]|x - T(x,xs) * S[L]|xs
|
||||
@@ -274,7 +278,7 @@ Ray calculateRayRenderableGlobe(vec2 st) {
|
||||
* position and zenith cosine angle as in the paper.
|
||||
* Arguments:
|
||||
* x := camera position
|
||||
* t := ray displacement variable after calculating the intersection with the
|
||||
* t := ray displacement variable after calculating the intersection with the
|
||||
* atmosphere. It is the distance from the camera to the last intersection with the
|
||||
* atmosphere. If the ray hits the ground, t is updated to the correct value
|
||||
* v := view direction (ray's direction) (normalized)
|
||||
@@ -292,7 +296,7 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
const float INTERPOLATION_EPS = 0.004; // precision const from Brunetton
|
||||
|
||||
vec3 radiance;
|
||||
|
||||
|
||||
mu = dot(x, v) / r;
|
||||
|
||||
float r2 = r * r;
|
||||
@@ -300,7 +304,7 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
float muSun = dot(x, s) / r;
|
||||
float rayleighPhase = rayleighPhaseFunction(nu);
|
||||
float miePhase = miePhaseFunction(nu, mieG);
|
||||
|
||||
|
||||
// S[L](x,s,v)
|
||||
// I.e. the next line has the scattering light for the "infinite" ray passing through
|
||||
// the atmosphere. If this ray hits something inside the atmosphere, we will subtract
|
||||
@@ -310,7 +314,7 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
SAMPLES_MU_S, SAMPLES_NU),
|
||||
0.0
|
||||
);
|
||||
|
||||
|
||||
// After removing the initial path from camera pos to top of atmosphere (for an
|
||||
// observer in the space) we test if the light ray is hitting the atmosphere
|
||||
float r0 = length(fragPosObj);
|
||||
@@ -319,18 +323,18 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
float mu0 = dot(fragPosObj, v) * invr0;
|
||||
|
||||
if ((pixelDepth > INTERPOLATION_EPS) && (pixelDepth < maxLength)) {
|
||||
t = float(pixelDepth);
|
||||
t = float(pixelDepth);
|
||||
groundHit = true;
|
||||
|
||||
|
||||
// Transmittance from point r, direction mu, distance t
|
||||
// By Analytical calculation
|
||||
// attenuation = analyticTransmittance(r, mu, t);
|
||||
// JCC: change from analytical to LUT transmittance to avoid
|
||||
// acme on planet surface when looking from far away. (11/02/2017)
|
||||
attenuation = transmittance(transmittanceTexture, r, mu, t, Rg, Rt);
|
||||
|
||||
attenuation = transmittance(transmittanceTexture, r, mu, t, Rg, Rt);
|
||||
|
||||
// Here we use the idea of S[L](a->b) = S[L](b->a), and get the S[L](x0, v, s)
|
||||
// Then we calculate S[L] = S[L]|x - T(x, x0)*S[L]|x0
|
||||
// Then we calculate S[L] = S[L]|x - T(x, x0)*S[L]|x0
|
||||
// The "infinite" ray hist something inside the atmosphere, so we need to remove
|
||||
// the unsused contribution to the final radiance.
|
||||
vec4 inscatterFromSurface = texture4D(inscatterTexture, r0, mu0, muSun0, nu, Rg,
|
||||
@@ -367,13 +371,13 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
float halfCosineLaw1 = r2 + (t * t);
|
||||
float halfCosineLaw2 = 2.0 * r * t;
|
||||
r0 = sqrt(halfCosineLaw1 + halfCosineLaw2 * mu);
|
||||
|
||||
|
||||
// From the dot product: cos(theta0) = (x0 dot v)/(||ro||*||v||)
|
||||
// mu0 = ((x + t) dot v) / r0
|
||||
// mu0 = (x dot v + t dot v) / r0
|
||||
// mu0 = (r*mu + t) / r0
|
||||
mu0 = (r * mu + t) * (1.0 / r0);
|
||||
|
||||
|
||||
vec4 inScatterAboveX = texture4D(inscatterTexture, r, mu, muSun, nu, Rg,
|
||||
SAMPLES_MU, Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU);
|
||||
vec4 inScatterAboveXs = texture4D(inscatterTexture, r0, mu0, muSun0, nu, Rg,
|
||||
@@ -385,9 +389,9 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
mu = muHorizon + INTERPOLATION_EPS;
|
||||
//r0 = sqrt(r2 + t2 + 2.0 * r * t * mu);
|
||||
r0 = sqrt(halfCosineLaw1 + halfCosineLaw2 * mu);
|
||||
|
||||
|
||||
mu0 = (r * mu + t) * (1.0 / r0);
|
||||
|
||||
|
||||
vec4 inScatterBelowX = texture4D(inscatterTexture, r, mu, muSun, nu, Rg,
|
||||
SAMPLES_MU, Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU);
|
||||
vec4 inScatterBelowXs = texture4D(inscatterTexture, r0, mu0, muSun0, nu, Rg,
|
||||
@@ -397,12 +401,12 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
|
||||
// Interpolate between above and below inScattering radiance
|
||||
inscatterRadiance = mix(inScatterAbove, inScatterBelow, interpolationValue);
|
||||
}
|
||||
}
|
||||
|
||||
// The w component of inscatterRadiance has stored the Cm,r value (Cm = Sm[L0])
|
||||
// So, we must reintroduce the Mie inscatter by the proximity rule as described in the
|
||||
// paper by Bruneton and Neyret in "Angular precision" paragraph:
|
||||
|
||||
|
||||
// Hermite interpolation between two values
|
||||
// This step is done because imprecision problems happen when the Sun is slightly
|
||||
// below the horizon. When this happens, we avoid the Mie scattering contribution
|
||||
@@ -410,19 +414,19 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
vec3 inscatterMie =
|
||||
inscatterRadiance.rgb * inscatterRadiance.a / max(inscatterRadiance.r, 1e-4) *
|
||||
(betaRayleigh.r / betaRayleigh);
|
||||
|
||||
radiance = max(inscatterRadiance.rgb * rayleighPhase + inscatterMie * miePhase, 0.0);
|
||||
|
||||
|
||||
radiance = max(inscatterRadiance.rgb * rayleighPhase + inscatterMie * miePhase, 0.0);
|
||||
|
||||
// Finally we add the Lsun (all calculations are done with no Lsun so we can change it
|
||||
// on the fly with no precomputations)
|
||||
vec3 finalScatteringRadiance = radiance * sunIntensity;
|
||||
return groundHit ? finalScatteringRadiance : spaceColor + finalScatteringRadiance;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Calculates the light reflected in the view direction comming from other light rays
|
||||
* integrated over the hemispehre plus the direct light (L0) from Sun.
|
||||
* Following the paper: R[L]= R[L0]+R[L*]
|
||||
* Following the paper: R[L]= R[L0]+R[L*]
|
||||
* The ray is x + tv, v the view direction, s is the sun direction, r and mu the position
|
||||
* and zenith cosine angle as in the paper.
|
||||
* As for all calculations in the atmosphere, the center of the coordinate system is the
|
||||
@@ -430,7 +434,7 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3
|
||||
* Arguments:
|
||||
* x := camera position
|
||||
* t := ray displacement variable. Here, differently from the inScatter light calculation,
|
||||
* the position of the camera is already offset (on top of atmosphere) or inside
|
||||
* the position of the camera is already offset (on top of atmosphere) or inside
|
||||
* the atmosphere
|
||||
* v := view direction (ray's direction) (normalized)
|
||||
* s := Sun direction (normalized)
|
||||
@@ -465,7 +469,7 @@ vec3 groundColor(vec3 x, float t, vec3 v, vec3 s, vec3 attenuationXtoX0, vec3 gr
|
||||
groundReflectance * mix(30.0, 1.0, smoothstep(-1.0, 0.05, dotNS)) * RLStar :
|
||||
groundReflectance * RLStar;
|
||||
|
||||
// Specular reflection from sun on oceans and rivers
|
||||
// Specular reflection from sun on oceans and rivers
|
||||
if ((waterReflectance > 0.1) && (muSun > 0.0)) {
|
||||
vec3 h = normalize(s - v);
|
||||
// Fresnell Schlick's approximation
|
||||
@@ -478,11 +482,11 @@ vec3 groundColor(vec3 x, float t, vec3 v, vec3 s, vec3 attenuationXtoX0, vec3 gr
|
||||
}
|
||||
|
||||
// Finally, we attenuate the surface Radiance from the point x0 to the camera location
|
||||
vec3 reflectedRadiance = attenuationXtoX0 * groundRadiance;
|
||||
return reflectedRadiance;
|
||||
vec3 reflectedRadiance = attenuationXtoX0 * groundRadiance;
|
||||
return reflectedRadiance;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Calculates the Sun color. The ray is x + tv, v the view direction, s is the sun
|
||||
* direction, r and mu the position and zenith cosine angle as in the paper. As for all
|
||||
* calculations in the atmosphere, the center of the coordinate system is the planet's
|
||||
@@ -502,11 +506,11 @@ vec3 sunColor(vec3 v, vec3 s, float r, float mu, float irradianceFactor) {
|
||||
|
||||
// @TODO (abock, 2021-07-01) This value is hard-coded to our sun+earth right now
|
||||
// Convert 0.3 degrees -> radians
|
||||
const float SunAngularSize = (0.3 * M_PI / 180.0);
|
||||
// const float SunAngularSize = (0.3 * M_PI / 180.0);
|
||||
const float FuzzyFactor = 0.5; // How fuzzy should the edges be
|
||||
|
||||
const float p1 = cos(SunAngularSize);
|
||||
const float p2 = cos(SunAngularSize * FuzzyFactor);
|
||||
float p1 = cos(sunAngularSize);
|
||||
float p2 = cos(sunAngularSize * FuzzyFactor);
|
||||
|
||||
float t = (angle - p1) / (p2 - p1);
|
||||
float scale = clamp(t, 0.0, 1.0);
|
||||
@@ -527,20 +531,20 @@ void main() {
|
||||
st.y = st.y / (resolution.y / viewport[3]) + (viewport[1] / resolution.y);
|
||||
|
||||
// Color from G-Buffer
|
||||
vec3 color = texture(mainColorTexture, st).rgb;
|
||||
vec4 color = texture(mainColorTexture, st);
|
||||
if (cullAtmosphere == 1) {
|
||||
renderTarget.rgb = color;
|
||||
renderTarget = color;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the ray from camera to atm in object space
|
||||
Ray ray = calculateRayRenderableGlobe(texCoord);
|
||||
|
||||
|
||||
double offset = 0.0; // in KM
|
||||
double maxLength = 0.0; // in KM
|
||||
bool intersect = atmosphereIntersection(ray, Rt - (ATM_EPSILON * 0.001), offset, maxLength);
|
||||
if (!intersect) {
|
||||
renderTarget.rgb = color;
|
||||
renderTarget = color;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -561,7 +565,7 @@ void main() {
|
||||
// Data in the mainPositionTexture are written in view space (view plus camera rig)
|
||||
vec4 position = texture(mainPositionTexture, st);
|
||||
|
||||
// OS Eye to World coords
|
||||
// OS Eye to World coords
|
||||
dvec4 positionWorldCoords = viewToWorldMatrix * position;
|
||||
|
||||
// World to Object (Normal and Position in meters)
|
||||
@@ -571,7 +575,7 @@ void main() {
|
||||
// JCC (12/12/2017): AMD distance function is buggy.
|
||||
//double pixelDepth = distance(cameraPositionInObject.xyz, positionObjectsCoords.xyz);
|
||||
double pixelDepth = length(camPosObj - positionObjectsCoords);
|
||||
|
||||
|
||||
// JCC (12/13/2017): Trick to remove floating error in texture.
|
||||
// We see a squared noise on planet's surface when seeing the planet from far away
|
||||
// @TODO (abock, 2021-07-01) I don't think this does anything. Remove?
|
||||
@@ -581,12 +585,12 @@ void main() {
|
||||
pixelDepth += 1000.0;
|
||||
const float alpha = 1000.0;
|
||||
const float beta = 1000000.0;
|
||||
const float x2 = 1e9;
|
||||
const float x2 = 1e9;
|
||||
const float diffGreek = beta - alpha;
|
||||
const float diffDist = x2 - x1;
|
||||
const float varA = diffGreek / diffDist;
|
||||
const float varB = (alpha - varA * x1);
|
||||
pixelDepth += double(varA * dC + varB);
|
||||
pixelDepth += double(varA * dC + varB);
|
||||
}
|
||||
|
||||
// All calculations are done in KM:
|
||||
@@ -595,12 +599,12 @@ void main() {
|
||||
|
||||
if (pixelDepth < offset) {
|
||||
// ATM Occluded - Something in front of ATM
|
||||
renderTarget.rgb = color;
|
||||
renderTarget = color;
|
||||
return;
|
||||
}
|
||||
|
||||
// Following paper nomenclature
|
||||
double t = offset;
|
||||
// Following paper nomenclature
|
||||
double t = offset;
|
||||
|
||||
// Moving observer from camera location to top atmosphere. If the observer is already
|
||||
// inside the atm, offset = 0.0 and no changes at all
|
||||
@@ -615,32 +619,34 @@ void main() {
|
||||
// adjust the pixelDepth for tdCalculateRayRenderableGlobe' offset so the next
|
||||
// comparison with the planet's ground make sense:
|
||||
pixelDepth -= offset;
|
||||
|
||||
|
||||
dvec3 onATMPos = (modelTransformMatrix * dvec4(x * 1000.0, 1.0)).xyz;
|
||||
float eclipseShadowATM = calcShadow(shadowDataArray, onATMPos, false);
|
||||
float sunIntensityInscatter = sunRadiance * eclipseShadowATM;
|
||||
vec2 eclipseShadowATM = calcShadow(shadowDataArray, onATMPos, false);
|
||||
float sunIntensityInscatter = sunRadiance * eclipseShadowATM.y;
|
||||
|
||||
float irradianceFactor = 0.0;
|
||||
|
||||
bool groundHit = false;
|
||||
vec3 attenuation;
|
||||
|
||||
vec3 attenuation;
|
||||
|
||||
vec3 inscatterColor = inscatterRadiance(x, tF, irradianceFactor, v, s, r,
|
||||
vec3(positionObjectsCoords), maxLength, pixelDepth, color, sunIntensityInscatter, mu,
|
||||
vec3(positionObjectsCoords), maxLength, pixelDepth, color.rgb, sunIntensityInscatter, mu,
|
||||
attenuation, groundHit);
|
||||
vec3 atmColor = vec3(0.0);
|
||||
if (groundHit) {
|
||||
float eclipseShadowPlanet = calcShadow(shadowDataArray, positionWorldCoords.xyz, true);
|
||||
float sunIntensityGround = sunRadiance * eclipseShadowPlanet;
|
||||
atmColor = groundColor(x, tF, v, s, attenuation, color, normal.xyz, irradianceFactor,
|
||||
vec2 eclipseShadowPlanet = calcShadow(shadowDataArray, positionWorldCoords.xyz, true);
|
||||
float sunIntensityGround = sunRadiance * eclipseShadowPlanet.y;
|
||||
atmColor = groundColor(x, tF, v, s, attenuation, color.rgb, normal.xyz, irradianceFactor,
|
||||
normal.w, sunIntensityGround);
|
||||
}
|
||||
else {
|
||||
// In order to get better performance, we are not tracing multiple rays per pixel
|
||||
// when the ray doesn't intersect the ground
|
||||
atmColor = sunColor(v, s, r, mu, irradianceFactor);
|
||||
}
|
||||
|
||||
// Final Color of ATM plus terrain:
|
||||
renderTarget = vec4(inscatterColor + atmColor, 1.0);;
|
||||
atmColor = sunColor(v, s, r, mu, irradianceFactor) * (1.0 - eclipseShadowATM.x);
|
||||
}
|
||||
|
||||
// Final Color of ATM plus terrain. We want to support opacity so we blend between the
|
||||
// planet color and the full atmosphere color using the opacity value
|
||||
vec3 c = mix(color.rgb, inscatterColor + atmColor, opacity);
|
||||
renderTarget = vec4(c, 1.0);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -21,7 +21,7 @@
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
|
||||
#version __CONTEXT__
|
||||
|
||||
layout (triangles) in;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,6 +27,6 @@
|
||||
out vec4 renderTableColor;
|
||||
|
||||
|
||||
void main() {
|
||||
void main() {
|
||||
renderTableColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -21,7 +21,7 @@
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
|
||||
#version __CONTEXT__
|
||||
|
||||
#include "atmosphere_common.glsl"
|
||||
@@ -100,7 +100,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
|
||||
// Now we get vec(v) and vec(s) from mu, muSun and nu:
|
||||
// Assuming:
|
||||
// z |theta
|
||||
// z |theta
|
||||
// |\ vec(v) ||vec(v)|| = 1
|
||||
// | \
|
||||
// |__\_____x
|
||||
@@ -121,7 +121,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
// 1 = sqrt(s.x*s.x + s.y*s.y + s.z*s.z)
|
||||
// s.y = sqrt(1 - s.x*s.x - s.z*s.z) = sqrt(1 - s.x*s.x - muSun*muSun)
|
||||
vec3 s = vec3(sx, sqrt(max(0.0, 1.0 - sx * sx - muSun2)), muSun);
|
||||
|
||||
|
||||
// In order to integrate over 4PI, we scan the sphere using the spherical coordinates
|
||||
// previously defined
|
||||
vec3 radianceJAcc = vec3(0.0);
|
||||
@@ -132,7 +132,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
float distanceToGround = 0.0;
|
||||
float groundReflectance = 0.0;
|
||||
vec3 groundTransmittance = vec3(0.0);
|
||||
|
||||
|
||||
// If the ray w can see the ground we must compute the transmittance
|
||||
// effect from the starting point x to the ground point in direction -vec(v):
|
||||
if (cosineTheta < cosHorizon) { // ray hits ground
|
||||
@@ -146,7 +146,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
// |\ distGround
|
||||
// r | \ alpha
|
||||
// | \/
|
||||
// | /
|
||||
// | /
|
||||
// | / Rg
|
||||
// |/
|
||||
// So cos(alpha) = ((vec(x)+vec(dg)) dot -vec(distG))/(||(vec(x)+vec(distG))|| * ||vec(distG)||)
|
||||
@@ -178,7 +178,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
float nuWV = dot(v, w);
|
||||
float phaseRayleighWV = rayleighPhaseFunction(nuWV);
|
||||
float phaseMieWV = miePhaseFunction(nuWV, mieG);
|
||||
|
||||
|
||||
vec3 groundNormal = (vec3(0.0, 0.0, r) + distanceToGround * w) / Rg;
|
||||
vec3 groundIrradiance = irradianceLUT(deltaETexture, dot(groundNormal, s), Rg);
|
||||
|
||||
@@ -194,7 +194,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
// light. We stored these values in the deltaS textures (Ray and Mie), and in order
|
||||
// to avoid problems with the high angle dependency in the phase functions, we don't
|
||||
// include the phase functions on those tables (that's why we calculate them now).
|
||||
if (firstIteration == 1) {
|
||||
if (firstIteration == 1) {
|
||||
float phaseRaySW = rayleighPhaseFunction(nuSW);
|
||||
float phaseMieSW = miePhaseFunction(nuSW, mieG);
|
||||
// We can now access the values for the single InScattering in the textures deltaS textures.
|
||||
@@ -204,7 +204,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU).rgb;
|
||||
|
||||
// Initial InScattering including the phase functions
|
||||
radianceJ1 += singleRay * phaseRaySW + singleMie * phaseMieSW;
|
||||
radianceJ1 += singleRay * phaseRaySW + singleMie * phaseMieSW;
|
||||
}
|
||||
else {
|
||||
// On line 9 of the algorithm, the texture table deltaSR is updated, so when we
|
||||
@@ -219,7 +219,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
// Finally, we add the atmospheric scale height (See: Radiation Transfer on the
|
||||
// Atmosphere and Ocean from Thomas and Stamnes, pg 9-10.
|
||||
radianceJAcc += radianceJ1 * (betaRayleigh * exp(-(r - Rg) / HR) * phaseRayleighWV +
|
||||
betaMieScattering * exp(-(r - Rg) / HM) * phaseMieWV) * dw;
|
||||
betaMieScattering * exp(-(r - Rg) / HM) * phaseMieWV) * dw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -41,11 +41,11 @@ void main() {
|
||||
// First we convert the window's fragment coordinate to texel coordinates
|
||||
vec3 rst = vec3(gl_FragCoord.xy, float(layer) + 0.5) /
|
||||
vec3(ivec3(SAMPLES_MU_S * SAMPLES_NU, SAMPLES_MU, SAMPLES_R));
|
||||
|
||||
|
||||
vec3 rayleighInscattering = texture(deltaSRTexture, rst).rgb;
|
||||
float mieInscattering = texture(deltaSMTexture, rst).r;
|
||||
|
||||
|
||||
// We are using only the red component of the Mie scattering. See the Precomputed
|
||||
// Atmosphere Scattering paper for details about the angular precision
|
||||
renderTarget = vec4(rayleighInscattering, mieInscattering);
|
||||
renderTarget = vec4(rayleighInscattering, mieInscattering);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -21,7 +21,7 @@
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
|
||||
#version __CONTEXT__
|
||||
|
||||
#include "atmosphere_common.glsl"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -45,7 +45,7 @@ uniform float r;
|
||||
uniform vec4 dhdH;
|
||||
|
||||
|
||||
void integrand(float r, float mu, float muSun, float nu, float y, out vec3 S_R,
|
||||
void integrand(float r, float mu, float muSun, float nu, float y, out vec3 S_R,
|
||||
out vec3 S_M)
|
||||
{
|
||||
// The integral's integrand is the single inscattering radiance:
|
||||
@@ -59,10 +59,10 @@ void integrand(float r, float mu, float muSun, float nu, float y, out vec3 S_R,
|
||||
// angular precision
|
||||
S_R = vec3(0.0);
|
||||
S_M = vec3(0.0);
|
||||
|
||||
|
||||
// cosine law
|
||||
float ri = max(sqrt(r * r + y * y + 2.0 * r * mu * y), Rg);
|
||||
|
||||
|
||||
// Considering the Sun as a parallel light source, thew vector s_i = s.
|
||||
// So muSun_i = (vec(y_i) dot vec(s))/r_i = ((vec(x) + vec(yi-x)) dot vec(s))/r_i
|
||||
// muSun_i = (vec(x) dot vec(s) + vec(yi-x) dot vec(s))/r_i = (r*muSun + yi*nu)/r_i
|
||||
@@ -103,7 +103,7 @@ void inscatter(float r, float mu, float muSun, float nu, out vec3 S_R, out vec3
|
||||
vec3 S_Ri;
|
||||
vec3 S_Mi;
|
||||
integrand(r, mu, muSun, nu, 0.0, S_Ri, S_Mi);
|
||||
for (int i = 1; i <= INSCATTER_INTEGRAL_SAMPLES; ++i) {
|
||||
for (int i = 1; i <= INSCATTER_INTEGRAL_SAMPLES; i++) {
|
||||
float yj = float(i) * dy;
|
||||
vec3 S_Rj;
|
||||
vec3 S_Mj;
|
||||
@@ -123,7 +123,7 @@ void main() {
|
||||
// parameters (uv), we unmapping mu, muSun and nu.
|
||||
float mu, muSun, nu;
|
||||
unmappingMuMuSunNu(r, dhdH, SAMPLES_MU, Rg, Rt, SAMPLES_MU_S, SAMPLES_NU, mu, muSun, nu);
|
||||
|
||||
|
||||
// Here we calculate the single inScattered light. Because this is a single
|
||||
// inscattering, the light that arrives at a point y in the path from the eye to the
|
||||
// infinity (top of atmosphere or planet's ground), comes only from the light source,
|
||||
@@ -135,7 +135,7 @@ void main() {
|
||||
// S[L0] = P_R*S_R[L0] + P_M*S_M[L0]
|
||||
// In order to save memory, we just store the red component of S_M[L0], and later we use
|
||||
// the proportionality rule to calcule the other components.
|
||||
vec3 S_R; // First Order Rayleigh InScattering
|
||||
vec3 S_R; // First Order Rayleigh InScattering
|
||||
vec3 S_M; // First Order Mie InScattering
|
||||
inscatter(r, mu, muSun, nu, S_R, S_M);
|
||||
renderTarget1 = vec4(S_R, 1.0);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -64,11 +64,11 @@ vec3 inscatter(float r, float mu, float muSun, float nu) {
|
||||
vec3 inScatteringRadiance = vec3(0.0);
|
||||
float dy = rayDistance(r, mu, Rt, Rg) / float(INSCATTER_INTEGRAL_SAMPLES);
|
||||
vec3 inScatteringRadiance_i = integrand(r, mu, muSun, nu, 0.0);
|
||||
|
||||
|
||||
// In order to solve the integral from equation (11) we use the trapezoidal rule:
|
||||
// Integral(f(y)dy)(from a to b) = ((b-a)/2n_steps)*(Sum(f(y_i+1)+f(y_i)))
|
||||
// where y_i+1 = y_j
|
||||
for (int i = 1; i <= INSCATTER_INTEGRAL_SAMPLES; ++i) {
|
||||
for (int i = 1; i <= INSCATTER_INTEGRAL_SAMPLES; i++) {
|
||||
float y_j = float(i) * dy;
|
||||
vec3 inScatteringRadiance_j = integrand(r, mu, muSun, nu, y_j);
|
||||
inScatteringRadiance += (inScatteringRadiance_i + inScatteringRadiance_j) / 2.0 * dy;
|
||||
@@ -84,7 +84,7 @@ void main() {
|
||||
float nu = 0.0;
|
||||
// Unmapping the variables from texture texels coordinates to mapped coordinates
|
||||
unmappingMuMuSunNu(r, dhdH, SAMPLES_MU, Rg, Rt, SAMPLES_MU_S, SAMPLES_NU, mu, muSun, nu);
|
||||
|
||||
// Write to texture deltaSR
|
||||
|
||||
// Write to texture deltaSR
|
||||
renderTarget = vec4(inscatter(r, mu, muSun, nu), 1.0);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -30,9 +30,9 @@ uniform ivec2 OTHER_TEXTURES;
|
||||
uniform sampler2D deltaETexture;
|
||||
|
||||
|
||||
void main() {
|
||||
void main() {
|
||||
vec2 uv = gl_FragCoord.xy / vec2(OTHER_TEXTURES);
|
||||
|
||||
// Update texture E with E plus deltaE textures.
|
||||
renderTableColor = texture(deltaETexture, uv);
|
||||
renderTableColor = texture(deltaETexture, uv);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -21,7 +21,7 @@
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
|
||||
#version __CONTEXT__
|
||||
|
||||
#include "atmosphere_common.glsl"
|
||||
@@ -60,9 +60,9 @@ void main() {
|
||||
// In order to solve the integral from equation (15) we use the trapezoidal rule:
|
||||
// Integral(f(y)dy)(from a to b) = ((b-a)/2n_steps)*(Sum(f(y_i+1)+f(y_i)))
|
||||
vec3 irradianceE = vec3(0.0);
|
||||
for (int iphi = 0; iphi < IRRADIANCE_INTEGRAL_SAMPLES; ++iphi) {
|
||||
for (int iphi = 0; iphi < IRRADIANCE_INTEGRAL_SAMPLES; iphi++) {
|
||||
float phi = (float(iphi) + 0.5) * stepPhi;
|
||||
for (int itheta = 0; itheta < IRRADIANCE_INTEGRAL_SAMPLES; ++itheta) {
|
||||
for (int itheta = 0; itheta < IRRADIANCE_INTEGRAL_SAMPLES; itheta++) {
|
||||
float theta = (float(itheta) + 0.5) * stepTheta;
|
||||
// spherical coordinates: dw = dtheta*dphi*sin(theta)*rho^2
|
||||
// rho = 1, we are integrating over a unit sphere
|
||||
@@ -98,5 +98,5 @@ void main() {
|
||||
}
|
||||
|
||||
// Write the higher order irradiance to texture deltaE
|
||||
renderTableColor = vec4(irradianceE, 0.0);
|
||||
renderTableColor = vec4(irradianceE, 0.0);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -47,14 +47,14 @@ const int TRANSMITTANCE_STEPS = 500;
|
||||
// r := height of starting point vect(x)
|
||||
// mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r
|
||||
// H := Thickness of atmosphere if its density were uniform (used for Rayleigh and Mie)
|
||||
float opticalDepth(float r, float mu, float H) {
|
||||
float opticalDepth(float r, float mu, float H) {
|
||||
float r2 = r * r;
|
||||
// Is ray below horizon? The transmittance table will have only the values for
|
||||
// transmittance starting at r (x) until the light ray touches the atmosphere or the
|
||||
// ground and only for view angles v between 0 and pi/2 + eps. That's because we can
|
||||
// calculate the transmittance for angles bigger than pi/2 just inverting the ray
|
||||
// direction and starting and ending points.
|
||||
|
||||
|
||||
// cosine law for triangles: y_i^2 = a^2 + b^2 - 2abcos(alpha)
|
||||
float cosZenithHorizon = -sqrt(1.0 - ((Rg * Rg) / r2));
|
||||
if (mu < cosZenithHorizon) {
|
||||
@@ -67,9 +67,9 @@ float opticalDepth(float r, float mu, float H) {
|
||||
float deltaStep = b_a / float(TRANSMITTANCE_STEPS);
|
||||
// cosine law
|
||||
float y_i = exp(-(r - Rg) / H);
|
||||
|
||||
|
||||
float accumulation = 0.0;
|
||||
for (int i = 1; i <= TRANSMITTANCE_STEPS; ++i) {
|
||||
for (int i = 1; i <= TRANSMITTANCE_STEPS; i++) {
|
||||
float x_i = float(i) * deltaStep;
|
||||
// cosine law for triangles: y_i^2 = a^2 + b^2 - 2abcos(alpha)
|
||||
// In this case, a = r, b = x_i and cos(alpha) = cos(PI-zenithView) = mu
|
||||
@@ -84,11 +84,11 @@ float opticalDepth(float r, float mu, float H) {
|
||||
void main() {
|
||||
float u_mu = gl_FragCoord.x / float(TRANSMITTANCE.x);
|
||||
float u_r = gl_FragCoord.y / float(TRANSMITTANCE.y);
|
||||
|
||||
|
||||
// In the paper u_r^2 = (r^2-Rg^2)/(Rt^2-Rg^2)
|
||||
// So, extracting r from u_r in the above equation:
|
||||
float r = Rg + (u_r * u_r) * (Rt - Rg);
|
||||
|
||||
|
||||
// In the paper the Bruneton suggest mu = dot(v,x)/||x|| with ||v|| = 1.0
|
||||
// Later he proposes u_mu = (1-exp(-3mu-0.6))/(1-exp(-3.6))
|
||||
// But the below one is better. See Collienne.
|
||||
@@ -99,9 +99,9 @@ void main() {
|
||||
if (ozoneLayerEnabled) {
|
||||
ozoneContribution = betaOzoneExtinction * 0.0000006 * opticalDepth(r, muSun, HO);
|
||||
}
|
||||
vec3 opDepth = ozoneContribution +
|
||||
vec3 opDepth = ozoneContribution +
|
||||
betaMieExtinction * opticalDepth(r, muSun, HM) +
|
||||
betaRayleigh * opticalDepth(r, muSun, HR);
|
||||
|
||||
|
||||
renderTableColor = vec4(exp(-opDepth), 0.0);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2022 #
|
||||
# Copyright (c) 2014-2025 #
|
||||
# #
|
||||
# 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 #
|
||||
@@ -22,16 +22,39 @@
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
|
||||
##########################################################################################
|
||||
|
||||
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/support/cmake/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
rendering/renderabledistancelabel.h
|
||||
audiomodule.h
|
||||
)
|
||||
source_group("Header Files" FILES ${HEADER_FILES})
|
||||
|
||||
set(SOURCE_FILES
|
||||
rendering/renderabledistancelabel.cpp
|
||||
audiomodule.cpp
|
||||
audiomodule_lua.inl
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
create_new_module("VisLab" vislab_module ${HEADER_FILES} ${SOURCE_FILES})
|
||||
create_new_module(
|
||||
"Audio"
|
||||
audio_module
|
||||
STATIC
|
||||
${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES}
|
||||
)
|
||||
|
||||
begin_dependency("SoLoud")
|
||||
set(SOLOUD_BACKEND_SDL2 OFF CACHE BOOL "")
|
||||
if (WIN32)
|
||||
set(SOLOUD_BACKEND_WINMM ON CACHE BOOL "")
|
||||
elseif (UNIX AND NOT APPLE)
|
||||
set(SOLOUD_BACKEND_ALSA ON CACHE BOOL "")
|
||||
elseif (APPLE)
|
||||
set(SOLOUD_BACKEND_COREAUDIO ON CACHE BOOL "")
|
||||
endif ()
|
||||
add_subdirectory(ext/soloud/contrib SYSTEM)
|
||||
|
||||
# Unfortunately, the soloud cmake tarket doesn't set the include directories correctly
|
||||
target_include_directories(openspace-module-audio SYSTEM PRIVATE ext/soloud/include)
|
||||
target_link_libraries(openspace-module-audio PRIVATE soloud)
|
||||
set_property(TARGET soloud PROPERTY FOLDER "External")
|
||||
end_dependency("SoLoud")
|
||||
418
modules/audio/audiomodule.cpp
Normal file
418
modules/audio/audiomodule.cpp
Normal file
@@ -0,0 +1,418 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/audio/audiomodule.h>
|
||||
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/globalscallbacks.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <soloud.h>
|
||||
#include <soloud_wav.h>
|
||||
|
||||
#include "audiomodule_lua.inl"
|
||||
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "AudioModule";
|
||||
|
||||
struct [[codegen::Dictionary(AudioModule)]] Parameters {
|
||||
// Sets the maximum number of simultaneous channels that can be played back by the
|
||||
// audio subsystem. If this value is not specified, it defaults to 128.
|
||||
std::optional<int> maxNumberOfChannels [[codegen::greater(0)]];
|
||||
};
|
||||
|
||||
#include "audiomodule_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation AudioModule::Documentation() {
|
||||
return codegen::doc<Parameters>("module_audio");
|
||||
}
|
||||
|
||||
AudioModule::AudioModule()
|
||||
: OpenSpaceModule(Name)
|
||||
, _engine(std::make_unique<SoLoud::Soloud>())
|
||||
{}
|
||||
|
||||
AudioModule::~AudioModule() {}
|
||||
|
||||
void AudioModule::internalInitialize(const ghoul::Dictionary& dictionary) {
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
LDEBUG(std::format("Initializing SoLoud version: {}", SOLOUD_VERSION));
|
||||
|
||||
_engine->init();
|
||||
_engine->setGlobalVolume(0.5f);
|
||||
const int nChannels = p.maxNumberOfChannels.value_or(16);
|
||||
_engine->setMaxActiveVoiceCount(static_cast<unsigned int>(nChannels));
|
||||
|
||||
global::callback::postDraw->emplace_back([this]() {
|
||||
if (!_sounds.empty()) {
|
||||
_engine->update3dAudio();
|
||||
}
|
||||
});
|
||||
|
||||
const char* backend = _engine->getBackendString();
|
||||
if (backend) {
|
||||
LDEBUG(std::format("Audio backend: {}", backend));
|
||||
LDEBUG(std::format("Number of channels: {}", _engine->getBackendChannels()));
|
||||
}
|
||||
else {
|
||||
LINFO(std::format("Audio subsystem disabled as no backend was detected"));
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::internalDeinitializeGL() {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
_sounds.clear();
|
||||
_engine->deinit();
|
||||
_engine = nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<SoLoud::Wav> AudioModule::loadSound(const std::filesystem::path& path) {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
std::unique_ptr<SoLoud::Wav> sound = std::make_unique<SoLoud::Wav>();
|
||||
const std::string p = path.string();
|
||||
SoLoud::result res = sound->load(p.c_str());
|
||||
if (res != 0) {
|
||||
throw ghoul::RuntimeError(std::format(
|
||||
"Error loading sound from {}. {}: {}",
|
||||
path, static_cast<int>(res), _engine->getErrorString(res)
|
||||
));
|
||||
}
|
||||
|
||||
// While we are loading a sound, we also want to do a little garbage collection on our
|
||||
// internal data structure to remove the songs that someone has loaded at some point
|
||||
// and that have since organically stopped. In general, this should only happen if the
|
||||
// song was started without looping and has ended
|
||||
for (auto it = _sounds.begin(); it != _sounds.end();) {
|
||||
if (!isPlaying(it->first)) {
|
||||
// We have found one of the candidates
|
||||
LDEBUG(std::format("Removing song {} as it has ended", it->first));
|
||||
_sounds.erase(it);
|
||||
// It is easier to just reset the iterator to the beginning than deal with the
|
||||
// off-by-one error when deleting the last element in the list and the
|
||||
// subsequent crash
|
||||
it = _sounds.begin();
|
||||
}
|
||||
else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
void AudioModule::playAudio(const std::filesystem::path& path, std::string identifier,
|
||||
ShouldLoop loop)
|
||||
{
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
if (_sounds.find(identifier) != _sounds.end()) {
|
||||
LERROR(std::format("Sound with name '{}' already played", identifier));
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<SoLoud::Wav> sound = loadSound(path);
|
||||
sound->setLooping(loop);
|
||||
SoLoud::handle handle = _engine->playBackground(*sound);
|
||||
|
||||
ghoul_assert(_sounds.find(identifier) == _sounds.end(), "Handle already used");
|
||||
_sounds[identifier] = {
|
||||
.sound = std::move(sound),
|
||||
.handle = handle
|
||||
};
|
||||
}
|
||||
|
||||
void AudioModule::playAudio3d(const std::filesystem::path& path, std::string identifier,
|
||||
const glm::vec3& position, ShouldLoop loop)
|
||||
{
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
if (_sounds.find(identifier) != _sounds.end()) {
|
||||
LERROR(std::format("Sound with name '{}' already played", identifier));
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<SoLoud::Wav> sound = loadSound(path);
|
||||
sound->setLooping(loop);
|
||||
SoLoud::handle handle = _engine->play3d(*sound, position.x, position.y, position.z);
|
||||
|
||||
_sounds[identifier] = {
|
||||
.sound = std::move(sound),
|
||||
.handle = handle
|
||||
};
|
||||
}
|
||||
|
||||
void AudioModule::stopAudio(const std::string& identifier) {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
_engine->stop(it->second.handle);
|
||||
_sounds.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::stopAll() {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
_engine->stopAll();
|
||||
_sounds.clear();
|
||||
}
|
||||
|
||||
void AudioModule::pauseAll() const {
|
||||
for (const std::pair<const std::string, Info>& sound : _sounds) {
|
||||
pauseAudio(sound.first);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::resumeAll() const {
|
||||
for (const std::pair<const std::string, Info>& sound : _sounds) {
|
||||
resumeAudio(sound.first);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::playAllFromStart() const {
|
||||
for (const std::pair<const std::string, Info>& sound : _sounds) {
|
||||
_engine->seek(sound.second.handle, 0.f);
|
||||
}
|
||||
resumeAll();
|
||||
}
|
||||
|
||||
bool AudioModule::isPlaying(const std::string& identifier) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
return _engine->isValidVoiceHandle(it->second.handle);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::pauseAudio(const std::string& identifier) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
_engine->setPause(it->second.handle, true);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::resumeAudio(const std::string& identifier) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
_engine->setPause(it->second.handle, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioModule::isPaused(const std::string& identifier) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
const bool isPaused = _engine->getPause(it->second.handle);
|
||||
return isPaused;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::setLooping(const std::string& identifier, ShouldLoop loop) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
_engine->setLooping(it->second.handle, loop);
|
||||
}
|
||||
}
|
||||
|
||||
AudioModule::ShouldLoop AudioModule::isLooping(const std::string& identifier) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
return _engine->getLooping(it->second.handle) ? ShouldLoop::Yes : ShouldLoop::No;
|
||||
}
|
||||
else {
|
||||
return ShouldLoop::No;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::setVolume(const std::string& identifier, float volume, float fade) const
|
||||
{
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it == _sounds.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We clamp the volume level between [0, 1] to not accidentally blow any speakers
|
||||
volume = glm::clamp(volume, 0.f, 1.f);
|
||||
if (fade == 0.f) {
|
||||
_engine->setVolume(it->second.handle, volume);
|
||||
}
|
||||
else {
|
||||
_engine->fadeVolume(it->second.handle, volume, fade);
|
||||
}
|
||||
}
|
||||
|
||||
float AudioModule::volume(const std::string& identifier) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
const float volume = _engine->getVolume(it->second.handle);
|
||||
return volume;
|
||||
}
|
||||
else {
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::set3dSourcePosition(const std::string& identifier,
|
||||
const glm::vec3& position) const
|
||||
{
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
auto it = _sounds.find(identifier);
|
||||
if (it != _sounds.end()) {
|
||||
_engine->set3dSourcePosition(
|
||||
it->second.handle,
|
||||
position.x, position.y, position.z
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> AudioModule::currentlyPlaying() const {
|
||||
// This function is *technically* not the ones that are playing, but that ones that we
|
||||
// are keeping track of. So we still have songs in our internal data structure that
|
||||
// were started as not-looping and that have ended playing. We need to filter them out
|
||||
// here.
|
||||
// The alternative would be to have a periodic garbage collection running, but that
|
||||
// feels worse. We are doing the garbage collection in the two playAudio functions
|
||||
// instead, since we have to do some work their either way
|
||||
|
||||
std::vector<std::string> res;
|
||||
res.reserve(_sounds.size());
|
||||
for (const std::pair<const std::string, Info>& sound : _sounds) {
|
||||
if (isPlaying(sound.first)) {
|
||||
res.push_back(sound.first);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void AudioModule::setGlobalVolume(float volume, float fade) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
// We clamp the volume level between [0, 1] to not accidentally blow any speakers
|
||||
volume = glm::clamp(volume, 0.f, 1.f);
|
||||
if (fade == 0.f) {
|
||||
_engine->setGlobalVolume(volume);
|
||||
}
|
||||
else {
|
||||
_engine->fadeGlobalVolume(volume, fade);
|
||||
}
|
||||
}
|
||||
|
||||
float AudioModule::globalVolume() const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
return _engine->getGlobalVolume();
|
||||
}
|
||||
|
||||
void AudioModule::set3dListenerParameters(const std::optional<glm::vec3>& position,
|
||||
const std::optional<glm::vec3>& lookAt,
|
||||
const std::optional<glm::vec3>& up) const
|
||||
{
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
if (position.has_value()) {
|
||||
_engine->set3dListenerPosition(position->x, position->y, position->z);
|
||||
}
|
||||
if (lookAt.has_value()) {
|
||||
_engine->set3dListenerAt(lookAt->x, lookAt->y, lookAt->z);
|
||||
}
|
||||
if (up.has_value()) {
|
||||
_engine->set3dListenerUp(up->x, up->y, up->z);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioModule::setSpeakerPosition(int channel, const glm::vec3& position) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
_engine->setSpeakerPosition(channel, position.x, position.y, position.z);
|
||||
}
|
||||
|
||||
glm::vec3 AudioModule::speakerPosition(int channel) const {
|
||||
ghoul_assert(_engine, "No audio engine loaded");
|
||||
|
||||
float x = 0.f;
|
||||
float y = 0.f;
|
||||
float z = 0.f;
|
||||
_engine->getSpeakerPosition(channel, x, y, z);
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
std::vector<documentation::Documentation> AudioModule::documentations() const {
|
||||
return {
|
||||
};
|
||||
}
|
||||
|
||||
scripting::LuaLibrary AudioModule::luaLibrary() const {
|
||||
return {
|
||||
"audio",
|
||||
{
|
||||
codegen::lua::PlayAudio,
|
||||
codegen::lua::PlayAudio3d,
|
||||
codegen::lua::StopAudio,
|
||||
codegen::lua::StopAll,
|
||||
codegen::lua::PauseAll,
|
||||
codegen::lua::ResumeAll,
|
||||
codegen::lua::PlayAllFromStart,
|
||||
codegen::lua::IsPlaying,
|
||||
codegen::lua::PauseAudio,
|
||||
codegen::lua::ResumeAudio,
|
||||
codegen::lua::IsPaused,
|
||||
codegen::lua::SetLooping,
|
||||
codegen::lua::IsLooping,
|
||||
codegen::lua::SetVolume,
|
||||
codegen::lua::Volume,
|
||||
codegen::lua::Set3dSourcePosition,
|
||||
codegen::lua::CurrentlyPlaying,
|
||||
codegen::lua::SetGlobalVolume,
|
||||
codegen::lua::GlobalVolume,
|
||||
codegen::lua::Set3dListenerPosition,
|
||||
codegen::lua::SetSpeakerPosition,
|
||||
codegen::lua::SpeakerPosition
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
318
modules/audio/audiomodule.h
Normal file
318
modules/audio/audiomodule.h
Normal file
@@ -0,0 +1,318 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_AUDIO___AUDIOMODULE___H__
|
||||
#define __OPENSPACE_MODULE_AUDIO___AUDIOMODULE___H__
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/scripting/lualibrary.h>
|
||||
#include <ghoul/misc/boolean.h>
|
||||
|
||||
namespace SoLoud {
|
||||
class Soloud;
|
||||
class Wav;
|
||||
} // namespace SoLoud
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class AudioModule : public OpenSpaceModule {
|
||||
public:
|
||||
constexpr static const char* Name = "Audio";
|
||||
|
||||
AudioModule();
|
||||
~AudioModule() override;
|
||||
std::vector<documentation::Documentation> documentations() const override;
|
||||
|
||||
scripting::LuaLibrary luaLibrary() const override;
|
||||
|
||||
BooleanType(ShouldLoop);
|
||||
|
||||
/**
|
||||
* Starts playing the audio file located and the provided \p path. The \p loop
|
||||
* parameter determines whether the file is only played once, or on a loop. The sound
|
||||
* is later referred to by the \p identifier name. The audio file will be played in
|
||||
* "background" mode, which means that each channel will be played at full volume. To
|
||||
* play a video using spatial audio, use the #playAudio3d function instead.
|
||||
*
|
||||
* \param path The audio file that should be played
|
||||
* \param identifier The name for the sound that is used to refer to the sound
|
||||
* \param loop If `Yes` then the song will be played in a loop until the program is
|
||||
* closed or the playing is stopped through the #stopAudio function
|
||||
*/
|
||||
void playAudio(const std::filesystem::path& path, std::string identifier,
|
||||
ShouldLoop loop);
|
||||
|
||||
/**
|
||||
* Starts playing the audio file located and the provided \p path. The \p loop
|
||||
* parameter determines whether the file is only played once, or on a loop. The sound
|
||||
* is later referred to by the \p identifier name. The \p position parameter
|
||||
* determines the spatial location of the sound in a meter-based coordinate system.
|
||||
* The position of the listener is (0,0,0) with the forward direction along the +y
|
||||
* axis. This means that the "left" channel in a stereo setting is towards -x and the
|
||||
* "right" channel towards x. This default value can be customized through the
|
||||
* #set3dListenerParameters function. If you want to play a video without spatial
|
||||
* audio, use the #playAudio function instead.
|
||||
*
|
||||
* \param path The audio file that should be played
|
||||
* \param identifier The name for the sound that is used to refer to the sound
|
||||
* \param position The position of the audio file in the 3D environment
|
||||
* \param loop If `Yes` then the song will be played in a loop until the program is
|
||||
* closed or the playing is stopped through the #stopAudio function
|
||||
*/
|
||||
void playAudio3d(const std::filesystem::path& path, std::string identifier,
|
||||
const glm::vec3& position, ShouldLoop loop);
|
||||
|
||||
/**
|
||||
* Stops the audio referenced by the \p identifier. The \p identifier must be a name
|
||||
* for a sound that was started through the #playAudio or #playAudio3d functions.
|
||||
* After this function, the \p identifier can not be used for any other function
|
||||
* anymore except for #playAudio or #playAudio3d to start a new sound.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
*/
|
||||
void stopAudio(const std::string& identifier);
|
||||
|
||||
/**
|
||||
* Stops all currently playing tracks. After this function, none of the identifiers
|
||||
* used to previously play a sound a valid any longer, but can still be used by the
|
||||
* #playAudio or #playAudio3d functions to start a new sound.
|
||||
* This function behaves the same way as if manually calling #stopAudio on all of the
|
||||
* sounds that have been started.
|
||||
*/
|
||||
void stopAll();
|
||||
|
||||
/**
|
||||
* Pauses the playback for all sounds, while keeping them valid. This function behaves
|
||||
* the same as if calling #pauseAudio on all of the sounds that are currently playing.
|
||||
*/
|
||||
void pauseAll() const;
|
||||
|
||||
/**
|
||||
* Resumes the playback for all sounds that have been paused. Please note that this
|
||||
* will also resume the playback for the sounds that have been manually paused, not
|
||||
* just those that were paused through the #pauseAll function.
|
||||
*/
|
||||
void resumeAll() const;
|
||||
|
||||
/**
|
||||
* Takes all of the sounds that are currently registers, unpauses them and plays them
|
||||
* from their starting points
|
||||
*/
|
||||
void playAllFromStart() const;
|
||||
|
||||
/**
|
||||
* Returns whether the track referred to by the \p identifier is currently playing. A
|
||||
* volume of 0 is still considered to be playing. The \p identifier must be a name for
|
||||
* a sound that was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \return `true` if the track is currently playing, `false` otherwise
|
||||
*/
|
||||
bool isPlaying(const std::string& identifier) const;
|
||||
|
||||
/**
|
||||
* Pauses the playback of the track referred to by the \p identifier. The playback can
|
||||
* later be resumed through the #resumeAudio function. Trying to pause an already
|
||||
* paused track will not do anything, but is valid. The \p identifier must be a name
|
||||
* for a sound that was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
*/
|
||||
void pauseAudio(const std::string& identifier) const;
|
||||
|
||||
/**
|
||||
* Resumes the playback of a track that was previously paused through the #pauseAudio
|
||||
* function. Trying to resume an already playing track will not do anything, but is
|
||||
* valid. The \p identifier must be a name for a sound that was started through the
|
||||
* #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
*/
|
||||
void resumeAudio(const std::string& identifier) const;
|
||||
|
||||
/**
|
||||
* Returns whether the track refered to by the \p identifier is currently playing or
|
||||
* paused. If it was be paused through a previous call to #pauseAudio, this function
|
||||
* will return `true`. If it has just been created or resumed through a call to
|
||||
* #resumeAudio, it will return `false`. The \p identifier must be a name for a sound
|
||||
* that was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \return `true` if the track is currently paused, `false` if it is playing
|
||||
*/
|
||||
bool isPaused(const std::string& identifier) const;
|
||||
|
||||
/**
|
||||
* Controls whether the track referred to by the \p identifier should be looping or
|
||||
* just be played once. If a track is converted to not looping, it will finish playing
|
||||
* until the end of the file. The \p identifier must be a name for a sound that was
|
||||
* started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \param loop If `Yes` then the song will be played in a loop until the program is
|
||||
* closed or the playing is stopped through the #stopAudio function
|
||||
*/
|
||||
void setLooping(const std::string& identifier, ShouldLoop loop) const;
|
||||
|
||||
/**
|
||||
* Returns whether the track referred to by the \p identifier is set to be looping or
|
||||
* whether it should played only once. The \p identifier must be a name for a sound
|
||||
* that was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \return `Yes` if the track is looping, `No` otherwise
|
||||
*/
|
||||
ShouldLoop isLooping(const std::string& identifier) const;
|
||||
|
||||
/**
|
||||
* Sets the volume of the track referred to by \p identifier to the new \p volume. The
|
||||
* volume should be a number bigger than 0, where 1 is the maximum volume level.
|
||||
* The \p fade controls whether the volume change should be immediately (if
|
||||
* it is 0) or over how many seconds it should change. The default is for it to change
|
||||
* over 500 ms. The \p identifier must be a name for a sound that was started through
|
||||
* the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \param volume The new volume level. Must be greater or equal to 0
|
||||
* \param fade How much time the fade from the current volume to the new volume should
|
||||
* take
|
||||
*/
|
||||
void setVolume(const std::string& identifier, float volume, float fade = 0.5f) const;
|
||||
|
||||
/**
|
||||
* Returns the volume for the track referred to by the \p handle. The number returned
|
||||
* will be greater or equal to 0. The \p identifier must be a name for a sound that
|
||||
* was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \return The volume for the track referred to by the \p handle, which will be
|
||||
* greater or equal to 0
|
||||
*/
|
||||
float volume(const std::string& identifier) const;
|
||||
|
||||
/**
|
||||
* Updates the 3D position of a track started through the #playAudio3d function. See
|
||||
* that function and the #set3dListenerParameters function for a complete description.
|
||||
* The \p identifier must be a name for a sound that was started through the
|
||||
* #playAudio3d function.
|
||||
*
|
||||
* \param identifier The identifier of a track started through the #playAudio3d
|
||||
* function
|
||||
* \param position The new position from which the track originates
|
||||
*/
|
||||
void set3dSourcePosition(const std::string& identifier,
|
||||
const glm::vec3& position) const;
|
||||
|
||||
/**
|
||||
* Returns the list of all tracks that are currently playing.
|
||||
*
|
||||
* \return The list of all tracks that are currently playing
|
||||
*/
|
||||
std::vector<std::string> currentlyPlaying() const;
|
||||
|
||||
/**
|
||||
* Sets the global volume for all track referred to the new \p volume. The total
|
||||
* for each track is the global volume set by this function multiplied with the volume
|
||||
* for the specific track set through the #setVolume function. The default value for
|
||||
* the global volume is 0.5. The volume should be a number bigger than 0, where 1 is
|
||||
* the maximum volume level. The \p fade controls whether the volume change should be
|
||||
* immediately (if it is 0) or over how many seconds it should change. The default is
|
||||
* for it to change over 500 ms.
|
||||
*
|
||||
* \param volume The new volume level. Must be greater or equal to 0
|
||||
* \param fade How much time the fade from the current volume to the new volume should
|
||||
* take
|
||||
*/
|
||||
void setGlobalVolume(float volume, float fade = 0.5f) const;
|
||||
|
||||
/**
|
||||
* Returns the global volume for all track. The number returned will be greater or
|
||||
* equal to 0.
|
||||
*
|
||||
* \return The global volume
|
||||
*/
|
||||
float globalVolume() const;
|
||||
|
||||
/**
|
||||
* Sets the position and orientation of the listener. This new position is
|
||||
* automatically used to adjust the relative position of all 3D tracks. Each parameter
|
||||
* to this function call is optional and if a value is omitted, the currently set
|
||||
* value continues to be used instead. The coordinate system for the tracks and the
|
||||
* listener is a meter-based coordinate system.
|
||||
*
|
||||
* \param position The position of the listener.
|
||||
* \param lookAt The direction vector of the forward direction
|
||||
* \param up The up-vector of the coordinate system
|
||||
*/
|
||||
void set3dListenerParameters(const std::optional<glm::vec3>& position,
|
||||
const std::optional<glm::vec3>& lookAt = std::nullopt,
|
||||
const std::optional<glm::vec3>& up = std::nullopt) const;
|
||||
|
||||
/**
|
||||
* Sets the position of the speaker for the provided \p channel to the provided
|
||||
* \p position. In general, this is considered an advanced feature to accommodate
|
||||
* non-standard audio environments.
|
||||
*
|
||||
* \param channel The channel whose speaker's position should be changed
|
||||
* \param position The new position for the speaker
|
||||
*/
|
||||
void setSpeakerPosition(int channel, const glm::vec3& position) const;
|
||||
|
||||
/**
|
||||
* Returns the position for the speaker of the provided \p channel.
|
||||
* \return The position for the speaker of the provided \p channel
|
||||
*/
|
||||
glm::vec3 speakerPosition(int channel) const;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
struct Info {
|
||||
std::unique_ptr<SoLoud::Wav> sound;
|
||||
unsigned int handle = 0;
|
||||
};
|
||||
|
||||
void internalInitialize(const ghoul::Dictionary&) override;
|
||||
void internalDeinitializeGL() override;
|
||||
|
||||
/**
|
||||
* Loads the sound at the provided \p path as an audio source and returns the pointer
|
||||
* to it. The sound has only been loaded and no other attributes have changed.
|
||||
*
|
||||
* \param path The path to the audio file on disk that should be loaded
|
||||
* \return The SoLoud::Wav object of the loaded file
|
||||
* \throw ghoul::RuntimeError If the \p path is not a loadable audio file
|
||||
*/
|
||||
std::unique_ptr<SoLoud::Wav> loadSound(const std::filesystem::path& path);
|
||||
|
||||
std::unique_ptr<SoLoud::Soloud> _engine;
|
||||
|
||||
std::map<std::string, Info> _sounds;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUDIO___AUDIOMODULE___H__
|
||||
355
modules/audio/audiomodule_lua.inl
Normal file
355
modules/audio/audiomodule_lua.inl
Normal file
@@ -0,0 +1,355 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 <openspace/engine/moduleengine.h>
|
||||
#include <ghoul/lua/lua_helper.h>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Starts playing the audio file located and the provided \p path. The \p loop parameter
|
||||
* determines whether the file is only played once, or on a loop. The sound is later
|
||||
* referred to by the \p identifier name. The audio file will be played in "background"
|
||||
* mode, which means that each channel will be played at full volume. To play a video
|
||||
* using spatial audio, use the #playAudio3d function instead.
|
||||
*
|
||||
* \param path The audio file that should be played
|
||||
* \param identifier The name for the sound that is used to refer to the sound
|
||||
* \param loop If `Yes` then the song will be played in a loop until the program is closed
|
||||
* or the playing is stopped through the #stopAudio function
|
||||
*/
|
||||
[[codegen::luawrap]] void playAudio(std::filesystem::path path, std::string identifier,
|
||||
bool shouldLoop = true)
|
||||
{
|
||||
using namespace openspace;
|
||||
|
||||
global::moduleEngine->module<AudioModule>()->playAudio(
|
||||
path,
|
||||
std::move(identifier),
|
||||
AudioModule::ShouldLoop(shouldLoop)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts playing the audio file located and the provided \p path. The \p loop parameter
|
||||
* determines whether the file is only played once, or on a loop. The sound is later
|
||||
* referred to by the \p identifier name. The \p position parameter determines the spatial
|
||||
* location of the sound in a meter-based coordinate system. The position of the listener
|
||||
* is (0,0,0) with the forward direction along the +y axis. This means that the "left"
|
||||
* channel in a stereo setting is towards -x and the "right" channel towards x. This
|
||||
* default value can be customized through the #set3dListenerParameters function. If you
|
||||
* want to play a video without spatial audio, use the #playAudio function instead.
|
||||
*
|
||||
* \param path The audio file that should be played
|
||||
* \param identifier The name for the sound that is used to refer to the sound
|
||||
* \param position The position of the audio file in the 3D environment
|
||||
* \param loop If `Yes` then the song will be played in a loop until the program is closed
|
||||
* or the playing is stopped through the #stopAudio function
|
||||
*/
|
||||
[[codegen::luawrap]] void playAudio3d(std::filesystem::path path, std::string identifier,
|
||||
glm::vec3 position, bool shouldLoop = true)
|
||||
{
|
||||
using namespace openspace;
|
||||
|
||||
global::moduleEngine->module<AudioModule>()->playAudio3d(
|
||||
path,
|
||||
std::move(identifier),
|
||||
position,
|
||||
AudioModule::ShouldLoop(shouldLoop)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the audio referenced by the \p identifier. The \p identifier must be a name for a
|
||||
* sound that was started through the #playAudio or #playAudio3d functions. After this
|
||||
* function, the \p identifier can not be used for any other function anymore except for
|
||||
* #playAudio or #playAudio3d to start a new sound.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
*/
|
||||
[[codegen::luawrap]] void stopAudio(std::string identifier) {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->stopAudio(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all currently playing tracks. After this function, none of the identifiers used
|
||||
* to previously play a sound a valid any longer, but can still be used by the #playAudio
|
||||
* or #playAudio3d functions to start a new sound. This function behaves the same way as
|
||||
* if manually calling #stopAudio on all of the sounds that have been started.
|
||||
*/
|
||||
[[codegen::luawrap]] void stopAll() {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->stopAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the playback for all sounds, while keeping them valid. This function behaves the
|
||||
* same as if calling #pauseAudio on all of the sounds that are currently playing.
|
||||
*/
|
||||
[[codegen::luawrap]] void pauseAll() {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->pauseAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the playback for all sounds that have been paused. Please note that this will
|
||||
* also resume the playback for the sounds that have been manually paused, not just those
|
||||
* that were paused through the #pauseAll function.
|
||||
*/
|
||||
[[codegen::luawrap]] void resumeAll() {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->resumeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes all of the sounds that are currently registers, unpauses them and plays them
|
||||
* from their starting points
|
||||
*/
|
||||
[[codegen::luawrap]] void playAllFromStart() {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->playAllFromStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the track referred to by the \p identifier is currently playing. A
|
||||
* volume of 0 is still considered to be playing. The \p identifier must be a name for a
|
||||
* sound that was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \return `true` if the track is currently playing, `false` otherwise
|
||||
*/
|
||||
[[codegen::luawrap]] bool isPlaying(std::string identifier) {
|
||||
using namespace openspace;
|
||||
return global::moduleEngine->module<AudioModule>()->isPlaying(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the playback of the track referred to by the \p identifier. The playback can
|
||||
* later be resumed through the #resumeAudio function. Trying to pause an already paused
|
||||
* track will not do anything, but is valid. The \p identifier must be a name for a sound
|
||||
* that was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
*/
|
||||
[[codegen::luawrap]] void pauseAudio(std::string identifier) {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->pauseAudio(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the track refered to by the \p identifier is currently playing or
|
||||
* paused. If it was be paused through a previous call to #pauseAudio, this function will
|
||||
* return `true`. If it has just been created or resumed through a call to #resumeAudio,
|
||||
* it will return `false`. The \p identifier must be a name for a sound that was started
|
||||
* through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \return `true` if the track is currently paused, `false` if it is playing
|
||||
*/
|
||||
[[codegen::luawrap]] bool isPaused(std::string identifier) {
|
||||
using namespace openspace;
|
||||
return global::moduleEngine->module<AudioModule>()->isPaused(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the playback of a track that was previously paused through the #pauseAudio
|
||||
* function. Trying to resume an already playing track will not do anything, but is valid.
|
||||
* The \p identifier must be a name for a sound that was started through the #playAudio or
|
||||
* #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
*/
|
||||
[[codegen::luawrap]] void resumeAudio(std::string identifier) {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->resumeAudio(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether the track referred to by the \p identifier should be looping or just
|
||||
* be played once. If a track is converted to not looping, it will finish playing until
|
||||
* the end of the file. The \p identifier must be a name for a sound that was started
|
||||
* through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \param loop If `Yes` then the song will be played in a loop until the program is closed
|
||||
* or the playing is stopped through the #stopAudio function
|
||||
*/
|
||||
[[codegen::luawrap]] void setLooping(std::string identifier, bool shouldLoop) {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->setLooping(
|
||||
identifier,
|
||||
AudioModule::ShouldLoop(shouldLoop)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the track referred to by the \p identifier is set to be looping or
|
||||
* whether it should played only once. The \p identifier must be a name for a sound that
|
||||
* was started through the #playAudio or #playAudio3d functions.
|
||||
*
|
||||
* \param identifier The identifier to the track that should be stopped
|
||||
* \return `Yes` if the track is looping, `No` otherwise
|
||||
*/
|
||||
[[codegen::luawrap]] bool isLooping(std::string identifier) {
|
||||
using namespace openspace;
|
||||
return global::moduleEngine->module<AudioModule>()->isLooping(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume of the track referred to by \p handle to the new \p volume. The volume
|
||||
* should be a number bigger than 0, where 1 is the maximum volume level. The \p fade
|
||||
* controls whether the volume change should be immediately (if it is 0) or over how many
|
||||
* seconds it should change. The default is for it to change over 500 ms.
|
||||
*
|
||||
* \param handle The handle to the track whose volume should be changed
|
||||
* \param volume The new volume level. Must be greater or equal to 0
|
||||
* \param fade How much time the fade from the current volume to the new volume should
|
||||
* take
|
||||
*/
|
||||
[[codegen::luawrap]] void setVolume(std::string identifier, float volume,
|
||||
float fade = 0.5f)
|
||||
{
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->setVolume(identifier, volume, fade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the volume for the track referred to by the \p handle. The number returned will
|
||||
* be greater or equal to 0.
|
||||
*
|
||||
* \return The volume for the track referred to by the \p handle, which will be
|
||||
* greater or equal to 0
|
||||
*/
|
||||
[[codegen::luawrap]] float volume(std::string identifier) {
|
||||
using namespace openspace;
|
||||
return global::moduleEngine->module<AudioModule>()->volume(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the 3D position of a track started through the #playAudio3d function. See that
|
||||
* function and the #set3dListenerParameters function for a complete description. The
|
||||
* \p identifier must be a name for a sound that was started through the #playAudio3d
|
||||
* function.
|
||||
*
|
||||
* \param handle A valid handle for a track started through the #playAudio3d function
|
||||
* \param position The new position from which the track originates
|
||||
*/
|
||||
[[codegen::luawrap]] void set3dSourcePosition(std::string identifier,
|
||||
glm::vec3 position)
|
||||
{
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->set3dSourcePosition(
|
||||
identifier,
|
||||
position
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all tracks that are currently playing.
|
||||
*
|
||||
* \return The list of all tracks that are currently playing
|
||||
*/
|
||||
[[codegen::luawrap]] std::vector<std::string> currentlyPlaying() {
|
||||
using namespace openspace;
|
||||
return global::moduleEngine->module<AudioModule>()->currentlyPlaying();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the global volume for all track referred to the new \p volume. The total for each
|
||||
* track is the global volume set by this function multiplied with the volume for the
|
||||
* specific track set through the #setVolume function. The default value for the global
|
||||
* volume is 0.5. The volume should be a number bigger than 0, where 1 is the maximum
|
||||
* volume level. The \p fade controls whether the volume change should be immediately (if
|
||||
* it is 0) or over how many seconds it should change. The default is for it to change
|
||||
* over 500 ms.
|
||||
*
|
||||
* \param volume The new volume level. Must be greater or equal to 0
|
||||
* \param fade How much time the fade from the current volume to the new volume should
|
||||
* take
|
||||
*/
|
||||
[[codegen::luawrap]] void setGlobalVolume(float volume, float fade = 0.5f) {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->setGlobalVolume(volume, fade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the global volume for all track. The number returned will be greater or equal
|
||||
* to 0.
|
||||
*
|
||||
* \return The global volume
|
||||
*/
|
||||
[[codegen::luawrap]] float globalVolume() {
|
||||
using namespace openspace;
|
||||
return global::moduleEngine->module<AudioModule>()->globalVolume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position and orientation of the listener. This new position is automatically
|
||||
* used to adjust the relative position of all 3D tracks. Each parameter to this function
|
||||
* call is optional and if a value is omitted, the currently set value continues to be
|
||||
* used instead. The coordinate system for the tracks and the listener is a meter-based
|
||||
* coordinate system.
|
||||
*
|
||||
* \param position The position of the listener.
|
||||
* \param lookAt The direction vector of the forward direction
|
||||
* \param up The up-vector of the coordinate system
|
||||
*/
|
||||
[[codegen::luawrap]] void set3dListenerPosition(glm::vec3 position,
|
||||
std::optional<glm::vec3> lookAt,
|
||||
std::optional<glm::vec3> up)
|
||||
{
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->set3dListenerParameters(
|
||||
position,
|
||||
lookAt,
|
||||
up
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of the speaker for the provided \p channel to the provided
|
||||
* \p position. In general, this is considered an advanced feature to accommodate
|
||||
* non-standard audio environments.
|
||||
*
|
||||
* \param channel The channel whose speaker's position should be changed
|
||||
* \param position The new position for the speaker
|
||||
*/
|
||||
[[codegen::luawrap]] void setSpeakerPosition(int handle, glm::vec3 position) {
|
||||
using namespace openspace;
|
||||
global::moduleEngine->module<AudioModule>()->setSpeakerPosition(handle, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position for the speaker of the provided \p channel.
|
||||
* \return The position for the speaker of the provided \p channel
|
||||
*/
|
||||
[[codegen::luawrap]] glm::vec3 speakerPosition(int handle) {
|
||||
using namespace openspace;
|
||||
return global::moduleEngine->module<AudioModule>()->speakerPosition(handle);
|
||||
}
|
||||
|
||||
#include "audiomodule_lua_codegen.cpp"
|
||||
|
||||
} // namespace
|
||||
1
modules/audio/ext/soloud
Submodule
1
modules/audio/ext/soloud
Submodule
Submodule modules/audio/ext/soloud added at e82fd32c1f
@@ -2,7 +2,7 @@
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2022 #
|
||||
# Copyright (c) 2014-2025 #
|
||||
# #
|
||||
# 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 #
|
||||
@@ -22,20 +22,23 @@
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #
|
||||
##########################################################################################
|
||||
|
||||
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/support/cmake/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
dashboard/dashboarditemangle.h
|
||||
dashboard/dashboarditemcameraorientation.h
|
||||
dashboard/dashboarditemdate.h
|
||||
dashboard/dashboarditemdistance.h
|
||||
dashboard/dashboarditemelapsedtime.h
|
||||
dashboard/dashboarditemframerate.h
|
||||
dashboard/dashboarditeminputstate.h
|
||||
dashboard/dashboarditemmission.h
|
||||
dashboard/dashboarditemparallelconnection.h
|
||||
dashboard/dashboarditempropertyvalue.h
|
||||
dashboard/dashboarditemsimulationincrement.h
|
||||
dashboard/dashboarditemspacing.h
|
||||
dashboard/dashboarditemtext.h
|
||||
dashboard/dashboarditemtimevaryingtext.h
|
||||
dashboard/dashboarditemvelocity.h
|
||||
lightsource/cameralightsource.h
|
||||
lightsource/scenegraphlightsource.h
|
||||
@@ -43,10 +46,16 @@ set(HEADER_FILES
|
||||
rendering/grids/renderablegrid.h
|
||||
rendering/grids/renderableradialgrid.h
|
||||
rendering/grids/renderablesphericalgrid.h
|
||||
rendering/pointcloud/renderableinterpolatedpoints.h
|
||||
rendering/pointcloud/renderablepointcloud.h
|
||||
rendering/pointcloud/renderablepolygoncloud.h
|
||||
rendering/pointcloud/sizemappingcomponent.h
|
||||
rendering/renderablecartesianaxes.h
|
||||
rendering/renderabledisc.h
|
||||
rendering/renderabledistancelabel.h
|
||||
rendering/renderablelabel.h
|
||||
rendering/renderablemodel.h
|
||||
rendering/renderablenodearrow.h
|
||||
rendering/renderablenodeline.h
|
||||
rendering/renderableplane.h
|
||||
rendering/renderableplaneimagelocal.h
|
||||
@@ -54,6 +63,9 @@ set(HEADER_FILES
|
||||
rendering/renderableplanetimevaryingimage.h
|
||||
rendering/renderableprism.h
|
||||
rendering/renderablesphere.h
|
||||
rendering/renderablesphereimagelocal.h
|
||||
rendering/renderablesphereimageonline.h
|
||||
rendering/renderableswitch.h
|
||||
rendering/renderabletimevaryingsphere.h
|
||||
rendering/renderabletrail.h
|
||||
rendering/renderabletrailorbit.h
|
||||
@@ -62,18 +74,27 @@ set(HEADER_FILES
|
||||
rendering/screenspaceframebuffer.h
|
||||
rendering/screenspaceimagelocal.h
|
||||
rendering/screenspaceimageonline.h
|
||||
rendering/screenspacerenderablerenderable.h
|
||||
rendering/screenspacetimevaryingimageonline.h
|
||||
rotation/timelinerotation.h
|
||||
rotation/constantrotation.h
|
||||
rotation/fixedrotation.h
|
||||
rotation/globerotation.h
|
||||
rotation/luarotation.h
|
||||
rotation/multirotation.h
|
||||
rotation/staticrotation.h
|
||||
scale/luascale.h
|
||||
scale/multiscale.h
|
||||
scale/nonuniformstaticscale.h
|
||||
scale/staticscale.h
|
||||
scale/timedependentscale.h
|
||||
scale/timelinescale.h
|
||||
task/convertmodeltask.h
|
||||
timeframe/timeframeinterval.h
|
||||
timeframe/timeframeunion.h
|
||||
translation/globetranslation.h
|
||||
translation/luatranslation.h
|
||||
translation/multitranslation.h
|
||||
translation/statictranslation.h
|
||||
translation/timelinetranslation.h
|
||||
)
|
||||
@@ -81,16 +102,19 @@ source_group("Header Files" FILES ${HEADER_FILES})
|
||||
|
||||
set(SOURCE_FILES
|
||||
dashboard/dashboarditemangle.cpp
|
||||
dashboard/dashboarditemcameraorientation.cpp
|
||||
dashboard/dashboarditemdate.cpp
|
||||
dashboard/dashboarditemdistance.cpp
|
||||
dashboard/dashboarditemelapsedtime.cpp
|
||||
dashboard/dashboarditemframerate.cpp
|
||||
dashboard/dashboarditeminputstate.cpp
|
||||
dashboard/dashboarditemmission.cpp
|
||||
dashboard/dashboarditemparallelconnection.cpp
|
||||
dashboard/dashboarditempropertyvalue.cpp
|
||||
dashboard/dashboarditemsimulationincrement.cpp
|
||||
dashboard/dashboarditemspacing.cpp
|
||||
dashboard/dashboarditemtext.cpp
|
||||
dashboard/dashboarditemtimevaryingtext.cpp
|
||||
dashboard/dashboarditemvelocity.cpp
|
||||
lightsource/cameralightsource.cpp
|
||||
lightsource/scenegraphlightsource.cpp
|
||||
@@ -98,10 +122,16 @@ set(SOURCE_FILES
|
||||
rendering/grids/renderablegrid.cpp
|
||||
rendering/grids/renderableradialgrid.cpp
|
||||
rendering/grids/renderablesphericalgrid.cpp
|
||||
rendering/pointcloud/renderableinterpolatedpoints.cpp
|
||||
rendering/pointcloud/renderablepointcloud.cpp
|
||||
rendering/pointcloud/renderablepolygoncloud.cpp
|
||||
rendering/pointcloud/sizemappingcomponent.cpp
|
||||
rendering/renderablecartesianaxes.cpp
|
||||
rendering/renderabledisc.cpp
|
||||
rendering/renderabledistancelabel.cpp
|
||||
rendering/renderablelabel.cpp
|
||||
rendering/renderablemodel.cpp
|
||||
rendering/renderablenodearrow.cpp
|
||||
rendering/renderablenodeline.cpp
|
||||
rendering/renderableplane.cpp
|
||||
rendering/renderableplaneimagelocal.cpp
|
||||
@@ -109,6 +139,9 @@ set(SOURCE_FILES
|
||||
rendering/renderableplanetimevaryingimage.cpp
|
||||
rendering/renderableprism.cpp
|
||||
rendering/renderablesphere.cpp
|
||||
rendering/renderablesphereimagelocal.cpp
|
||||
rendering/renderablesphereimageonline.cpp
|
||||
rendering/renderableswitch.cpp
|
||||
rendering/renderabletimevaryingsphere.cpp
|
||||
rendering/renderabletrail.cpp
|
||||
rendering/renderabletrailorbit.cpp
|
||||
@@ -118,24 +151,35 @@ set(SOURCE_FILES
|
||||
rendering/screenspaceframebuffer.cpp
|
||||
rendering/screenspaceimagelocal.cpp
|
||||
rendering/screenspaceimageonline.cpp
|
||||
rendering/screenspacerenderablerenderable.cpp
|
||||
rendering/screenspacetimevaryingimageonline.cpp
|
||||
rotation/timelinerotation.cpp
|
||||
rotation/constantrotation.cpp
|
||||
rotation/fixedrotation.cpp
|
||||
rotation/globerotation.cpp
|
||||
rotation/luarotation.cpp
|
||||
rotation/multirotation.cpp
|
||||
rotation/staticrotation.cpp
|
||||
scale/luascale.cpp
|
||||
scale/multiscale.cpp
|
||||
scale/nonuniformstaticscale.cpp
|
||||
scale/staticscale.cpp
|
||||
scale/timedependentscale.cpp
|
||||
scale/timelinescale.cpp
|
||||
task/convertmodeltask.cpp
|
||||
timeframe/timeframeinterval.cpp
|
||||
timeframe/timeframeunion.cpp
|
||||
translation/globetranslation.cpp
|
||||
translation/luatranslation.cpp
|
||||
translation/multitranslation.cpp
|
||||
translation/statictranslation.cpp
|
||||
translation/timelinetranslation.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
set(SHADER_FILES
|
||||
shaders/arrow_fs.glsl
|
||||
shaders/arrow_vs.glsl
|
||||
shaders/axes_fs.glsl
|
||||
shaders/axes_vs.glsl
|
||||
shaders/disc_fs.glsl
|
||||
@@ -150,6 +194,13 @@ set(SHADER_FILES
|
||||
shaders/model_vs.glsl
|
||||
shaders/plane_fs.glsl
|
||||
shaders/plane_vs.glsl
|
||||
shaders/pointcloud/pointcloud_fs.glsl
|
||||
shaders/pointcloud/pointcloud_gs.glsl
|
||||
shaders/pointcloud/pointcloud_vs.glsl
|
||||
shaders/pointcloud/pointcloud_interpolated_vs.glsl
|
||||
shaders/polygon_fs.glsl
|
||||
shaders/polygon_gs.glsl
|
||||
shaders/polygon_vs.glsl
|
||||
shaders/prism_fs.glsl
|
||||
shaders/prism_vs.glsl
|
||||
shaders/renderabletrail_fs.glsl
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -25,16 +25,19 @@
|
||||
#include <modules/base/basemodule.h>
|
||||
|
||||
#include <modules/base/dashboard/dashboarditemangle.h>
|
||||
#include <modules/base/dashboard/dashboarditemcameraorientation.h>
|
||||
#include <modules/base/dashboard/dashboarditemdate.h>
|
||||
#include <modules/base/dashboard/dashboarditemdistance.h>
|
||||
#include <modules/base/dashboard/dashboarditemelapsedtime.h>
|
||||
#include <modules/base/dashboard/dashboarditemframerate.h>
|
||||
#include <modules/base/dashboard/dashboarditeminputstate.h>
|
||||
#include <modules/base/dashboard/dashboarditemmission.h>
|
||||
#include <modules/base/dashboard/dashboarditemparallelconnection.h>
|
||||
#include <modules/base/dashboard/dashboarditempropertyvalue.h>
|
||||
#include <modules/base/dashboard/dashboarditemsimulationincrement.h>
|
||||
#include <modules/base/dashboard/dashboarditemspacing.h>
|
||||
#include <modules/base/dashboard/dashboarditemtext.h>
|
||||
#include <modules/base/dashboard/dashboarditemtimevaryingtext.h>
|
||||
#include <modules/base/dashboard/dashboarditemvelocity.h>
|
||||
#include <modules/base/lightsource/cameralightsource.h>
|
||||
#include <modules/base/lightsource/scenegraphlightsource.h>
|
||||
@@ -42,12 +45,20 @@
|
||||
#include <modules/base/rendering/grids/renderablegrid.h>
|
||||
#include <modules/base/rendering/grids/renderableradialgrid.h>
|
||||
#include <modules/base/rendering/grids/renderablesphericalgrid.h>
|
||||
#include <modules/base/rendering/pointcloud/renderableinterpolatedpoints.h>
|
||||
#include <modules/base/rendering/pointcloud/renderablepointcloud.h>
|
||||
#include <modules/base/rendering/pointcloud/renderablepolygoncloud.h>
|
||||
#include <modules/base/rendering/pointcloud/sizemappingcomponent.h>
|
||||
#include <modules/base/rendering/renderablecartesianaxes.h>
|
||||
#include <modules/base/rendering/renderabledisc.h>
|
||||
#include <modules/base/rendering/renderabledistancelabel.h>
|
||||
#include <modules/base/rendering/renderablelabel.h>
|
||||
#include <modules/base/rendering/renderablemodel.h>
|
||||
#include <modules/base/rendering/renderablenodearrow.h>
|
||||
#include <modules/base/rendering/renderablenodeline.h>
|
||||
#include <modules/base/rendering/renderablesphere.h>
|
||||
#include <modules/base/rendering/renderablesphereimagelocal.h>
|
||||
#include <modules/base/rendering/renderablesphereimageonline.h>
|
||||
#include <modules/base/rendering/renderableswitch.h>
|
||||
#include <modules/base/rendering/renderabletrailorbit.h>
|
||||
#include <modules/base/rendering/renderabletrailtrajectory.h>
|
||||
#include <modules/base/rendering/renderableplaneimagelocal.h>
|
||||
@@ -59,17 +70,26 @@
|
||||
#include <modules/base/rendering/screenspaceimagelocal.h>
|
||||
#include <modules/base/rendering/screenspaceimageonline.h>
|
||||
#include <modules/base/rendering/screenspaceframebuffer.h>
|
||||
#include <modules/base/rendering/screenspacerenderablerenderable.h>
|
||||
#include <modules/base/rendering/screenspacetimevaryingimageonline.h>
|
||||
#include <modules/base/rotation/constantrotation.h>
|
||||
#include <modules/base/rotation/fixedrotation.h>
|
||||
#include <modules/base/rotation/globerotation.h>
|
||||
#include <modules/base/rotation/luarotation.h>
|
||||
#include <modules/base/rotation/multirotation.h>
|
||||
#include <modules/base/rotation/staticrotation.h>
|
||||
#include <modules/base/rotation/timelinerotation.h>
|
||||
#include <modules/base/scale/luascale.h>
|
||||
#include <modules/base/scale/multiscale.h>
|
||||
#include <modules/base/scale/nonuniformstaticscale.h>
|
||||
#include <modules/base/scale/staticscale.h>
|
||||
#include <modules/base/scale/timedependentscale.h>
|
||||
#include <modules/base/scale/timelinescale.h>
|
||||
#include <modules/base/task/convertmodeltask.h>
|
||||
#include <modules/base/translation/timelinetranslation.h>
|
||||
#include <modules/base/translation/globetranslation.h>
|
||||
#include <modules/base/translation/luatranslation.h>
|
||||
#include <modules/base/translation/multitranslation.h>
|
||||
#include <modules/base/translation/statictranslation.h>
|
||||
#include <modules/base/timeframe/timeframeinterval.h>
|
||||
#include <modules/base/timeframe/timeframeunion.h>
|
||||
@@ -89,26 +109,33 @@ ghoul::opengl::TextureManager BaseModule::TextureManager;
|
||||
BaseModule::BaseModule() : OpenSpaceModule(BaseModule::Name) {}
|
||||
|
||||
void BaseModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
FactoryManager::ref().addFactory<ScreenSpaceRenderable>("ScreenSpaceRenderable");
|
||||
|
||||
ghoul::TemplateFactory<ScreenSpaceRenderable>* fSsRenderable =
|
||||
FactoryManager::ref().factory<ScreenSpaceRenderable>();
|
||||
ghoul_assert(fSsRenderable, "ScreenSpaceRenderable factory was not created");
|
||||
|
||||
fSsRenderable->registerClass<ScreenSpaceDashboard>("ScreenSpaceDashboard");
|
||||
fSsRenderable->registerClass<ScreenSpaceFramebuffer>("ScreenSpaceFramebuffer");
|
||||
fSsRenderable->registerClass<ScreenSpaceImageLocal>("ScreenSpaceImageLocal");
|
||||
fSsRenderable->registerClass<ScreenSpaceImageOnline>("ScreenSpaceImageOnline");
|
||||
fSsRenderable->registerClass<ScreenSpaceFramebuffer>("ScreenSpaceFramebuffer");
|
||||
fSsRenderable->registerClass<ScreenSpaceRenderableRenderable>(
|
||||
"ScreenSpaceRenderableRenderable"
|
||||
);
|
||||
fSsRenderable->registerClass<ScreenSpaceTimeVaryingImageOnline>("ScreenSpaceTimeVaryingImageOnline");
|
||||
|
||||
|
||||
ghoul::TemplateFactory<DashboardItem>* fDashboard =
|
||||
FactoryManager::ref().factory<DashboardItem>();
|
||||
ghoul_assert(fDashboard, "Dashboard factory was not created");
|
||||
|
||||
fDashboard->registerClass<DashboardItemAngle>("DashboardItemAngle");
|
||||
fDashboard->registerClass<DashboardItemCameraOrientation>(
|
||||
"DashboardItemCameraOrientation"
|
||||
);
|
||||
fDashboard->registerClass<DashboardItemDate>("DashboardItemDate");
|
||||
fDashboard->registerClass<DashboardItemDistance>("DashboardItemDistance");
|
||||
fDashboard->registerClass<DashboardItemElapsedTime>("DashboardItemElapsedTime");
|
||||
fDashboard->registerClass<DashboardItemFramerate>("DashboardItemFramerate");
|
||||
fDashboard->registerClass<DashboardItemInputState>("DashboardItemInputState");
|
||||
fDashboard->registerClass<DashboardItemMission>("DashboardItemMission");
|
||||
fDashboard->registerClass<DashboardItemParallelConnection>(
|
||||
"DashboardItemParallelConnection"
|
||||
@@ -121,8 +148,20 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
);
|
||||
fDashboard->registerClass<DashboardItemSpacing>("DashboardItemSpacing");
|
||||
fDashboard->registerClass<DashboardItemText>("DashboardItemText");
|
||||
fDashboard->registerClass<DashboardItemTimeVaryingText>(
|
||||
"DashboardItemTimeVaryingText"
|
||||
);
|
||||
fDashboard->registerClass<DashboardItemVelocity>("DashboardItemVelocity");
|
||||
|
||||
|
||||
ghoul::TemplateFactory<LightSource>* fLightSource =
|
||||
FactoryManager::ref().factory<LightSource>();
|
||||
ghoul_assert(fLightSource, "Light Source factory was not created");
|
||||
|
||||
fLightSource->registerClass<CameraLightSource>("CameraLightSource");
|
||||
fLightSource->registerClass<SceneGraphLightSource>("SceneGraphLightSource");
|
||||
|
||||
|
||||
ghoul::TemplateFactory<Renderable>* fRenderable =
|
||||
FactoryManager::ref().factory<Renderable>();
|
||||
ghoul_assert(fRenderable, "Renderable factory was not created");
|
||||
@@ -130,32 +169,36 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
fRenderable->registerClass<RenderableBoxGrid>("RenderableBoxGrid");
|
||||
fRenderable->registerClass<RenderableCartesianAxes>("RenderableCartesianAxes");
|
||||
fRenderable->registerClass<RenderableDisc>("RenderableDisc");
|
||||
fRenderable->registerClass<RenderableDistanceLabel>("RenderableDistanceLabel");
|
||||
fRenderable->registerClass<RenderableGrid>("RenderableGrid");
|
||||
fRenderable->registerClass<RenderableLabel>("RenderableLabel");
|
||||
fRenderable->registerClass<RenderableModel>("RenderableModel");
|
||||
fRenderable->registerClass<RenderableNodeArrow>("RenderableNodeArrow");
|
||||
fRenderable->registerClass<RenderableNodeLine>("RenderableNodeLine");
|
||||
fRenderable->registerClass<RenderablePlaneImageLocal>("RenderablePlaneImageLocal");
|
||||
fRenderable->registerClass<RenderablePlaneImageOnline>("RenderablePlaneImageOnline");
|
||||
fRenderable->registerClass<RenderablePlaneTimeVaryingImage>(
|
||||
"RenderablePlaneTimeVaryingImage"
|
||||
);
|
||||
fRenderable->registerClass<RenderableInterpolatedPoints>(
|
||||
"RenderableInterpolatedPoints"
|
||||
);
|
||||
fRenderable->registerClass<RenderablePointCloud>("RenderablePointCloud");
|
||||
fRenderable->registerClass<RenderablePolygonCloud>("RenderablePolygonCloud");
|
||||
fRenderable->registerClass<RenderablePrism>("RenderablePrism");
|
||||
fRenderable->registerClass<RenderableTimeVaryingSphere>(
|
||||
"RenderableTimeVaryingSphere"
|
||||
);
|
||||
fRenderable->registerClass<RenderableRadialGrid>("RenderableRadialGrid");
|
||||
fRenderable->registerClass<RenderableSphere>("RenderableSphere");
|
||||
fRenderable->registerClass<RenderableSphereImageLocal>("RenderableSphereImageLocal");
|
||||
fRenderable->registerClass<RenderableSphereImageOnline>(
|
||||
"RenderableSphereImageOnline"
|
||||
);
|
||||
fRenderable->registerClass<RenderableSwitch>("RenderableSwitch");
|
||||
fRenderable->registerClass<RenderableSphericalGrid>("RenderableSphericalGrid");
|
||||
fRenderable->registerClass<RenderableTrailOrbit>("RenderableTrailOrbit");
|
||||
fRenderable->registerClass<RenderableTrailTrajectory>("RenderableTrailTrajectory");
|
||||
|
||||
ghoul::TemplateFactory<Translation>* fTranslation =
|
||||
FactoryManager::ref().factory<Translation>();
|
||||
ghoul_assert(fTranslation, "Ephemeris factory was not created");
|
||||
|
||||
fTranslation->registerClass<TimelineTranslation>("TimelineTranslation");
|
||||
fTranslation->registerClass<LuaTranslation>("LuaTranslation");
|
||||
fTranslation->registerClass<StaticTranslation>("StaticTranslation");
|
||||
|
||||
ghoul::TemplateFactory<Rotation>* fRotation =
|
||||
FactoryManager::ref().factory<Rotation>();
|
||||
@@ -163,7 +206,9 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
|
||||
fRotation->registerClass<ConstantRotation>("ConstantRotation");
|
||||
fRotation->registerClass<FixedRotation>("FixedRotation");
|
||||
fRotation->registerClass<GlobeRotation>("GlobeRotation");
|
||||
fRotation->registerClass<LuaRotation>("LuaRotation");
|
||||
fRotation->registerClass<MultiRotation>("MultiRotation");
|
||||
fRotation->registerClass<StaticRotation>("StaticRotation");
|
||||
fRotation->registerClass<TimelineRotation>("TimelineRotation");
|
||||
|
||||
@@ -172,9 +217,12 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
ghoul_assert(fScale, "Scale factory was not created");
|
||||
|
||||
fScale->registerClass<LuaScale>("LuaScale");
|
||||
fScale->registerClass<MultiScale>("MultiScale");
|
||||
fScale->registerClass<NonUniformStaticScale>("NonUniformStaticScale");
|
||||
fScale->registerClass<StaticScale>("StaticScale");
|
||||
fScale->registerClass<TimeDependentScale>("TimeDependentScale");
|
||||
fScale->registerClass<TimelineScale>("TimelineScale");
|
||||
|
||||
|
||||
ghoul::TemplateFactory<TimeFrame>* fTimeFrame =
|
||||
FactoryManager::ref().factory<TimeFrame>();
|
||||
@@ -183,12 +231,23 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
fTimeFrame->registerClass<TimeFrameInterval>("TimeFrameInterval");
|
||||
fTimeFrame->registerClass<TimeFrameUnion>("TimeFrameUnion");
|
||||
|
||||
ghoul::TemplateFactory<LightSource>* fLightSource =
|
||||
FactoryManager::ref().factory<LightSource>();
|
||||
ghoul_assert(fLightSource, "Light Source factory was not created");
|
||||
|
||||
fLightSource->registerClass<CameraLightSource>("CameraLightSource");
|
||||
fLightSource->registerClass<SceneGraphLightSource>("SceneGraphLightSource");
|
||||
ghoul::TemplateFactory<Translation>* fTranslation =
|
||||
FactoryManager::ref().factory<Translation>();
|
||||
ghoul_assert(fTranslation, "Translation factory was not created");
|
||||
|
||||
fTranslation->registerClass<GlobeTranslation>("GlobeTranslation");
|
||||
fTranslation->registerClass<LuaTranslation>("LuaTranslation");
|
||||
fTranslation->registerClass<MultiTranslation>("MultiTranslation");
|
||||
fTranslation->registerClass<StaticTranslation>("StaticTranslation");
|
||||
fTranslation->registerClass<TimelineTranslation>("TimelineTranslation");
|
||||
|
||||
|
||||
ghoul::TemplateFactory<Task>* fTask =
|
||||
FactoryManager::ref().factory<Task>();
|
||||
ghoul_assert(fTask, "Task factory was not created");
|
||||
|
||||
fTask->registerClass<ConvertModelTask>("ConvertModelTask");
|
||||
}
|
||||
|
||||
void BaseModule::internalDeinitializeGL() {
|
||||
@@ -199,61 +258,83 @@ void BaseModule::internalDeinitializeGL() {
|
||||
std::vector<documentation::Documentation> BaseModule::documentations() const {
|
||||
return {
|
||||
DashboardItemAngle::Documentation(),
|
||||
DashboardItemCameraOrientation::Documentation(),
|
||||
DashboardItemDate::Documentation(),
|
||||
DashboardItemDistance::Documentation(),
|
||||
DashboardItemElapsedTime::Documentation(),
|
||||
DashboardItemFramerate::Documentation(),
|
||||
DashboardItemInputState::Documentation(),
|
||||
DashboardItemMission::Documentation(),
|
||||
DashboardItemParallelConnection::Documentation(),
|
||||
DashboardItemPropertyValue::Documentation(),
|
||||
DashboardItemSimulationIncrement::Documentation(),
|
||||
DashboardItemSpacing::Documentation(),
|
||||
DashboardItemText::Documentation(),
|
||||
DashboardItemTimeVaryingText::Documentation(),
|
||||
DashboardItemVelocity::Documentation(),
|
||||
|
||||
CameraLightSource::Documentation(),
|
||||
SceneGraphLightSource::Documentation(),
|
||||
|
||||
RenderableBoxGrid::Documentation(),
|
||||
RenderableCartesianAxes::Documentation(),
|
||||
RenderableDisc::Documentation(),
|
||||
RenderableDistanceLabel::Documentation(),
|
||||
RenderableGrid::Documentation(),
|
||||
RenderableInterpolatedPoints::Documentation(),
|
||||
RenderableLabel::Documentation(),
|
||||
RenderableModel::Documentation(),
|
||||
RenderableNodeArrow::Documentation(),
|
||||
RenderableNodeLine::Documentation(),
|
||||
RenderablePlane::Documentation(),
|
||||
RenderablePlaneImageLocal::Documentation(),
|
||||
RenderablePlaneImageOnline::Documentation(),
|
||||
RenderablePlaneTimeVaryingImage::Documentation(),
|
||||
RenderablePointCloud::Documentation(),
|
||||
RenderablePolygonCloud::Documentation(),
|
||||
RenderablePrism::Documentation(),
|
||||
RenderableRadialGrid::Documentation(),
|
||||
RenderableSphere::Documentation(),
|
||||
RenderableSphereImageLocal::Documentation(),
|
||||
RenderableSphereImageOnline::Documentation(),
|
||||
RenderableSphericalGrid::Documentation(),
|
||||
RenderableSwitch::Documentation(),
|
||||
RenderableTimeVaryingSphere::Documentation(),
|
||||
RenderableTrailOrbit::Documentation(),
|
||||
RenderableTrailTrajectory::Documentation(),
|
||||
|
||||
SizeMappingComponent::Documentation(),
|
||||
|
||||
ScreenSpaceDashboard::Documentation(),
|
||||
ScreenSpaceFramebuffer::Documentation(),
|
||||
ScreenSpaceImageLocal::Documentation(),
|
||||
ScreenSpaceImageOnline::Documentation(),
|
||||
ScreenSpaceRenderableRenderable::Documentation(),
|
||||
ScreenSpaceTimeVaryingImageOnline::Documentation(),
|
||||
|
||||
ConstantRotation::Documentation(),
|
||||
FixedRotation::Documentation(),
|
||||
GlobeRotation::Documentation(),
|
||||
LuaRotation::Documentation(),
|
||||
MultiRotation::Documentation(),
|
||||
StaticRotation::Documentation(),
|
||||
TimelineRotation::Documentation(),
|
||||
|
||||
LuaScale::Documentation(),
|
||||
MultiScale::Documentation(),
|
||||
NonUniformStaticScale::Documentation(),
|
||||
StaticScale::Documentation(),
|
||||
TimeDependentScale::Documentation(),
|
||||
|
||||
LuaTranslation::Documentation(),
|
||||
StaticTranslation::Documentation(),
|
||||
TimelineTranslation::Documentation(),
|
||||
TimelineScale::Documentation(),
|
||||
|
||||
TimeFrameInterval::Documentation(),
|
||||
TimeFrameUnion::Documentation(),
|
||||
|
||||
CameraLightSource::Documentation(),
|
||||
SceneGraphLightSource::Documentation()
|
||||
GlobeTranslation::Documentation(),
|
||||
LuaTranslation::Documentation(),
|
||||
MultiTranslation::Documentation(),
|
||||
StaticTranslation::Documentation(),
|
||||
TimelineTranslation::Documentation(),
|
||||
|
||||
ConvertModelTask::Documentation()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -50,16 +50,18 @@ namespace {
|
||||
"SourceType",
|
||||
"Source Type",
|
||||
"The type of position that is used as the triangle apex used to calculate the "
|
||||
"angle. The default value is 'Camera'"
|
||||
"angle.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SourceNodeNameInfo = {
|
||||
"SourceNodeName",
|
||||
"Source Node Name",
|
||||
"If a scene graph node is selected as type, this value specifies the name of the "
|
||||
"node that is to be used as the apex of the triangle used to calculate the "
|
||||
"angle. The computed angle is the incident angle to Source in the triangle ("
|
||||
"Source, Reference, Destination)"
|
||||
constexpr openspace::properties::Property::PropertyInfo SourceNodeIdentifierInfo = {
|
||||
"SourceNodeIdentifier",
|
||||
"Source Node Identifier",
|
||||
"If a scene graph node is selected as type, this value specifies the identifier "
|
||||
"of the node that is to be used as the apex of the triangle used to calculate "
|
||||
"the angle. The computed angle is the incident angle to Source in the triangle ("
|
||||
"Source, Reference, Destination).",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ReferenceTypeInfo = {
|
||||
@@ -67,14 +69,17 @@ namespace {
|
||||
"Reference Type",
|
||||
"The type of position that is used as the destination of the reference line used "
|
||||
"to calculate the angle. The computed angle is the incident angle to Source in "
|
||||
"the triangle (Source, Reference, Destination)"
|
||||
"the triangle (Source, Reference, Destination).",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ReferenceNodeNameInfo = {
|
||||
"ReferenceNodeName",
|
||||
"Reference Node Name",
|
||||
"If a scene graph node is selected as type, this value specifies the name of the "
|
||||
"node that is to be used as the reference direction to compute the angle"
|
||||
constexpr openspace::properties::Property::PropertyInfo ReferenceNodeIdentifierInfo =
|
||||
{
|
||||
"ReferenceNodeIdentifier",
|
||||
"Reference Node Identifier",
|
||||
"If a scene graph node is selected as type, this value specifies the identifier "
|
||||
"of the node that is to be used as the reference direction to compute the angle.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DestinationTypeInfo = {
|
||||
@@ -82,16 +87,27 @@ namespace {
|
||||
"Destination Type",
|
||||
"The type of position that is used as the destination to calculate the angle. "
|
||||
"The computed angle is the incident angle to Source in the triangle ("
|
||||
"Source, Reference, Destination). The default value for this is 'Focus'"
|
||||
"Source, Reference, Destination).",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DestinationNodeNameInfo = {
|
||||
"DestinationNodeName",
|
||||
"Destination Node Name",
|
||||
"If a scene graph node is selected as type, this value specifies the name of the "
|
||||
"node that is to be used as the destination for computing the angle"
|
||||
constexpr openspace::properties::Property::PropertyInfo
|
||||
DestinationNodeIdentifierInfo =
|
||||
{
|
||||
"DestinationNodeIdentifier",
|
||||
"Destination Node Identifier",
|
||||
"If a scene graph node is selected as type, this value specifies the identifier "
|
||||
"of the node that is to be used as the destination for computing the angle.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `DashboardItem` shows the angle between the lines `Source`->`Reference` and
|
||||
// `Source`->`Destination`. Each of `Source`, `Reference`, and `Destination` can be
|
||||
// either the identifier of a node, the current focus node, or the position of the
|
||||
// camera. The angle cannot be calculated if two of these three items are located in
|
||||
// the same position, in which case an error message is printed. The `SourceNodeName`,
|
||||
// `ReferenceNodeName`, and `DestinationNodeName` parameters are only used if the
|
||||
// `SourceType`, `ReferenceType`, or `DestinationType` respectively is set to `Node`.
|
||||
struct [[codegen::Dictionary(DashboardItemAngle)]] Parameters {
|
||||
enum class [[codegen::map(Type)]] Type {
|
||||
Node,
|
||||
@@ -100,17 +116,17 @@ namespace {
|
||||
};
|
||||
|
||||
// [[codegen::verbatim(SourceTypeInfo.description)]]
|
||||
std::optional<Type> sourceType;
|
||||
// [[codegen::verbatim(SourceNodeNameInfo.description)]]
|
||||
std::optional<std::string> sourceNodeName;
|
||||
Type sourceType;
|
||||
// [[codegen::verbatim(SourceNodeIdentifierInfo.description)]]
|
||||
std::optional<std::string> sourceNodeIdentifier;
|
||||
// [[codegen::verbatim(ReferenceTypeInfo.description)]]
|
||||
Type referenceType;
|
||||
// [[codegen::verbatim(ReferenceNodeNameInfo.description)]]
|
||||
std::optional<std::string> referenceNodeName;
|
||||
// [[codegen::verbatim(ReferenceNodeIdentifierInfo.description)]]
|
||||
std::optional<std::string> referenceNodeIdentifier;
|
||||
// [[codegen::verbatim(DestinationTypeInfo.description)]]
|
||||
std::optional<Type> destinationType;
|
||||
// [[codegen::verbatim(DestinationNodeNameInfo.description)]]
|
||||
std::optional<std::string> destinationNodeName;
|
||||
Type destinationType;
|
||||
// [[codegen::verbatim(DestinationNodeIdentifierInfo.description)]]
|
||||
std::optional<std::string> destinationNodeIdentifier;
|
||||
};
|
||||
#include "dashboarditemangle_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -118,46 +134,27 @@ namespace {
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemAngle::Documentation() {
|
||||
documentation::Documentation doc =
|
||||
codegen::doc<Parameters>("base_dashboarditem_angle");
|
||||
|
||||
// @TODO cleanup
|
||||
// Insert the parent's documentation entries until we have a verifier that can deal
|
||||
// with class hierarchy
|
||||
documentation::Documentation parentDoc = DashboardTextItem::Documentation();
|
||||
doc.entries.insert(
|
||||
doc.entries.end(),
|
||||
parentDoc.entries.begin(),
|
||||
parentDoc.entries.end()
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_angle",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
, _source{
|
||||
properties::OptionProperty(
|
||||
SourceTypeInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
),
|
||||
properties::StringProperty(SourceNodeNameInfo),
|
||||
properties::OptionProperty(SourceTypeInfo),
|
||||
properties::StringProperty(SourceNodeIdentifierInfo),
|
||||
nullptr
|
||||
}
|
||||
, _reference{
|
||||
properties::OptionProperty(
|
||||
ReferenceTypeInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
),
|
||||
properties::StringProperty(ReferenceNodeNameInfo),
|
||||
properties::OptionProperty(ReferenceTypeInfo),
|
||||
properties::StringProperty(ReferenceNodeIdentifierInfo),
|
||||
nullptr
|
||||
}
|
||||
, _destination{
|
||||
properties::OptionProperty(
|
||||
DestinationTypeInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
),
|
||||
properties::StringProperty(DestinationNodeNameInfo),
|
||||
properties::OptionProperty(DestinationTypeInfo),
|
||||
properties::StringProperty(DestinationNodeIdentifierInfo),
|
||||
nullptr
|
||||
}
|
||||
{
|
||||
@@ -168,18 +165,13 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
{ Type::Focus, "Focus" },
|
||||
{ Type::Camera, "Camera" }
|
||||
});
|
||||
if (p.sourceType.has_value()) {
|
||||
_source.type = codegen::map<Type>(*p.sourceType);
|
||||
}
|
||||
else {
|
||||
_source.type = Type::Camera;
|
||||
}
|
||||
_source.type = codegen::map<Type>(p.sourceType);
|
||||
addProperty(_source.type);
|
||||
|
||||
_source.nodeName.onChange([this]() { _source.node = nullptr; });
|
||||
_source.nodeIdentifier.onChange([this]() { _source.node = nullptr; });
|
||||
if (_source.type == Type::Node) {
|
||||
if (p.sourceNodeName.has_value()) {
|
||||
_source.nodeName = *p.sourceNodeName;
|
||||
if (p.sourceNodeIdentifier.has_value()) {
|
||||
_source.nodeIdentifier = *p.sourceNodeIdentifier;
|
||||
}
|
||||
else {
|
||||
LERRORC(
|
||||
@@ -188,7 +180,7 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
);
|
||||
}
|
||||
}
|
||||
addProperty(_source.nodeName);
|
||||
addProperty(_source.nodeIdentifier);
|
||||
|
||||
|
||||
_reference.type.addOptions({
|
||||
@@ -199,10 +191,10 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
_reference.type = codegen::map<Type>(p.referenceType);
|
||||
addProperty(_reference.type);
|
||||
|
||||
_reference.nodeName.onChange([this]() { _reference.node = nullptr; });
|
||||
_reference.nodeIdentifier.onChange([this]() { _reference.node = nullptr; });
|
||||
if (_reference.type == Type::Node) {
|
||||
if (p.referenceNodeName.has_value()) {
|
||||
_reference.nodeName = *p.referenceNodeName;
|
||||
if (p.referenceNodeIdentifier.has_value()) {
|
||||
_reference.nodeIdentifier = *p.referenceNodeIdentifier;
|
||||
}
|
||||
else {
|
||||
LERRORC(
|
||||
@@ -211,24 +203,19 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
);
|
||||
}
|
||||
}
|
||||
addProperty(_reference.nodeName);
|
||||
addProperty(_reference.nodeIdentifier);
|
||||
|
||||
_destination.type.addOptions({
|
||||
{ Type::Node, "Node" },
|
||||
{ Type::Focus, "Focus" },
|
||||
{ Type::Camera, "Camera" }
|
||||
});
|
||||
if (p.destinationType.has_value()) {
|
||||
_destination.type = codegen::map<Type>(*p.destinationType);
|
||||
}
|
||||
else {
|
||||
_destination.type = Type::Focus;
|
||||
}
|
||||
_destination.type = codegen::map<Type>(p.destinationType);
|
||||
addProperty(_destination.type);
|
||||
_destination.nodeName.onChange([this]() { _destination.node = nullptr; });
|
||||
_destination.nodeIdentifier.onChange([this]() { _destination.node = nullptr; });
|
||||
if (_destination.type == Type::Node) {
|
||||
if (p.destinationNodeName.has_value()) {
|
||||
_destination.nodeName = *p.destinationNodeName;
|
||||
if (p.destinationNodeIdentifier.has_value()) {
|
||||
_destination.nodeIdentifier = *p.destinationNodeIdentifier;
|
||||
}
|
||||
else {
|
||||
LERRORC(
|
||||
@@ -237,22 +224,55 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
);
|
||||
}
|
||||
}
|
||||
addProperty(_destination.nodeName);
|
||||
addProperty(_destination.nodeIdentifier);
|
||||
|
||||
_buffer.resize(128);
|
||||
_localBuffer.resize(128);
|
||||
}
|
||||
|
||||
std::pair<glm::dvec3, std::string> DashboardItemAngle::positionAndLabel(
|
||||
Component& comp) const
|
||||
{
|
||||
void DashboardItemAngle::update() {
|
||||
ZoneScoped;
|
||||
|
||||
std::pair<glm::dvec3, std::string> sourceInfo = positionAndLabel(_source);
|
||||
std::pair<glm::dvec3, std::string> referenceInfo = positionAndLabel(_reference);
|
||||
std::pair<glm::dvec3, std::string> destinationInfo = positionAndLabel(_destination);
|
||||
|
||||
const glm::dvec3 a = referenceInfo.first - sourceInfo.first;
|
||||
const glm::dvec3 b = destinationInfo.first - sourceInfo.first;
|
||||
|
||||
std::fill(_localBuffer.begin(), _localBuffer.end(), char(0));
|
||||
if (glm::length(a) == 0.0 || glm::length(b) == 0) {
|
||||
char* end = std::format_to(
|
||||
_localBuffer.data(),
|
||||
"Could not compute angle at {} between {} and {}. At least two of the three "
|
||||
"items are placed in the same location",
|
||||
sourceInfo.second, destinationInfo.second, referenceInfo.second
|
||||
);
|
||||
_buffer = std::string(_localBuffer.data(), end - _localBuffer.data());
|
||||
}
|
||||
else {
|
||||
const double angle = glm::degrees(
|
||||
glm::acos(glm::dot(a, b) / (glm::length(a) * glm::length(b)))
|
||||
);
|
||||
|
||||
char* end = std::format_to(
|
||||
_localBuffer.data(),
|
||||
"Angle at {} between {} and {}: {} degrees",
|
||||
sourceInfo.second, destinationInfo.second, referenceInfo.second, angle
|
||||
);
|
||||
_buffer = std::string(_localBuffer.data(), end - _localBuffer.data());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<glm::dvec3, std::string> DashboardItemAngle::positionAndLabel(Component& comp) {
|
||||
if (comp.type == Type::Node) {
|
||||
if (!comp.node) {
|
||||
comp.node = global::renderEngine->scene()->sceneGraphNode(comp.nodeName);
|
||||
comp.node =
|
||||
global::renderEngine->scene()->sceneGraphNode(comp.nodeIdentifier);
|
||||
|
||||
if (!comp.node) {
|
||||
LERRORC(
|
||||
"DashboardItemAngle",
|
||||
"Could not find node '" + comp.nodeName.value() + "'"
|
||||
"Could not find node '" + comp.nodeIdentifier.value() + "'"
|
||||
);
|
||||
return { glm::dvec3(0.0), "Node" };
|
||||
}
|
||||
@@ -278,48 +298,4 @@ std::pair<glm::dvec3, std::string> DashboardItemAngle::positionAndLabel(
|
||||
}
|
||||
}
|
||||
|
||||
void DashboardItemAngle::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
|
||||
std::pair<glm::dvec3, std::string> sourceInfo = positionAndLabel(_source);
|
||||
std::pair<glm::dvec3, std::string> referenceInfo = positionAndLabel(_reference);
|
||||
std::pair<glm::dvec3, std::string> destinationInfo = positionAndLabel(_destination);
|
||||
|
||||
const glm::dvec3 a = referenceInfo.first - sourceInfo.first;
|
||||
const glm::dvec3 b = destinationInfo.first - sourceInfo.first;
|
||||
|
||||
std::fill(_buffer.begin(), _buffer.end(), char(0));
|
||||
if (glm::length(a) == 0.0 || glm::length(b) == 0) {
|
||||
char* end = fmt::format_to(
|
||||
_buffer.data(),
|
||||
"Could not compute angle at {} between {} and {}",
|
||||
sourceInfo.second, destinationInfo.second, referenceInfo.second
|
||||
);
|
||||
std::string_view text = std::string_view(_buffer.data(), end - _buffer.data());
|
||||
RenderFont(*_font, penPosition, text);
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
else {
|
||||
const double angle = glm::degrees(
|
||||
glm::acos(glm::dot(a, b) / (glm::length(a) * glm::length(b)))
|
||||
);
|
||||
|
||||
char* end = fmt::format_to(
|
||||
_buffer.data(),
|
||||
"Angle at {} between {} and {}: {} degrees",
|
||||
sourceInfo.second, destinationInfo.second, referenceInfo.second, angle
|
||||
);
|
||||
std::string_view text = std::string_view(_buffer.data(), end - _buffer.data());
|
||||
RenderFont(*_font, penPosition, text);
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemAngle::size() const {
|
||||
ZoneScoped
|
||||
|
||||
constexpr double Angle = 120;
|
||||
return _font->boundingBox("Angle: " + std::to_string(Angle));
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <utility>
|
||||
|
||||
namespace openspace {
|
||||
@@ -39,29 +39,27 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemAngle : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemAngle(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemAngle(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemAngle() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
struct Component {
|
||||
properties::OptionProperty type;
|
||||
properties::StringProperty nodeName;
|
||||
properties::StringProperty nodeIdentifier;
|
||||
SceneGraphNode* node;
|
||||
};
|
||||
|
||||
std::pair<glm::dvec3, std::string> positionAndLabel(Component& comp) const;
|
||||
static std::pair<glm::dvec3, std::string> positionAndLabel(Component& comp);
|
||||
|
||||
Component _source;
|
||||
Component _reference;
|
||||
Component _destination;
|
||||
|
||||
std::vector<char> _buffer;
|
||||
std::vector<char> _localBuffer;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
71
modules/base/dashboard/dashboarditemcameraorientation.cpp
Normal file
71
modules/base/dashboard/dashboarditemcameraorientation.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/dashboard/dashboarditemcameraorientation.h>
|
||||
|
||||
#include <openspace/camera/camera.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <ghoul/font/font.h>
|
||||
|
||||
namespace {
|
||||
// This `DashboardItem` shows the current camera orientation in the yaw, pitch, and
|
||||
// roll directions in degrees. Note that the camera's orientation is relative to the
|
||||
// global coordinate system used in the system.
|
||||
struct [[codegen::Dictionary(DashboardItemCameraOrientation)]] Parameters {
|
||||
};
|
||||
#include "dashboarditemcameraorientation_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemCameraOrientation::Documentation() {
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_cameraorientation",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemCameraOrientation::DashboardItemCameraOrientation(
|
||||
const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
{}
|
||||
|
||||
void DashboardItemCameraOrientation::update() {
|
||||
ZoneScoped;
|
||||
|
||||
const Camera* camera = global::renderEngine->scene()->camera();
|
||||
const glm::dquat orientation = camera->rotationQuaternion();
|
||||
const glm::dvec3 pitchYawRoll = glm::eulerAngles(orientation);
|
||||
const glm::dvec3 pitchYawRollDeg = glm::degrees(pitchYawRoll);
|
||||
|
||||
_buffer = std::format(
|
||||
"Yaw: {:.2f}\nPitch: {:.2f}\nRoll: {:.2f}",
|
||||
pitchYawRollDeg.y, pitchYawRollDeg.x, pitchYawRollDeg.z
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
44
modules/base/dashboard/dashboarditemcameraorientation.h
Normal file
44
modules/base/dashboard/dashboarditemcameraorientation.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMCAMERAORIENTATION___H__
|
||||
#define __OPENSPACE_MODULE_BASE___DASHBOARDITEMCAMERAORIENTATION___H__
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class DashboardItemCameraOrientation : public DashboardTextItem {
|
||||
public:
|
||||
explicit DashboardItemCameraOrientation(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemCameraOrientation() override = default;
|
||||
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___DASHBOARDITEMCAMERAORIENTATION___H__
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -40,7 +40,9 @@ namespace {
|
||||
"FormatString",
|
||||
"Format String",
|
||||
"The format text describing how this dashboard item renders its text. This text "
|
||||
"must contain exactly one {} which is a placeholder that will contain the date"
|
||||
"must contain exactly one {} which is a placeholder that will contain the date "
|
||||
"in the format as specified by `TimeFormat`.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo TimeFormatInfo = {
|
||||
@@ -49,9 +51,14 @@ namespace {
|
||||
"The format string used for formatting the date/time before being passed to the "
|
||||
"string in FormatString. See "
|
||||
"https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/timout_c.html for full "
|
||||
"information about how to structure this format"
|
||||
"information about how to structure this format.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `DashboardItem` shows the current in-game simulation time. The `FormatString`
|
||||
// and the `TimeFormat` options provide the ability to customize the output that is
|
||||
// printed. See these two parameters for more information on how to structure the
|
||||
// inputs.
|
||||
struct [[codegen::Dictionary(DashboardItemDate)]] Parameters {
|
||||
// [[codegen::verbatim(FormatStringInfo.description)]]
|
||||
std::optional<std::string> formatString;
|
||||
@@ -65,13 +72,16 @@ namespace {
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemDate::Documentation() {
|
||||
return codegen::doc<Parameters>("base_dashboarditem_date");
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_date",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemDate::DashboardItemDate(const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary, 15.f)
|
||||
, _formatString(FormatStringInfo, "Date: {} UTC")
|
||||
, _timeFormat(TimeFormatInfo, "YYYY MON DDTHR:MN:SC.### ::RND")
|
||||
, _formatString(FormatStringInfo, "Date: {}")
|
||||
, _timeFormat(TimeFormatInfo, "YYYY MON DD HR:MN:SC.### UTC ::RND")
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
@@ -82,8 +92,8 @@ DashboardItemDate::DashboardItemDate(const ghoul::Dictionary& dictionary)
|
||||
addProperty(_timeFormat);
|
||||
}
|
||||
|
||||
void DashboardItemDate::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemDate::update() {
|
||||
ZoneScoped;
|
||||
|
||||
std::string time = SpiceManager::ref().dateFromEphemerisTime(
|
||||
global::timeManager->time().j2000Seconds(),
|
||||
@@ -91,23 +101,12 @@ void DashboardItemDate::render(glm::vec2& penPosition) {
|
||||
);
|
||||
|
||||
try {
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(fmt::runtime(_formatString.value()), time)
|
||||
);
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_formatString.value(), std::make_format_args(time));
|
||||
}
|
||||
catch (const fmt::format_error&) {
|
||||
catch (const std::format_error&) {
|
||||
LERRORC("DashboardItemDate", "Illegal format string");
|
||||
}
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemDate::size() const {
|
||||
ZoneScoped
|
||||
|
||||
std::string_view time = global::timeManager->time().UTC();
|
||||
return _font->boundingBox(fmt::format(fmt::runtime(_formatString.value()), time));
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -35,12 +35,10 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemDate : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemDate(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemDate(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemDate() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -51,29 +51,33 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo SourceTypeInfo = {
|
||||
"SourceType",
|
||||
"Source Type",
|
||||
"The type of position that is used as the source to calculate the distance. The "
|
||||
"default value is 'Camera'"
|
||||
"The type of position that is used as the source to calculate the distance.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SourceNodeNameInfo = {
|
||||
"SourceNodeName",
|
||||
"Source Node Name",
|
||||
"If a scene graph node is selected as type, this value specifies the name of the "
|
||||
"node that is to be used as the source for computing the distance"
|
||||
constexpr openspace::properties::Property::PropertyInfo SourceNodeIdentifierInfo = {
|
||||
"SourceNodeIdentifier",
|
||||
"Source Node Identifier",
|
||||
"If a scene graph node is selected as type, this value specifies the identifier "
|
||||
"of the node that is to be used as the source for computing the distance.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DestinationTypeInfo = {
|
||||
"DestinationType",
|
||||
"Destination Type",
|
||||
"The type of position that is used as the destination to calculate the distance. "
|
||||
"The default value for this is 'Focus'"
|
||||
"The type of position that is used as the destination to calculate the distance.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DestinationNodeNameInfo = {
|
||||
"DestinationNodeName",
|
||||
"Destination Node Name",
|
||||
"If a scene graph node is selected as type, this value specifies the name of the "
|
||||
"node that is to be used as the destination for computing the distance"
|
||||
constexpr openspace::properties::Property::PropertyInfo
|
||||
DestinationNodeIdentifierInfo =
|
||||
{
|
||||
"DestinationNodeIdentifier",
|
||||
"Destination Node Identifier",
|
||||
"If a scene graph node is selected as type, this value specifies the identifier "
|
||||
"of the node that is to be used as the destination for computing the distance.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SimplificationInfo = {
|
||||
@@ -81,14 +85,16 @@ namespace {
|
||||
"Simplification",
|
||||
"If this value is enabled, the distance is displayed in nuanced units, such as "
|
||||
"km, AU, light years, parsecs, etc. If this value is disabled, the unit can be "
|
||||
"explicitly requested"
|
||||
"explicitly requested.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RequestedUnitInfo = {
|
||||
"RequestedUnit",
|
||||
"Requested Unit",
|
||||
"If the simplification is disabled, this distance unit is used as a destination "
|
||||
"to convert the meters into"
|
||||
"to convert the meters into.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FormatStringInfo = {
|
||||
@@ -96,22 +102,18 @@ namespace {
|
||||
"Format String",
|
||||
"The format string that is used for formatting the distance string. This format "
|
||||
"receives four parameters: The name of the source, the name of the destination "
|
||||
"the value of the distance and the unit of the distance"
|
||||
"the value of the distance and the unit of the distance.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
std::vector<std::string> unitList() {
|
||||
std::vector<std::string> res(openspace::DistanceUnits.size());
|
||||
std::transform(
|
||||
openspace::DistanceUnits.begin(),
|
||||
openspace::DistanceUnits.end(),
|
||||
res.begin(),
|
||||
[](openspace::DistanceUnit unit) {
|
||||
return std::string(nameForDistanceUnit(unit));
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
// This `DashboardItem` displays the distance between two points. The points can be
|
||||
// defined either by the location of a scene graph node, the surface of a scene graph
|
||||
// node's bounding sphere, the location of the current focus node, or the position of
|
||||
// the camera. These definitions can be mixed and matched to calculate any combination
|
||||
// of positions.
|
||||
//
|
||||
// The resulting text can be formatted in the `FormatString` and the measurement unit
|
||||
// is chosed by changing the `Simplification` and `RequestedUnit` parameters.
|
||||
struct [[codegen::Dictionary(DashboardItemDistance)]] Parameters {
|
||||
enum class [[codegen::map(Type)]] TypeInfo {
|
||||
Node,
|
||||
@@ -121,22 +123,23 @@ namespace {
|
||||
};
|
||||
|
||||
// [[codegen::verbatim(SourceTypeInfo.description)]]
|
||||
std::optional<TypeInfo> sourceType;
|
||||
TypeInfo sourceType;
|
||||
|
||||
// [[codegen::verbatim(SourceNodeNameInfo.description)]]
|
||||
std::optional<std::string> sourceNodeName;
|
||||
// [[codegen::verbatim(SourceNodeIdentifierInfo.description)]]
|
||||
std::optional<std::string> sourceNodeIdentifier;
|
||||
|
||||
// [[codegen::verbatim(DestinationTypeInfo.description)]]
|
||||
std::optional<TypeInfo> destinationType;
|
||||
TypeInfo destinationType;
|
||||
|
||||
// [[codegen::verbatim(DestinationNodeNameInfo.description)]]
|
||||
std::optional<std::string> destinationNodeName;
|
||||
// [[codegen::verbatim(DestinationNodeIdentifierInfo.description)]]
|
||||
std::optional<std::string> destinationNodeIdentifier;
|
||||
|
||||
// [[codegen::verbatim(SimplificationInfo.description)]]
|
||||
std::optional<bool> simplification;
|
||||
|
||||
// [[codegen::verbatim(RequestedUnitInfo.description)]]
|
||||
std::optional<std::string> requestedUnit [[codegen::inlist(unitList())]];
|
||||
std::optional<std::string> requestedUnit
|
||||
[[codegen::inlist(openspace::distanceUnitList())]];
|
||||
|
||||
// [[codegen::verbatim(FormatStringInfo.description)]]
|
||||
std::optional<std::string> formatString;
|
||||
@@ -156,22 +159,16 @@ documentation::Documentation DashboardItemDistance::Documentation() {
|
||||
DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
, _doSimplification(SimplificationInfo, true)
|
||||
, _requestedUnit(RequestedUnitInfo, properties::OptionProperty::DisplayType::Dropdown)
|
||||
, _requestedUnit(RequestedUnitInfo)
|
||||
, _formatString(FormatStringInfo, "Distance from {} to {}: {:f} {}")
|
||||
, _source{
|
||||
properties::OptionProperty(
|
||||
SourceTypeInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
),
|
||||
properties::StringProperty(SourceNodeNameInfo),
|
||||
properties::OptionProperty(SourceTypeInfo),
|
||||
properties::StringProperty(SourceNodeIdentifierInfo),
|
||||
nullptr
|
||||
}
|
||||
, _destination{
|
||||
properties::OptionProperty(
|
||||
DestinationTypeInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
),
|
||||
properties::StringProperty(DestinationNodeNameInfo),
|
||||
properties::OptionProperty(DestinationTypeInfo),
|
||||
properties::StringProperty(DestinationNodeIdentifierInfo),
|
||||
nullptr
|
||||
}
|
||||
{
|
||||
@@ -184,24 +181,19 @@ DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary
|
||||
{ Type::Camera, "Camera" }
|
||||
});
|
||||
_source.type.onChange([this]() {
|
||||
_source.nodeName.setVisibility(
|
||||
_source.nodeIdentifier.setVisibility(
|
||||
properties::Property::Visibility(
|
||||
_source.type == Type::Node || _source.type == Type::NodeSurface
|
||||
)
|
||||
);
|
||||
});
|
||||
if (p.sourceType.has_value()) {
|
||||
_source.type = codegen::map<Type>(*p.sourceType);
|
||||
}
|
||||
else {
|
||||
_source.type = Type::Camera;
|
||||
}
|
||||
_source.type = codegen::map<Type>(p.sourceType);
|
||||
addProperty(_source.type);
|
||||
|
||||
_source.nodeName.onChange([this]() { _source.node = nullptr; });
|
||||
_source.nodeIdentifier.onChange([this]() { _source.node = nullptr; });
|
||||
if (_source.type == Type::Node || _source.type == Type::NodeSurface) {
|
||||
if (p.sourceNodeName.has_value()) {
|
||||
_source.nodeName = *p.sourceNodeName;
|
||||
if (p.sourceNodeIdentifier.has_value()) {
|
||||
_source.nodeIdentifier = *p.sourceNodeIdentifier;
|
||||
}
|
||||
else {
|
||||
LERRORC(
|
||||
@@ -210,7 +202,7 @@ DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary
|
||||
);
|
||||
}
|
||||
}
|
||||
addProperty(_source.nodeName);
|
||||
addProperty(_source.nodeIdentifier);
|
||||
|
||||
_destination.type.addOptions({
|
||||
{ Type::Node, "Node" },
|
||||
@@ -219,23 +211,18 @@ DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary
|
||||
{ Type::Camera, "Camera" }
|
||||
});
|
||||
_destination.type.onChange([this]() {
|
||||
_destination.nodeName.setVisibility(
|
||||
_destination.nodeIdentifier.setVisibility(
|
||||
properties::Property::Visibility(
|
||||
_source.type == Type::Node || _source.type == Type::NodeSurface
|
||||
)
|
||||
);
|
||||
});
|
||||
if (p.destinationType.has_value()) {
|
||||
_destination.type = codegen::map<Type>(*p.destinationType);
|
||||
}
|
||||
else {
|
||||
_destination.type = Type::Focus;
|
||||
}
|
||||
_destination.type = codegen::map<Type>(p.destinationType);
|
||||
addProperty(_destination.type);
|
||||
_destination.nodeName.onChange([this]() { _destination.node = nullptr; });
|
||||
_destination.nodeIdentifier.onChange([this]() { _destination.node = nullptr; });
|
||||
if (_destination.type == Type::Node || _destination.type == Type::NodeSurface) {
|
||||
if (p.destinationNodeName.has_value()) {
|
||||
_destination.nodeName = *p.destinationNodeName;
|
||||
if (p.destinationNodeIdentifier.has_value()) {
|
||||
_destination.nodeIdentifier = *p.destinationNodeIdentifier;
|
||||
}
|
||||
else {
|
||||
LERRORC(
|
||||
@@ -244,19 +231,12 @@ DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary
|
||||
);
|
||||
}
|
||||
}
|
||||
addProperty(_destination.nodeName);
|
||||
addProperty(_destination.nodeIdentifier);
|
||||
|
||||
_doSimplification = p.simplification.value_or(_doSimplification);
|
||||
_doSimplification.onChange([this]() {
|
||||
_requestedUnit.setVisibility(
|
||||
_doSimplification ?
|
||||
properties::Property::Visibility::Hidden :
|
||||
properties::Property::Visibility::User
|
||||
);
|
||||
});
|
||||
addProperty(_doSimplification);
|
||||
|
||||
for (DistanceUnit u : DistanceUnits) {
|
||||
for (const DistanceUnit u : DistanceUnits) {
|
||||
_requestedUnit.addOption(
|
||||
static_cast<int>(u),
|
||||
std::string(nameForDistanceUnit(u))
|
||||
@@ -264,16 +244,15 @@ DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary
|
||||
}
|
||||
_requestedUnit = static_cast<int>(DistanceUnit::Meter);
|
||||
if (p.requestedUnit.has_value()) {
|
||||
DistanceUnit unit = distanceUnitFromString(p.requestedUnit->c_str());
|
||||
const DistanceUnit unit = distanceUnitFromString(*p.requestedUnit);
|
||||
_requestedUnit = static_cast<int>(unit);
|
||||
}
|
||||
_requestedUnit.setVisibility(properties::Property::Visibility::Hidden);
|
||||
addProperty(_requestedUnit);
|
||||
|
||||
_formatString = p.formatString.value_or(_formatString);
|
||||
addProperty(_formatString);
|
||||
|
||||
_buffer.resize(256);
|
||||
_localBuffer.resize(256);
|
||||
}
|
||||
|
||||
std::pair<glm::dvec3, std::string> DashboardItemDistance::positionAndLabel(
|
||||
@@ -281,15 +260,15 @@ std::pair<glm::dvec3, std::string> DashboardItemDistance::positionAndLabel(
|
||||
Component& otherComp) const
|
||||
{
|
||||
if ((mainComp.type == Type::Node) || (mainComp.type == Type::NodeSurface)) {
|
||||
if (!mainComp.node) {
|
||||
if (!mainComp.node) [[unlikely]] {
|
||||
mainComp.node = global::renderEngine->scene()->sceneGraphNode(
|
||||
mainComp.nodeName
|
||||
mainComp.nodeIdentifier
|
||||
);
|
||||
|
||||
if (!mainComp.node) {
|
||||
LERRORC(
|
||||
"DashboardItemDistance",
|
||||
"Could not find node '" + mainComp.nodeName.value() + "'"
|
||||
"Could not find node '" + mainComp.nodeIdentifier.value() + "'"
|
||||
);
|
||||
return { glm::dvec3(0.0), "Node" };
|
||||
}
|
||||
@@ -313,9 +292,9 @@ std::pair<glm::dvec3, std::string> DashboardItemDistance::positionAndLabel(
|
||||
const glm::dvec3 thisPos = mainComp.node->worldPosition();
|
||||
|
||||
const glm::dvec3 dir = glm::normalize(otherPos - thisPos);
|
||||
glm::dvec3 dirLength = dir * glm::dvec3(mainComp.node->boundingSphere());
|
||||
const glm::dvec3 dirLen = dir * glm::dvec3(mainComp.node->boundingSphere());
|
||||
|
||||
return { thisPos + dirLength, "surface of " + mainComp.node->guiName() };
|
||||
return { thisPos + dirLen, "surface of " + mainComp.node->guiName() };
|
||||
}
|
||||
case Type::Focus: {
|
||||
const SceneGraphNode* anchor =
|
||||
@@ -334,8 +313,8 @@ std::pair<glm::dvec3, std::string> DashboardItemDistance::positionAndLabel(
|
||||
}
|
||||
}
|
||||
|
||||
void DashboardItemDistance::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemDistance::update() {
|
||||
ZoneScoped;
|
||||
|
||||
std::pair<glm::dvec3, std::string> sourceInfo = positionAndLabel(
|
||||
_source,
|
||||
@@ -354,43 +333,28 @@ void DashboardItemDistance::render(glm::vec2& penPosition) {
|
||||
else {
|
||||
const DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
|
||||
const double convertedD = convertMeters(d, unit);
|
||||
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
|
||||
dist = std::pair(convertedD, nameForDistanceUnit(unit, convertedD != 1.0));
|
||||
}
|
||||
|
||||
std::fill(_buffer.begin(), _buffer.end(), char(0));
|
||||
std::fill(_localBuffer.begin(), _localBuffer.end(), char(0));
|
||||
try {
|
||||
char* end = fmt::format_to(
|
||||
_buffer.data(),
|
||||
fmt::runtime(_formatString.value()),
|
||||
sourceInfo.second, destinationInfo.second, dist.first, dist.second
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
char* end = std::vformat_to(
|
||||
_localBuffer.data(),
|
||||
_formatString.value(),
|
||||
std::make_format_args(
|
||||
sourceInfo.second,
|
||||
destinationInfo.second,
|
||||
dist.first,
|
||||
dist.second
|
||||
)
|
||||
);
|
||||
|
||||
std::string_view text = std::string_view(_buffer.data(), end - _buffer.data());
|
||||
RenderFont(*_font, penPosition, text);
|
||||
_buffer = std::string(_localBuffer.data(), end - _localBuffer.data());
|
||||
}
|
||||
catch (const fmt::format_error&) {
|
||||
catch (const std::format_error&) {
|
||||
LERRORC("DashboardItemDate", "Illegal format string");
|
||||
}
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemDistance::size() const {
|
||||
ZoneScoped
|
||||
|
||||
const double d = glm::length(1e20);
|
||||
std::pair<double, std::string_view> dist;
|
||||
if (_doSimplification) {
|
||||
dist = simplifyDistance(d);
|
||||
}
|
||||
else {
|
||||
DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
|
||||
double convertedD = convertMeters(d, unit);
|
||||
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
|
||||
}
|
||||
|
||||
return _font->boundingBox(
|
||||
fmt::format("Distance from focus: {} {}", dist.first, dist.second)
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <utility>
|
||||
|
||||
@@ -40,19 +40,17 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemDistance : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemDistance(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemDistance(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemDistance() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
struct Component {
|
||||
properties::OptionProperty type;
|
||||
properties::StringProperty nodeName;
|
||||
properties::StringProperty nodeIdentifier;
|
||||
SceneGraphNode* node;
|
||||
};
|
||||
|
||||
@@ -66,7 +64,7 @@ private:
|
||||
Component _source;
|
||||
Component _destination;
|
||||
|
||||
std::vector<char> _buffer;
|
||||
std::vector<char> _localBuffer;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -41,14 +41,16 @@ namespace {
|
||||
"Format String",
|
||||
"The format text describing how this dashboard item renders its text. This text "
|
||||
"must contain exactly one {} which is a placeholder that will contain the value "
|
||||
"of the elapsed time."
|
||||
"of the elapsed time.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ReferenceTimeInfo = {
|
||||
"ReferenceTime",
|
||||
"Reference Time",
|
||||
"The reference time relative to which the elapsed time is specified. The format "
|
||||
"must be an ISO 8601-compliant date string"
|
||||
"must be an ISO 8601-compliant date string.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SimplifyTimeInfo = {
|
||||
@@ -56,22 +58,27 @@ namespace {
|
||||
"Simplify Time",
|
||||
"If this value is enabled, the elapsed time will be simplified into seconds, "
|
||||
"minutes, hours, etc. If the value is disabled, the elapsed time is always "
|
||||
"presented in seconds. The default value for this is 'true'."
|
||||
"presented in seconds. The default value for this is 'true'.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LowestTimeUnitInfo = {
|
||||
"LowestTimeUnit",
|
||||
"Lowest Time Unit when Simplifying",
|
||||
"If 'SimplifyTime' is enabled, this is the lowest time unit that will be shown. "
|
||||
"All finer grained timesteps will be ignored."
|
||||
"All finer grained timesteps will be ignored.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `DashboardItem` displays the remaining time until a provided `ReferenceTime`
|
||||
// or the elapsed time since the `ReferenceTime`. The output can be configured through
|
||||
// the `FormatString` and the unit that is used to display the configurable as well.
|
||||
struct [[codegen::Dictionary(DashboardItemElapsedTime)]] Parameters {
|
||||
// [[codegen::verbatim(FormatStringInfo.description)]]
|
||||
std::optional<std::string> formatString;
|
||||
|
||||
// [[codegen::verbatim(ReferenceTimeInfo.description)]]
|
||||
std::string referenceTime;
|
||||
std::string referenceTime [[codegen::datetime()]];
|
||||
|
||||
// [[codegen::verbatim(SimplifyTimeInfo.description)]]
|
||||
std::optional<bool> simplifyTime;
|
||||
@@ -97,7 +104,10 @@ namespace {
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemElapsedTime::Documentation() {
|
||||
return codegen::doc<Parameters>("base_dashboarditem_elapsedtime");
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_elapsedtime",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemElapsedTime::DashboardItemElapsedTime(const ghoul::Dictionary& dictionary)
|
||||
@@ -110,6 +120,7 @@ DashboardItemElapsedTime::DashboardItemElapsedTime(const ghoul::Dictionary& dict
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_formatString = p.formatString.value_or(_formatString);
|
||||
addProperty(_formatString);
|
||||
|
||||
_referenceTime.onChange([this]() {
|
||||
_referenceJ2000 = Time::convertTime(_referenceTime);
|
||||
@@ -120,65 +131,48 @@ DashboardItemElapsedTime::DashboardItemElapsedTime(const ghoul::Dictionary& dict
|
||||
_simplifyTime = p.simplifyTime.value_or(_simplifyTime);
|
||||
addProperty(_simplifyTime);
|
||||
|
||||
for (TimeUnit u : TimeUnits) {
|
||||
for (const TimeUnit u : TimeUnits) {
|
||||
_lowestTimeUnit.addOption(static_cast<int>(u), std::string(nameForTimeUnit(u)));
|
||||
}
|
||||
_lowestTimeUnit = static_cast<int>(TimeUnit::Second);
|
||||
TimeUnit u = codegen::map<TimeUnit>(
|
||||
const TimeUnit u = codegen::map<TimeUnit>(
|
||||
p.lowestTimeUnit.value_or(Parameters::TimeUnit::Second)
|
||||
);
|
||||
_lowestTimeUnit = static_cast<int>(u);
|
||||
addProperty(_lowestTimeUnit);
|
||||
}
|
||||
|
||||
void DashboardItemElapsedTime::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemElapsedTime::update() {
|
||||
ZoneScoped;
|
||||
|
||||
const double delta = global::timeManager->time().j2000Seconds() - _referenceJ2000;
|
||||
|
||||
double delta = global::timeManager->time().j2000Seconds() - _referenceJ2000;
|
||||
|
||||
if (_simplifyTime) {
|
||||
using namespace std::chrono;
|
||||
|
||||
TimeUnit lowestTime = TimeUnit(_lowestTimeUnit.value());
|
||||
std::string_view lowestUnitS = nameForTimeUnit(lowestTime, false);
|
||||
std::string_view lowestUnitP = nameForTimeUnit(lowestTime, true);
|
||||
const TimeUnit lowestTime = TimeUnit(_lowestTimeUnit.value());
|
||||
|
||||
std::vector<std::pair<double, std::string_view>> ts = splitTime(delta);
|
||||
const std::vector<std::pair<double, std::string_view>> ts = splitTime(delta);
|
||||
std::string time;
|
||||
for (const std::pair<double, std::string_view>& t : ts) {
|
||||
time += fmt::format("{} {} ", t.first, t.second);
|
||||
if (t.second == lowestUnitS || t.second == lowestUnitP) {
|
||||
if (timeUnitFromString(t.second) < lowestTime) {
|
||||
// We have reached the lowest unit the user was interested in
|
||||
break;
|
||||
}
|
||||
time += std::format("{} {} ", t.first, t.second);
|
||||
}
|
||||
|
||||
// Remove the " " at the end
|
||||
time = time.substr(0, time.size() - 2);
|
||||
time = time.substr(0, time.size() - 1);
|
||||
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(fmt::runtime(_formatString.value()), time)
|
||||
);
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_formatString.value(), std::make_format_args(time));
|
||||
}
|
||||
else {
|
||||
std::string time = fmt::format("{} s", delta);
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(fmt::runtime(_formatString.value()), time)
|
||||
);
|
||||
std::string time = std::format("{} s", delta);
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_formatString.value(), std::make_format_args(time));
|
||||
}
|
||||
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemElapsedTime::size() const {
|
||||
ZoneScoped
|
||||
|
||||
const double delta = global::timeManager->time().j2000Seconds() - _referenceJ2000;
|
||||
return _font->boundingBox(fmt::format(fmt::runtime(_formatString.value()), delta));
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,9 +27,9 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -37,12 +37,10 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemElapsedTime : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemElapsedTime(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemElapsedTime(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemElapsedTime() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
@@ -56,4 +54,4 @@ private:
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___DASHBOARDITEMDATE___H__
|
||||
#endif // __OPENSPACE_MODULE_BASE___DASHBOARDITEMELAPSEDTIME___H__
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -36,41 +36,51 @@
|
||||
|
||||
namespace {
|
||||
enum FrametimeType {
|
||||
DtTimeAvg = 0,
|
||||
DtTime = 0,
|
||||
DtTimeAvg,
|
||||
DtTimeExtremes,
|
||||
DtStandardDeviation,
|
||||
DtCoefficientOfVariation,
|
||||
FPS,
|
||||
FPSAvg,
|
||||
None
|
||||
FPSAvg
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FrametimeInfo = {
|
||||
"FrametimeType",
|
||||
"Type of the frame time display",
|
||||
"This value determines the units in which the frame time is displayed"
|
||||
"This value determines the units in which the frame time is displayed.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ClearCacheInfo = {
|
||||
"ClearCache",
|
||||
"Clear Cache",
|
||||
"Clears the cache of this DashboardItemFramerate item. If the selected option "
|
||||
"does not use any caching, this trigger does not do anything"
|
||||
"does not use any caching, this trigger does not do anything.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
[[ nodiscard ]] char* formatDt(std::vector<char>& buffer) {
|
||||
return fmt::format_to(
|
||||
[[nodiscard]] char* formatDt(std::vector<char>& buffer) {
|
||||
return std::format_to(
|
||||
buffer.data(),
|
||||
"Frametime: {:.2f} ms\0",
|
||||
openspace::global::windowDelegate->deltaTime()
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] char* formatDtAvg(std::vector<char>& buffer) {
|
||||
return std::format_to(
|
||||
buffer.data(),
|
||||
"Avg. Frametime: {:.2f} ms\0",
|
||||
openspace::global::windowDelegate->averageDeltaTime() * 1000.0
|
||||
);
|
||||
}
|
||||
|
||||
[[ nodiscard ]] char* formatDtExtremes(std::vector<char>& buffer,
|
||||
[[nodiscard]] char* formatDtExtremes(std::vector<char>& buffer,
|
||||
double minFrametimeCache,
|
||||
double maxFrametimeCache)
|
||||
{
|
||||
return fmt::format_to(
|
||||
return std::format_to(
|
||||
buffer.data(),
|
||||
"Last frametimes between: {:.2f} and {:.2f} ms\n"
|
||||
"Overall between: {:.2f} and {:.2f} ms\0",
|
||||
@@ -81,16 +91,16 @@ namespace {
|
||||
);
|
||||
}
|
||||
|
||||
[[ nodiscard ]] char* formatDtStandardDeviation(std::vector<char>& buffer) {
|
||||
return fmt::format_to(
|
||||
[[nodiscard]] char* formatDtStandardDeviation(std::vector<char>& buffer) {
|
||||
return std::format_to(
|
||||
buffer.data(),
|
||||
"Frametime standard deviation : {:.2f} ms\0",
|
||||
openspace::global::windowDelegate->deltaTimeStandardDeviation() * 1000.0
|
||||
);
|
||||
}
|
||||
|
||||
[[ nodiscard ]] char* formatDtCoefficientOfVariation(std::vector<char>& buffer) {
|
||||
return fmt::format_to(
|
||||
[[nodiscard]] char* formatDtCoefficientOfVariation(std::vector<char>& buffer) {
|
||||
return std::format_to(
|
||||
buffer.data(),
|
||||
"Frametime coefficient of variation : {:.2f} %\0",
|
||||
openspace::global::windowDelegate->deltaTimeStandardDeviation() /
|
||||
@@ -98,29 +108,31 @@ namespace {
|
||||
);
|
||||
}
|
||||
|
||||
[[ nodiscard ]] char* formatFps(std::vector<char>& buffer) {
|
||||
return fmt::format_to(
|
||||
[[nodiscard]] char* formatFps(std::vector<char>& buffer) {
|
||||
return std::format_to(
|
||||
buffer.data(),
|
||||
"FPS: {:3.2f}\0",
|
||||
1.0 / openspace::global::windowDelegate->deltaTime()
|
||||
);
|
||||
}
|
||||
|
||||
[[ nodiscard ]] char* formatAverageFps(std::vector<char>& buffer) {
|
||||
return fmt::format_to(
|
||||
[[nodiscard]] char* formatAverageFps(std::vector<char>& buffer) {
|
||||
return std::format_to(
|
||||
buffer.data(),
|
||||
"Avg. FPS: {:3.2f}\0",
|
||||
1.0 / openspace::global::windowDelegate->averageDeltaTime()
|
||||
);
|
||||
}
|
||||
|
||||
[[ nodiscard ]] char* format(std::vector<char>& buffer, FrametimeType frametimeType,
|
||||
[[nodiscard]] char* format(std::vector<char>& buffer, FrametimeType frametimeType,
|
||||
double minFrametimeCache, double maxFrametimeCache)
|
||||
{
|
||||
using namespace openspace;
|
||||
switch (frametimeType) {
|
||||
case FrametimeType::DtTimeAvg:
|
||||
case FrametimeType::DtTime:
|
||||
return formatDt(buffer);
|
||||
case FrametimeType::DtTimeAvg:
|
||||
return formatDtAvg(buffer);
|
||||
case FrametimeType::DtTimeExtremes:
|
||||
return formatDtExtremes(buffer, minFrametimeCache, maxFrametimeCache);
|
||||
case FrametimeType::DtStandardDeviation:
|
||||
@@ -136,8 +148,29 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// This `DashboardItem` provides information about the current framerate at which the
|
||||
// rendering updates. The `FrametimeType` can have different values that will show
|
||||
// different statistical aspects of the framerate.
|
||||
//
|
||||
// - `Deltatime`: Shows the time in milliseconds it took to render the previous
|
||||
// frame
|
||||
// - `Average Deltatime`: Shows the time that it took to render in milliseconds
|
||||
// averaged over the last 100 or so frames
|
||||
// - `Deltatime extremes`: Shows the minimum and maximum values of the render time
|
||||
// in milliseconds over the last 100 or so frames
|
||||
// - `Deltatime standard deviation`: Shows the standard deviation of the render time
|
||||
// in milliseconds over the last 100 or so frames
|
||||
// - `Deltatime coefficient of variation`: Shows the normalized root-mean-square
|
||||
// deviation of the render time in
|
||||
// milliseconds over the last 100 or so
|
||||
// frames
|
||||
// - `Frames per second`: Shows the inverse of the delta time it took the render the
|
||||
// last frame.
|
||||
// - `Average frames per second`: Shows average number of frames that have been
|
||||
// presented over the last 100 or so frames
|
||||
struct [[codegen::Dictionary(DashboardItemFramerate)]] Parameters {
|
||||
enum class [[codegen::map(FrametimeType)]] Type {
|
||||
DtTime [[codegen::key("Deltatime")]],
|
||||
DtTimeAvg [[codegen::key("Average Deltatime")]],
|
||||
DtTimeExtremes [[codegen::key("Deltatime extremes")]],
|
||||
DtStandardDeviation [[codegen::key("Deltatime standard deviation")]],
|
||||
@@ -151,7 +184,6 @@ namespace {
|
||||
std::optional<Type> frametimeType;
|
||||
};
|
||||
#include "dashboarditemframerate_codegen.cpp"
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
@@ -165,12 +197,13 @@ documentation::Documentation DashboardItemFramerate::Documentation() {
|
||||
|
||||
DashboardItemFramerate::DashboardItemFramerate(const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
, _frametimeType(FrametimeInfo, properties::OptionProperty::DisplayType::Dropdown)
|
||||
, _frametimeType(FrametimeInfo)
|
||||
, _clearCache(ClearCacheInfo)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_frametimeType.addOptions({
|
||||
{ static_cast<int>(FrametimeType::DtTime), "Deltatime" },
|
||||
{ static_cast<int>(FrametimeType::DtTimeAvg), "Average Deltatime" },
|
||||
{ static_cast<int>(FrametimeType::DtTimeExtremes), "Deltatime extremes" },
|
||||
{
|
||||
@@ -182,8 +215,7 @@ DashboardItemFramerate::DashboardItemFramerate(const ghoul::Dictionary& dictiona
|
||||
"Deltatime coefficient of variation"
|
||||
},
|
||||
{ static_cast<int>(FrametimeType::FPS), "Frames per second" },
|
||||
{ static_cast<int>(FrametimeType::FPSAvg), "Average frames per second" },
|
||||
{ static_cast<int>(FrametimeType::None), "None" }
|
||||
{ static_cast<int>(FrametimeType::FPSAvg), "Average frames per second" }
|
||||
});
|
||||
|
||||
if (p.frametimeType.has_value()) {
|
||||
@@ -198,14 +230,13 @@ DashboardItemFramerate::DashboardItemFramerate(const ghoul::Dictionary& dictiona
|
||||
_shouldClearCache = true;
|
||||
});
|
||||
addProperty(_clearCache);
|
||||
|
||||
_buffer.resize(128);
|
||||
_localBuffer.resize(128);
|
||||
}
|
||||
|
||||
void DashboardItemFramerate::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemFramerate::update() {
|
||||
ZoneScoped;
|
||||
|
||||
if (_shouldClearCache) {
|
||||
if (_shouldClearCache) [[unlikely]] {
|
||||
_minDeltaTimeCache = 1.0;
|
||||
_maxDeltaTimeCache = -1.0;
|
||||
_shouldClearCache = false;
|
||||
@@ -220,40 +251,16 @@ void DashboardItemFramerate::render(glm::vec2& penPosition) {
|
||||
global::windowDelegate->maxDeltaTime() * 1000.0
|
||||
);
|
||||
|
||||
FrametimeType frametimeType = FrametimeType(_frametimeType.value());
|
||||
const FrametimeType frametimeType = FrametimeType(_frametimeType.value());
|
||||
|
||||
std::fill(_buffer.begin(), _buffer.end(), char(0));
|
||||
std::fill(_localBuffer.begin(), _localBuffer.end(), char(0));
|
||||
char* end = format(
|
||||
_buffer,
|
||||
_localBuffer,
|
||||
frametimeType,
|
||||
_minDeltaTimeCache,
|
||||
_maxDeltaTimeCache
|
||||
);
|
||||
std::string_view output = std::string_view(_buffer.data(), end - _buffer.data());
|
||||
|
||||
int nLines = output.empty() ? 0 :
|
||||
static_cast<int>((std::count(output.begin(), output.end(), '\n') + 1));
|
||||
|
||||
ghoul::fontrendering::FontRenderer::defaultRenderer().render(
|
||||
*_font,
|
||||
penPosition,
|
||||
output
|
||||
);
|
||||
penPosition.y -= _font->height() * static_cast<float>(nLines);
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemFramerate::size() const {
|
||||
ZoneScoped
|
||||
|
||||
const FrametimeType t = FrametimeType(_frametimeType.value());
|
||||
char* end = format(_buffer, t, _minDeltaTimeCache, _maxDeltaTimeCache);
|
||||
std::string_view output = std::string_view(_buffer.data(), end - _buffer.data());
|
||||
|
||||
if (output.empty()) {
|
||||
return { 0.f, 0.f };
|
||||
}
|
||||
|
||||
return _font->boundingBox(output);
|
||||
_buffer = std::string(_localBuffer.data(), end - _localBuffer.data());
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/triggerproperty.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/triggerproperty.h>
|
||||
|
||||
namespace ghoul { class Dictionary; }
|
||||
|
||||
@@ -38,10 +38,10 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemFramerate : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemFramerate(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemFramerate(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void update() override;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
glm::vec2 size() const override;
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
@@ -51,7 +51,7 @@ private:
|
||||
double _minDeltaTimeCache = 1.0;
|
||||
double _maxDeltaTimeCache = -1.0;
|
||||
bool _shouldClearCache = true;
|
||||
mutable std::vector<char> _buffer;
|
||||
mutable std::vector<char> _localBuffer;
|
||||
};
|
||||
|
||||
} // openspace
|
||||
|
||||
177
modules/base/dashboard/dashboarditeminputstate.cpp
Normal file
177
modules/base/dashboard/dashboarditeminputstate.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/dashboard/dashboarditeminputstate.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <ghoul/font/font.h>
|
||||
#include <ghoul/font/fontmanager.h>
|
||||
#include <ghoul/font/fontrenderer.h>
|
||||
#include <ghoul/misc/stringhelper.h>
|
||||
|
||||
namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowWhenEnabledInfo = {
|
||||
"ShowWhenEnabled",
|
||||
"Show when enabled",
|
||||
"Show text when the input is enabled.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowWhenDisabledInfo = {
|
||||
"ShowWhenDisabled",
|
||||
"Show when disabled",
|
||||
"Show text when the input is disabled.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowKeyboardInfo = {
|
||||
"ShowKeyboard",
|
||||
"Show Keyboard information",
|
||||
"Display the state of the keyboard input.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowMouseInfo = {
|
||||
"ShowMouse",
|
||||
"Show Mouse information",
|
||||
"Display the state of the mouse input.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowJoystickInfo = {
|
||||
"ShowJoystick",
|
||||
"Show Joystick information",
|
||||
"Display the state of the joystick input.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `DashboardItem` shows the current state of the different methods to provide
|
||||
// user input: keyboard, mouse, and/or joystick.
|
||||
//
|
||||
// Each input method has the ability to be selectively disabled, meaning that all
|
||||
// inputs from that input method are ignored by the system entirely.
|
||||
struct [[codegen::Dictionary(DashboardItemInputState)]] Parameters {
|
||||
// [[codegen::verbatim(ShowWhenEnabledInfo.description)]]
|
||||
std::optional<bool> showWhenEnabled;
|
||||
|
||||
// [[codegen::verbatim(ShowWhenDisabledInfo.description)]]
|
||||
std::optional<bool> showWhenDisabled;
|
||||
|
||||
// [[codegen::verbatim(ShowKeyboardInfo.description)]]
|
||||
std::optional<bool> showKeyboard;
|
||||
|
||||
// [[codegen::verbatim(ShowMouseInfo.description)]]
|
||||
std::optional<bool> showMouse;
|
||||
|
||||
// [[codegen::verbatim(ShowJoystickInfo.description)]]
|
||||
std::optional<bool> showJoystick;
|
||||
};
|
||||
#include "dashboarditeminputstate_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemInputState::Documentation() {
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_inputstate",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemInputState::DashboardItemInputState(const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
, _showWhenEnabled(ShowWhenEnabledInfo, true)
|
||||
, _showWhenDisabled(ShowWhenDisabledInfo, true)
|
||||
, _showKeyboard(ShowKeyboardInfo, true)
|
||||
, _showMouse(ShowMouseInfo, true)
|
||||
, _showJoystick(ShowJoystickInfo, true)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_showWhenEnabled = p.showWhenEnabled.value_or(_showWhenEnabled);
|
||||
addProperty(_showWhenEnabled);
|
||||
|
||||
_showWhenDisabled = p.showWhenDisabled.value_or(_showWhenDisabled);
|
||||
addProperty(_showWhenDisabled);
|
||||
|
||||
_showKeyboard = p.showKeyboard.value_or(_showKeyboard);
|
||||
addProperty(_showKeyboard);
|
||||
|
||||
_showMouse = p.showMouse.value_or(_showMouse);
|
||||
addProperty(_showMouse);
|
||||
|
||||
_showJoystick = p.showJoystick.value_or(_showJoystick);
|
||||
addProperty(_showJoystick);
|
||||
}
|
||||
|
||||
void DashboardItemInputState::update() {
|
||||
ZoneScoped;
|
||||
|
||||
std::vector<std::string> text;
|
||||
if (_showKeyboard) {
|
||||
if (global::navigationHandler->disabledKeybindings()) {
|
||||
if (_showWhenDisabled) {
|
||||
text.emplace_back("Keyboard shortcuts disabled");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_showWhenEnabled) {
|
||||
text.emplace_back("Keyboard shortcuts enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_showMouse) {
|
||||
if (global::navigationHandler->disabledMouse()) {
|
||||
if (_showWhenDisabled) {
|
||||
text.emplace_back("Mouse input disabled");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_showWhenEnabled) {
|
||||
text.emplace_back("Mouse input enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_showJoystick) {
|
||||
if (global::navigationHandler->disabledJoystick()) {
|
||||
if (_showWhenDisabled) {
|
||||
text.emplace_back("Joystick input disabled");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_showWhenEnabled) {
|
||||
text.emplace_back("Joystick input enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_buffer = ghoul::join(std::move(text), "\n");
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
57
modules/base/dashboard/dashboarditeminputstate.h
Normal file
57
modules/base/dashboard/dashboarditeminputstate.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMINPUTSTATE___H__
|
||||
#define __OPENSPACE_MODULE_BASE___DASHBOARDITEMINPUTSTATE___H__
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace properties { class Property; }
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemInputState : public DashboardTextItem {
|
||||
public:
|
||||
explicit DashboardItemInputState(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemInputState() override = default;
|
||||
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
properties::BoolProperty _showWhenEnabled;
|
||||
properties::BoolProperty _showWhenDisabled;
|
||||
|
||||
properties::BoolProperty _showKeyboard;
|
||||
properties::BoolProperty _showMouse;
|
||||
properties::BoolProperty _showJoystick;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___DASHBOARDITEMINPUTSTATE___H__
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -51,28 +51,36 @@ namespace {
|
||||
progress.append("|");
|
||||
return progress;
|
||||
}
|
||||
|
||||
// This `DashboardItem` shows information about the currently active mission. This
|
||||
// includes information about the currently active mission phase, the next phase, and
|
||||
// all subphases of the currently active phase.
|
||||
struct [[codegen::Dictionary(DashboardItemMission)]] Parameters {};
|
||||
#include "dashboarditemmission_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemMission::Documentation() {
|
||||
documentation::Documentation doc = DashboardTextItem::Documentation();
|
||||
doc.name = "DashboardItemMission";
|
||||
doc.id = "base_dashboarditem_mission";
|
||||
return doc;
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_mission",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemMission::DashboardItemMission(const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary, 15.f)
|
||||
{}
|
||||
|
||||
void DashboardItemMission::update() {}
|
||||
|
||||
void DashboardItemMission::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
if (!global::missionManager->hasCurrentMission()) {
|
||||
return;
|
||||
}
|
||||
double currentTime = global::timeManager->time().j2000Seconds();
|
||||
const double currentTime = global::timeManager->time().j2000Seconds();
|
||||
const Mission& mission = global::missionManager->currentMission();
|
||||
|
||||
if (mission.phases().empty()) {
|
||||
@@ -84,13 +92,7 @@ void DashboardItemMission::render(glm::vec2& penPosition) {
|
||||
static constexpr glm::vec4 nonCurrentMissionColor = glm::vec4(0.3f, 0.3f, 0.3f, 1.f);
|
||||
|
||||
// Add spacing
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
" ",
|
||||
nonCurrentMissionColor,
|
||||
ghoul::fontrendering::CrDirection::Down
|
||||
);
|
||||
penPosition.y -= _font->height();
|
||||
|
||||
MissionPhase::Trace phaseTrace = mission.phaseTrace(currentTime);
|
||||
if (!phaseTrace.empty()) {
|
||||
@@ -99,7 +101,7 @@ void DashboardItemMission::render(glm::vec2& penPosition) {
|
||||
penPosition.y -= _font->height();
|
||||
RenderFont(*_font, penPosition, title, missionProgressColor);
|
||||
double remaining = phase.timeRange().end - currentTime;
|
||||
float t = static_cast<float>(
|
||||
const float t = static_cast<float>(
|
||||
1.0 - remaining / phase.timeRange().duration()
|
||||
);
|
||||
std::string progress = progressToStr(25, t);
|
||||
@@ -107,7 +109,7 @@ void DashboardItemMission::render(glm::vec2& penPosition) {
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format("{:.0f} s {:s} {:.1f} %", remaining, progress, t * 100),
|
||||
std::format("{:.0f} s {:s} {:.1f} %", remaining, progress, t * 100),
|
||||
missionProgressColor
|
||||
);
|
||||
}
|
||||
@@ -119,18 +121,21 @@ void DashboardItemMission::render(glm::vec2& penPosition) {
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format("{:.0f} s", remaining),
|
||||
std::format("{:.0f} s", remaining),
|
||||
nextMissionColor
|
||||
);
|
||||
}
|
||||
|
||||
bool showAllPhases = false;
|
||||
// Add spacing
|
||||
penPosition.y -= _font->height();
|
||||
|
||||
constexpr bool ShowAllPhases = false;
|
||||
|
||||
using PhaseWithDepth = std::pair<const MissionPhase*, int>;
|
||||
std::stack<PhaseWithDepth> S;
|
||||
|
||||
constexpr int PixelIndentation = 20;
|
||||
S.push({ &mission, 0 });
|
||||
S.emplace(&mission, 0);
|
||||
while (!S.empty()) {
|
||||
const MissionPhase* phase = S.top().first;
|
||||
const int depth = S.top().second;
|
||||
@@ -145,16 +150,16 @@ void DashboardItemMission::render(glm::vec2& penPosition) {
|
||||
1.0 - remaining / phase->timeRange().duration()
|
||||
);
|
||||
const std::string progress = progressToStr(25, t);
|
||||
penPosition.y -= _font->height();
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(
|
||||
std::format(
|
||||
"{:s} {:s} {:.1f} %",
|
||||
phase->name(),progress,t * 100
|
||||
),
|
||||
currentMissionColor
|
||||
);
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
else {
|
||||
if (!phase->name().empty()) {
|
||||
@@ -169,25 +174,18 @@ void DashboardItemMission::render(glm::vec2& penPosition) {
|
||||
}
|
||||
penPosition.x -= depth * PixelIndentation;
|
||||
|
||||
if (isCurrentPhase || showAllPhases) {
|
||||
if (isCurrentPhase || ShowAllPhases) {
|
||||
// phases are sorted increasingly by start time, and will be
|
||||
// popped last-in-first-out from the stack, so add them in
|
||||
// reversed order.
|
||||
int indexLastPhase = static_cast<int>(
|
||||
phase->phases().size()
|
||||
) - 1;
|
||||
const int indexLastPhase = static_cast<int>(phase->phases().size()) - 1;
|
||||
for (int i = indexLastPhase; 0 <= i; --i) {
|
||||
S.push({ &phase->phases()[i], depth + 1 });
|
||||
S.emplace(&phase->phases()[i], depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemMission::size() const {
|
||||
ZoneScoped
|
||||
|
||||
// @TODO fix this up ---abock
|
||||
return { 0.f, 0.f };
|
||||
penPosition.y += _font->height();
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -33,13 +33,12 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemMission : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemMission(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemMission(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemMission() override = default;
|
||||
|
||||
void update() override;
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -32,17 +32,28 @@
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/util/distanceconversion.h>
|
||||
#include <ghoul/font/font.h>
|
||||
#include <ghoul/font/fontmanager.h>
|
||||
#include <ghoul/font/fontrenderer.h>
|
||||
#include <ghoul/misc/profiling.h>
|
||||
|
||||
namespace {
|
||||
// This `DashboardItem` displays information about the status of the parallel
|
||||
// connection, which is whether OpenSpace is directly connected to other OpenSpace
|
||||
// instances and can either control those instances or be controlled by the master of
|
||||
// the session. If OpenSpace is not connected, this `DashboardItem` will not display
|
||||
// anything.
|
||||
//
|
||||
// The information presented contains how many clients are connected to the same
|
||||
// session and whether this machine is currently the host of the session.
|
||||
struct [[codegen::Dictionary(DashboardItemParallelConnection)]] Parameters {};
|
||||
#include "dashboarditemparallelconnection_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemParallelConnection::Documentation() {
|
||||
documentation::Documentation doc = DashboardTextItem::Documentation();
|
||||
doc.name = "DashboardItemParallelConnection";
|
||||
doc.id = "base_dashboarditem_parallelconnection";
|
||||
return doc;
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_parallelconnection",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemParallelConnection::DashboardItemParallelConnection(
|
||||
@@ -50,109 +61,48 @@ DashboardItemParallelConnection::DashboardItemParallelConnection(
|
||||
: DashboardTextItem(dictionary)
|
||||
{}
|
||||
|
||||
void DashboardItemParallelConnection::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemParallelConnection::update() {
|
||||
ZoneScoped;
|
||||
|
||||
const ParallelConnection::Status status = global::parallelPeer->status();
|
||||
const size_t nConnections = global::parallelPeer->nConnections();
|
||||
const std::string& hostName = global::parallelPeer->hostName();
|
||||
|
||||
int nLines = 1;
|
||||
|
||||
std::string connectionInfo;
|
||||
int nClients = static_cast<int>(nConnections);
|
||||
if (status == ParallelConnection::Status::Host) {
|
||||
nClients--;
|
||||
constexpr std::string_view Singular = "Hosting session with {} client";
|
||||
constexpr std::string_view Plural = "Hosting session with {} clients";
|
||||
|
||||
connectionInfo =
|
||||
_buffer =
|
||||
(nClients == 1) ?
|
||||
fmt::format(Singular, nClients) :
|
||||
fmt::format(Plural, nClients);
|
||||
std::format(Singular, nClients) :
|
||||
std::format(Plural, nClients);
|
||||
}
|
||||
else if (status == ParallelConnection::Status::ClientWithHost) {
|
||||
nClients--;
|
||||
connectionInfo = "Session hosted by '" + hostName + "'";
|
||||
_buffer = "Session hosted by '" + hostName + "'";
|
||||
}
|
||||
else if (status == ParallelConnection::Status::ClientWithoutHost) {
|
||||
connectionInfo = "Host is disconnected";
|
||||
_buffer = "Host is disconnected";
|
||||
}
|
||||
|
||||
if (status == ParallelConnection::Status::ClientWithHost ||
|
||||
status == ParallelConnection::Status::ClientWithoutHost)
|
||||
{
|
||||
connectionInfo += "\n";
|
||||
_buffer += "\n";
|
||||
|
||||
if (nClients > 2) {
|
||||
constexpr std::string_view Plural = "You and {} more clients are tuned in";
|
||||
connectionInfo += fmt::format(Plural, nClients - 1);
|
||||
_buffer += std::format(Plural, nClients - 1);
|
||||
}
|
||||
else if (nClients == 2) {
|
||||
constexpr std::string_view Singular = "You and {} more client are tuned in";
|
||||
connectionInfo += fmt::format(Singular, nClients - 1);
|
||||
_buffer += std::format(Singular, nClients - 1);
|
||||
}
|
||||
else if (nClients == 1) {
|
||||
connectionInfo += "You are the only client";
|
||||
_buffer += "You are the only client";
|
||||
}
|
||||
|
||||
nLines = 2;
|
||||
}
|
||||
|
||||
if (!connectionInfo.empty()) {
|
||||
RenderFont(*_font, penPosition, connectionInfo);
|
||||
penPosition.y -= _font->height() * nLines;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemParallelConnection::size() const {
|
||||
ZoneScoped
|
||||
|
||||
ParallelConnection::Status status = global::parallelPeer->status();
|
||||
size_t nConnections = global::parallelPeer->nConnections();
|
||||
const std::string& hostName = global::parallelPeer->hostName();
|
||||
|
||||
std::string connectionInfo;
|
||||
int nClients = static_cast<int>(nConnections);
|
||||
if (status == ParallelConnection::Status::Host) {
|
||||
nClients--;
|
||||
if (nClients == 1) {
|
||||
connectionInfo = "Hosting session with 1 client";
|
||||
}
|
||||
else {
|
||||
connectionInfo = fmt::format("Hosting session with {} clients", nClients);
|
||||
}
|
||||
}
|
||||
else if (status == ParallelConnection::Status::ClientWithHost) {
|
||||
nClients--;
|
||||
connectionInfo = "Session hosted by '" + hostName + "'";
|
||||
}
|
||||
else if (status == ParallelConnection::Status::ClientWithoutHost) {
|
||||
connectionInfo = "Host is disconnected";
|
||||
}
|
||||
|
||||
if (status == ParallelConnection::Status::ClientWithHost ||
|
||||
status == ParallelConnection::Status::ClientWithoutHost)
|
||||
{
|
||||
connectionInfo += "\n";
|
||||
if (nClients > 2) {
|
||||
constexpr std::string_view Plural = "You and {} more clients are tuned in";
|
||||
connectionInfo += fmt::format(Plural, nClients);
|
||||
}
|
||||
else if (nClients == 2) {
|
||||
constexpr std::string_view Singular = "You and {} more client are tuned in";
|
||||
connectionInfo += fmt::format(Singular, nClients - 1);
|
||||
}
|
||||
else if (nClients == 1) {
|
||||
connectionInfo += "You are the only client";
|
||||
}
|
||||
}
|
||||
|
||||
if (!connectionInfo.empty()) {
|
||||
return _font->boundingBox(connectionInfo);
|
||||
}
|
||||
else {
|
||||
return { 0.f, 0.f };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -33,12 +33,10 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemParallelConnection : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemParallelConnection(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemParallelConnection(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemParallelConnection() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,6 +27,24 @@
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
#include <openspace/properties/scalar/longproperty.h>
|
||||
#include <openspace/properties/scalar/shortproperty.h>
|
||||
#include <openspace/properties/scalar/uintproperty.h>
|
||||
#include <openspace/properties/scalar/ulongproperty.h>
|
||||
#include <openspace/properties/scalar/ushortproperty.h>
|
||||
#include <openspace/properties/vector/dvec2property.h>
|
||||
#include <openspace/properties/vector/dvec3property.h>
|
||||
#include <openspace/properties/vector/dvec4property.h>
|
||||
#include <openspace/properties/vector/ivec2property.h>
|
||||
#include <openspace/properties/vector/ivec3property.h>
|
||||
#include <openspace/properties/vector/ivec4property.h>
|
||||
#include <openspace/properties/vector/uvec2property.h>
|
||||
#include <openspace/properties/vector/uvec3property.h>
|
||||
#include <openspace/properties/vector/uvec4property.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <openspace/properties/vector/vec4property.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <openspace/util/timemanager.h>
|
||||
#include <ghoul/font/font.h>
|
||||
@@ -38,17 +56,27 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo PropertyUriInfo = {
|
||||
"URI",
|
||||
"Property URI",
|
||||
"The URI of the property that is displayed in this dashboarditem"
|
||||
"The URI of the property that is displayed in this dashboard item.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DisplayStringInfo = {
|
||||
"DisplayString",
|
||||
"Display String",
|
||||
"The String that is being displayed. It must either be empty (in which case only "
|
||||
"the value itself will be displayed), or it must contain extact one instance of "
|
||||
"{}, which will be replaced with the value of the property during rendering"
|
||||
"the value itself will be displayed), or it must contain extact one or more "
|
||||
"instances of {}, which will be replaced with the value(s) of the property "
|
||||
"during rendering. For scalar types, there has to be exactly one instance of {}, "
|
||||
"for vector types, there need to be as many {} as there are components in the "
|
||||
"vector, for example two {} for vec2 types, three for vec3 types, etc. For more "
|
||||
"information on how to structure the formatting string, see the documentation at "
|
||||
"https://en.cppreference.com/w/cpp/utility/format/spec.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `DashboardItem` will show the value of the provided property. Depending on the
|
||||
// type of the property, the `DisplayString` will have to be adapted. See the
|
||||
// documentation for the `DisplayString` for more information.
|
||||
struct [[codegen::Dictionary(DashboardItemPropertyValue)]] Parameters {
|
||||
// [[codegen::verbatim(PropertyUriInfo.description)]]
|
||||
std::optional<std::string> uri [[codegen::key("URI")]];
|
||||
@@ -84,29 +112,149 @@ DashboardItemPropertyValue::DashboardItemPropertyValue(
|
||||
addProperty(_displayString);
|
||||
}
|
||||
|
||||
void DashboardItemPropertyValue::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemPropertyValue::update() {
|
||||
ZoneScoped;
|
||||
|
||||
if (_propertyIsDirty) {
|
||||
if (_propertyIsDirty) [[unlikely]] {
|
||||
_property = openspace::property(_propertyUri);
|
||||
_propertyIsDirty = false;
|
||||
}
|
||||
|
||||
if (_property) {
|
||||
std::string value = _property->stringValue();
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(fmt::runtime(_displayString.value()), value)
|
||||
if (!_property) {
|
||||
return;
|
||||
}
|
||||
const std::string_view type = _property->className();
|
||||
if (type == "DoubleProperty") {
|
||||
double value = static_cast<properties::DoubleProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(value));
|
||||
}
|
||||
else if (type == "FloatProperty") {
|
||||
float value = static_cast<properties::FloatProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(value));
|
||||
}
|
||||
else if (type == "IntProperty") {
|
||||
int value = static_cast<properties::IntProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(value));
|
||||
}
|
||||
else if (type == "LongProperty") {
|
||||
long value = static_cast<properties::LongProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(value));
|
||||
}
|
||||
else if (type == "ShortProperty") {
|
||||
short value = static_cast<properties::ShortProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(value));
|
||||
}
|
||||
else if (type == "UIntProperty") {
|
||||
unsigned int v = static_cast<properties::UIntProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(v));
|
||||
}
|
||||
else if (type == "ULongProperty") {
|
||||
unsigned long v = static_cast<properties::ULongProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(v));
|
||||
}
|
||||
else if (type == "UShortProperty") {
|
||||
unsigned short v = static_cast<properties::UShortProperty*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(v));
|
||||
}
|
||||
else if (type == "DVec2Property") {
|
||||
glm::dvec2 v = static_cast<properties::DVec2Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(v.x, v.y));
|
||||
}
|
||||
else if (type == "DVec3Property") {
|
||||
glm::dvec3 v = static_cast<properties::DVec3Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z)
|
||||
);
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
else if (type == "DVec4Property") {
|
||||
glm::dvec4 v = static_cast<properties::DVec4Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z, v.w)
|
||||
);
|
||||
}
|
||||
else if (type == "IVec2Property") {
|
||||
glm::ivec2 v = static_cast<properties::IVec2Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(v.x, v.y));
|
||||
}
|
||||
else if (type == "IVec3Property") {
|
||||
glm::ivec3 v = static_cast<properties::IVec3Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z)
|
||||
);
|
||||
}
|
||||
else if (type == "IVec4Property") {
|
||||
glm::ivec4 v = static_cast<properties::IVec4Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z, v.w)
|
||||
);
|
||||
}
|
||||
else if (type == "UVec2Property") {
|
||||
glm::uvec2 v = static_cast<properties::UVec2Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(v.x, v.y));
|
||||
}
|
||||
else if (type == "UVec3Property") {
|
||||
glm::uvec3 v = static_cast<properties::UVec3Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z)
|
||||
);
|
||||
}
|
||||
else if (type == "UVec4Property") {
|
||||
glm::uvec4 v = static_cast<properties::UVec4Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z, v.w)
|
||||
);
|
||||
}
|
||||
else if (type == "Vec2Property") {
|
||||
glm::vec2 v = static_cast<properties::Vec2Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(v.x, v.y));
|
||||
}
|
||||
else if (type == "Vec3Property") {
|
||||
glm::vec3 v = static_cast<properties::Vec3Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z)
|
||||
);
|
||||
}
|
||||
else if (type == "Vec4Property") {
|
||||
glm::vec4 v = static_cast<properties::Vec4Property*>(_property)->value();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_displayString.value(),
|
||||
std::make_format_args(v.x, v.y, v.z, v.w)
|
||||
);
|
||||
}
|
||||
else {
|
||||
// Fallback if we don't have a special case above
|
||||
|
||||
std::string value = _property->stringValue();
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(_displayString.value(), std::make_format_args(value));
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemPropertyValue::size() const {
|
||||
ZoneScoped
|
||||
|
||||
return _font->boundingBox(_displayString.value());
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -40,9 +40,7 @@ public:
|
||||
DashboardItemPropertyValue(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemPropertyValue() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -41,14 +41,16 @@ namespace {
|
||||
"Time Simplification",
|
||||
"If this value is enabled, the time is displayed in nuanced units, such as "
|
||||
"minutes, hours, days, years, etc. If this value is disabled, it is always "
|
||||
"displayed in seconds"
|
||||
"displayed in seconds.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RequestedUnitInfo = {
|
||||
"RequestedUnit",
|
||||
"Requested Unit",
|
||||
"If the simplification is disabled, this time unit is used as a destination to "
|
||||
"convert the seconds into"
|
||||
"convert the seconds into.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo TransitionFormatInfo = {
|
||||
@@ -59,7 +61,10 @@ namespace {
|
||||
"delta time. This format gets five parameters in this order: The target delta "
|
||||
"time value, the target delta time unit, the string 'Paused' if the delta time "
|
||||
"is paused or the empty string otherwise, the current delta time value, and the "
|
||||
"current delta time unit"
|
||||
"current delta time unit. More information about how to make use of the format "
|
||||
"string, see the documentation at "
|
||||
"https://en.cppreference.com/w/cpp/utility/format/spec.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RegularFormatInfo = {
|
||||
@@ -68,7 +73,10 @@ namespace {
|
||||
"The format string used to format the text if the target delta time is the same "
|
||||
"as the current delta time. This format gets three parameters in this order: "
|
||||
"The target delta value, the target delta unit, and the string 'Paused' if the "
|
||||
"delta time is paused or the empty string otherwise"
|
||||
"delta time is paused or the empty string otherwise. More information about how "
|
||||
"to make use of the format string, see the documentation at "
|
||||
"https://en.cppreference.com/w/cpp/utility/format/spec.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
std::vector<std::string> unitList() {
|
||||
@@ -84,6 +92,10 @@ namespace {
|
||||
return res;
|
||||
}
|
||||
|
||||
// This `DashboardItem` shows how fast the in-game time progresses. The display string
|
||||
// for the `RegularFormat` is used when the current simulation increment is not
|
||||
// changing, the `TransitionFormat` is used if the simulation increment is currently
|
||||
// interpolating to a new value.
|
||||
struct [[codegen::Dictionary(DashboardItemSimulationIncrement)]] Parameters {
|
||||
// [[codegen::verbatim(SimplificationInfo.description)]]
|
||||
std::optional<bool> simplification;
|
||||
@@ -113,7 +125,7 @@ DashboardItemSimulationIncrement::DashboardItemSimulationIncrement(
|
||||
const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
, _doSimplification(SimplificationInfo, true)
|
||||
, _requestedUnit(RequestedUnitInfo, properties::OptionProperty::DisplayType::Dropdown)
|
||||
, _requestedUnit(RequestedUnitInfo)
|
||||
, _transitionFormat(
|
||||
TransitionFormatInfo,
|
||||
"Simulation increment: {:.1f} {:s} / second{:s} (current: {:.1f} {:s})"
|
||||
@@ -132,13 +144,14 @@ DashboardItemSimulationIncrement::DashboardItemSimulationIncrement(
|
||||
});
|
||||
addProperty(_doSimplification);
|
||||
|
||||
for (TimeUnit u : TimeUnits) {
|
||||
for (const TimeUnit u : TimeUnits) {
|
||||
_requestedUnit.addOption(static_cast<int>(u), std::string(nameForTimeUnit(u)));
|
||||
}
|
||||
_requestedUnit = static_cast<int>(TimeUnit::Second);
|
||||
if (p.requestedUnit.has_value()) {
|
||||
TimeUnit unit = timeUnitFromString(p.requestedUnit->c_str());
|
||||
const TimeUnit unit = timeUnitFromString(*p.requestedUnit);
|
||||
_requestedUnit = static_cast<int>(unit);
|
||||
_doSimplification = false;
|
||||
}
|
||||
_requestedUnit.setVisibility(properties::Property::Visibility::Hidden);
|
||||
addProperty(_requestedUnit);
|
||||
@@ -150,8 +163,8 @@ DashboardItemSimulationIncrement::DashboardItemSimulationIncrement(
|
||||
addProperty(_regularFormat);
|
||||
}
|
||||
|
||||
void DashboardItemSimulationIncrement::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemSimulationIncrement::update() {
|
||||
ZoneScoped;
|
||||
|
||||
const double targetDt = global::timeManager->targetDeltaTime();
|
||||
const double currentDt = global::timeManager->deltaTime();
|
||||
@@ -167,17 +180,17 @@ void DashboardItemSimulationIncrement::render(glm::vec2& penPosition) {
|
||||
const TimeUnit unit = static_cast<TimeUnit>(_requestedUnit.value());
|
||||
|
||||
const double convTarget = convertTime(targetDt, TimeUnit::Second, unit);
|
||||
targetDeltaTime = {
|
||||
targetDeltaTime = std::pair(
|
||||
convTarget,
|
||||
std::string(nameForTimeUnit(unit, convTarget != 1.0))
|
||||
};
|
||||
);
|
||||
|
||||
if (targetDt != currentDt) {
|
||||
const double convCurrent = convertTime(currentDt, TimeUnit::Second, unit);
|
||||
currentDeltaTime = {
|
||||
currentDeltaTime = std::pair(
|
||||
convCurrent,
|
||||
std::string(nameForTimeUnit(unit, convCurrent != 1.0))
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,11 +199,10 @@ void DashboardItemSimulationIncrement::render(glm::vec2& penPosition) {
|
||||
try {
|
||||
if (targetDt != currentDt && !global::timeManager->isPaused()) {
|
||||
// We are in the middle of a transition
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(
|
||||
fmt::runtime(_transitionFormat.value()),
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_transitionFormat.value(),
|
||||
std::make_format_args(
|
||||
targetDeltaTime.first, targetDeltaTime.second,
|
||||
pauseText,
|
||||
currentDeltaTime.first, currentDeltaTime.second
|
||||
@@ -198,45 +210,20 @@ void DashboardItemSimulationIncrement::render(glm::vec2& penPosition) {
|
||||
);
|
||||
}
|
||||
else {
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(
|
||||
fmt::runtime(_regularFormat.value()),
|
||||
targetDeltaTime.first, targetDeltaTime.second, pauseText
|
||||
// @CPP26(abock): This can be replaced with std::runtime_format
|
||||
_buffer = std::vformat(
|
||||
_regularFormat.value(),
|
||||
std::make_format_args(
|
||||
targetDeltaTime.first,
|
||||
targetDeltaTime.second,
|
||||
pauseText
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (const fmt::format_error&) {
|
||||
catch (const std::format_error&) {
|
||||
LERRORC("DashboardItemDate", "Illegal format string");
|
||||
}
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemSimulationIncrement::size() const {
|
||||
ZoneScoped
|
||||
|
||||
double t = global::timeManager->targetDeltaTime();
|
||||
std::pair<double, std::string> deltaTime;
|
||||
if (_doSimplification) {
|
||||
deltaTime = simplifyTime(t);
|
||||
}
|
||||
else {
|
||||
TimeUnit unit = static_cast<TimeUnit>(_requestedUnit.value());
|
||||
double convertedT = convertTime(t, TimeUnit::Second, unit);
|
||||
deltaTime = {
|
||||
convertedT,
|
||||
std::string(nameForTimeUnit(unit, convertedT != 1.0))
|
||||
};
|
||||
}
|
||||
|
||||
return _font->boundingBox(
|
||||
fmt::format(
|
||||
"Simulation increment: {:.1f} {:s} / second",
|
||||
deltaTime.first, deltaTime.second
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
@@ -37,11 +37,10 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemSimulationIncrement : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemSimulationIncrement(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemSimulationIncrement(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemSimulationIncrement() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -33,9 +33,12 @@ namespace {
|
||||
"Spacing",
|
||||
"Spacing",
|
||||
"This value determines the spacing (in pixels) that this item represents. The "
|
||||
"default value is 15"
|
||||
"default value is 15.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `DashboardItem` adds a variable amount of spacing between two other
|
||||
// `DashboardItem`s.
|
||||
struct [[codegen::Dictionary(DashboardItemSpacing)]] Parameters {
|
||||
// [[codegen::verbatim(SpacingInfo.description)]]
|
||||
std::optional<float> spacing;
|
||||
@@ -46,7 +49,10 @@ namespace {
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemSpacing::Documentation() {
|
||||
return codegen::doc<Parameters>("base_dashboarditem_spacing");
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_spacing",
|
||||
DashboardItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemSpacing::DashboardItemSpacing(const ghoul::Dictionary& dictionary)
|
||||
@@ -59,12 +65,10 @@ DashboardItemSpacing::DashboardItemSpacing(const ghoul::Dictionary& dictionary)
|
||||
addProperty(_spacing);
|
||||
}
|
||||
|
||||
void DashboardItemSpacing::update() {}
|
||||
|
||||
void DashboardItemSpacing::render(glm::vec2& penPosition) {
|
||||
penPosition.y -= _spacing;
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemSpacing::size() const {
|
||||
return { 0.f, _spacing };
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -35,13 +35,12 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemSpacing : public DashboardItem {
|
||||
public:
|
||||
DashboardItemSpacing(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemSpacing(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemSpacing() override = default;
|
||||
|
||||
void update() override;
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -28,8 +28,6 @@
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <ghoul/font/font.h>
|
||||
#include <ghoul/font/fontmanager.h>
|
||||
#include <ghoul/font/fontrenderer.h>
|
||||
#include <ghoul/misc/profiling.h>
|
||||
#include <optional>
|
||||
|
||||
@@ -37,7 +35,8 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo TextInfo = {
|
||||
"Text",
|
||||
"Text",
|
||||
"The text to be displayed"
|
||||
"The text to be displayed.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(DashboardItemText)]] Parameters {
|
||||
@@ -65,17 +64,8 @@ DashboardItemText::DashboardItemText(const ghoul::Dictionary& dictionary)
|
||||
addProperty(_text);
|
||||
}
|
||||
|
||||
void DashboardItemText::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
|
||||
RenderFont(*_font, penPosition, _text.value());
|
||||
penPosition.y -= _font->height();
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemText::size() const {
|
||||
ZoneScoped
|
||||
|
||||
return _font->boundingBox(_text.value());
|
||||
void DashboardItemText::update() {
|
||||
_buffer = _text.value();
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -35,12 +35,10 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemText : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemText(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemText(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemText() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
|
||||
237
modules/base/dashboard/dashboarditemtimevaryingtext.cpp
Normal file
237
modules/base/dashboard/dashboarditemtimevaryingtext.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/dashboard/dashboarditemtimevaryingtext.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/json.h>
|
||||
#include <openspace/util/timemanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo FormatStringInfo = {
|
||||
"FormatString",
|
||||
"Format String",
|
||||
"The format text describing how this dashboard item renders its text. This text "
|
||||
"must contain exactly one {} which is a placeholder that will be replaced "
|
||||
"with the values read from the file provided in `DataFile`",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DataFileInfo = {
|
||||
"DataFile",
|
||||
"Data File Path",
|
||||
"The file path to the JSON data.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `DashboardItem` displays text based on the content of a provided data file.
|
||||
// The value that is displayed depends on the current in-game simulation time.
|
||||
//
|
||||
// The JSON must contain a 'data' array with timestamp-value pairs. Example format:
|
||||
// {\"data\": [[\"2024-05-10T00:00:00Z\", 2.33], [\"2024-05-10T03:00:00Z\", 3.0]]}
|
||||
struct [[codegen::Dictionary(DashboardItemTimeVaryingText)]] Parameters {
|
||||
// [[codegen::verbatim(FormatStringInfo.description)]]
|
||||
std::optional<std::string> formatString;
|
||||
|
||||
// [[codegen::verbatim(DataFileInfo.description)]]
|
||||
std::filesystem::path dataFile;
|
||||
};
|
||||
#include "dashboarditemtimevaryingtext_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation DashboardItemTimeVaryingText::Documentation() {
|
||||
return codegen::doc<Parameters>(
|
||||
"base_dashboarditem_timevaryingtext",
|
||||
DashboardTextItem::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
DashboardItemTimeVaryingText::DashboardItemTimeVaryingText(
|
||||
const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
, _formatString(FormatStringInfo, "{}")
|
||||
, _dataFile(DataFileInfo, "")
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_formatString = p.formatString.value_or(_formatString);
|
||||
addProperty(_formatString);
|
||||
|
||||
_dataFile.onChange([this]() { loadDataFromJson(_dataFile); });
|
||||
_dataFile = p.dataFile.string();
|
||||
addProperty(_dataFile);
|
||||
}
|
||||
|
||||
void DashboardItemTimeVaryingText::update() {
|
||||
if (_startTimes.empty()) {
|
||||
_buffer.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
double current = global::timeManager->time().j2000Seconds();
|
||||
double first = _startTimes.front();
|
||||
double last = _sequenceEndTime;
|
||||
|
||||
if (current >= first && current < last) {
|
||||
int newIdx = updateActiveTriggerTimeIndex(current);
|
||||
if (newIdx != _activeTriggerTimeIndex) {
|
||||
_activeTriggerTimeIndex = newIdx;
|
||||
double timeKey = _startTimes[_activeTriggerTimeIndex];
|
||||
const nlohmann::json value = _data[timeKey];
|
||||
try {
|
||||
switch (value.type()) {
|
||||
case nlohmann::json::value_t::null:
|
||||
case nlohmann::json::value_t::discarded:
|
||||
break;
|
||||
case nlohmann::json::value_t::boolean: {
|
||||
const bool v = value.get<bool>();
|
||||
_buffer = std::vformat(
|
||||
_formatString.value(),
|
||||
std::make_format_args(v)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case nlohmann::json::value_t::string: {
|
||||
const std::string v = value.get<std::string>();
|
||||
_buffer = std::vformat(
|
||||
_formatString.value(),
|
||||
std::make_format_args(v)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case nlohmann::json::value_t::number_integer: {
|
||||
const int v = value.get<int>();
|
||||
_buffer = std::vformat(
|
||||
_formatString.value(),
|
||||
std::make_format_args(v)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case nlohmann::json::value_t::number_unsigned: {
|
||||
const unsigned v = value.get<unsigned>();
|
||||
_buffer = std::vformat(
|
||||
_formatString.value(),
|
||||
std::make_format_args(v)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case nlohmann::json::value_t::number_float: {
|
||||
const double v = value.get<double>();
|
||||
_buffer = std::vformat(
|
||||
_formatString.value(),
|
||||
std::make_format_args(v)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case nlohmann::json::value_t::object:
|
||||
case nlohmann::json::value_t::array: {
|
||||
const std::string v = nlohmann::to_string(value);
|
||||
_buffer = std::vformat(
|
||||
_formatString.value(),
|
||||
std::make_format_args(v)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case nlohmann::json::value_t::binary: {
|
||||
LWARNINGC(
|
||||
"DashboardItemTimeVaryingText",
|
||||
"Binary data is not supported"
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::format_error&) {
|
||||
LERRORC("DashboardItemTimeVaryingText", "Illegal format string");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_activeTriggerTimeIndex = -1;
|
||||
_buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void DashboardItemTimeVaryingText::loadDataFromJson(const std::string& filePath) {
|
||||
std::ifstream file = std::ifstream(filePath);
|
||||
if (!file.is_open()) {
|
||||
throw ghoul::RuntimeError(std::format(
|
||||
"Time varying text, '{}' is not a valid JSON file",
|
||||
filePath
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json jsonData;
|
||||
file >> jsonData;
|
||||
|
||||
if (jsonData.find("data") == jsonData.end()) {
|
||||
throw ghoul::RuntimeError(std::format(
|
||||
"Error loading JSON file. No 'data' was found in '{}'", filePath
|
||||
));
|
||||
}
|
||||
|
||||
_data.clear();
|
||||
_startTimes.clear();
|
||||
|
||||
for (const nlohmann::json& item : jsonData["data"]) {
|
||||
const std::string& timeString = item[0].get<std::string>();
|
||||
double j2000Time = Time::convertTime(timeString);
|
||||
_data[j2000Time] = item[1];
|
||||
_startTimes.push_back(j2000Time);
|
||||
}
|
||||
|
||||
std::sort(_startTimes.begin(), _startTimes.end());
|
||||
computeSequenceEndTime();
|
||||
}
|
||||
|
||||
void DashboardItemTimeVaryingText::computeSequenceEndTime() {
|
||||
if (_startTimes.size() > 1) {
|
||||
double first = _startTimes.front();
|
||||
double last = _startTimes.back();
|
||||
double avgDuration = (last - first) / static_cast<double>(_startTimes.size() - 1);
|
||||
// Extend end time so the last value remains visible for one more interval
|
||||
_sequenceEndTime = last + avgDuration;
|
||||
}
|
||||
}
|
||||
|
||||
int DashboardItemTimeVaryingText::updateActiveTriggerTimeIndex(double currentTime) const {
|
||||
auto it = std::upper_bound(_startTimes.begin(), _startTimes.end(), currentTime);
|
||||
if (it != _startTimes.end()) {
|
||||
if (it != _startTimes.begin()) {
|
||||
return static_cast<int>(std::distance(_startTimes.begin(), it)) - 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return static_cast<int>(_startTimes.size()) - 1;
|
||||
}
|
||||
}// namespace openspace
|
||||
63
modules/base/dashboard/dashboarditemtimevaryingtext.h
Normal file
63
modules/base/dashboard/dashboarditemtimevaryingtext.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMTIMEVARYINGTEXT___H__
|
||||
#define __OPENSPACE_MODULE_BASE___DASHBOARDITEMTIMEVARYINGTEXT___H__
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/json.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemTimeVaryingText : public DashboardTextItem {
|
||||
public:
|
||||
explicit DashboardItemTimeVaryingText(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemTimeVaryingText() override = default;
|
||||
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
void loadDataFromJson(const std::string& filePath);
|
||||
void computeSequenceEndTime();
|
||||
int updateActiveTriggerTimeIndex(double currentTime) const;
|
||||
|
||||
properties::StringProperty _formatString;
|
||||
properties::StringProperty _dataFile;
|
||||
|
||||
std::unordered_map<double, nlohmann::json> _data;
|
||||
std::vector<double> _startTimes;
|
||||
|
||||
int _activeTriggerTimeIndex = -1;
|
||||
double _sequenceEndTime = 0.0;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___DASHBOARDITEMTIMEVARYINGTEXT___H__
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -34,8 +34,6 @@
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/util/distanceconversion.h>
|
||||
#include <ghoul/font/font.h>
|
||||
#include <ghoul/font/fontmanager.h>
|
||||
#include <ghoul/font/fontrenderer.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/profiling.h>
|
||||
|
||||
@@ -45,38 +43,33 @@ namespace {
|
||||
"Simplification",
|
||||
"If this value is enabled, the velocity is displayed in nuanced units, such as "
|
||||
"m/s, AU/s, light years / s etc. If this value is disabled, the unit can be "
|
||||
"explicitly requested"
|
||||
"explicitly requested.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RequestedUnitInfo = {
|
||||
"RequestedUnit",
|
||||
"Requested Unit",
|
||||
"If the simplification is disabled, this distance unit is used for the velocity "
|
||||
"display"
|
||||
"display.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
std::vector<std::string> unitList() {
|
||||
std::vector<std::string> res(openspace::DistanceUnits.size());
|
||||
std::transform(
|
||||
openspace::DistanceUnits.begin(),
|
||||
openspace::DistanceUnits.end(),
|
||||
res.begin(),
|
||||
[](openspace::DistanceUnit unit) {
|
||||
return std::string(nameForDistanceUnit(unit));
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
// This `DashboardItem` shows the velocity of the camera, that is how fast the camera
|
||||
// has moved since the last frame in the amount of time it took to render that frame.
|
||||
// The `Simplification` and `RequestedUnit` can be used to control which unit is used
|
||||
// to display the speed. By default, `Simplification` is enabled, which means that the
|
||||
// most natural unit, that is, the one that gives the least number of printed digits,
|
||||
// is used.
|
||||
struct [[codegen::Dictionary(DashboardItemVelocity)]] Parameters {
|
||||
// [[codegen::verbatim(SimplificationInfo.description)]]
|
||||
std::optional<bool> simplification;
|
||||
|
||||
// [[codegen::verbatim(RequestedUnitInfo.description)]]
|
||||
std::optional<std::string> requestedUnit [[codegen::inlist(unitList())]];
|
||||
std::optional<std::string> requestedUnit
|
||||
[[codegen::inlist(openspace::distanceUnitList())]];
|
||||
};
|
||||
#include "dashboarditemvelocity_codegen.cpp"
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
@@ -91,10 +84,9 @@ documentation::Documentation DashboardItemVelocity::Documentation() {
|
||||
DashboardItemVelocity::DashboardItemVelocity(const ghoul::Dictionary& dictionary)
|
||||
: DashboardTextItem(dictionary)
|
||||
, _doSimplification(SimplificationInfo, true)
|
||||
, _requestedUnit(RequestedUnitInfo, properties::OptionProperty::DisplayType::Dropdown)
|
||||
, _requestedUnit(RequestedUnitInfo)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_doSimplification = p.simplification.value_or(_doSimplification);
|
||||
_doSimplification.onChange([this]() {
|
||||
_requestedUnit.setVisibility(
|
||||
_doSimplification ?
|
||||
@@ -102,9 +94,10 @@ DashboardItemVelocity::DashboardItemVelocity(const ghoul::Dictionary& dictionary
|
||||
properties::Property::Visibility::User
|
||||
);
|
||||
});
|
||||
_doSimplification = p.simplification.value_or(_doSimplification);
|
||||
addProperty(_doSimplification);
|
||||
|
||||
for (DistanceUnit u : DistanceUnits) {
|
||||
for (const DistanceUnit u : DistanceUnits) {
|
||||
_requestedUnit.addOption(
|
||||
static_cast<int>(u),
|
||||
std::string(nameForDistanceUnit(u))
|
||||
@@ -112,22 +105,22 @@ DashboardItemVelocity::DashboardItemVelocity(const ghoul::Dictionary& dictionary
|
||||
}
|
||||
_requestedUnit = static_cast<int>(DistanceUnit::Meter);
|
||||
if (p.requestedUnit.has_value()) {
|
||||
DistanceUnit unit = distanceUnitFromString(p.requestedUnit->c_str());
|
||||
const DistanceUnit unit = distanceUnitFromString(*p.requestedUnit);
|
||||
_requestedUnit = static_cast<int>(unit);
|
||||
_doSimplification = false;
|
||||
}
|
||||
_requestedUnit.setVisibility(properties::Property::Visibility::Hidden);
|
||||
addProperty(_requestedUnit);
|
||||
}
|
||||
|
||||
void DashboardItemVelocity::render(glm::vec2& penPosition) {
|
||||
ZoneScoped
|
||||
void DashboardItemVelocity::update() {
|
||||
ZoneScoped;
|
||||
|
||||
const glm::dvec3 currentPos = global::renderEngine->scene()->camera()->positionVec3();
|
||||
const glm::dvec3 dt = currentPos - _prevPosition;
|
||||
_prevPosition = currentPos;
|
||||
|
||||
const double speedPerFrame = glm::length(dt);
|
||||
|
||||
const double secondsPerFrame = global::windowDelegate->averageDeltaTime();
|
||||
|
||||
const double speedPerSecond = speedPerFrame / secondsPerFrame;
|
||||
|
||||
std::pair<double, std::string_view> dist;
|
||||
@@ -137,38 +130,10 @@ void DashboardItemVelocity::render(glm::vec2& penPosition) {
|
||||
else {
|
||||
const DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
|
||||
const double convertedD = convertMeters(speedPerSecond, unit);
|
||||
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
|
||||
dist = std::pair(convertedD, nameForDistanceUnit(unit, convertedD != 1.0));
|
||||
}
|
||||
|
||||
RenderFont(
|
||||
*_font,
|
||||
penPosition,
|
||||
fmt::format(
|
||||
"Camera velocity: {:.4f} {}/s", dist.first, dist.second
|
||||
)
|
||||
);
|
||||
penPosition.y -= _font->height();
|
||||
|
||||
_prevPosition = currentPos;
|
||||
}
|
||||
|
||||
glm::vec2 DashboardItemVelocity::size() const {
|
||||
ZoneScoped
|
||||
|
||||
const double d = glm::length(1e20);
|
||||
std::pair<double, std::string_view> dist;
|
||||
if (_doSimplification) {
|
||||
dist = simplifyDistance(d);
|
||||
}
|
||||
else {
|
||||
DistanceUnit unit = static_cast<DistanceUnit>(_requestedUnit.value());
|
||||
double convertedD = convertMeters(d, unit);
|
||||
dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) };
|
||||
}
|
||||
|
||||
return _font->boundingBox(
|
||||
fmt::format("Camera velocity: {} {}/s", dist.first, dist.second)
|
||||
);
|
||||
_buffer = std::format("Camera velocity: {:.4f} {}/s", dist.first, dist.second);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <openspace/rendering/dashboardtextitem.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <utility>
|
||||
|
||||
@@ -39,12 +39,10 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class DashboardItemVelocity : public DashboardTextItem {
|
||||
public:
|
||||
DashboardItemVelocity(const ghoul::Dictionary& dictionary);
|
||||
explicit DashboardItemVelocity(const ghoul::Dictionary& dictionary);
|
||||
~DashboardItemVelocity() override = default;
|
||||
|
||||
void render(glm::vec2& penPosition) override;
|
||||
|
||||
glm::vec2 size() const override;
|
||||
void update() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -33,9 +33,13 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo IntensityInfo = {
|
||||
"Intensity",
|
||||
"Intensity",
|
||||
"The intensity of this light source"
|
||||
"The intensity of this light source.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
// This `LightSource` type represents a light source placed at the position of the
|
||||
// camera. An object with this light source will always be illuminated from the
|
||||
// current view direction.
|
||||
struct [[codegen::Dictionary(CameraLightSource)]] Parameters {
|
||||
// [[codegen::verbatim(IntensityInfo.description)]]
|
||||
std::optional<float> intensity;
|
||||
@@ -49,12 +53,6 @@ documentation::Documentation CameraLightSource::Documentation() {
|
||||
return codegen::doc<Parameters>("base_camera_light_source");
|
||||
}
|
||||
|
||||
CameraLightSource::CameraLightSource()
|
||||
: _intensity(IntensityInfo, 1.f, 0.f, 1.f)
|
||||
{
|
||||
addProperty(_intensity);
|
||||
}
|
||||
|
||||
CameraLightSource::CameraLightSource(const ghoul::Dictionary& dictionary)
|
||||
: LightSource(dictionary)
|
||||
, _intensity(IntensityInfo, 1.f, 0.f, 1.f)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <openspace/scene/lightsource.h>
|
||||
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -36,14 +35,13 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class CameraLightSource : public LightSource {
|
||||
public:
|
||||
CameraLightSource();
|
||||
CameraLightSource(const ghoul::Dictionary& dictionary);
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
explicit CameraLightSource(const ghoul::Dictionary& dictionary);
|
||||
|
||||
glm::vec3 directionViewSpace(const RenderData& renderData) const override;
|
||||
float intensity() const override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
properties::FloatProperty _intensity;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -37,21 +37,31 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo IntensityInfo = {
|
||||
"Intensity",
|
||||
"Intensity",
|
||||
"The intensity of this light source"
|
||||
"The intensity of this light source.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo NodeInfo = {
|
||||
"Node",
|
||||
"Node",
|
||||
"The identifier of the scene graph node to follow"
|
||||
"The identifier of the scene graph node to follow.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
// This `LightSource` type represents a light source placed at the position of a
|
||||
// scene graph node. That is, the direction of the light will follow the position
|
||||
// of an existing object in the scene. It will also update dynamically as the
|
||||
// object moves.
|
||||
//
|
||||
// Note that the brightness of the light from the light source does not depend on
|
||||
// the distance between the two scene graph nodes. Only the `Intensity` value has
|
||||
// an impact on the brightness.
|
||||
struct [[codegen::Dictionary(SceneGraphLightSource)]] Parameters {
|
||||
// [[codegen::verbatim(IntensityInfo.description)]]
|
||||
std::optional<float> intensity;
|
||||
|
||||
// [[codegen::verbatim(NodeInfo.description)]]
|
||||
std::string node;
|
||||
std::string node [[codegen::identifier()]];
|
||||
};
|
||||
#include "scenegraphlightsource_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -62,28 +72,26 @@ documentation::Documentation SceneGraphLightSource::Documentation() {
|
||||
return codegen::doc<Parameters>("base_scene_graph_light_source");
|
||||
}
|
||||
|
||||
SceneGraphLightSource::SceneGraphLightSource()
|
||||
: _intensity(IntensityInfo, 1.f, 0.f, 1.f)
|
||||
SceneGraphLightSource::SceneGraphLightSource(const ghoul::Dictionary& dictionary)
|
||||
: LightSource(dictionary)
|
||||
, _intensity(IntensityInfo, 1.f, 0.f, 1.f)
|
||||
, _sceneGraphNodeReference(NodeInfo, "")
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_intensity = p.intensity.value_or(_intensity);
|
||||
addProperty(_intensity);
|
||||
|
||||
_sceneGraphNodeReference.onChange([this]() {
|
||||
_sceneGraphNode =
|
||||
global::renderEngine->scene()->sceneGraphNode(_sceneGraphNodeReference);
|
||||
});
|
||||
addProperty(_sceneGraphNodeReference);
|
||||
}
|
||||
|
||||
SceneGraphLightSource::SceneGraphLightSource(const ghoul::Dictionary& dictionary)
|
||||
: SceneGraphLightSource()
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_intensity = p.intensity.value_or(_intensity);
|
||||
_sceneGraphNodeReference = p.node;
|
||||
}
|
||||
|
||||
bool SceneGraphLightSource::initialize() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
_sceneGraphNode =
|
||||
global::renderEngine->scene()->sceneGraphNode(_sceneGraphNodeReference);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/scene/lightsource.h>
|
||||
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -36,8 +36,7 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class SceneGraphLightSource : public LightSource {
|
||||
public:
|
||||
SceneGraphLightSource();
|
||||
SceneGraphLightSource(const ghoul::Dictionary& dictionary);
|
||||
explicit SceneGraphLightSource(const ghoul::Dictionary& dictionary);
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -39,34 +39,28 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
||||
"Color",
|
||||
"Color",
|
||||
"This value determines the color of the grid lines that are rendered"
|
||||
"The color used for the grid lines.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
||||
"LineWidth",
|
||||
"Line Width",
|
||||
"This value specifies the line width of the spherical grid"
|
||||
"The width of the grid lines. The larger number, the thicker the lines.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
|
||||
"Size",
|
||||
"Grid Size",
|
||||
"This value species the size of each dimensions of the box"
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DrawLabelInfo = {
|
||||
"DrawLabels",
|
||||
"Draw Labels",
|
||||
"Determines whether labels should be drawn or hidden"
|
||||
};
|
||||
|
||||
static const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo =
|
||||
{
|
||||
"Labels",
|
||||
"Labels",
|
||||
"The labels for the grid"
|
||||
"The size of each dimension of the box, in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
// A RenderableBoxGrid creates a 3D box that is rendered using grid lines.
|
||||
//
|
||||
// Per default the box is given a uniform size of 1x1x1 meters. It can then be scaled
|
||||
// to the desired size. Alternatively, the size in each dimension can be specified.
|
||||
struct [[codegen::Dictionary(RenderableBoxGrid)]] Parameters {
|
||||
// [[codegen::verbatim(ColorInfo.description)]]
|
||||
std::optional<glm::vec3> color [[codegen::color()]];
|
||||
@@ -76,13 +70,6 @@ namespace {
|
||||
|
||||
// [[codegen::verbatim(SizeInfo.description)]]
|
||||
std::optional<glm::vec3> size;
|
||||
|
||||
// [[codegen::verbatim(DrawLabelInfo.description)]]
|
||||
std::optional<bool> drawLabels;
|
||||
|
||||
// [[codegen::verbatim(LabelsInfo.description)]]
|
||||
std::optional<ghoul::Dictionary> labels
|
||||
[[codegen::reference("space_labelscomponent")]];
|
||||
};
|
||||
#include "renderableboxgrid_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -97,13 +84,11 @@ RenderableBoxGrid::RenderableBoxGrid(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary)
|
||||
, _color(ColorInfo, glm::vec3(0.5f), glm::vec3(0.f), glm::vec3(1.f))
|
||||
, _lineWidth(LineWidthInfo, 0.5f, 1.f, 20.f)
|
||||
, _size(SizeInfo, glm::vec3(1.f), glm::vec3(1.f), glm::vec3(100.f))
|
||||
, _drawLabels(DrawLabelInfo, false)
|
||||
, _size(SizeInfo, glm::vec3(1.f), glm::vec3(0.0001f), glm::vec3(100.f))
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(_opacity);
|
||||
registerUpdateRenderBinFromOpacity();
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_color = p.color.value_or(_color);
|
||||
_color.setViewOption(properties::Property::ViewOptions::Color);
|
||||
@@ -113,28 +98,12 @@ RenderableBoxGrid::RenderableBoxGrid(const ghoul::Dictionary& dictionary)
|
||||
addProperty(_lineWidth);
|
||||
|
||||
_size = p.size.value_or(_size);
|
||||
_size.onChange([&]() { _gridIsDirty = true; });
|
||||
_size.onChange([this]() { _gridIsDirty = true; });
|
||||
addProperty(_size);
|
||||
|
||||
if (p.labels.has_value()) {
|
||||
_drawLabels = p.drawLabels.value_or(_drawLabels);
|
||||
addProperty(_drawLabels);
|
||||
|
||||
_labels = std::make_unique<LabelsComponent>(*p.labels);
|
||||
_hasLabels = true;
|
||||
addPropertySubOwner(_labels.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableBoxGrid::isReady() const {
|
||||
return _hasLabels ? _gridProgram && _labels->isReady() : _gridProgram != nullptr;
|
||||
}
|
||||
|
||||
void RenderableBoxGrid::initialize() {
|
||||
if (_hasLabels) {
|
||||
_labels->initialize();
|
||||
_labels->loadLabels();
|
||||
}
|
||||
return _gridProgram != nullptr;
|
||||
}
|
||||
|
||||
void RenderableBoxGrid::initializeGL() {
|
||||
@@ -174,30 +143,23 @@ void RenderableBoxGrid::deinitializeGL() {
|
||||
_gridProgram = nullptr;
|
||||
}
|
||||
|
||||
void RenderableBoxGrid::render(const RenderData& data, RendererTasks&){
|
||||
void RenderableBoxGrid::render(const RenderData& data, RendererTasks&) {
|
||||
_gridProgram->activate();
|
||||
|
||||
glm::dmat4 modelTransform =
|
||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation
|
||||
glm::dmat4(data.modelTransform.rotation) * // Spice rotation
|
||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
||||
|
||||
glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform;
|
||||
const glm::dmat4 projectionMatrix = data.camera.projectionMatrix();
|
||||
|
||||
const glm::dmat4 modelViewProjectionMatrix = projectionMatrix * modelViewTransform;
|
||||
auto [modelTransform, modelViewTransform, modelViewProjectionTransform] =
|
||||
calcAllTransforms(data);
|
||||
|
||||
_gridProgram->setUniform("modelViewTransform", modelViewTransform);
|
||||
_gridProgram->setUniform("MVPTransform", modelViewProjectionMatrix);
|
||||
_gridProgram->setUniform("MVPTransform", modelViewProjectionTransform);
|
||||
_gridProgram->setUniform("opacity", opacity());
|
||||
_gridProgram->setUniform("gridColor", _color);
|
||||
|
||||
// Change GL state:
|
||||
#ifndef __APPLE__
|
||||
glLineWidth(_lineWidth);
|
||||
#else
|
||||
#else // ^^^^ __APPLE__ // !__APPLE__ vvvv
|
||||
glLineWidth(1.f);
|
||||
#endif
|
||||
#endif // __APPLE__
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnablei(GL_BLEND, 0);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
@@ -213,35 +175,10 @@ void RenderableBoxGrid::render(const RenderData& data, RendererTasks&){
|
||||
global::renderEngine->openglStateCache().resetBlendState();
|
||||
global::renderEngine->openglStateCache().resetLineState();
|
||||
global::renderEngine->openglStateCache().resetDepthState();
|
||||
|
||||
// Draw labels
|
||||
if (_drawLabels && _hasLabels) {
|
||||
const glm::vec3 lookup = data.camera.lookUpVectorWorldSpace();
|
||||
const glm::vec3 viewDirection = data.camera.viewDirectionWorldSpace();
|
||||
glm::vec3 right = glm::cross(viewDirection, lookup);
|
||||
const glm::vec3 up = glm::cross(right, viewDirection);
|
||||
|
||||
const glm::dmat4 worldToModelTransform = glm::inverse(modelTransform);
|
||||
glm::vec3 orthoRight = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
|
||||
);
|
||||
|
||||
if (orthoRight == glm::vec3(0.0)) {
|
||||
glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
|
||||
right = glm::cross(viewDirection, otherVector);
|
||||
orthoRight = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
|
||||
);
|
||||
}
|
||||
const glm::vec3 orthoUp = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::dvec4(up, 0.0))
|
||||
);
|
||||
_labels->render(data, modelViewProjectionMatrix, orthoRight, orthoUp);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableBoxGrid::update(const UpdateData&) {
|
||||
if (_gridIsDirty) {
|
||||
if (_gridIsDirty) [[unlikely]] {
|
||||
const glm::vec3 llf = -_size.value() / 2.f;
|
||||
const glm::vec3 urb = _size.value() / 2.f;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <modules/space/labelscomponent.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
@@ -42,9 +41,8 @@ namespace openspace {
|
||||
|
||||
class RenderableBoxGrid : public Renderable {
|
||||
public:
|
||||
RenderableBoxGrid(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableBoxGrid(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initialize() override;
|
||||
void initializeGL() override;
|
||||
void deinitializeGL() override;
|
||||
|
||||
@@ -73,11 +71,6 @@ protected:
|
||||
|
||||
GLenum _mode = GL_LINE_STRIP;
|
||||
std::vector<Vertex> _varray;
|
||||
|
||||
// Labels
|
||||
bool _hasLabels = false;
|
||||
properties::BoolProperty _drawLabels;
|
||||
std::unique_ptr<LabelsComponent> _labels;
|
||||
};
|
||||
|
||||
}// namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -39,20 +39,22 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
||||
"Color",
|
||||
"Color",
|
||||
"This value determines the color of the grid lines that are rendered"
|
||||
"The color of the grid lines.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo HighlightColorInfo = {
|
||||
"HighlightColor",
|
||||
"Highlight Color",
|
||||
"This value determines the color of the highlighted lines in the grid"
|
||||
"The color of the highlighted lines in the grid.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
|
||||
"Segments",
|
||||
"Number of Segments",
|
||||
"This value specifies the number of segments that are used to render the "
|
||||
"grid in each direction"
|
||||
"The number of segments to split the grid into, in each direction (x and y).",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo HighlightRateInfo = {
|
||||
@@ -60,40 +62,44 @@ namespace {
|
||||
"Highlight Rate",
|
||||
"The rate that the columns and rows are highlighted, counted with respect to the "
|
||||
"center of the grid. If the number of segments in the grid is odd, the "
|
||||
"highlighting might be offset from the center."
|
||||
"highlighting might be offset from the center.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
||||
"LineWidth",
|
||||
"Line Width",
|
||||
"This value specifies the line width of the grid"
|
||||
"The width of the grid lines. The larger number, the thicker the lines.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo HighlightLineWidthInfo = {
|
||||
"HighlightLineWidth",
|
||||
"Highlight Line Width",
|
||||
"This value specifies the line width of the highlighted lines in the grid"
|
||||
"The width of the highlighted grid lines. The larger number, the thicker the "
|
||||
"lines.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
|
||||
"Size",
|
||||
"Grid Size",
|
||||
"This value species the size of each dimensions of the grid"
|
||||
"The size of the grid (in the x and y direction), given in meters.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DrawLabelInfo = {
|
||||
"DrawLabels",
|
||||
"Draw Labels",
|
||||
"Determines whether labels should be drawn or hidden"
|
||||
};
|
||||
|
||||
static const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo =
|
||||
{
|
||||
const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo = {
|
||||
"Labels",
|
||||
"Labels",
|
||||
"The labels for the grid"
|
||||
"The labels for the grid."
|
||||
};
|
||||
|
||||
// This `Renderable` can be used to create a planar grid, to for example illustrate
|
||||
// distances in 3D space.
|
||||
//
|
||||
// The grid is created by specifying a size and how many segments to split each
|
||||
// dimension into. A secondary color can be used to highlight grid lines with a
|
||||
// given interval.
|
||||
struct [[codegen::Dictionary(RenderableGrid)]] Parameters {
|
||||
// [[codegen::verbatim(ColorInfo.description)]]
|
||||
std::optional<glm::vec3> color [[codegen::color()]];
|
||||
@@ -116,12 +122,8 @@ namespace {
|
||||
// [[codegen::verbatim(SizeInfo.description)]]
|
||||
std::optional<glm::vec2> size;
|
||||
|
||||
// [[codegen::verbatim(DrawLabelInfo.description)]]
|
||||
std::optional<bool> drawLabels;
|
||||
|
||||
// [[codegen::verbatim(LabelsInfo.description)]]
|
||||
std::optional<ghoul::Dictionary> labels
|
||||
[[codegen::reference("space_labelscomponent")]];
|
||||
std::optional<ghoul::Dictionary> labels [[codegen::reference("labelscomponent")]];
|
||||
};
|
||||
#include "renderablegrid_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -141,12 +143,10 @@ RenderableGrid::RenderableGrid(const ghoul::Dictionary& dictionary)
|
||||
, _lineWidth(LineWidthInfo, 0.5f, 1.f, 20.f)
|
||||
, _highlightLineWidth(HighlightLineWidthInfo, 0.5f, 1.f, 20.f)
|
||||
, _size(SizeInfo, glm::vec2(1.f), glm::vec2(1.f), glm::vec2(1e11f))
|
||||
, _drawLabels(DrawLabelInfo, false)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(_opacity);
|
||||
registerUpdateRenderBinFromOpacity();
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_color = p.color.value_or(_color);
|
||||
_color.setViewOption(properties::Property::ViewOptions::Color);
|
||||
@@ -158,11 +158,11 @@ RenderableGrid::RenderableGrid(const ghoul::Dictionary& dictionary)
|
||||
addProperty(_highlightColor);
|
||||
|
||||
_segments = p.segments.value_or(_segments);
|
||||
_segments.onChange([&]() { _gridIsDirty = true; });
|
||||
_segments.onChange([this]() { _gridIsDirty = true; });
|
||||
addProperty(_segments);
|
||||
|
||||
_highlightRate = p.highlightRate.value_or(_highlightRate);
|
||||
_highlightRate.onChange([&]() { _gridIsDirty = true; });
|
||||
_highlightRate.onChange([this]() { _gridIsDirty = true; });
|
||||
addProperty(_highlightRate);
|
||||
|
||||
_lineWidth = p.lineWidth.value_or(_lineWidth);
|
||||
@@ -174,27 +174,25 @@ RenderableGrid::RenderableGrid(const ghoul::Dictionary& dictionary)
|
||||
|
||||
_size.setExponent(10.f);
|
||||
_size = p.size.value_or(_size);
|
||||
_size.onChange([&]() { _gridIsDirty = true; });
|
||||
_size.onChange([this]() { _gridIsDirty = true; });
|
||||
addProperty(_size);
|
||||
|
||||
if (p.labels.has_value()) {
|
||||
_drawLabels = p.drawLabels.value_or(_drawLabels);
|
||||
addProperty(_drawLabels);
|
||||
|
||||
_labels = std::make_unique<LabelsComponent>(*p.labels);
|
||||
_hasLabels = true;
|
||||
addPropertySubOwner(_labels.get());
|
||||
// Fading of the labels should also depend on the fading of the renderable
|
||||
_labels->setParentFadeable(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableGrid::isReady() const {
|
||||
return _hasLabels ? _gridProgram && _labels->isReady() : _gridProgram != nullptr;
|
||||
return _gridProgram && (_hasLabels ? _labels->isReady() : true);
|
||||
}
|
||||
|
||||
void RenderableGrid::initialize() {
|
||||
if (_hasLabels) {
|
||||
_labels->initialize();
|
||||
_labels->loadLabels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,15 +242,12 @@ void RenderableGrid::deinitializeGL() {
|
||||
_gridProgram = nullptr;
|
||||
}
|
||||
|
||||
void RenderableGrid::render(const RenderData& data, RendererTasks&){
|
||||
void RenderableGrid::render(const RenderData& data, RendererTasks&) {
|
||||
_gridProgram->activate();
|
||||
|
||||
const glm::dmat4 modelMatrix =
|
||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation
|
||||
glm::dmat4(data.modelTransform.rotation) * // Spice rotation
|
||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
||||
const glm::dmat4 modelTransform = calcModelTransform(data);
|
||||
const glm::dmat4 modelViewTransform = calcModelViewTransform(data, modelTransform);
|
||||
|
||||
const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelMatrix;
|
||||
const glm::dmat4 projectionMatrix = data.camera.projectionMatrix();
|
||||
|
||||
const glm::dmat4 modelViewProjectionMatrix = projectionMatrix * modelViewTransform;
|
||||
@@ -262,13 +257,13 @@ void RenderableGrid::render(const RenderData& data, RendererTasks&){
|
||||
glm::vec3 right = glm::cross(viewDirection, lookup);
|
||||
const glm::vec3 up = glm::cross(right, viewDirection);
|
||||
|
||||
const glm::dmat4 worldToModelTransform = glm::inverse(modelMatrix);
|
||||
const glm::mat4 worldToModelTransform = glm::inverse(modelTransform);
|
||||
glm::vec3 orthoRight = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
|
||||
);
|
||||
|
||||
if (orthoRight == glm::vec3(0.0)) {
|
||||
glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
|
||||
const glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
|
||||
right = glm::cross(viewDirection, otherVector);
|
||||
orthoRight = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
|
||||
@@ -283,9 +278,9 @@ void RenderableGrid::render(const RenderData& data, RendererTasks&){
|
||||
// Change GL state:
|
||||
#ifndef __APPLE__
|
||||
glLineWidth(_lineWidth);
|
||||
#else
|
||||
#else // ^^^^ __APPLE__ // !__APPLE__ vvvv
|
||||
glLineWidth(1.f);
|
||||
#endif
|
||||
#endif // __APPLE__
|
||||
glEnablei(GL_BLEND, 0);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
@@ -298,9 +293,9 @@ void RenderableGrid::render(const RenderData& data, RendererTasks&){
|
||||
// Render major grid
|
||||
#ifndef __APPLE__
|
||||
glLineWidth(_highlightLineWidth);
|
||||
#else
|
||||
#else // ^^^^ __APPLE__ // !__APPLE__ vvvv
|
||||
glLineWidth(1.f);
|
||||
#endif
|
||||
#endif // __APPLE__
|
||||
_gridProgram->setUniform("gridColor", _highlightColor);
|
||||
|
||||
glBindVertexArray(_highlightVaoID);
|
||||
@@ -314,16 +309,16 @@ void RenderableGrid::render(const RenderData& data, RendererTasks&){
|
||||
global::renderEngine->openglStateCache().resetDepthState();
|
||||
|
||||
// Draw labels
|
||||
if (_drawLabels && _hasLabels) {
|
||||
if (_hasLabels && _labels->enabled()) {
|
||||
const glm::vec3 orthoUp = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::dvec4(up, 0.0))
|
||||
);
|
||||
_labels->render(data, modelViewProjectionMatrix, orthoRight, orthoUp);
|
||||
_labels->render(data, modelViewProjectionMatrix, orthoRight, orthoUp);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableGrid::update(const UpdateData&) {
|
||||
if (!_gridIsDirty) {
|
||||
if (!_gridIsDirty) [[likely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -342,8 +337,8 @@ void RenderableGrid::update(const UpdateData&) {
|
||||
|
||||
// If the number of segments are uneven the center won't be completly centered
|
||||
const glm::uvec2 center = glm::uvec2(nSegments.x / 2.f, nSegments.y / 2.f);
|
||||
for (unsigned int i = 0; i < nSegments.x; ++i) {
|
||||
for (unsigned int j = 0; j < nSegments.y; ++j) {
|
||||
for (unsigned int i = 0; i < nSegments.x; i++) {
|
||||
for (unsigned int j = 0; j < nSegments.y; j++) {
|
||||
const double y0 = -halfSize.y + j * step.y;
|
||||
const double y1 = y0 + step.y;
|
||||
|
||||
@@ -353,8 +348,8 @@ void RenderableGrid::update(const UpdateData&) {
|
||||
// Line in y direction
|
||||
bool shouldHighlight = false;
|
||||
if (_highlightRate.value().x != 0) {
|
||||
int dist = abs(static_cast<int>(i) - static_cast<int>(center.x));
|
||||
int rest = dist % _highlightRate.value().x;
|
||||
const int dist = abs(static_cast<int>(i) - static_cast<int>(center.x));
|
||||
const int rest = dist % _highlightRate.value().x;
|
||||
shouldHighlight = rest == 0;
|
||||
}
|
||||
|
||||
@@ -370,8 +365,8 @@ void RenderableGrid::update(const UpdateData&) {
|
||||
// Line in x direction
|
||||
shouldHighlight = false;
|
||||
if (_highlightRate.value().y != 0) {
|
||||
int dist = abs(static_cast<int>(j) - static_cast<int>(center.y));
|
||||
int rest = dist % _highlightRate.value().y;
|
||||
const int dist = abs(static_cast<int>(j) - static_cast<int>(center.y));
|
||||
const int rest = dist % _highlightRate.value().y;
|
||||
shouldHighlight = abs(rest) == 0;
|
||||
}
|
||||
|
||||
@@ -387,15 +382,17 @@ void RenderableGrid::update(const UpdateData&) {
|
||||
}
|
||||
|
||||
// last x row
|
||||
for (unsigned int i = 0; i < nSegments.x; ++i) {
|
||||
for (unsigned int i = 0; i < nSegments.x; i++) {
|
||||
const double x0 = -halfSize.x + i * step.x;
|
||||
const double x1 = x0 + step.x;
|
||||
|
||||
bool shouldHighlight = false;
|
||||
if (_highlightRate.value().y != 0) {
|
||||
int dist = abs(static_cast<int>(nSegments.y) - static_cast<int>(center.y));
|
||||
int rest = dist % _highlightRate.value().y;
|
||||
shouldHighlight = abs(rest) == 0;
|
||||
const int dist = std::abs(
|
||||
static_cast<int>(nSegments.y) - static_cast<int>(center.y)
|
||||
);
|
||||
const int rest = dist % _highlightRate.value().y;
|
||||
shouldHighlight = std::abs(rest) == 0;
|
||||
}
|
||||
|
||||
if (shouldHighlight) {
|
||||
@@ -409,15 +406,17 @@ void RenderableGrid::update(const UpdateData&) {
|
||||
}
|
||||
|
||||
// last y col
|
||||
for (unsigned int j = 0; j < nSegments.y; ++j) {
|
||||
for (unsigned int j = 0; j < nSegments.y; j++) {
|
||||
const double y0 = -halfSize.y + j * step.y;
|
||||
const double y1 = y0 + step.y;
|
||||
|
||||
bool shouldHighlight = false;
|
||||
if (_highlightRate.value().x != 0) {
|
||||
int dist = abs(static_cast<int>(nSegments.x) - static_cast<int>(center.x));
|
||||
int rest = dist % _highlightRate.value().x;
|
||||
shouldHighlight = abs(rest) == 0;
|
||||
const int dist = std::abs(
|
||||
static_cast<int>(nSegments.x) - static_cast<int>(center.x)
|
||||
);
|
||||
const int rest = dist % _highlightRate.value().x;
|
||||
shouldHighlight = std::abs(rest) == 0;
|
||||
}
|
||||
if (shouldHighlight) {
|
||||
_highlightArray.push_back({ halfSize.x, y0, 0.0 });
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,12 +27,12 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <modules/space/labelscomponent.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/vector/ivec2property.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/vector/ivec2property.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <openspace/rendering/labelscomponent.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
|
||||
namespace ghoul::opengl { class ProgramObject; }
|
||||
@@ -42,7 +42,7 @@ namespace openspace {
|
||||
|
||||
class RenderableGrid : public Renderable {
|
||||
public:
|
||||
RenderableGrid(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableGrid(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initialize() override;
|
||||
void initializeGL() override;
|
||||
@@ -85,7 +85,6 @@ protected:
|
||||
|
||||
// Labels
|
||||
bool _hasLabels = false;
|
||||
properties::BoolProperty _drawLabels;
|
||||
std::unique_ptr<LabelsComponent> _labels;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -40,27 +40,30 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
||||
"Color",
|
||||
"Color",
|
||||
"This value determines the color of the grid lines that are rendered"
|
||||
"The color used for the grid lines.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo GridSegmentsInfo = {
|
||||
"GridSegments",
|
||||
"Number of Grid Segments",
|
||||
"Specifies the number of segments for the grid, in the radial and angular "
|
||||
"direction respectively"
|
||||
"direction respectively.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo CircleSegmentsInfo = {
|
||||
"CircleSegments",
|
||||
"Number of Circle Segments",
|
||||
"This value specifies the number of segments that is used to render each circle "
|
||||
"in the grid"
|
||||
"The number of segments that is used to render each circle in the grid.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
||||
"LineWidth",
|
||||
"Line Width",
|
||||
"This value specifies the line width of the spherical grid"
|
||||
"The width of the grid lines. The larger number, the thicker the lines.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RadiiInfo = {
|
||||
@@ -68,22 +71,22 @@ namespace {
|
||||
"Inner and Outer Radius",
|
||||
"The radii values that determine the size of the circular grid. The first value "
|
||||
"is the radius of the inmost ring and the second is the radius of the outmost "
|
||||
"ring"
|
||||
"ring.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DrawLabelInfo = {
|
||||
"DrawLabels",
|
||||
"Draw Labels",
|
||||
"Determines whether labels should be drawn or hidden"
|
||||
};
|
||||
|
||||
static const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo =
|
||||
{
|
||||
const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo = {
|
||||
"Labels",
|
||||
"Labels",
|
||||
"The labels for the grid"
|
||||
"The labels for the grid."
|
||||
};
|
||||
|
||||
// This `Renderable` creates a planar circular grid with a given size. Optionally, it
|
||||
// may have a hole in the center.
|
||||
//
|
||||
// The size is determined by two radii values: The first (inner) radius defines the
|
||||
// hole in the center. The second (outer) radius defines the full grid size. To create
|
||||
// a solid circle that connects at the center, set the inner radius to zero (default).
|
||||
struct [[codegen::Dictionary(RenderableRadialGrid)]] Parameters {
|
||||
// [[codegen::verbatim(ColorInfo.description)]]
|
||||
std::optional<glm::vec3> color [[codegen::color()]];
|
||||
@@ -100,12 +103,9 @@ namespace {
|
||||
// [[codegen::verbatim(RadiiInfo.description)]]
|
||||
std::optional<glm::vec2> radii;
|
||||
|
||||
// [[codegen::verbatim(DrawLabelInfo.description)]]
|
||||
std::optional<bool> drawLabels;
|
||||
|
||||
// [[codegen::verbatim(LabelsInfo.description)]]
|
||||
std::optional<ghoul::Dictionary> labels
|
||||
[[codegen::reference("space_labelscomponent")]];
|
||||
[[codegen::reference("labelscomponent")]];
|
||||
};
|
||||
#include "renderableradialgrid_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -119,27 +119,25 @@ documentation::Documentation RenderableRadialGrid::Documentation() {
|
||||
RenderableRadialGrid::RenderableRadialGrid(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary)
|
||||
, _color(ColorInfo, glm::vec3(0.5f), glm::vec3(0.f), glm::vec3(1.f))
|
||||
, _gridSegments(GridSegmentsInfo, glm::ivec2(1), glm::ivec2(1), glm::ivec2(200))
|
||||
, _gridSegments(GridSegmentsInfo, glm::ivec2(10), glm::ivec2(1), glm::ivec2(200))
|
||||
, _circleSegments(CircleSegmentsInfo, 36, 4, 200)
|
||||
, _lineWidth(LineWidthInfo, 0.5f, 1.f, 20.f)
|
||||
, _radii(RadiiInfo, glm::vec2(0.f, 1.f), glm::vec2(0.f), glm::vec2(20.f))
|
||||
, _drawLabels(DrawLabelInfo, false)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(_opacity);
|
||||
registerUpdateRenderBinFromOpacity();
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_color = p.color.value_or(_color);
|
||||
_color.setViewOption(properties::Property::ViewOptions::Color);
|
||||
addProperty(_color);
|
||||
|
||||
_gridSegments = p.gridSegments.value_or(_gridSegments);
|
||||
_gridSegments.onChange([&]() { _gridIsDirty = true; });
|
||||
_gridSegments.onChange([this]() { _gridIsDirty = true; });
|
||||
addProperty(_gridSegments);
|
||||
|
||||
_circleSegments = p.circleSegments.value_or(_circleSegments);
|
||||
_circleSegments.onChange([&]() {
|
||||
_circleSegments.onChange([this]() {
|
||||
if (_circleSegments.value() % 2 == 1) {
|
||||
_circleSegments = _circleSegments - 1;
|
||||
}
|
||||
@@ -152,28 +150,26 @@ RenderableRadialGrid::RenderableRadialGrid(const ghoul::Dictionary& dictionary)
|
||||
|
||||
_radii = p.radii.value_or(_radii);
|
||||
_radii.setViewOption(properties::Property::ViewOptions::MinMaxRange);
|
||||
_radii.onChange([&]() { _gridIsDirty = true; });
|
||||
_radii.onChange([this]() { _gridIsDirty = true; });
|
||||
|
||||
addProperty(_radii);
|
||||
|
||||
if (p.labels.has_value()) {
|
||||
_drawLabels = p.drawLabels.value_or(_drawLabels);
|
||||
addProperty(_drawLabels);
|
||||
|
||||
_labels = std::make_unique<LabelsComponent>(*p.labels);
|
||||
_hasLabels = true;
|
||||
addPropertySubOwner(_labels.get());
|
||||
// Fading of the labels should also depend on the fading of the renderable
|
||||
_labels->setParentFadeable(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableRadialGrid::isReady() const {
|
||||
return _hasLabels ? _gridProgram && _labels->isReady() : _gridProgram != nullptr;
|
||||
return _gridProgram && (_hasLabels ? _labels->isReady() : true);
|
||||
}
|
||||
|
||||
void RenderableRadialGrid::initialize() {
|
||||
if (_hasLabels) {
|
||||
_labels->initialize();
|
||||
_labels->loadLabels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,19 +199,11 @@ void RenderableRadialGrid::deinitializeGL() {
|
||||
void RenderableRadialGrid::render(const RenderData& data, RendererTasks&) {
|
||||
_gridProgram->activate();
|
||||
|
||||
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(1.0), glm::dvec3(data.modelTransform.scale));
|
||||
|
||||
const glm::dmat4 modelViewTransform =
|
||||
data.camera.combinedViewMatrix() * modelTransform;
|
||||
const glm::dmat4 projectionMatrix = data.camera.projectionMatrix();
|
||||
|
||||
const glm::dmat4 modelViewProjectionMatrix = projectionMatrix * modelViewTransform;
|
||||
auto [modelTransform, modelViewTransform, modelViewProjectionTransform] =
|
||||
calcAllTransforms(data);
|
||||
|
||||
_gridProgram->setUniform("modelViewTransform", modelViewTransform);
|
||||
_gridProgram->setUniform("MVPTransform", modelViewProjectionMatrix);
|
||||
_gridProgram->setUniform("MVPTransform", modelViewProjectionTransform);
|
||||
_gridProgram->setUniform("opacity", opacity());
|
||||
_gridProgram->setUniform("gridColor", _color);
|
||||
|
||||
@@ -230,7 +218,7 @@ void RenderableRadialGrid::render(const RenderData& data, RendererTasks&) {
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glDepthMask(false);
|
||||
|
||||
for (GeometryData& c : _circles) {
|
||||
for (const GeometryData& c : _circles) {
|
||||
c.render();
|
||||
}
|
||||
|
||||
@@ -244,7 +232,7 @@ void RenderableRadialGrid::render(const RenderData& data, RendererTasks&) {
|
||||
global::renderEngine->openglStateCache().resetDepthState();
|
||||
|
||||
// Draw labels
|
||||
if (_drawLabels && _hasLabels) {
|
||||
if (_hasLabels && _labels->enabled()) {
|
||||
const glm::vec3 lookup = data.camera.lookUpVectorWorldSpace();
|
||||
const glm::vec3 viewDirection = data.camera.viewDirectionWorldSpace();
|
||||
glm::vec3 right = glm::cross(viewDirection, lookup);
|
||||
@@ -256,7 +244,7 @@ void RenderableRadialGrid::render(const RenderData& data, RendererTasks&) {
|
||||
);
|
||||
|
||||
if (orthoRight == glm::vec3(0.0)) {
|
||||
glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
|
||||
const glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
|
||||
right = glm::cross(viewDirection, otherVector);
|
||||
orthoRight = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
|
||||
@@ -265,12 +253,12 @@ void RenderableRadialGrid::render(const RenderData& data, RendererTasks&) {
|
||||
const glm::vec3 orthoUp = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::dvec4(up, 0.0))
|
||||
);
|
||||
_labels->render(data, modelViewProjectionMatrix, orthoRight, orthoUp);
|
||||
_labels->render(data, modelViewProjectionTransform, orthoRight, orthoUp);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableRadialGrid::update(const UpdateData&) {
|
||||
if (!_gridIsDirty) {
|
||||
if (!_gridIsDirty) [[likely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -292,8 +280,8 @@ void RenderableRadialGrid::update(const UpdateData&) {
|
||||
std::vector<rendering::helper::Vertex> vertices =
|
||||
rendering::helper::createRing(nSegments, radius);
|
||||
|
||||
_circles.push_back(GeometryData(GL_LINE_STRIP));
|
||||
_circles.back().varray = rendering::helper::convert(vertices);
|
||||
_circles.emplace_back(GL_LINE_STRIP);
|
||||
_circles.back().varray = rendering::helper::convert(std::move(vertices));
|
||||
_circles.back().update();
|
||||
};
|
||||
|
||||
@@ -302,7 +290,7 @@ void RenderableRadialGrid::update(const UpdateData&) {
|
||||
addRing(_circleSegments, innerRadius);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nRadialSegments; ++i) {
|
||||
for (int i = 0; i < nRadialSegments; i++) {
|
||||
const float ri = static_cast<float>(i + 1) * deltaRadius + innerRadius;
|
||||
addRing(_circleSegments, ri);
|
||||
}
|
||||
@@ -321,7 +309,7 @@ void RenderableRadialGrid::update(const UpdateData&) {
|
||||
std::vector<rendering::helper::Vertex> innerVertices =
|
||||
rendering::helper::createRing(nLines, innerRadius);
|
||||
|
||||
for (int i = 0; i < nLines; ++i) {
|
||||
for (int i = 0; i < nLines; i++) {
|
||||
const rendering::helper::VertexXYZ vOut =
|
||||
rendering::helper::convertToXYZ(outerVertices[i]);
|
||||
|
||||
@@ -352,7 +340,10 @@ RenderableRadialGrid::GeometryData::GeometryData(GLenum renderMode)
|
||||
}
|
||||
|
||||
RenderableRadialGrid::GeometryData::GeometryData(GeometryData&& other) noexcept {
|
||||
if (this == &other) return;
|
||||
if (this == &other) {
|
||||
return;
|
||||
}
|
||||
|
||||
vao = other.vao;
|
||||
vbo = other.vbo;
|
||||
varray = std::move(other.varray);
|
||||
@@ -385,7 +376,7 @@ RenderableRadialGrid::GeometryData::~GeometryData() {
|
||||
vbo = 0;
|
||||
}
|
||||
|
||||
void RenderableRadialGrid::GeometryData::update() {
|
||||
void RenderableRadialGrid::GeometryData::update() const {
|
||||
glBindVertexArray(vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(
|
||||
@@ -405,7 +396,7 @@ void RenderableRadialGrid::GeometryData::update() {
|
||||
);
|
||||
}
|
||||
|
||||
void RenderableRadialGrid::GeometryData::render() {
|
||||
void RenderableRadialGrid::GeometryData::render() const {
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(mode, 0, static_cast<GLsizei>(varray.size()));
|
||||
glBindVertexArray(0);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <modules/space/labelscomponent.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
#include <openspace/properties/vector/ivec2property.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <openspace/rendering/helper.h>
|
||||
#include <openspace/rendering/labelscomponent.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
|
||||
namespace ghoul::opengl { class ProgramObject; }
|
||||
@@ -44,7 +44,7 @@ namespace openspace {
|
||||
|
||||
class RenderableRadialGrid : public Renderable {
|
||||
public:
|
||||
RenderableRadialGrid(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableRadialGrid(const ghoul::Dictionary& dictionary);
|
||||
~RenderableRadialGrid() override = default;
|
||||
|
||||
void initialize() override;
|
||||
@@ -60,14 +60,14 @@ public:
|
||||
|
||||
protected:
|
||||
struct GeometryData {
|
||||
GeometryData(GLenum renderMode);
|
||||
explicit GeometryData(GLenum renderMode);
|
||||
GeometryData(GeometryData&& other) noexcept;
|
||||
GeometryData& operator=(const GeometryData& other) = delete;
|
||||
GeometryData& operator=(GeometryData&& other) noexcept;
|
||||
~GeometryData();
|
||||
|
||||
void update();
|
||||
void render();
|
||||
void update() const;
|
||||
void render() const;
|
||||
|
||||
std::vector<rendering::helper::VertexXYZ> varray;
|
||||
GLuint vao = 0;
|
||||
@@ -90,7 +90,6 @@ protected:
|
||||
|
||||
// Labels
|
||||
bool _hasLabels = false;
|
||||
properties::BoolProperty _drawLabels;
|
||||
std::unique_ptr<LabelsComponent> _labels;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -39,51 +39,76 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
||||
"Color",
|
||||
"Color",
|
||||
"This value determines the color of the grid lines that are rendered"
|
||||
"The color of the grid lines.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
|
||||
"Segments",
|
||||
"Number of Segments",
|
||||
"This value specifies the number of segments that are used to render the "
|
||||
"surrounding sphere"
|
||||
constexpr openspace::properties::Property::PropertyInfo LongSegmentsInfo = {
|
||||
"LongSegments",
|
||||
"Number of Longitudinal Segments",
|
||||
"The number of longitudinal segments the sphere is split into. Determines the "
|
||||
"resolution of the rendered sphere in a left/right direction when looking "
|
||||
"straight at the equator. Should be an even value (if an odd value is provided, "
|
||||
"the value will be set to the new value minus one). If the `Segments` value is "
|
||||
"provided as well, it will have precedence over this value",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LatSegmentsInfo = {
|
||||
"LatSegments",
|
||||
"Number of Latitudinal Segments",
|
||||
"The number of latitudinal segments the sphere is split into. Determines the "
|
||||
"resolution of the rendered sphere in a up/down direction when looking "
|
||||
"straight at the equator. Should be an even value (if an odd value is provided, "
|
||||
"the value will be set to the new value minus one). If the `Segments` value is "
|
||||
"provided as well, it will have precedence over this value",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
||||
"LineWidth",
|
||||
"Line Width",
|
||||
"This value specifies the line width of the spherical grid"
|
||||
"The width of the grid lines. The larger number, the thicker the lines.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DrawLabelInfo = {
|
||||
"DrawLabels",
|
||||
"Draw Labels",
|
||||
"Determines whether labels should be drawn or hidden"
|
||||
};
|
||||
|
||||
static const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo =
|
||||
{
|
||||
const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo = {
|
||||
"Labels",
|
||||
"Labels",
|
||||
"The labels for the grid"
|
||||
"The labels for the grid."
|
||||
};
|
||||
|
||||
// This `Renderable` creates a grid in the shape of a sphere. Note that the sphere
|
||||
// will always be given a radius of one meter. To change its size, use a `Scale`
|
||||
// transform, such as the [StaticScale](#base_transform_scale_static).
|
||||
//
|
||||
// The grid may be split up into equal segments in both directions using the `Segments`
|
||||
// parameter, or different number of segments in the latitudal and longtudal direction
|
||||
// using the `LatSegments` and `LongSegments` parameters.
|
||||
struct [[codegen::Dictionary(RenderableSphericalGrid)]] Parameters {
|
||||
// [[codegen::verbatim(ColorInfo.description)]]
|
||||
std::optional<glm::vec3> color [[codegen::color()]];
|
||||
|
||||
// [[codegen::verbatim(SegmentsInfo.description)]]
|
||||
// [[codegen::verbatim(LongSegmentsInfo.description)]]
|
||||
std::optional<int> longSegments;
|
||||
|
||||
// [[codegen::verbatim(LatSegmentsInfo.description)]]
|
||||
std::optional<int> latSegments;
|
||||
|
||||
// The number of segments the sphere is split into. Determines the resolution
|
||||
// of the rendered sphere. Should be an even value (if an odd value is provided,
|
||||
// the value will be set to the new value minus one). Setting this value is equal
|
||||
// to setting `LongSegments` and `LatSegments` to the same value. If this value is
|
||||
// specified, it will overwrite the values provided in `LongSegments` and
|
||||
//`LatSegments`.
|
||||
std::optional<int> segments;
|
||||
|
||||
// [[codegen::verbatim(LineWidthInfo.description)]]
|
||||
std::optional<float> lineWidth;
|
||||
|
||||
// [[codegen::verbatim(DrawLabelInfo.description)]]
|
||||
std::optional<bool> drawLabels;
|
||||
|
||||
// [[codegen::verbatim(LabelsInfo.description)]]
|
||||
std::optional<ghoul::Dictionary> labels
|
||||
[[codegen::reference("space_labelscomponent")]];
|
||||
[[codegen::reference("labelscomponent")]];
|
||||
};
|
||||
#include "renderablesphericalgrid_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -98,27 +123,31 @@ RenderableSphericalGrid::RenderableSphericalGrid(const ghoul::Dictionary& dictio
|
||||
: Renderable(dictionary)
|
||||
, _gridProgram(nullptr)
|
||||
, _color(ColorInfo, glm::vec3(0.5f), glm::vec3(0.f), glm::vec3(1.f))
|
||||
, _segments(SegmentsInfo, 36, 4, 200)
|
||||
, _longSegments(LongSegmentsInfo, 36, 4, 200)
|
||||
, _latSegments(LatSegmentsInfo, 36, 4, 200)
|
||||
, _lineWidth(LineWidthInfo, 0.5f, 1.f, 20.f)
|
||||
, _drawLabels(DrawLabelInfo, false)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(_opacity);
|
||||
registerUpdateRenderBinFromOpacity();
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_color = p.color.value_or(_color);
|
||||
_color.setViewOption(properties::Property::ViewOptions::Color);
|
||||
addProperty(_color);
|
||||
|
||||
_segments = p.segments.value_or(_segments);
|
||||
_segments.onChange([&]() {
|
||||
if (_segments.value() % 2 == 1) {
|
||||
_segments = _segments - 1;
|
||||
auto gridDirty = [this]() {
|
||||
if (_longSegments.value() % 2 == 1) {
|
||||
_longSegments = _longSegments - 1;
|
||||
}
|
||||
_gridIsDirty = true;
|
||||
});
|
||||
addProperty(_segments);
|
||||
};
|
||||
_longSegments = p.segments.value_or(p.longSegments.value_or(_longSegments));
|
||||
_longSegments.onChange(gridDirty);
|
||||
addProperty(_longSegments);
|
||||
|
||||
_latSegments = p.segments.value_or(p.latSegments.value_or(_latSegments));
|
||||
_latSegments.onChange(gridDirty);
|
||||
addProperty(_latSegments);
|
||||
|
||||
_lineWidth = p.lineWidth.value_or(_lineWidth);
|
||||
addProperty(_lineWidth);
|
||||
@@ -127,23 +156,21 @@ RenderableSphericalGrid::RenderableSphericalGrid(const ghoul::Dictionary& dictio
|
||||
setBoundingSphere(1.0);
|
||||
|
||||
if (p.labels.has_value()) {
|
||||
_drawLabels = p.drawLabels.value_or(_drawLabels);
|
||||
addProperty(_drawLabels);
|
||||
|
||||
_labels = std::make_unique<LabelsComponent>(*p.labels);
|
||||
_hasLabels = true;
|
||||
addPropertySubOwner(_labels.get());
|
||||
// Fading of the labels should also depend on the fading of the renderable
|
||||
_labels->setParentFadeable(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableSphericalGrid::isReady() const {
|
||||
return _hasLabels ? _gridProgram && _labels->isReady() : _gridProgram != nullptr;
|
||||
return _gridProgram && (_hasLabels ? _labels->isReady() : true);
|
||||
}
|
||||
|
||||
void RenderableSphericalGrid::initialize() {
|
||||
if (_hasLabels) {
|
||||
_labels->initialize();
|
||||
_labels->loadLabels();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,39 +216,31 @@ void RenderableSphericalGrid::deinitializeGL() {
|
||||
_gridProgram = nullptr;
|
||||
}
|
||||
|
||||
void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&){
|
||||
void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&) {
|
||||
_gridProgram->activate();
|
||||
|
||||
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(1.0), glm::dvec3(data.modelTransform.scale));
|
||||
|
||||
const glm::dmat4 modelViewTransform =
|
||||
data.camera.combinedViewMatrix() * modelTransform;
|
||||
const glm::dmat4 projectionMatrix = data.camera.projectionMatrix();
|
||||
|
||||
const glm::dmat4 modelViewProjectionMatrix = projectionMatrix * modelViewTransform;
|
||||
auto [modelTransform, modelViewTransform, modelViewProjectionTransform] =
|
||||
calcAllTransforms(data);
|
||||
|
||||
_gridProgram->setUniform("modelViewTransform", modelViewTransform);
|
||||
_gridProgram->setUniform("MVPTransform", modelViewProjectionMatrix);
|
||||
_gridProgram->setUniform("MVPTransform", modelViewProjectionTransform);
|
||||
_gridProgram->setUniform("opacity", opacity());
|
||||
_gridProgram->setUniform("gridColor", _color);
|
||||
|
||||
// Change GL state:
|
||||
#ifndef __APPLE__
|
||||
glLineWidth(_lineWidth);
|
||||
#else
|
||||
#else // ^^^^ !__APPLE__ // __APPLE__ vvvv
|
||||
glLineWidth(1.f);
|
||||
#endif
|
||||
#endif // __APPLE__
|
||||
|
||||
glEnablei(GL_BLEND, 0);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glDepthMask(false);
|
||||
|
||||
glBindVertexArray(_vaoID);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
|
||||
glDrawElements(_mode, _isize, GL_UNSIGNED_INT, nullptr);
|
||||
glDrawElements(GL_LINES, 6 * _longSegments * _latSegments, GL_UNSIGNED_INT, nullptr);
|
||||
glBindVertexArray(0);
|
||||
|
||||
_gridProgram->deactivate();
|
||||
@@ -232,7 +251,7 @@ void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&){
|
||||
global::renderEngine->openglStateCache().resetDepthState();
|
||||
|
||||
// Draw labels
|
||||
if (_drawLabels && _hasLabels) {
|
||||
if (_hasLabels && _labels->enabled()) {
|
||||
const glm::vec3 lookup = data.camera.lookUpVectorWorldSpace();
|
||||
const glm::vec3 viewDirection = data.camera.viewDirectionWorldSpace();
|
||||
glm::vec3 right = glm::cross(viewDirection, lookup);
|
||||
@@ -240,105 +259,99 @@ void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&){
|
||||
|
||||
const glm::dmat4 worldToModelTransform = glm::inverse(modelTransform);
|
||||
glm::vec3 orthoRight = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.f))
|
||||
);
|
||||
|
||||
if (orthoRight == glm::vec3(0.0)) {
|
||||
glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
|
||||
if (orthoRight == glm::vec3(0.f)) {
|
||||
const glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
|
||||
right = glm::cross(viewDirection, otherVector);
|
||||
orthoRight = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
|
||||
glm::vec3(worldToModelTransform * glm::vec4(right, 0.f))
|
||||
);
|
||||
}
|
||||
const glm::vec3 orthoUp = glm::normalize(
|
||||
glm::vec3(worldToModelTransform * glm::dvec4(up, 0.0))
|
||||
);
|
||||
_labels->render(data, modelViewProjectionMatrix, orthoRight, orthoUp);
|
||||
_labels->render(data, modelViewProjectionTransform, orthoRight, orthoUp);
|
||||
}
|
||||
|
||||
// Reset
|
||||
global::renderEngine->openglStateCache().resetBlendState();
|
||||
global::renderEngine->openglStateCache().resetLineState();
|
||||
global::renderEngine->openglStateCache().resetDepthState();
|
||||
}
|
||||
|
||||
void RenderableSphericalGrid::update(const UpdateData&) {
|
||||
if (_gridIsDirty) {
|
||||
_isize = 6 * _segments * _segments;
|
||||
_vsize = (_segments + 1) * (_segments + 1);
|
||||
_varray.resize(_vsize);
|
||||
Vertex v = { 0.f, 0.f, 0.f };
|
||||
std::fill(_varray.begin(), _varray.end(), v);
|
||||
_iarray.resize(_isize);
|
||||
std::fill(_iarray.begin(), _iarray.end(), 0);
|
||||
|
||||
int nr = 0;
|
||||
const float fsegments = static_cast<float>(_segments);
|
||||
|
||||
for (int nSegment = 0; nSegment <= _segments; ++nSegment) {
|
||||
// define an extra vertex around the y-axis due to texture mapping
|
||||
for (int j = 0; j <= _segments; j++) {
|
||||
const float fi = static_cast<float>(nSegment);
|
||||
const float fj = static_cast<float>(j);
|
||||
|
||||
// inclination angle (north to south)
|
||||
const float theta = fi * glm::pi<float>() / fsegments * 2.f; // 0 -> PI
|
||||
|
||||
// azimuth angle (east to west)
|
||||
const float phi = fj * glm::pi<float>() * 2.0f / fsegments; // 0 -> 2*PI
|
||||
|
||||
const float x = sin(phi) * sin(theta); //
|
||||
const float y = cos(theta); // up
|
||||
const float z = cos(phi) * sin(theta); //
|
||||
|
||||
glm::vec3 normal = glm::vec3(x, y, z);
|
||||
if (!(x == 0.f && y == 0.f && z == 0.f)) {
|
||||
normal = glm::normalize(normal);
|
||||
}
|
||||
|
||||
glm::vec4 tmp(x, y, z, 1.f);
|
||||
glm::mat4 rot = glm::rotate(
|
||||
glm::mat4(1.f),
|
||||
glm::half_pi<float>(),
|
||||
glm::vec3(1.f, 0.f, 0.f)
|
||||
);
|
||||
tmp = glm::vec4(glm::dmat4(rot) * glm::dvec4(tmp));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
_varray[nr].location[i] = tmp[i];
|
||||
}
|
||||
++nr;
|
||||
}
|
||||
}
|
||||
nr = 0;
|
||||
// define indices for all triangles
|
||||
for (int i = 1; i <= _segments; ++i) {
|
||||
for (int j = 0; j < _segments; ++j) {
|
||||
const int t = _segments + 1;
|
||||
_iarray[nr] = t * (i - 1) + j + 0; ++nr;
|
||||
_iarray[nr] = t * (i + 0) + j + 0; ++nr;
|
||||
_iarray[nr] = t * (i + 0) + j + 1; ++nr;
|
||||
_iarray[nr] = t * (i - 1) + j + 1; ++nr;
|
||||
_iarray[nr] = t * (i - 1) + j + 0; ++nr;
|
||||
}
|
||||
}
|
||||
|
||||
glBindVertexArray(_vaoID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
|
||||
glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
_vsize * sizeof(Vertex),
|
||||
_varray.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
|
||||
glBufferData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
_isize * sizeof(int),
|
||||
_iarray.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
|
||||
_gridIsDirty = false;
|
||||
if (!_gridIsDirty) [[likely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int vertSize = (_longSegments + 1) * (_latSegments + 1);
|
||||
std::vector<Vertex> vert = std::vector<Vertex>(vertSize, { 0.f, 0.f, 0.f });
|
||||
unsigned int idxSize = 6 * _longSegments * _latSegments;
|
||||
std::vector<int> idx = std::vector<int>(idxSize, 0);
|
||||
|
||||
int nr = 0;
|
||||
|
||||
for (int lat = 0; lat <= _latSegments; ++lat) {
|
||||
// define an extra vertex around the y-axis due to texture mapping
|
||||
for (int lng = 0; lng <= _longSegments; lng++) {
|
||||
// inclination angle (north to south)
|
||||
const float theta = lat * glm::pi<float>() / _latSegments * 2.f; // 0 -> PI
|
||||
|
||||
// azimuth angle (east to west)
|
||||
const float phi = lng * 2.f * glm::pi<float>() / _longSegments; // 0 -> 2*PI
|
||||
|
||||
const float x = std::sin(phi) * std::sin(theta); //
|
||||
const float y = std::cos(theta); // up
|
||||
const float z = std::cos(phi) * std::sin(theta); //
|
||||
|
||||
glm::vec3 normal = glm::vec3(x, y, z);
|
||||
if (x != 0.f || y != 0.f || z != 0.f) {
|
||||
normal = glm::normalize(normal);
|
||||
}
|
||||
|
||||
glm::vec4 tmp = glm::vec4(x, y, z, 1.f);
|
||||
const glm::mat4 rot = glm::rotate(
|
||||
glm::mat4(1.f),
|
||||
glm::half_pi<float>(),
|
||||
glm::vec3(1.f, 0.f, 0.f)
|
||||
);
|
||||
tmp = glm::vec4(glm::dmat4(rot) * glm::dvec4(tmp));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vert[nr].location[i] = tmp[i];
|
||||
}
|
||||
++nr;
|
||||
}
|
||||
}
|
||||
|
||||
nr = 0;
|
||||
// define indices for all triangles
|
||||
for (int i = 1; i <= _latSegments; i++) {
|
||||
for (int j = 0; j < _longSegments; j++) {
|
||||
const int t = _longSegments + 1;
|
||||
idx[nr] = t * (i - 1) + j + 0; ++nr;
|
||||
idx[nr] = t * (i + 0) + j + 0; ++nr;
|
||||
idx[nr] = t * (i + 0) + j + 1; ++nr;
|
||||
idx[nr] = t * (i - 1) + j + 1; ++nr;
|
||||
idx[nr] = t * (i - 1) + j + 0; ++nr;
|
||||
}
|
||||
}
|
||||
|
||||
glBindVertexArray(_vaoID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
|
||||
glBufferData(GL_ARRAY_BUFFER, vertSize * sizeof(Vertex), vert.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
|
||||
glBufferData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
idxSize * sizeof(int),
|
||||
idx.data(), GL_STATIC_DRAW
|
||||
);
|
||||
|
||||
_gridIsDirty = false;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,21 +27,21 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <modules/space/labelscomponent.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <openspace/rendering/labelscomponent.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
|
||||
namespace ghoul::opengl { class ProgramObject; }
|
||||
|
||||
namespace openspace::documentation { struct Documentation; }
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
class RenderableSphericalGrid : public Renderable {
|
||||
public:
|
||||
RenderableSphericalGrid(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableSphericalGrid(const ghoul::Dictionary& dictionary);
|
||||
~RenderableSphericalGrid() override = default;
|
||||
|
||||
void initialize() override;
|
||||
@@ -63,24 +63,18 @@ protected:
|
||||
ghoul::opengl::ProgramObject* _gridProgram;
|
||||
|
||||
properties::Vec3Property _color;
|
||||
properties::IntProperty _segments;
|
||||
properties::IntProperty _longSegments;
|
||||
properties::IntProperty _latSegments;
|
||||
properties::FloatProperty _lineWidth;
|
||||
|
||||
bool _gridIsDirty = true;
|
||||
|
||||
GLuint _vaoID = 0;
|
||||
GLuint _vBufferID = 0;
|
||||
GLuint _iBufferID = 0;
|
||||
|
||||
GLenum _mode = GL_LINES;
|
||||
unsigned int _isize = 0;
|
||||
unsigned int _vsize = 0;
|
||||
std::vector<Vertex> _varray;
|
||||
std::vector<int> _iarray;
|
||||
bool _gridIsDirty = true;
|
||||
|
||||
// Labels
|
||||
bool _hasLabels = false;
|
||||
properties::BoolProperty _drawLabels;
|
||||
std::unique_ptr<LabelsComponent> _labels;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,588 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/pointcloud/renderableinterpolatedpoints.h>
|
||||
|
||||
#include <modules/base/basemodule.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/interpolator.h>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <optional>
|
||||
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "RenderableInterpolatedPoints";
|
||||
|
||||
void triggerInterpolation(std::string_view identifier, float v, float d) {
|
||||
using namespace openspace;
|
||||
|
||||
std::string script = std::format(
|
||||
"openspace.setPropertyValueSingle(\"{}\", {}, {})",
|
||||
identifier, v, d
|
||||
);
|
||||
// No syncing, as this was triggered from a property change (which happened
|
||||
// based on an already synced script)
|
||||
global::scriptEngine->queueScript({
|
||||
.code = script,
|
||||
.synchronized = scripting::ScriptEngine::Script::ShouldBeSynchronized::No,
|
||||
.sendToRemote = scripting::ScriptEngine::Script::ShouldSendToRemote::No
|
||||
});
|
||||
}
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InterpolationValueInfo = {
|
||||
"Value",
|
||||
"Value",
|
||||
"The value used for interpolation. The max value is set from the number of "
|
||||
"steps in the dataset, so a step of one corresponds to one step in the dataset "
|
||||
"and values in-between will be determined using interpolation.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo StepsInfo = {
|
||||
"NumberOfSteps",
|
||||
"Number of Steps",
|
||||
"The number of steps available in the dataset, including the initial positions.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo JumpToNextInfo = {
|
||||
"JumpToNext",
|
||||
"Jump to Next",
|
||||
"Immediately set the interpolation value to correspond to the next set of point "
|
||||
"positions compared to the current.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo JumpToPrevInfo = {
|
||||
"JumpToPrevious",
|
||||
"Jump to Previous",
|
||||
"Immediately set the interpolation value to correspond to the previous set of "
|
||||
"point positions compared to the current.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InterpolateToNextInfo = {
|
||||
"InterpolateToNext",
|
||||
"Interpolate to Next",
|
||||
"Trigger an interpolation to the next set of point positions. The duration of "
|
||||
"the interpolation is set based on the Interpolaton Speed property.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InterpolateToPrevInfo = {
|
||||
"InterpolateToPrevious",
|
||||
"Interpolate to Previous",
|
||||
"Trigger an interpolation to the previous set of point positions. The duration "
|
||||
"of the interpolation is set based on the Interpolaton Speed property.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InterpolateToEndInfo = {
|
||||
"InterpolateToEnd",
|
||||
"Interpolate to End",
|
||||
"Trigger an interpolation all the way to the final set of positions. The "
|
||||
"duration of the interpolation is set based on the Interpolaton Speed property.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InterpolateToStartInfo = {
|
||||
"InterpolateToStart",
|
||||
"Interpolate to Start",
|
||||
"Trigger an inverted interpolation to the initial set of positions. The duration "
|
||||
"of the interpolation is set based on the Interpolaton Speed property.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InterpolationSpeedInfo = {
|
||||
"Speed",
|
||||
"Interpolation Speed",
|
||||
"Affects how long the interpolation takes when triggered using one of the "
|
||||
"trigger properties. A value of 1 means that a step takes 1 second.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo UseSplineInfo = {
|
||||
"UseSplineInterpolation",
|
||||
"Use Spline Interpolation",
|
||||
"If true, the points will be interpolated using a Catmull-Rom spline instead of "
|
||||
"linearly. This leads to a smoother transition at the breakpoints, i.e. between "
|
||||
"each step.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
// RenderableInterpolatedPoints is a version of the RenderablePointCloud class, where
|
||||
// the dataset may contain multiple time steps that can be interpolated between. It
|
||||
// supports interpolation of both of positions and data values used for color mapping
|
||||
// or size.
|
||||
//
|
||||
// The dataset should be structured in a way so that the first N rows correspond to
|
||||
// the first set of positions for the objects, the next N rows to the second set of
|
||||
// positions, and so on. The number of objects in the dataset must be specified in the
|
||||
// asset.
|
||||
//
|
||||
// MultiTexture:
|
||||
// Note that if using multiple textures for the points based on values in the dataset,
|
||||
// the used texture will be decided based on the first N set of points.
|
||||
struct [[codegen::Dictionary(RenderableInterpolatedPoints)]] Parameters {
|
||||
// The number of objects to read from the dataset. Every N:th datapoint will
|
||||
// be interpreted as the same point, but at a different step in the interpolation.
|
||||
int numberOfObjects [[codegen::greaterequal(1)]];
|
||||
|
||||
struct Interpolation {
|
||||
// [[codegen::verbatim(InterpolationValueInfo.description)]]
|
||||
std::optional<float> value;
|
||||
|
||||
// [[codegen::verbatim(InterpolationSpeedInfo.description)]]
|
||||
std::optional<float> speed;
|
||||
|
||||
// [[codegen::verbatim(UseSplineInfo.description)]]
|
||||
std::optional<bool> useSplineInterpolation;
|
||||
};
|
||||
// Initial settings for the interpolation.
|
||||
std::optional<Interpolation> interpolation;
|
||||
};
|
||||
|
||||
#include "renderableinterpolatedpoints_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation RenderableInterpolatedPoints::Documentation() {
|
||||
return codegen::doc<Parameters>(
|
||||
"base_renderableinterpolatedpoints",
|
||||
RenderablePointCloud::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
RenderableInterpolatedPoints::Interpolation::Interpolation()
|
||||
: properties::PropertyOwner({ "Interpolation", "Interpolation", "" })
|
||||
, value(InterpolationValueInfo, 0.f, 0.f, 1.f)
|
||||
, nSteps(StepsInfo, 1)
|
||||
, goToNextStep(JumpToNextInfo)
|
||||
, goToPrevStep(JumpToPrevInfo)
|
||||
, interpolateToNextStep(InterpolateToNextInfo)
|
||||
, interpolateToPrevStep(InterpolateToPrevInfo)
|
||||
, interpolateToEnd(InterpolateToEndInfo)
|
||||
, interpolateToStart(InterpolateToStartInfo)
|
||||
, speed(InterpolationSpeedInfo, 1.f, 0.01f, 100.f)
|
||||
, useSpline(UseSplineInfo, false)
|
||||
{
|
||||
addProperty(value);
|
||||
|
||||
interpolateToEnd.onChange([this]() {
|
||||
const float remaining = value.maxValue() - value;
|
||||
const float duration = remaining / speed;
|
||||
triggerInterpolation(
|
||||
value.uri(),
|
||||
value.maxValue(),
|
||||
duration
|
||||
);
|
||||
});
|
||||
addProperty(interpolateToEnd);
|
||||
|
||||
interpolateToStart.onChange([this]() {
|
||||
const float duration = value / speed;
|
||||
triggerInterpolation(value.uri(), 0.f, duration);
|
||||
});
|
||||
addProperty(interpolateToStart);
|
||||
|
||||
interpolateToNextStep.onChange([this]() {
|
||||
const float prevValue = glm::floor(value);
|
||||
const float newValue = glm::min(prevValue + 1.f, value.maxValue());
|
||||
const float duration = 1.f / speed;
|
||||
triggerInterpolation(value.uri(), newValue, duration);
|
||||
});
|
||||
addProperty(interpolateToNextStep);
|
||||
|
||||
interpolateToPrevStep.onChange([this]() {
|
||||
const float prevValue = glm::ceil(value);
|
||||
const float newValue = glm::max(prevValue - 1.f, value.minValue());
|
||||
const float duration = 1.f / speed;
|
||||
triggerInterpolation(value.uri(), newValue, duration);
|
||||
});
|
||||
addProperty(interpolateToPrevStep);
|
||||
|
||||
addProperty(speed);
|
||||
|
||||
goToNextStep.onChange([this]() {
|
||||
float prevValue = glm::floor(value);
|
||||
value = glm::min(prevValue + 1.f, value.maxValue());
|
||||
});
|
||||
addProperty(goToNextStep);
|
||||
|
||||
goToPrevStep.onChange([this]() {
|
||||
float prevValue = glm::ceil(value);
|
||||
value = glm::max(prevValue - 1.f, value.minValue());
|
||||
});
|
||||
addProperty(goToPrevStep);
|
||||
|
||||
nSteps.setReadOnly(true);
|
||||
addProperty(nSteps);
|
||||
|
||||
addProperty(useSpline);
|
||||
}
|
||||
|
||||
RenderableInterpolatedPoints::RenderableInterpolatedPoints(
|
||||
const ghoul::Dictionary& dictionary)
|
||||
: RenderablePointCloud(dictionary)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addPropertySubOwner(_interpolation);
|
||||
|
||||
if (p.interpolation.has_value()) {
|
||||
_interpolation.value = p.interpolation->value.value_or(_interpolation.value);
|
||||
_interpolation.speed = p.interpolation->speed.value_or(_interpolation.speed);
|
||||
_interpolation.useSpline = p.interpolation->useSplineInterpolation.value_or(
|
||||
_interpolation.useSpline
|
||||
);
|
||||
}
|
||||
|
||||
_interpolation.value.onChange([this]() {
|
||||
bool passedAKnot =
|
||||
glm::ceil(_interpolation.value) != glm::ceil(_prevInterpolationValue);
|
||||
|
||||
if (passedAKnot) {
|
||||
_dataIsDirty = true;
|
||||
}
|
||||
_prevInterpolationValue = _interpolation.value;
|
||||
});
|
||||
|
||||
_interpolation.useSpline.onChange([this]() {
|
||||
_dataIsDirty = true;
|
||||
_shouldReinitializeBufferdata = true;
|
||||
});
|
||||
|
||||
_nObjectsInDataset = static_cast<unsigned int>(p.numberOfObjects);
|
||||
|
||||
if (_skipFirstDataPoint) {
|
||||
LWARNING(
|
||||
"Found setting to skip first data point in asset. This is not supported for "
|
||||
"interpolated point clouds. Ignoring"
|
||||
);
|
||||
_skipFirstDataPoint = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::initialize() {
|
||||
RenderablePointCloud::initialize();
|
||||
|
||||
// At this point, the dataset has been loaded and we know how many data points it
|
||||
// contains => we can compute the number of interpolation steps
|
||||
if (_nDataPoints % _nObjectsInDataset != 0) {
|
||||
LERROR(std::format(
|
||||
"Mismatch between provided number of data entries and the specified number "
|
||||
"of points. Expected the number of entries in the data file '{}' to be "
|
||||
"evenly divisible by the number of objects", _dataFile
|
||||
));
|
||||
}
|
||||
|
||||
if (_nObjectsInDataset > 0) {
|
||||
_interpolation.nSteps = _nDataPoints / _nObjectsInDataset;
|
||||
}
|
||||
_interpolation.value.setMaxValue(static_cast<float>(_interpolation.nSteps - 1));
|
||||
|
||||
// This is the property that is shown in the user interface, so update it so the user
|
||||
// can get an idea of how many points will be rendered
|
||||
_nDataPoints = _nObjectsInDataset;
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::initializeShadersAndGlExtras() {
|
||||
_program = BaseModule::ProgramObjectManager.request(
|
||||
"RenderablePointCloud_Interpolated",
|
||||
[]() {
|
||||
std::filesystem::path path = absPath("${MODULE_BASE}/shaders/pointcloud");
|
||||
return global::renderEngine->buildRenderProgram(
|
||||
"RenderablePointCloud_Interpolated",
|
||||
path / "pointcloud_interpolated_vs.glsl",
|
||||
path / "pointcloud_fs.glsl",
|
||||
path / "pointcloud_gs.glsl"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
initializeBufferData();
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::deinitializeShaders() {
|
||||
BaseModule::ProgramObjectManager.release(
|
||||
"RenderablePointCloud_Interpolated",
|
||||
[](ghoul::opengl::ProgramObject* p) {
|
||||
global::renderEngine->removeRenderProgram(p);
|
||||
}
|
||||
);
|
||||
_program = nullptr;
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::setExtraUniforms() {
|
||||
float t0 = computeCurrentLowerValue();
|
||||
float t = glm::clamp(_interpolation.value - t0, 0.f, 1.f);
|
||||
|
||||
_program->setUniform("interpolationValue", t);
|
||||
_program->setUniform("useSpline", useSplineInterpolation());
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::preUpdate() {
|
||||
if (_shouldReinitializeBufferdata) [[unlikely]] {
|
||||
initializeBufferData();
|
||||
_shouldReinitializeBufferdata = false;
|
||||
}
|
||||
}
|
||||
|
||||
int RenderableInterpolatedPoints::nAttributesPerPoint() const {
|
||||
int n = RenderablePointCloud::nAttributesPerPoint();
|
||||
|
||||
// Always at least three extra position values (xyz)
|
||||
n += 3;
|
||||
if (useSplineInterpolation()) {
|
||||
// Use two more positions (xyz)
|
||||
n += 2 * 3;
|
||||
}
|
||||
if (useOrientationData()) {
|
||||
// Use one more orientation quaternion (wxyz)
|
||||
n += 4;
|
||||
}
|
||||
// And potentially some more color and size data
|
||||
n += hasColorData() ? 1 : 0;
|
||||
n += hasSizeData() ? 1 : 0;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
bool RenderableInterpolatedPoints::useSplineInterpolation() const {
|
||||
return _interpolation.useSpline && _interpolation.nSteps > 1;
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::addPositionDataForPoint(unsigned int index,
|
||||
std::vector<float>& result,
|
||||
double& maxRadius) const
|
||||
{
|
||||
auto [firstIndex, secondIndex] = interpolationIndices(index);
|
||||
|
||||
const dataloader::Dataset::Entry& e0 = _dataset.entries[firstIndex];
|
||||
const dataloader::Dataset::Entry& e1 = _dataset.entries[secondIndex];
|
||||
|
||||
glm::dvec3 position0 = transformedPosition(e0);
|
||||
glm::dvec3 position1 = transformedPosition(e1);
|
||||
|
||||
const double r = glm::max(glm::length(position0), glm::length(position1));
|
||||
maxRadius = glm::max(maxRadius, r);
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
result.push_back(static_cast<float>(position0[j]));
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
result.push_back(static_cast<float>(position1[j]));
|
||||
}
|
||||
|
||||
if (useSplineInterpolation()) {
|
||||
// Compute the extra positions, before and after the other ones. But make sure
|
||||
// we do not overflow the allowed bound for the current interpolation step
|
||||
int beforeIndex = glm::max(static_cast<int>(firstIndex - _nDataPoints), 0);
|
||||
int maxT = static_cast<int>(_interpolation.value.maxValue() - 1.f);
|
||||
int maxAllowedindex = maxT * _nDataPoints + index;
|
||||
int afterIndex = glm::min(
|
||||
static_cast<int>(secondIndex + _nDataPoints),
|
||||
maxAllowedindex
|
||||
);
|
||||
|
||||
const dataloader::Dataset::Entry& e00 = _dataset.entries[beforeIndex];
|
||||
const dataloader::Dataset::Entry& e11 = _dataset.entries[afterIndex];
|
||||
glm::dvec3 positionBefore = transformedPosition(e00);
|
||||
glm::dvec3 positionAfter = transformedPosition(e11);
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
result.push_back(static_cast<float>(positionBefore[j]));
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
result.push_back(static_cast<float>(positionAfter[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::addColorAndSizeDataForPoint(unsigned int index,
|
||||
std::vector<float>& result) const
|
||||
{
|
||||
auto [firstIndex, secondIndex] = interpolationIndices(index);
|
||||
const dataloader::Dataset::Entry& e0 = _dataset.entries[firstIndex];
|
||||
const dataloader::Dataset::Entry& e1 = _dataset.entries[secondIndex];
|
||||
|
||||
if (hasColorData()) {
|
||||
const int colorParamIndex = currentColorParameterIndex();
|
||||
result.push_back(e0.data[colorParamIndex]);
|
||||
result.push_back(e1.data[colorParamIndex]);
|
||||
}
|
||||
|
||||
if (hasSizeData()) {
|
||||
const int sizeParamIndex = currentSizeParameterIndex();
|
||||
// @TODO: Consider more detailed control over the scaling. Currently the value
|
||||
// is multiplied with the value as is. Should have similar mapping properties
|
||||
// as the color mapping
|
||||
|
||||
// Convert to diameter if data is given as radius
|
||||
float multiplier = _sizeSettings.sizeMapping->isRadius ? 2.f : 1.f;
|
||||
result.push_back(multiplier * e0.data[sizeParamIndex]);
|
||||
result.push_back(multiplier * e1.data[sizeParamIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::addOrientationDataForPoint(unsigned int index,
|
||||
std::vector<float>& result) const
|
||||
{
|
||||
auto [firstIndex, secondIndex] = interpolationIndices(index);
|
||||
const dataloader::Dataset::Entry& e0 = _dataset.entries[firstIndex];
|
||||
const dataloader::Dataset::Entry& e1 = _dataset.entries[secondIndex];
|
||||
|
||||
glm::quat q0 = orientationQuaternion(e0);
|
||||
glm::quat q1 = orientationQuaternion(e1);
|
||||
|
||||
result.push_back(q0.x);
|
||||
result.push_back(q0.y);
|
||||
result.push_back(q0.z);
|
||||
result.push_back(q0.w);
|
||||
|
||||
result.push_back(q1.x);
|
||||
result.push_back(q1.y);
|
||||
result.push_back(q1.z);
|
||||
result.push_back(q1.w);
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::initializeBufferData() {
|
||||
if (_vao == 0) {
|
||||
glGenVertexArrays(1, &_vao);
|
||||
LDEBUG(std::format("Generating Vertex Array id '{}'", _vao));
|
||||
}
|
||||
if (_vbo == 0) {
|
||||
glGenBuffers(1, &_vbo);
|
||||
LDEBUG(std::format("Generating Vertex Buffer Object id '{}'", _vbo));
|
||||
}
|
||||
|
||||
const int attibsPerPoint = nAttributesPerPoint();
|
||||
const unsigned int bufferSize = attibsPerPoint * _nDataPoints * sizeof(float);
|
||||
|
||||
// Allocate the memory for the buffer (we will want to upload the data quite often)
|
||||
glBindVertexArray(_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, bufferSize, nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
int offset = 0;
|
||||
|
||||
offset = bufferVertexAttribute("in_position0", 3, attibsPerPoint, offset);
|
||||
offset = bufferVertexAttribute("in_position1", 3, attibsPerPoint, offset);
|
||||
|
||||
if (useSplineInterpolation()) {
|
||||
offset = bufferVertexAttribute("in_position_before", 3, attibsPerPoint, offset);
|
||||
offset = bufferVertexAttribute("in_position_after", 3, attibsPerPoint, offset);
|
||||
}
|
||||
|
||||
if (hasColorData()) {
|
||||
offset = bufferVertexAttribute("in_colorParameter0", 1, attibsPerPoint, offset);
|
||||
offset = bufferVertexAttribute("in_colorParameter1", 1, attibsPerPoint, offset);
|
||||
}
|
||||
|
||||
if (hasSizeData()) {
|
||||
offset = bufferVertexAttribute("in_scalingParameter0", 1, attibsPerPoint, offset);
|
||||
offset = bufferVertexAttribute("in_scalingParameter1", 1, attibsPerPoint, offset);
|
||||
}
|
||||
|
||||
if (useOrientationData()) {
|
||||
offset = bufferVertexAttribute("in_orientation0", 4, attibsPerPoint, offset);
|
||||
offset = bufferVertexAttribute("in_orientation1", 4, attibsPerPoint, offset);
|
||||
}
|
||||
|
||||
if (_hasSpriteTexture) {
|
||||
offset = bufferVertexAttribute("in_textureLayer", 1, attibsPerPoint, offset);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void RenderableInterpolatedPoints::updateBufferData() {
|
||||
if (!_hasDataFile || _dataset.entries.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScopedN("Data dirty");
|
||||
TracyGpuZone("Data dirty");
|
||||
LDEBUG("Regenerating data");
|
||||
|
||||
// Regenerate data and update buffer
|
||||
std::vector<float> slice = createDataSlice();
|
||||
|
||||
glBindVertexArray(_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, slice.size() * sizeof(float), slice.data());
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
_dataIsDirty = false;
|
||||
}
|
||||
|
||||
bool RenderableInterpolatedPoints::isAtKnot() const {
|
||||
float v = _interpolation.value;
|
||||
return (v - glm::floor(v)) < std::numeric_limits<float>::epsilon();
|
||||
}
|
||||
|
||||
float RenderableInterpolatedPoints::computeCurrentLowerValue() const {
|
||||
float t0 = glm::floor(_interpolation.value);
|
||||
|
||||
if (isAtKnot()) {
|
||||
t0 = t0 - 1.f;
|
||||
}
|
||||
|
||||
const float maxTValue = _interpolation.value.maxValue();
|
||||
const float maxAllowedT0 = glm::max(maxTValue - 1.f, 0.f);
|
||||
t0 = glm::clamp(t0, 0.f, maxAllowedT0);
|
||||
return t0;
|
||||
}
|
||||
|
||||
float RenderableInterpolatedPoints::computeCurrentUpperValue() const {
|
||||
const float t0 = computeCurrentLowerValue();
|
||||
const float t1 = glm::clamp(t0 + 1.f, 0.f, _interpolation.value.maxValue());
|
||||
return t1;
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t>
|
||||
RenderableInterpolatedPoints::interpolationIndices(unsigned int index) const
|
||||
{
|
||||
const float t0 = computeCurrentLowerValue();
|
||||
const float t1 = computeCurrentUpperValue();
|
||||
const unsigned int t0Index = static_cast<unsigned int>(t0);
|
||||
const unsigned int t1Index = static_cast<unsigned int>(t1);
|
||||
|
||||
const size_t lower = size_t(t0Index * _nDataPoints + index);
|
||||
const size_t upper = size_t(t1Index * _nDataPoints + index);
|
||||
|
||||
return { lower, upper };
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
122
modules/base/rendering/pointcloud/renderableinterpolatedpoints.h
Normal file
122
modules/base/rendering/pointcloud/renderableinterpolatedpoints.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___RENDERABLEINTERPOLATEDPOINTS___H__
|
||||
#define __OPENSPACE_MODULE_BASE___RENDERABLEINTERPOLATEDPOINTS___H__
|
||||
|
||||
#include <modules/base/rendering/pointcloud/renderablepointcloud.h>
|
||||
|
||||
#include <openspace/properties/misc/triggerproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/uintproperty.h>
|
||||
|
||||
namespace ghoul::opengl { class Texture; }
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
/**
|
||||
* A specialization of the RenderablePointCloud that supports interpolation beetween
|
||||
* different positions for the points.
|
||||
*/
|
||||
class RenderableInterpolatedPoints : public RenderablePointCloud {
|
||||
public:
|
||||
explicit RenderableInterpolatedPoints(const ghoul::Dictionary& dictionary);
|
||||
~RenderableInterpolatedPoints() override = default;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
protected:
|
||||
void initialize() override;
|
||||
void initializeShadersAndGlExtras() override;
|
||||
void deinitializeShaders() override;
|
||||
void setExtraUniforms() override;
|
||||
void preUpdate() override;
|
||||
|
||||
int nAttributesPerPoint() const override;
|
||||
|
||||
bool useSplineInterpolation() const;
|
||||
|
||||
/**
|
||||
* Create the rendering data for the positions for the point with the given index
|
||||
* and append that to the result. Compared to the base class, this class may require
|
||||
* 2-4 positions, depending on if spline interpolation is used or not.
|
||||
*
|
||||
* The values are computed based on the current interpolation value.
|
||||
*
|
||||
* Also, compute the maxRadius to use for setting the bounding sphere.
|
||||
*/
|
||||
void addPositionDataForPoint(unsigned int index, std::vector<float>& result,
|
||||
double& maxRadius) const override;
|
||||
|
||||
/**
|
||||
* Create the rendering data for the color and size data for the point with the given
|
||||
* index and append that to the result. Compared to the base class, this class require
|
||||
* 2 values per data value, to use for interpolation.
|
||||
*
|
||||
* The values are computed based on the current interpolation value.
|
||||
*/
|
||||
void addColorAndSizeDataForPoint(unsigned int index,
|
||||
std::vector<float>& result) const override;
|
||||
|
||||
void addOrientationDataForPoint(unsigned int index,
|
||||
std::vector<float>& result) const override;
|
||||
|
||||
void initializeBufferData();
|
||||
void updateBufferData() override;
|
||||
|
||||
private:
|
||||
bool isAtKnot() const;
|
||||
float computeCurrentLowerValue() const;
|
||||
float computeCurrentUpperValue() const;
|
||||
std::pair<size_t, size_t> interpolationIndices(unsigned int index) const;
|
||||
|
||||
struct Interpolation : public properties::PropertyOwner {
|
||||
Interpolation();
|
||||
properties::FloatProperty value;
|
||||
properties::UIntProperty nSteps;
|
||||
|
||||
properties::TriggerProperty goToNextStep;
|
||||
properties::TriggerProperty goToPrevStep;
|
||||
properties::TriggerProperty interpolateToNextStep;
|
||||
properties::TriggerProperty interpolateToPrevStep;
|
||||
properties::TriggerProperty interpolateToEnd;
|
||||
properties::TriggerProperty interpolateToStart;
|
||||
properties::FloatProperty speed;
|
||||
|
||||
properties::BoolProperty useSpline;
|
||||
};
|
||||
Interpolation _interpolation;
|
||||
|
||||
float _prevInterpolationValue = 0.f;
|
||||
bool _shouldReinitializeBufferdata = false;
|
||||
|
||||
unsigned int _nObjectsInDataset = 0;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___RENDERABLEINTERPOLATEDPOINTS___H__
|
||||
1747
modules/base/rendering/pointcloud/renderablepointcloud.cpp
Normal file
1747
modules/base/rendering/pointcloud/renderablepointcloud.cpp
Normal file
File diff suppressed because it is too large
Load Diff
289
modules/base/rendering/pointcloud/renderablepointcloud.h
Normal file
289
modules/base/rendering/pointcloud/renderablepointcloud.h
Normal file
@@ -0,0 +1,289 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___RENDERABLEPOINTCLOUD___H__
|
||||
#define __OPENSPACE_MODULE_BASE___RENDERABLEPOINTCLOUD___H__
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <modules/base/rendering/pointcloud/sizemappingcomponent.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/misc/triggerproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/uintproperty.h>
|
||||
#include <openspace/properties/vector/ivec2property.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <openspace/rendering/colormappingcomponent.h>
|
||||
#include <openspace/rendering/labelscomponent.h>
|
||||
#include <openspace/util/distanceconversion.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/uniformcache.h>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
|
||||
namespace ghoul::opengl {
|
||||
class ProgramObject;
|
||||
class Texture;
|
||||
} // namespace ghoul::opengl
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
struct TextureFormat {
|
||||
glm::uvec2 resolution;
|
||||
bool useAlpha = false;
|
||||
|
||||
friend bool operator==(const TextureFormat& l, const TextureFormat& r);
|
||||
};
|
||||
struct TextureFormatHash {
|
||||
size_t operator()(const TextureFormat& k) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class describes a point cloud renderable that can be used to draw billboraded
|
||||
* points based on a data file with 3D positions. Alternatively the points can also
|
||||
* be colored and sized based on a separate column in the data file.
|
||||
*/
|
||||
class RenderablePointCloud : public Renderable {
|
||||
public:
|
||||
explicit RenderablePointCloud(const ghoul::Dictionary& dictionary);
|
||||
~RenderablePointCloud() override = default;
|
||||
|
||||
void initialize() override;
|
||||
void initializeGL() override;
|
||||
void deinitializeGL() override;
|
||||
|
||||
bool isReady() const override;
|
||||
|
||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
||||
void update(const UpdateData& data) override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
protected:
|
||||
enum class TextureInputMode {
|
||||
Single = 0,
|
||||
Multi,
|
||||
Other // For subclasses that need to handle their own texture
|
||||
};
|
||||
|
||||
virtual void initializeShadersAndGlExtras();
|
||||
virtual void deinitializeShaders();
|
||||
virtual void setExtraUniforms();
|
||||
virtual void preUpdate();
|
||||
|
||||
glm::dvec3 transformedPosition(const dataloader::Dataset::Entry& e) const;
|
||||
glm::quat orientationQuaternion(const dataloader::Dataset::Entry& e) const;
|
||||
|
||||
virtual int nAttributesPerPoint() const;
|
||||
|
||||
/**
|
||||
* Helper function to buffer the vertex attribute with the given name and number
|
||||
* of values. Assumes that the value is a float value.
|
||||
*
|
||||
* Returns the updated offset after this attribute is added
|
||||
*/
|
||||
int bufferVertexAttribute(const std::string& name, GLint nValues,
|
||||
int nAttributesPerPoint, int offset) const;
|
||||
|
||||
virtual void updateBufferData();
|
||||
void updateSpriteTexture();
|
||||
|
||||
/// Find the index of the currently chosen color parameter in the dataset
|
||||
int currentColorParameterIndex() const;
|
||||
/// Find the index of the currently chosen size parameter in the dataset
|
||||
int currentSizeParameterIndex() const;
|
||||
|
||||
bool hasColorData() const;
|
||||
bool hasSizeData() const;
|
||||
bool hasMultiTextureData() const;
|
||||
bool useOrientationData() const;
|
||||
|
||||
virtual void addPositionDataForPoint(unsigned int index, std::vector<float>& result,
|
||||
double& maxRadius) const;
|
||||
virtual void addColorAndSizeDataForPoint(unsigned int index,
|
||||
std::vector<float>& result) const;
|
||||
virtual void addOrientationDataForPoint(unsigned int index,
|
||||
std::vector<float>& result) const;
|
||||
|
||||
std::vector<float> createDataSlice();
|
||||
|
||||
/**
|
||||
* A function that subclasses could override to initialize their own textures to
|
||||
* use for rendering, when the `_textureMode` is set to Other
|
||||
*/
|
||||
virtual void initializeCustomTexture();
|
||||
void initializeSingleTexture();
|
||||
void initializeMultiTextures();
|
||||
void clearTextureDataStructures();
|
||||
|
||||
void loadTexture(const std::filesystem::path& path, int index);
|
||||
|
||||
void initAndAllocateTextureArray(unsigned int textureId,
|
||||
glm::uvec2 resolution, size_t nLayers, bool useAlpha);
|
||||
|
||||
void fillAndUploadTextureLayer(unsigned int arrayindex, unsigned int layer,
|
||||
size_t textureIndex, glm::uvec2 resolution, bool useAlpha, const void* pixelData);
|
||||
|
||||
void generateArrayTextures();
|
||||
|
||||
float computeDistanceFadeValue(const RenderData& data) const;
|
||||
|
||||
void renderPoints(const RenderData& data, const glm::dmat4& modelMatrix,
|
||||
const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable);
|
||||
|
||||
gl::GLenum internalGlFormat(bool useAlpha) const;
|
||||
ghoul::opengl::Texture::Format glFormat(bool useAlpha) const;
|
||||
|
||||
bool _dataIsDirty = true;
|
||||
bool _spriteTextureIsDirty = false;
|
||||
bool _cmapIsDirty = true;
|
||||
|
||||
bool _hasSpriteTexture = false;
|
||||
bool _hasDataFile = false;
|
||||
bool _hasColorMapFile = false;
|
||||
bool _hasDatavarSize = false;
|
||||
bool _hasLabels = false;
|
||||
|
||||
struct SizeSettings : properties::PropertyOwner {
|
||||
explicit SizeSettings(const ghoul::Dictionary& dictionary);
|
||||
|
||||
std::unique_ptr<SizeMappingComponent> sizeMapping;
|
||||
|
||||
properties::FloatProperty scaleExponent;
|
||||
properties::FloatProperty scaleFactor;
|
||||
|
||||
properties::BoolProperty useMaxSizeControl;
|
||||
properties::FloatProperty maxAngularSize;
|
||||
};
|
||||
SizeSettings _sizeSettings;
|
||||
|
||||
struct ColorSettings : properties::PropertyOwner {
|
||||
explicit ColorSettings(const ghoul::Dictionary& dictionary);
|
||||
properties::Vec3Property pointColor;
|
||||
std::unique_ptr<ColorMappingComponent> colorMapping;
|
||||
properties::BoolProperty enableOutline;
|
||||
properties::Vec3Property outlineColor;
|
||||
properties::FloatProperty outlineWidth;
|
||||
properties::OptionProperty outlineStyle;
|
||||
properties::BoolProperty applyCmapToOutline;
|
||||
};
|
||||
ColorSettings _colorSettings;
|
||||
|
||||
struct Fading : properties::PropertyOwner {
|
||||
explicit Fading(const ghoul::Dictionary& dictionary);
|
||||
properties::Vec2Property fadeInDistances;
|
||||
properties::BoolProperty enabled;
|
||||
properties::BoolProperty invert;
|
||||
};
|
||||
Fading _fading;
|
||||
|
||||
properties::BoolProperty _useAdditiveBlending;
|
||||
properties::BoolProperty _useRotation;
|
||||
|
||||
properties::BoolProperty _drawElements;
|
||||
properties::OptionProperty _renderOption;
|
||||
|
||||
properties::UIntProperty _nDataPoints;
|
||||
properties::BoolProperty _hasOrientationData;
|
||||
|
||||
struct Texture : properties::PropertyOwner {
|
||||
Texture();
|
||||
|
||||
properties::BoolProperty enabled;
|
||||
properties::BoolProperty allowCompression;
|
||||
properties::BoolProperty useAlphaChannel;
|
||||
properties::StringProperty spriteTexturePath;
|
||||
properties::StringProperty inputMode;
|
||||
};
|
||||
Texture _texture;
|
||||
TextureInputMode _textureMode = TextureInputMode::Single;
|
||||
std::filesystem::path _texturesDirectory;
|
||||
|
||||
ghoul::opengl::ProgramObject* _program = nullptr;
|
||||
|
||||
UniformCache(
|
||||
cameraViewMatrix, projectionMatrix, modelMatrix, cameraPosition, cameraLookUp,
|
||||
renderOption, maxAngularSize, color, opacity, scaleExponent, scaleFactor, up,
|
||||
right, fadeInValue, hasSpriteTexture, spriteTexture, useColorMap, colorMapTexture,
|
||||
cmapRangeMin, cmapRangeMax, nanColor, useNanColor, hideOutsideRange,
|
||||
enableMaxSizeControl, aboveRangeColor, useAboveRangeColor, belowRangeColor,
|
||||
useBelowRangeColor, hasDvarScaling, dvarScaleFactor, enableOutline, outlineColor,
|
||||
outlineWeight, outlineStyle, useCmapOutline, aspectRatioScale, useOrientationData
|
||||
) _uniformCache;
|
||||
|
||||
std::filesystem::path _dataFile;
|
||||
|
||||
DistanceUnit _unit = DistanceUnit::Parsec;
|
||||
|
||||
bool _useCaching = true;
|
||||
bool _shouldComputeScaleExponent = false;
|
||||
bool _createLabelsFromDataset = false;
|
||||
bool _skipFirstDataPoint = false;
|
||||
|
||||
dataloader::Dataset _dataset;
|
||||
dataloader::DataMapping _dataMapping;
|
||||
|
||||
std::unique_ptr<LabelsComponent> _labels;
|
||||
|
||||
glm::dmat4 _transformationMatrix = glm::dmat4(1.0);
|
||||
|
||||
GLuint _vao = 0;
|
||||
GLuint _vbo = 0;
|
||||
|
||||
// List of (unique) loaded textures. The other maps refer to the index in this vector
|
||||
std::vector<std::unique_ptr<ghoul::opengl::Texture>> _textures;
|
||||
std::unordered_map<std::string, size_t> _textureNameToIndex;
|
||||
|
||||
// Texture index in dataset to index in vector of textures
|
||||
std::unordered_map<int, size_t> _indexInDataToTextureIndex;
|
||||
|
||||
// Resolution/format to index in textures vector (used to generate one texture
|
||||
// array per unique format)
|
||||
std::unordered_map<TextureFormat, std::vector<size_t>, TextureFormatHash>
|
||||
_textureMapByFormat;
|
||||
|
||||
// One per resolution above
|
||||
struct TextureArrayInfo {
|
||||
GLuint renderId;
|
||||
GLint startOffset = -1;
|
||||
int nPoints = -1;
|
||||
glm::vec2 aspectRatioScale = glm::vec2(1.f);
|
||||
};
|
||||
std::vector<TextureArrayInfo> _textureArrays;
|
||||
|
||||
struct TextureId {
|
||||
unsigned int arrayId;
|
||||
unsigned int layer;
|
||||
};
|
||||
std::unordered_map<size_t, TextureId> _textureIndexToArrayMap;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___RENDERABLEPOINTCLOUD___H__
|
||||
226
modules/base/rendering/pointcloud/renderablepolygoncloud.cpp
Normal file
226
modules/base/rendering/pointcloud/renderablepolygoncloud.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/pointcloud/renderablepolygoncloud.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <optional>
|
||||
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "RenderablePolygonCloud";
|
||||
|
||||
// A RenderablePolygonCloud is a RenderablePointCloud where the shape of the points
|
||||
// is a uniform polygon with a given number of sides instead of a texture. For
|
||||
// instance, PolygonSides = 5 results in the points being rendered as pentagons.
|
||||
//
|
||||
// Note that while this renderable inherits the texture component from
|
||||
// RenderablePointCloud, any added texture information will be ignored in favor of the
|
||||
// polygon shape.
|
||||
//
|
||||
// See documentation of RenderablePointCloud for details on the other parts of the
|
||||
// point cloud rendering.
|
||||
struct [[codegen::Dictionary(RenderablePolygonCloud)]] Parameters {
|
||||
// The number of sides for the polygon used to represent each point. Default is
|
||||
// 3, i.e. to use triangles.
|
||||
std::optional<int> polygonSides [[codegen::greaterequal(3)]];
|
||||
};
|
||||
|
||||
#include "renderablepolygoncloud_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation RenderablePolygonCloud::Documentation() {
|
||||
return codegen::doc<Parameters>(
|
||||
"base_renderablepolygoncloud",
|
||||
RenderablePointCloud::Documentation()
|
||||
);
|
||||
}
|
||||
|
||||
RenderablePolygonCloud::RenderablePolygonCloud(const ghoul::Dictionary& dictionary)
|
||||
: RenderablePointCloud(dictionary)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_nPolygonSides = p.polygonSides.value_or(_nPolygonSides);
|
||||
|
||||
// The texture to use for the rendering will be generated in initializeGl. Make sure
|
||||
// we use it in the rendering
|
||||
_hasSpriteTexture = true;
|
||||
|
||||
_textureMode = TextureInputMode::Other;
|
||||
removePropertySubOwner(_texture);
|
||||
}
|
||||
|
||||
void RenderablePolygonCloud::deinitializeGL() {
|
||||
glDeleteBuffers(1, &_polygonVbo);
|
||||
_polygonVbo = 0;
|
||||
glDeleteVertexArrays(1, &_polygonVao);
|
||||
_polygonVao = 0;
|
||||
|
||||
glDeleteTextures(1, &_pTexture);
|
||||
|
||||
RenderablePointCloud::deinitializeGL();
|
||||
}
|
||||
|
||||
void RenderablePolygonCloud::initializeCustomTexture() {
|
||||
ZoneScoped;
|
||||
|
||||
if (_textureIsInitialized) {
|
||||
LWARNING("RenderablePolygonCloud texture cannot be updated during runtime");
|
||||
return;
|
||||
}
|
||||
|
||||
LDEBUG("Creating Polygon Texture");
|
||||
constexpr gl::GLsizei TexSize = 512;
|
||||
|
||||
// We don't use the helper function for the format and internal format here,
|
||||
// as we don't want the compression to be used for the polygon texture and we
|
||||
// always want alpha. This is also why we do not need to update the texture
|
||||
bool useAlpha = true;
|
||||
gl::GLenum format = gl::GLenum(glFormat(useAlpha));
|
||||
gl::GLenum internalFormat = GL_RGBA8;
|
||||
|
||||
glGenTextures(1, &_pTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, _pTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
// Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
internalFormat,
|
||||
TexSize,
|
||||
TexSize,
|
||||
0,
|
||||
format,
|
||||
GL_UNSIGNED_BYTE,
|
||||
nullptr
|
||||
);
|
||||
|
||||
renderToTexture(_pTexture, TexSize, TexSize);
|
||||
|
||||
// Download the data and use it to intialize the data we need to rendering.
|
||||
// Allocate memory: N channels, with one byte each
|
||||
constexpr unsigned int nChannels = 4;
|
||||
unsigned int arraySize = TexSize * TexSize * nChannels;
|
||||
std::vector<GLubyte> pixelData;
|
||||
pixelData.resize(arraySize);
|
||||
glBindTexture(GL_TEXTURE_2D, _pTexture);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, format, GL_UNSIGNED_BYTE, pixelData.data());
|
||||
|
||||
// Create array from data, size and format
|
||||
unsigned int id = 0;
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, id);
|
||||
initAndAllocateTextureArray(id, glm::uvec2(TexSize), 1, useAlpha);
|
||||
fillAndUploadTextureLayer(0, 0, 0, glm::uvec2(TexSize), useAlpha, pixelData.data());
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
|
||||
_textureIsInitialized = true;
|
||||
}
|
||||
|
||||
void RenderablePolygonCloud::renderToTexture(GLuint textureToRenderTo,
|
||||
GLuint textureWidth, GLuint textureHeight)
|
||||
{
|
||||
LDEBUG("Rendering to Texture");
|
||||
|
||||
// Saves initial Application's OpenGL State
|
||||
GLint defaultFBO = 0;
|
||||
std::array<GLint, 4> viewport;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
||||
glGetIntegerv(GL_VIEWPORT, viewport.data());
|
||||
|
||||
GLuint textureFBO;
|
||||
glGenFramebuffers(1, &textureFBO);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, textureFBO);
|
||||
const GLenum drawBuffers = GL_COLOR_ATTACHMENT0;
|
||||
glDrawBuffers(1, &drawBuffers);
|
||||
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureToRenderTo, 0);
|
||||
|
||||
glViewport(viewport[0], viewport[1], textureWidth, textureHeight);
|
||||
|
||||
if (_polygonVao == 0) {
|
||||
glGenVertexArrays(1, &_polygonVao);
|
||||
}
|
||||
if (_polygonVbo == 0) {
|
||||
glGenBuffers(1, &_polygonVbo);
|
||||
}
|
||||
|
||||
glBindVertexArray(_polygonVao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _polygonVbo);
|
||||
|
||||
constexpr std::array<GLfloat, 4> VertexData = {
|
||||
// x y z w
|
||||
0.f, 0.f, 0.f, 1.f,
|
||||
};
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData.data(), GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
renderPolygonGeometry(_polygonVao);
|
||||
|
||||
// Restores Applications' OpenGL State
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
||||
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
||||
|
||||
glDeleteBuffers(1, &_polygonVbo);
|
||||
glDeleteVertexArrays(1, &_polygonVao);
|
||||
glDeleteFramebuffers(1, &textureFBO);
|
||||
}
|
||||
|
||||
void RenderablePolygonCloud::renderPolygonGeometry(GLuint vao) const {
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> program =
|
||||
ghoul::opengl::ProgramObject::Build(
|
||||
"RenderablePointCloud_Polygon",
|
||||
absPath("${MODULE_BASE}/shaders/polygon_vs.glsl"),
|
||||
absPath("${MODULE_BASE}/shaders/polygon_fs.glsl"),
|
||||
absPath("${MODULE_BASE}/shaders/polygon_gs.glsl")
|
||||
);
|
||||
|
||||
program->activate();
|
||||
constexpr glm::vec4 Black = glm::vec4(0.f, 0.f, 0.f, 0.f);
|
||||
glClearBufferfv(GL_COLOR, 0, glm::value_ptr(Black));
|
||||
|
||||
program->setUniform("sides", _nPolygonSides);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_POINTS, 0, 1);
|
||||
glBindVertexArray(0);
|
||||
|
||||
program->deactivate();
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
69
modules/base/rendering/pointcloud/renderablepolygoncloud.h
Normal file
69
modules/base/rendering/pointcloud/renderablepolygoncloud.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___RENDERABLEPOLYGONCLOUD___H__
|
||||
#define __OPENSPACE_MODULE_BASE___RENDERABLEPOLYGONCLOUD___H__
|
||||
|
||||
#include <modules/base/rendering/pointcloud/renderablepointcloud.h>
|
||||
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
|
||||
namespace ghoul::opengl { class Texture; }
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
/**
|
||||
* A billboarded point cloud, but with dynamically created uniform polygon shapes instead
|
||||
* of a custom texture. Overwrites the sprite set in parent class, RenderablePointCloud
|
||||
*/
|
||||
class RenderablePolygonCloud : public RenderablePointCloud {
|
||||
public:
|
||||
explicit RenderablePolygonCloud(const ghoul::Dictionary& dictionary);
|
||||
~RenderablePolygonCloud() override = default;
|
||||
|
||||
void deinitializeGL() override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
void initializeCustomTexture() override;
|
||||
void renderToTexture(GLuint textureToRenderTo, GLuint textureWidth,
|
||||
GLuint textureHeight);
|
||||
void renderPolygonGeometry(GLuint vao) const;
|
||||
|
||||
int _nPolygonSides = 3;
|
||||
|
||||
GLuint _pTexture = 0;
|
||||
|
||||
GLuint _polygonVao = 0;
|
||||
GLuint _polygonVbo = 0;
|
||||
|
||||
bool _textureIsInitialized = false;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___RENDERABLEPOLYGONCLOUD___H__
|
||||
180
modules/base/rendering/pointcloud/sizemappingcomponent.cpp
Normal file
180
modules/base/rendering/pointcloud/sizemappingcomponent.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/pointcloud/sizemappingcomponent.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/util/distanceconversion.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "SizeMapping";
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo EnabledInfo = {
|
||||
"Enabled",
|
||||
"Size Mapping Enabled",
|
||||
"If this value is set to 'true' and at least one column was loaded as an option "
|
||||
"for size mapping, the chosen data column will be used to scale the size of the "
|
||||
"points. The first option in the list is selected per default.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo OptionInfo = {
|
||||
"Parameter",
|
||||
"Parameter Option",
|
||||
"This value determines which parameter is used for scaling of the point. The "
|
||||
"parameter value will be used as a multiplicative factor to scale the size of "
|
||||
"the points. Note that they may however still be scaled by max size adjustment "
|
||||
"effects.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ScaleFactorInfo = {
|
||||
"ScaleFactor",
|
||||
"Scale Factor",
|
||||
"This value is a multiplicative factor that is applied to the data values that "
|
||||
"are used to scale the points, when size mapping is applied.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo IsRadiusInfo = {
|
||||
"IsRadius",
|
||||
"Size is Radius",
|
||||
"If true, the size value in the data is interpreted as the radius of the points. "
|
||||
"Otherwise, it is interpreted as the diameter.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(SizeMappingComponent)]] Parameters {
|
||||
// [[codegen::verbatim(EnabledInfo.description)]]
|
||||
std::optional<bool> enabled;
|
||||
|
||||
// A list specifying all parameters that may be used for size mapping, i.e.
|
||||
// scaling the points based on the provided data columns.
|
||||
std::optional<std::vector<std::string>> parameterOptions;
|
||||
|
||||
// [[codegen::verbatim(OptionInfo.description)]]
|
||||
std::optional<std::string> parameter;
|
||||
|
||||
// [[codegen::verbatim(ScaleFactorInfo.description)]]
|
||||
enum class [[codegen::map(openspace::DistanceUnit)]] ScaleUnit {
|
||||
Nanometer,
|
||||
Micrometer,
|
||||
Millimeter,
|
||||
Centimeter,
|
||||
Decimeter,
|
||||
Meter,
|
||||
Kilometer,
|
||||
AU,
|
||||
Lighthour,
|
||||
Lightday,
|
||||
Lightmonth,
|
||||
Lightyear,
|
||||
Parsec,
|
||||
Kiloparsec,
|
||||
Megaparsec,
|
||||
Gigaparsec,
|
||||
Gigalightyear
|
||||
};
|
||||
|
||||
// The scale used for the size values in the dataset, given as either a string
|
||||
// representing a specific unit or a value to multiply all the datapoints with
|
||||
// to convert the value to meter. The resulting value will be applied as a
|
||||
// multiplicative factor. For example, if the size data is given in is in
|
||||
// kilometers then specify either <code>ScaleFactor = 'Kilometer'</code> or
|
||||
// <code>ScaleFactor = 1000.0</code>.
|
||||
std::optional<std::variant<ScaleUnit, double>> scaleFactor;
|
||||
|
||||
// [[codegen::verbatim(IsRadiusInfo.description)]]
|
||||
std::optional<bool> isRadius;
|
||||
};
|
||||
#include "sizemappingcomponent_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation SizeMappingComponent::Documentation() {
|
||||
return codegen::doc<Parameters>("base_sizemappingcomponent");
|
||||
}
|
||||
|
||||
SizeMappingComponent::SizeMappingComponent()
|
||||
: properties::PropertyOwner({ "SizeMapping", "Size Mapping", "" })
|
||||
, enabled(EnabledInfo, true)
|
||||
, parameterOption(OptionInfo)
|
||||
, scaleFactor(ScaleFactorInfo, 1.f, 0.f, 1000.f)
|
||||
, isRadius(IsRadiusInfo, false)
|
||||
{
|
||||
addProperty(enabled);
|
||||
addProperty(parameterOption);
|
||||
addProperty(scaleFactor);
|
||||
addProperty(isRadius);
|
||||
}
|
||||
|
||||
SizeMappingComponent::SizeMappingComponent(const ghoul::Dictionary& dictionary)
|
||||
: SizeMappingComponent()
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
enabled = p.enabled.value_or(enabled);
|
||||
|
||||
int indexOfProvidedOption = -1;
|
||||
|
||||
if (p.parameterOptions.has_value()) {
|
||||
std::vector<std::string> opts = *p.parameterOptions;
|
||||
for (size_t i = 0; i < opts.size(); ++i) {
|
||||
// Note that options are added in order
|
||||
parameterOption.addOption(static_cast<int>(i), opts[i]);
|
||||
|
||||
if (p.parameter.has_value() && *p.parameter == opts[i]) {
|
||||
indexOfProvidedOption = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (indexOfProvidedOption >= 0) {
|
||||
parameterOption = indexOfProvidedOption;
|
||||
}
|
||||
else if (p.parameter.has_value()) {
|
||||
LERROR(std::format(
|
||||
"Error when reading Parameter. Could not find provided parameter '{}' in "
|
||||
"list of parameter options. Using default.", *p.parameter
|
||||
));
|
||||
}
|
||||
|
||||
if (p.scaleFactor.has_value()) {
|
||||
if (std::holds_alternative<Parameters::ScaleUnit>(*p.scaleFactor)) {
|
||||
const Parameters::ScaleUnit scaleUnit =
|
||||
std::get<Parameters::ScaleUnit>(*p.scaleFactor);
|
||||
const DistanceUnit distanceUnit = codegen::map<DistanceUnit>(scaleUnit);
|
||||
scaleFactor = static_cast<float>(toMeter(distanceUnit));
|
||||
}
|
||||
else if (std::holds_alternative<double>(*p.scaleFactor)) {
|
||||
scaleFactor = static_cast<float>(std::get<double>(*p.scaleFactor));
|
||||
}
|
||||
}
|
||||
|
||||
isRadius = p.isRadius.value_or(isRadius);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
57
modules/base/rendering/pointcloud/sizemappingcomponent.h
Normal file
57
modules/base/rendering/pointcloud/sizemappingcomponent.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___SIZEMAPPINGCOMPONENT___H__
|
||||
#define __OPENSPACE_MODULE_BASE___SIZEMAPPINGCOMPONENT___H__
|
||||
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
/**
|
||||
* This is a component that can be used to hold parameters and properties for scaling
|
||||
* point cloud points (or other data-based entities) based a parameter in a dataset.
|
||||
*/
|
||||
struct SizeMappingComponent : public properties::PropertyOwner {
|
||||
SizeMappingComponent();
|
||||
explicit SizeMappingComponent(const ghoul::Dictionary& dictionary);
|
||||
~SizeMappingComponent() override = default;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
properties::BoolProperty enabled;
|
||||
properties::OptionProperty parameterOption;
|
||||
properties::FloatProperty scaleFactor;
|
||||
properties::BoolProperty isRadius;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___SIZEMAPPINGCOMPONENT___H__
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -40,21 +40,34 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo XColorInfo = {
|
||||
"XColor",
|
||||
"X Color",
|
||||
"This value determines the color of the x axis"
|
||||
"The color of the x-axis.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo YColorInfo = {
|
||||
"YColor",
|
||||
"Y Color",
|
||||
"This value determines the color of the y axis"
|
||||
"The color of the y-axis.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ZColorInfo = {
|
||||
"ZColor",
|
||||
"Z Color",
|
||||
"This value determines the color of the z axis"
|
||||
"The color of the z-axis.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
// The RenderableCartesianAxes can be used to render the local Cartesian coordinate
|
||||
// system, or reference frame, of another scene graph node. The colors of the axes
|
||||
// can be customized but are per default set to Red, Green and Blue, for the X-, Y-
|
||||
// and Z-axis, respectively.
|
||||
//
|
||||
// To add the axes, create a scene graph node with the RenderableCartesianAxes
|
||||
// renderable and add it as a child to the other scene graph node, i.e. specify the
|
||||
// other node as the `Parent` of the node with this renderable. Also, the axes have to
|
||||
// be scaled to match the parent object for the axes to be visible in the scene, for
|
||||
// example using a [StaticScale](#base_transform_scale_static).
|
||||
struct [[codegen::Dictionary(RenderableCartesianAxes)]] Parameters {
|
||||
// [[codegen::verbatim(XColorInfo.description)]]
|
||||
std::optional<glm::vec3> xColor [[codegen::color()]];
|
||||
@@ -64,7 +77,6 @@ namespace {
|
||||
|
||||
// [[codegen::verbatim(ZColorInfo.description)]]
|
||||
std::optional<glm::vec3> zColor [[codegen::color()]];
|
||||
|
||||
};
|
||||
#include "renderablecartesianaxes_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -83,6 +95,9 @@ RenderableCartesianAxes::RenderableCartesianAxes(const ghoul::Dictionary& dictio
|
||||
, _zColor(ZColorInfo, glm::vec3(0.f, 0.f, 1.f), glm::vec3(0.f), glm::vec3(1.f))
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_xColor = p.xColor.value_or(_xColor);
|
||||
_xColor.setViewOption(properties::Property::ViewOptions::Color);
|
||||
addProperty(_xColor);
|
||||
@@ -117,14 +132,14 @@ void RenderableCartesianAxes::initializeGL() {
|
||||
glGenVertexArrays(1, &_vaoId);
|
||||
glBindVertexArray(_vaoId);
|
||||
|
||||
std::vector<Vertex> vertices({
|
||||
constexpr std::array<Vertex, 4> vertices = {
|
||||
Vertex{0.f, 0.f, 0.f},
|
||||
Vertex{1.f, 0.f, 0.f},
|
||||
Vertex{0.f, 1.f, 0.f},
|
||||
Vertex{0.f, 0.f, 1.f}
|
||||
});
|
||||
};
|
||||
|
||||
std::vector<int> indices = {
|
||||
constexpr std::array<int, 6> indices = {
|
||||
0, 1,
|
||||
0, 2,
|
||||
0, 3
|
||||
@@ -172,16 +187,10 @@ void RenderableCartesianAxes::deinitializeGL() {
|
||||
_program = nullptr;
|
||||
}
|
||||
|
||||
void RenderableCartesianAxes::render(const RenderData& data, RendererTasks&){
|
||||
void RenderableCartesianAxes::render(const RenderData& data, RendererTasks&) {
|
||||
_program->activate();
|
||||
|
||||
const glm::dmat4 modelTransform =
|
||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
||||
glm::dmat4(data.modelTransform.rotation) *
|
||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
||||
|
||||
const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() *
|
||||
modelTransform;
|
||||
const glm::dmat4 modelViewTransform = calcModelViewTransform(data);
|
||||
|
||||
_program->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
|
||||
_program->setUniform("projectionTransform", data.camera.projectionMatrix());
|
||||
@@ -189,12 +198,17 @@ void RenderableCartesianAxes::render(const RenderData& data, RendererTasks&){
|
||||
_program->setUniform("xColor", _xColor);
|
||||
_program->setUniform("yColor", _yColor);
|
||||
_program->setUniform("zColor", _zColor);
|
||||
_program->setUniform("opacity", opacity());
|
||||
|
||||
// Changes GL state:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnablei(GL_BLEND, 0);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glLineWidth(3.0);
|
||||
#ifndef __APPLE__
|
||||
glLineWidth(3.f);
|
||||
#else // ^^^^ __APPLE__ // !__APPLE__ vvvv
|
||||
glLineWidth(1.f);
|
||||
#endif // __APPLE__
|
||||
|
||||
glBindVertexArray(_vaoId);
|
||||
glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, nullptr);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/matrix/dmat4property.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
@@ -42,7 +42,7 @@ namespace openspace {
|
||||
|
||||
class RenderableCartesianAxes : public Renderable {
|
||||
public:
|
||||
RenderableCartesianAxes(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableCartesianAxes(const ghoul::Dictionary& dictionary);
|
||||
~RenderableCartesianAxes() override = default;
|
||||
|
||||
void initializeGL() override;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -39,31 +39,36 @@
|
||||
#include <optional>
|
||||
|
||||
namespace {
|
||||
constexpr std::array<const char*, 4> UniformNames = {
|
||||
"modelViewProjectionTransform", "opacity", "width", "colorTexture"
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo TextureInfo = {
|
||||
"Texture",
|
||||
"Texture",
|
||||
"This value is the path to a texture on disk that contains a one-dimensional "
|
||||
"texture to be used for the color"
|
||||
"The path to a file with a one-dimensional texture to be used for the disc "
|
||||
"color. The leftmost color will be innermost color when rendering the disc, "
|
||||
"and the rightmost color will be the outermost color.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
|
||||
"Size",
|
||||
"Size",
|
||||
"This value specifies the outer radius of the disc in meter"
|
||||
"The outer radius of the disc, in meters.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo WidthInfo = {
|
||||
"Width",
|
||||
"Width",
|
||||
"This value is used to set the width of the disc. The actual width is set "
|
||||
"based on the given size and this value should be set between 0 and 1. A value "
|
||||
"of 1 results in a full circle and 0.5 a disc with an inner radius of 0.5*size"
|
||||
"The disc width, given as a ratio of the full disc radius. For example, a value "
|
||||
"of 1 results in a full circle, while 0.5 results in a disc where the inner "
|
||||
"radius is half of the full radius.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
// This renderable can be used to create a circular disc that is colored based on a
|
||||
// one-dimensional texture.
|
||||
//
|
||||
// The disc will be filled i.e. a full circle, per default, but may also be made
|
||||
// with a hole in the center using the `Width` parameter.
|
||||
struct [[codegen::Dictionary(RenderableDisc)]] Parameters {
|
||||
// [[codegen::verbatim(TextureInfo.description)]]
|
||||
std::filesystem::path texture;
|
||||
@@ -72,7 +77,7 @@ namespace {
|
||||
std::optional<float> size;
|
||||
|
||||
// [[codegen::verbatim(WidthInfo.description)]]
|
||||
std::optional<float> width;
|
||||
std::optional<float> width [[codegen::inrange(0.0, 1.0)]];
|
||||
};
|
||||
#include "renderabledisc_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -86,31 +91,33 @@ documentation::Documentation RenderableDisc::Documentation() {
|
||||
RenderableDisc::RenderableDisc(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary)
|
||||
, _texturePath(TextureInfo)
|
||||
, _size(SizeInfo, 1.f, 0.f, 1e13f)
|
||||
, _width(WidthInfo, 0.5f, 0.f, 1.f)
|
||||
, _size(SizeInfo, 1.f, 0.001f, 1e13f)
|
||||
, _width(WidthInfo, 1.f, 0.001f, 1.f)
|
||||
, _plane(_size)
|
||||
, _planeIsDirty(true)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_texturePath = p.texture.string();
|
||||
_texturePath.onChange([&]() { _texture->loadFromFile(_texturePath.value()); });
|
||||
_texturePath.onChange([this]() { _texture->loadFromFile(_texturePath.value()); });
|
||||
addProperty(_texturePath);
|
||||
|
||||
_size.setExponent(13.f);
|
||||
_size.setExponent(7.f);
|
||||
_size = p.size.value_or(_size);
|
||||
setBoundingSphere(_size);
|
||||
_size.onChange([&]() { _planeIsDirty = true; });
|
||||
_size.onChange([this]() { _planeIsDirty = true; });
|
||||
addProperty(_size);
|
||||
|
||||
_width = p.width.value_or(_width);
|
||||
addProperty(_width);
|
||||
|
||||
addProperty(_opacity);
|
||||
|
||||
setRenderBin(Renderable::RenderBin::PostDeferredTransparent);
|
||||
}
|
||||
|
||||
bool RenderableDisc::isReady() const {
|
||||
return _shader && _texture && _plane;
|
||||
return _shader && _texture;
|
||||
}
|
||||
|
||||
void RenderableDisc::initialize() {
|
||||
@@ -118,8 +125,6 @@ void RenderableDisc::initialize() {
|
||||
_texture->setFilterMode(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
|
||||
_texture->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToEdge);
|
||||
_texture->setShouldWatchFileForChanges(true);
|
||||
|
||||
_plane = std::make_unique<PlaneGeometry>(planeSize());
|
||||
}
|
||||
|
||||
void RenderableDisc::initializeGL() {
|
||||
@@ -128,12 +133,11 @@ void RenderableDisc::initializeGL() {
|
||||
_texture->loadFromFile(_texturePath.value());
|
||||
_texture->uploadToGpu();
|
||||
|
||||
_plane->initialize();
|
||||
_plane.initialize();
|
||||
}
|
||||
|
||||
void RenderableDisc::deinitializeGL() {
|
||||
_plane->deinitialize();
|
||||
_plane = nullptr;
|
||||
_plane.deinitialize();
|
||||
_texture = nullptr;
|
||||
|
||||
global::renderEngine->removeRenderProgram(_shader.get());
|
||||
@@ -143,16 +147,12 @@ void RenderableDisc::deinitializeGL() {
|
||||
void RenderableDisc::render(const RenderData& data, RendererTasks&) {
|
||||
_shader->activate();
|
||||
|
||||
glm::dmat4 modelTransform =
|
||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
||||
glm::dmat4(data.modelTransform.rotation) *
|
||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
||||
|
||||
glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform;
|
||||
const glm::dmat4 modelViewProjectionTransform =
|
||||
calcModelViewProjectionTransform(data);
|
||||
|
||||
_shader->setUniform(
|
||||
_uniformCache.modelViewProjection,
|
||||
data.camera.projectionMatrix() * glm::mat4(modelViewTransform)
|
||||
_uniformCache.modelViewProjectionTransform,
|
||||
glm::mat4(modelViewProjectionTransform)
|
||||
);
|
||||
_shader->setUniform(_uniformCache.width, _width);
|
||||
_shader->setUniform(_uniformCache.opacity, opacity());
|
||||
@@ -160,14 +160,14 @@ void RenderableDisc::render(const RenderData& data, RendererTasks&) {
|
||||
ghoul::opengl::TextureUnit unit;
|
||||
unit.activate();
|
||||
_texture->bind();
|
||||
_shader->setUniform(_uniformCache.texture, unit);
|
||||
_shader->setUniform(_uniformCache.colorTexture, unit);
|
||||
|
||||
glEnablei(GL_BLEND, 0);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(false);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
_plane->render();
|
||||
_plane.render();
|
||||
|
||||
_shader->deactivate();
|
||||
|
||||
@@ -178,13 +178,13 @@ void RenderableDisc::render(const RenderData& data, RendererTasks&) {
|
||||
}
|
||||
|
||||
void RenderableDisc::update(const UpdateData&) {
|
||||
if (_shader->isDirty()) {
|
||||
if (_shader->isDirty()) [[unlikely]] {
|
||||
_shader->rebuildFromFile();
|
||||
updateUniformLocations();
|
||||
}
|
||||
|
||||
if (_planeIsDirty) {
|
||||
_plane->updateSize(planeSize());
|
||||
if (_planeIsDirty) [[unlikely]] {
|
||||
_plane.updateSize(planeSize());
|
||||
_planeIsDirty = false;
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ void RenderableDisc::initializeShader() {
|
||||
}
|
||||
|
||||
void RenderableDisc::updateUniformLocations() {
|
||||
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
|
||||
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache);
|
||||
}
|
||||
|
||||
float RenderableDisc::planeSize() const {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -25,9 +25,10 @@
|
||||
#ifndef __OPENSPACE_MODULE_BASE___RENDERABLEDISC___H__
|
||||
#define __OPENSPACE_MODULE_BASE___RENDERABLEDISC___H__
|
||||
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/rendering/texturecomponent.h>
|
||||
#include <openspace/util/planegeometry.h>
|
||||
#include <ghoul/opengl/uniformcache.h>
|
||||
@@ -42,7 +43,7 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class RenderableDisc : public Renderable {
|
||||
public:
|
||||
RenderableDisc(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableDisc(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initialize() override;
|
||||
void initializeGL() override;
|
||||
@@ -67,11 +68,12 @@ protected:
|
||||
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> _shader;
|
||||
|
||||
std::unique_ptr<PlaneGeometry> _plane;
|
||||
PlaneGeometry _plane;
|
||||
std::unique_ptr<TextureComponent> _texture;
|
||||
|
||||
private:
|
||||
UniformCache(modelViewProjection, opacity, width, texture) _uniformCache;
|
||||
UniformCache(modelViewProjectionTransform, opacity, width,
|
||||
colorTexture) _uniformCache;
|
||||
|
||||
bool _planeIsDirty = false;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -22,7 +22,7 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/vislab/rendering/renderabledistancelabel.h>
|
||||
#include <modules/base/rendering/renderabledistancelabel.h>
|
||||
|
||||
#include <modules/base/rendering/renderablenodeline.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/util/distanceconversion.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -40,33 +41,58 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo NodeLineInfo = {
|
||||
"NodeLine",
|
||||
"Node Line",
|
||||
"Property to track a nodeline. When tracking the label text will be updating the "
|
||||
"distance from the nodeline start and end"
|
||||
"The identifier of a scene graph node with a RenderableNodeLine that this label "
|
||||
"should track. The label text will be updating based on the distance from the "
|
||||
"node line's start and end.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DistanceUnitInfo = {
|
||||
"DistanceUnit",
|
||||
"Distance Unit",
|
||||
"Property to define the unit in which the distance should be displayed"
|
||||
"Defaults to 'km' if not specified"
|
||||
"Display Distance Unit",
|
||||
"The unit in which the distance value should be displayed. Defaults to 'km' if "
|
||||
"not specified.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo CustomUnitDescriptorInfo = {
|
||||
"CustomUnitDescriptor",
|
||||
"Custom Unit Descriptor",
|
||||
"Property to define a custom unit descriptor to use to describe the distance "
|
||||
"value. Defaults to the units SI descriptor if not specified"
|
||||
"value. Defaults to the selected unit's SI descriptor if not specified.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo PrecisionInfo = {
|
||||
"Precision",
|
||||
"Precision",
|
||||
"The precision in which to to show the distance number, i.e. the number of "
|
||||
"digits after the decimal point.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// This `Renderable` creates a label that shows the distance between two nodes, based
|
||||
// on an existing [RenderableNodeLine](#base_renderable_nodeline). The label
|
||||
// will be placed halfway between the two scene graph nodes that the line connects.
|
||||
//
|
||||
// The unit in which the distance is displayed can be customized, as well as the
|
||||
// precision of the number.
|
||||
struct [[codegen::Dictionary(RenderableDistanceLabel)]] Parameters {
|
||||
// [[codegen::verbatim(NodeLineInfo.description)]]
|
||||
// The identifier of a scene graph node with a
|
||||
// [RenderableNodeLine](#base_renderable_nodeline) that this label
|
||||
// should track. The label text will be updating based on the distance from the
|
||||
// node line's start and end.
|
||||
std::string nodeLine;
|
||||
|
||||
// [[codegen::verbatim(DistanceUnitInfo.description)]]
|
||||
std::optional<int> distanceUnit;
|
||||
std::optional<std::string> distanceUnit
|
||||
[[codegen::inlist(openspace::distanceUnitList())]];
|
||||
|
||||
// [[codegen::verbatim(CustomUnitDescriptorInfo.description)]]
|
||||
std::optional<std::string> customUnitDescriptor;
|
||||
|
||||
// [[codegen::verbatim(PrecisionInfo.description)]]
|
||||
std::optional<int> precision [[codegen::greaterequal(0)]];
|
||||
};
|
||||
#include "renderabledistancelabel_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -74,29 +100,46 @@ namespace {
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation RenderableDistanceLabel::Documentation() {
|
||||
return codegen::doc<Parameters>("vislab_renderable_distance_label");
|
||||
return codegen::doc<Parameters>("base_renderable_distancelabel");
|
||||
}
|
||||
|
||||
RenderableDistanceLabel::RenderableDistanceLabel(const ghoul::Dictionary& dictionary)
|
||||
: RenderableLabel(dictionary)
|
||||
, _nodelineId(NodeLineInfo)
|
||||
, _distanceUnit(DistanceUnitInfo, 1, 0, 11)
|
||||
, _distanceUnit(DistanceUnitInfo)
|
||||
, _customUnitDescriptor(CustomUnitDescriptorInfo)
|
||||
, _precision(PrecisionInfo, 0, 0, 10)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_nodelineId = p.nodeLine;
|
||||
addProperty(_nodelineId);
|
||||
|
||||
_distanceUnit = p.distanceUnit.value_or(_distanceUnit);
|
||||
for (const DistanceUnit u : DistanceUnits) {
|
||||
_distanceUnit.addOption(
|
||||
static_cast<int>(u),
|
||||
std::string(nameForDistanceUnit(u))
|
||||
);
|
||||
}
|
||||
_distanceUnit = static_cast<int>(DistanceUnit::Kilometer);
|
||||
if (p.distanceUnit.has_value()) {
|
||||
const DistanceUnit unit = distanceUnitFromString(*p.distanceUnit);
|
||||
_distanceUnit = static_cast<int>(unit);
|
||||
}
|
||||
addProperty(_distanceUnit);
|
||||
|
||||
_customUnitDescriptor = p.customUnitDescriptor.value_or(_customUnitDescriptor);
|
||||
addProperty(_customUnitDescriptor);
|
||||
|
||||
_precision = p.precision.value_or(_precision);
|
||||
addProperty(_precision);
|
||||
|
||||
// The text will be updated automatically, so set the property to readonly
|
||||
_text.setReadOnly(true);
|
||||
}
|
||||
|
||||
void RenderableDistanceLabel::update(const UpdateData&) {
|
||||
if (_errorThrown) {
|
||||
if (_errorThrown) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,45 +156,43 @@ void RenderableDistanceLabel::update(const UpdateData&) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get used unit scale
|
||||
const float scale = unit(_distanceUnit);
|
||||
const DistanceUnit unit = static_cast<DistanceUnit>(_distanceUnit.value());
|
||||
|
||||
// Get unit descriptor text
|
||||
std::string_view unitDescriptor = toString(_distanceUnit);
|
||||
std::string_view unitDescriptor = abbreviationForDistanceUnit(unit);
|
||||
if (!_customUnitDescriptor.value().empty()) {
|
||||
unitDescriptor = _customUnitDescriptor;
|
||||
}
|
||||
|
||||
// Get distance as string and remove fractional part
|
||||
std::string distanceText = std::to_string(
|
||||
std::round(nodeline->distance() / scale)
|
||||
);
|
||||
int pos = static_cast<int>(distanceText.find("."));
|
||||
std::string subStr = distanceText.substr(pos);
|
||||
distanceText.erase(pos, subStr.size());
|
||||
// Get distance as string
|
||||
const double convertedDistance = convertMeters(nodeline->distance(), unit);
|
||||
|
||||
std::string distanceText = std::format("{:.{}f}", convertedDistance, _precision.value());
|
||||
|
||||
// Create final label text and set it
|
||||
const std::string finalText = fmt::format("{} {}", distanceText, unitDescriptor);
|
||||
const std::string finalText = std::format("{} {}", distanceText, unitDescriptor);
|
||||
setLabelText(finalText);
|
||||
|
||||
// Update placement of label with transformation matrix
|
||||
SceneGraphNode* startNode = RE.scene()->sceneGraphNode(nodeline->start());
|
||||
SceneGraphNode* endNode = RE.scene()->sceneGraphNode(nodeline->end());
|
||||
if (startNode && endNode) {
|
||||
glm::dvec3 start = startNode->worldPosition();
|
||||
glm::dvec3 end = endNode->worldPosition();
|
||||
glm::dvec3 goalPos = start + (end - start) / 2.0;
|
||||
if (startNode && endNode) [[likely]] {
|
||||
const glm::dvec3 start = startNode->worldPosition();
|
||||
const glm::dvec3 end = endNode->worldPosition();
|
||||
const glm::dvec3 goalPos = start + (end - start) / 2.0;
|
||||
_transformationMatrix = glm::translate(glm::dmat4(1.0), goalPos);
|
||||
}
|
||||
else {
|
||||
LERROR(fmt::format(
|
||||
"Could not find scene graph node {} or {}",
|
||||
LERROR(std::format(
|
||||
"Could not find scene graph node '{}' or '{}'",
|
||||
nodeline->start(), nodeline->end()
|
||||
));
|
||||
}
|
||||
}
|
||||
else {
|
||||
LERROR(fmt::format("There is no scenegraph node with id {}", _nodelineId));
|
||||
LERROR(std::format(
|
||||
"There is no scenegraph node with id {}", _nodelineId.value()
|
||||
));
|
||||
_errorThrown = true;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -22,29 +22,34 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_VISLAB___RENDERABLEDISTANCELABEL___H__
|
||||
#define __OPENSPACE_MODULE_VISLAB___RENDERABLEDISTANCELABEL___H__
|
||||
#ifndef __OPENSPACE_MODULE_BASE___RENDERABLEDISTANCELABEL___H__
|
||||
#define __OPENSPACE_MODULE_BASE___RENDERABLEDISTANCELABEL___H__
|
||||
|
||||
#include <modules/base/rendering/renderablelabel.h>
|
||||
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
|
||||
class RenderableDistanceLabel : public RenderableLabel {
|
||||
public:
|
||||
RenderableDistanceLabel(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableDistanceLabel(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void update(const UpdateData& data) override;
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
properties::StringProperty _nodelineId;
|
||||
properties::IntProperty _distanceUnit;
|
||||
properties::OptionProperty _distanceUnit;
|
||||
properties::StringProperty _customUnitDescriptor;
|
||||
properties::IntProperty _precision;
|
||||
bool _errorThrown = false;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_VISLAB___RENDERABLEDISTANCELABEL___H__
|
||||
#endif // __OPENSPACE_MODULE_BASE___RENDERABLEDISTANCELABEL___H__
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -92,55 +92,65 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo BlendModeInfo = {
|
||||
"BlendMode",
|
||||
"Blending Mode",
|
||||
"This determines the blending mode that is applied to the renderable"
|
||||
"This determines the blending mode that is applied to the renderable.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
||||
"Color",
|
||||
"Color",
|
||||
"The label text color"
|
||||
"The label text color.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FontSizeInfo = {
|
||||
"FontSize",
|
||||
"Font Size",
|
||||
"The font size (in points) for the label"
|
||||
"The font size (in points) for the label.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
|
||||
"Size",
|
||||
"Size",
|
||||
"This value affects the size scale of the label"
|
||||
"Scales the size of the label, exponentially. The value is used as the exponent "
|
||||
"in a 10^x computation to scale the label size.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo TextInfo = {
|
||||
"Text",
|
||||
"Text",
|
||||
"The text that will be displayed on screen"
|
||||
"The text that will be displayed on screen.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo MinMaxSizeInfo = {
|
||||
"MinMaxSize",
|
||||
"Min and Max Size",
|
||||
"The minimum and maximum size (in pixels) of the label"
|
||||
"The minimum and maximum size (in pixels) of the label.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo TransformationMatrixInfo = {
|
||||
"TransformationMatrix",
|
||||
"Transformation Matrix",
|
||||
"Transformation matrix to be applied to the label"
|
||||
"Transformation matrix to be applied to the label.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo OrientationOptionInfo = {
|
||||
"OrientationOption",
|
||||
"Orientation Option",
|
||||
"Label orientation rendering mode"
|
||||
"Label orientation rendering mode.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo EnableFadingEffectInfo = {
|
||||
"EnableFading",
|
||||
"Enable/Disable Fade-in Effect",
|
||||
"Enable/Disable the Fade-in effect"
|
||||
"Enable/Disable the Fade-in effect.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FadeWidthsInfo = {
|
||||
@@ -150,20 +160,23 @@ namespace {
|
||||
"The first value is the distance before the closest distance and the second "
|
||||
"the one after the furthest distance. For example, with the unit Parsec (pc), "
|
||||
"a value of {1, 2} will make the label being fully faded out 1 Parsec before "
|
||||
"the closest distance and 2 Parsec away from the furthest distance"
|
||||
"the closest distance and 2 Parsec away from the furthest distance.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FadeDistancesInfo = {
|
||||
"FadeDistances",
|
||||
"Fade Distances",
|
||||
"The distance range in which the labels should be fully opaque, specified in "
|
||||
"the chosen unit. The distance from the position of the label to the camera"
|
||||
"the chosen unit. The distance from the position of the label to the camera.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FadeUnitOptionInfo = {
|
||||
"FadeUnit",
|
||||
"Fade Distance Unit",
|
||||
"Distance unit for fade-in/-out distance calculations. Defaults to \"au\""
|
||||
"Distance unit for fade-in/-out distance calculations. Defaults to \"au\".",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(RenderableLabel)]] Parameters {
|
||||
@@ -238,35 +251,28 @@ documentation::Documentation RenderableLabel::Documentation() {
|
||||
}
|
||||
|
||||
RenderableLabel::RenderableLabel(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary)
|
||||
, _blendMode(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown)
|
||||
: Renderable(dictionary, { .automaticallyUpdateRenderBin = false })
|
||||
, _blendMode(BlendModeInfo)
|
||||
, _text(TextInfo, "")
|
||||
, _color(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
|
||||
, _fontSize(FontSizeInfo, 50.f, 1.f, 100.f)
|
||||
, _size(SizeInfo, 8.f, 0.5f, 30.f)
|
||||
, _minMaxSize(MinMaxSizeInfo, glm::ivec2(8, 20), glm::ivec2(0), glm::ivec2(100))
|
||||
, _text(TextInfo, "")
|
||||
, _enableFadingEffect(EnableFadingEffectInfo, false)
|
||||
, _fadeWidths(FadeWidthsInfo, glm::vec2(1.f), glm::vec2(0.f), glm::vec2(100.f))
|
||||
, _fadeDistances(FadeDistancesInfo, glm::vec2(1.f), glm::vec2(0.f), glm::vec2(100.f))
|
||||
, _fadeUnitOption(
|
||||
FadeUnitOptionInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
)
|
||||
, _orientationOption(
|
||||
OrientationOptionInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
)
|
||||
, _fadeUnitOption(FadeUnitOptionInfo)
|
||||
, _orientationOption(OrientationOptionInfo)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(_opacity);
|
||||
registerUpdateRenderBinFromOpacity();
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_blendMode.addOptions({
|
||||
{ BlendMode::Normal, "Normal" },
|
||||
{ BlendMode::Additive, "Additive"}
|
||||
{ BlendMode::Additive, "Additive" }
|
||||
});
|
||||
_blendMode.onChange([&]() {
|
||||
_blendMode.onChange([this]() {
|
||||
switch (_blendMode) {
|
||||
case BlendMode::Normal:
|
||||
setRenderBinFromOpacity();
|
||||
@@ -274,8 +280,6 @@ RenderableLabel::RenderableLabel(const ghoul::Dictionary& dictionary)
|
||||
case BlendMode::Additive:
|
||||
setRenderBin(Renderable::RenderBin::PreDeferredTransparent);
|
||||
break;
|
||||
default:
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -302,7 +306,7 @@ RenderableLabel::RenderableLabel(const ghoul::Dictionary& dictionary)
|
||||
addProperty(_color);
|
||||
|
||||
_fontSize = p.fontSize.value_or(_fontSize);
|
||||
_fontSize.onChange([&]() {
|
||||
_fontSize.onChange([this]() {
|
||||
_font = global::fontManager->font(
|
||||
"Mono",
|
||||
_fontSize,
|
||||
@@ -362,20 +366,18 @@ bool RenderableLabel::isReady() const {
|
||||
}
|
||||
|
||||
void RenderableLabel::initialize() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
setRenderBin(Renderable::RenderBin::PreDeferredTransparent);
|
||||
}
|
||||
|
||||
void RenderableLabel::initializeGL() {
|
||||
if (_font == nullptr) {
|
||||
_font = global::fontManager->font(
|
||||
"Mono",
|
||||
_fontSize,
|
||||
ghoul::fontrendering::FontManager::Outline::Yes,
|
||||
ghoul::fontrendering::FontManager::LoadGlyphs::No
|
||||
);
|
||||
}
|
||||
_font = global::fontManager->font(
|
||||
"Mono",
|
||||
_fontSize,
|
||||
ghoul::fontrendering::FontManager::Outline::Yes,
|
||||
ghoul::fontrendering::FontManager::LoadGlyphs::No
|
||||
);
|
||||
}
|
||||
|
||||
void RenderableLabel::deinitializeGL() {}
|
||||
@@ -387,34 +389,34 @@ void RenderableLabel::render(const RenderData& data, RendererTasks&) {
|
||||
float fadeInVariable = 1.f;
|
||||
|
||||
if (_enableFadingEffect) {
|
||||
float distanceNodeToCamera = static_cast<float>(
|
||||
const float distanceNodeToCamera = static_cast<float>(
|
||||
glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
|
||||
);
|
||||
fadeInVariable = computeFadeFactor(distanceNodeToCamera);
|
||||
}
|
||||
|
||||
glm::dmat4 modelMatrix(1.0);
|
||||
glm::dmat4 modelViewMatrix = data.camera.combinedViewMatrix() * modelMatrix;
|
||||
glm::dmat4 projectionMatrix = glm::dmat4(data.camera.projectionMatrix());
|
||||
const glm::dmat4 modelMatrix = glm::dmat4(1.0);
|
||||
const glm::dmat4 modelViewProjectionTransform =
|
||||
calcModelViewProjectionTransform(data, modelMatrix);
|
||||
|
||||
glm::dmat4 modelViewProjectionMatrix = projectionMatrix * modelViewMatrix;
|
||||
|
||||
glm::dvec3 cameraViewDirectionWorld = -data.camera.viewDirectionWorldSpace();
|
||||
glm::dvec3 cameraUpDirectionWorld = data.camera.lookUpVectorWorldSpace();
|
||||
const glm::dvec3 cameraViewDirectionWorld = -data.camera.viewDirectionWorldSpace();
|
||||
const glm::dvec3 cameraUpDirectionWorld = data.camera.lookUpVectorWorldSpace();
|
||||
glm::dvec3 orthoRight = glm::normalize(
|
||||
glm::cross(cameraUpDirectionWorld, cameraViewDirectionWorld)
|
||||
);
|
||||
if (orthoRight == glm::dvec3(0.0)) {
|
||||
glm::dvec3 otherVector(
|
||||
const glm::dvec3 otherVector = glm::dvec3(
|
||||
cameraUpDirectionWorld.y,
|
||||
cameraUpDirectionWorld.x,
|
||||
cameraUpDirectionWorld.z
|
||||
);
|
||||
orthoRight = glm::normalize(glm::cross(otherVector, cameraViewDirectionWorld));
|
||||
}
|
||||
glm::dvec3 orthoUp = glm::normalize(glm::cross(cameraViewDirectionWorld, orthoRight));
|
||||
const glm::dvec3 orthoUp = glm::normalize(
|
||||
glm::cross(cameraViewDirectionWorld, orthoRight)
|
||||
);
|
||||
|
||||
renderLabels(data, modelViewProjectionMatrix, orthoRight, orthoUp, fadeInVariable);
|
||||
renderLabels(data, modelViewProjectionTransform, orthoRight, orthoUp, fadeInVariable);
|
||||
|
||||
global::renderEngine->openglStateCache().resetBlendState();
|
||||
global::renderEngine->openglStateCache().resetDepthState();
|
||||
@@ -450,7 +452,7 @@ void RenderableLabel::renderLabels(const RenderData& data,
|
||||
labelInfo.enableFalseDepth = false;
|
||||
|
||||
// We don't use spice rotation and scale
|
||||
glm::vec3 transformedPos(
|
||||
const glm::vec3 transformedPos = glm::vec3(
|
||||
_transformationMatrix * glm::dvec4(data.modelTransform.translation, 1.0)
|
||||
);
|
||||
|
||||
@@ -464,62 +466,62 @@ void RenderableLabel::renderLabels(const RenderData& data,
|
||||
}
|
||||
|
||||
float RenderableLabel::computeFadeFactor(float distanceNodeToCamera) const {
|
||||
float distanceUnit = unit(_fadeUnitOption);
|
||||
const float distanceUnit = unit(_fadeUnitOption);
|
||||
|
||||
float x = distanceNodeToCamera;
|
||||
float startX = _fadeDistances.value().x * distanceUnit;
|
||||
float endX = _fadeDistances.value().y * distanceUnit;
|
||||
const float x = distanceNodeToCamera;
|
||||
const float startX = _fadeDistances.value().x * distanceUnit;
|
||||
const float endX = _fadeDistances.value().y * distanceUnit;
|
||||
|
||||
// The distances over which the fading should happen
|
||||
float fadingStartDistance = _fadeWidths.value().x * distanceUnit;
|
||||
float fadingEndDistance = _fadeWidths.value().y * distanceUnit;
|
||||
const float fadingStartDistance = _fadeWidths.value().x * distanceUnit;
|
||||
const float fadingEndDistance = _fadeWidths.value().y * distanceUnit;
|
||||
|
||||
if (x <= startX) {
|
||||
float f1 = 1.f - (startX - x) / fadingStartDistance;
|
||||
const float f1 = 1.f - (startX - x) / fadingStartDistance;
|
||||
return std::clamp(f1, 0.f, 1.f);
|
||||
}
|
||||
else if (x > startX && x < endX) {
|
||||
return 1.f; // not faded
|
||||
}
|
||||
else { // x >= endX
|
||||
float f2 = 1.f - (x - endX) / fadingEndDistance;
|
||||
const float f2 = 1.f - (x - endX) / fadingEndDistance;
|
||||
return std::clamp(f2, 0.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
float RenderableLabel::unit(int unit) const {
|
||||
switch (static_cast<Unit>(unit)) {
|
||||
case Meter: return 1.f;
|
||||
case Kilometer: return 1e3f;
|
||||
case Megameter: return 1e6f;
|
||||
case Gigameter: return 1e9f;
|
||||
case Meter: return 1.f;
|
||||
case Kilometer: return 1e3f;
|
||||
case Megameter: return 1e6f;
|
||||
case Gigameter: return 1e9f;
|
||||
case AstronomicalUnit: return 149597870700.f;
|
||||
case Terameter: return 1e12f;
|
||||
case Petameter: return 1e15f;
|
||||
case Parsec: return static_cast<float>(PARSEC);
|
||||
case KiloParsec: return static_cast<float>(1e3 * PARSEC);
|
||||
case MegaParsec: return static_cast<float>(1e6 * PARSEC);
|
||||
case GigaParsec: return static_cast<float>(1e9 * PARSEC);
|
||||
case GigaLightyear: return static_cast<float>(306391534.73091 * PARSEC);
|
||||
default: throw std::logic_error("Missing case label");
|
||||
case Terameter: return 1e12f;
|
||||
case Petameter: return 1e15f;
|
||||
case Parsec: return static_cast<float>(PARSEC);
|
||||
case KiloParsec: return static_cast<float>(1e3 * PARSEC);
|
||||
case MegaParsec: return static_cast<float>(1e6 * PARSEC);
|
||||
case GigaParsec: return static_cast<float>(1e9 * PARSEC);
|
||||
case GigaLightyear: return static_cast<float>(306391534.73091 * PARSEC);
|
||||
default: throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view RenderableLabel::toString(int unit) const {
|
||||
switch (static_cast<Unit>(unit)) {
|
||||
case Meter: return MeterUnit;
|
||||
case Kilometer: return KilometerUnit;
|
||||
case Megameter: return MegameterUnit;
|
||||
case Gigameter: return GigameterUnit;
|
||||
case Meter: return MeterUnit;
|
||||
case Kilometer: return KilometerUnit;
|
||||
case Megameter: return MegameterUnit;
|
||||
case Gigameter: return GigameterUnit;
|
||||
case AstronomicalUnit: return AstronomicalUnitUnit;
|
||||
case Terameter: return TerameterUnit;
|
||||
case Petameter: return PetameterUnit;
|
||||
case Parsec: return ParsecUnit;
|
||||
case KiloParsec: return KiloparsecUnit;
|
||||
case MegaParsec: return MegaparsecUnit;
|
||||
case GigaParsec: return GigaparsecUnit;
|
||||
case GigaLightyear: return GigalightyearUnit;
|
||||
default: throw std::logic_error("Missing case label");
|
||||
case Terameter: return TerameterUnit;
|
||||
case Petameter: return PetameterUnit;
|
||||
case Parsec: return ParsecUnit;
|
||||
case KiloParsec: return KiloparsecUnit;
|
||||
case MegaParsec: return MegaparsecUnit;
|
||||
case GigaParsec: return GigaparsecUnit;
|
||||
case GigaLightyear: return GigalightyearUnit;
|
||||
default: throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/vector/ivec2property.h>
|
||||
@@ -56,7 +56,7 @@ struct LinePoint;
|
||||
|
||||
class RenderableLabel : public Renderable {
|
||||
public:
|
||||
RenderableLabel(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableLabel(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initialize() override;
|
||||
void initializeGL() override;
|
||||
@@ -73,6 +73,8 @@ public:
|
||||
protected:
|
||||
properties::OptionProperty _blendMode;
|
||||
|
||||
properties::StringProperty _text;
|
||||
|
||||
float unit(int unit) const;
|
||||
|
||||
std::string_view toString(int unit) const;
|
||||
@@ -92,8 +94,6 @@ private:
|
||||
properties::FloatProperty _size;
|
||||
properties::IVec2Property _minMaxSize;
|
||||
|
||||
properties::StringProperty _text;
|
||||
|
||||
properties::BoolProperty _enableFadingEffect;
|
||||
properties::Vec2Property _fadeWidths;
|
||||
properties::Vec2Property _fadeDistances;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -26,14 +26,13 @@
|
||||
#define __OPENSPACE_MODULE_BASE___RENDERABLEMODEL___H__
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/matrix/dmat4property.h>
|
||||
#include <openspace/properties/matrix/mat3property.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/misc/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <openspace/util/distanceconversion.h>
|
||||
#include <ghoul/misc/managedmemoryuniqueptr.h>
|
||||
#include <ghoul/io/model/modelreader.h>
|
||||
#include <ghoul/opengl/uniformcache.h>
|
||||
@@ -56,7 +55,7 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class RenderableModel : public Renderable {
|
||||
public:
|
||||
RenderableModel(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableModel(const ghoul::Dictionary& dictionary);
|
||||
~RenderableModel() override = default;
|
||||
|
||||
void initialize() override;
|
||||
@@ -79,13 +78,15 @@ private:
|
||||
BounceInfinitely
|
||||
};
|
||||
|
||||
std::filesystem::path _file;
|
||||
std::unique_ptr<ghoul::modelgeometry::ModelGeometry> _geometry;
|
||||
double _modelScale = 1.0;
|
||||
bool _invertModelScale = false;
|
||||
bool _forceRenderInvisible = false;
|
||||
bool _notifyInvisibleDropped = true;
|
||||
bool _modelHasAnimation = false;
|
||||
std::string _animationStart;
|
||||
AnimationMode _animationMode = AnimationMode::Once;
|
||||
double _animationTimeScale = 1.0;
|
||||
properties::BoolProperty _enableAnimation;
|
||||
|
||||
properties::FloatProperty _ambientIntensity;
|
||||
@@ -93,21 +94,26 @@ private:
|
||||
properties::FloatProperty _specularIntensity;
|
||||
|
||||
properties::BoolProperty _performShading;
|
||||
properties::BoolProperty _disableFaceCulling;
|
||||
properties::BoolProperty _enableFaceCulling;
|
||||
properties::DMat4Property _modelTransform;
|
||||
properties::Vec3Property _pivot;
|
||||
properties::DoubleProperty _modelScale;
|
||||
properties::Vec3Property _rotationVec;
|
||||
|
||||
properties::BoolProperty _disableDepthTest;
|
||||
properties::BoolProperty _enableOpacityBlending;
|
||||
properties::BoolProperty _enableDepthTest;
|
||||
properties::OptionProperty _blendingFuncOption;
|
||||
|
||||
std::string _vertexShaderPath;
|
||||
std::string _fragmentShaderPath;
|
||||
std::filesystem::path _vertexShaderPath;
|
||||
std::filesystem::path _fragmentShaderPath;
|
||||
ghoul::opengl::ProgramObject* _program = nullptr;
|
||||
UniformCache(opacity, nLightSources, lightDirectionsViewSpace, lightIntensities,
|
||||
modelViewTransform, normalTransform, projectionTransform,
|
||||
performShading, ambientIntensity, diffuseIntensity,
|
||||
specularIntensity, opacityBlending) _uniformCache;
|
||||
UniformCache(modelViewTransform, projectionTransform, normalTransform, meshTransform,
|
||||
meshNormalTransform, ambientIntensity, diffuseIntensity,
|
||||
specularIntensity, performShading, use_forced_color, has_texture_diffuse,
|
||||
has_texture_normal, has_texture_specular, has_color_specular,
|
||||
texture_diffuse, texture_normal, texture_specular, color_diffuse,
|
||||
color_specular, opacity, nLightSources, lightDirectionsViewSpace,
|
||||
lightIntensities, performManualDepthTest, gBufferDepthTexture, resolution
|
||||
) _uniformCache;
|
||||
|
||||
std::vector<std::unique_ptr<LightSource>> _lightSources;
|
||||
|
||||
@@ -116,6 +122,20 @@ private:
|
||||
std::vector<glm::vec3> _lightDirectionsViewSpaceBuffer;
|
||||
|
||||
properties::PropertyOwner _lightSourcePropertyOwner;
|
||||
|
||||
// Framebuffer and screen space quad
|
||||
GLuint _framebuffer = 0;
|
||||
GLuint _quadVao = 0;
|
||||
GLuint _quadVbo = 0;
|
||||
bool _shouldRenderTwice = false;
|
||||
|
||||
// Opacity program
|
||||
ghoul::opengl::ProgramObject* _quadProgram = nullptr;
|
||||
UniformCache(opacity, colorTexture, depthTexture, viewport,
|
||||
resolution) _uniformOpacityCache;
|
||||
|
||||
// Store the original RenderBin
|
||||
Renderable::RenderBin _originalRenderBin;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
529
modules/base/rendering/renderablenodearrow.cpp
Normal file
529
modules/base/rendering/renderablenodearrow.cpp
Normal file
@@ -0,0 +1,529 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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/renderablenodearrow.h>
|
||||
|
||||
#include <modules/base/basemodule.h>
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/navigation/orbitalnavigator.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <openspace/rendering/helper.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scene/translation.h>
|
||||
#include <openspace/util/updatestructures.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/opengl/openglstatecache.h>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
#include <glm/gtx/projection.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "RenderableNodeArrow";
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo StartNodeInfo = {
|
||||
"StartNode",
|
||||
"Start Node",
|
||||
"The identifier of the node the arrow starts from.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo EndNodeInfo = {
|
||||
"EndNode",
|
||||
"End Node",
|
||||
"The identifier of the node the arrow should point towards.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
||||
"Color",
|
||||
"Color",
|
||||
"The RGB color for the arrow.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
|
||||
"Segments",
|
||||
"Number of Segments",
|
||||
"The number of segments that the shapes of the arrow are divided into. A higher "
|
||||
"number leads to a higher resolution and smoother shape.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InvertInfo = {
|
||||
"Invert",
|
||||
"Invert Direction",
|
||||
"If true, the arrow direction is inverted so that it points to the start node "
|
||||
"instead of the end node.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ArrowHeadSizeInfo = {
|
||||
"ArrowHeadSize",
|
||||
"Arrow Head Size",
|
||||
"The length of the arrow head, given in relative value of the entire length of "
|
||||
"the arrow. For example, 0.1 makes the arrow head length be 10% of the full "
|
||||
"arrow length.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ArrowHeadWidthInfo = {
|
||||
"ArrowHeadWidthFactor",
|
||||
"Arrow Head Width Factor",
|
||||
"A factor that is multiplied with the width, or the arrow itself, to determine "
|
||||
"the width of the base of the arrow head.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo OffsetDistanceInfo = {
|
||||
"Offset",
|
||||
"Offset Distance",
|
||||
"The distance from the center of the start node where the arrow starts. "
|
||||
"If 'UseRelativeOffset' is true, the value should be given as a factor to "
|
||||
"multiply with the bounding sphere of the node. Otherwise, the value is "
|
||||
"specified in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RelativeOffsetInfo = {
|
||||
"UseRelativeOffset",
|
||||
"Use Relative Offset Distance",
|
||||
"Decides whether to use relative distances for the offset distance. This means "
|
||||
"that the offset distance will be computed as the provided 'Offset' value times "
|
||||
"the bounding sphere of the start node. If false, meters is used.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LengthInfo = {
|
||||
"Length",
|
||||
"Length",
|
||||
"The length of the arrow, given either in meters or as a factor to be "
|
||||
"multiplied with the bounding sphere of the start node (if "
|
||||
"'UseRelativeLength' is true).",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RelativeLengthInfo = {
|
||||
"UseRelativeLength",
|
||||
"Use Relative Length",
|
||||
"Decides whether to use relative size for the length of the arrow. This means "
|
||||
"that the arrow length will be computed as the provided 'Length' value times "
|
||||
"the bounding sphere of the start node. If false, meters is used.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo WidthInfo = {
|
||||
"Width",
|
||||
"Width",
|
||||
"The width of the arrow, in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo AmbientIntensityInfo = {
|
||||
"AmbientIntensity",
|
||||
"Ambient Intensity",
|
||||
"A multiplier for ambient lighting for the shading of the arrow.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DiffuseIntensityInfo = {
|
||||
"DiffuseIntensity",
|
||||
"Diffuse Intensity",
|
||||
"A multiplier for diffuse lighting for the shading of the arrow.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SpecularIntensityInfo = {
|
||||
"SpecularIntensity",
|
||||
"Specular Intensity",
|
||||
"A multiplier for specular lighting for the shading of the arrow.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShadingEnabledInfo = {
|
||||
"PerformShading",
|
||||
"Perform Shading",
|
||||
"Determines whether shading should be applied to the arrow model.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
void updateDistanceBasedOnRelativeValues(const std::string& nodeName,
|
||||
bool useRelative,
|
||||
openspace::properties::FloatProperty& prop)
|
||||
{
|
||||
using namespace::openspace;
|
||||
|
||||
SceneGraphNode* startNode = sceneGraphNode(nodeName);
|
||||
if (!startNode) {
|
||||
LERROR(std::format("Could not find start node '{}'", nodeName));
|
||||
return;
|
||||
}
|
||||
const double boundingSphere = startNode->boundingSphere();
|
||||
|
||||
if (!useRelative) {
|
||||
// Recompute distance (previous value was relative)
|
||||
prop = static_cast<float>(prop * boundingSphere);
|
||||
prop.setExponent(11.f);
|
||||
prop.setMaxValue(1e20f);
|
||||
}
|
||||
else {
|
||||
// Recompute distance (previous value was in meters)
|
||||
if (boundingSphere < std::numeric_limits<double>::epsilon()) {
|
||||
LERROR(std::format(
|
||||
"Start node '{}' has invalid bounding sphere", nodeName
|
||||
));
|
||||
return;
|
||||
}
|
||||
prop = static_cast<float>(prop / boundingSphere);
|
||||
prop.setExponent(3.f);
|
||||
prop.setMaxValue(1000.f);
|
||||
}
|
||||
}
|
||||
|
||||
// A RenderableNodeArrow can be used to create a 3D arrow pointing in the direction
|
||||
// of one scene graph node to another.
|
||||
//
|
||||
// The arrow will be placed at the `StartNode` at a distance of the provided
|
||||
// `Offset` value. Per default, the `Length` and `Offset` of the arrow is specified
|
||||
// in meters, but they may also be specified as a multiplier of the bounding sphere
|
||||
// of the `StartNode`. The look of the arrow can be customized to change the width
|
||||
// and length of both the arrow body and head.
|
||||
struct [[codegen::Dictionary(RenderableNodeArrow)]] Parameters {
|
||||
// [[codegen::verbatim(StartNodeInfo.description)]]
|
||||
std::string startNode [[codegen::identifier()]];
|
||||
|
||||
// [[codegen::verbatim(EndNodeInfo.description)]]
|
||||
std::string endNode [[codegen::identifier()]];
|
||||
|
||||
// [[codegen::verbatim(ColorInfo.description)]]
|
||||
std::optional<glm::vec3> color [[codegen::color()]];
|
||||
|
||||
// [[codegen::verbatim(SegmentsInfo.description)]]
|
||||
std::optional<int> segments [[codegen::greaterequal(3)]];
|
||||
|
||||
// [[codegen::verbatim(InvertInfo.description)]]
|
||||
std::optional<bool> invert;
|
||||
|
||||
// [[codegen::verbatim(ArrowHeadSizeInfo.description)]]
|
||||
std::optional<float> arrowHeadSize [[codegen::greaterequal(0.f)]];
|
||||
|
||||
// [[codegen::verbatim(ArrowHeadWidthInfo.description)]]
|
||||
std::optional<float> arrowHeadWidthFactor [[codegen::greaterequal(0.f)]];
|
||||
|
||||
// [[codegen::verbatim(OffsetDistanceInfo.description)]]
|
||||
std::optional<float> offset;
|
||||
|
||||
// [[codegen::verbatim(RelativeOffsetInfo.description)]]
|
||||
std::optional<bool> useRelativeOffset;
|
||||
|
||||
// [[codegen::verbatim(LengthInfo.description)]]
|
||||
std::optional<float> length [[codegen::greaterequal(0.f)]];
|
||||
|
||||
// [[codegen::verbatim(RelativeLengthInfo.description)]]
|
||||
std::optional<bool> useRelativeLength;
|
||||
|
||||
// [[codegen::verbatim(WidthInfo.description)]]
|
||||
std::optional<float> width [[codegen::greaterequal(0.f)]];
|
||||
|
||||
// [[codegen::verbatim(ShadingEnabledInfo.description)]]
|
||||
std::optional<bool> performShading;
|
||||
|
||||
// [[codegen::verbatim(AmbientIntensityInfo.description)]]
|
||||
std::optional<float> ambientIntensity [[codegen::greaterequal(0.f)]];
|
||||
|
||||
// [[codegen::verbatim(DiffuseIntensityInfo.description)]]
|
||||
std::optional<float> diffuseIntensity [[codegen::greaterequal(0.f)]];
|
||||
|
||||
// [[codegen::verbatim(SpecularIntensityInfo.description)]]
|
||||
std::optional<float> specularIntensity [[codegen::greaterequal(0.f)]];
|
||||
};
|
||||
#include "renderablenodearrow_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation RenderableNodeArrow::Documentation() {
|
||||
return codegen::doc<Parameters>("base_renderable_renderablenodearrow");
|
||||
}
|
||||
|
||||
RenderableNodeArrow::Shading::Shading()
|
||||
: properties::PropertyOwner({ "Shading" })
|
||||
, enabled(ShadingEnabledInfo, true)
|
||||
, ambientIntensity(AmbientIntensityInfo, 0.2f, 0.f, 1.f)
|
||||
, diffuseIntensity(DiffuseIntensityInfo, 0.7f, 0.f, 1.f)
|
||||
, specularIntensity(SpecularIntensityInfo, 0.f, 0.f, 1.f)
|
||||
{
|
||||
addProperty(enabled);
|
||||
addProperty(ambientIntensity);
|
||||
addProperty(diffuseIntensity);
|
||||
addProperty(specularIntensity);
|
||||
}
|
||||
|
||||
RenderableNodeArrow::RenderableNodeArrow(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary)
|
||||
, _start(StartNodeInfo)
|
||||
, _end(EndNodeInfo)
|
||||
, _color(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
|
||||
, _segments(SegmentsInfo, 10, 3, 100)
|
||||
, _invertArrowDirection(InvertInfo, false)
|
||||
, _arrowHeadSize(ArrowHeadSizeInfo, 0.1f, 0.f, 1.f)
|
||||
, _arrowHeadWidthFactor(ArrowHeadWidthInfo, 2.f, 1.f, 100.f)
|
||||
, _offsetDistance(OffsetDistanceInfo, 0.f, 0.f, 1e20f)
|
||||
, _useRelativeOffset(RelativeOffsetInfo, false)
|
||||
, _length(LengthInfo, 100.f, 0.f, 1e20f)
|
||||
, _useRelativeLength(RelativeLengthInfo, false)
|
||||
, _width(WidthInfo, 10.f, 0.f, 1e11f)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_shading.enabled = p.performShading.value_or(_shading.enabled);
|
||||
_shading.ambientIntensity = p.ambientIntensity.value_or(_shading.ambientIntensity);
|
||||
_shading.diffuseIntensity = p.diffuseIntensity.value_or(_shading.diffuseIntensity);
|
||||
_shading.specularIntensity = p.specularIntensity.value_or(_shading.specularIntensity);
|
||||
addPropertySubOwner(_shading);
|
||||
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_color = p.color.value_or(_color);
|
||||
_color.setViewOption(properties::Property::ViewOptions::Color);
|
||||
addProperty(_color);
|
||||
|
||||
_start = p.startNode;
|
||||
addProperty(_start);
|
||||
|
||||
_end = p.endNode;
|
||||
addProperty(_end);
|
||||
|
||||
_segments = p.segments.value_or(_segments);
|
||||
addProperty(_segments);
|
||||
|
||||
_invertArrowDirection = p.invert.value_or(_invertArrowDirection);
|
||||
addProperty(_invertArrowDirection);
|
||||
|
||||
_arrowHeadSize = p.arrowHeadSize.value_or(_arrowHeadSize);
|
||||
addProperty(_arrowHeadSize);
|
||||
|
||||
_arrowHeadWidthFactor = p.arrowHeadWidthFactor.value_or(_arrowHeadWidthFactor);
|
||||
addProperty(_arrowHeadWidthFactor);
|
||||
|
||||
_width = p.width.value_or(_width);
|
||||
_width.setExponent(10.f);
|
||||
addProperty(_width);
|
||||
|
||||
_useRelativeLength.onChange([this]() {
|
||||
updateDistanceBasedOnRelativeValues(_start, _useRelativeLength, _length);
|
||||
});
|
||||
_useRelativeLength = p.useRelativeLength.value_or(_useRelativeLength);
|
||||
|
||||
_length = p.length.value_or(_length);
|
||||
if (!_useRelativeLength) {
|
||||
_length.setExponent(11.f);
|
||||
}
|
||||
addProperty(_length);
|
||||
|
||||
_useRelativeOffset.onChange([this]() {
|
||||
updateDistanceBasedOnRelativeValues(_start, _useRelativeOffset, _offsetDistance);
|
||||
});
|
||||
_useRelativeOffset = p.useRelativeOffset.value_or(_useRelativeOffset);
|
||||
|
||||
_offsetDistance = p.offset.value_or(_offsetDistance);
|
||||
if (!_useRelativeOffset) {
|
||||
_offsetDistance.setExponent(11.f);
|
||||
}
|
||||
addProperty(_offsetDistance);
|
||||
|
||||
addProperty(_useRelativeLength);
|
||||
addProperty(_useRelativeOffset);
|
||||
}
|
||||
|
||||
void RenderableNodeArrow::initializeGL() {
|
||||
_shaderProgram = BaseModule::ProgramObjectManager.request(
|
||||
"NodeDirectionLineProgram",
|
||||
[]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
|
||||
return global::renderEngine->buildRenderProgram(
|
||||
"NodeDirectionLineProgram",
|
||||
absPath("${MODULE_BASE}/shaders/arrow_vs.glsl"),
|
||||
absPath("${MODULE_BASE}/shaders/arrow_fs.glsl")
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void RenderableNodeArrow::deinitializeGL() {
|
||||
BaseModule::ProgramObjectManager.release(
|
||||
"NodeDirectionLineProgram",
|
||||
[](ghoul::opengl::ProgramObject* p) {
|
||||
global::renderEngine->removeRenderProgram(p);
|
||||
}
|
||||
);
|
||||
_shaderProgram = nullptr;
|
||||
}
|
||||
|
||||
bool RenderableNodeArrow::isReady() const {
|
||||
return _shaderProgram != nullptr;
|
||||
}
|
||||
|
||||
void RenderableNodeArrow::updateShapeTransforms(const RenderData& data) {
|
||||
SceneGraphNode* startNode = sceneGraphNode(_start);
|
||||
SceneGraphNode* endNode = sceneGraphNode(_end);
|
||||
|
||||
if (!startNode) {
|
||||
LERROR(std::format("Could not find start node '{}'", _start.value()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!endNode) {
|
||||
LERROR(std::format("Could not find end node '{}'", _end.value()));
|
||||
return;
|
||||
}
|
||||
|
||||
const double boundingSphere = startNode->boundingSphere();
|
||||
const bool hasNoBoundingSphere =
|
||||
boundingSphere < std::numeric_limits<double>::epsilon();
|
||||
|
||||
if (hasNoBoundingSphere && (_useRelativeLength || _useRelativeOffset)) {
|
||||
LERROR(std::format(
|
||||
"Node '{}' has no valid bounding sphere. Can not use relative values",
|
||||
_end.value()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
double offset = static_cast<double>(_offsetDistance);
|
||||
if (_useRelativeOffset) {
|
||||
offset *= boundingSphere;
|
||||
}
|
||||
|
||||
double length = static_cast<double>(_length);
|
||||
if (_useRelativeLength) {
|
||||
length *= boundingSphere;
|
||||
}
|
||||
|
||||
// Take additional transformation scale into account
|
||||
const glm::dmat4 s = glm::scale(
|
||||
glm::dmat4(1.0),
|
||||
glm::dvec3(data.modelTransform.scale)
|
||||
);
|
||||
|
||||
// Update the position based on the arrowDirection of the nodes
|
||||
const glm::dvec3 startNodePos = startNode->worldPosition();
|
||||
const glm::dvec3 endNodePos = endNode->worldPosition();
|
||||
|
||||
glm::dvec3 arrowDirection = glm::normalize(endNodePos - startNodePos);
|
||||
glm::dvec3 startPos = glm::dvec3(startNodePos + offset * arrowDirection);
|
||||
glm::dvec3 endPos = glm::dvec3(startPos + length * arrowDirection);
|
||||
|
||||
if (_invertArrowDirection) {
|
||||
std::swap(startPos, endPos);
|
||||
arrowDirection *= -1.0;
|
||||
}
|
||||
|
||||
const double coneLength = _arrowHeadSize * length;
|
||||
const double cylinderLength = length - coneLength;
|
||||
const double arrowHeadWidth = _width * _arrowHeadWidthFactor;
|
||||
|
||||
// Create transformation matrices to reshape to size and position
|
||||
_cylinderTranslation = glm::translate(glm::dmat4(1.0), startPos);
|
||||
const glm::dvec3 cylinderScale = glm::dvec3(
|
||||
s * glm::dvec4(_width, _width, cylinderLength, 0.0)
|
||||
);
|
||||
_cylinderScale = glm::scale(glm::dmat4(1.0), cylinderScale);
|
||||
|
||||
// Adapt arrow head start to scaled size
|
||||
const glm::dvec3 arrowHeadStartPos = startPos + cylinderScale.z * arrowDirection;
|
||||
|
||||
_coneTranslation = glm::translate(glm::dmat4(1.0), arrowHeadStartPos);
|
||||
const glm::dvec3 coneScale = glm::dvec3(arrowHeadWidth, arrowHeadWidth, coneLength);
|
||||
_coneScale = s * glm::scale(glm::dmat4(1.0), coneScale);
|
||||
|
||||
// Rotation to point at the end node
|
||||
const glm::quat rotQuat = glm::rotation(glm::dvec3(0.0, 0.0, 1.0), arrowDirection);
|
||||
_pointDirectionRotation = glm::dmat4(glm::toMat4(rotQuat));
|
||||
|
||||
setBoundingSphere(length + offset);
|
||||
}
|
||||
|
||||
void RenderableNodeArrow::render(const RenderData& data, RendererTasks&) {
|
||||
updateShapeTransforms(data);
|
||||
|
||||
// Cylinder transforms
|
||||
glm::dmat4 modelTransform =
|
||||
_cylinderTranslation * _pointDirectionRotation * _cylinderScale;
|
||||
glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform;
|
||||
glm::dmat4 normalTransform = glm::transpose(glm::inverse(modelViewTransform));
|
||||
|
||||
_shaderProgram->activate();
|
||||
|
||||
_shaderProgram->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
|
||||
_shaderProgram->setUniform("projectionTransform", data.camera.projectionMatrix());
|
||||
_shaderProgram->setUniform("normalTransform", glm::mat3(normalTransform));
|
||||
|
||||
_shaderProgram->setUniform("color", _color);
|
||||
_shaderProgram->setUniform("opacity", opacity());
|
||||
|
||||
_shaderProgram->setUniform("ambientIntensity", _shading.ambientIntensity);
|
||||
_shaderProgram->setUniform("diffuseIntensity", _shading.diffuseIntensity);
|
||||
_shaderProgram->setUniform("specularIntensity", _shading.specularIntensity);
|
||||
_shaderProgram->setUniform("performShading", _shading.enabled);
|
||||
|
||||
// Change GL state:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnablei(GL_BLEND, 0);
|
||||
|
||||
// Draw cylinder
|
||||
glBindVertexArray(rendering::helper::vertexObjects.cylinder.vao);
|
||||
glDrawElements(
|
||||
GL_TRIANGLES,
|
||||
rendering::helper::vertexObjects.cylinder.nElements,
|
||||
GL_UNSIGNED_SHORT,
|
||||
nullptr
|
||||
);
|
||||
|
||||
// Update transforms and render cone
|
||||
modelTransform = _coneTranslation * _pointDirectionRotation * _coneScale;
|
||||
modelViewTransform = data.camera.combinedViewMatrix() * modelTransform;
|
||||
normalTransform = glm::transpose(glm::inverse(modelViewTransform));
|
||||
|
||||
_shaderProgram->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
|
||||
_shaderProgram->setUniform("normalTransform", glm::mat3(normalTransform));
|
||||
|
||||
glBindVertexArray(rendering::helper::vertexObjects.cone.vao);
|
||||
glDrawElements(
|
||||
GL_TRIANGLES,
|
||||
rendering::helper::vertexObjects.cone.nElements,
|
||||
GL_UNSIGNED_SHORT,
|
||||
nullptr
|
||||
);
|
||||
|
||||
// Restore GL State
|
||||
glBindVertexArray(0);
|
||||
global::renderEngine->openglStateCache().resetBlendState();
|
||||
|
||||
_shaderProgram->deactivate();
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
103
modules/base/rendering/renderablenodearrow.h
Normal file
103
modules/base/rendering/renderablenodearrow.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_BASE___RENDERABLENODEARROW___H__
|
||||
#define __OPENSPACE_MODULE_BASE___RENDERABLENODEARROW___H__
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/uintproperty.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
namespace ghoul::opengl { class ProgramObject; }
|
||||
|
||||
namespace openspace {
|
||||
|
||||
namespace documentation { struct Documentation; }
|
||||
class Translation;
|
||||
|
||||
/**
|
||||
* Generates an arrow shape that points from the start node to the end node.
|
||||
*/
|
||||
class RenderableNodeArrow : public Renderable {
|
||||
public:
|
||||
explicit RenderableNodeArrow(const ghoul::Dictionary& dictionary);
|
||||
~RenderableNodeArrow() override = default;
|
||||
|
||||
void initializeGL() override;
|
||||
void deinitializeGL() override;
|
||||
|
||||
bool isReady() const override;
|
||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
private:
|
||||
struct Shading : properties::PropertyOwner {
|
||||
Shading();
|
||||
|
||||
properties::BoolProperty enabled;
|
||||
properties::FloatProperty ambientIntensity;
|
||||
properties::FloatProperty diffuseIntensity;
|
||||
properties::FloatProperty specularIntensity;
|
||||
};
|
||||
|
||||
void updateShapeTransforms(const RenderData& data);
|
||||
|
||||
Shading _shading;
|
||||
|
||||
ghoul::opengl::ProgramObject* _shaderProgram = nullptr;
|
||||
|
||||
properties::StringProperty _start;
|
||||
properties::StringProperty _end;
|
||||
properties::Vec3Property _color;
|
||||
|
||||
properties::UIntProperty _segments;
|
||||
properties::BoolProperty _invertArrowDirection;
|
||||
|
||||
properties::FloatProperty _arrowHeadSize;
|
||||
properties::FloatProperty _arrowHeadWidthFactor;
|
||||
|
||||
properties::FloatProperty _offsetDistance;
|
||||
properties::BoolProperty _useRelativeOffset;
|
||||
properties::FloatProperty _length;
|
||||
properties::BoolProperty _useRelativeLength;
|
||||
properties::FloatProperty _width;
|
||||
|
||||
glm::dmat4 _cylinderTranslation = glm::dmat4(1.0);
|
||||
glm::dmat4 _cylinderScale = glm::dmat4(1.0);
|
||||
|
||||
glm::dmat4 _coneTranslation = glm::dmat4(1.0);
|
||||
glm::dmat4 _coneScale = glm::dmat4(1.0);
|
||||
|
||||
glm::dmat4 _pointDirectionRotation = glm::dmat4(1.0);
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_BASE___RENDERABLENODEARROW___H__
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -39,30 +39,63 @@
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
|
||||
namespace {
|
||||
constexpr std::string_view _loggerCat = "RenderableNodeLine";
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo StartNodeInfo = {
|
||||
"StartNode",
|
||||
"Start Node",
|
||||
"The identifier of the node the line starts from. "
|
||||
"Defaults to 'Root' if not specified. "
|
||||
"The identifier of the node the line starts from. Defaults to 'Root' if not "
|
||||
"specified.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo EndNodeInfo = {
|
||||
"EndNode",
|
||||
"End Node",
|
||||
"The identifier of the node the line ends at. "
|
||||
"Defaults to 'Root' if not specified. "
|
||||
"The identifier of the node the line ends at. Defaults to 'Root' if not "
|
||||
"specified.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LineColorInfo = {
|
||||
"Color",
|
||||
"Color",
|
||||
"This value determines the RGB color for the line"
|
||||
"The RGB color for the line.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
||||
"LineWidth",
|
||||
"Line Width",
|
||||
"This value specifies the line width"
|
||||
"The width of the line. The larger number, the thicker the line.",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo StartOffsetInfo = {
|
||||
"StartOffset",
|
||||
"Offset to Start Node",
|
||||
"A distance from the start node at which the rendered line should begin. "
|
||||
"By default it takes a value in meters, but if 'UseRelativeOffsets' is set "
|
||||
"to true it is read as a multiplier times the bounding sphere of the node.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo EndOffsetInfo = {
|
||||
"EndOffset",
|
||||
"Offset to End Node",
|
||||
"A distance to the end node at which the rendered line should end. "
|
||||
"By default it takes a value in meters, but if 'UseRelativeOffsets' is set "
|
||||
"to true it is read as a multiplier times the bounding sphere of the node.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RelativeOffsetsInfo = {
|
||||
"UseRelativeOffsets",
|
||||
"Use Relative Offsets",
|
||||
"If true, the offset values are interpreted as relative values to be multiplied "
|
||||
"with the bounding sphere of the start/end node. If false, the value is "
|
||||
"interpreted as a distance in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
// Returns a position that is relative to the current anchor node. This is a method to
|
||||
@@ -77,22 +110,38 @@ namespace {
|
||||
if (nav.anchorNode()) {
|
||||
anchorNodePos = nav.anchorNode()->worldPosition();
|
||||
}
|
||||
glm::dvec3 diffPos = worldPos - anchorNodePos;
|
||||
const glm::dvec3 diffPos = worldPos - anchorNodePos;
|
||||
return diffPos;
|
||||
}
|
||||
|
||||
// This `Renderable` connects two scene graph nodes by drawing a line between them.
|
||||
// The line will update dynamically if the position of the nodes change.
|
||||
//
|
||||
// One use case for the `RenderableNodeLine` is to visualize the distance between two
|
||||
// objects. For this, a [RenderableDistanceLabel](#base_renderable_distancelabel) can
|
||||
// also be added to show the distance as a number. That renderable is designed to show
|
||||
// the distance between the start and end node for a given `RenderableNodeLine`.
|
||||
struct [[codegen::Dictionary(RenderableNodeLine)]] Parameters {
|
||||
// [[codegen::verbatim(StartNodeInfo.description)]]
|
||||
std::optional<std::string> startNode;
|
||||
std::optional<std::string> startNode [[codegen::identifier()]];
|
||||
|
||||
// [[codegen::verbatim(EndNodeInfo.description)]]
|
||||
std::optional<std::string> endNode;
|
||||
std::optional<std::string> endNode [[codegen::identifier()]];
|
||||
|
||||
// [[codegen::verbatim(LineColorInfo.description)]]
|
||||
std::optional<glm::vec3> color [[codegen::color()]];
|
||||
|
||||
// [[codegen::verbatim(LineWidthInfo.description)]]
|
||||
std::optional<float> lineWidth;
|
||||
|
||||
// [[codegen::verbatim(StartOffsetInfo.description)]]
|
||||
std::optional<float> startOffset;
|
||||
|
||||
// [[codegen::verbatim(EndOffsetInfo.description)]]
|
||||
std::optional<float> endOffset;
|
||||
|
||||
// [[codegen::verbatim(RelativeOffsetsInfo.description)]]
|
||||
std::optional<bool> useRelativeOffsets;
|
||||
};
|
||||
#include "renderablenodeline_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -100,7 +149,7 @@ namespace {
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation RenderableNodeLine::Documentation() {
|
||||
return codegen::doc<Parameters>("base_renderable_renderablenodeline");
|
||||
return codegen::doc<Parameters>("base_renderable_nodeline");
|
||||
}
|
||||
|
||||
RenderableNodeLine::RenderableNodeLine(const ghoul::Dictionary& dictionary)
|
||||
@@ -109,14 +158,13 @@ RenderableNodeLine::RenderableNodeLine(const ghoul::Dictionary& dictionary)
|
||||
, _end(EndNodeInfo, "Root")
|
||||
, _lineColor(LineColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
|
||||
, _lineWidth(LineWidthInfo, 2.f, 1.f, 20.f)
|
||||
, _startOffset(StartOffsetInfo, 0.f, 0.f, 1e13f)
|
||||
, _endOffset(EndOffsetInfo, 0.f, 0.f, 1e13f)
|
||||
, _useRelativeOffsets(RelativeOffsetsInfo, true)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
_start = p.startNode.value_or(_start);
|
||||
addProperty(_start);
|
||||
|
||||
_end = p.endNode.value_or(_end);
|
||||
addProperty(_end);
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_lineColor = p.color.value_or(_lineColor);
|
||||
_lineColor.setViewOption(properties::Property::ViewOptions::Color);
|
||||
@@ -125,7 +173,82 @@ RenderableNodeLine::RenderableNodeLine(const ghoul::Dictionary& dictionary)
|
||||
_lineWidth = p.lineWidth.value_or(_lineWidth);
|
||||
addProperty(_lineWidth);
|
||||
|
||||
addProperty(_opacity);
|
||||
_start = p.startNode.value_or(_start);
|
||||
addProperty(_start);
|
||||
|
||||
_end = p.endNode.value_or(_end);
|
||||
addProperty(_end);
|
||||
|
||||
addProperty(_startOffset);
|
||||
_startOffset.setExponent(7.f);
|
||||
_startOffset.onChange([&] {
|
||||
if (_useRelativeOffsets) {
|
||||
SceneGraphNode* node = global::renderEngine->scene()->sceneGraphNode(_start);
|
||||
if (!node || node->boundingSphere() > 0.0) {
|
||||
return;
|
||||
}
|
||||
LWARNING(std::format(
|
||||
"Setting StartOffset for node line '{}': Trying to use relative offsets "
|
||||
"for start node '{}' that has no bounding sphere. This will result in no "
|
||||
"offset. Use direct values by setting UseRelativeOffsets to false",
|
||||
parent()->identifier(), _start.value()
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
addProperty(_endOffset);
|
||||
_endOffset.setExponent(7.f);
|
||||
_endOffset.onChange([&] {
|
||||
if (_useRelativeOffsets) {
|
||||
SceneGraphNode* node = global::renderEngine->scene()->sceneGraphNode(_end);
|
||||
if (!node || node->boundingSphere() > 0.0) {
|
||||
return;
|
||||
}
|
||||
LWARNING(std::format(
|
||||
"Setting EndOffset for node line '{}': Trying to use relative offsets "
|
||||
"for end node '{}' that has no bounding sphere. This will result in no "
|
||||
"offset. Use direct values by setting UseRelativeOffsets to false",
|
||||
parent()->identifier(), _end.value()
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
addProperty(_useRelativeOffsets);
|
||||
_useRelativeOffsets.onChange([this]() {
|
||||
SceneGraphNode* startNode = global::renderEngine->scene()->sceneGraphNode(_start);
|
||||
SceneGraphNode* endNode = global::renderEngine->scene()->sceneGraphNode(_end);
|
||||
|
||||
if (!startNode) {
|
||||
LERROR(std::format(
|
||||
"Error when recomputing node line offsets for scene graph node '{}'. "
|
||||
"Could not find start node '{}'", parent()->identifier(), _start.value()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!endNode) {
|
||||
LERROR(std::format(
|
||||
"Error when recomputing node line offsets for scene graph node '{}'. "
|
||||
"Could not find end node '{}'", parent()->identifier(), _end.value()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_useRelativeOffsets) {
|
||||
// Recompute previous offsets to relative values
|
||||
const double startBs = startNode->boundingSphere();
|
||||
const double endBs = endNode->boundingSphere();
|
||||
_startOffset =
|
||||
static_cast<float>(startBs > 0.0 ? _startOffset / startBs : 0.0);
|
||||
_endOffset =
|
||||
static_cast<float>(endBs > 0.0 ? _endOffset / startBs : 0.0);
|
||||
}
|
||||
else {
|
||||
// Recompute relative values to meters
|
||||
_startOffset = static_cast<float>(_startOffset * startNode->boundingSphere());
|
||||
_endOffset = static_cast<float>(_endOffset * endNode->boundingSphere());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
double RenderableNodeLine::distance() const {
|
||||
@@ -156,12 +279,14 @@ void RenderableNodeLine::initializeGL() {
|
||||
glGenVertexArrays(1, &_vaoId);
|
||||
glGenBuffers(1, &_vBufferId);
|
||||
|
||||
bindGL();
|
||||
glBindVertexArray(_vaoId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferId);
|
||||
|
||||
glVertexAttribPointer(_locVertex, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
|
||||
glEnableVertexAttribArray(_locVertex);
|
||||
|
||||
unbindGL();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void RenderableNodeLine::deinitializeGL() {
|
||||
@@ -186,29 +311,17 @@ bool RenderableNodeLine::isReady() const {
|
||||
return ready;
|
||||
}
|
||||
|
||||
void RenderableNodeLine::unbindGL() {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void RenderableNodeLine::bindGL() {
|
||||
glBindVertexArray(_vaoId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferId);
|
||||
}
|
||||
|
||||
void RenderableNodeLine::updateVertexData() {
|
||||
SceneGraphNode* startNode = global::renderEngine->scene()->sceneGraphNode(_start);
|
||||
SceneGraphNode* endNode = global::renderEngine->scene()->sceneGraphNode(_end);
|
||||
|
||||
if (!startNode || !endNode) {
|
||||
LERRORC(
|
||||
"RenderableNodeLine",
|
||||
fmt::format(
|
||||
"Could not find starting '{}' or ending '{}'",
|
||||
_start.value(), _end.value()
|
||||
)
|
||||
);
|
||||
if (!startNode) {
|
||||
LERROR(std::format("Could not find start node '{}'", _start.value()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!endNode) {
|
||||
LERROR(std::format("Could not find end node '{}'", _end.value()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -218,15 +331,29 @@ void RenderableNodeLine::updateVertexData() {
|
||||
_startPos = coordinatePosFromAnchorNode(startNode->worldPosition());
|
||||
_endPos = coordinatePosFromAnchorNode(endNode->worldPosition());
|
||||
|
||||
_vertexArray.push_back(static_cast<float>(_startPos.x));
|
||||
_vertexArray.push_back(static_cast<float>(_startPos.y));
|
||||
_vertexArray.push_back(static_cast<float>(_startPos.z));
|
||||
// Handle relative values
|
||||
double startOffset = static_cast<double>(_startOffset);
|
||||
double endOffset = static_cast<double>(_endOffset);
|
||||
if (_useRelativeOffsets) {
|
||||
startOffset *= startNode->boundingSphere();
|
||||
endOffset *= endNode->boundingSphere();
|
||||
}
|
||||
|
||||
_vertexArray.push_back(static_cast<float>(_endPos.x));
|
||||
_vertexArray.push_back(static_cast<float>(_endPos.y));
|
||||
_vertexArray.push_back(static_cast<float>(_endPos.z));
|
||||
// Compute line positions
|
||||
const glm::dvec3 dir = glm::normalize(_endPos - _startPos);
|
||||
const glm::dvec3 startPos = _startPos + startOffset * dir;
|
||||
const glm::dvec3 endPos = _endPos - endOffset * dir;
|
||||
|
||||
bindGL();
|
||||
_vertexArray.push_back(static_cast<float>(startPos.x));
|
||||
_vertexArray.push_back(static_cast<float>(startPos.y));
|
||||
_vertexArray.push_back(static_cast<float>(startPos.z));
|
||||
|
||||
_vertexArray.push_back(static_cast<float>(endPos.x));
|
||||
_vertexArray.push_back(static_cast<float>(endPos.y));
|
||||
_vertexArray.push_back(static_cast<float>(endPos.z));
|
||||
|
||||
glBindVertexArray(_vaoId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferId);
|
||||
glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
_vertexArray.size() * sizeof(float),
|
||||
@@ -237,7 +364,8 @@ void RenderableNodeLine::updateVertexData() {
|
||||
// update vertex attributes
|
||||
glVertexAttribPointer(_locVertex, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
|
||||
|
||||
unbindGL();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void RenderableNodeLine::update(const UpdateData&) {
|
||||
@@ -256,13 +384,9 @@ void RenderableNodeLine::render(const RenderData& data, RendererTasks&) {
|
||||
);
|
||||
}
|
||||
|
||||
const glm::dmat4 modelTransform =
|
||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
||||
glm::dmat4(data.modelTransform.rotation) *
|
||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));
|
||||
|
||||
const glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() *
|
||||
modelTransform * anchorTranslation;
|
||||
const glm::dmat4 modelTransform = calcModelTransform(data);
|
||||
const glm::dmat4 modelViewTransform =
|
||||
calcModelViewTransform(data, modelTransform) * anchorTranslation;
|
||||
|
||||
_program->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
|
||||
_program->setUniform("projectionTransform", data.camera.projectionMatrix());
|
||||
@@ -275,11 +399,13 @@ void RenderableNodeLine::render(const RenderData& data, RendererTasks&) {
|
||||
glLineWidth(_lineWidth);
|
||||
|
||||
// Bind and draw
|
||||
bindGL();
|
||||
glBindVertexArray(_vaoId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferId);
|
||||
glDrawArrays(GL_LINES, 0, 2);
|
||||
|
||||
// Restore GL State
|
||||
unbindGL();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
_program->deactivate();
|
||||
global::renderEngine->openglStateCache().resetBlendState();
|
||||
global::renderEngine->openglStateCache().resetLineState();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -45,7 +45,7 @@ class Translation;
|
||||
*/
|
||||
class RenderableNodeLine : public Renderable {
|
||||
public:
|
||||
RenderableNodeLine(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderableNodeLine(const ghoul::Dictionary& dictionary);
|
||||
~RenderableNodeLine() override = default;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
@@ -65,10 +65,7 @@ private:
|
||||
void update(const UpdateData& data) override;
|
||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
||||
|
||||
void unbindGL();
|
||||
void bindGL();
|
||||
|
||||
ghoul::opengl::ProgramObject* _program;
|
||||
ghoul::opengl::ProgramObject* _program = nullptr;
|
||||
/// The vertex attribute location for position
|
||||
/// must correlate to layout location in vertex shader
|
||||
const GLuint _locVertex = 0;
|
||||
@@ -83,6 +80,9 @@ private:
|
||||
properties::StringProperty _end;
|
||||
properties::Vec3Property _lineColor;
|
||||
properties::FloatProperty _lineWidth;
|
||||
properties::FloatProperty _startOffset;
|
||||
properties::FloatProperty _endOffset;
|
||||
properties::BoolProperty _useRelativeOffsets;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -29,18 +29,16 @@
|
||||
#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 {
|
||||
@@ -50,39 +48,55 @@ namespace {
|
||||
|
||||
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"
|
||||
"Billboard Mode",
|
||||
"Specifies whether the plane should be a billboard, which means that it is "
|
||||
"always facing the camera. If it is not, it can be oriented using other "
|
||||
"transformations.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
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"
|
||||
"Mirror Backside of Image Plane",
|
||||
"If false, the image plane will not be mirrored when viewed 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.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
|
||||
"Size",
|
||||
"Size (in meters)",
|
||||
"This value specifies the size of the plane in meters"
|
||||
"Size",
|
||||
"The size of the plane in meters.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo AutoScaleInfo = {
|
||||
"AutoScale",
|
||||
"Auto Scale",
|
||||
"Decides whether the plane should 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"
|
||||
"Determines the blending mode that is applied to this plane.",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
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"
|
||||
"An RGB color to multiply with the plane's texture. Useful for applying "
|
||||
"a color to grayscale images.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// A `RenderablePlane` is a renderable that will shows some form of contents projected
|
||||
// on a two-dimensional plane, which in turn is placed in three-dimensional space as
|
||||
// any other `Renderable`. It is possible to specify the `Size` of the plane, whether
|
||||
// it should always face the camera (`Billboard`), and other parameters shown below.
|
||||
struct [[codegen::Dictionary(RenderablePlane)]] Parameters {
|
||||
// [[codegen::verbatim(BillboardInfo.description)]]
|
||||
std::optional<bool> billboard;
|
||||
@@ -91,7 +105,10 @@ namespace {
|
||||
std::optional<bool> mirrorBackside;
|
||||
|
||||
// [[codegen::verbatim(SizeInfo.description)]]
|
||||
float size;
|
||||
std::variant<float, glm::vec2> size;
|
||||
|
||||
// [[codegen::verbatim(AutoScaleInfo.description)]]
|
||||
std::optional<bool> autoScale;
|
||||
|
||||
enum class [[codegen::map(BlendMode)]] BlendMode {
|
||||
Normal,
|
||||
@@ -113,61 +130,68 @@ documentation::Documentation RenderablePlane::Documentation() {
|
||||
}
|
||||
|
||||
RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary)
|
||||
, _blendMode(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown)
|
||||
: Renderable(dictionary, { .automaticallyUpdateRenderBin = false })
|
||||
, _blendMode(BlendModeInfo)
|
||||
, _billboard(BillboardInfo, false)
|
||||
, _mirrorBackside(MirrorBacksideInfo, false)
|
||||
, _size(SizeInfo, 10.f, 0.f, 1e25f)
|
||||
, _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();
|
||||
_opacity.onChange([this]() {
|
||||
if (_blendMode == static_cast<int>(BlendMode::Normal)) {
|
||||
setRenderBinFromOpacity();
|
||||
}
|
||||
});
|
||||
addProperty(Fadeable::_opacity);
|
||||
|
||||
_size = p.size;
|
||||
_billboard = p.billboard.value_or(_billboard);
|
||||
_mirrorBackside = p.mirrorBackside.value_or(_mirrorBackside);
|
||||
if (std::holds_alternative<float>(p.size)) {
|
||||
_size = glm::vec2(std::get<float>(p.size));
|
||||
}
|
||||
else {
|
||||
_size = std::get<glm::vec2>(p.size);
|
||||
}
|
||||
_size.setExponent(15.f);
|
||||
_size.onChange([this]() { _planeIsDirty = true; });
|
||||
addProperty(_size);
|
||||
|
||||
_blendMode.addOptions({
|
||||
{ static_cast<int>(BlendMode::Normal), "Normal" },
|
||||
{ static_cast<int>(BlendMode::Additive), "Additive"}
|
||||
{ static_cast<int>(BlendMode::Additive), "Additive" }
|
||||
});
|
||||
_blendMode.onChange([&]() {
|
||||
switch (_blendMode) {
|
||||
case static_cast<int>(BlendMode::Normal):
|
||||
_blendMode.onChange([this]() {
|
||||
const BlendMode m = static_cast<BlendMode>(_blendMode.value());
|
||||
switch (m) {
|
||||
case BlendMode::Normal:
|
||||
setRenderBinFromOpacity();
|
||||
break;
|
||||
case static_cast<int>(BlendMode::Additive):
|
||||
case 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);
|
||||
}
|
||||
addProperty(_blendMode);
|
||||
|
||||
_billboard = p.billboard.value_or(_billboard);
|
||||
addProperty(_billboard);
|
||||
|
||||
_mirrorBackside = p.mirrorBackside.value_or(_mirrorBackside);
|
||||
addProperty(_mirrorBackside);
|
||||
|
||||
_autoScale = p.autoScale.value_or(_autoScale);
|
||||
addProperty(_autoScale);
|
||||
|
||||
_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(_multiplyColor);
|
||||
|
||||
setBoundingSphere(_size);
|
||||
setBoundingSphere(glm::compMax(_size.value()));
|
||||
}
|
||||
|
||||
bool RenderablePlane::isReady() const {
|
||||
@@ -175,7 +199,7 @@ bool RenderablePlane::isReady() const {
|
||||
}
|
||||
|
||||
void RenderablePlane::initializeGL() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glGenVertexArrays(1, &_quad); // generate array
|
||||
glGenBuffers(1, &_vertexPositionBuffer); // generate buffer
|
||||
@@ -191,10 +215,12 @@ void RenderablePlane::initializeGL() {
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache);
|
||||
}
|
||||
|
||||
void RenderablePlane::deinitializeGL() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
glDeleteVertexArrays(1, &_quad);
|
||||
_quad = 0;
|
||||
@@ -212,24 +238,24 @@ void RenderablePlane::deinitializeGL() {
|
||||
}
|
||||
|
||||
void RenderablePlane::render(const RenderData& data, RendererTasks&) {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
_shader->activate();
|
||||
_shader->setUniform("opacity", opacity());
|
||||
_shader->setUniform(_uniformCache.opacity, opacity());
|
||||
|
||||
_shader->setUniform("mirrorBackside", _mirrorBackside);
|
||||
_shader->setUniform(_uniformCache.mirrorBackside, _mirrorBackside);
|
||||
|
||||
glm::dvec3 objectPositionWorld = glm::dvec3(
|
||||
const glm::dvec3 objPosWorld = 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(
|
||||
const glm::dvec3 normal = glm::normalize(data.camera.positionVec3() - objPosWorld);
|
||||
const glm::dvec3 newRight = glm::normalize(
|
||||
glm::cross(data.camera.lookUpVectorWorldSpace(), normal)
|
||||
);
|
||||
glm::dvec3 newUp = glm::cross(normal, newRight);
|
||||
const glm::dvec3 newUp = glm::cross(normal, newRight);
|
||||
|
||||
glm::dmat4 cameraOrientedRotation = glm::dmat4(1.0);
|
||||
cameraOrientedRotation[0] = glm::dvec4(newRight, 0.0);
|
||||
@@ -240,30 +266,25 @@ void RenderablePlane::render(const RenderData& data, RendererTasks&) {
|
||||
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;
|
||||
auto [modelTransform, modelViewTransform, modelViewProjectionTransform] =
|
||||
calcAllTransforms(data, { .rotation = rotationTransform });
|
||||
|
||||
_shader->setUniform("modelViewProjectionTransform",
|
||||
data.camera.projectionMatrix() * glm::mat4(modelViewTransform));
|
||||
|
||||
_shader->setUniform("modelViewTransform",
|
||||
glm::mat4(data.camera.combinedViewMatrix() * glm::dmat4(modelViewTransform)));
|
||||
_shader->setUniform(
|
||||
_uniformCache.modelViewProjection,
|
||||
glm::mat4(modelViewProjectionTransform)
|
||||
);
|
||||
_shader->setUniform(_uniformCache.modelViewTransform, glm::mat4(modelViewTransform));
|
||||
|
||||
ghoul::opengl::TextureUnit unit;
|
||||
unit.activate();
|
||||
bindTexture();
|
||||
defer { unbindTexture(); };
|
||||
|
||||
_shader->setUniform("texture1", unit);
|
||||
_shader->setUniform(_uniformCache.colorTexture, unit);
|
||||
|
||||
_shader->setUniform("multiplyColor", _multiplyColor);
|
||||
_shader->setUniform(_uniformCache.multiplyColor, _multiplyColor);
|
||||
|
||||
bool additiveBlending = (_blendMode == static_cast<int>(BlendMode::Additive));
|
||||
const bool additiveBlending = (_blendMode == static_cast<int>(BlendMode::Additive));
|
||||
if (additiveBlending) {
|
||||
glDepthMask(false);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
@@ -287,32 +308,34 @@ void RenderablePlane::bindTexture() {}
|
||||
void RenderablePlane::unbindTexture() {}
|
||||
|
||||
void RenderablePlane::update(const UpdateData&) {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
if (_shader->isDirty()) {
|
||||
if (_shader->isDirty()) [[unlikely]] {
|
||||
_shader->rebuildFromFile();
|
||||
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache);
|
||||
}
|
||||
|
||||
if (_planeIsDirty) {
|
||||
if (_planeIsDirty) [[unlikely]] {
|
||||
createPlane();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderablePlane::createPlane() {
|
||||
const GLfloat size = _size;
|
||||
const GLfloat vertexData[] = {
|
||||
// x y z w s t
|
||||
-size, -size, 0.f, 0.f, 0.f, 0.f,
|
||||
size, size, 0.f, 0.f, 1.f, 1.f,
|
||||
-size, size, 0.f, 0.f, 0.f, 1.f,
|
||||
-size, -size, 0.f, 0.f, 0.f, 0.f,
|
||||
size, -size, 0.f, 0.f, 1.f, 0.f,
|
||||
size, size, 0.f, 0.f, 1.f, 1.f,
|
||||
const GLfloat sizeX = _size.value().x;
|
||||
const GLfloat sizeY = _size.value().y;
|
||||
const std::array<GLfloat, 36> 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);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData.data(), GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, nullptr);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -27,12 +27,11 @@
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/misc/optionproperty.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
#include <openspace/properties/vector/vec3property.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/uniformcache.h>
|
||||
|
||||
namespace ghoul::filesystem { class File; }
|
||||
|
||||
@@ -52,7 +51,7 @@ struct LinePoint;
|
||||
|
||||
class RenderablePlane : public Renderable {
|
||||
public:
|
||||
RenderablePlane(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderablePlane(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initializeGL() override;
|
||||
void deinitializeGL() override;
|
||||
@@ -72,7 +71,8 @@ protected:
|
||||
properties::OptionProperty _blendMode;
|
||||
properties::BoolProperty _billboard;
|
||||
properties::BoolProperty _mirrorBackside;
|
||||
properties::FloatProperty _size;
|
||||
properties::Vec2Property _size;
|
||||
properties::BoolProperty _autoScale;
|
||||
properties::Vec3Property _multiplyColor;
|
||||
|
||||
ghoul::opengl::ProgramObject* _shader = nullptr;
|
||||
@@ -82,6 +82,9 @@ protected:
|
||||
|
||||
private:
|
||||
bool _planeIsDirty = false;
|
||||
|
||||
UniformCache(modelViewProjection, modelViewTransform, colorTexture, opacity,
|
||||
mirrorBackside, multiplyColor) _uniformCache;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -41,17 +41,19 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo TextureInfo = {
|
||||
"Texture",
|
||||
"Texture",
|
||||
"This value specifies an image that is loaded from disk and is used as a texture "
|
||||
"that is applied to this plane. This image has to be square"
|
||||
"A path to an image file to use as a texture for the plane.",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// A RenderablePlaneImageLocal creates a textured 3D plane, where the texture is
|
||||
// provided by a local file on disk.
|
||||
struct [[codegen::Dictionary(RenderablePlaneImageLocal)]] Parameters {
|
||||
// [[codegen::verbatim(TextureInfo.description)]]
|
||||
std::string texture;
|
||||
|
||||
// If this value is set to 'true', the image for this plane will not be loaded at
|
||||
// If this value is set to true, the image for this plane will not be loaded at
|
||||
// startup but rather when image is shown for the first time. Additionally, if the
|
||||
// plane is hidden, the image will automatically be unloaded
|
||||
// plane is hidden, the image will automatically be unloaded.
|
||||
std::optional<bool> lazyLoading;
|
||||
};
|
||||
#include "renderableplaneimagelocal_codegen.cpp"
|
||||
@@ -72,8 +74,6 @@ RenderablePlaneImageLocal::RenderablePlaneImageLocal(const ghoul::Dictionary& di
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
addProperty(_blendMode);
|
||||
|
||||
_texturePath = absPath(p.texture).string();
|
||||
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath.value());
|
||||
|
||||
@@ -93,10 +93,31 @@ RenderablePlaneImageLocal::RenderablePlaneImageLocal(const ghoul::Dictionary& di
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderablePlaneImageLocal::isReady() const {
|
||||
return RenderablePlane::isReady();
|
||||
_autoScale.onChange([this]() {
|
||||
if (!_autoScale) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shape the plane based on the aspect ration of the image
|
||||
const glm::vec2 textureDim = glm::vec2(_texture->dimensions());
|
||||
if (_textureDimensions != textureDim) {
|
||||
const float aspectRatio = textureDim.x / textureDim.y;
|
||||
const float planeAspectRatio = _size.value().x / _size.value().y;
|
||||
|
||||
if (std::abs(planeAspectRatio - aspectRatio) >
|
||||
std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
const glm::vec2 newSize =
|
||||
aspectRatio > 0.f ?
|
||||
glm::vec2(_size.value().x * aspectRatio, _size.value().y) :
|
||||
glm::vec2(_size.value().x, _size.value().y * aspectRatio);
|
||||
_size = newSize;
|
||||
}
|
||||
|
||||
_textureDimensions = textureDim;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RenderablePlaneImageLocal::initializeGL() {
|
||||
@@ -119,36 +140,33 @@ void RenderablePlaneImageLocal::bindTexture() {
|
||||
}
|
||||
|
||||
void RenderablePlaneImageLocal::update(const UpdateData& data) {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
RenderablePlane::update(data);
|
||||
|
||||
if (_textureIsDirty) {
|
||||
if (_textureIsDirty) [[unlikely]] {
|
||||
loadTexture();
|
||||
_textureIsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderablePlaneImageLocal::loadTexture() {
|
||||
ZoneScoped
|
||||
ZoneScoped;
|
||||
|
||||
if (!_texturePath.value().empty()) {
|
||||
ghoul::opengl::Texture* t = _texture;
|
||||
|
||||
unsigned int hash = ghoul::hashCRC32File(_texturePath);
|
||||
const unsigned int hash = ghoul::hashCRC32File(_texturePath);
|
||||
|
||||
_texture = BaseModule::TextureManager.request(
|
||||
std::to_string(hash),
|
||||
[path = _texturePath]() -> std::unique_ptr<ghoul::opengl::Texture> {
|
||||
std::unique_ptr<ghoul::opengl::Texture> texture =
|
||||
ghoul::io::TextureReader::ref().loadTexture(
|
||||
absPath(path).string(),
|
||||
2
|
||||
);
|
||||
ghoul::io::TextureReader::ref().loadTexture(absPath(path), 2);
|
||||
|
||||
LDEBUGC(
|
||||
"RenderablePlaneImageLocal",
|
||||
fmt::format("Loaded texture from {}", absPath(path))
|
||||
std::format("Loaded texture from '{}'", absPath(path))
|
||||
);
|
||||
texture->uploadTexture();
|
||||
texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap);
|
||||
@@ -162,6 +180,29 @@ void RenderablePlaneImageLocal::loadTexture() {
|
||||
|
||||
_textureFile = std::make_unique<ghoul::filesystem::File>(_texturePath.value());
|
||||
_textureFile->setCallback([this]() { _textureIsDirty = true; });
|
||||
|
||||
if (!_autoScale) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shape the plane based on the aspect ration of the image
|
||||
const glm::vec2 textureDim = glm::vec2(_texture->dimensions());
|
||||
if (_textureDimensions != textureDim) {
|
||||
const float aspectRatio = textureDim.x / textureDim.y;
|
||||
const float planeAspectRatio = _size.value().x / _size.value().y;
|
||||
|
||||
if (std::abs(planeAspectRatio - aspectRatio) >
|
||||
std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
const glm::vec2 newSize =
|
||||
aspectRatio > 0.f ?
|
||||
glm::vec2(_size.value().x * aspectRatio, _size.value().y) :
|
||||
glm::vec2(_size.value().x, _size.value().y * aspectRatio);
|
||||
_size = newSize;
|
||||
}
|
||||
|
||||
_textureDimensions = textureDim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* Copyright (c) 2014-2025 *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -39,13 +39,11 @@ namespace documentation { struct Documentation; }
|
||||
|
||||
class RenderablePlaneImageLocal : public RenderablePlane {
|
||||
public:
|
||||
RenderablePlaneImageLocal(const ghoul::Dictionary& dictionary);
|
||||
explicit RenderablePlaneImageLocal(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initializeGL() override;
|
||||
void deinitializeGL() override;
|
||||
|
||||
bool isReady() const override;
|
||||
|
||||
void update(const UpdateData& data) override;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
@@ -58,6 +56,7 @@ private:
|
||||
|
||||
properties::StringProperty _texturePath;
|
||||
ghoul::opengl::Texture* _texture = nullptr;
|
||||
glm::vec2 _textureDimensions = glm::vec2(0.f);
|
||||
std::unique_ptr<ghoul::filesystem::File> _textureFile;
|
||||
|
||||
bool _isLoadingLazily = false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user