Feature/gui for touch (#967)

Merging in feature gui for touch. Enable touch module to run.
Currently only run on development mode with the "touch" scene.
This commit is contained in:
liuloppan
2019-09-18 23:49:51 +02:00
committed by GitHub
parent c3b481f1e9
commit 78eb673611
42 changed files with 543 additions and 120 deletions

View File

@@ -25,7 +25,7 @@ asset.require('util/default_dashboard')
asset.require('util/default_joystick')
-- Load web gui
asset.require('util/webgui')
local webGui = asset.require('util/webgui')
asset.request('customization/globebrowsing')
@@ -66,6 +66,8 @@ local Keybindings = {
}
asset.onInitialize(function ()
webGui.setCefRoute("onscreen")
sceneHelper.bindKeys(Keybindings)
openspace.setDefaultGuiSorting()

View File

@@ -5,4 +5,4 @@ asset.export("webguiDevelopmentMode", false)
-- 1) Set the above `webguiDevelopmentMode` to true
-- 2) Clone the repository: https://github.com/OpenSpace/OpenSpace-WebGuiFrontend
-- 3) Install nodejs (including npm)
-- 4) Within the repository, run `npm install` and `npm start`
-- 4) Within the repository, run `npm install` and `npm start`

View File

@@ -0,0 +1,48 @@
-- This asset requires OpenSpace to be built with the OPENSPACE_MODULE_SYNC enabled
local assetHelper = asset.require("util/asset_helper")
asset.syncedResource({
Type = "UrlSynchronization",
Name = "Icons",
Identifier = "planet_icons",
Url = {
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/earth.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/moon.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/jupiter.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/mars.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/mercury.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/neptune.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/saturn.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/uranus.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/venus.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/callisto.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/europa.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/ganymede.png",
"http://data.openspaceproject.com/files/webgui/assets/icons/solarsystem/io.png",
},
UseHash = false
})
asset.syncedResource({
Type = "UrlSynchronization",
Name = "Stories",
Identifier = "story_images",
Url = {
"http://data.openspaceproject.com/files/webgui/assets/images/stories/story_solarsystem.png",
"http://data.openspaceproject.com/files/webgui/assets/images/stories/story_jupitermoons.png",
"http://data.openspaceproject.com/files/webgui/assets/images/stories/story_earthweather.png",
"http://data.openspaceproject.com/files/webgui/assets/images/stories/story_mars.png",
"http://data.openspaceproject.com/files/webgui/assets/images/stories/story_galaxies.png",
"http://data.openspaceproject.com/files/webgui/assets/images/stories/story_example.png",
},
UseHash = false
})
asset.syncedResource({
Type = "UrlSynchronization",
Name = "Instructions",
Identifier = "images",
Url = "http://data.openspaceproject.com/files/webgui/assets/images/instructions.png",
UseHash = false
})

View File

@@ -20,10 +20,6 @@ local Charon = {
Type = "SpiceRotation",
SourceFrame = "IAU_CHARON",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -20,10 +20,6 @@ local Pluto = {
Type = "SpiceRotation",
SourceFrame = "IAU_PLUTO",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -46,10 +46,6 @@ local PlutoProjection = {
Type = "SpiceRotation",
SourceFrame = "IAU_PLUTO",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -14,12 +14,6 @@ local mapServiceConfigsPath = asset.localResource("map_service_configs")
local Earth = {
Identifier = "Earth",
Parent = transforms.EarthIAU.Identifier,
Transform = {
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {
Type = "RenderableGlobe",
Radii = earthEllipsoid,

View File

@@ -29,7 +29,7 @@ local Io = {
Target = "IO",
Observer = "JUPITER BARYCENTER",
Kernels = kernel
},
}
},
Renderable = {
Type = "RenderableGlobe",

View File

@@ -20,10 +20,6 @@ local Jupiter = {
Type = "SpiceRotation",
SourceFrame = "IAU_JUPITER",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -153,10 +153,6 @@ local Mars = {
Type = "SpiceRotation",
SourceFrame = "IAU_MARS",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -189,10 +189,6 @@ local Mercury = {
Type = "SpiceRotation",
SourceFrame = "IAU_MERCURY",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -18,10 +18,6 @@ local Neptune = {
Type = "SpiceRotation",
SourceFrame = "IAU_NEPTUNE",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -20,10 +20,6 @@ local Saturn = {
Type = "SpiceRotation",
SourceFrame = "IAU_SATURN",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -20,10 +20,6 @@ local Uranus = {
Type = "SpiceRotation",
SourceFrame = "IAU_URANUS",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

View File

@@ -26,10 +26,6 @@ local Venus = {
Type = "SpiceTranslation",
Target = "VENUS",
Observer = "VENUS BARYCENTER"
},
Scale = {
Type = "StaticScale",
Scale = 1.0
}
},
Renderable = {

37
data/assets/touch.scene Normal file
View File

@@ -0,0 +1,37 @@
local has_touch = openspace.modules.isLoaded('Touch')
if not has_touch then
openspace.printFatal('Could not load scene "' .. asset.filePath .. '" due to missing module "touch"')
do return end
end
asset.require('./base')
local webGui = asset.require('util/webgui')
local earthAsset = asset.require('scene/solarsystem/planets/earth/earth')
asset.onInitialize(function ()
local now = openspace.time.currentWallTime()
-- Jump back one day to be able to show complete weather data on Earth.
openspace.time.setTime(openspace.time.advancedTime(now, "-1d"))
openspace.markInterestingNodes(
{ "Earth", "Mars", "Moon" }
)
openspace.navigation.setNavigationState({
Anchor = earthAsset.Earth.Identifier,
Position = { 58.5877, 16.1924, 20000000 }
})
openspace.setPropertyValueSingle('Scene.Pluto.Renderable.Enabled', false)
openspace.setPropertyValueSingle('Scene.Charon.Renderable.Enabled', false)
openspace.setPropertyValueSingle('Scene.PlutoBarycenterTrail.Renderable.Enabled', false)
webGui.setCefRoute("ontouch")
end)
asset.onDeinitialize(function ()
openspace.removeInterestingNodes(
{ "Earth", "Mars", "Moon" }
)
end)

View File

@@ -37,16 +37,6 @@ asset.onInitialize(function ()
openspace.setPropertyValueSingle("Modules.WebGui.DefaultEndpoint", "frontend")
openspace.setPropertyValueSingle("Modules.WebGui.ServerProcessEnabled", enabled)
if guiCustomization.webguiDevelopmentMode then
-- Route CEF to the deveopment version of the GUI.
-- This must be manually served using `npm start`
-- in the OpenSpace-WebGuiFrontend repository.
openspace.setPropertyValueSingle(
"Modules.CefWebGui.GuiUrl",
"http://127.0.0.1:4690/frontend/#/onscreen"
)
end
-- The GUI contains date and simulation increment,
-- so let's remove these from the dashboard.
if openspace.getPropertyValue('Modules.CefWebGui.Visible') then
@@ -76,3 +66,19 @@ asset.onDeinitialize(function ()
-- need to address in webguimodule.cpp
--openspace.setPropertyValueSingle("Modules.WebGui.Directories", newDirectories)
end)
function setCefRoute(route)
local port = 4680
-- As a developer, you can manually serve the webgui from port 4690
-- with the command `npm start` in the web gui repository
if guiCustomization.webguiDevelopmentMode then
port = 4690
end
openspace.setPropertyValueSingle(
"Modules.CefWebGui.GuiUrl",
"http://127.0.0.1:" .. port .. "/frontend/#/" .. route
)
end
asset.export("setCefRoute", setCefRoute)

View File

@@ -80,6 +80,10 @@ public:
void deinitialize();
// Mutators
void setFocusNode(SceneGraphNode* node);
void resetCameraDirection();
void setNavigationStateNextFame(NavigationState state);
void setCamera(Camera* camera);
void setInterpolationTime(float durationInSeconds);
@@ -92,6 +96,7 @@ public:
// Accessors
Camera* camera() const;
const SceneGraphNode* anchorNode() const;
const InputState& inputState() const;
const OrbitalNavigator& orbitalNavigator() const;
OrbitalNavigator& orbitalNavigator();

View File

@@ -64,6 +64,8 @@ public:
void setCamera(Camera* camera);
void clearPreviousState();
SceneGraphNode* focusNode() const;
void setFocusNode(const SceneGraphNode* focusNode);
void setFocusNode(const std::string& focusNode);
void setAnchorNode(const std::string& anchorNode);
void setAimNode(const std::string& aimNode);
@@ -118,7 +120,6 @@ private:
properties::FloatProperty friction;
};
void setFocusNode(const SceneGraphNode* focusNode);
void setAnchorNode(const SceneGraphNode* anchorNode);
void setAimNode(const SceneGraphNode* aimNode);
@@ -141,7 +142,11 @@ private:
properties::FloatProperty _followAnchorNodeRotationDistance;
properties::FloatProperty _minimumAllowedDistance;
properties::FloatProperty _flightDestinationDistance;
properties::BoolProperty _applyLinearFlight;
properties::FloatProperty _velocitySensitivity;
properties::FloatProperty _mouseSensitivity;
properties::FloatProperty _joystickSensitivity;
properties::FloatProperty _websocketSensitivity;
@@ -246,6 +251,18 @@ private:
const glm::dvec3& objectPosition, const glm::dquat& globalCameraRotation,
const SurfacePositionHandle& positionHandle) const;
/**
* Moves the camera along a vector, camPosToCenterPosDiff, until it reaches the focusLimit.
* The velocity of the zooming depend on distFromCameraToFocus and the final frame
* where the camera stops moving depends on the distance set in the variable focusLimit.
* The bool determines whether to move/fly towards the focus node or away from it.
* \returns a new position of the camera, closer to the focusLimit than the previous
* position.
*/
glm::dvec3 moveCameraAlongVector(const glm::dvec3& camPos,
double distFromCameraToFocus, const glm::dvec3& camPosToCenterPosDiff,
double destination) const;
/*
* Adds rotation to the camera position so that it follows the rotation of the anchor
* node defined by the differential anchorNodeRotationDiff.

View File

@@ -28,6 +28,7 @@
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/vector/ivec2property.h>
#include <openspace/rendering/dashboarditem.h>
#include <ghoul/glm.h>
#include <memory>
@@ -50,6 +51,7 @@ public:
void removeDashboardItem(const std::string& identifier);
void removeDashboardItem(int index);
void clearDashboardItems();
glm::vec2 getStartPositionOffset();
/**
* Returns the Lua library that contains all Lua functions available to affect the
@@ -59,6 +61,7 @@ public:
private:
properties::BoolProperty _isEnabled;
properties::IVec2Property _startPositionOffset;
std::vector<std::unique_ptr<DashboardItem>> _items;
};

View File

@@ -94,6 +94,7 @@ public:
protected:
properties::BoolProperty _enabled;
properties::FloatProperty _opacity;
properties::FloatProperty _boundingSphere;
properties::StringProperty _renderableType;
void setRenderBinFromOpacity();
@@ -101,7 +102,6 @@ protected:
private:
RenderBin _renderBin = RenderBin::Opaque;
float _boundingSphere = 0.f;
};
} // namespace openspace

View File

@@ -29,12 +29,16 @@
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/doubleproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/vector/ivec2property.h>
#include <ghoul/glm.h>
#include <ghoul/misc/boolean.h>
#include <atomic>
#include <functional>
#include <memory>
#include <vector>
#include <chrono>
//#define Debugging_Core_SceneGraphNode_Indices
@@ -149,6 +153,7 @@ private:
glm::dvec3 calculateWorldPosition() const;
glm::dmat3 calculateWorldRotation() const;
double calculateWorldScale() const;
void computeScreenSpaceData(RenderData& newData);
std::atomic<State> _state = State::Loaded;
std::vector<std::unique_ptr<SceneGraphNode>> _children;
@@ -187,6 +192,17 @@ private:
glm::dmat4 _modelTransformCached;
glm::dmat4 _inverseModelTransformCached;
properties::BoolProperty _computeScreenSpaceValues;
properties::IVec2Property _screenSpacePosition;
properties::BoolProperty _screenVisibility;
properties::DoubleProperty _distFromCamToNode;
properties::DoubleProperty _screenSizeRadius;
properties::FloatProperty _visibilityDistance;
// This variable is used for the rate-limiting of the screenspace positions (if they
// are calculated when _computeScreenSpaceValues is true)
std::chrono::high_resolution_clock::time_point _lastScreenSpaceUpdateTime;
#ifdef Debugging_Core_SceneGraphNode_Indices
int index = 0;
static int nextIndex;

View File

@@ -101,6 +101,7 @@ public:
// Right now this function returns the actual combined matrix which makes some
// of the old calls to the function wrong..
const glm::dmat4& combinedViewMatrix() const;
const glm::dmat4& combinedViewMatrixNoScale() const;
void invalidateCache();

View File

@@ -440,6 +440,7 @@ void DashboardItemDistance::render(glm::vec2& penPosition) {
}
penPosition.y -= _font->height();
RenderFont(
*_font,
penPosition,

View File

@@ -52,6 +52,8 @@ public:
static documentation::Documentation Documentation();
bool storyStyleActive();
private:
enum Type {
Node = 0,

View File

@@ -58,7 +58,7 @@ double StaticScale::scaleValue(const UpdateData&) const {
return _scaleValue;
}
StaticScale::StaticScale() : _scaleValue(ScaleInfo, 1.0, 1.0, 1e6) {
StaticScale::StaticScale() : _scaleValue(ScaleInfo, 1.0, 0.1, 100) {
addProperty(_scaleValue);
_scaleValue.onChange([this]() {

View File

@@ -160,21 +160,21 @@ void CefWebGuiModule::internalInitialize(const ghoul::Dictionary& configuration)
}
});
if (configuration.hasValue<std::string>(GuiUrlInfo.identifier)) {
_url = configuration.value<std::string>(GuiUrlInfo.identifier);
} else {
WebGuiModule* webGuiModule = global::moduleEngine.module<WebGuiModule>();
_url = "http://127.0.0.1:" +
std::to_string(webGuiModule->port()) + "/frontend/#/onscreen";
// We need this to make sure that the browser is reloaded
// once the endpoint comes online, on OpenSpace startup.
_endpointCallback = webGuiModule->addEndpointChangeCallback(
[this](const std::string& endpoint, bool exists) {
if (exists && endpoint == "frontend" && _instance) {
_instance->reloadBrowser();
}
// TODO: See if the hardcoded endpoint `frontend` below can be removed.
// Possible fix: Reload browser if cefwebgui is routed to localhost
// and the same endpoint that just came online.
WebGuiModule* webGuiModule = global::moduleEngine.module<WebGuiModule>();
_endpointCallback = webGuiModule->addEndpointChangeCallback(
[this](const std::string& endpoint, bool exists) {
if (exists && endpoint == "frontend" && _instance) {
_instance->reloadBrowser();
}
);
}
}
);
if (configuration.hasValue<float>(GuiScaleInfo.identifier)) {
_guiScale = configuration.value<float>(GuiScaleInfo.identifier);

View File

@@ -333,4 +333,4 @@ openspace.globebrowsing.loadWMSServersFromFile = function (file_path)
end
end
end
end

View File

@@ -32,6 +32,7 @@
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/doubleproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/vector/ivec2property.h>
@@ -208,6 +209,8 @@ private:
properties::FloatProperty _zoomSensitivityProportionalDist;
properties::FloatProperty _zoomSensitivityDistanceThreshold;
properties::FloatProperty _zoomBoundarySphereMultiplier;
properties::DoubleProperty _zoomInLimit;
properties::DoubleProperty _zoomOutLimit;
properties::FloatProperty _inputStillThreshold;
properties::FloatProperty _centroidStillThreshold;
properties::BoolProperty _panEnabled;

View File

@@ -38,6 +38,7 @@
#include <ghoul/misc/invariants.h>
#include <ghoul/logging/logmanager.h>
#include <openspace/util/camera.h>
#include <openspace/util/updatestructures.h>
#include <glm/gtx/quaternion.hpp>
@@ -227,6 +228,22 @@ namespace {
"Minimum radius for picking in NDC coordinates",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo ZoomOutLimitInfo = {
"ZoomOutLimit",
"Zoom Out Limit",
"The maximum distance you are allowed to navigate away from the anchor. "
"This should always be larger than the zoom in value if you want to be able "
"to zoom. Defaults to maximum allowed double."
};
constexpr openspace::properties::Property::PropertyInfo ZoomInLimitInfo = {
"ZoomInLimit",
"Zoom In Limit",
"The minimum distance from the anchor that you are allowed to navigate to. "
"Its purpose is to limit zooming in on a node. If this value is not set it "
"defaults to the surface of the current anchor. "
};
} // namespace
using namespace TUIO;
@@ -256,6 +273,8 @@ TouchInteraction::TouchInteraction()
0.25f
)
, _zoomBoundarySphereMultiplier(ZoomBoundarySphereMultiplierInfo, 1.001f, 1.f, 1.01f)
, _zoomOutLimit(ZoomOutLimitInfo, std::numeric_limits<double>::max(), 1000.0f, std::numeric_limits<double>::max())
, _zoomInLimit(ZoomInLimitInfo, -1.0f, 0.0f, std::numeric_limits<double>::max())
, _inputStillThreshold(InputSensitivityInfo, 0.0005f, 0.f, 0.001f)
// used to void wrongly interpreted roll interactions
, _centroidStillThreshold(StationaryCentroidInfo, 0.0018f, 0.f, 0.01f)
@@ -321,6 +340,8 @@ TouchInteraction::TouchInteraction()
addProperty(_zoomSensitivityProportionalDist);
addProperty(_zoomSensitivityDistanceThreshold);
addProperty(_zoomBoundarySphereMultiplier);
addProperty(_zoomInLimit);
addProperty(_zoomOutLimit);
addProperty(_constTimeDecay_secs);
addProperty(_inputStillThreshold);
addProperty(_centroidStillThreshold);
@@ -673,7 +694,8 @@ void TouchInteraction::directControl(const std::vector<TuioCursor>& list) {
if (c != list.end()) {
// normalized -1 to 1 coordinates on screen
screenPoints.emplace_back(2 * (c->getX() - 0.5), -2 * (c->getY() - 0.5));
} else {
}
else {
global::moduleEngine.module<ImGUIModule>()->touchInput = {
true,
glm::dvec2(0.0, 0.0),
@@ -1130,7 +1152,8 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
static_cast<float>(_zoomSensitivityExponential)
);
}
} else {
}
else {
zoomFactor = 1.0;
}
_vel.zoom = zoomFactor * _zoomSensitivityProportionalDist *
@@ -1283,7 +1306,6 @@ void TouchInteraction::step(double dt) {
dquat localCamRot = inverse(globalCamRot) * _camera->rotationQuaternion();
double boundingSphere = anchor->boundingSphere();
dvec3 centerToBoundingSphere;
double distance = std::max(length(centerToCamera) - boundingSphere, 0.0);
_currentRadius = boundingSphere /
std::max(distance * _projectionScaleFactor, 1.0);
@@ -1327,19 +1349,67 @@ void TouchInteraction::step(double dt) {
globalCamRot = normalize(quat_cast(inverse(lookAtMatrix)));
}
{ // Zooming
centerToBoundingSphere = -directionToCenter * boundingSphere;
centerToCamera = camPos - centerPos;
double planetBoundaryRadius = length(centerToBoundingSphere);
planetBoundaryRadius *= _zoomBoundarySphereMultiplier;
double distToSurface = length(centerToCamera - planetBoundaryRadius);
// This is a rough estimate of the node surface
double zoomInBounds = boundingSphere * _zoomBoundarySphereMultiplier;
// If nobody has set another zoom in limit, use the default zoom in bounds
if (_zoomInLimit.value() < 0) {
_zoomInLimit.setValue(zoomInBounds);
}
else if (_zoomInLimit.value() < zoomInBounds) {
// If zoom in limit is less than the estimated node radius we need to
// make sure we do not get too close to possible height maps
SurfacePositionHandle posHandle = anchor->calculateSurfacePositionHandle(camPos);
glm::dvec3 centerToActualSurfaceModelSpace = posHandle.centerToReferenceSurface +
posHandle.referenceSurfaceOutDirection * posHandle.heightToSurface;
glm::dvec3 centerToActualSurface = glm::dmat3(anchor->modelTransform()) *
centerToActualSurfaceModelSpace;
double nodeRadius = length(centerToActualSurface);
// Because of heightmaps we should make sure we do not go through the surface
if (_zoomInLimit.value() < nodeRadius) {
#ifdef TOUCH_DEBUG_PROPERTIES
LINFO(fmt::format(
"{}: Zoom In Limit should be larger than anchor center to surface, setting it to {}",
_loggerCat, zoomInBounds);
#endif
_zoomInLimit.setValue(zoomInBounds);
}
}
// Make sure zoom in limit is not larger than zoom out limit
if (_zoomInLimit.value() > _zoomOutLimit.value()) {
LWARNING(fmt::format(
"{}: Zoom In Limit should be smaller than Zoom Out Limit",
_loggerCat, _zoomOutLimit.value()
));
}
//Apply the velocity to update camera position
glm::dvec3 velocityIncr = directionToCenter * _vel.zoom * dt;
bool isDeltaLessThanDistToSurface = (length(_vel.zoom * dt) < distToSurface);
bool isNewPosOutsidePlanetRadius =
(length(centerToCamera + velocityIncr) > planetBoundaryRadius);
if (isDeltaLessThanDistToSurface && isNewPosOutsidePlanetRadius) {
camPos += velocityIncr;
glm::dvec3 zoomDistanceIncrement = directionToCenter * _vel.zoom * dt;
double newPosDistance = length(centerToCamera + zoomDistanceIncrement);
double currentPosDistance = length(centerToCamera);
// Possible with other navigations performed outside touch interaction
bool currentPosViolatingZoomOutLimit = (currentPosDistance >= _zoomOutLimit.value());
bool willNewPositionViolateZoomOutLimit = (newPosDistance >= _zoomOutLimit.value());
bool willNewPositionViolateZoomInLimit = (newPosDistance < _zoomInLimit.value());
if(!willNewPositionViolateZoomInLimit && !willNewPositionViolateZoomOutLimit){
camPos += zoomDistanceIncrement;
}
else if (currentPosViolatingZoomOutLimit) {
#ifdef TOUCH_DEBUG_PROPERTIES
LINFO(fmt::format(
"{}: You are outside zoom out {} limit, only zoom in allowed",
_loggerCat, _zoomOutLimit.value());
#endif
// Only allow zooming in if you are outside the zoom out limit
if (newPosDistance < currentPosDistance)
{
camPos += zoomDistanceIncrement;
}
}
else {
#ifdef TOUCH_DEBUG_PROPERTIES
@@ -1534,7 +1604,8 @@ void TouchInteraction::setFocusNode(const SceneGraphNode* focusNode) {
global::navigationHandler.orbitalNavigator().setAnchorNode(
focusNode->identifier()
);
} else {
}
else {
global::navigationHandler.orbitalNavigator().setAnchorNode("");
}
}

View File

@@ -131,7 +131,7 @@ void TouchMarker::render(const std::vector<TUIO::TuioCursor>& list) {
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_PROGRAM_POINT_SIZE); // Enable gl_PointSize in vertex shader
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
// glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // When included this line makes the webgui disappear at touch interaction
glBindVertexArray(_quad);
glDrawArrays(GL_POINTS, 0, static_cast<int>(_vertexData.size() / 2));

View File

@@ -86,7 +86,6 @@ public:
void sendTouchPressEvent(const CefMouseEvent & event,
CefBrowserHost::MouseButtonType button, const int clickCount);
void sendResleasePressEvent(const CefMouseEvent & event,
CefBrowserHost::MouseButtonType button, const int clickCount);

View File

@@ -27,10 +27,10 @@
#include <openspace/util/openspacemodule.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/stringlistproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <ghoul/misc/process.h>
#include <memory>
#include <vector>

View File

@@ -55,6 +55,7 @@ Asset = "default"
-- Asset = "insight"
-- Asset = "apollo8"
-- Asset = "apollo_sites"
-- Asset = "touch"
-- These scripts are executed after the initialization of each scene, thus making
-- it possible to have global overrides to default values or execute other scripts
@@ -130,7 +131,6 @@ ModuleConfigurations = {
WebSocketInterface = "DefaultWebSocketInterface"
},
CefWebGui = {
-- GuiUrl = "http://localhost:4680/#/onscreen/",
-- GuiScale = 2.0,
Enabled = true,
Visible = true

View File

@@ -1,6 +1,6 @@
openspace.documentation = {
{
Name = "markInteratingNodes",
Name = "markInterestingNodes",
Arguments = "List of nodes",
Documentation = "This function marks the scene graph nodes identified by name " ..
"as interesting, which will provide shortcut access to focus buttons and " ..

View File

@@ -296,7 +296,6 @@ void parseLuaState(Configuration& configuration) {
getValue(s, KeyMasterRotation, c.masterRotation);
getValue(s, KeyDisableInGameConsole, c.isConsoleDisabled);
getValue(s, KeyRenderingMethod, c.renderingMethod);
getValue(s, KeyLogging, c.logging);
getValue(s, KeyDocumentation, c.documentation);
getValue(s, KeyLoadingScreen, c.loadingScreen);

View File

@@ -165,6 +165,15 @@ void NavigationHandler::deinitialize() {
global::parallelPeer.connectionEvent().unsubscribe("NavigationHandler");
}
void NavigationHandler::setFocusNode(SceneGraphNode* node) {
_orbitalNavigator.setFocusNode(node);
_camera->setPositionVec3(anchorNode()->worldPosition());
}
void NavigationHandler::resetCameraDirection() {
_orbitalNavigator.startRetargetAnchor();
}
void NavigationHandler::setCamera(Camera* camera) {
_camera = camera;
_orbitalNavigator.setCamera(camera);
@@ -299,6 +308,10 @@ void NavigationHandler::stopPlayback() {
_playbackModeEnabled = false;
}
const SceneGraphNode* NavigationHandler::anchorNode() const {
return _orbitalNavigator.anchorNode();
}
Camera* NavigationHandler::camera() const {
return _camera;
}

View File

@@ -23,7 +23,6 @@
****************************************************************************************/
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/updatestructures.h>
#include <openspace/query/query.h>
@@ -35,6 +34,7 @@ namespace {
constexpr const double AngleEpsilon = 1E-7;
constexpr const double DistanceRatioAimThreshold = 1E-4;
constexpr const double FlightDestinationFactor = 1E-4;
constexpr const openspace::properties::Property::PropertyInfo AnchorInfo = {
"Anchor",
@@ -136,12 +136,31 @@ namespace {
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo StereoInterpolationTimeInfo =
{
"StereoInterpolationTime",
"Stereo interpolation time",
"The time to interpolate to a new stereoscopic depth "
"when the anchor node is changed."
constexpr openspace::properties::Property::PropertyInfo VelocityZoomControlInfo = {
"VelocityZoomControl",
"Velocity zoom control",
"Controls the velocity of the camera motion when zooming in to the focus node. "
"The higher the value the faster the camera will move towards the focus."
};
constexpr openspace::properties::Property::PropertyInfo ApplyLinearFlightInfo = {
"ApplyLinearFlight",
"Apply Linear Flight",
"This property makes the camera move to the specified distance 'FlightDestinationDistance' while facing the anchor"
};
constexpr openspace::properties::Property::PropertyInfo FlightDestinationDistanceInfo = {
"FlightDestinationDistance",
"Flight Destination Distance",
"The final distance we want to fly to, with regards to the anchor node."
};
constexpr openspace::properties::Property::PropertyInfo
StereoInterpolationTimeInfo = {
"StereoInterpolationTime",
"Stereo interpolation time",
"The time to interpolate to a new stereoscopic depth "
"when the anchor node is changed."
};
constexpr openspace::properties::Property::PropertyInfo
@@ -210,6 +229,9 @@ OrbitalNavigator::OrbitalNavigator()
, _retargetAim(RetargetAimInfo)
, _followAnchorNodeRotationDistance(FollowAnchorNodeInfo, 5.0f, 0.0f, 20.f)
, _minimumAllowedDistance(MinimumDistanceInfo, 10.0f, 0.0f, 10000.f)
, _velocitySensitivity(VelocityZoomControlInfo, 0.02f, 0.01f, 0.15f)
, _applyLinearFlight(ApplyLinearFlightInfo, false)
, _flightDestinationDistance(FlightDestinationDistanceInfo, 2e8f, 0.0f, 1e10f)
, _mouseSensitivity(MouseSensitivityInfo, 15.0f, 1.0f, 50.f)
, _joystickSensitivity(JoystickSensitivityInfo, 10.0f, 1.0f, 50.f)
, _websocketSensitivity(WebsocketSensitivityInfo, 10.0f, 1.0f, 50.f)
@@ -262,7 +284,8 @@ OrbitalNavigator::OrbitalNavigator()
_retargetAim.onChange([this]() {
if (_aimNode && _aimNode != _anchorNode) {
startRetargetAim();
} else {
}
else {
startRetargetAnchor();
}
});
@@ -337,6 +360,9 @@ OrbitalNavigator::OrbitalNavigator()
addProperty(_retargetAim);
addProperty(_followAnchorNodeRotationDistance);
addProperty(_minimumAllowedDistance);
addProperty(_velocitySensitivity);
addProperty(_flightDestinationDistance);
addProperty(_applyLinearFlight);
addProperty(_useAdaptiveStereoscopicDepth);
addProperty(_staticViewScaleExponent);
@@ -393,6 +419,9 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
}
const glm::dvec3 anchorPos = _anchorNode->worldPosition();
SurfacePositionHandle posHandle;
const glm::dvec3 prevCameraPosition = _camera->positionVec3();
const glm::dvec3 anchorDisplacement = _previousAnchorNodePosition.has_value() ?
(anchorPos - _previousAnchorNodePosition.value()) :
@@ -403,6 +432,31 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
_camera->rotationQuaternion()
};
if (_applyLinearFlight) {
// Calculate a position handle based on the camera position in world space
glm::dvec3 camPosToAnchorPosDiff = prevCameraPosition - anchorPos;
// Use the boundingsphere to get an approximate distance to the surface of the node
double nodeRadius = static_cast<double>(_anchorNode->boundingSphere());
double distFromCameraToFocus = glm::distance(prevCameraPosition, anchorPos) - nodeRadius;
// Make the approximation delta size depending on the flight distance
double arrivalThreshold = _flightDestinationDistance.value() * FlightDestinationFactor;
// Fly towards the flight destination distance. When getting closer than arrivalThreshold terminate the flight
if (abs(distFromCameraToFocus - _flightDestinationDistance.value()) > arrivalThreshold) {
pose.position = moveCameraAlongVector(
pose.position,
distFromCameraToFocus,
camPosToAnchorPosDiff,
_flightDestinationDistance
);
}
else {
_applyLinearFlight.setValue(false);
}
}
const bool hasPreviousPositions =
_previousAnchorNodePosition.has_value() &&
_previousAimNodePosition.has_value();
@@ -433,8 +487,7 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
_previousAnchorNodePosition = _anchorNode->worldPosition();
// Calculate a position handle based on the camera position in world space
SurfacePositionHandle posHandle =
calculateSurfacePositionHandle(*_anchorNode, pose.position);
posHandle = calculateSurfacePositionHandle(*_anchorNode, pose.position);
// Decompose camera rotation so that we can handle global and local rotation
// individually. Then we combine them again when finished.
@@ -532,7 +585,8 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
if (_directlySetStereoDistance) {
_currentCameraToSurfaceDistance = targetCameraToSurfaceDistance;
_directlySetStereoDistance = false;
} else {
}
else {
_currentCameraToSurfaceDistance = interpolateCameraToSurfaceDistance(
deltaTime,
_currentCameraToSurfaceDistance,
@@ -543,7 +597,8 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
_stereoscopicDepthOfFocusSurface /
static_cast<float>(_currentCameraToSurfaceDistance)
);
} else {
}
else {
_camera->setScaling(glm::pow(10.f, _staticViewScaleExponent));
}
}
@@ -972,6 +1027,7 @@ glm::dquat OrbitalNavigator::rotateLocally(double deltaTime,
return localCameraRotation * joystickRotationDiff * mouseRotationDiff *
websocketRotationDiff * scriptRotationDiff;
}
glm::dquat OrbitalNavigator::interpolateLocalRotation(double deltaTime,
const glm::dquat& localCameraRotation)
{
@@ -1173,6 +1229,27 @@ glm::dvec3 OrbitalNavigator::translateHorizontally(double deltaTime,
return cameraPosition + rotationDiffVec3;
}
glm::dvec3 OrbitalNavigator::moveCameraAlongVector(const glm::dvec3& camPos,
double distFromCameraToFocus,
const glm::dvec3& camPosToAnchorPosDiff,
double destination) const
{
// This factor adapts the velocity so it slows down when getting closer
// to our final destination
double velocity = 0.0;
if (destination < distFromCameraToFocus) { // When flying towards anchor
velocity = 1.0 - destination / distFromCameraToFocus;
}
else { // When flying away from anchor
velocity = distFromCameraToFocus / destination - 1.0;
}
velocity *= _velocitySensitivity;
// Return the updated camera position
return camPos - velocity * camPosToAnchorPosDiff;
}
glm::dvec3 OrbitalNavigator::followAnchorNodeRotation(const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const glm::dquat& focusNodeRotationDiff) const

View File

@@ -37,6 +37,12 @@ namespace {
"If this value is 'false', this dashboard will be invisible, regardless of the "
"state of the individual components"
};
constexpr openspace::properties::Property::PropertyInfo StartPositionOffsetInfo = {
"StartPositionOffset",
"Start Position Offset",
"A 2D vector controlling where the dashboard rendering starts."
"Adding an offset in x and y-direction on screen"
};
} // namespace
namespace openspace {
@@ -44,8 +50,12 @@ namespace openspace {
Dashboard::Dashboard()
: properties::PropertyOwner({ "Dashboard" })
, _isEnabled(EnabledInfo, true)
, _startPositionOffset(
properties::IVec2Property(StartPositionOffsetInfo, glm::ivec2(10, -10))
)
{
addProperty(_isEnabled);
addProperty(_startPositionOffset);
}
void Dashboard::addDashboardItem(std::unique_ptr<DashboardItem> item) {
@@ -128,6 +138,10 @@ void Dashboard::render(glm::vec2& penPosition) {
}
}
glm::vec2 Dashboard::getStartPositionOffset() {
return _startPositionOffset.value();
};
scripting::LuaLibrary Dashboard::luaLibrary() {
return {
"dashboard",

View File

@@ -54,6 +54,12 @@ namespace {
openspace::properties::Property::Visibility::Hidden
};
constexpr openspace::properties::Property::PropertyInfo BoundingSphereInfo = {
"BoundingSphere",
"Bounding Sphere",
"The size of the bounding sphere radius."
};
} // namespace
namespace openspace {
@@ -109,6 +115,7 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary)
, _enabled(EnabledInfo, true)
, _opacity(OpacityInfo, 1.f, 0.f, 1.f)
, _renderableType(RenderableTypeInfo, "Renderable")
, _boundingSphere(BoundingSphereInfo, 0.f, 0.f, 3e10f)
{
// I can't come up with a good reason not to do this for all renderables
registerUpdateRenderBinFromOpacity();
@@ -144,6 +151,7 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary)
_renderableType = dictionary.value<std::string>(RenderableTypeInfo.identifier);
}
addProperty(_renderableType);
addProperty(_boundingSphere);
}
void Renderable::initialize() {}
@@ -159,11 +167,12 @@ void Renderable::update(const UpdateData&) {}
void Renderable::render(const RenderData&, RendererTasks&) {}
void Renderable::setBoundingSphere(float boundingSphere) {
_boundingSphere = boundingSphere;
_boundingSphere.setValue(boundingSphere);
}
float Renderable::boundingSphere() const {
return _boundingSphere;
return _boundingSphere.value();
}
SurfacePositionHandle Renderable::calculateSurfacePositionHandle(

View File

@@ -793,7 +793,7 @@ void RenderEngine::renderEndscreen() {
);
glm::vec2 penPosition = glm::vec2(
fontResolution().x / 2 - size.boundingBox.x / 2,
fontResolution().y / 2- size.boundingBox.y / 2
fontResolution().y / 2 - size.boundingBox.y / 2
);
RenderFont(*_fontDate, penPosition, "Shutting down");
}
@@ -836,9 +836,11 @@ void RenderEngine::renderDashboard() {
"Main Dashboard::render"
);
}
glm::vec2 dashboardStart = global::dashboard.getStartPositionOffset();
glm::vec2 penPosition = glm::vec2(
10.f,
fontResolution().y - global::luaConsole.currentHeight()
dashboardStart.x,
dashboardStart.y + fontResolution().y - global::luaConsole.currentHeight()
);
global::dashboard.render(penPosition);

View File

@@ -27,13 +27,14 @@
#include <modules/base/scale/staticscale.h>
#include <modules/base/rotation/staticrotation.h>
#include <modules/base/translation/statictranslation.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/timeframe.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/opengl/ghoul_gl.h>
#include "scenegraphnode_doc.inl"
namespace {
@@ -49,8 +50,48 @@ namespace {
constexpr const char* KeyTimeFrame = "TimeFrame";
constexpr openspace::properties::Property::PropertyInfo ComputeScreenSpaceInfo =
{
"ComputeScreenSpaceData",
"Screen Space Data",
"If this value is set to 'true', the screenspace-based properties are calculated "
"at regular intervals. If these values are set to 'false', they are not updated."
};
constexpr openspace::properties::Property::PropertyInfo ScreenSpacePositionInfo = {
"ScreenSpacePosition",
"ScreenSpacePosition",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo ScreenVisibilityInfo = {
"ScreenVisibility",
"ScreenVisibility",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo DistanceFromCamToNodeInfo = {
"DistanceFromCamToNode",
"DistanceFromCamToNode",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo ScreenSizeRadiusInfo = {
"ScreenSizeRadius",
"ScreenSizeRadius",
"" // @TODO Missing documentation
};
constexpr openspace::properties::Property::PropertyInfo VisibilityDistanceInfo = {
"VisibilityDistance",
"VisibilityDistance",
"" // @TODO Missing documentation
};
constexpr const char* KeyFixedBoundingSphere = "FixedBoundingSphere";
constexpr openspace::properties::Property::PropertyInfo BoundingSphereInfo = {
"BoundingSphere",
"Bounding Sphere",
"The bounding sphere of the scene graph node. This can be the "
"bounding sphere of a renderable or a fixed bounding sphere. "
};
constexpr openspace::properties::Property::PropertyInfo GuiPathInfo = {
"GuiPath",
"Gui Path",
@@ -235,6 +276,8 @@ std::unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
}
LDEBUG(fmt::format("Successfully created SceneGraphNode '{}'", result->identifier()));
result->_lastScreenSpaceUpdateTime = std::chrono::high_resolution_clock::now();
return result;
}
@@ -248,7 +291,22 @@ SceneGraphNode::SceneGraphNode()
std::make_unique<StaticRotation>(),
std::make_unique<StaticScale>()
}
{}
, _computeScreenSpaceValues(ComputeScreenSpaceInfo, false)
, _screenSpacePosition(
properties::IVec2Property(ScreenSpacePositionInfo, glm::ivec2(-1, -1))
)
, _screenVisibility(properties::BoolProperty(ScreenVisibilityInfo, false))
, _distFromCamToNode(properties::DoubleProperty(DistanceFromCamToNodeInfo, -1.0))
, _screenSizeRadius(properties::DoubleProperty(ScreenSizeRadiusInfo, 0))
, _visibilityDistance(properties::FloatProperty(VisibilityDistanceInfo, 6e10f))
{
addProperty(_computeScreenSpaceValues);
addProperty(_screenSpacePosition);
addProperty(_screenVisibility);
addProperty(_distFromCamToNode);
addProperty(_screenSizeRadius);
addProperty(_visibilityDistance);
}
SceneGraphNode::~SceneGraphNode() {} // NOLINT
@@ -455,6 +513,9 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) {
auto start = std::chrono::high_resolution_clock::now();
_renderable->render(newData, tasks);
if (_computeScreenSpaceValues) {
computeScreenSpaceData(newData);
}
glFinish();
auto end = std::chrono::high_resolution_clock::now();
@@ -462,6 +523,9 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) {
}
else {
_renderable->render(newData, tasks);
if (_computeScreenSpaceValues) {
computeScreenSpaceData(newData);
}
}
}
@@ -595,6 +659,86 @@ void SceneGraphNode::setDependencies(const std::vector<SceneGraphNode*>& depende
}
}
void SceneGraphNode::computeScreenSpaceData(RenderData& newData) {
// Purposely slow the update rate of screen space position in order to reduce the
// effects of jittering in the position of information icon markers in web gui.
auto now = std::chrono::high_resolution_clock::now();
if ((now - _lastScreenSpaceUpdateTime) < std::chrono::milliseconds(100)) {
return;
}
_lastScreenSpaceUpdateTime = now;
// Calculate ndc
const Camera& cam = newData.camera;
const glm::dvec3& worldPos = _worldPositionCached;
const glm::dvec4 clipSpace = glm::dmat4(cam.projectionMatrix()) *
cam.combinedViewMatrix() * glm::vec4(worldPos, 1.0);
const glm::dvec2 worldPosNDC = glm::dvec2(clipSpace / clipSpace.w);
const bool visible = worldPosNDC.x >= -1.0 && worldPosNDC.x <= 1.0 &&
worldPosNDC.y >= -1.0 && worldPosNDC.y <= 1.0 && clipSpace.z > 0;
// If not on the screen, we want to reset it or don't update it
if (!visible) {
_screenVisibility = false;
return;
}
glm::ivec2 res = global::windowDelegate.currentWindowSize();
// Get the radius of node
double nodeRadius = static_cast<double>(this->boundingSphere());
// Distance from the camera to the node
double distFromCamToNode = glm::distance(cam.positionVec3(), worldPos) - nodeRadius;
// Fix to limit the update of properties
if (distFromCamToNode >= _visibilityDistance) {
_screenVisibility = false;
return;
}
_screenVisibility = true;
// Calculate the node radius to screensize pixels
const glm::dvec3 lookUp = normalize(cam.lookUpVectorWorldSpace());
const glm::dvec3 radiusPos = worldPos + (nodeRadius * lookUp);
const glm::dvec4 clipSpaceRadius = glm::dmat4(cam.projectionMatrix()) *
cam.combinedViewMatrix() * glm::vec4(radiusPos, 1.0);
const glm::dvec3 radiusNDC = clipSpaceRadius / clipSpaceRadius.w;
const glm::ivec2 centerScreenSpace = glm::ivec2(
(worldPosNDC.x + 1.0) * res.x / 2,
(worldPosNDC.y + 1.0) * res.y / 2
);
const glm::ivec2 radiusScreenSpace = glm::ivec2(
(radiusNDC.x + 1.0) * res.x / 2,
(radiusNDC.y + 1.0) * res.y / 2
);
const double screenSpaceRadius = length(
glm::vec2(centerScreenSpace) - glm::vec2(radiusScreenSpace)
);
constexpr const double RadiusThreshold = 2.0;
const double r = abs(_screenSizeRadius - screenSpaceRadius);
if (r > RadiusThreshold) {
_screenSizeRadius = screenSpaceRadius;
}
constexpr const double ZoomThreshold = 0.1;
const double d = abs(_distFromCamToNode - distFromCamToNode);
if (d > (ZoomThreshold * distFromCamToNode)) {
_distFromCamToNode = distFromCamToNode;
}
constexpr const double MoveThreshold = 1.0;
const glm::ivec2 ssp = _screenSpacePosition;
const glm::dvec2 c = glm::abs(ssp - centerScreenSpace);
if (c.x > MoveThreshold || c.y > MoveThreshold) {
_screenSpacePosition = centerScreenSpace;
}
}
SurfacePositionHandle SceneGraphNode::calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace) const
{
@@ -765,13 +909,13 @@ Renderable* SceneGraphNode::renderable() {
return _renderable.get();
}
SceneGraphNode* SceneGraphNode::childNode(const std::string& identifier) {
if (this->identifier() == identifier) {
SceneGraphNode* SceneGraphNode::childNode(const std::string& id) {
if (identifier() == id) {
return this;
}
else {
for (std::unique_ptr<SceneGraphNode>& it : _children) {
SceneGraphNode* tmp = it->childNode(identifier);
SceneGraphNode* tmp = it->childNode(id);
if (tmp) {
return tmp;
}