mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-02 01:30:34 -06:00
* Fix faulty log category and minor inconsistencies in GlobeTranslation and GlobeRotation * Small refactor * Remove unused return value from initialize function * Fill attached node in globetranslation initialize and initialize all timeline Should make the timeline translation work with globetranslations in other cases that the renderabletrails * Do the same type of change for rotation and scale * Update initialize function singatures * Initialize and update translation in trail before use * Screenspace renderable renderable - move initialize call to initialize * Refactor trail position calls to make it clearer what is being done * Call timeframe initialize functions in transform classes * Do not call update in trail position after all - it borks up the performance * GlobeTransform: Only fill attached node in init and onchange, and allow nodes without renderable * Correctly initialize the multi-transform types * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Alexander Bock <alexander.bock@liu.se> * Address code review comment * Update comments in renderabletrail --------- Co-authored-by: Alexander Bock <alexander.bock@liu.se>
767 lines
32 KiB
C++
767 lines
32 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* 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/rotation/fixedrotation.h>
|
|
|
|
#include <openspace/documentation/documentation.h>
|
|
#include <openspace/documentation/verifier.h>
|
|
#include <openspace/query/query.h>
|
|
#include <openspace/scene/scenegraphnode.h>
|
|
#include <ghoul/format.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/assert.h>
|
|
#include <ghoul/misc/profiling.h>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <variant>
|
|
|
|
namespace {
|
|
constexpr std::string_view _loggerCat = "FixedRotation";
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo EnableInfo = {
|
|
"Enable",
|
|
"Enabled",
|
|
"If this value is 'true', all the machinery of this rotation is used, of it is "
|
|
"'false', it provides the ability to change its attributes without risking some "
|
|
"undefined behavior.",
|
|
openspace::properties::Property::Visibility::User
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo XAxisTypeInfo = {
|
|
"xAxisType",
|
|
"xAxis: Specification Type",
|
|
"This value specifies how this axis is being specified, that is whether it is "
|
|
"referencing another object, specifying an absolute vector, or whether it is "
|
|
"using the right handed coordinate system completion based off the other two "
|
|
"vectors.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo YAxisTypeInfo = {
|
|
"yAxisType",
|
|
"yAxis: Specification Type",
|
|
"This value specifies how this axis is being specified, that is whether it is "
|
|
"referencing another object, specifying an absolute vector, or whether it is "
|
|
"using the right handed coordinate system completion based off the other two "
|
|
"vectors.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ZAxisTypeInfo = {
|
|
"zAxisType",
|
|
"zAxis: Specification Type",
|
|
"This value specifies how this axis is being specified, that is whether it is "
|
|
"referencing another object, specifying an absolute vector, or whether it is "
|
|
"using the right handed coordinate system completion based off the other two "
|
|
"vectors.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo XAxisObjectInfo = {
|
|
"xAxisObject",
|
|
"xAxis: Focus Object",
|
|
"This is the object that the axis will focus on. This object must name an "
|
|
"existing scene graph node in the currently loaded scene and the rotation will "
|
|
"stay fixed to the current position of that object.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo YAxisObjectInfo = {
|
|
"yAxisObject",
|
|
"yAxis: Focus Object",
|
|
"This is the object that the axis will focus on. This object must name an "
|
|
"existing scene graph node in the currently loaded scene and the rotation will "
|
|
"stay fixed to the current position of that object.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ZAxisObjectInfo = {
|
|
"zAxisObject",
|
|
"zAxis: Focus Object",
|
|
"This is the object that the axis will focus on. This object must name an "
|
|
"existing scene graph node in the currently loaded scene and the rotation will "
|
|
"stay fixed to the current position of that object.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo XAxisInvertObjectInfo = {
|
|
"xAxisInvertObject",
|
|
"xAxis: Invert Object Point Direction",
|
|
"If this value is set to 'true', and the type is set to 'Object', the inverse of "
|
|
"the pointing direction is used, causing the object to point away from the "
|
|
"referenced object.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo YAxisInvertObjectInfo = {
|
|
"yAxisInvertObject",
|
|
"yAxis: Invert Object Point Direction",
|
|
"If this value is set to 'true', and the type is set to 'Object', the inverse of "
|
|
"the pointing direction is used, causing the object to point away from the "
|
|
"referenced object.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ZAxisInvertObjectInfo = {
|
|
"zAxisInvertObject",
|
|
"zAxis: Invert Object Point Direction",
|
|
"If this value is set to 'true', and the type is set to 'Object', the inverse of "
|
|
"the pointing direction is used, causing the object to point away from the "
|
|
"referenced object.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo XAxisVectorInfo = {
|
|
"xAxisVector",
|
|
"xAxis: Direction vector",
|
|
"This value specifies a static direction vector that is used for a fixed "
|
|
"rotation.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo YAxisVectorInfo = {
|
|
"yAxisVector",
|
|
"yAxis: Direction vector",
|
|
"This value specifies a static direction vector that is used for a fixed "
|
|
"rotation.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ZAxisVectorInfo = {
|
|
"zAxisVector",
|
|
"zAxis: Direction vector",
|
|
"This value specifies a static direction vector that is used for a fixed "
|
|
"rotation.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo XAxisOrthogonalVectorInfo = {
|
|
"xAxisOrthogonal",
|
|
"xAxis: Vector is orthogonal",
|
|
"This value determines whether the vector specified is used directly, or whether "
|
|
"it is used together with another non-coordinate system completion vector to "
|
|
"construct an orthogonal vector instead.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo YAxisOrthogonalVectorInfo = {
|
|
"yAxisOrthogonal",
|
|
"yAxis: Vector is orthogonal",
|
|
"This value determines whether the vector specified is used directly, or whether "
|
|
"it is used together with another non-coordinate system completion vector to "
|
|
"construct an orthogonal vector instead.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ZAxisOrthogonalVectorInfo = {
|
|
"zAxisOrthogonal",
|
|
"zAxis: Vector is orthogonal",
|
|
"This value determines whether the vector specified is used directly, or whether "
|
|
"it is used together with another non-coordinate system completion vector to "
|
|
"construct an orthogonal vector instead.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo AttachedInfo = {
|
|
"Attached",
|
|
"Attached Node",
|
|
"This is the name of the node that this rotation is attached to, this value is "
|
|
"only needed if any of the three axis uses the Object type. In this case, the "
|
|
"location of the attached node is required to compute the relative direction.",
|
|
openspace::properties::Property::Visibility::AdvancedUser
|
|
};
|
|
|
|
// This `Rotation` calculates the rotation in such a way that the attached scene graph
|
|
// node will always be relative to some other direction or pointing at another scene
|
|
// graph node.
|
|
//
|
|
// The first use-case of the `FixedRotation` needs exactly two of its three axes
|
|
// (`XAxis`, `YAxis`, `ZAxis`) specified with the last axis being unspecified. The
|
|
// axis that is missing will be calculated using a right-handed coordinate completion
|
|
// using the two provided axes. Each axis can be specified either by providing the
|
|
// `Identifier` of another scene graph node, or by providing a direction vector. For
|
|
// the general use-case of this `Rotation`, one of the provided axes usually is an
|
|
// `Identifier` and the other is a direction vector (see the examples below).
|
|
// If any of the axes is using the `Identifier` of another scene graph node, the
|
|
// `Attached` value must be specified and should almost always be the identifier of
|
|
// the scene graph node to which this `Rotation` belongs.
|
|
//
|
|
// A second use-case for this `Rotation` is to directly specify mappings for the
|
|
// coordinate axes. In this use-case, two or all three axes are specified using
|
|
// direction vectors. The orientation of the rotated coordinate system will then be
|
|
// determined by the specified axes, such that the new x-axis points in the direction
|
|
// provided to the `XAxis`, for example. Note that all direction vectors are assumed
|
|
// to be normalized. If only two direction vectors are specified, the third direction
|
|
// is computed using a right-handed coordinate system completion.
|
|
//
|
|
// Each axis has an `invert` option that will cause the provided axes to be considered
|
|
// inverted. For the direction-type of axis, this just inverts the provided values,
|
|
// but it is more useful for the `Identifier` version to construct a direction that
|
|
// either points towards or away from the provided scene graph node.
|
|
//
|
|
// Lastly, each axis has an `orthogonal` option. If that value is specified, the
|
|
// provided axis is instead cross-producted with the other axis first with the
|
|
// resulting orthogonal vector used as the mapped axis. This is primarily useful when
|
|
// specifying a direction vector and wanting to ensure that the total rotation remains
|
|
// a valid non-skewed rotation (meaning that all three coordinate axes are orthogonal
|
|
// to each other) when the second axis can assume arbitrary values. Unless there is a
|
|
// very good reason not to, whenever an axis is specified as a direction vector, that
|
|
// axis' `orthogonal` setting should also probably be enabled.
|
|
struct [[codegen::Dictionary(FixedRotation)]] Parameters {
|
|
// [[codegen::verbatim(AttachedInfo.description)]]
|
|
std::optional<std::string> attached;
|
|
|
|
// This value specifies the direction of the new X axis. If this value is not
|
|
// specified, it will be computed by completing a right handed coordinate system
|
|
// from the Y and Z axis, which must be specified instead. If this value is a
|
|
// string, it is interpreted as the identifier of another scenegraph node. If this
|
|
// value is a 3-vector, it is interpreted as a direction vector
|
|
std::optional<std::variant<std::string, glm::vec3>> xAxis;
|
|
|
|
// [[codegen::verbatim(XAxisOrthogonalVectorInfo.description)]]
|
|
std::optional<bool> xAxisOrthogonal;
|
|
|
|
// [[codegen::verbatim(XAxisInvertObjectInfo.description)]]
|
|
std::optional<bool> xAxisInvert;
|
|
|
|
// This value specifies the direction of the new Y axis. If this value is not
|
|
// specified, it will be computed by completing a right handed coordinate system
|
|
// from the X and Z axis, which must be specified instead. If this value is a
|
|
// string, it is interpreted as the identifier of another scenegraph node. If this
|
|
// value is a 3-vector, it is interpreted as a direction vector
|
|
std::optional<std::variant<std::string, glm::vec3>> yAxis;
|
|
|
|
// [[codegen::verbatim(YAxisOrthogonalVectorInfo.description)]]
|
|
std::optional<bool> yAxisOrthogonal;
|
|
|
|
// [[codegen::verbatim(YAxisInvertObjectInfo.description)]]
|
|
std::optional<bool> yAxisInvert;
|
|
|
|
// This value specifies the direction of the new Z axis. If this value is not
|
|
// specified, it will be computed by completing a right handed coordinate system
|
|
// from the X and Y axis, which must be specified instead. If this value is a
|
|
// string, it is interpreted as the identifier of another scenegraph node. If this
|
|
// value is a 3-vector, it is interpreted as a direction vector
|
|
std::optional<std::variant<std::string, glm::vec3>> zAxis;
|
|
|
|
// [[codegen::verbatim(ZAxisOrthogonalVectorInfo.description)]]
|
|
std::optional<bool> zAxisOrthogonal;
|
|
|
|
// [[codegen::verbatim(ZAxisInvertObjectInfo.description)]]
|
|
std::optional<bool> zAxisInvert;
|
|
};
|
|
#include "fixedrotation_codegen.cpp"
|
|
} // namespace
|
|
|
|
namespace openspace {
|
|
|
|
documentation::Documentation FixedRotation::Documentation() {
|
|
return codegen::doc<Parameters>("base_transform_rotation_fixed");
|
|
}
|
|
|
|
FixedRotation::FixedRotation(const ghoul::Dictionary& dictionary)
|
|
: Rotation(dictionary)
|
|
, _enabled(EnableInfo, true)
|
|
, _xAxis{
|
|
properties::OptionProperty(XAxisTypeInfo),
|
|
properties::StringProperty(XAxisObjectInfo, ""),
|
|
properties::BoolProperty(XAxisInvertObjectInfo, false),
|
|
properties::Vec3Property(
|
|
XAxisVectorInfo,
|
|
glm::vec3(1.f, 0.f, 0.f),
|
|
glm::vec3(-1.f),
|
|
glm::vec3(1.f)
|
|
),
|
|
properties::BoolProperty(XAxisOrthogonalVectorInfo, false),
|
|
nullptr
|
|
}
|
|
, _yAxis{
|
|
properties::OptionProperty(YAxisTypeInfo),
|
|
properties::StringProperty(YAxisObjectInfo, ""),
|
|
properties::BoolProperty(YAxisInvertObjectInfo, false),
|
|
properties::Vec3Property(
|
|
YAxisVectorInfo,
|
|
glm::vec3(0.f, 1.f, 0.f),
|
|
glm::vec3(-1.f),
|
|
glm::vec3(1.f)
|
|
),
|
|
properties::BoolProperty(YAxisOrthogonalVectorInfo, false),
|
|
nullptr
|
|
}
|
|
, _zAxis{
|
|
properties::OptionProperty(ZAxisTypeInfo),
|
|
properties::StringProperty(ZAxisObjectInfo, ""),
|
|
properties::BoolProperty(ZAxisInvertObjectInfo, false),
|
|
properties::Vec3Property(
|
|
ZAxisVectorInfo,
|
|
glm::vec3(0.f, 0.f, 1.f),
|
|
glm::vec3(-1.f),
|
|
glm::vec3(1.f)
|
|
),
|
|
properties::BoolProperty(ZAxisOrthogonalVectorInfo, false),
|
|
nullptr
|
|
}
|
|
, _attachedObject(AttachedInfo, "")
|
|
, _constructorDictionary(dictionary)
|
|
{
|
|
// We check the Dictionary here in order to detect the errors early
|
|
codegen::bake<Parameters>(dictionary);
|
|
|
|
documentation::testSpecificationAndThrow(
|
|
Documentation(),
|
|
dictionary,
|
|
"FixedRotation"
|
|
);
|
|
|
|
setPropertyGroupName("global", "Global");
|
|
setPropertyGroupName("xAxis", "X Axis");
|
|
setPropertyGroupName("yAxis", "Y Axis");
|
|
setPropertyGroupName("zAxis", "Z Axis");
|
|
|
|
_enabled.setGroupIdentifier("global");
|
|
addProperty(_enabled);
|
|
|
|
_attachedObject.setGroupIdentifier("global");
|
|
addProperty(_attachedObject);
|
|
_attachedObject.onChange([this](){
|
|
_attachedNode = sceneGraphNode(_attachedObject);
|
|
});
|
|
|
|
auto setPropertyVisibility = [](Axis& axis) {
|
|
using Visibility = properties::Property::Visibility;
|
|
switch (axis.type) {
|
|
case Axis::Type::Object:
|
|
axis.object.setVisibility(Visibility::User);
|
|
axis.invertObject.setVisibility(Visibility::User);
|
|
axis.vector.setVisibility(Visibility::Hidden);
|
|
break;
|
|
case Axis::Type::Vector:
|
|
case Axis::Type::OrthogonalVector:
|
|
axis.object.setVisibility(Visibility::Hidden);
|
|
axis.invertObject.setVisibility(Visibility::Hidden);
|
|
axis.vector.setVisibility(Visibility::User);
|
|
break;
|
|
case Axis::Type::CoordinateSystemCompletion:
|
|
axis.object.setVisibility(Visibility::Hidden);
|
|
axis.invertObject.setVisibility(Visibility::Hidden);
|
|
axis.vector.setVisibility(Visibility::Hidden);
|
|
break;
|
|
}
|
|
};
|
|
|
|
_xAxis.type.addOptions({
|
|
{ Axis::Type::Object, "Object" },
|
|
{ Axis::Type::Vector, "Vector" },
|
|
{ Axis::Type::OrthogonalVector, "Orthogonal Vector" },
|
|
{ Axis::Type::CoordinateSystemCompletion, "Coordinate System Completion" }
|
|
});
|
|
_xAxis.type.setGroupIdentifier("xAxis");
|
|
_xAxis.type.onChange(
|
|
[this, setPropertyVisibility]() { setPropertyVisibility(_xAxis); }
|
|
);
|
|
addProperty(_xAxis.type);
|
|
|
|
_xAxis.object.setGroupIdentifier("xAxis");
|
|
addProperty(_xAxis.object);
|
|
_xAxis.object.onChange([this](){
|
|
_xAxis.node = sceneGraphNode(_xAxis.object);
|
|
});
|
|
|
|
_xAxis.invertObject.setGroupIdentifier("xAxis");
|
|
addProperty(_xAxis.invertObject);
|
|
|
|
_xAxis.vector.setGroupIdentifier("xAxis");
|
|
addProperty(_xAxis.vector);
|
|
|
|
|
|
_yAxis.type.addOptions({
|
|
{ Axis::Type::Object, "Object" },
|
|
{ Axis::Type::Vector, "Vector" },
|
|
{ Axis::Type::OrthogonalVector, "Orthogonal Vector" },
|
|
{ Axis::Type::CoordinateSystemCompletion, "Coordinate System Completion" }
|
|
});
|
|
_yAxis.type.setGroupIdentifier("yAxis");
|
|
_yAxis.type.onChange(
|
|
[this, setPropertyVisibility]() { setPropertyVisibility(_yAxis); }
|
|
);
|
|
addProperty(_yAxis.type);
|
|
|
|
_yAxis.object.setGroupIdentifier("yAxis");
|
|
addProperty(_yAxis.object);
|
|
_yAxis.object.onChange([this]() { _yAxis.node = sceneGraphNode(_yAxis.object); });
|
|
|
|
_yAxis.invertObject.setGroupIdentifier("yAxis");
|
|
addProperty(_yAxis.invertObject);
|
|
|
|
_yAxis.vector.setGroupIdentifier("yAxis");
|
|
addProperty(_yAxis.vector);
|
|
|
|
_zAxis.type.addOptions({
|
|
{ Axis::Type::Object, "Object" },
|
|
{ Axis::Type::Vector, "Vector" },
|
|
{ Axis::Type::OrthogonalVector, "Orthogonal Vector" },
|
|
{ Axis::Type::CoordinateSystemCompletion, "Coordinate System Completion" }
|
|
});
|
|
_zAxis.type.setGroupIdentifier("zAxis");
|
|
_zAxis.type.onChange(
|
|
[this, setPropertyVisibility]() { setPropertyVisibility(_zAxis); }
|
|
);
|
|
addProperty(_zAxis.type);
|
|
|
|
_zAxis.object.setGroupIdentifier("zAxis");
|
|
addProperty(_zAxis.object);
|
|
_zAxis.object.onChange([this]() { _zAxis.node = sceneGraphNode(_zAxis.object); });
|
|
|
|
_zAxis.invertObject.setGroupIdentifier("zAxis");
|
|
addProperty(_zAxis.invertObject);
|
|
|
|
_zAxis.vector.setGroupIdentifier("zAxis");
|
|
addProperty(_zAxis.vector);
|
|
|
|
setPropertyVisibility(_xAxis);
|
|
setPropertyVisibility(_yAxis);
|
|
setPropertyVisibility(_zAxis);
|
|
}
|
|
|
|
void FixedRotation::initialize() {
|
|
ZoneScoped;
|
|
|
|
// We have already checked this before, but still
|
|
const Parameters p = codegen::bake<Parameters>(_constructorDictionary);
|
|
|
|
// We need to do this in the initialize and not the constructor as the scene graph
|
|
// nodes referenced in the dictionary might not exist yet at construction time. At
|
|
// initialization time, however, we know that they already have been created
|
|
|
|
Rotation::initialize();
|
|
|
|
_attachedObject = p.attached.value_or(_attachedObject);
|
|
|
|
if (p.xAxis.has_value()) {
|
|
if (std::holds_alternative<std::string>(*p.xAxis)) {
|
|
_xAxis.type = Axis::Type::Object;
|
|
_xAxis.object = std::get<std::string>(*p.xAxis);
|
|
}
|
|
else {
|
|
ghoul_assert(std::holds_alternative<glm::vec3>(*p.xAxis), "");
|
|
_xAxis.type = Axis::Type::Vector;
|
|
_xAxis.vector = std::get<glm::vec3>(*p.xAxis);
|
|
}
|
|
}
|
|
_xAxis.isOrthogonal = p.xAxisOrthogonal.value_or(_xAxis.isOrthogonal);
|
|
if (_xAxis.isOrthogonal) {
|
|
_xAxis.type = Axis::Type::OrthogonalVector;
|
|
}
|
|
_xAxis.invertObject = p.xAxisInvert.value_or(_xAxis.invertObject);
|
|
|
|
if (p.yAxis.has_value()) {
|
|
if (std::holds_alternative<std::string>(*p.yAxis)) {
|
|
_yAxis.type = Axis::Type::Object;
|
|
_yAxis.object = std::get<std::string>(*p.yAxis);
|
|
}
|
|
else {
|
|
ghoul_assert(std::holds_alternative<glm::vec3>(*p.yAxis), "");
|
|
_yAxis.type = Axis::Type::Vector;
|
|
_yAxis.vector = std::get<glm::vec3>(*p.yAxis);
|
|
}
|
|
}
|
|
_yAxis.isOrthogonal = p.yAxisOrthogonal.value_or(_yAxis.isOrthogonal);
|
|
if (_yAxis.isOrthogonal) {
|
|
_yAxis.type = Axis::Type::OrthogonalVector;
|
|
}
|
|
_yAxis.invertObject = p.yAxisInvert.value_or(_yAxis.invertObject);
|
|
|
|
if (p.zAxis.has_value()) {
|
|
if (std::holds_alternative<std::string>(*p.zAxis)) {
|
|
_zAxis.type = Axis::Type::Object;
|
|
_zAxis.object = std::get<std::string>(*p.zAxis);
|
|
}
|
|
else {
|
|
ghoul_assert(std::holds_alternative<glm::vec3>(*p.zAxis), "");
|
|
_zAxis.type = Axis::Type::Vector;
|
|
_zAxis.vector = std::get<glm::vec3>(*p.zAxis);
|
|
}
|
|
}
|
|
_zAxis.isOrthogonal = p.zAxisOrthogonal.value_or(_zAxis.isOrthogonal);
|
|
if (_zAxis.isOrthogonal) {
|
|
_zAxis.type = Axis::Type::OrthogonalVector;
|
|
}
|
|
_zAxis.invertObject = p.zAxisInvert.value_or(_zAxis.invertObject);
|
|
|
|
|
|
|
|
if (!p.xAxis.has_value() && p.yAxis.has_value() && p.zAxis.has_value()) {
|
|
_xAxis.type = Axis::Type::CoordinateSystemCompletion;
|
|
}
|
|
|
|
if (p.xAxis.has_value() && !p.yAxis.has_value() && p.zAxis.has_value()) {
|
|
_yAxis.type = Axis::Type::CoordinateSystemCompletion;
|
|
}
|
|
|
|
if (p.xAxis.has_value() && p.yAxis.has_value() && !p.zAxis.has_value()) {
|
|
_zAxis.type = Axis::Type::CoordinateSystemCompletion;
|
|
}
|
|
|
|
// No need to hold on to the data
|
|
_constructorDictionary = ghoul::Dictionary();
|
|
}
|
|
|
|
void FixedRotation::update(const UpdateData& data) {
|
|
const bool anyAxisIsObjectType = (
|
|
_xAxis.type == Axis::Type::Object ||
|
|
_yAxis.type == Axis::Type::Object ||
|
|
_zAxis.type == Axis::Type::Object
|
|
);
|
|
|
|
// @TODO (2024-06-15, abock) None of the following four checks should be necessary
|
|
// as the nodes are retrieved whenever the property value is changed, but we
|
|
// had an initialization issue and this was the fastest way to fix the bug
|
|
if (!_attachedNode) {
|
|
_attachedNode = sceneGraphNode(_attachedObject);
|
|
}
|
|
if (_xAxis.type == Axis::Type::Object && !_xAxis.node) {
|
|
_xAxis.node = sceneGraphNode(_xAxis.object);
|
|
}
|
|
if (_yAxis.type == Axis::Type::Object && !_yAxis.node) {
|
|
_yAxis.node = sceneGraphNode(_yAxis.object);
|
|
}
|
|
if (_zAxis.type == Axis::Type::Object && !_zAxis.node) {
|
|
_zAxis.node = sceneGraphNode(_zAxis.object);
|
|
}
|
|
|
|
if (_attachedNode || anyAxisIsObjectType) {
|
|
requireUpdate();
|
|
}
|
|
|
|
Rotation::update(data);
|
|
}
|
|
|
|
glm::dmat3 FixedRotation::matrix(const UpdateData&) const {
|
|
if (!_enabled) {
|
|
return glm::dmat3();
|
|
}
|
|
|
|
const glm::vec3 x = xAxis();
|
|
const glm::vec3 y = yAxis();
|
|
const glm::vec3 z = zAxis();
|
|
|
|
constexpr float Epsilon = 1e-3f;
|
|
|
|
if (glm::dot(x, y) > 1.f - Epsilon ||
|
|
glm::dot(y, z) > 1.f - Epsilon ||
|
|
glm::dot(x, z) > 1.f - Epsilon) [[unlikely]]
|
|
{
|
|
LWARNING(
|
|
std::format(
|
|
"Near-collinear vectors detected: "
|
|
"x ({}, {}, {}) y ({}, {}, {}) z ({}, {}, {})",
|
|
x.x, x.y, x.z, y.x, y.y, y.z, z.x, z.y, z.z
|
|
)
|
|
);
|
|
return glm::dmat3();
|
|
}
|
|
|
|
return {
|
|
x.x, x.y, x.z,
|
|
y.x, y.y, y.z,
|
|
z.x, z.y, z.z
|
|
};
|
|
}
|
|
|
|
glm::vec3 FixedRotation::xAxis() const {
|
|
switch (_xAxis.type) {
|
|
case Axis::Type::Unspecified:
|
|
LWARNING("Unspecified axis type for X axis");
|
|
return glm::vec3(1.f, 0.f, 0.f);
|
|
case Axis::Type::Object:
|
|
if (_xAxis.node && _attachedNode) {
|
|
glm::dvec3 dir = _xAxis.node->worldPosition() -
|
|
_attachedNode->worldPosition();
|
|
|
|
if (dir == glm::dvec3(0.0)) {
|
|
dir = glm::dvec3(1.0, 0.0, 0.0);
|
|
}
|
|
const glm::vec3 dirNorm = glm::vec3(glm::normalize(dir));
|
|
return _xAxis.invertObject ? -dirNorm : dirNorm;
|
|
}
|
|
else {
|
|
if (_xAxis.node) {
|
|
LWARNING("Missing attachment node");
|
|
return glm::vec3(1.f, 0.f, 0.f);
|
|
}
|
|
else {
|
|
LWARNING(std::format(
|
|
"Missing node '{}' for X axis", _xAxis.object.value()
|
|
));
|
|
return glm::vec3(1.f, 0.f, 0.f);
|
|
}
|
|
}
|
|
case Axis::Type::Vector:
|
|
if (_xAxis.vector.value() == glm::vec3(0.f)) {
|
|
LWARNING("Zero vector detected for X Axis");
|
|
return glm::vec3(1.f, 0.f, 0.f);
|
|
}
|
|
else {
|
|
return glm::normalize(_xAxis.vector.value());
|
|
}
|
|
case Axis::Type::OrthogonalVector:
|
|
if (_xAxis.vector.value() == glm::vec3(0.f)) {
|
|
LWARNING("Zero vector detected for X Axis");
|
|
return glm::vec3(1.f, 0.f, 0.f);
|
|
}
|
|
else {
|
|
if (_yAxis.type != Axis::Type::CoordinateSystemCompletion) {
|
|
return glm::normalize(
|
|
glm::cross(_xAxis.vector.value(), yAxis())
|
|
);
|
|
}
|
|
else {
|
|
return glm::normalize(
|
|
glm::cross(_xAxis.vector.value(), zAxis())
|
|
);
|
|
}
|
|
}
|
|
case Axis::Type::CoordinateSystemCompletion:
|
|
return glm::normalize(-glm::cross(yAxis(), zAxis()));
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
glm::vec3 FixedRotation::yAxis() const {
|
|
switch (_yAxis.type) {
|
|
case Axis::Type::Unspecified:
|
|
LWARNING("Unspecified axis type for Y axis");
|
|
return glm::vec3(0.f, 1.f, 0.f);
|
|
case Axis::Type::Object:
|
|
if (_yAxis.node && _attachedNode) {
|
|
const glm::vec3 dir = glm::vec3(glm::normalize(
|
|
// @TODO(abock): This should be changed to be in the coordinate system
|
|
// of the attached node // same with xAxis and zAxis ofc
|
|
_yAxis.node->worldPosition() - _attachedNode->worldPosition()
|
|
));
|
|
return _yAxis.invertObject ? -dir : dir;
|
|
}
|
|
else {
|
|
if (_yAxis.node) {
|
|
LWARNING("Missing attachment node");
|
|
return glm::vec3(0.f, 1.f, 0.f);
|
|
}
|
|
else {
|
|
LWARNING(std::format(
|
|
"Missing node '{}' for Y axis", _yAxis.object.value()
|
|
));
|
|
return glm::vec3(0.f, 1.f, 0.f);
|
|
}
|
|
}
|
|
case Axis::Type::Vector:
|
|
if (_yAxis.vector.value() == glm::vec3(0.f)) {
|
|
LWARNING("Zero vector detected for Y Axis");
|
|
return glm::vec3(0.f, 1.f, 0.f);
|
|
}
|
|
else {
|
|
return glm::normalize(_yAxis.vector.value());
|
|
}
|
|
case Axis::Type::OrthogonalVector:
|
|
if (_yAxis.vector.value() == glm::vec3(0.f)) {
|
|
LWARNING("Zero vector detected for Y Axis");
|
|
return glm::vec3(0.f, 1.f, 0.f);
|
|
}
|
|
else {
|
|
if (_zAxis.type != Axis::Type::CoordinateSystemCompletion) {
|
|
return glm::normalize(
|
|
glm::cross(_yAxis.vector.value(), zAxis())
|
|
);
|
|
}
|
|
else {
|
|
return glm::normalize(
|
|
glm::cross(_yAxis.vector.value(), xAxis())
|
|
);
|
|
}
|
|
}
|
|
case Axis::Type::CoordinateSystemCompletion:
|
|
return glm::normalize(glm::cross(xAxis(), -zAxis()));
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
glm::vec3 FixedRotation::zAxis() const {
|
|
switch (_zAxis.type) {
|
|
case Axis::Type::Unspecified:
|
|
LWARNING("Unspecified axis type for Z axis");
|
|
return glm::vec3(0.f, 0.f, 1.f);
|
|
case Axis::Type::Object:
|
|
if (_zAxis.node && _attachedNode) {
|
|
const glm::vec3 dir = glm::vec3(glm::normalize(
|
|
_zAxis.node->worldPosition() - _attachedNode->worldPosition()
|
|
));
|
|
return _zAxis.invertObject ? -dir : dir;
|
|
}
|
|
else {
|
|
if (_zAxis.node) {
|
|
LWARNING("Missing attachment node");
|
|
return glm::vec3(0.f, 0.f, 1.f);
|
|
}
|
|
else {
|
|
LWARNING(std::format(
|
|
"Missing node '{}' for Z axis", _zAxis.object.value()
|
|
));
|
|
return glm::vec3(0.f, 0.f, 1.f);
|
|
}
|
|
}
|
|
case Axis::Type::Vector:
|
|
if (_zAxis.vector.value() == glm::vec3(0.f)) {
|
|
LWARNING("Zero vector detected for Z Axis");
|
|
return glm::vec3(0.f, 0.f, 1.f);
|
|
}
|
|
else {
|
|
return glm::normalize(_zAxis.vector.value());
|
|
}
|
|
case Axis::Type::OrthogonalVector:
|
|
if (_zAxis.vector.value() == glm::vec3(0.f)) {
|
|
LWARNING("Zero vector detected for Z Axis");
|
|
return glm::vec3(0.f, 0.f, 1.f);
|
|
}
|
|
else {
|
|
if (_xAxis.type != Axis::Type::CoordinateSystemCompletion) {
|
|
return glm::normalize(
|
|
glm::cross(_zAxis.vector.value(), xAxis())
|
|
);
|
|
}
|
|
else {
|
|
return glm::normalize(
|
|
glm::cross(_zAxis.vector.value(), yAxis())
|
|
);
|
|
}
|
|
}
|
|
case Axis::Type::CoordinateSystemCompletion:
|
|
return glm::normalize(glm::cross(xAxis(), yAxis()));
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
} // namespace openspace
|