mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-04 10:40:09 -06:00
Feature/arrow renderable (#2219)
* Create a RenderableNodeLine example asset * First version of node direction hint renderable (arrow) * Add possibility to set length as a multiplier of the bounding sphere * Draw arrow cylinder using index array * Update exponents and min max values for lengths * Draw arrow head * Only update arrow geometry when positions change * Add some properties to control visuals of arrow * Implement invert option * Add normals and shading * Set arrow head size by length percentage instead of angle * Add bottom circle to cone * Cleanup and update examples * Remove non-existing property from example asset * Fix vertices not updating if anchor node was changed And some missing updates on property change * Start cleaning up some shape rendering helper functions * Cleanup code and move cylinder function to helper class * Refactor cylinder creation code (fewer loops over same vector) * Update transformations to correctly scale and place arrow * Add the cone to make the arrowhead * Update faulty triangle normals * Add property visibilities * Rename NodeDirectionHint to NodeArrow * Apply suggestions from code review Co-authored-by: Alexander Bock <alexander.bock@liu.se> --------- Co-authored-by: Alexander Bock <alexander.bock@liu.se>
This commit is contained in:
77
data/assets/examples/nodearrow.asset
Normal file
77
data/assets/examples/nodearrow.asset
Normal file
@@ -0,0 +1,77 @@
|
||||
local earth = asset.require("scene/solarsystem/planets/earth/earth")
|
||||
local moon = asset.require("scene/solarsystem/planets/earth/moon/moon")
|
||||
local mars = asset.require("scene/solarsystem/planets/mars/mars")
|
||||
|
||||
|
||||
|
||||
local EarthRadius = 6371000.0
|
||||
|
||||
local ArrowExample = {
|
||||
Identifier = "RenderableNodeArrowExample",
|
||||
Parent = earth.Earth.Identifier,
|
||||
Renderable = {
|
||||
Type = "RenderableNodeArrow",
|
||||
StartNode = earth.Earth.Identifier,
|
||||
EndNode = mars.Mars.Identifier,
|
||||
Color = { 0.8, 1.0, 0.8 },
|
||||
Offset = 2 * EarthRadius,
|
||||
Length = 5 * EarthRadius,
|
||||
Width = 900000.0
|
||||
},
|
||||
GUI = {
|
||||
Name = "Node Arrow Example",
|
||||
Path = "/Example",
|
||||
Description = [[Example node direction arrow, using absolute sizes]]
|
||||
}
|
||||
}
|
||||
|
||||
-- Relative values: Multiplied with bounding sphere
|
||||
local ArrowExample_RelativeUnits = {
|
||||
Identifier = "RenderableNodeArrowExample_Relative",
|
||||
Parent = earth.Earth.Identifier,
|
||||
Renderable = {
|
||||
Type = "RenderableNodeArrow",
|
||||
StartNode = earth.Earth.Identifier,
|
||||
EndNode = moon.Moon.Identifier,
|
||||
Color = { 0.78, 0.0, 1.0 },
|
||||
UseRelativeOffset = true,
|
||||
UseRelativeLength = true,
|
||||
Offset = 2.0,
|
||||
Length = 5.0,
|
||||
Width = 900000.0,
|
||||
Invert = true -- Point to start node instead of end node
|
||||
},
|
||||
GUI = {
|
||||
Name = "Node Arrow Example (relative units)",
|
||||
Path = "/Example",
|
||||
Description = [[Example node direction arrow, using relative sizes]]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.addSceneGraphNode(ArrowExample)
|
||||
openspace.addSceneGraphNode(ArrowExample_RelativeUnits)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.removeSceneGraphNode(ArrowExample_RelativeUnits)
|
||||
openspace.removeSceneGraphNode(ArrowExample)
|
||||
end)
|
||||
|
||||
asset.export(ArrowExample)
|
||||
asset.export(ArrowExample_RelativeUnits)
|
||||
|
||||
|
||||
|
||||
asset.meta = {
|
||||
Name = "RenderableNodeArrow Example asset",
|
||||
Version = "1.0",
|
||||
Description = [[Examples of the RenderableNodeArrow renderable, that can be used to draw
|
||||
an arrow pointing from one scene graph node in the direction of another. Note that
|
||||
the arrows are generated as objects in 3D space and need to have a size that is
|
||||
suitable for their 3D context.]],
|
||||
Author = "OpenSpace Team",
|
||||
URL = "http://openspaceproject.com",
|
||||
License = "MIT license"
|
||||
}
|
||||
43
data/assets/examples/nodeline.asset
Normal file
43
data/assets/examples/nodeline.asset
Normal file
@@ -0,0 +1,43 @@
|
||||
local earth = asset.require("scene/solarsystem/planets/earth/earth")
|
||||
local mars = asset.require("scene/solarsystem/planets/mars/mars")
|
||||
|
||||
|
||||
|
||||
local RenderableNodeLineExample = {
|
||||
Identifier = "RenderableNodeLineExample",
|
||||
Renderable = {
|
||||
Type = "RenderableNodeLine",
|
||||
StartNode = earth.Earth.Identifier,
|
||||
EndNode = mars.Mars.Identifier,
|
||||
Color = { 0.5, 0.5, 0.5 },
|
||||
LineWidth = 2
|
||||
},
|
||||
GUI = {
|
||||
Name = "RenderableNodeLine Example",
|
||||
Path = "/Example",
|
||||
Description = [[Draws a line between two nodes in the scene.]]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.addSceneGraphNode(RenderableNodeLineExample)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.removeSceneGraphNode(RenderableNodeLineExample)
|
||||
end)
|
||||
|
||||
asset.export(RenderableNodeLineExample)
|
||||
|
||||
|
||||
|
||||
asset.meta = {
|
||||
Name = "RenderableNodeLine Example asset",
|
||||
Version = "1.0",
|
||||
Description = [[Example of a RenderableNodeLine, that can be used to draw an line
|
||||
between two scene graph nodes.]],
|
||||
Author = "OpenSpace Team",
|
||||
URL = "http://openspaceproject.com",
|
||||
License = "MIT license"
|
||||
}
|
||||
@@ -89,6 +89,22 @@ struct VertexObjects {
|
||||
int nElements = 64;
|
||||
} sphere;
|
||||
|
||||
struct {
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ibo = 0;
|
||||
|
||||
int nElements = 64;
|
||||
} cylinder;
|
||||
|
||||
struct {
|
||||
GLuint vao = 0;
|
||||
GLuint vbo = 0;
|
||||
GLuint ibo = 0;
|
||||
|
||||
int nElements = 64;
|
||||
} cone;
|
||||
|
||||
struct {
|
||||
GLuint vao = 0;
|
||||
} empty;
|
||||
@@ -119,6 +135,12 @@ struct VertexXYZNormal {
|
||||
GLfloat normal[3];
|
||||
};
|
||||
|
||||
template <typename V>
|
||||
struct VertexIndexListCombo {
|
||||
std::vector<V> vertices;
|
||||
std::vector<GLushort> indices;
|
||||
};
|
||||
|
||||
VertexXYZ convertToXYZ(const Vertex& v);
|
||||
|
||||
std::vector<VertexXYZ> convert(std::vector<Vertex> v);
|
||||
@@ -126,9 +148,17 @@ std::vector<VertexXYZ> convert(std::vector<Vertex> v);
|
||||
std::vector<Vertex> createRing(int nSegments, float radius,
|
||||
glm::vec4 colors = glm::vec4(1.f));
|
||||
|
||||
std::pair<std::vector<Vertex>, std::vector<GLushort>>
|
||||
std::vector<VertexXYZ> createRingXYZ(int nSegments, float radius);
|
||||
|
||||
VertexIndexListCombo<Vertex>
|
||||
createSphere(int nSegments, glm::vec3 radii, glm::vec4 colors = glm::vec4(1.f));
|
||||
|
||||
VertexIndexListCombo<VertexXYZNormal> createCylinder(unsigned int nSegments,
|
||||
float radius, float height);
|
||||
|
||||
VertexIndexListCombo<VertexXYZNormal> createCone(unsigned int nSegments, float radius,
|
||||
float height);
|
||||
|
||||
/**
|
||||
* Data structure that can be used for rendering using multiple light directions
|
||||
*/
|
||||
|
||||
@@ -48,6 +48,7 @@ set(HEADER_FILES
|
||||
rendering/renderabledisc.h
|
||||
rendering/renderablelabel.h
|
||||
rendering/renderablemodel.h
|
||||
rendering/renderablenodearrow.h
|
||||
rendering/renderablenodeline.h
|
||||
rendering/renderableplane.h
|
||||
rendering/renderableplaneimagelocal.h
|
||||
@@ -106,6 +107,7 @@ set(SOURCE_FILES
|
||||
rendering/renderabledisc.cpp
|
||||
rendering/renderablelabel.cpp
|
||||
rendering/renderablemodel.cpp
|
||||
rendering/renderablenodearrow.cpp
|
||||
rendering/renderablenodeline.cpp
|
||||
rendering/renderableplane.cpp
|
||||
rendering/renderableplaneimagelocal.cpp
|
||||
@@ -142,6 +144,8 @@ set(SOURCE_FILES
|
||||
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
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <modules/base/rendering/renderabledisc.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/renderablesphereimagelocal.h>
|
||||
#include <modules/base/rendering/renderablesphereimageonline.h>
|
||||
@@ -136,6 +137,7 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
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");
|
||||
@@ -222,6 +224,7 @@ std::vector<documentation::Documentation> BaseModule::documentations() const {
|
||||
RenderableGrid::Documentation(),
|
||||
RenderableLabel::Documentation(),
|
||||
RenderableModel::Documentation(),
|
||||
RenderableNodeArrow::Documentation(),
|
||||
RenderableNodeLine::Documentation(),
|
||||
RenderablePlane::Documentation(),
|
||||
RenderablePlaneImageLocal::Documentation(),
|
||||
|
||||
515
modules/base/rendering/renderablenodearrow.cpp
Normal file
515
modules/base/rendering/renderablenodearrow.cpp
Normal file
@@ -0,0 +1,515 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2023 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/base/rendering/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",
|
||||
"This value determines the RGB color for the arrow",
|
||||
openspace::properties::Property::Visibility::NoviceUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
|
||||
"Segments",
|
||||
"Number of Segments",
|
||||
"This value specifies the number of segments that the shapes for the arrow are "
|
||||
"separated in. A higher number leads to a higher resolution",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo InvertInfo = {
|
||||
"Invert",
|
||||
"Invert Direction",
|
||||
"If set to 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",
|
||||
"This size 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",
|
||||
"Decide whether to use relative distances (in units of start node bounding "
|
||||
"sphere) for the offset distance. 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",
|
||||
"Decide whether to use relative size (in units of start node bounding "
|
||||
"sphere) for the length of the arrow. If false, meters is used",
|
||||
openspace::properties::Property::Visibility::AdvancedUser
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo WidthInfo = {
|
||||
"Width",
|
||||
"Width",
|
||||
"This value specifies the width of the arrow shape",
|
||||
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",
|
||||
"This value determines whether shading should be applied to the arrow model",
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(RenderableNodeArrow)]] Parameters {
|
||||
// [[codegen::verbatim(StartNodeInfo.description)]]
|
||||
std::string startNode [[codegen::notempty]];
|
||||
|
||||
// [[codegen::verbatim(EndNodeInfo.description)]]
|
||||
std::string endNode [[codegen::notempty]];
|
||||
|
||||
// [[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<float> 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"
|
||||
|
||||
void updateDistanceBasedOnRelativeValues(const std::string& nodeName,
|
||||
bool useRelative,
|
||||
openspace::properties::FloatProperty& prop)
|
||||
{
|
||||
using namespace::openspace;
|
||||
|
||||
SceneGraphNode* startNode = sceneGraphNode(nodeName);
|
||||
if (!startNode) {
|
||||
LERROR(fmt::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(fmt::format(
|
||||
"Start node '{}' has invalid bounding sphere", nodeName
|
||||
));
|
||||
return;
|
||||
}
|
||||
prop = static_cast<float>(prop / boundingSphere);
|
||||
prop.setExponent(3.f);
|
||||
prop.setMaxValue(1000.f);
|
||||
}
|
||||
// @TODO (emmbr, 2022-08-22): make GUI update when min/max value is updated
|
||||
};
|
||||
} // 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;
|
||||
}
|
||||
|
||||
void RenderableNodeArrow::updateShapeTransforms(const RenderData& data) {
|
||||
SceneGraphNode* startNode = sceneGraphNode(_start);
|
||||
SceneGraphNode* endNode = sceneGraphNode(_end);
|
||||
|
||||
if (!startNode) {
|
||||
LERROR(fmt::format("Could not find start node '{}'", _start.value()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!endNode) {
|
||||
LERROR(fmt::format("Could not find end node '{}'", _end.value()));
|
||||
return;
|
||||
}
|
||||
|
||||
const double boundingSphere = startNode->boundingSphere();
|
||||
bool hasNoBoundingSphere = boundingSphere < std::numeric_limits<double>::epsilon();
|
||||
|
||||
if (hasNoBoundingSphere && (_useRelativeLength || _useRelativeOffset)) {
|
||||
LERROR(fmt::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
|
||||
glm::dvec3 startNodePos = startNode->worldPosition();
|
||||
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;
|
||||
}
|
||||
|
||||
double coneLength = _arrowHeadSize * length;
|
||||
double cylinderLength = length - coneLength;
|
||||
double arrowHeadWidth = _width * _arrowHeadWidthFactor;
|
||||
|
||||
// Create transformation matrices to reshape to size and position
|
||||
_cylinderTranslation = glm::translate(glm::dmat4(1.0), startPos);
|
||||
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
|
||||
glm::dvec3 arrowHeadStartPos = startPos + cylinderScale.z * arrowDirection;
|
||||
|
||||
_coneTranslation = glm::translate(glm::dmat4(1.0), arrowHeadStartPos);
|
||||
glm::dvec3 coneScale = glm::dvec3(arrowHeadWidth, arrowHeadWidth, coneLength);
|
||||
_coneScale = s * glm::scale(glm::dmat4(1.0), coneScale);
|
||||
|
||||
// Rotation to point at the end node
|
||||
glm::quat rotQuat = glm::rotation(glm::dvec3(0.0, 0.0, 1.0), arrowDirection);
|
||||
_pointDirectionRotation = glm::dmat4(glm::toMat4(rotQuat));
|
||||
}
|
||||
|
||||
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-2023 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#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:
|
||||
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;
|
||||
|
||||
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__
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/documentation/verifier.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/rendering/helper.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/util/updatestructures.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
@@ -98,20 +99,6 @@ namespace {
|
||||
openspace::properties::Property::Visibility::User
|
||||
};
|
||||
|
||||
// Generate vertices around the unit circle on the XY-plane
|
||||
std::vector<float> unitCircleVertices(int sectorCount) {
|
||||
std::vector<float> vertices;
|
||||
vertices.reserve(2 * sectorCount);
|
||||
float sectorStep = glm::two_pi<float>() / sectorCount;
|
||||
|
||||
for (int i = 0; i < sectorCount; ++i) {
|
||||
float sectorAngle = i * sectorStep;
|
||||
vertices.push_back(cos(sectorAngle)); // x
|
||||
vertices.push_back(sin(sectorAngle)); // y
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
struct [[codegen::Dictionary(RenderablePrism)]] Parameters {
|
||||
// [[codegen::verbatim(SegmentsInfo.description)]]
|
||||
int segments;
|
||||
@@ -236,17 +223,16 @@ void RenderablePrism::updateVertexData() {
|
||||
_vertexArray.clear();
|
||||
_indexArray.clear();
|
||||
|
||||
using namespace rendering::helper;
|
||||
|
||||
// Get unit circle vertices on the XY-plane
|
||||
std::vector<float> unitVertices = unitCircleVertices(_nShapeSegments);
|
||||
std::vector<float> unitVerticesLines = unitCircleVertices(_nLines);
|
||||
std::vector<VertexXYZ> unitVertices = createRingXYZ(_nShapeSegments.value(), 1.f);
|
||||
std::vector<VertexXYZ> unitVerticesLines = createRingXYZ(_nLines.value(), 1.f);
|
||||
|
||||
// Put base vertices into array
|
||||
for (int j = 0, k = 0;
|
||||
j < _nShapeSegments && k < static_cast<int>(unitVertices.size());
|
||||
++j, k += 2)
|
||||
{
|
||||
float ux = unitVertices[k];
|
||||
float uy = unitVertices[k + 1];
|
||||
for (int j = 0; j < _nShapeSegments; ++j) {
|
||||
float ux = unitVertices[j].xyz[0];
|
||||
float uy = unitVertices[j].xyz[1];
|
||||
|
||||
_vertexArray.push_back(ux * _baseRadius); // x
|
||||
_vertexArray.push_back(uy * _baseRadius); // y
|
||||
@@ -254,12 +240,9 @@ void RenderablePrism::updateVertexData() {
|
||||
}
|
||||
|
||||
// Put top shape vertices into array
|
||||
for (int j = 0, k = 0;
|
||||
j < _nShapeSegments && k < static_cast<int>(unitVertices.size());
|
||||
++j, k += 2)
|
||||
{
|
||||
float ux = unitVertices[k];
|
||||
float uy = unitVertices[k + 1];
|
||||
for (int j = 0; j < _nShapeSegments; ++j) {
|
||||
float ux = unitVertices[j].xyz[0];
|
||||
float uy = unitVertices[j].xyz[1];
|
||||
|
||||
_vertexArray.push_back(ux * _radius); // x
|
||||
_vertexArray.push_back(uy * _radius); // y
|
||||
@@ -280,12 +263,9 @@ void RenderablePrism::updateVertexData() {
|
||||
_vertexArray.push_back(_length);
|
||||
}
|
||||
else {
|
||||
for (int j = 0, k = 0;
|
||||
j < _nLines && k < static_cast<int>(unitVerticesLines.size());
|
||||
++j, k += 2)
|
||||
{
|
||||
float ux = unitVerticesLines[k];
|
||||
float uy = unitVerticesLines[k + 1];
|
||||
for (int j = 0; j < _nLines; ++j) {
|
||||
float ux = unitVerticesLines[j].xyz[0];
|
||||
float uy = unitVerticesLines[j].xyz[1];
|
||||
|
||||
// Base
|
||||
_vertexArray.push_back(ux * _baseRadius); // x
|
||||
|
||||
75
modules/base/shaders/arrow_fs.glsl
Normal file
75
modules/base/shaders/arrow_fs.glsl
Normal file
@@ -0,0 +1,75 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2023 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include "fragment.glsl"
|
||||
|
||||
in float vs_depth;
|
||||
in vec3 vs_normal;
|
||||
in vec4 vs_positionViewSpace;
|
||||
|
||||
uniform vec3 color;
|
||||
uniform float opacity;
|
||||
uniform float ambientIntensity = 0.2;
|
||||
uniform float diffuseIntensity = 1.0;
|
||||
uniform float specularIntensity = 1.0;
|
||||
uniform bool performShading = true;
|
||||
|
||||
const vec3 LightColor = vec3(1.0);
|
||||
|
||||
Fragment getFragment() {
|
||||
Fragment frag;
|
||||
|
||||
frag.color = vec4(color, opacity);
|
||||
|
||||
// Simple phong shading (same color for diffuse and ambient. White specular)
|
||||
if (performShading) {
|
||||
const vec3 lightDirectionViewSpace = vec3(0.0, 0.0, 1.0);
|
||||
const float lightIntensity = 1.0;
|
||||
const float specularPower = 100.0;
|
||||
|
||||
// Ambient color
|
||||
vec3 shadedColor = ambientIntensity * color;
|
||||
|
||||
// Diffuse
|
||||
vec3 n = normalize(vs_normal);
|
||||
vec3 l = lightDirectionViewSpace;
|
||||
shadedColor += diffuseIntensity * max(dot(n,l), 0.0) * color;
|
||||
|
||||
// Specular
|
||||
vec3 viewDir = normalize(vs_positionViewSpace.xyz);
|
||||
vec3 reflectDir = reflect(l, n);
|
||||
shadedColor +=
|
||||
specularIntensity * pow(max(dot(viewDir,reflectDir), 0), specularPower) * color;
|
||||
|
||||
// Light contribution (one light soruce)
|
||||
shadedColor *= lightIntensity * LightColor;
|
||||
|
||||
frag.color.xyz = shadedColor;
|
||||
}
|
||||
|
||||
frag.depth = vs_depth;
|
||||
frag.gPosition = vs_positionViewSpace;
|
||||
frag.gNormal = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
return frag;
|
||||
}
|
||||
47
modules/base/shaders/arrow_vs.glsl
Normal file
47
modules/base/shaders/arrow_vs.glsl
Normal file
@@ -0,0 +1,47 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2023 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#version __CONTEXT__
|
||||
|
||||
layout(location = 0) in vec3 in_position;
|
||||
layout(location = 1) in vec3 in_normal;
|
||||
|
||||
out float vs_depth;
|
||||
out vec3 vs_normal;
|
||||
out vec4 vs_positionViewSpace;
|
||||
|
||||
uniform mat4 modelViewTransform;
|
||||
uniform mat4 projectionTransform;
|
||||
uniform mat3 normalTransform;
|
||||
|
||||
void main() {
|
||||
vs_positionViewSpace = vec4(modelViewTransform * dvec4(in_position, 1));
|
||||
vec4 positionScreenSpace = projectionTransform * vs_positionViewSpace;
|
||||
vs_depth = positionScreenSpace.w;
|
||||
vs_normal = normalize(normalTransform * in_normal);
|
||||
gl_Position = positionScreenSpace;
|
||||
|
||||
// Set z to 0 to disable near and far plane, unique handling for perspective in space
|
||||
gl_Position.z = 0.f;
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <ghoul/opengl/textureunit.h>
|
||||
#include <glm/gtx/closest_point.hpp>
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -246,10 +247,11 @@ void initialize() {
|
||||
reinterpret_cast<GLvoid*>(offsetof(VertexXYUVRGBA, rgba)));
|
||||
glBindVertexArray(0);
|
||||
|
||||
|
||||
//
|
||||
// Sphere vertex array object
|
||||
//
|
||||
std::pair<std::vector<Vertex>, std::vector<GLushort>> sphereData = createSphere(
|
||||
VertexIndexListCombo<Vertex> sphereData = createSphere(
|
||||
64, glm::vec3(1.f, 1.f, 1.f), glm::vec4(1.f, 1.f, 1.f, 1.f)
|
||||
);
|
||||
|
||||
@@ -261,16 +263,16 @@ void initialize() {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexObjects.sphere.vbo);
|
||||
glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
sphereData.first.size() * sizeof(Vertex),
|
||||
sphereData.first.data(),
|
||||
sphereData.vertices.size() * sizeof(Vertex),
|
||||
sphereData.vertices.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexObjects.sphere.ibo);
|
||||
glBufferData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
sphereData.second.size() * sizeof(GLushort),
|
||||
sphereData.second.data(),
|
||||
sphereData.indices.size() * sizeof(GLushort),
|
||||
sphereData.indices.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
glEnableVertexAttribArray(0);
|
||||
@@ -282,7 +284,75 @@ void initialize() {
|
||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<GLvoid*>(offsetof(Vertex, rgba)));
|
||||
glBindVertexArray(0);
|
||||
vertexObjects.sphere.nElements = static_cast<int>(sphereData.second.size());
|
||||
vertexObjects.sphere.nElements = static_cast<int>(sphereData.indices.size());
|
||||
|
||||
|
||||
//
|
||||
// Cylinder vertex array object
|
||||
//
|
||||
VertexIndexListCombo<VertexXYZNormal> cylinderData = createCylinder(64, 1.f, 1.f);
|
||||
|
||||
glGenVertexArrays(1, &vertexObjects.cylinder.vao);
|
||||
glGenBuffers(1, &vertexObjects.cylinder.vbo);
|
||||
glGenBuffers(1, &vertexObjects.cylinder.ibo);
|
||||
|
||||
glBindVertexArray(vertexObjects.cylinder.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexObjects.cylinder.vbo);
|
||||
glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
cylinderData.vertices.size() * sizeof(VertexXYZNormal),
|
||||
cylinderData.vertices.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexObjects.cylinder.ibo);
|
||||
glBufferData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
cylinderData.indices.size() * sizeof(GLushort),
|
||||
cylinderData.indices.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexXYZNormal), nullptr);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexXYZNormal),
|
||||
reinterpret_cast<GLvoid*>(offsetof(VertexXYZNormal, normal)));
|
||||
glBindVertexArray(0);
|
||||
vertexObjects.cylinder.nElements = static_cast<int>(cylinderData.indices.size());
|
||||
|
||||
|
||||
//
|
||||
// Cone vertex array object
|
||||
//
|
||||
VertexIndexListCombo<VertexXYZNormal> coneData = createCone(64, 1.f, 1.f);
|
||||
|
||||
glGenVertexArrays(1, &vertexObjects.cone.vao);
|
||||
glGenBuffers(1, &vertexObjects.cone.vbo);
|
||||
glGenBuffers(1, &vertexObjects.cone.ibo);
|
||||
|
||||
glBindVertexArray(vertexObjects.cone.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertexObjects.cone.vbo);
|
||||
glBufferData(
|
||||
GL_ARRAY_BUFFER,
|
||||
coneData.vertices.size() * sizeof(VertexXYZNormal),
|
||||
coneData.vertices.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexObjects.cone.ibo);
|
||||
glBufferData(
|
||||
GL_ELEMENT_ARRAY_BUFFER,
|
||||
coneData.indices.size() * sizeof(GLushort),
|
||||
coneData.indices.data(),
|
||||
GL_STATIC_DRAW
|
||||
);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexXYZNormal), nullptr);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexXYZNormal),
|
||||
reinterpret_cast<GLvoid*>(offsetof(VertexXYZNormal, normal)));
|
||||
glBindVertexArray(0);
|
||||
vertexObjects.cone.nElements = static_cast<int>(coneData.indices.size());
|
||||
|
||||
|
||||
//
|
||||
@@ -320,6 +390,14 @@ void deinitialize() {
|
||||
glDeleteBuffers(1, &vertexObjects.sphere.vbo);
|
||||
glDeleteBuffers(1, &vertexObjects.sphere.ibo);
|
||||
|
||||
glDeleteVertexArrays(1, &vertexObjects.cylinder.vao);
|
||||
glDeleteBuffers(1, &vertexObjects.cylinder.vbo);
|
||||
glDeleteBuffers(1, &vertexObjects.cylinder.ibo);
|
||||
|
||||
glDeleteVertexArrays(1, &vertexObjects.cone.vao);
|
||||
glDeleteBuffers(1, &vertexObjects.cone.vbo);
|
||||
glDeleteBuffers(1, &vertexObjects.cone.ibo);
|
||||
|
||||
glDeleteVertexArrays(1, &vertexObjects.empty.vao);
|
||||
|
||||
isInitialized = false;
|
||||
@@ -423,32 +501,48 @@ std::vector<VertexXYZ> convert(std::vector<Vertex> v) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Vertex computeCircleVertex(int i, int nSegments, float radius,
|
||||
glm::vec4 color = glm::vec4(1.f))
|
||||
{
|
||||
const float fsegments = static_cast<float>(nSegments);
|
||||
|
||||
const float fi = static_cast<float>(i);
|
||||
|
||||
const float theta = fi * glm::two_pi<float>() / fsegments; // 0 -> 2*PI
|
||||
|
||||
const float x = radius * std::cos(theta);
|
||||
const float y = radius * std::sin(theta);
|
||||
const float z = 0.f;
|
||||
|
||||
const float u = std::cos(theta);
|
||||
const float v = std::sin(theta);
|
||||
|
||||
return { x, y, z, u, v, color.r, color.g, color.b, color.a };
|
||||
}
|
||||
|
||||
std::vector<Vertex> createRing(int nSegments, float radius, glm::vec4 colors) {
|
||||
const int nVertices = nSegments + 1;
|
||||
std::vector<Vertex> vertices(nVertices);
|
||||
|
||||
const float fsegments = static_cast<float>(nSegments);
|
||||
|
||||
for (int i = 0; i <= nSegments; ++i) {
|
||||
const float fi = static_cast<float>(i);
|
||||
|
||||
const float theta = fi * glm::pi<float>() * 2.f / fsegments; // 0 -> 2*PI
|
||||
|
||||
const float x = radius * std::cos(theta);
|
||||
const float y = radius * std::sin(theta);
|
||||
const float z = 0.f;
|
||||
|
||||
const float u = std::cos(theta);
|
||||
const float v = std::sin(theta);
|
||||
|
||||
vertices[i] = { x, y, z, u, v, colors.r, colors.g, colors.b, colors.a };
|
||||
vertices[i] = computeCircleVertex(i, nSegments, radius, colors);
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
std::pair<std::vector<Vertex>, std::vector<GLushort>> createSphere(int nSegments,
|
||||
glm::vec3 radii,
|
||||
glm::vec4 colors)
|
||||
std::vector<VertexXYZ> createRingXYZ(int nSegments, float radius) {
|
||||
const int nVertices = nSegments + 1;
|
||||
std::vector<VertexXYZ> vertices(nVertices);
|
||||
|
||||
for (int i = 0; i <= nSegments; ++i) {
|
||||
Vertex fullVertex = computeCircleVertex(i, nSegments, radius);
|
||||
vertices[i] = { fullVertex.xyz[0], fullVertex.xyz[1], fullVertex.xyz[2] };
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
VertexIndexListCombo<Vertex> createSphere(int nSegments, glm::vec3 radii,
|
||||
glm::vec4 colors)
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
vertices.reserve(nSegments * nSegments);
|
||||
@@ -505,6 +599,153 @@ std::pair<std::vector<Vertex>, std::vector<GLushort>> createSphere(int nSegments
|
||||
return { vertices, indices };
|
||||
}
|
||||
|
||||
VertexIndexListCombo<VertexXYZNormal> createConicalCylinder(unsigned int nSegments,
|
||||
float bottomRadius,
|
||||
float topRadius,
|
||||
float height)
|
||||
{
|
||||
// Create a ring for the top and bottom vertices (XY plane)
|
||||
std::vector<VertexXYZ> bottomVertices = createRingXYZ(nSegments, bottomRadius);
|
||||
std::vector<VertexXYZ> topVertices = createRingXYZ(nSegments, topRadius);
|
||||
|
||||
// Build the 4 rings of vertices (with different normals), that will make up the
|
||||
// shape for the cylinder
|
||||
std::vector<VertexXYZNormal> vertices;
|
||||
vertices.reserve(4 * bottomVertices.size() + 2);
|
||||
|
||||
// Center bottom vertex
|
||||
vertices.push_back({
|
||||
.xyz = { 0.f, 0.f, 0.f },
|
||||
.normal = { 0.f, 0.f, -1.f }
|
||||
});
|
||||
|
||||
std::vector<VertexXYZNormal> verts0;
|
||||
verts0.reserve(bottomVertices.size());
|
||||
std::vector<VertexXYZNormal> verts1;
|
||||
verts1.reserve(bottomVertices.size());
|
||||
std::vector<VertexXYZNormal> verts2;
|
||||
verts2.reserve(bottomVertices.size());
|
||||
std::vector<VertexXYZNormal> verts3;
|
||||
verts3.reserve(bottomVertices.size());
|
||||
|
||||
for (size_t i = 0; i < bottomVertices.size(); i++) {
|
||||
const VertexXYZ& vBot = bottomVertices[i];
|
||||
VertexXYZ& vTop = topVertices[i];
|
||||
vTop.xyz[2] += height;
|
||||
|
||||
glm::vec3 sideNormal;
|
||||
if (std::abs(bottomRadius - topRadius) < std::numeric_limits<float>::epsilon()) {
|
||||
sideNormal = glm::normalize(
|
||||
glm::vec3(vBot.xyz[0], vBot.xyz[1], vBot.xyz[2])
|
||||
);
|
||||
}
|
||||
else {
|
||||
glm::vec3 p = glm::closestPointOnLine(
|
||||
glm::vec3(0.f),
|
||||
glm::vec3(vBot.xyz[0], vBot.xyz[1], vBot.xyz[2]),
|
||||
glm::vec3(vTop.xyz[0], vTop.xyz[1], vTop.xyz[2])
|
||||
);
|
||||
sideNormal = glm::normalize(p);
|
||||
}
|
||||
|
||||
// Ring 0 - vertices of bottom circle, with normals pointing down
|
||||
verts0.push_back({
|
||||
.xyz = { vBot.xyz[0], vBot.xyz[1], vBot.xyz[2] },
|
||||
.normal = { 0.f, 0.f, -1.f }
|
||||
});
|
||||
|
||||
// Ring 1 - bottom vertices of cylider sides with normals pointing outwards
|
||||
verts1.push_back({
|
||||
.xyz = { vBot.xyz[0], vBot.xyz[1], vBot.xyz[2] },
|
||||
.normal = { sideNormal.x, sideNormal.y, sideNormal.z }
|
||||
});
|
||||
|
||||
// Ring 2 - top vertices of cylinder side, normals pointing outwards
|
||||
// Note that only difference between top and bottom is the height added to Z
|
||||
verts2.push_back({
|
||||
.xyz = { vTop.xyz[0], vTop.xyz[1], vTop.xyz[2] },
|
||||
.normal = { sideNormal.x, sideNormal.y, sideNormal.z }
|
||||
});
|
||||
|
||||
// Ring 3 - vertices of top circle, normals pointing up
|
||||
verts3.push_back({
|
||||
.xyz = { vTop.xyz[0], vTop.xyz[1], vTop.xyz[2] },
|
||||
.normal = { 0.f, 0.f, 1.f }
|
||||
});
|
||||
}
|
||||
|
||||
vertices.insert(vertices.end(), verts0.begin(), verts0.end());
|
||||
vertices.insert(vertices.end(), verts1.begin(), verts1.end());
|
||||
vertices.insert(vertices.end(), verts2.begin(), verts2.end());
|
||||
vertices.insert(vertices.end(), verts3.begin(), verts3.end());
|
||||
|
||||
// Center top vertex
|
||||
vertices.push_back({
|
||||
.xyz = { 0.f, 0.f, height },
|
||||
.normal = { 0.f, 0.f, 1.f }
|
||||
});
|
||||
|
||||
// Contruct the index list, based on the above vertex rings
|
||||
std::vector<GLushort> indexArray;
|
||||
indexArray.reserve(4 * 3 * nSegments);
|
||||
|
||||
auto ringVerticeIndex = [&nSegments](unsigned int ringIndex, unsigned int i) {
|
||||
return static_cast<GLushort>(1 + ringIndex * (nSegments + 1) + i);
|
||||
};
|
||||
|
||||
GLushort botCenterIndex = 0;
|
||||
GLushort topCenterIndex = static_cast<GLushort>(vertices.size()) - 1;
|
||||
|
||||
for (unsigned int i = 0; i < nSegments; ++i) {
|
||||
bool isLast = (i == nSegments - 1);
|
||||
GLushort v0, v1, v2, v3;
|
||||
|
||||
// Bottom triangle
|
||||
v0 = ringVerticeIndex(0, i);
|
||||
v1 = ringVerticeIndex(0, isLast ? 0 : i + 1);
|
||||
indexArray.push_back(botCenterIndex);
|
||||
indexArray.push_back(v1);
|
||||
indexArray.push_back(v0);
|
||||
|
||||
// Side of cylinder
|
||||
|
||||
// Bottom ring
|
||||
v0 = ringVerticeIndex(1, i);
|
||||
v1 = ringVerticeIndex(1, isLast ? 0 : i + 1);
|
||||
// Top ring
|
||||
v2 = ringVerticeIndex(2, i);
|
||||
v3 = ringVerticeIndex(2, isLast ? 0 : i + 1);
|
||||
indexArray.push_back(v0);
|
||||
indexArray.push_back(v1);
|
||||
indexArray.push_back(v2);
|
||||
|
||||
indexArray.push_back(v1);
|
||||
indexArray.push_back(v3);
|
||||
indexArray.push_back(v2);
|
||||
|
||||
// Top triangle
|
||||
v0 = ringVerticeIndex(3, i);
|
||||
v1 = ringVerticeIndex(3, isLast ? 0 : i + 1);
|
||||
indexArray.push_back(topCenterIndex);
|
||||
indexArray.push_back(v0);
|
||||
indexArray.push_back(v1);
|
||||
}
|
||||
|
||||
return { vertices, indexArray };
|
||||
}
|
||||
|
||||
VertexIndexListCombo<VertexXYZNormal> createCylinder(unsigned int nSegments,
|
||||
float radius, float height)
|
||||
{
|
||||
return createConicalCylinder(nSegments, radius, radius, height);
|
||||
}
|
||||
|
||||
VertexIndexListCombo<VertexXYZNormal> createCone(unsigned int nSegments, float radius,
|
||||
float height)
|
||||
{
|
||||
return createConicalCylinder(nSegments, radius, 0.f, height);
|
||||
}
|
||||
|
||||
void LightSourceRenderData::updateBasedOnLightSources(const RenderData& renderData,
|
||||
const std::vector<std::unique_ptr<LightSource>>& sources)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user