Touch module code cleanup (#2465)

* Remove unused feature that allowed us to "pick" nodes using touch (it didn't really work and had some nasty hardcoded and costly implementation). Fixes Touch interaction picking refactor #1041

* General refactoring of code and removing redundant code

* Make touch markers prettier (change default color and smoothen edges)

* Add module property to control which renderable types are "directly touchable"

* Add SGN property to control which individual nodes are "directly touchable" ("SupportsDirectInteraction")

* Fix stuttering when zooming in closer than the orbitalnavigator allows
This commit is contained in:
Emma Broman
2023-02-14 13:55:24 +01:00
committed by GitHub
parent 9d53b1f62d
commit 352c9dd5ec
15 changed files with 464 additions and 492 deletions

View File

@@ -19,6 +19,7 @@ asset.require("util/launcher_images")
-- Modules and component settings
asset.require("modules/exoplanets/exoplanets")
asset.require("modules/skybrowser/skybrowser")
asset.require("modules/touch/default_settings")
asset.onInitialize(function ()

View File

@@ -0,0 +1,19 @@
asset.onInitialize(function ()
openspace.setPropertyValueSingle("Modules.Touch.EnableTouchInteraction", true)
-- A list of renderable types that apply the "direct manipulation". Works best for
-- things with a sperical-ish shape and an intearction sphere of about the same size
-- as the bounding sphere.
-- Can also be set for each scene graph node using the "IsDirectlyTouchable" property
local directTouchList = { "RenderableGlobe" }
openspace.setPropertyValueSingle("Modules.Touch.DefaultDirectTouchRenderableTypes", directTouchList)
end)
asset.meta = {
Name = "Touch Module Default Settings",
Version = "1.0",
Description = "Some default settings related to the touch module",
Author = "OpenSpace Team",
URL = "http://openspaceproject.com",
License = "MIT license"
}

View File

@@ -129,9 +129,18 @@ public:
bool hasZoomFriction() const;
bool hasRollFriction() const;
double minAllowedDistance() const;
glm::dvec3 anchorNodeToCameraVector() const;
glm::quat anchorNodeToCameraRotation() const;
/**
* Compute a camera position that pushed the camera position to
* a valid position over the anchor node, accounting for the
* minimal allowed distance
*/
glm::dvec3 pushToSurfaceOfAnchor(const glm::dvec3& cameraPosition) const;
private:
struct CameraRotationDecomposition {
glm::dquat localRotation = glm::dquat(1.0, 0.0, 0.0, 0.0);
@@ -328,11 +337,11 @@ private:
/**
* Push the camera out to the surface of the object.
*
* \return a position vector adjusted to be at least minHeightAboveGround meters
* \return a position vector adjusted to be at least _minimumAllowedDistance meters
* above the actual surface of the object
*/
glm::dvec3 pushToSurface(double minHeightAboveGround,
const glm::dvec3& cameraPosition, const glm::dvec3& objectPosition,
glm::dvec3 pushToSurface(const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const SurfacePositionHandle& positionHandle) const;
/**
@@ -351,7 +360,7 @@ private:
* Calculates a SurfacePositionHandle given a camera position in world space.
*/
SurfacePositionHandle calculateSurfacePositionHandle(const SceneGraphNode& node,
const glm::dvec3 cameraPositionWorldSpace);
const glm::dvec3& cameraPositionWorldSpace) const;
void resetIdleBehavior();

View File

@@ -33,6 +33,7 @@
#include <openspace/properties/stringproperty.h>
#include <openspace/scene/scenegraphnode.h>
#include <ghoul/misc/managedmemoryuniqueptr.h>
#include <string_view>
namespace ghoul { class Dictionary; }
namespace ghoul::opengl {
@@ -79,6 +80,8 @@ public:
double boundingSphere() const;
double interactionSphere() const;
std::string_view typeAsString() const;
virtual void render(const RenderData& data, RendererTasks& rendererTask);
virtual void update(const UpdateData& data);

View File

@@ -139,6 +139,8 @@ public:
double reachFactor() const;
double approachFactor() const;
bool supportsDirectInteraction() const;
SceneGraphNode* childNode(const std::string& identifier);
const Renderable* renderable() const;
@@ -204,6 +206,7 @@ private:
properties::DoubleProperty _distFromCamToNode;
properties::DoubleProperty _screenSizeRadius;
properties::FloatProperty _visibilityDistance;
properties::BoolProperty _supportsDirectInteraction;
// This variable is used for the rate-limiting of the screenspace positions (if they
// are calculated when _computeScreenSpaceValues is true)

View File

@@ -41,7 +41,6 @@
#include <memory>
//#define TOUCH_DEBUG_PROPERTIES
//#define TOUCH_DEBUG_NODE_PICK_MESSAGES
namespace openspace {
@@ -51,9 +50,9 @@ class SceneGraphNode;
// Class used for keeping track of the recent average frame time
class FrameTimeAverage {
public:
//Update the circular buffer with the most recent frame time
// Update the circular buffer with the most recent frame time
void updateWithNewFrame(double sample);
//Get the value of the most recent average frame time (seconds)
// Get the value of the most recent average frame time (seconds)
double averageFrameTime() const;
private:
@@ -67,13 +66,11 @@ class TouchInteraction : public properties::PropertyOwner {
public:
TouchInteraction();
// for interpretInteraction()
enum Type {
enum class InteractionType {
ROTATION = 0,
PINCH,
PAN,
ROLL,
PICK,
ZOOM_OUT
};
@@ -85,18 +82,19 @@ public:
glm::dvec2 pan = glm::dvec2(0.0);
};
/* Main function call
/**
* Main function call
* 1 Checks if doubleTap occured
* 2 If the node in focus is large enough and all contact points have selected it,
* calls directControl() function for direct-manipulation
* 3 Updates std::vector<SelectedBody> _selected (only if LMA successfully
* 3 Updates std::vector<SelectedBody> _selectedContactPoints (only if LMA successfully
* converged, avoids interaction to snap on LMA fails)
* 4 If directControl() wasn't called this frame, interpret the incoming
* list and decide what type of interaction this frame should do
* 5 Compute the new total velocities after interaction
* 6 Evaluate if directControl should be called next frame- true if all contact points
* select the same node and said node is larger than _nodeRadiusThreshold
*/
*/
void updateStateFromInput(const std::vector<TouchInputHolder>& list,
std::vector<TouchInput>& lastProcessed);
@@ -109,46 +107,43 @@ public:
// Sets _tap to true, called if tap occured current frame (called from touchmodule)
void tap();
// Set touchactive as true from the touchmodule if incoming list isn't empty, used to
// void mouse input
void touchActive(bool active);
// Get & Setters
Camera* getCamera();
const SceneGraphNode* getFocusNode();
void setFocusNode(const SceneGraphNode* focusNode);
void setCamera(Camera* camera);
private:
/* Function that calculates the new camera state such that it minimizes the L2 error
/**
* Function that calculates the new camera state such that it minimizes the L2 error
* in screenspace
* between contact points and surface coordinates projected to clip space using LMA
*/
void directControl(const std::vector<TouchInputHolder>& list);
/* Traces each contact point into the scene as a ray
* if the ray hits a node, save the id, node and surface coordinates the cursor hit
* in the list _selected
/**
* Traces each contact point into the scene as a ray and find the intersection
* points on the surface of the current anchor node, if any. Saves the input id
* the node and surface coordinates the cursor hit
*/
void findSelectedNode(const std::vector<TouchInputHolder>& list);
void updateNodeSurfacePoints(const std::vector<TouchInputHolder>& list);
/* Returns an int (ROT = 0, PINCH, PAN, ROLL, PICK) for what interaction to be used,
* depending on what input was gotten
/**
* Returns an enum for what interaction to be used, depending on what input was
* received
*/
int interpretInteraction(const std::vector<TouchInputHolder>& list,
InteractionType interpretInteraction(const std::vector<TouchInputHolder>& list,
const std::vector<TouchInput>& lastProcessed);
// Compute new velocity according to the interpreted action
void computeVelocities(const std::vector<TouchInputHolder>& list,
const std::vector<TouchInput>& lastProcessed);
//Compute velocity based on double-tap for zooming
// Compute velocity based on double-tap for zooming
double computeTapZoomDistance(double zoomGain);
//Compute coefficient for velocity decay to be applied in decceleration
// Compute coefficient for velocity decay to be applied in decceleration
double computeConstTimeDecayCoefficient(double velocity);
/* Decelerate the velocities. Function is called in step() but is dereferenced from
/**
* Decelerate the velocities. Function is called in step() but is dereferenced from
* frame time to assure same behaviour on all systems
*/
void decelerate(double dt);
@@ -156,12 +151,13 @@ private:
// Resets all properties that can be changed in the GUI to default
void resetPropertiesToDefault();
// Set all velocities to zero
void resetVelocities();
Camera* _camera = nullptr;
// Property variables
properties::StringProperty _origin;
properties::BoolProperty _unitTest;
properties::BoolProperty _touchActive;
properties::BoolProperty _disableZoom;
properties::BoolProperty _disableRoll;
properties::TriggerProperty _reset;
@@ -170,10 +166,7 @@ private:
properties::FloatProperty _touchScreenSize;
properties::FloatProperty _tapZoomFactor;
properties::FloatProperty _pinchZoomFactor;
properties::FloatProperty _nodeRadiusThreshold;
properties::FloatProperty _rollAngleThreshold;
properties::FloatProperty _orbitSpeedThreshold;
properties::FloatProperty _spinSensitivity;
properties::FloatProperty _zoomSensitivityExponential;
properties::FloatProperty _zoomSensitivityProportionalDist;
properties::FloatProperty _zoomSensitivityDistanceThreshold;
@@ -184,11 +177,12 @@ private:
properties::FloatProperty _centroidStillThreshold;
properties::BoolProperty _panEnabled;
properties::FloatProperty _interpretPan;
properties::FloatProperty _slerpTime;
properties::Vec4Property _friction;
properties::FloatProperty _pickingRadiusMinimum;
properties::FloatProperty _constTimeDecay_secs;
properties::BoolProperty _enableDirectManipulation;
properties::FloatProperty _directTouchDistanceThreshold;
#ifdef TOUCH_DEBUG_PROPERTIES
struct DebugProperties : PropertyOwner {
DebugProperties();
@@ -205,14 +199,13 @@ private:
int stepVelUpdate = 0;
#endif
std::array<TouchInputHolder, 2> _pinchInputs;
// Class variables
VelocityStates _vel;
VelocityStates _lastVel;
VelocityStates _sensitivity;
double _projectionScaleFactor = 1.000004;
double _currentRadius = 1.0;
double _slerpdT = 10001.0;
bool _isWithinDirectTouchDistance = false;
double _timeSlack = 0.0;
std::chrono::milliseconds _time;
bool _directTouchMode = false;
@@ -220,12 +213,9 @@ private:
bool _tap = false;
bool _doubleTap = false;
bool _zoomOutTap = false;
bool _lmSuccess = true;
std::vector<DirectInputSolver::SelectedBody> _selected;
SceneGraphNode* _pickingSelected = nullptr;
DirectInputSolver _solver;
std::vector<DirectInputSolver::SelectedBody> _selectedNodeSurfacePoints;
DirectInputSolver _directInputSolver;
glm::dquat _toSlerp = glm::dquat(1.0, 0.0, 0.0, 0.0);
glm::vec2 _centroid = glm::vec2(0.f);
FrameTimeAverage _frameTimeAvg;

View File

@@ -33,20 +33,28 @@ uniform vec3 color;
Fragment getFragment() {
// calculate normal from texture coordinates
// Calculate normal from texture coordinates
vec3 n;
n.xy = gl_PointCoord.st * vec2(2.0, -2.0) + vec2(-1.0, 1.0);
float mag = dot(n.xy, n.xy);
if (mag > 1.0) {
// kill pixels outside circle
discard;
float edgeSmoothing = 1.0;
float w = 0.1; // wdith for smoothing
if (mag > 1.0 - w) {
// Kill pixels outside circle. Do a smoothstep for soft border
float t = (mag - (1.0-w)) / w;
edgeSmoothing = smoothstep(1.0, 0.0, t);
if (edgeSmoothing <= 0.0) {
discard;
}
}
n.z = sqrt(1.0 - mag);
// calculate lighting
// Calculate lighting
vec3 light_dir = vec3(0.0, 0.0, 1.0);
float diffuse = max(0.0, dot(light_dir, n));
float alpha = min(pow(sqrt(mag), thickness), opacity);
alpha *= edgeSmoothing;
Fragment frag;
frag.color = vec4(color * diffuse, alpha);

View File

@@ -212,6 +212,13 @@ bool DirectInputSolver::solve(const std::vector<TouchInputHolder>& list,
const std::vector<SelectedBody>& selectedBodies,
std::vector<double>* parameters, const Camera& camera)
{
ZoneScopedN("Direct touch input solver")
ghoul_assert(
selectedBodies.size() >= list.size(),
"Number of touch inputs must match the number of 'selected bodies'"
);
int nFingers = std::min(static_cast<int>(list.size()), 3);
_nDof = std::min(nFingers * 2, 6);

File diff suppressed because it is too large Load Diff

View File

@@ -72,7 +72,7 @@ TouchMarker::TouchMarker()
, _radiusSize(RadiusInfo, 30.f, 0.f, 100.f)
, _opacity(OpacityInfo, 0.8f, 0.f, 1.f)
, _thickness(ThicknessInfo, 2.f, 0.f, 4.f)
, _color(ColorInfo, glm::vec3(0.96f, 0.2f, 0.2f), glm::vec3(0.f), glm::vec3(1.f))
, _color(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
{
addProperty(_visible);
addProperty(_radiusSize);

View File

@@ -28,44 +28,98 @@
#include <modules/touch/include/win32_touch.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/globalscallbacks.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/interaction/interactionmonitor.h>
#include <openspace/navigation/navigationhandler.h>
#include <openspace/rendering/renderable.h>
#include <openspace/util/factorymanager.h>
#include <algorithm>
using namespace TUIO;
namespace {
constexpr openspace::properties::Property::PropertyInfo TouchActiveInfo = {
"TouchActive",
"True if we want to use touch input as 3D navigation",
"Use this if we want to turn on or off Touch input navigation. "
"Disabling this will reset all current touch inputs to the navigation."
constexpr std::string_view _loggerCat = "TouchModule";
constexpr openspace::properties::Property::PropertyInfo EnableTouchInfo = {
"EnableTouchInteraction",
"Enable Touch Interaction",
"Use this property to turn on/off touch input navigation in the 3D scene. "
"Disabling will reset all current touch inputs to the navigation."
};
constexpr openspace::properties::Property::PropertyInfo EventsInfo = {
"DetectedTouchEvent",
"Detected Touch Event",
"True when there is an active touch event",
openspace::properties::Property::Visibility::Hidden
};
constexpr openspace::properties::Property::PropertyInfo
DefaultDirectTouchRenderableTypesInfo =
{
"DefaultDirectTouchRenderableTypes",
"Default Direct Touch Renderable Types",
"A list of renderable types that will automatically use the \'direct "
"manipulation\' scheme when interacted with, keeping the finger on a static "
"position on the interaction sphere of the object when touching. Good for "
"relatively spherical objects.",
openspace::properties::Property::Visibility::AdvancedUser
};
}
namespace openspace {
TouchModule::TouchModule()
: OpenSpaceModule("Touch")
, _touchActive(TouchActiveInfo, true)
, _touchIsEnabled(EnableTouchInfo, true)
, _hasActiveTouchEvent(EventsInfo, false)
, _defaultDirectTouchRenderableTypes(DefaultDirectTouchRenderableTypesInfo)
{
addPropertySubOwner(_touch);
addPropertySubOwner(_markers);
addProperty(_touchActive);
_touchActive.onChange([&] {
addProperty(_touchIsEnabled);
_touchIsEnabled.onChange([&]() {
_touch.resetAfterInput();
_lastTouchInputs.clear();
});
_hasActiveTouchEvent.setReadOnly(true);
addProperty(_hasActiveTouchEvent);
_defaultDirectTouchRenderableTypes.onChange([&]() {
_sortedDefaultRenderableTypes.clear();
for (const std::string& s : _defaultDirectTouchRenderableTypes.value()) {
ghoul::TemplateFactory<Renderable>* fRenderable =
FactoryManager::ref().factory<Renderable>();
if (!fRenderable->hasClass(s)) {
LWARNING(fmt::format(
"In property 'DefaultDirectTouchRenderableTypes': '{}' is not a "
"registered renderable type. Ignoring", s
));
continue;
}
_sortedDefaultRenderableTypes.insert(s);
}
});
addProperty(_defaultDirectTouchRenderableTypes);
}
TouchModule::~TouchModule() {
// intentionally left empty
}
void TouchModule::internalInitialize(const ghoul::Dictionary& /*dictionary*/){
bool TouchModule::isDefaultDirectTouchType(std::string_view renderableType) const {
return _sortedDefaultRenderableTypes.find(std::string(renderableType)) !=
_sortedDefaultRenderableTypes.end();
}
void TouchModule::internalInitialize(const ghoul::Dictionary&){
_ear.reset(new TuioEar());
global::callback::initializeGL->push_back([&]() {
LDEBUGC("TouchModule", "Initializing TouchMarker OpenGL");
LDEBUG("Initializing TouchMarker OpenGL");
_markers.initialize();
#ifdef WIN32
// We currently only support one window of touch input internally
@@ -78,7 +132,7 @@ void TouchModule::internalInitialize(const ghoul::Dictionary& /*dictionary*/){
});
global::callback::deinitializeGL->push_back([&]() {
LDEBUGC("TouchMarker", "Deinitialize TouchMarker OpenGL");
LDEBUG("Deinitialize TouchMarker OpenGL");
_markers.deinitialize();
});
@@ -104,26 +158,37 @@ void TouchModule::internalInitialize(const ghoul::Dictionary& /*dictionary*/){
global::callback::preSync->push_back([&]() {
if (!_touchActive) {
if (!_touchIsEnabled) {
return;
}
OpenSpaceEngine::Mode mode = global::openSpaceEngine->currentMode();
if (mode == OpenSpaceEngine::Mode::CameraPath ||
mode == OpenSpaceEngine::Mode::SessionRecordingPlayback)
{
// Reset everything, to avoid problems once we process inputs again
_lastTouchInputs.clear();
_touch.resetAfterInput();
clearInputs();
return;
}
_touch.setCamera(global::navigationHandler->camera());
_touch.setFocusNode(global::navigationHandler->orbitalNavigator().anchorNode());
if (processNewInput() && global::windowDelegate->isMaster()) {
bool gotNewInput = processNewInput();
if (gotNewInput && global::windowDelegate->isMaster()) {
_touch.updateStateFromInput(_touchPoints, _lastTouchInputs);
}
else if (_touchPoints.empty()) {
_touch.resetAfterInput();
}
// update lastProcessed
// Update last processed touch inputs
_lastTouchInputs.clear();
for (const TouchInputHolder& points : _touchPoints) {
_lastTouchInputs.emplace_back(points.latestInput());
}
// calculate the new camera state for this frame
// Calculate the new camera state for this frame
_touch.step(global::windowDelegate->deltaTime());
clearInputs();
});
@@ -145,14 +210,15 @@ bool TouchModule::processNewInput() {
removeTouchInput(removal);
}
// Set touch property to active (to void mouse input, mainly for mtdev bridges)
_touch.touchActive(!_touchPoints.empty());
bool touchHappened = !_touchPoints.empty();
_hasActiveTouchEvent = touchHappened;
if (!_touchPoints.empty()) {
// Set touch property to active (to void mouse input, mainly for mtdev bridges)
if (touchHappened) {
global::interactionMonitor->markInteraction();
}
// Erase old input id's that no longer exists
// Erase old input ids that no longer exist
_lastTouchInputs.erase(
std::remove_if(
_lastTouchInputs.begin(),
@@ -168,7 +234,7 @@ bool TouchModule::processNewInput() {
}
),
_lastTouchInputs.end()
);
);
if (_tap) {
_touch.tap();
@@ -180,9 +246,11 @@ bool TouchModule::processNewInput() {
if (_touchPoints.size() == _lastTouchInputs.size() &&
!_touchPoints.empty())
{
// @TODO (emmbr26, 2023-02-03) Looks to me like this code will always return
// true? That's a bit weird and should probably be investigated
bool newInput = true;
// go through list and check if the last registrered time is newer than the one in
// lastProcessed (last frame)
// Go through list and check if the last registrered time is newer than the
// last processed touch inputs (last frame)
std::for_each(
_lastTouchInputs.begin(),
_lastTouchInputs.end(),
@@ -197,7 +265,8 @@ bool TouchModule::processNewInput() {
if (!holder->isMoving()) {
newInput = true;
}
});
}
);
return newInput;
}
else {

View File

@@ -27,6 +27,8 @@
#include <modules/touch/include/touchmarker.h>
#include <modules/touch/include/touchinteraction.h>
#include <openspace/properties/list/stringlistproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/util/openspacemodule.h>
#include <openspace/util/touch.h>
#include <memory>
@@ -41,9 +43,15 @@ class Win32TouchHook;
class TouchModule : public OpenSpaceModule {
public:
constexpr static const char* Name = "Touch";
TouchModule();
~TouchModule();
// Function to check if the given renderable type is one that should
// use direct maniuplation
bool isDefaultDirectTouchType(std::string_view renderableType) const;
protected:
void internalInitialize(const ghoul::Dictionary& dictionary) override;
@@ -64,7 +72,13 @@ private:
std::vector<TouchInput> _deferredRemovals;
std::vector<TouchInput> _lastTouchInputs;
properties::BoolProperty _touchActive;
properties::BoolProperty _touchIsEnabled;
properties::BoolProperty _hasActiveTouchEvent;
properties::StringListProperty _defaultDirectTouchRenderableTypes;
// A sorted version of the list in the property
std::set<std::string> _sortedDefaultRenderableTypes;
// contains an id and the Point that was processed last frame
glm::ivec2 _webPositionCallback = glm::ivec2(0);
#ifdef WIN32

View File

@@ -539,6 +539,20 @@ glm::quat OrbitalNavigator::anchorNodeToCameraRotation() const {
return glm::quat(invWorldRotation) * glm::quat(_camera->rotationQuaternion());
}
glm::dvec3 OrbitalNavigator::pushToSurfaceOfAnchor(
const glm::dvec3& cameraPosition) const
{
const SurfacePositionHandle posHandle =
calculateSurfacePositionHandle(*_anchorNode, cameraPosition);
return pushToSurface(
cameraPosition,
_anchorNode->worldPosition(),
posHandle
);
}
void OrbitalNavigator::resetVelocities() {
_mouseStates.resetVelocities();
_joystickStates.resetVelocities();
@@ -703,7 +717,6 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
// Perform the vertical movements based on user input
pose.position = translateVertically(deltaTime, pose.position, anchorPos, posHandle);
pose.position = pushToSurface(
_minimumAllowedDistance,
pose.position,
anchorPos,
posHandle
@@ -992,6 +1005,10 @@ bool OrbitalNavigator::hasRollFriction() const {
return _friction.roll;
}
double OrbitalNavigator::minAllowedDistance() const {
return _minimumAllowedDistance;
}
OrbitalNavigator::CameraRotationDecomposition
OrbitalNavigator::decomposeCameraRotationSurface(CameraPose cameraPose,
const SceneGraphNode& reference)
@@ -1473,8 +1490,7 @@ glm::dquat OrbitalNavigator::rotateHorizontally(double deltaTime,
return mouseCameraRollRotation * globalCameraRotation;
}
glm::dvec3 OrbitalNavigator::pushToSurface(double minHeightAboveGround,
const glm::dvec3& cameraPosition,
glm::dvec3 OrbitalNavigator::pushToSurface(const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const SurfacePositionHandle& positionHandle) const
{
@@ -1495,7 +1511,7 @@ glm::dvec3 OrbitalNavigator::pushToSurface(double minHeightAboveGround,
glm::sign(dot(actualSurfaceToCamera, referenceSurfaceOutDirection));
return cameraPosition + referenceSurfaceOutDirection *
glm::max(minHeightAboveGround - surfaceToCameraSigned, 0.0);
glm::max(_minimumAllowedDistance - surfaceToCameraSigned, 0.0);
}
glm::dquat OrbitalNavigator::interpolateRotationDifferential(double deltaTime,
@@ -1520,7 +1536,7 @@ glm::dquat OrbitalNavigator::interpolateRotationDifferential(double deltaTime,
SurfacePositionHandle OrbitalNavigator::calculateSurfacePositionHandle(
const SceneGraphNode& node,
const glm::dvec3 cameraPositionWorldSpace)
const glm::dvec3& cameraPositionWorldSpace) const
{
ghoul_assert(
glm::length(cameraPositionWorldSpace) > 0.0,

View File

@@ -240,6 +240,10 @@ double Renderable::interactionSphere() const {
return _interactionSphere;
}
std::string_view Renderable::typeAsString() const {
return _renderableType;
}
SurfacePositionHandle Renderable::calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace) const
{

View File

@@ -160,25 +160,40 @@ namespace {
openspace::properties::Property::Visibility::Developer
};
constexpr openspace::properties::Property::PropertyInfo
SupportsDirectInteractionInfo =
{
"SupportsDirectInteraction",
"Supports Direct Interaction",
"Only relevant when using touch interaction. If true, the \'direct "
"manipulation\' scheme will be used when interacting with this scene graph "
"node, meaning that the positions on the interaction sphere that intersects "
"with the touch points will directly follow the motion of the touch points. "
"Works best for objects that have an interaction sphere of about the same size "
"as the bounding sphere, and that are somewhat spherical. Note that using this "
"feature might significalty reduce the performance.",
openspace::properties::Property::Visibility::AdvancedUser
};
struct [[codegen::Dictionary(SceneGraphNode)]] Parameters {
// The identifier of this scenegraph node. This name must be unique among all
// The identifier of this scene graph node. This name must be unique among all
// scene graph nodes that are loaded in a specific scene. If a duplicate is
// detected the loading of the node will fail, as will all childing that depend on
// the node. The identifier must not contain any whitespaces or '.'
std::string identifier;
// This names the parent of the currently specified scenegraph node. The parent
// This names the parent of the currently specified scene graph node. The parent
// must already exist in the scene graph. If not specified, the node will be
// attached to the root of the scenegraph
// attached to the root of the scene graph
std::optional<std::string> parent
[[codegen::annotation(
"If specified, this must be a name for another scenegraph node"
"If specified, this must be a name for another scene graph node"
)]];
// The renderable that is to be created for this scenegraph node. A renderable is
// a component of a scenegraph node that will lead to some visual result on the
// The renderable that is to be created for this scene graph node. A renderable is
// a component of a scene graph node that will lead to some visual result on the
// screen. The specifics heavily depend on the 'Type' of the renderable. If no
// Renderable is specified, this scenegraph node is an internal node and can be
// Renderable is specified, this scene graph node is an internal node and can be
// used for either group children, or apply common transformations to a group of
// children
std::optional<ghoul::Dictionary> renderable [[codegen::reference("renderable")]];
@@ -189,27 +204,30 @@ namespace {
// [[codegen::verbatim(InteractionSphereInfo.description)]]
std::optional<double> interactionSphere;
// [[codegen::verbatim(SupportsDirectInteractionInfo.description)]]
std::optional<bool> supportsDirectInteraction;
struct Transform {
// This node describes a translation that is applied to the scenegraph node
// This node describes a translation that is applied to the scene graph node
// and all its children. Depending on the 'Type' of the translation, this can
// either be a static translation or a time-varying one
std::optional<ghoul::Dictionary> translation
[[codegen::reference("core_transform_translation")]];
// This nodes describes a rotation that is applied to the scenegraph node and
// This nodes describes a rotation that is applied to the scene graph node and
// all its children. Depending on the 'Type' of the rotation, this can either
// be a static rotation or a time-varying one
std::optional<ghoul::Dictionary> rotation
[[codegen::reference("core_transform_rotation")]];
// This node describes a scaling that is applied to the scenegraph node and
// This node describes a scaling that is applied to the scene graph node and
// all its children. Depending on the 'Type' of the scaling, this can either
// be a static scaling or a time-varying one
std::optional<ghoul::Dictionary> scale
[[codegen::reference("core_transform_scaling")]];
};
// This describes a set of transformations that are applied to this scenegraph
// This describes a set of transformations that are applied to this scene graph
// node and all of its children. There are only three possible values
// corresponding to a 'Translation', a 'Rotation', and a 'Scale'
std::optional<Transform> transform;
@@ -248,7 +266,7 @@ namespace {
std::optional<ghoul::Dictionary> timeFrame
[[codegen::reference("core_time_frame")]];
// A tag or list of tags that can be used to reference to a group of scenegraph
// A tag or list of tags that can be used to reference to a group of scene graph
// nodes.
std::optional<std::variant<std::string, std::vector<std::string>>> tag;
@@ -258,7 +276,7 @@ namespace {
std::optional<std::string> name;
// If this value is specified, this '/' separated URI specifies the location
// of this scenegraph node in a GUI representation, for instance
// of this scene graph node in a GUI representation, for instance
// '/SolarSystem/Earth/Moon'
std::optional<std::string> path;
@@ -266,12 +284,12 @@ namespace {
std::optional<std::string> description;
// If this value is specified, GUI applications are incouraged to ignore this
// scenegraph node. This is most useful to trim collective lists of nodes and
// scene graph node. This is most useful to trim collective lists of nodes and
// not display, for example, barycenters
std::optional<bool> hidden;
};
// Additional information that is passed to GUI applications. These are all hints
// and do not have any impact on the actual function of the scenegraph node
// and do not have any impact on the actual function of the scene graph node
std::optional<Gui> gui [[codegen::key("GUI")]];
};
#include "scenegraphnode_codegen.cpp"
@@ -513,6 +531,7 @@ SceneGraphNode::SceneGraphNode()
, _screenSizeRadius(ScreenSizeRadiusInfo, 0)
, _visibilityDistance(VisibilityDistanceInfo, 6e10f)
, _showDebugSphere(ShowDebugSphereInfo, false)
, _supportsDirectInteraction(SupportsDirectInteractionInfo, false)
{
addProperty(_computeScreenSpaceValues);
addProperty(_screenSpacePosition);
@@ -552,6 +571,8 @@ SceneGraphNode::SceneGraphNode()
addProperty(_approachFactor);
addProperty(_showDebugSphere);
addProperty(_supportsDirectInteraction);
}
SceneGraphNode::~SceneGraphNode() {}
@@ -1207,6 +1228,10 @@ double SceneGraphNode::approachFactor() const {
return _approachFactor;
}
bool SceneGraphNode::supportsDirectInteraction() const {
return _supportsDirectInteraction;
}
const Renderable* SceneGraphNode::renderable() const {
return _renderable.get();
}