merge from upstream

This commit is contained in:
Seth Alves 2016-03-14 11:15:16 -07:00
commit 908481d5d4
86 changed files with 1417 additions and 1147 deletions

View file

@ -272,6 +272,18 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
tree->setWantTerseEditLogging(wantTerseEditLogging);
}
void EntityServer::nodeAdded(SharedNodePointer node) {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
tree->knowAvatarID(node->getUUID());
OctreeServer::nodeAdded(node);
}
void EntityServer::nodeKilled(SharedNodePointer node) {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
tree->deleteDescendantsOfAvatar(node->getUUID());
tree->forgetAvatarID(node->getUUID());
OctreeServer::nodeKilled(node);
}
// FIXME - this stats tracking is somewhat temporary to debug the Whiteboard issues. It's not a bad
// set of stats to have, but we'd probably want a different data structure if we keep it very long.

View file

@ -58,6 +58,8 @@ public:
virtual void trackViewerGone(const QUuid& sessionID) override;
public slots:
virtual void nodeAdded(SharedNodePointer node);
virtual void nodeKilled(SharedNodePointer node);
void pruneDeletedEntities();
protected:

View file

@ -127,8 +127,8 @@ public:
public slots:
/// runs the octree server assignment
void run();
void nodeAdded(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
virtual void nodeAdded(SharedNodePointer node);
virtual void nodeKilled(SharedNodePointer node);
void sendStatsPacket();
private slots:

View file

@ -46,6 +46,8 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
set(SHADER_TARGET ${SHADER_TARGET}_frag.h)
endif()
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}")
# Target dependant Custom rule on the SHADER_FILE
if (APPLE)
set(GLPROFILE MAC_GL)
@ -87,10 +89,14 @@ macro(AUTOSCRIBE_SHADER_LIB)
file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf)
#make the shader folder
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
file(MAKE_DIRECTORY ${SHADERS_DIR})
#message(${SHADER_INCLUDE_FILES})
foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
file(TO_CMAKE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
endforeach()
#message(${AUTOSCRIBE_SHADER_SRC})
@ -105,4 +111,7 @@ macro(AUTOSCRIBE_SHADER_LIB)
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_SOURCE_FILES})
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC})
# Link library shaders, if they exist
include_directories("${SHADERS_DIR}")
endmacro()

View file

@ -19,20 +19,14 @@ macro(LINK_HIFI_LIBRARIES)
endif ()
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
# link the actual library - it is static so don't bubble it up
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
# ask the library what its include dependencies are and link them
get_target_property(LINKED_TARGET_DEPENDENCY_INCLUDES ${HIFI_LIBRARY} DEPENDENCY_INCLUDES)
if(LINKED_TARGET_DEPENDENCY_INCLUDES)
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES ${LINKED_TARGET_DEPENDENCY_INCLUDES})
endif()
endforeach()
setup_memory_debugger()
endmacro(LINK_HIFI_LIBRARIES)
endmacro(LINK_HIFI_LIBRARIES)

View file

@ -152,9 +152,19 @@ function maybeMoveOverlay() {
// MAIN CONTROL
var wasMuted, isAway;
var wasOverlaysVisible = Menu.isOptionChecked("Overlays");
var eventMappingName = "io.highfidelity.away"; // goActive on hand controller button events, too.
var eventMapping = Controller.newMapping(eventMappingName);
// backward compatible version of getting HMD.mounted, so it works in old clients
function safeGetHMDMounted() {
if (HMD.mounted === undefined) {
return true;
}
return HMD.mounted;
}
var wasHmdMounted = safeGetHMDMounted();
function goAway() {
if (isAway) {
return;
@ -169,12 +179,20 @@ function goAway() {
playAwayAnimation(); // animation is still seen by others
showOverlay();
// remember the View > Overlays state...
wasOverlaysVisible = Menu.isOptionChecked("Overlays");
// show overlays so that people can see the "Away" message
Menu.setIsOptionChecked("Overlays", true);
// tell the Reticle, we want to stop capturing the mouse until we come back
Reticle.allowMouseCapture = false;
if (HMD.active) {
Reticle.visible = false;
}
wasHmdMounted = safeGetHMDMounted(); // always remember the correct state
}
function goActive() {
if (!isAway) {
return;
@ -188,12 +206,16 @@ function goActive() {
stopAwayAnimation();
hideOverlay();
// restore overlays state to what it was when we went "away"
Menu.setIsOptionChecked("Overlays", wasOverlaysVisible);
// tell the Reticle, we are ready to capture the mouse again and it should be visible
Reticle.allowMouseCapture = true;
Reticle.visible = true;
if (HMD.active) {
Reticle.position = HMD.getHUDLookAtPosition2D();
}
wasHmdMounted = safeGetHMDMounted(); // always remember the correct state
}
function maybeGoActive(event) {
@ -206,6 +228,7 @@ function maybeGoActive(event) {
goActive();
}
}
var wasHmdActive = HMD.active;
var wasMouseCaptured = Reticle.mouseCaptured;
@ -225,6 +248,13 @@ function maybeGoAway() {
goAway();
}
}
// If you've removed your HMD from your head, and we can detect it, we will also go away...
var hmdMounted = safeGetHMDMounted();
if (HMD.active && !hmdMounted && wasHmdMounted) {
wasHmdMounted = hmdMounted;
goAway();
}
}
Script.update.connect(maybeMoveOverlay);

View file

@ -21,7 +21,7 @@ var MINIMUM_DEPTH_ADJUST = 0.01;
var NON_LINEAR_DIVISOR = 2;
var MINIMUM_SEEK_DISTANCE = 0.01;
var lastMouseMove = Date.now();
var lastMouseMoveOrClick = Date.now();
var lastMouseX = Reticle.position.x;
var lastMouseY = Reticle.position.y;
var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds
@ -32,6 +32,14 @@ var WEIGHTING = 1/20; // simple moving average over last 20 samples
var ONE_MINUS_WEIGHTING = 1 - WEIGHTING;
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50;
function showReticleOnMouseClick() {
Reticle.visible = true;
lastMouseMoveOrClick = Date.now(); // move or click
}
Controller.mousePressEvent.connect(showReticleOnMouseClick);
Controller.mouseDoublePressEvent.connect(showReticleOnMouseClick);
Controller.mouseMoveEvent.connect(function(mouseEvent) {
var now = Date.now();
@ -47,7 +55,7 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) {
if (HMD.active && !shouldSeekToLookAt && Reticle.allowMouseCapture) {
var dx = Reticle.position.x - lastMouseX;
var dy = Reticle.position.y - lastMouseY;
var dt = Math.max(1, (now - lastMouseMove)); // mSecs since last mouse move
var dt = Math.max(1, (now - lastMouseMoveOrClick)); // mSecs since last mouse move
var mouseMoveDistance = Math.sqrt((dx*dx) + (dy*dy));
var mouseVelocity = mouseMoveDistance / dt;
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * mouseVelocity);
@ -56,7 +64,7 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) {
}
}
}
lastMouseMove = now;
lastMouseMoveOrClick = now;
lastMouseX = mouseEvent.x;
lastMouseY = mouseEvent.y;
});
@ -94,7 +102,7 @@ function autoHideReticle() {
// system overlay (like a window), then hide the reticle
if (Reticle.visible && !Reticle.pointingAtSystemOverlay) {
var now = Date.now();
var timeSinceLastMouseMove = now - lastMouseMove;
var timeSinceLastMouseMove = now - lastMouseMoveOrClick;
if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {
Reticle.visible = false;
}

View file

@ -1237,7 +1237,7 @@
<div class="section-header">
<label>Spacial Properties</label>
<label>Spatial Properties</label>
</div>
<div class="property">

96
examples/playTestSound.js Normal file
View file

@ -0,0 +1,96 @@
//
// playTestSound.js
// examples
//
// Created by Philip Rosedale
// Copyright 2014 High Fidelity, Inc.
//
// Creates an object in front of you that changes color and plays a light
// at the start of a drum clip that loops. As you move away it will tell you in the
// log how many meters you are from the source.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var sound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Drums/deepdrum1.wav");
var position = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), Quat.getFront(MyAvatar.orientation));
var time;
var soundPlaying = null;
var baseColor = { red: 100, green: 100, blue: 100 };
var litColor = { red: 255, green: 100, blue: 0 };
var lightTime = 250;
var distance = 0.0;
// Create object for visual reference
var box = Entities.addEntity({
type: "Box",
dimensions: { x: 0.25, y: 0.5, z: 0.25 },
color: baseColor,
position: position
});
function checkSound(deltaTime) {
var started = false;
if (!sound.downloaded) {
return;
}
if (soundPlaying == null) {
soundPlaying = Audio.playSound(sound, {
position: position,
volume: 1.0,
loop: false } );
started = true;
} else if (!soundPlaying.isPlaying) {
soundPlaying.restart();
started = true;
}
if (started) {
Entities.editEntity(box, { color: litColor });
Entities.addEntity({
type: "Light",
intensity: 5.0,
falloffRadius: 10.0,
dimensions: {
x: 40,
y: 40,
z: 40
},
position: Vec3.sum(position, { x: 0, y: 1, z: 0 }),
color: litColor,
lifetime: lightTime / 1000
});
Script.setTimeout(resetColor, lightTime);
}
var currentDistance = Vec3.distance(MyAvatar.position, position);
if (Math.abs(currentDistance - distance) > 1.0) {
print("Distance from source: " + currentDistance);
distance = currentDistance;
}
}
function resetColor() {
Entities.editEntity(box, { color: baseColor });
}
function scriptEnding() {
Entities.deleteEntity(box);
if (soundPlaying) {
print("stop injector");
soundPlaying.stop();
}
}
// Connect a call back that happens every frame
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(checkSound);

View file

@ -0,0 +1,91 @@
// rapidProceduralChangeTest.js
// examples/tests/rapidProceduralChange
//
// Created by Eric Levin on 3/9/2016.
// Copyright 2016 High Fidelity, Inc.
//
// This test creates primitives with fragment shaders and rapidly updates its uniforms, as well as a skybox.
// For the test to pass:
// - The primitives (cube and sphere) should update at rate of update loop, cycling through red values.
// - The skymap should do the same, although its periodicity may be different.
//
// Under the hood, the primitives are driven by a uniform, while the skymap is driven by a timer.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var orientation = Camera.getOrientation();
orientation = Quat.safeEulerAngles(orientation);
orientation.x = 0;
orientation = Quat.fromVec3Degrees(orientation);
var centerUp = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
centerUp.y += 0.5;
var centerDown = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
centerDown.y -= 0.5;
var ENTITY_SHADER_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/shaders/uniformTest.fs";
var SKYBOX_SHADER_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/shaders/timerTest.fs";
var entityData = {
ProceduralEntity: {
shaderUrl: ENTITY_SHADER_URL,
uniforms: { red: 0.0 }
}
};
var skyboxData = {
ProceduralEntity: {
shaderUrl: SKYBOX_SHADER_URL,
uniforms: { red: 0.0 }
}
};
var testBox = Entities.addEntity({
type: "Box",
dimensions: { x: 0.5, y: 0.5, z: 0.5 },
position: centerUp,
userData: JSON.stringify(entityData)
});
var testSphere = Entities.addEntity({
type: "Sphere",
dimensions: { x: 0.5, y: 0.5, z: 0.5 },
position: centerDown,
userData: JSON.stringify(entityData)
});
var testZone = Entities.addEntity({
type: "Zone",
dimensions: { x: 50, y: 50, z: 50 },
position: MyAvatar.position,
userData: JSON.stringify(skyboxData),
backgroundMode: "skybox",
skybox: { url: "http://kyoub.googlecode.com/svn/trunk/KYouB/textures/skybox_test.png" }
});
var currentTime = 0;
function update(deltaTime) {
var red = (Math.sin(currentTime) + 1) / 2;
entityData.ProceduralEntity.uniforms.red = red;
skyboxData.ProceduralEntity.uniforms.red = red;
entityEdit = { userData: JSON.stringify(entityData) };
skyboxEdit = { userData: JSON.stringify(skyboxData) };
Entities.editEntity(testBox, entityEdit);
Entities.editEntity(testSphere, entityEdit);
Entities.editEntity(testZone, skyboxEdit);
currentTime += deltaTime;
}
Script.update.connect(update);
Script.scriptEnding.connect(cleanup);
function cleanup() {
Entities.deleteEntity(testBox);
Entities.deleteEntity(testSphere);
Entities.deleteEntity(testZone);
}

View file

@ -0,0 +1,21 @@
//
// timerTest.fs
// examples/tests/rapidProceduralChange
//
// Created by Eric Levin on 3/9/16.
// Copyright 2016 High Fidelity, Inc.
//
// This fragment shader is designed to test the rapid changing of a uniform on the timer.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
uniform float red;
vec3 getSkyboxColor() {
float blue = red;
blue = (cos(iGlobalTime) + 1) / 2;
return vec3(1.0, 0.0, blue);
}

View file

@ -0,0 +1,27 @@
//
// uniformTest.fs
// examples/tests/rapidProceduralChange
//
// Created by Eric Levin on 3/9/16.
// Copyright 2016 High Fidelity, Inc.
//
// This fragment shader is designed to test the rapid changing of a uniform.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
uniform float red;
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
fragColor = vec4(red, 0.0, 1.0, 1.0);
}
vec4 getProceduralColor() {
vec4 result;
vec2 position = _position.xz;
position += 0.5;
mainImage(result, position * iWorldScale.xz);
return result;
}

View file

@ -0,0 +1,18 @@
//
// px.fs
// examples/tests/skybox
//
// Created by Zach Pomerantz on 3/10/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
vec3 getSkyboxColor() {
float red = (cos(iGlobalTime) + 1) / 2;
vec3 color = vec3(red, 1.0, 1.0);
return color;
}

View file

@ -0,0 +1,20 @@
//
// px_rgba.fs
// examples/tests/skybox
//
// Created by Zach Pomerantz on 3/10/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
vec3 getSkyboxColor() {
float red = (cos(iGlobalTime) + 1) / 2;
vec3 color = vec3(red, 1.0, 1.0);
color *= skybox.color.rgb;
return color;
}

View file

@ -0,0 +1,22 @@
//
// px_rgba.fs
// examples/tests/skybox
//
// Created by Zach Pomerantz on 3/10/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
vec3 getSkyboxColor() {
float red = (cos(iGlobalTime) + 1) / 2;
vec3 color = vec3(red, 1.0, 1.0);
vec3 coord = normalize(_normal);
vec3 texel = texture(cubeMap, coord).rgb;
color *= texel;
return color;
}

View file

@ -0,0 +1,24 @@
//
// px_rgba.fs
// examples/tests/skybox
//
// Created by Zach Pomerantz on 3/10/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
vec3 getSkyboxColor() {
float red = (cos(iGlobalTime) + 1) / 2;
vec3 color = vec3(red, 1.0, 1.0);
vec3 coord = normalize(_normal);
vec3 texel = texture(cubeMap, coord).rgb;
color *= texel;
color *= skybox.color.rgb;
return color;
}

View file

@ -0,0 +1,83 @@
// skyboxTest.js
// examples/tests/skybox
//
// Created by Zach Pomerantz on 3/10/2016.
// Copyright 2016 High Fidelity, Inc.
//
// This test cycles through different variations on the skybox with a mouseclick.
// For the test to pass, you should observe the following cycle:
// - Procedural skybox (no texture, no color)
// - Procedural skybox (no texture, with color)
// - Procedural skybox (with texture, no color)
// - Procedural skybox (with texture, with color)
// - Color skybox (no texture)
// - Color skybox (with texture)
// - Texture skybox (no color)
//
// As you run the test, descriptions of the expected rendered skybox will appear as overlays.
//
// NOTE: This does not test uniforms/textures applied to a procedural shader through userData.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var PX_URL = Script.resolvePath('px.fs');
var PX_RGBA_URL = Script.resolvePath('px_rgba.fs');
var PX_TEX_URL = Script.resolvePath('px_tex.fs');
var PX_TEX_RGBA_URL = Script.resolvePath('px_tex_rgba.fs');
var TEX_URL = 'https://hifi-public.s3.amazonaws.com/alan/Playa/Skies/Test-Sky_out.png';
var NO_TEX = '';
var COLOR = { red: 255, green: 0, blue: 255 };
var NO_COLOR = { red: 0, green: 0, blue: 0 };
var data = { ProceduralEntity: { shaderUrl: PX_URL } };
var zone = Entities.addEntity({
type: 'Zone',
dimensions: { x: 50, y: 50, z: 50 },
position: MyAvatar.position,
backgroundMode: 'skybox'
});
var text = Overlays.addOverlay('text', {
text: 'Click this box to advance tests; note that red value cycling means white->light blue',
x: Window.innerWidth / 2 - 250, y: Window.innerHeight / 2 - 25,
width: 500, height: 50
});
print('Zone:', zone);
print('Text:', text);
var edits = [
['Red value should cycle', getEdit(PX_URL, NO_TEX, NO_COLOR)],
['Red value should cycle, no green', getEdit(PX_RGBA_URL, NO_TEX, COLOR)],
['Red value should cycle, each face tinted differently', getEdit(PX_TEX_URL, TEX_URL, NO_COLOR)],
['Red value should cycle, each face tinted differently, no green', getEdit(PX_TEX_RGBA_URL, TEX_URL, COLOR)],
['No green', getEdit(null, NO_TEX, COLOR)],
['Each face colored differently, no green', getEdit(null, TEX_URL, COLOR)],
['Each face colored differently', getEdit(null, TEX_URL, NO_COLOR)],
];
Controller.mousePressEvent.connect(function(e) { if (Overlays.getOverlayAtPoint(e) === text) next(); });
Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(text);
Entities.deleteEntity(zone);
});
var i = 0;
function next() {
var edit = edits[i];
Overlays.editOverlay(text, { text: edit[0] });
Entities.editEntity(zone, edit[1]);
i++;
i %= edits.length;
}
function getEdit(px, url, color) {
return { userData: px ? getUserData(px) : '', backgroundMode: 'skybox', skybox: { url: url, color: color } }
}
function getUserData(px) { return JSON.stringify({ ProceduralEntity: { shaderUrl: px } }); }

View file

@ -1,3 +1,13 @@
//
// MessageDialog.qml
//
// Created by Bradley Austin Davis on 18 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
@ -94,8 +104,8 @@ Item {
function buildMenu(items, targetPosition) {
var model = toModel(items);
// Menu's must be childed to desktop for Z-ordering
var newMenu = menuViewMaker.createObject(desktop, { model: model, z: topMenu ? topMenu.z + 1 : desktop.zLevels.menu });
// Menus must be childed to desktop for Z-ordering
var newMenu = menuViewMaker.createObject(desktop, { model: model, z: topMenu ? topMenu.z + 1 : desktop.zLevels.menu, isSubMenu: topMenu !== null });
if (targetPosition) {
newMenu.x = targetPosition.x
newMenu.y = targetPosition.y - newMenu.height / 3 * 1

View file

@ -1,9 +1,19 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
//
// VrMenuItem.qml
//
// Created by Bradley Austin Davis on 29 Apr 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import "../controls"
import "../styles"
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../controls-uit"
import "../styles-uit"
Item {
id: root
@ -11,54 +21,93 @@ Item {
property alias text: label.text
property var source
implicitHeight: source.visible ? label.implicitHeight * 1.5 : 0
implicitWidth: label.width + label.height * 2.5
implicitHeight: source.visible ? 2 * label.implicitHeight : 0
implicitWidth: 2 * hifi.dimensions.menuPadding.x + check.width + label.width + tail.width
visible: source.visible
width: parent.width
FontAwesome {
clip: true
CheckBox {
id: check
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors.verticalCenter: parent.verticalCenter
color: label.color
text: checkText()
size: label.height
visible: source.visible
font.pixelSize: size
function checkText() {
if (!source || source.type != 1 || !source.checkable) {
return ""
// FIXME: Should use radio buttons if source.exclusiveGroup.
anchors {
left: parent.left
leftMargin: hifi.dimensions.menuPadding.x
top: label.top
topMargin: 0
}
width: 20
visible: source.visible && source.type === 1 && source.checkable
checked: setChecked()
function setChecked() {
if (!source || source.type !== 1 || !source.checkable) {
return false;
}
// FIXME this works for native QML menus but I don't think it will
// for proxied QML menus
if (source.exclusiveGroup) {
return source.checked ? "\uF05D" : "\uF10C"
}
return source.checked ? "\uF046" : "\uF096"
return source.checked;
}
}
Text {
RalewaySemiBold {
id: label
size: hifi.fontSizes.rootMenu
font.capitalization: isSubMenu ? Font.MixedCase : Font.AllUppercase
anchors.left: check.right
anchors.leftMargin: 4
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50
enabled: source.visible && (source.type !== 0 ? source.enabled : false)
visible: source.visible
}
FontAwesome {
id: tag
x: root.parent.width - width
size: label.height
width: implicitWidth
visible: source.visible && (source.type == 2)
text: "\uF0DA"
anchors.verticalCenter: parent.verticalCenter
color: label.color
Item {
id: separator
anchors {
fill: parent
leftMargin: hifi.dimensions.menuPadding.x + check.width
rightMargin: hifi.dimensions.menuPadding.x + tail.width
}
visible: source.type === MenuItemType.Separator
Rectangle {
anchors {
left: parent.left
right: parent.right
verticalCenter: parent.verticalCenter
}
height: 1
color: hifi.colors.lightGray50
}
}
Item {
id: tail
width: 48 + (shortcut.visible ? shortcut.width : 0)
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: hifi.dimensions.menuPadding.x
}
RalewayLight {
id: shortcut
text: source.shortcut ? source.shortcut : ""
size: hifi.fontSizes.shortcutText
color: hifi.colors.baseGrayShadow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 15
visible: source.visible && text != ""
}
HiFiGlyphs {
text: hifi.glyphs.disclosureExpand
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25
size: 2 * hifi.fontSizes.rootMenuDisclosure
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
horizontalAlignment: Text.AlignRight
visible: source.visible && (source.type === 2)
}
}
}

View file

@ -1,44 +1,59 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
//
// VrMenuView.qml
//
// Created by Bradley Austin Davis on 18 Jan 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import "../styles"
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
FocusScope {
id: root
implicitHeight: border.height
implicitWidth: border.width
implicitHeight: background.height
implicitWidth: background.width
property alias currentItem: listView.currentItem
property alias model: listView.model
property bool isSubMenu: false
signal selected(var item)
HifiConstants { id: hifi }
Border {
id: border
Rectangle {
id: background
anchors.fill: listView
anchors.margins: -8
border.color: hifi.colors.hifiBlue
color: hifi.colors.window
// color: "#7f7f7f7f"
radius: hifi.dimensions.borderRadius
border.width: hifi.dimensions.borderWidth
border.color: hifi.colors.lightGrayText80
color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80
}
ListView {
id: listView
x: 8; y: 8
HifiConstants { id: hifi }
width: 128
height: count * 32
topMargin: hifi.dimensions.menuPadding.y
onEnabledChanged: recalcSize();
onVisibleChanged: recalcSize();
onCountChanged: recalcSize();
focus: true
highlight: Rectangle {
width: listView.currentItem ? listView.currentItem.width : 0
height: listView.currentItem ? listView.currentItem.height : 0
color: "lightsteelblue"; radius: 3
anchors {
left: parent ? parent.left : undefined
right: parent ? parent.right : undefined
leftMargin: hifi.dimensions.borderWidth
rightMargin: hifi.dimensions.borderWidth
}
color: hifi.colors.white
}
delegate: VrMenuItem {
@ -75,6 +90,7 @@ FocusScope {
newHeight += currentItem.implicitHeight
}
}
newHeight += 2 * hifi.dimensions.menuPadding.y; // White space at top and bottom.
if (maxWidth > width) {
width = maxWidth;
}

View file

@ -1,26 +0,0 @@
//
// FontAwesome.qml
//
// Created by Bradley Austin Davis on 24 Apr 2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Text {
id: root
FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; }
property int size: 32
width: size
height: size
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: iconFont.name
}

View file

@ -67,9 +67,13 @@ Item {
readonly property color darkGray30: "#4d121212"
readonly property color darkGray0: "#00121212"
readonly property color baseGrayShadow60: "#99252525"
readonly property color baseGrayShadow50: "#80252525"
readonly property color baseGrayShadow25: "#40252525"
readonly property color baseGrayHighlight40: "#66575757"
readonly property color baseGrayHighlight15: "#26575757"
readonly property color lightGray50: "#806a6a6a"
readonly property color lightGrayText80: "#ccafafaf"
readonly property color faintGray80: "#cce3e3e3"
readonly property color faintGray50: "#80e3e3e3"
// Other colors
@ -135,6 +139,7 @@ Item {
readonly property real modalDialogTitleHeight: 40
readonly property real controlLineHeight: 29 // Height of spinbox control on 1920 x 1080 monitor
readonly property real controlInterlineHeight: 22 // 75% of controlLineHeight
readonly property vector2d menuPadding: Qt.vector2d(14, 12)
}
Item {
@ -152,6 +157,7 @@ Item {
readonly property real logs: dimensions.largeScreen ? 16 : 12
readonly property real code: dimensions.largeScreen ? 16 : 12
readonly property real rootMenu: dimensions.largeScreen ? 15 : 11
readonly property real rootMenuDisclosure: dimensions.largeScreen ? 20 : 16
readonly property real menuItem: dimensions.largeScreen ? 15 : 11
readonly property real shortcutText: dimensions.largeScreen ? 13 : 9
readonly property real carat: dimensions.largeScreen ? 38 : 30

View file

@ -3179,7 +3179,6 @@ void Application::update(float deltaTime) {
auto myAvatar = getMyAvatar();
auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->update(deltaTime);
controller::InputCalibrationData calibrationData = {
myAvatar->getSensorToWorldMatrix(),
@ -3187,9 +3186,12 @@ void Application::update(float deltaTime) {
myAvatar->getHMDSensorMatrix()
};
InputPluginPointer keyboardMousePlugin;
bool jointsCaptured = false;
for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
if (inputPlugin->isActive()) {
if (inputPlugin->getName() == KeyboardMouseDevice::NAME) {
keyboardMousePlugin = inputPlugin;
} else if (inputPlugin->isActive()) {
inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
if (inputPlugin->isJointController()) {
jointsCaptured = true;
@ -3197,6 +3199,12 @@ void Application::update(float deltaTime) {
}
}
userInputMapper->update(deltaTime);
if (keyboardMousePlugin && keyboardMousePlugin->isActive()) {
keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured);
}
_controllerScriptingInterface->updateInputControllers();
// Transfer the user inputs to the driveKeys
@ -3216,16 +3224,14 @@ void Application::update(float deltaTime) {
myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
}
controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));
setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
myAvatar->setHandControllerPosesInWorldFrame(leftHandPose.transform(myAvatarMatrix), rightHandPose.transform(myAvatarMatrix));
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateDialogs(deltaTime); // update various stats dialogs if present
_avatarUpdate->synchronousProcess();
if (_physicsEnabled) {
PerformanceTimer perfTimer("physics");
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
@ -3297,6 +3303,8 @@ void Application::update(float deltaTime) {
}
}
_avatarUpdate->synchronousProcess();
{
PerformanceTimer perfTimer("overlays");
_overlays.update(deltaTime);
@ -3757,19 +3765,19 @@ namespace render {
switch (backgroundMode) {
case model::SunSkyStage::SKY_BOX: {
auto skybox = skyStage->getSkybox();
if (skybox && skybox->getCubemap() && skybox->getCubemap()->isDefined()) {
if (skybox) {
PerformanceTimer perfTimer("skybox");
skybox->render(batch, *(args->_viewFrustum));
break;
}
// If no skybox texture is available, render the SKY_DOME while it loads
}
// fall through to next case
// Fall through: if no skybox is available, render the SKY_DOME
case model::SunSkyStage::SKY_DOME: {
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
PerformanceTimer perfTimer("stars");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::payloadRender<BackgroundRenderData>() ... stars...");
"Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");
// should be the first rendering pass - w/o depth buffer / lighting
static const float alpha = 1.0f;
@ -3777,6 +3785,7 @@ namespace render {
}
}
break;
case model::SunSkyStage::NO_BACKGROUND:
default:
// this line intentionally left blank
@ -4982,49 +4991,6 @@ mat4 Application::getHMDSensorPose() const {
return mat4();
}
void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) {
// NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't
// reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more
// of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about
// controller::Pose. More work is needed to clean this up.
hand->modifyPalm(whichHand, [&](PalmData& palm) {
palm.setActive(pose.isValid());
// controller pose is in Avatar frame.
glm::vec3 position = pose.getTranslation();
glm::quat rotation = pose.getRotation();
glm::vec3 rawVelocity = pose.getVelocity();
glm::vec3 angularVelocity = pose.getAngularVelocity();
palm.setRawVelocity(rawVelocity);
palm.setRawAngularVelocity(angularVelocity);
if (controller::InputDevice::getLowVelocityFilter()) {
// Use a velocity sensitive filter to damp small motions and preserve large ones with
// no latency.
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter);
}
palm.setRawPosition(position);
palm.setRawRotation(rotation);
// Store the one fingertip in the palm structure so we can track velocity
const float FINGER_LENGTH = 0.3f; // meters
const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
glm::vec3 oldTipPosition = palm.getTipRawPosition();
if (deltaTime > 0.0f) {
palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
} else {
palm.setTipVelocity(glm::vec3(0.0f));
}
palm.setTipPosition(newTipPosition);
palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger
});
}
void Application::crashApplication() {
qCDebug(interfaceapp) << "Intentionally crashed Interface";
QObject* object = nullptr;

View file

@ -299,7 +299,7 @@ private slots:
void loadSettings();
void saveSettings();
bool acceptSnapshot(const QString& urlString);
bool askToSetAvatarUrl(const QString& url);
bool askToLoadScript(const QString& scriptFilenameOrURL);
@ -327,8 +327,6 @@ private:
void update(float deltaTime);
void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
// Various helper functions called during update()
void updateLOD();
void updateThreads(float deltaTime);

View file

@ -481,7 +481,8 @@ Menu::Menu() {
// Developer > Hands >>>
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false,
avatar, SLOT(setEnableDebugDrawHandControllers(bool)));
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
qApp, SLOT(setLowVelocityFilter(bool)));

View file

@ -22,11 +22,11 @@
#include <RenderArgs.h>
#include <ViewFrustum.h>
#include <stars_vert.h>
#include <stars_frag.h>
#include <render-utils/stars_vert.h>
#include <render-utils/stars_frag.h>
#include <standardTransformPNTC_vert.h>
#include <starsGrid_frag.h>
#include <render-utils/standardTransformPNTC_vert.h>
#include <render-utils/starsGrid_frag.h>
//static const float TILT = 0.23f;
static const float TILT = 0.0f;

View file

@ -35,7 +35,6 @@
#include "Avatar.h"
#include "AvatarManager.h"
#include "AvatarMotionState.h"
#include "Hand.h"
#include "Head.h"
#include "Menu.h"
#include "Physics.h"
@ -101,7 +100,6 @@ Avatar::Avatar(RigPointer rig) :
// give the pointer to our head to inherited _headData variable from AvatarData
_headData = static_cast<HeadData*>(new Head(this));
_handData = static_cast<HandData*>(new Hand(this));
}
Avatar::~Avatar() {
@ -190,11 +188,6 @@ void Avatar::simulate(float deltaTime) {
float boundingRadius = getBoundingRadius();
bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
{
PerformanceTimer perfTimer("hand");
getHand()->simulate(deltaTime, false);
}
if (_shouldAnimate && !_shouldSkipRender && inView) {
{
PerformanceTimer perfTimer("skeleton");
@ -578,11 +571,6 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, floa
if (_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable()) {
getHead()->render(renderArgs, 1.0f, renderFrustum);
}
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) {
getHand()->renderHandTargets(renderArgs, false);
}
}
getHead()->renderLookAts(renderArgs);
}

View file

@ -22,7 +22,6 @@
#include <render/Scene.h>
#include "Hand.h"
#include "Head.h"
#include "SkeletonModel.h"
#include "world.h"
@ -91,7 +90,7 @@ public:
float getUniformScale() const { return getScale().y; }
const Head* getHead() const { return static_cast<const Head*>(_headData); }
Head* getHead() { return static_cast<Head*>(_headData); }
Hand* getHand() { return static_cast<Hand*>(_handData); }
glm::quat getWorldAlignedOrientation() const;
AABox getBounds() const;

View file

@ -104,48 +104,57 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(float deltaTimeStep, glm::qu
withReadLock([&]{
bool isRightHand = (_hand == "right");
glm::vec3 palmPosition;
glm::quat palmRotation;
PalmData palmData = holdingAvatar->getHand()->getCopyOfPalmData(isRightHand ? HandData::RightHand : HandData::LeftHand);
if (palmData.isValid()) {
// TODO: adjust according to _relativePosition and _relativeRotation?
linearVelocity = palmData.getVelocity();
angularVelocity = palmData.getAngularVelocity();
}
if (_ignoreIK && holdingAvatar->isMyAvatar() && palmData.isValid()) {
// We cannot ignore other avatars IK and this is not the point of this option
// This is meant to make the grabbing behavior more reactive.
palmPosition = palmData.getPosition();
palmRotation = palmData.getRotation();
} else if (holdingAvatar->isMyAvatar()) {
glm::vec3 avatarRigidBodyPosition;
glm::quat avatarRigidBodyRotation;
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
// the offset and rotation between the avatar's rigid body and the palm were determined earlier
// in prepareForPhysicsSimulation. At this point, the avatar's rigid body has been moved by bullet
// and the data in the Avatar class is stale. This means that the result of get*PalmPosition will
// be stale. Instead, determine the current palm position with the current avatar's rigid body
// location and the saved offsets.
// this line is more correct but breaks for the current way avatar data is updated.
// palmPosition = avatarRigidBodyPosition + avatarRigidBodyRotation * _palmOffsetFromRigidBody;
// instead, use this for now:
palmPosition = avatarRigidBodyPosition + _palmOffsetFromRigidBody;
// the item jitters the least by getting the rotation based on the opinion of Avatar.h rather
// than that of the rigid body. leaving this next line here for future reference:
// palmRotation = avatarRigidBodyRotation * _palmRotationFromRigidBody;
if (holdingAvatar->isMyAvatar()) {
// fetch the hand controller pose
controller::Pose pose;
if (isRightHand) {
palmRotation = holdingAvatar->getRightPalmRotation();
pose = avatarManager->getMyAvatar()->getRightHandControllerPoseInWorldFrame();
} else {
palmRotation = holdingAvatar->getLeftPalmRotation();
pose = avatarManager->getMyAvatar()->getLeftHandControllerPoseInWorldFrame();
}
} else {
if (pose.isValid()) {
linearVelocity = pose.getVelocity();
angularVelocity = pose.getAngularVelocity();
}
if (_ignoreIK && pose.isValid()) {
// We cannot ignore other avatars IK and this is not the point of this option
// This is meant to make the grabbing behavior more reactive.
palmPosition = pose.getTranslation();
palmRotation = pose.getRotation();
} else {
glm::vec3 avatarRigidBodyPosition;
glm::quat avatarRigidBodyRotation;
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
// the offset and rotation between the avatar's rigid body and the palm were determined earlier
// in prepareForPhysicsSimulation. At this point, the avatar's rigid body has been moved by bullet
// and the data in the Avatar class is stale. This means that the result of get*PalmPosition will
// be stale. Instead, determine the current palm position with the current avatar's rigid body
// location and the saved offsets.
// this line is more correct but breaks for the current way avatar data is updated.
// palmPosition = avatarRigidBodyPosition + avatarRigidBodyRotation * _palmOffsetFromRigidBody;
// instead, use this for now:
palmPosition = avatarRigidBodyPosition + _palmOffsetFromRigidBody;
// the item jitters the least by getting the rotation based on the opinion of Avatar.h rather
// than that of the rigid body. leaving this next line here for future reference:
// palmRotation = avatarRigidBodyRotation * _palmRotationFromRigidBody;
if (isRightHand) {
palmRotation = holdingAvatar->getRightPalmRotation();
} else {
palmRotation = holdingAvatar->getLeftPalmRotation();
}
}
} else { // regular avatar
if (isRightHand) {
palmPosition = holdingAvatar->getRightPalmPosition();
palmRotation = holdingAvatar->getRightPalmRotation();

View file

@ -1,101 +0,0 @@
//
// Hand.cpp
// interface/src/avatar
//
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Hand.h"
#include <glm/glm.hpp>
#include <GeometryUtil.h>
#include <RenderArgs.h>
#include "Avatar.h"
#include "AvatarManager.h"
#include "MyAvatar.h"
#include "Util.h"
#include "world.h"
using namespace std;
Hand::Hand(Avatar* owningAvatar) :
HandData((AvatarData*)owningAvatar),
_owningAvatar(owningAvatar)
{
}
void Hand::simulate(float deltaTime, bool isMine) {
// nothing to do here
}
void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
float avatarScale = 1.0f;
if (_owningAvatar) {
avatarScale = _owningAvatar->getUniformScale();
}
const float alpha = 1.0f;
const glm::vec3 redColor(1.0f, 0.0f, 0.0f); // Color the hand targets red to be different than skin
const glm::vec3 greenColor(0.0f, 1.0f, 0.0f); // Color the hand targets red to be different than skin
const glm::vec3 blueColor(0.0f, 0.0f, 1.0f); // Color the hand targets red to be different than skin
const glm::vec3 grayColor(0.5f);
const float SPHERE_RADIUS = 0.03f * avatarScale;
auto palms = getCopyOfPalms();
gpu::Batch& batch = *renderArgs->_batch;
if (isMine) {
for (const auto& palm : palms) {
if (!palm.isActive()) {
continue;
}
// draw a gray sphere at the target position of the "Hand" joint
glm::vec3 position = palm.getPosition();
Transform transform = Transform();
transform.setTranslation(position);
transform.setRotation(palm.getRotation());
transform.postScale(SPHERE_RADIUS);
batch.setModelTransform(transform);
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, grayColor);
// draw a green sphere at the old "finger tip"
transform = Transform();
position = palm.getTipPosition();
transform.setTranslation(position);
transform.setRotation(palm.getRotation());
transform.postScale(SPHERE_RADIUS);
batch.setModelTransform(transform);
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, greenColor);
}
}
const float AXIS_RADIUS = 0.1f * SPHERE_RADIUS;
const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;
// Draw the coordinate frames of the hand targets
for (const auto& palm : palms) {
if (palm.isActive()) {
glm::vec3 root = palm.getPosition();
const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::quat palmRotation = palm.getRotation();
Transform transform = Transform();
transform.setTranslation(glm::vec3());
batch.setModelTransform(transform);
glm::vec3 tip = root + palmRotation * glm::vec3(AXIS_LENGTH, 0.0f, 0.0f);
Avatar::renderJointConnectingCone(batch, root, tip, AXIS_RADIUS, AXIS_RADIUS, glm::vec4(redColor.r, redColor.g, redColor.b, alpha));
tip = root + palmRotation * glm::vec3(0.0f, AXIS_LENGTH, 0.0f);
Avatar::renderJointConnectingCone(batch, root, tip, AXIS_RADIUS, AXIS_RADIUS, glm::vec4(greenColor.r, greenColor.g, greenColor.b, alpha));
tip = root + palmRotation * glm::vec3(0.0f, 0.0f, AXIS_LENGTH);
Avatar::renderJointConnectingCone(batch, root, tip, AXIS_RADIUS, AXIS_RADIUS, glm::vec4(blueColor.r, blueColor.g, blueColor.b, alpha));
}
}
}

View file

@ -1,36 +0,0 @@
//
// Hand.h
// interface/src/avatar
//
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Hand_h
#define hifi_Hand_h
#include <HandData.h>
class Avatar;
class RenderArgs;
class Hand : public HandData {
public:
Hand(Avatar* owningAvatar);
void simulate(float deltaTime, bool isMine);
void renderHandTargets(RenderArgs* renderArgs, bool isMine);
private:
// disallow copies of the Hand, copy of owning Avatar is disallowed too
Hand(const Hand&);
Hand& operator= (const Hand&);
int _controllerButtons; /// Button states read from hand-held controllers
Avatar* _owningAvatar;
};
#endif // hifi_Hand_h

View file

@ -341,12 +341,6 @@ void MyAvatar::simulate(float deltaTime) {
updatePosition(deltaTime);
}
{
PerformanceTimer perfTimer("hand");
// update avatar skeleton and simulate hand and head
getHand()->simulate(deltaTime, true);
}
{
PerformanceTimer perfTimer("skeleton");
_skeletonModel.simulate(deltaTime);
@ -522,49 +516,50 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
-MAX_LEAN, MAX_LEAN));
}
glm::vec3 MyAvatar::getLeftHandPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
auto pose = getLeftHandControllerPoseInAvatarFrame();
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getRightHandPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
auto pose = getRightHandControllerPoseInAvatarFrame();
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getLeftHandTipPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
const float TIP_LENGTH = 0.3f;
auto pose = getLeftHandControllerPoseInAvatarFrame();
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getRightHandTipPosition() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
const float TIP_LENGTH = 0.3f;
auto pose = getRightHandControllerPoseInAvatarFrame();
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
}
controller::Pose MyAvatar::getLeftHandPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
return getLeftHandControllerPoseInAvatarFrame();
}
controller::Pose MyAvatar::getRightHandPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
return getRightHandControllerPoseInAvatarFrame();
}
controller::Pose MyAvatar::getLeftHandTipPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
auto pose = getLeftHandControllerPoseInAvatarFrame();
glm::vec3 tipTrans = getLeftHandTipPosition();
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
pose.translation = tipTrans;
return pose;
}
controller::Pose MyAvatar::getRightHandTipPose() const {
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
auto pose = getRightHandControllerPoseInAvatarFrame();
glm::vec3 tipTrans = getRightHandTipPosition();
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
pose.translation = tipTrans;
return pose;
}
// virtual
@ -702,6 +697,15 @@ void MyAvatar::setEnableDebugDrawPosition(bool isEnabled) {
}
}
void MyAvatar::setEnableDebugDrawHandControllers(bool isEnabled) {
_enableDebugDrawHandControllers = isEnabled;
if (!isEnabled) {
DebugDraw::getInstance().removeMarker("leftHandController");
DebugDraw::getInstance().removeMarker("rightHandController");
}
}
void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) {
_enableDebugDrawSensorToWorldMatrix = isEnabled;
@ -1092,6 +1096,48 @@ void MyAvatar::rebuildCollisionShape() {
_characterController.setLocalBoundingBox(corner, diagonal);
}
static controller::Pose applyLowVelocityFilter(const controller::Pose& oldPose, const controller::Pose& newPose) {
controller::Pose finalPose = newPose;
if (newPose.isValid()) {
// Use a velocity sensitive filter to damp small motions and preserve large ones with
// no latency.
float velocityFilter = glm::clamp(1.0f - glm::length(oldPose.getVelocity()), 0.0f, 1.0f);
finalPose.translation = oldPose.getTranslation() * velocityFilter + newPose.getTranslation() * (1.0f - velocityFilter);
finalPose.rotation = safeMix(oldPose.getRotation(), newPose.getRotation(), 1.0f - velocityFilter);
}
return finalPose;
}
void MyAvatar::setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right) {
if (controller::InputDevice::getLowVelocityFilter()) {
auto oldLeftPose = getLeftHandControllerPoseInWorldFrame();
auto oldRightPose = getRightHandControllerPoseInWorldFrame();
_leftHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
_rightHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
} else {
_leftHandControllerPoseInWorldFrameCache.set(left);
_rightHandControllerPoseInWorldFrameCache.set(right);
}
}
controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const {
return _leftHandControllerPoseInWorldFrameCache.get();
}
controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const {
return _rightHandControllerPoseInWorldFrameCache.get();
}
controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getLeftHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::prepareForPhysicsSimulation() {
relayDriveKeysToCharacterController();
@ -1227,11 +1273,6 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
} else {
getHead()->renderLookAts(renderArgs);
}
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) {
getHand()->renderHandTargets(renderArgs, true);
}
}
void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
@ -1340,6 +1381,23 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
}
}
if (_enableDebugDrawHandControllers) {
auto leftHandPose = getLeftHandControllerPoseInWorldFrame();
auto rightHandPose = getRightHandControllerPoseInWorldFrame();
if (leftHandPose.isValid()) {
DebugDraw::getInstance().addMarker("leftHandController", leftHandPose.getRotation(), leftHandPose.getTranslation(), glm::vec4(1));
} else {
DebugDraw::getInstance().removeMarker("leftHandController");
}
if (rightHandPose.isValid()) {
DebugDraw::getInstance().addMarker("rightHandController", rightHandPose.getRotation(), rightHandPose.getTranslation(), glm::vec4(1));
} else {
DebugDraw::getInstance().removeMarker("rightHandController");
}
}
DebugDraw::getInstance().updateMyAvatarPos(getPosition());
DebugDraw::getInstance().updateMyAvatarRot(getOrientation());

View file

@ -249,6 +249,12 @@ public:
virtual void rebuildCollisionShape() override;
void setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right);
controller::Pose getLeftHandControllerPoseInWorldFrame() const;
controller::Pose getRightHandControllerPoseInWorldFrame() const;
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
controller::Pose getRightHandControllerPoseInAvatarFrame() const;
public slots:
void increaseSize();
void decreaseSize();
@ -271,6 +277,7 @@ public slots:
void setEnableDebugDrawDefaultPose(bool isEnabled);
void setEnableDebugDrawAnimPose(bool isEnabled);
void setEnableDebugDrawPosition(bool isEnabled);
void setEnableDebugDrawHandControllers(bool isEnabled);
void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled);
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
void setEnableMeshVisible(bool isEnabled);
@ -435,6 +442,7 @@ private:
bool _enableDebugDrawDefaultPose { false };
bool _enableDebugDrawAnimPose { false };
bool _enableDebugDrawHandControllers { false };
bool _enableDebugDrawSensorToWorldMatrix { false };
AudioListenerMode _audioListenerMode;
@ -446,6 +454,10 @@ private:
bool _hoverReferenceCameraFacingIsCaptured { false };
glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space
// These are stored in WORLD frame
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInWorldFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInWorldFrameCache { controller::Pose() };
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
float AUDIO_ENERGY_CONSTANT { 0.000001f };
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };

View file

@ -16,7 +16,6 @@
#include "Application.h"
#include "Avatar.h"
#include "Hand.h"
#include "Menu.h"
#include "SkeletonModel.h"
#include "Util.h"
@ -127,20 +126,20 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
Rig::HandParameters handParams;
auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand);
if (leftPalm.isValid() && leftPalm.isActive()) {
auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
if (leftPose.isValid()) {
handParams.isLeftEnabled = true;
handParams.leftPosition = Quaternions::Y_180 * leftPalm.getRawPosition();
handParams.leftOrientation = Quaternions::Y_180 * leftPalm.getRawRotation();
handParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation();
handParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation();
} else {
handParams.isLeftEnabled = false;
}
auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand);
if (rightPalm.isValid() && rightPalm.isActive()) {
auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame();
if (rightPose.isValid()) {
handParams.isRightEnabled = true;
handParams.rightPosition = Quaternions::Y_180 * rightPalm.getRawPosition();
handParams.rightOrientation = Quaternions::Y_180 * rightPalm.getRawRotation();
handParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation();
handParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation();
} else {
handParams.isRightEnabled = false;
}
@ -247,17 +246,6 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
return firstIndex.value < secondIndex.value;
}
void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int parentJointIndex = geometry.joints.at(jointIndex).parentIndex;
if (parentJointIndex == -1) {
return;
}
}
bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const {
int knuckleIndex = _rig->indexOfJoint("LeftHandMiddle1");
int handIndex = _rig->indexOfJoint("LeftHand");

View file

@ -111,7 +111,6 @@ protected:
void computeBoundingShape();
void applyPalmData(int jointIndex, const PalmData& palm);
private:
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;

View file

@ -13,7 +13,6 @@
#include <avatar/AvatarManager.h>
#include <avatar/MyAvatar.h>
#include <HandData.h>
#include <HFBackEvent.h>
#include <plugins/PluginManager.h>

View file

@ -92,3 +92,8 @@ glm::quat HMDScriptingInterface::getOrientation() const {
}
return glm::quat();
}
bool HMDScriptingInterface::isMounted() const{
auto displayPlugin = qApp->getActiveDisplayPlugin();
return (displayPlugin->isHmd() && displayPlugin->isDisplayVisible());
}

View file

@ -25,6 +25,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition)
Q_PROPERTY(glm::quat orientation READ getOrientation)
Q_PROPERTY(bool mounted READ isMounted)
public:
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
@ -39,6 +40,7 @@ public:
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine);
bool isMounted() const;
private:
// Get the position of the HMD

View file

@ -164,7 +164,7 @@ void Stats::updateStats(bool force) {
MyAvatar* myAvatar = avatarManager->getMyAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getVelocity()), 0.1f);
STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getVelocity()), 0.01f);
STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f);
if (_expanded || force) {
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);

View file

@ -51,27 +51,24 @@ void QmlOverlay::buildQmlElement(const QUrl& url) {
}
QmlOverlay::~QmlOverlay() {
if (_qmlElement) {
_qmlElement->deleteLater();
_qmlElement = nullptr;
}
_qmlElement.reset();
}
void QmlOverlay::setProperties(const QVariantMap& properties) {
Overlay2D::setProperties(properties);
auto bounds = _bounds;
std::weak_ptr<QQuickItem> weakQmlElement;
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
std::weak_ptr<QQuickItem> weakQmlElement = _qmlElement;
DependencyManager::get<OffscreenUi>()->executeOnUiThread([weakQmlElement, bounds, properties] {
// check to see if qmlElement still exists
auto qmlElement = weakQmlElement.lock();
if (qmlElement) {
_qmlElement->setX(bounds.left());
_qmlElement->setY(bounds.top());
_qmlElement->setWidth(bounds.width());
_qmlElement->setHeight(bounds.height());
qmlElement->setX(bounds.left());
qmlElement->setY(bounds.top());
qmlElement->setWidth(bounds.width());
qmlElement->setHeight(bounds.height());
QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
}
});
QMetaObject::invokeMethod(_qmlElement.get(), "updatePropertiesFromScript", Q_ARG(QVariant, properties));
}
void QmlOverlay::render(RenderArgs* args) {

View file

@ -57,7 +57,6 @@ AvatarData::AvatarData() :
_hasNewJointRotations(true),
_hasNewJointTranslations(true),
_headData(NULL),
_handData(NULL),
_faceModelURL("http://invalid.com"),
_displayNameTargetAlpha(1.0f),
_displayNameAlpha(1.0f),
@ -74,7 +73,6 @@ AvatarData::AvatarData() :
AvatarData::~AvatarData() {
delete _headData;
delete _handData;
}
// We cannot have a file-level variable (const or otherwise) in the header if it uses PathUtils, because that references Application, which will not yet initialized.
@ -418,11 +416,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
_headData = new HeadData(this);
}
// lazily allocate memory for HandData in case we're not an Avatar instance
if (!_handData) {
_handData = new HandData(this);
}
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(buffer.data());
const unsigned char* sourceBuffer = startPosition;
quint64 now = usecTimestampNow();

View file

@ -52,9 +52,9 @@ typedef unsigned long long quint64;
#include <RegisteredMetaTypes.h>
#include <SimpleMovingAverage.h>
#include <SpatiallyNestable.h>
#include <NumericalConstants.h>
#include "AABox.h"
#include "HandData.h"
#include "HeadData.h"
#include "PathUtils.h"
@ -290,7 +290,6 @@ public:
KeyState keyState() const { return _keyState; }
const HeadData* getHeadData() const { return _headData; }
const HandData* getHandData() const { return _handData; }
bool hasIdentityChangedAfterParsing(const QByteArray& data);
QByteArray identityByteArray();
@ -383,7 +382,6 @@ protected:
bool _hasNewJointTranslations; // set in AvatarData, cleared in Avatar
HeadData* _headData;
HandData* _handData;
QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit

View file

@ -1,110 +0,0 @@
//
// HandData.cpp
// libraries/avatars/src
//
// Created by Stephen Birarda on 5/20/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QDataStream>
#include <GeometryUtil.h>
#include <SharedUtil.h>
#include "AvatarData.h"
#include "HandData.h"
HandData::HandData(AvatarData* owningAvatar) :
_owningAvatarData(owningAvatar)
{
addNewPalm(LeftHand);
addNewPalm(RightHand);
}
glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const {
return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale();
}
PalmData& HandData::addNewPalm(Hand whichHand) {
QWriteLocker locker(&_palmsLock);
_palms.push_back(PalmData(this, whichHand));
return _palms.back();
}
PalmData HandData::getCopyOfPalmData(Hand hand) const {
QReadLocker locker(&_palmsLock);
// the palms are not necessarily added in left-right order,
// so we have to search for the correct hand
for (const auto& palm : _palms) {
if (palm.whichHand() == hand && palm.isActive()) {
return palm;
}
}
return PalmData(); // invalid hand
}
PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) :
_rawRotation(0.0f, 0.0f, 0.0f, 1.0f),
_rawPosition(0.0f),
_rawVelocity(0.0f),
_rawAngularVelocity(0.0f),
_totalPenetration(0.0f),
_isActive(false),
_numFramesWithoutData(0),
_owningHandData(owningHandData),
_hand(hand) {
}
void PalmData::addToPosition(const glm::vec3& delta) {
_rawPosition += _owningHandData->worldToLocalVector(delta);
}
bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
const PalmData*& collidingPalm) const {
QReadLocker locker(&_palmsLock);
for (const auto& palm : _palms) {
if (!palm.isActive()) {
continue;
}
glm::vec3 palmPosition = palm.getPosition();
const float PALM_RADIUS = 0.05f; // in world (not voxel) coordinates
if (findSphereSpherePenetration(penetratorCenter, penetratorRadius, palmPosition, PALM_RADIUS, penetration)) {
collidingPalm = &palm;
return true;
}
}
return false;
}
glm::quat HandData::getBaseOrientation() const {
return _owningAvatarData->getOrientation();
}
glm::vec3 HandData::getBasePosition() const {
return _owningAvatarData->getPosition();
}
float HandData::getBaseScale() const {
return _owningAvatarData->getTargetScale();
}
glm::vec3 PalmData::getFingerDirection() const {
// finger points along yAxis in hand-frame
const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 1.0f, 0.0f);
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION));
}
glm::vec3 PalmData::getNormal() const {
// palm normal points along zAxis in hand-frame
const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, 0.0f, 1.0f);
return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION));
}

View file

@ -1,174 +0,0 @@
//
// HandData.h
// libraries/avatars/src
//
// Created by Eric Johnston on 6/26/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_HandData_h
#define hifi_HandData_h
#include <functional>
#include <iostream>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QReadWriteLock>
#include <NumericalConstants.h>
#include <SharedUtil.h>
class AvatarData;
class PalmData;
class HandData {
public:
enum Hand {
LeftHand,
RightHand,
UnknownHand,
NUMBER_OF_HANDS
};
HandData(AvatarData* owningAvatar);
virtual ~HandData() {}
// position conversion
glm::vec3 localToWorldPosition(const glm::vec3& localPosition) {
return getBasePosition() + getBaseOrientation() * localPosition * getBaseScale();
}
glm::vec3 localToWorldDirection(const glm::vec3& localVector) {
return getBaseOrientation() * localVector * getBaseScale();
}
glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const;
PalmData getCopyOfPalmData(Hand hand) const;
std::vector<PalmData> getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; }
/// Checks for penetration between the described sphere and the hand.
/// \param penetratorCenter the center of the penetration test sphere
/// \param penetratorRadius the radius of the penetration test sphere
/// \param penetration[out] the vector in which to store the penetration
/// \param collidingPalm[out] a const PalmData* to the palm that was collided with
/// \return whether or not the sphere penetrated
bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
const PalmData*& collidingPalm) const;
glm::quat getBaseOrientation() const;
/// Allows a lamda function write access to the specific palm for this Hand, this might
/// modify the _palms vector
template<typename PalmModifierFunction> void modifyPalm(Hand whichHand, PalmModifierFunction callback);
friend class AvatarData;
protected:
AvatarData* _owningAvatarData;
std::vector<PalmData> _palms;
mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive };
glm::vec3 getBasePosition() const;
float getBaseScale() const;
PalmData& addNewPalm(Hand whichHand);
PalmData& getPalmData(Hand hand);
private:
// privatize copy ctor and assignment operator so copies of this object cannot be made
HandData(const HandData&);
HandData& operator= (const HandData&);
};
class PalmData {
public:
PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand);
glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); }
glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); }
glm::vec3 getAngularVelocity() const { return _owningHandData->localToWorldDirection(_rawAngularVelocity); }
const glm::vec3& getRawPosition() const { return _rawPosition; }
bool isActive() const { return _isActive; }
bool isValid() const { return _owningHandData; }
void setActive(bool active) { _isActive = active; }
HandData::Hand whichHand() const { return _hand; }
void setHand(HandData::Hand hand) { _hand = hand; }
void setRawRotation(const glm::quat& rawRotation) { _rawRotation = rawRotation; };
glm::quat getRawRotation() const { return _rawRotation; }
glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; }
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
void addToPosition(const glm::vec3& delta);
void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.0f); }
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
const glm::vec3 getTipPosition() const { return _owningHandData->localToWorldPosition(_tipPosition); }
const glm::vec3& getTipRawPosition() const { return _tipPosition; }
void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; }
const glm::vec3 getTipVelocity() const { return _owningHandData->localToWorldDirection(_tipVelocity); }
const glm::vec3& getTipRawVelocity() const { return _tipVelocity; }
void incrementFramesWithoutData() { _numFramesWithoutData++; }
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
int getFramesWithoutData() const { return _numFramesWithoutData; }
// FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information
// from an action and/or the UserInputMapper instead of piping it through here.
void setTrigger(float trigger) { _trigger = trigger; }
float getTrigger() const { return _trigger; }
// return world-frame:
glm::vec3 getFingerDirection() const;
glm::vec3 getNormal() const;
private:
// unless marked otherwise, these are all in the model-frame
glm::quat _rawRotation;
glm::vec3 _rawPosition;
glm::vec3 _rawVelocity;
glm::vec3 _rawAngularVelocity;
glm::quat _rawDeltaRotation;
glm::quat _lastRotation;
glm::vec3 _tipPosition;
glm::vec3 _tipVelocity;
glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations
float _trigger;
bool _isActive; /// This has current valid data
int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost.
HandData* _owningHandData;
HandData::Hand _hand;
};
template<typename PalmModifierFunction> void HandData::modifyPalm(Hand whichHand, PalmModifierFunction callback) {
QReadLocker locker(&_palmsLock);
for (auto& palm : _palms) {
if (palm.whichHand() == whichHand && palm.isValid()) {
callback(palm);
return;
}
}
}
#endif // hifi_HandData_h

View file

@ -11,6 +11,7 @@
#include <memory>
#include <math.h>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
@ -414,13 +415,39 @@ void CompositorHelper::updateTooltips() {
}
static const float FADE_DURATION = 500.0f;
static const float FADE_IN_ALPHA = 1.0f;
static const float FADE_OUT_ALPHA = 0.0f;
void CompositorHelper::startFadeFailsafe(float endValue) {
_fadeStarted = usecTimestampNow();
_fadeFailsafeEndValue = endValue;
const int SLIGHT_DELAY = 10;
QTimer::singleShot(FADE_DURATION + SLIGHT_DELAY, [this]{
checkFadeFailsafe();
});
}
void CompositorHelper::checkFadeFailsafe() {
auto elapsedInFade = usecTimestampNow() - _fadeStarted;
if (elapsedInFade > FADE_DURATION) {
setAlpha(_fadeFailsafeEndValue);
}
}
void CompositorHelper::fadeIn() {
_fadeInAlpha = true;
_alphaPropertyAnimation->setDuration(FADE_DURATION);
_alphaPropertyAnimation->setStartValue(_alpha);
_alphaPropertyAnimation->setEndValue(1.0f);
_alphaPropertyAnimation->setEndValue(FADE_IN_ALPHA);
_alphaPropertyAnimation->start();
// Sometimes, this "QPropertyAnimation" fails to complete the animation, and we end up with a partially faded
// state. So we will also have this fail-safe, where we record the timestamp of the fadeRequest, and the target
// value of the fade, and if after that time we still haven't faded all the way, we will kick it to the final
// fade value
startFadeFailsafe(FADE_IN_ALPHA);
}
void CompositorHelper::fadeOut() {
@ -428,8 +455,9 @@ void CompositorHelper::fadeOut() {
_alphaPropertyAnimation->setDuration(FADE_DURATION);
_alphaPropertyAnimation->setStartValue(_alpha);
_alphaPropertyAnimation->setEndValue(0.0f);
_alphaPropertyAnimation->setEndValue(FADE_OUT_ALPHA);
_alphaPropertyAnimation->start();
startFadeFailsafe(FADE_OUT_ALPHA);
}
void CompositorHelper::toggle() {

View file

@ -145,6 +145,11 @@ private:
float _fadeInAlpha { true };
float _oculusUIRadius { 1.0f };
quint64 _fadeStarted { 0 };
float _fadeFailsafeEndValue { 1.0f };
void checkFadeFailsafe();
void startFadeFailsafe(float endValue);
int _reticleQuad;
int _previousBorderWidth { -1 };

View file

@ -296,6 +296,9 @@ void OpenGLDisplayPlugin::customizeContext() {
if (uniform.Name() == "mvp") {
_mvpUniform = uniform.Index();
}
if (uniform.Name() == "alpha") {
_alphaUniform = uniform.Index();
}
uniforms.Next();
}
@ -406,33 +409,53 @@ void OpenGLDisplayPlugin::updateFramerate() {
void OpenGLDisplayPlugin::compositeOverlay() {
using namespace oglplus;
// Overlay draw
if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
auto compositorHelper = DependencyManager::get<CompositorHelper>();
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
}
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositePointer() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
if (isStereo()) {
for_each_eye([&](Eye eye) {
eyeViewport(eye);
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
if (isStereo()) {
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
});
} else {
drawUnitQuad();
});
} else {
drawUnitQuad();
}
}
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void OpenGLDisplayPlugin::compositeLayers() {

View file

@ -29,24 +29,26 @@ protected:
using TextureEscrow = GLEscrow<gpu::TexturePointer>;
public:
OpenGLDisplayPlugin();
virtual void activate() override;
virtual void deactivate() override;
virtual void stop() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
void activate() override;
void deactivate() override;
void stop() override;
bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; }
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
virtual float presentRate() override;
virtual glm::uvec2 getRecommendedRenderSize() const override {
void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
float presentRate() override;
glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels();
}
virtual glm::uvec2 getRecommendedUiSize() const override {
glm::uvec2 getRecommendedUiSize() const override {
return getSurfaceSize();
}
virtual QImage getScreenshot() const override;
QImage getScreenshot() const override;
protected:
#if THREADED_PRESENT
@ -86,6 +88,7 @@ protected:
ProgramPtr _program;
int32_t _mvpUniform { -1 };
int32_t _alphaUniform { -1 };
ShapeWrapperPtr _plane;
mutable Mutex _mutex;

View file

@ -62,30 +62,50 @@ void HmdDisplayPlugin::uncustomizeContext() {
void HmdDisplayPlugin::compositeOverlay() {
using namespace oglplus;
_sphereSection->Use();
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto modelView = glm::inverse(_currentRenderEyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1));
auto mvp = _eyeProjections[eye] * modelView;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_sphereSection->Draw();
});
auto compositorHelper = DependencyManager::get<CompositorHelper>();
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
_sphereSection->Use();
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto modelView = glm::inverse(_currentRenderEyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1));
auto mvp = _eyeProjections[eye] * modelView;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_sphereSection->Draw();
});
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void HmdDisplayPlugin::compositePointer() {
//Mouse Pointer
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
_plane->Use();
// Reconstruct the headpose from the eye poses
auto headPosition = (vec3(_currentRenderEyePoses[Left][3]) + vec3(_currentRenderEyePoses[Right][3])) / 2.0f;
for_each_eye([&](Eye eye) {
using namespace oglplus;
eyeViewport(eye);
auto reticleTransform = compositorHelper->getReticleTransform(_currentRenderEyePoses[eye], headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
});
// check the alpha
auto overlayAlpha = compositorHelper->getAlpha();
if (overlayAlpha > 0.0f) {
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(overlayAlpha);
// Mouse pointer
_plane->Use();
// Reconstruct the headpose from the eye poses
auto headPosition = (vec3(_currentRenderEyePoses[Left][3]) + vec3(_currentRenderEyePoses[Right][3])) / 2.0f;
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto reticleTransform = compositorHelper->getReticleTransform(_currentRenderEyePoses[eye], headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
});
}
Uniform<float>(*_program, _alphaUniform).Set(1.0);
}
void HmdDisplayPlugin::internalPresent() {

View file

@ -22,12 +22,16 @@ public:
glm::uvec2 getRecommendedUiSize() const override final;
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
bool isDisplayVisible() const override { return isHmdMounted(); }
void activate() override;
void deactivate() override;
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
void compositeOverlay() override;
void compositePointer() override;
void internalPresent() override;

View file

@ -140,8 +140,8 @@ void EntityTreeRenderer::update() {
// If we haven't already updated and previously attempted to load a texture,
// check if the texture loaded and apply it
if (!updated && (
(_pendingSkyboxTexture && _skyboxTexture && _skyboxTexture->isLoaded()) ||
(_pendingAmbientTexture && _ambientTexture && _ambientTexture->isLoaded()))) {
(_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())) ||
(_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())))) {
applyZonePropertiesToScene(_bestZone);
}
@ -158,6 +158,8 @@ void EntityTreeRenderer::update() {
}
bool EntityTreeRenderer::checkEnterLeaveEntities() {
bool didUpdate = false;
if (_tree && !_shuttingDown) {
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
@ -172,6 +174,7 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
std::static_pointer_cast<EntityTree>(_tree)->findEntities(avatarPosition, radius, foundEntities);
// Whenever you're in an intersection between zones, we will always choose the smallest zone.
auto oldBestZone = _bestZone;
_bestZone = nullptr; // NOTE: Is this what we want?
_bestZoneVolume = std::numeric_limits<float>::max();
@ -204,7 +207,10 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
}
}
applyZonePropertiesToScene(_bestZone);
if (_bestZone != oldBestZone) {
applyZonePropertiesToScene(_bestZone);
didUpdate = true;
}
});
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
@ -228,11 +234,9 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
}
_currentEntitiesInside = entitiesContainingAvatar;
_lastAvatarPosition = avatarPosition;
return true;
}
}
return false;
return didUpdate;
}
void EntityTreeRenderer::leaveAllEntities() {
@ -322,15 +326,19 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
_ambientTexture.clear();
} else {
_ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), CUBE_TEXTURE);
if (_ambientTexture && _ambientTexture->isLoaded() && _ambientTexture->getGPUTexture()) {
_pendingAmbientTexture = true;
if (_ambientTexture && _ambientTexture->isLoaded()) {
_pendingAmbientTexture = false;
if (_ambientTexture->getGPUTexture()->getIrradiance()) {
sceneKeyLight->setAmbientSphere(_ambientTexture->getGPUTexture()->getIrradiance());
sceneKeyLight->setAmbientMap(_ambientTexture->getGPUTexture());
auto texture = _ambientTexture->getGPUTexture();
if (texture) {
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
isAmbientTextureSet = true;
} else {
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << zone->getKeyLightProperties().getAmbientURL();
}
} else {
_pendingAmbientTexture = true;
}
}
@ -344,24 +352,27 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
skybox->parse(userData);
}
if (zone->getSkyboxProperties().getURL().isEmpty()) {
skybox->setCubemap(gpu::TexturePointer());
skybox->setCubemap(nullptr);
_pendingSkyboxTexture = false;
_skyboxTexture.clear();
} else {
// Update the Texture of the Skybox with the one pointed by this zone
_skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE);
_pendingSkyboxTexture = true;
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
_pendingSkyboxTexture = false;
if (_skyboxTexture && _skyboxTexture->isLoaded() && _skyboxTexture->getGPUTexture()) {
auto texture = _skyboxTexture->getGPUTexture();
skybox->setCubemap(texture);
_pendingSkyboxTexture = false;
if (!isAmbientTextureSet && texture->getIrradiance()) {
if (!isAmbientTextureSet) {
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
isAmbientTextureSet = true;
}
} else {
_pendingSkyboxTexture = true;
skybox->setCubemap(nullptr);
qCDebug(entitiesrenderer) << "Failed to load skybox:" << zone->getSkyboxProperties().getURL();
}
}

View file

@ -19,8 +19,8 @@
#include <ObjectMotionState.h>
#include <PerfStat.h>
#include "../render-utils/simple_vert.h"
#include "../render-utils/simple_frag.h"
#include <render-utils/simple_vert.h>
#include <render-utils/simple_frag.h>
EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer entity{ new RenderableBoxEntityItem(entityID) };

View file

@ -19,8 +19,8 @@
#include <GeometryCache.h>
#include <PerfStat.h>
#include "../render-utils/simple_vert.h"
#include "../render-utils/simple_frag.h"
#include <render-utils/simple_vert.h>
#include <render-utils/simple_frag.h>
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.

View file

@ -86,6 +86,11 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
if (_simulation) {
_simulation->addEntity(entity);
}
if (!entity->isParentIDValid()) {
_missingParent.append(entity);
}
_isDirty = true;
maybeNotifyNewCollisionSoundURL("", entity->getCollisionSoundURL());
emit addingEntity(entity->getEntityItemID());
@ -252,6 +257,9 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
_missingParent.append(childEntity);
continue;
}
if (!childEntity->isParentIDValid()) {
_missingParent.append(childEntity);
}
UpdateEntityOperator theChildOperator(getThisPointer(), containingElement, childEntity, queryCube);
recurseTreeWithOperator(&theChildOperator);
@ -448,6 +456,17 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
const RemovedEntities& entities = theOperator.getEntities();
foreach(const EntityToDeleteDetails& details, entities) {
EntityItemPointer theEntity = details.entity;
if (getIsServer()) {
QSet<EntityItemID> childrenIDs;
theEntity->forEachChild([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
childrenIDs += child->getID();
}
});
deleteEntities(childrenIDs, true, true);
}
theEntity->die();
if (getIsServer()) {
@ -992,21 +1011,37 @@ void EntityTree::fixupMissingParents() {
EntityItemWeakPointer entityWP = iter.next();
EntityItemPointer entity = entityWP.lock();
if (entity) {
bool maxAACubeSuccess;
AACube maxAACube = entity->getMaximumAACube(maxAACubeSuccess);
bool queryAACubeSuccess;
AACube newCube = entity->getQueryAACube(queryAACubeSuccess);
if (!maxAACubeSuccess || !queryAACubeSuccess) {
continue;
if (queryAACubeSuccess) {
// make sure queryAACube encompasses maxAACube
bool maxAACubeSuccess;
AACube maxAACube = entity->getMaximumAACube(maxAACubeSuccess);
if (maxAACubeSuccess && !newCube.contains(maxAACube)) {
newCube = maxAACube;
}
}
if (!newCube.contains(maxAACube)) {
newCube = maxAACube;
bool doMove = false;
if (entity->isParentIDValid()) {
// this entity's parent was previously not known, and now is. Update its location in the EntityTree...
iter.remove();
doMove = true;
} else if (getIsServer() && _avatarIDs.contains(entity->getParentID())) {
// this is a child of an avatar, which the entity server will never have
// a SpatiallyNestable object for. Add it to a list for cleanup when the avatar leaves.
if (!_childrenOfAvatars.contains(entity->getParentID())) {
_childrenOfAvatars[entity->getParentID()] = QSet<EntityItemID>();
}
_childrenOfAvatars[entity->getParentID()] += entity->getEntityItemID();
doMove = true;
}
if (queryAACubeSuccess && doMove) {
moveOperator.addEntityToMoveList(entity, newCube);
iter.remove();
entity->markAncestorMissing(false);
}
// this entity's parent (or ancestry) was previously not fully known, and now is. Update its
// location in the EntityTree.
moveOperator.addEntityToMoveList(entity, newCube);
iter.remove();
entity->markAncestorMissing(false);
} else {
// entity was deleted before we found its parent.
iter.remove();
@ -1017,7 +1052,13 @@ void EntityTree::fixupMissingParents() {
PerformanceTimer perfTimer("recurseTreeWithOperator");
recurseTreeWithOperator(&moveOperator);
}
}
void EntityTree::deleteDescendantsOfAvatar(QUuid avatarID) {
if (_childrenOfAvatars.contains(avatarID)) {
deleteEntities(_childrenOfAvatars[avatarID]);
_childrenOfAvatars.remove(avatarID);
}
}
void EntityTree::update() {

View file

@ -241,6 +241,10 @@ public:
Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name) const;
Q_INVOKABLE QStringList getJointNames(const QUuid& entityID) const;
void knowAvatarID(QUuid avatarID) { _avatarIDs += avatarID; }
void forgetAvatarID(QUuid avatarID) { _avatarIDs -= avatarID; }
void deleteDescendantsOfAvatar(QUuid avatarID);
public slots:
void callLoader(EntityItemID entityID);
@ -313,8 +317,11 @@ protected:
quint64 _maxEditDelta = 0;
quint64 _treeResetTime = 0;
void fixupMissingParents();
QVector<EntityItemWeakPointer> _missingParent;
void fixupMissingParents(); // try to hook members of _missingParent to parent instances
QVector<EntityItemWeakPointer> _missingParent; // entites with a parentID but no (yet) known parent instance
// we maintain a list of avatarIDs to notice when an entity is a child of one.
QSet<QUuid> _avatarIDs; // IDs of avatars connected to entity server
QHash<QUuid, QSet<EntityItemID>> _childrenOfAvatars; // which entities are children of which avatars
};
#endif // hifi_EntityTree_h

View file

@ -33,6 +33,7 @@ static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core
#pragma line __LINE__
uniform sampler2D sampler;
uniform float alpha = 1.0;
in vec2 vTexCoord;
out vec4 FragColor;
@ -40,6 +41,7 @@ out vec4 FragColor;
void main() {
FragColor = texture(sampler, vTexCoord);
FragColor.a *= alpha;
}
)FS";

View file

@ -146,7 +146,7 @@ public:
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type, const QByteArray& content) {
TextureExtra extra = { type, content };
return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast<NetworkTexture>();
return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast<NetworkTexture>();
}
/// Returns a texture version of an image file

View file

@ -101,8 +101,6 @@ private:
/// A simple object wrapper for an OpenGL texture.
class Texture {
public:
friend class TextureCache;
gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); }
gpu::TextureSourcePointer _textureSource;
};

View file

@ -15,71 +15,68 @@
#include <gpu/Context.h>
#include <ViewFrustum.h>
#include "Skybox_vert.h"
#include "Skybox_frag.h"
#include "skybox_vert.h"
#include "skybox_frag.h"
using namespace model;
Skybox::Skybox() {
Data data;
_dataBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Data), (const gpu::Byte*) &data));
/* // PLease create a default engineer skybox
_cubemap.reset( gpu::Texture::createCube(gpu::Element::COLOR_RGBA_32, 1));
unsigned char texels[] = {
255, 0, 0, 255,
0, 255, 255, 255,
0, 0, 255, 255,
255, 255, 0, 255,
0, 255, 0, 255,
255, 0, 255, 255,
};
_cubemap->assignStoredMip(0, gpu::Element::COLOR_RGBA_32, sizeof(texels), texels);*/
Schema schema;
_schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema));
}
void Skybox::setColor(const Color& color) {
_dataBuffer.edit<Data>()._color = color;
_schemaBuffer.edit<Schema>().color = color;
}
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
_cubemap = cubemap;
}
void Skybox::updateDataBuffer() const {
void Skybox::updateSchemaBuffer() const {
auto blend = 0.0f;
if (getCubemap() && getCubemap()->isDefined()) {
blend = 1.0f;
blend = 0.5f;
// If pitch black neutralize the color
if (glm::all(glm::equal(getColor(), glm::vec3(0.0f)))) {
blend = 2.0f;
blend = 1.0f;
}
}
if (blend != _dataBuffer.get<Data>()._blend) {
_dataBuffer.edit<Data>()._blend = blend;
if (blend != _schemaBuffer.get<Schema>().blend) {
_schemaBuffer.edit<Schema>().blend = blend;
}
}
void Skybox::prepare(gpu::Batch& batch, int textureSlot, int bufferSlot) const {
if (bufferSlot > -1) {
batch.setUniformBuffer(bufferSlot, _schemaBuffer);
}
void Skybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
updateDataBuffer();
Skybox::render(batch, frustum, (*this));
if (textureSlot > -1) {
gpu::TexturePointer skymap = getCubemap();
// FIXME: skymap->isDefined may not be threadsafe
if (skymap && skymap->isDefined()) {
batch.setResourceTexture(textureSlot, skymap);
}
}
}
void Skybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
updateSchemaBuffer();
Skybox::render(batch, frustum, (*this));
}
void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) {
// Create the static shared elements used to render the skybox
static gpu::BufferPointer theConstants;
static gpu::PipelinePointer thePipeline;
const int SKYBOX_SKYMAP_SLOT = 0;
const int SKYBOX_CONSTANTS_SLOT = 0;
static std::once_flag once;
std::call_once(once, [&] {
{
auto skyVS = gpu::Shader::createVertex(std::string(Skybox_vert));
auto skyFS = gpu::Shader::createPixel(std::string(Skybox_frag));
auto skyVS = gpu::Shader::createVertex(std::string(skybox_vert));
auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag));
auto skyShader = gpu::Shader::createProgram(skyVS, skyFS);
gpu::Shader::BindingSet bindings;
@ -98,10 +95,6 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
// Render
gpu::TexturePointer skymap = skybox.getCubemap();
// FIXME: skymap->isDefined may not be threadsafe
assert(skymap && skymap->isDefined());
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
@ -112,11 +105,8 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
batch.setModelTransform(Transform()); // only for Mac
batch.setPipeline(thePipeline);
batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer);
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap);
skybox.prepare(batch);
batch.draw(gpu::TRIANGLE_STRIP, 4);
batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr);
}

View file

@ -30,30 +30,33 @@ public:
virtual ~Skybox() {};
void setColor(const Color& color);
const Color getColor() const { return _dataBuffer.get<Data>()._color; }
const Color getColor() const { return _schemaBuffer.get<Schema>().color; }
void setCubemap(const gpu::TexturePointer& cubemap);
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const;
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
protected:
static const int SKYBOX_SKYMAP_SLOT { 0 };
static const int SKYBOX_CONSTANTS_SLOT { 0 };
gpu::TexturePointer _cubemap;
class Data {
class Schema {
public:
glm::vec3 _color{ 1.0f, 1.0f, 1.0f };
float _blend = 1.0f;
glm::vec3 color { 1.0f, 1.0f, 1.0f };
float blend { 0.0f };
};
mutable gpu::BufferView _dataBuffer;
mutable gpu::BufferView _schemaBuffer;
void updateDataBuffer() const;
void updateSchemaBuffer() const;
};
typedef std::shared_ptr< Skybox > SkyboxPointer;
typedef std::shared_ptr<Skybox> SkyboxPointer;
};

View file

@ -1,59 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// skybox.frag
// fragment shader
//
// Created by Sam Gateau on 5/5/2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
uniform samplerCube cubeMap;
struct Skybox {
vec4 _color;
};
uniform skyboxBuffer {
Skybox _skybox;
};
in vec3 _normal;
out vec4 _fragColor;
//PROCEDURAL_COMMON_BLOCK
#line 1001
//PROCEDURAL_BLOCK
#line 2033
void main(void) {
#ifdef PROCEDURAL
vec3 color = getSkyboxColor();
_fragColor = vec4(color, 0.0);
#else
vec3 coord = normalize(_normal);
// Skybox color or blend with skymap
vec3 color = _skybox._color.rgb;
if (_skybox._color.a > 0.0) {
vec3 texel = texture(cubeMap, coord).rgb;
if (_skybox._color.a < 2.0) {
color *= texel;
} else {
color = texel;
}
}
_fragColor = vec4(color, 0.0);
#endif
}

View file

@ -14,11 +14,11 @@
uniform samplerCube cubeMap;
struct Skybox {
vec4 _color;
vec4 color;
};
uniform skyboxBuffer {
Skybox _skybox;
Skybox skybox;
};
in vec3 _normal;
@ -39,11 +39,20 @@ void main(void) {
color = pow(color, vec3(2.2));
_fragColor = vec4(color, 0.0);
#else
// FIXME: scribe does not yet scrub out else statements
return;
#else
vec3 coord = normalize(_normal);
vec3 texel = texture(cubeMap, coord).rgb;
vec3 color = texel * _skybox._color.rgb;
vec3 color = skybox.color.rgb;
// blend is only set if there is a cubemap
if (skybox.color.a > 0.0) {
color = texture(cubeMap, coord).rgb;
if (skybox.color.a < 1.0) {
color *= skybox.color.rgb;
}
}
_fragColor = vec4(color, 0.0);
#endif

View file

@ -65,6 +65,12 @@ public:
virtual bool isThrottled() const { return false; }
virtual float getTargetFrameRate() { return 0.0f; }
/// Returns a boolean value indicating whether the display is currently visible
/// to the user. For monitor displays, false might indicate that a screensaver,
/// or power-save mode is active. For HMDs it may reflect a sensor indicating
/// whether the HMD is being worn
virtual bool isDisplayVisible() const { return false; }
// Rendering support
// Stop requesting renders, but don't do full deactivation

View file

@ -101,6 +101,7 @@ bool Procedural::parseUrl(const QUrl& shaderUrl) {
}
_shaderUrl = shaderUrl;
_shaderDirty = true;
if (_shaderUrl.isLocalFile()) {
_shaderPath = _shaderUrl.toLocalFile();
@ -230,7 +231,10 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
if (replaceIndex != std::string::npos) {
fragmentShaderSource.replace(replaceIndex, PROCEDURAL_BLOCK.size(), _shaderSource.toLocal8Bit().data());
}
//qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str();
// Leave this here for debugging
// qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str();
_fragmentShader = gpu::Shader::createPixel(fragmentShaderSource);
_shader = gpu::Shader::createProgram(_vertexShader, _fragmentShader);
@ -370,7 +374,8 @@ void Procedural::setupUniforms() {
v.y = date.month() - 1;
// But not the day... go figure
v.z = date.day();
v.w = (time.hour() * 3600) + (time.minute() * 60) + time.second();
float fractSeconds = (time.msec() / 1000.0f);
v.w = (time.hour() * 3600) + (time.minute() * 60) + time.second() + fractSeconds;
batch._glUniform(_standardUniformSlots[DATE], v);
});
}

View file

@ -37,6 +37,7 @@ public:
bool ready();
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size);
const gpu::ShaderPointer& getShader() const { return _shader; }
glm::vec4 getColor(const glm::vec4& entityColor);

View file

@ -15,40 +15,39 @@
#include <gpu/Context.h>
#include <ViewFrustum.h>
#include "ProceduralSkybox_vert.h"
#include "ProceduralSkybox_frag.h"
#include <model/skybox_vert.h>
#include <model/skybox_frag.h>
ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
_procedural._vertexSource = ProceduralSkybox_vert;
_procedural._fragmentSource = ProceduralSkybox_frag;
_procedural._vertexSource = skybox_vert;
_procedural._fragmentSource = skybox_frag;
// Adjust the pipeline state for background using the stencil test
_procedural._state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
}
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
ProceduralSkybox::render(batch, frustum, (*this));
if (_procedural.ready()) {
ProceduralSkybox::render(batch, frustum, (*this));
} else {
Skybox::render(batch, frustum);
}
}
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const ProceduralSkybox& skybox) {
if (!(skybox._procedural.ready())) {
skybox.updateDataBuffer();
Skybox::render(batch, viewFrustum, skybox);
} else {
gpu::TexturePointer skymap = skybox.getCubemap();
// FIXME: skymap->isDefined may not be threadsafe
assert(skymap && skymap->isDefined());
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
glm::mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
Transform viewTransform;
viewFrustum.evalViewTransform(viewTransform);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewTransform);
batch.setModelTransform(Transform()); // only for Mac
Transform viewTransform;
viewFrustum.evalViewTransform(viewTransform);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewTransform);
batch.setModelTransform(Transform()); // only for Mac
batch.setResourceTexture(0, skybox.getCubemap());
skybox._procedural.prepare(batch, glm::vec3(0), glm::vec3(1));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
auto& procedural = skybox._procedural;
procedural.prepare(batch, glm::vec3(0), glm::vec3(1));
auto textureSlot = procedural.getShader()->getTextures().findLocation("cubeMap");
auto bufferSlot = procedural.getShader()->getBuffers().findLocation("skyboxBuffer");
skybox.prepare(batch, textureSlot, bufferSlot);
batch.draw(gpu::TRIANGLE_STRIP, 4);
}

View file

@ -1,39 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// skybox.vert
// vertex shader
//
// Created by Sam Gateau on 5/5/2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
out vec3 _normal;
void main(void) {
const float depth = 0.0;
const vec4 UNIT_QUAD[4] = vec4[4](
vec4(-1.0, -1.0, depth, 1.0),
vec4(1.0, -1.0, depth, 1.0),
vec4(-1.0, 1.0, depth, 1.0),
vec4(1.0, 1.0, depth, 1.0)
);
vec4 inPosition = UNIT_QUAD[gl_VertexID];
// standard transform
TransformCamera cam = getTransformCamera();
vec3 clipDir = vec3(inPosition.xy, 0.0);
vec3 eyeDir;
<$transformClipToEyeDir(cam, clipDir, eyeDir)$>
<$transformEyeToWorldDir(cam, eyeDir, _normal)$>
// Position is supposed to come in clip space
gl_Position = vec4(inPosition.xy, 0.0, 1.0);
}

View file

@ -392,13 +392,20 @@ void AnimDebugDraw::update() {
assert(numVerts == (v - verts));
// The RenderItem culling is not working correctly
// Workaround this issue by using the default constructed
// item._bound which is a 16 km cube.
/*
render::Item::Bound theBound;
for (int i = 0; i < numVerts; i++) {
theBound += verts[i].pos;
}
data._bound = theBound;
*/
data._isVisible = (numVerts > 0);
data._bound = theBound;
data._indexBuffer->resize(sizeof(uint16_t) * numVerts);
uint16_t* indices = (uint16_t*)data._indexBuffer->editData();
for (int i = 0; i < numVerts; i++) {

View file

@ -96,6 +96,9 @@ void Scene::processPendingChangesQueue() {
// removes
removeItems(consolidatedPendingChanges._removedItems);
// Update the numItemsAtomic counter AFTER the pending changes went through
_numAllocatedItems.exchange(maxID);
// ready to go back to rendering activities
_itemsMutex.unlock();
}

View file

@ -264,12 +264,33 @@ Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& acc
return brickID;
}
Octree::Location ItemSpatialTree::evalLocation(const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf) const {
minCoordf = evalCoordf(bound.getMinimumPoint());
maxCoordf = evalCoordf(bound.getMaximumPoint());
// If the bound crosses any of the octree volume limit, then return root cell
if ( (minCoordf.x < 0.0f)
|| (minCoordf.y < 0.0f)
|| (minCoordf.z < 0.0f)
|| (maxCoordf.x >= _size)
|| (maxCoordf.y >= _size)
|| (maxCoordf.z >= _size)) {
return Location();
}
Coord3 minCoord(minCoordf);
Coord3 maxCoord(maxCoordf);
return Location::evalFromRange(minCoord, maxCoord);
}
Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const {
Locations locations;
Coord3f minCoordf, maxCoordf;
locations.reserve(bounds.size());
for (auto& bound : bounds) {
if (!bound.bound.isNull()) {
locations.emplace_back(evalLocation(bound.bound));
locations.emplace_back(evalLocation(bound.bound, minCoordf, maxCoordf));
} else {
locations.emplace_back(Location());
}
@ -344,11 +365,8 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID
ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey) {
auto newCell = INVALID_CELL;
if (!newKey.isViewSpace()) {
auto minCoordf = evalCoordf(bound.getMinimumPoint());
auto maxCoordf = evalCoordf(bound.getMaximumPoint());
Coord3 minCoord(minCoordf);
Coord3 maxCoord(maxCoordf);
auto location = Location::evalFromRange(minCoord, maxCoord);
Coord3f minCoordf, maxCoordf;
auto location = evalLocation(bound, minCoordf, maxCoordf);
// Compare range size vs cell location size and tag itemKey accordingly
// If Item bound fits in sub cell then tag as small
@ -403,7 +421,21 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey&
int Octree::select(CellSelection& selection, const FrustumSelector& selector) const {
Index cellID = ROOT_CELL;
return selectTraverse(cellID, selection, selector);
auto cell = getConcreteCell(cellID);
int numSelectedsIn = (int)selection.size();
// Always include the root cell partially containing potentially outer objects
selectCellBrick(cellID, selection, false);
// then traverse deeper
for (int i = 0; i < NUM_OCTANTS; i++) {
Index subCellID = cell.child((Link)i);
if (subCellID != INVALID_CELL) {
selectTraverse(subCellID, selection, selector);
}
}
return (int)selection.size() - numSelectedsIn;
}

View file

@ -117,7 +117,6 @@ namespace render {
return depth;
}
class Location {
void assertValid() {
assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0));
@ -157,6 +156,7 @@ namespace render {
// Eval the location best fitting the specified range
static Location evalFromRange(const Coord3& minCoord, const Coord3& maxCoord, Depth rangeDepth = MAX_DEPTH);
// Eval the intersection test against a frustum
enum Intersection {
Outside = 0,
@ -367,7 +367,7 @@ namespace render {
// An octree of Items organizing them efficiently for culling
// The octree only cares about the bound & the key of an item to store it a the right cell location
class ItemSpatialTree : public Octree {
float _size { 32768.0f };
float _size{ 32768.0f };
float _invSize { 1.0f / _size };
glm::vec3 _origin { -16384.0f };
@ -398,10 +398,26 @@ namespace render {
return getOrigin() + glm::vec3(coord) * cellWidth;
}
// Clamp a 3D relative position to make sure it is in the valid range space of the octree
glm::vec3 clampRelPosToTreeRange(const glm::vec3& pos) const {
const float EPSILON = 0.0001f;
return glm::vec3(
std::min(std::max(pos.x, 0.0f), _size - EPSILON),
std::min(std::max(pos.y, 0.0f), _size - EPSILON),
std::min(std::max(pos.z, 0.0f), _size - EPSILON));
}
// Eval an integer cell coordinate (at the specified deepth) from a given 3d position
// If the 3D position is out of the octree volume, then the position is clamped
// so the integer coordinate is meaningfull
Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const {
auto npos = (pos - getOrigin());
auto npos = clampRelPosToTreeRange((pos - getOrigin()));
return Coord3(npos * getInvCellWidth(depth)); // Truncate fractional part
}
// Eval a real cell coordinate (at the specified deepth) from a given 3d position
// Position is NOT clamped to the boundaries of the octree so beware of conversion to a Coord3!
Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const {
auto npos = (pos - getOrigin());
return Coord3f(npos * getInvCellWidth(depth));
@ -412,9 +428,10 @@ namespace render {
float cellWidth = getCellWidth(loc.depth);
return AABox(evalPos(loc.pos, cellWidth), cellWidth);
}
Location evalLocation(const AABox& bound) const {
return Location::evalFromRange(evalCoord(bound.getMinimumPoint()), evalCoord(bound.getMaximumPoint()));
}
// Eval the cell location for a given arbitrary Bound,
// if the Bound crosses any of the Octree planes then the root cell is returned
Location evalLocation(const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf) const;
Locations evalLocations(const ItemBounds& bounds) const;
// Managing itemsInserting items in cells

View file

@ -115,9 +115,6 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable
auto scriptContent = _scriptCache[url];
lock.unlock();
qCDebug(scriptengine) << "Found script in cache:" << url.toString();
#if 1 // def THREAD_DEBUGGING
qCDebug(scriptengine) << "ScriptCache::getScriptContents() about to call contentAvailable() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
contentAvailable(url.toString(), scriptContent, true, true);
} else {
bool alreadyWaiting = _contentCallbacks.contains(url);

View file

@ -471,3 +471,11 @@ bool isNaN(glm::quat value) {
return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z);
}
glm::mat4 orthoInverse(const glm::mat4& m) {
glm::mat4 r = m;
r[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
r = glm::transpose(r);
r[3] = -(r * m[3]);
r[3][3] = 1.0f;
return r;
}

View file

@ -232,4 +232,6 @@ glm::vec2 getFacingDir2D(const glm::mat4& m);
bool isNaN(glm::vec3 value);
bool isNaN(glm::quat value);
glm::mat4 orthoInverse(const glm::mat4& m);
#endif // hifi_GLMHelpers_h

View file

@ -104,6 +104,7 @@ void updateQmlItemFromAction(QObject* target, QAction* source) {
target->setProperty("checkable", source->isCheckable());
target->setProperty("enabled", source->isEnabled());
target->setProperty("text", source->text());
target->setProperty("shortcut", source->shortcut().toString());
target->setProperty("checked", source->isChecked());
target->setProperty("visible", source->isVisible());
}
@ -190,6 +191,20 @@ void VrMenu::addAction(QMenu* menu, QAction* action) {
bindActionToQmlAction(result, action);
}
void VrMenu::addSeparator(QMenu* menu) {
Q_ASSERT(MenuUserData::forObject(menu));
MenuUserData* userData = MenuUserData::forObject(menu);
if (!userData) {
return;
}
QObject* menuQml = findMenuObject(userData->uuid.toString());
Q_ASSERT(menuQml);
bool invokeResult = QMetaObject::invokeMethod(menuQml, "addSeparator", Qt::DirectConnection);
Q_ASSERT(invokeResult);
Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
}
void VrMenu::insertAction(QAction* before, QAction* action) {
QObject* beforeQml{ nullptr };
{

View file

@ -28,6 +28,7 @@ public:
VrMenu(QObject* parent = nullptr);
void addMenu(QMenu* menu);
void addAction(QMenu* parent, QAction* action);
void addSeparator(QMenu* parent);
void insertAction(QAction* before, QAction* action);
void removeAction(QAction* action);

View file

@ -222,8 +222,11 @@ void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
return;
}
QAction* menu = _actionHash.value(menuOption);
if (menu) {
menu->setChecked(isChecked);
if (menu && menu->isCheckable()) {
auto wasChecked = menu->isChecked();
if (wasChecked != isChecked) {
menu->trigger();
}
}
}
@ -511,7 +514,11 @@ void MenuWrapper::setEnabled(bool enabled) {
}
QAction* MenuWrapper::addSeparator() {
return _realMenu->addSeparator();
QAction* action = _realMenu->addSeparator();
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addSeparator(_realMenu);
});
return action;
}
void MenuWrapper::addAction(QAction* action) {

View file

@ -17,6 +17,7 @@ public:
protected:
void hmdPresent() override {}
bool isHmdMounted() const override { return true; }
private:
static const QString NAME;

View file

@ -24,6 +24,8 @@ public:
protected:
void hmdPresent() override;
// FIXME update with Oculus API call once it's available in the SDK
bool isHmdMounted() const override { return true; }
void customizeContext() override;
void uncustomizeContext() override;
void updateFrameData() override;

View file

@ -6,7 +6,9 @@
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
#
if (NOT WIN32)
# Windows doesn't need this, and building it currently make Linux unstable.
# if (NOT WIN32)
if (APPLE)
set(TARGET_NAME oculusLegacy)
setup_hifi_plugin()
@ -19,4 +21,4 @@ if (NOT WIN32)
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
endif()
endif()

View file

@ -35,6 +35,7 @@ public:
protected:
virtual void customizeContext() override;
void hmdPresent() override {}
bool isHmdMounted() const override { return true; }
#if 0
virtual void uncustomizeContext() override;
virtual void internalPresent() override;

View file

@ -86,13 +86,16 @@ void OpenVrDisplayPlugin::activate() {
}
void OpenVrDisplayPlugin::deactivate() {
// Base class deactivate must come before our local deactivate
// because the OpenGL base class handles the wait for the present
// thread before continuing
HmdDisplayPlugin::deactivate();
_container->setIsOptionChecked(StandingHMDSensorMode, false);
if (_system) {
releaseOpenVrSystem();
_system = nullptr;
}
_compositor = nullptr;
HmdDisplayPlugin::deactivate();
}
void OpenVrDisplayPlugin::customizeContext() {
@ -154,4 +157,10 @@ void OpenVrDisplayPlugin::hmdPresent() {
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
_hmdActivityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd);
}
bool OpenVrDisplayPlugin::isHmdMounted() const {
return _hmdActivityLevel == vr::k_EDeviceActivityLevel_UserInteraction;
}

View file

@ -33,9 +33,11 @@ public:
protected:
void hmdPresent() override;
bool isHmdMounted() const override;
private:
vr::IVRSystem* _system { nullptr };
std::atomic<vr::EDeviceActivityLevel> _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown };
static const QString NAME;
mutable Mutex _poseMutex;
};

View file

@ -477,118 +477,130 @@ function performContentMigration() {
var logWindow = null;
var labels = {
serverState: {
label: 'Server - Stopped',
enabled: false
},
version: {
label: 'Version - ' + buildInfo.buildIdentifier,
enabled: false
},
restart: {
label: 'Start Server',
click: function() {
homeServer.restart();
}
},
stopServer: {
label: 'Stop Server',
visible: false,
click: function() {
homeServer.stop();
}
},
goHome: {
label: 'Go Home',
click: goHomeClicked,
enabled: false
},
quit: {
label: 'Quit',
accelerator: 'Command+Q',
click: function() {
shutdown();
}
},
settings: {
label: 'Settings',
click: function() {
shell.openExternal('http://localhost:40100/settings');
},
enabled: false
},
viewLogs: {
label: 'View Logs',
click: function() {
logWindow.open();
}
},
share: {
label: 'Share',
click: function() {
shell.openExternal('http://localhost:40100/settings/?action=share')
}
},
migrateContent: {
label: 'Migrate Stack Manager Content',
click: function() {
promptToMigrateContent();
}
},
shuttingDown: {
label: "Shutting down...",
enabled: false
},
}
var separator = {
type: 'separator'
};
function buildMenuArray(serverState) {
var menuArray = null;
updateLabels(serverState);
var menuArray = [];
if (isShuttingDown) {
menuArray = [
{
label: "Shutting down...",
enabled: false
}
];
menuArray.push(labels.shuttingDown);
} else {
menuArray = [
{
label: 'Server - Stopped',
enabled: false
},
{
type: 'separator'
},
{
label: 'Go Home',
click: goHomeClicked,
enabled: false
},
{
type: 'separator'
},
{
label: 'Start Server',
click: function() { homeServer.restart(); }
},
{
label: 'Stop Server',
visible: false,
click: function() { homeServer.stop(); }
},
{
label: 'Settings',
click: function() { shell.openExternal('http://localhost:40100/settings'); },
enabled: false
},
{
label: 'View Logs',
click: function() { logWindow.open(); }
},
{
type: 'separator'
},
{
label: 'Share',
click: function() { shell.openExternal('http://localhost:40100/settings/?action=share') }
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Command+Q',
click: function() { shutdown(); }
}
];
menuArray.push(labels.serverState);
menuArray.push(labels.version);
menuArray.push(separator);
menuArray.push(labels.goHome);
menuArray.push(separator);
menuArray.push(labels.restart);
menuArray.push(labels.stopServer);
menuArray.push(labels.settings);
menuArray.push(labels.viewLogs);
menuArray.push(separator);
menuArray.push(labels.share);
menuArray.push(separator);
menuArray.push(labels.quit);
var foundStackManagerContent = isStackManagerContentPresent();
if (foundStackManagerContent) {
// add a separator and the stack manager content migration option
menuArray.splice(menuArray.length - 1, 0, {
label: 'Migrate Stack Manager Content',
click: function() { promptToMigrateContent(); }
}, {
type: 'separator'
});
menuArray.splice(menuArray.length - 1, 0, labels.migrateContent, separator);
}
updateMenuArray(menuArray, serverState);
}
return menuArray;
}
const GO_HOME_INDEX = 2;
const SERVER_LABEL_INDEX = 0;
const RESTART_INDEX = 4;
const STOP_INDEX = 5;
const SETTINGS_INDEX = 6;
function updateLabels(serverState) {
function updateMenuArray(menuArray, serverState) {
// update the tray menu state
var running = serverState == ProcessGroupStates.STARTED;
var serverLabelItem = menuArray[SERVER_LABEL_INDEX];
var restartItem = menuArray[RESTART_INDEX];
// Go Home is only enabled if running
menuArray[GO_HOME_INDEX].enabled = running;
// Stop is only visible if running
menuArray[STOP_INDEX].visible = running;
// Settings is only visible if running
menuArray[SETTINGS_INDEX].enabled = running;
labels.goHome.enabled = running;
labels.stopServer.visible = running;
labels.settings.enabled = running;
if (serverState == ProcessGroupStates.STARTED) {
serverLabelItem.label = "Server - Started";
restartItem.label = "Restart Server";
labels.serverState.label = "Server - Started";
labels.restart.label = "Restart Server";
} else if (serverState == ProcessGroupStates.STOPPED) {
serverLabelItem.label = "Server - Stopped";
restartItem.label = "Start Server";
labels.serverState.label = "Server - Stopped";
labels.restart.label = "Start Server";
labels.restart.enabled = true;
} else if (serverState == ProcessGroupStates.STOPPING) {
serverLabelItem.label = "Server - Stopping";
restartItem.label = "Restart Server";
restartItem.enabled = false;
labels.serverState.label = "Server - Stopping";
labels.restart.label = "Restart Server";
labels.restart.enabled = false;
}
}

View file

@ -23,83 +23,77 @@
#include <gl/QOpenGLDebugLoggerWrapper.h>
#include <gl/QOpenGLContextWrapper.h>
#include "../model/Skybox_vert.h"
#include "../model/Skybox_frag.h"
#include <render-utils/simple_vert.h>
#include <render-utils/simple_frag.h>
#include <render-utils/simple_textured_frag.h>
#include <render-utils/simple_textured_emisive_frag.h>
#include "simple_vert.h"
#include "simple_frag.h"
#include "simple_textured_frag.h"
#include "simple_textured_emisive_frag.h"
#include <render-utils/deferred_light_vert.h>
#include <render-utils/deferred_light_limited_vert.h>
#include "deferred_light_vert.h"
#include "deferred_light_limited_vert.h"
#include <render-utils/directional_light_frag.h>
#include <render-utils/directional_ambient_light_frag.h>
#include <render-utils/directional_skybox_light_frag.h>
#include "directional_light_frag.h"
#include <render-utils/point_light_frag.h>
#include <render-utils/spot_light_frag.h>
#include "directional_ambient_light_frag.h"
#include <render-utils/standardTransformPNTC_vert.h>
#include <render-utils/standardDrawTexture_frag.h>
#include "directional_skybox_light_frag.h"
#include <render-utils/model_vert.h>
#include <render-utils/model_shadow_vert.h>
#include <render-utils/model_normal_map_vert.h>
#include <render-utils/model_lightmap_vert.h>
#include <render-utils/model_lightmap_normal_map_vert.h>
#include <render-utils/skin_model_vert.h>
#include <render-utils/skin_model_shadow_vert.h>
#include <render-utils/skin_model_normal_map_vert.h>
#include "point_light_frag.h"
#include "spot_light_frag.h"
#include <render-utils/model_frag.h>
#include <render-utils/model_shadow_frag.h>
#include <render-utils/model_normal_map_frag.h>
#include <render-utils/model_normal_specular_map_frag.h>
#include <render-utils/model_specular_map_frag.h>
#include <render-utils/model_lightmap_frag.h>
#include <render-utils/model_lightmap_normal_map_frag.h>
#include <render-utils/model_lightmap_normal_specular_map_frag.h>
#include <render-utils/model_lightmap_specular_map_frag.h>
#include <render-utils/model_translucent_frag.h>
#include "standardTransformPNTC_vert.h"
#include "standardDrawTexture_frag.h"
#include <entities-renderer/untextured_particle_frag.h>
#include <entities-renderer/untextured_particle_vert.h>
#include <entities-renderer/textured_particle_frag.h>
#include <entities-renderer/textured_particle_vert.h>
#include "model_vert.h"
#include "model_shadow_vert.h"
#include "model_normal_map_vert.h"
#include "model_lightmap_vert.h"
#include "model_lightmap_normal_map_vert.h"
#include "skin_model_vert.h"
#include "skin_model_shadow_vert.h"
#include "skin_model_normal_map_vert.h"
#include <render-utils/hit_effect_vert.h>
#include <render-utils/hit_effect_frag.h>
#include "model_frag.h"
#include "model_shadow_frag.h"
#include "model_normal_map_frag.h"
#include "model_normal_specular_map_frag.h"
#include "model_specular_map_frag.h"
#include "model_lightmap_frag.h"
#include "model_lightmap_normal_map_frag.h"
#include "model_lightmap_normal_specular_map_frag.h"
#include "model_lightmap_specular_map_frag.h"
#include "model_translucent_frag.h"
#include <render-utils/overlay3D_vert.h>
#include <render-utils/overlay3D_frag.h>
#include "untextured_particle_frag.h"
#include "untextured_particle_vert.h"
#include "textured_particle_frag.h"
#include "textured_particle_vert.h"
#include <model/skybox_vert.h>
#include <model/skybox_frag.h>
#include <render-utils/stars_vert.h>
#include <render-utils/stars_frag.h>
#include <render-utils/starsGrid_frag.h>
#include "hit_effect_vert.h"
#include "hit_effect_frag.h"
#include <gpu/DrawTransformUnitQuad_vert.h>
#include <gpu/DrawTexcoordRectTransformUnitQuad_vert.h>
#include <gpu/DrawViewportQuadTransformTexcoord_vert.h>
#include <gpu/DrawTexture_frag.h>
#include <gpu/DrawTextureOpaque_frag.h>
#include <gpu/DrawColoredTexture_frag.h>
#include "overlay3D_vert.h"
#include "overlay3D_frag.h"
#include <render-utils/sdf_text3D_vert.h>
#include <render-utils/sdf_text3D_frag.h>
#include "Skybox_vert.h"
#include "Skybox_frag.h"
#include <entities-renderer/paintStroke_vert.h>
#include <entities-renderer/paintStroke_frag.h>
#include "stars_vert.h"
#include "stars_frag.h"
#include "starsGrid_frag.h"
#include "DrawTransformUnitQuad_vert.h"
#include "DrawTexcoordRectTransformUnitQuad_vert.h"
#include "DrawViewportQuadTransformTexcoord_vert.h"
#include "DrawTexture_frag.h"
#include "DrawTextureOpaque_frag.h"
#include "DrawColoredTexture_frag.h"
#include "sdf_text3D_vert.h"
#include "sdf_text3D_frag.h"
#include "paintStroke_vert.h"
#include "paintStroke_frag.h"
#include "polyvox_vert.h"
#include "polyvox_frag.h"
#include <entities-renderer/polyvox_vert.h>
#include <entities-renderer/polyvox_frag.h>
// Create a simple OpenGL window that renders text in various ways
class QTestWindow : public QWindow {
@ -163,7 +157,7 @@ void QTestWindow::draw() {
testShaderBuild(DrawTransformUnitQuad_vert, DrawTextureOpaque_frag);
testShaderBuild(DrawTransformUnitQuad_vert, DrawColoredTexture_frag);
testShaderBuild(Skybox_vert, Skybox_frag);
testShaderBuild(skybox_vert, skybox_frag);
testShaderBuild(simple_vert, simple_frag);
testShaderBuild(simple_vert, simple_textured_frag);
testShaderBuild(simple_vert, simple_textured_emisive_frag);
@ -209,8 +203,6 @@ void QTestWindow::draw() {
testShaderBuild(overlay3D_vert, overlay3D_frag);
testShaderBuild(Skybox_vert, Skybox_frag);
testShaderBuild(paintStroke_vert,paintStroke_frag);
testShaderBuild(polyvox_vert, polyvox_frag);