Merge branch 'master' into plugins

Conflicts:
	interface/src/Application.cpp
	interface/src/Application.h
	interface/src/avatar/Head.cpp
	interface/src/avatar/MyAvatar.cpp
	interface/src/avatar/MyAvatar.h
	interface/src/avatar/SkeletonModel.cpp
	interface/src/avatar/SkeletonModel.h
	interface/src/devices/3DConnexionClient.cpp
	interface/src/ui/overlays/Overlays.cpp
This commit is contained in:
Bradley Austin Davis 2015-08-06 11:03:37 -07:00
commit 7e60aee1bf
181 changed files with 5741 additions and 48042 deletions

View file

@ -5,6 +5,7 @@
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
* [zlib](http://www.zlib.net/)
####CMake External Project Dependencies

View file

@ -75,6 +75,16 @@ To prevent these problems, install OpenSSL yourself. Download the following bina
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
####zlib
Install zlib from
[Zlib for Windows](http://gnuwin32.sourceforge.net/packages/zlib.htm)
and fix a header file, as described here:
[zlib zconf.h bug](http://sourceforge.net/p/gnuwin32/bugs/169/)
###Build High Fidelity using Visual Studio
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.

View file

@ -186,6 +186,7 @@ option(GET_POLYVOX "Get polyvox library automatically as external project" 1)
option(GET_OPENVR "Get OpenVR library automatically as external project" 1)
option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1)
option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1)
option(GET_GLEW "Get GLEW library automatically as external project" 1)
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)

34
cmake/externals/glew/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,34 @@
set(EXTERNAL_NAME glew)
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple.zip
URL_MD5 0507dc08337a82a5e7ecbc5417f92cc1
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glew include directories")
if (UNIX)
set(LIB_PREFIX "lib")
set(LIB_EXT "a")
elseif (WIN32)
set(LIB_EXT "lib")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glew_d.${LIB_EXT} CACHE FILEPATH "Path to glew debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glew.${LIB_EXT} CACHE FILEPATH "Path to glew release library")

28
cmake/externals/zlib/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,28 @@
if (WIN32)
set(EXTERNAL_NAME zlib)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://zlib.net/zlib128.zip
URL_MD5 126f8676442ffbd97884eb4d6f32afb4
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of zlib include directories")
ExternalProject_Get_Property(${EXTERNAL_NAME} BINARY_DIR)
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${BINARY_DIR}/Release CACHE FILEPATH "Location of GLEW DLL")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${BINARY_DIR}/Release/zlib.lib CACHE FILEPATH "Location of ZLib release library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of ZLib debug library")
endif ()

View file

@ -18,20 +18,18 @@ include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("connexionclient")
if (APPLE)
find_library(3DConnexionClient 3DConnexionClient)
find_library(3DCONNEXIONCLIENT_LIBRARIES NAMES 3DConnexionClient HINTS 3DCONNEXIONCLIENT_SEARCH_DIRS)
if(EXISTS ${3DConnexionClient})
set(3DCONNEXIONCLIENT_FOUND true)
set(3DCONNEXIONCLIENT_INCLUDE_DIRS ${3DConnexionClient})
set(3DCONNEXIONCLIENT_LIBRARY ${3DConnexionClient})
message(STATUS "Found 3DConnexion at " ${3DConnexionClient})
mark_as_advanced(3DCONNEXIONCLIENT_INCLUDE_DIR CONNEXIONCLIENT_LIBRARY)
mark_as_advanced(3DCONNEXIONCLIENT_INCLUDE_DIR 3DCONNEXIONCLIENT_LIBRARY)
else ()
message(STATUS "Could NOT find 3DConnexionClient")
endif()
endif()
if (WIN32)
find_path(3DCONNEXIONCLIENT_INCLUDE_DIRS I3dMouseParams.h PATH_SUFFIXES Inc HINTS ${CONNEXIONCLIENT_SEARCH_DIRS})
elseif (WIN32)
find_path(3DCONNEXIONCLIENT_INCLUDE_DIRS I3dMouseParams.h PATH_SUFFIXES include HINTS ${3DCONNEXIONCLIENT_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(3DConnexionClient DEFAULT_MSG 3DCONNEXIONCLIENT_INCLUDE_DIRS)

View file

@ -0,0 +1,35 @@
#
# FindGLEW.cmake
#
# Try to find GLEW library and include path. Note that this only handles static GLEW.
# Once done this will define
#
# GLEW_FOUND
# GLEW_INCLUDE_DIRS
# GLEW_LIBRARIES
#
# Created on 2/6/2014 by Stephen Birarda
# Copyright 2014 High Fidelity, Inc.
#
# Adapted from FindGLEW.cmake available in the nvidia-texture-tools repository
# (https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/cmake/FindGLEW.cmake?r=96)
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("glew")
find_path(GLEW_INCLUDE_DIRS GL/glew.h PATH_SUFFIXES include HINTS ${GLEW_SEARCH_DIRS})
find_library(GLEW_LIBRARY_RELEASE glew32 PATH_SUFFIXES "lib/Release/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS})
find_library(GLEW_LIBRARY_DEBUG glew32d PATH_SUFFIXES "lib/Debug/Win32" "lib" HINTS ${GLEW_SEARCH_DIRS})
include(SelectLibraryConfigurations)
select_library_configurations(GLEW)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES)
message(STATUS "Found GLEW - Assuming that GLEW is static and defining GLEW_STATIC")

View file

@ -389,6 +389,10 @@
{
"value": "json",
"label": "Entity server persists data as JSON"
},
{
"value": "json.gz",
"label": "Entity server persists data as gzipped JSON"
}
],
"advanced": true

214
examples/controlPanel.js Normal file
View file

@ -0,0 +1,214 @@
//
// controlPanel.js
// examples
//
// Created by Zander Otavka on 7/15/15.
// Copyright 2015 High Fidelity, Inc.
//
// Shows a few common controls in a FloatingUIPanel on right click.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include([
"libraries/globals.js",
"libraries/overlayManager.js",
]);
var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg";
var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/close.svg";
var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/mic-toggle.svg";
var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/face-toggle.svg";
var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/address-bar-toggle.svg";
var panel = new FloatingUIPanel({
anchorPosition: {
bind: "myAvatar"
},
offsetPosition: { x: 0, y: 0.4, z: 1 }
});
var background = new BillboardOverlay({
url: BG_IMAGE_URL,
dimensions: {
x: 0.5,
y: 0.5,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false
});
panel.addChild(background);
var closeButton = new BillboardOverlay({
url: CLOSE_IMAGE_URL,
dimensions: {
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: -0.1,
y: 0.1,
z: -0.001
}
});
closeButton.onClick = function(event) {
panel.visible = false;
};
panel.addChild(closeButton);
var micMuteButton = new BillboardOverlay({
url: MIC_IMAGE_URL,
subImage: {
x: 0,
y: 0,
width: 45,
height: 45
},
dimensions: {
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.1,
y: 0.1,
z: -0.001
}
});
micMuteButton.onClick = function(event) {
AudioDevice.toggleMute();
};
panel.addChild(micMuteButton);
var faceMuteButton = new BillboardOverlay({
url: FACE_IMAGE_URL,
subImage: {
x: 0,
y: 0,
width: 45,
height: 45
},
dimensions: {
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.1,
y: -0.1,
z: -0.001
}
});
faceMuteButton.onClick = function(event) {
FaceTracker.toggleMute();
};
panel.addChild(faceMuteButton);
var addressBarButton = new BillboardOverlay({
url: ADDRESS_BAR_IMAGE_URL,
subImage: {
x: 0,
y: 0,
width: 45,
height: 45
},
dimensions: {
x: 0.15,
y: 0.15,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: -0.1,
y: -0.1,
z: -0.001
}
});
addressBarButton.onClick = function(event) {
DialogsManager.toggleAddressBar();
};
panel.addChild(addressBarButton);
function onMicMuteToggled() {
var offset;
if (AudioDevice.getMuted()) {
offset = 45;
} else {
offset = 0;
}
micMuteButton.subImage = {
x: offset,
y: 0,
width: 45,
height: 45
};
}
onMicMuteToggled();
function onFaceMuteToggled() {
var offset;
if (FaceTracker.getMuted()) {
offset = 45;
} else {
offset = 0;
}
faceMuteButton.subImage = {
x: offset,
y: 0,
width: 45,
height: 45
};
}
onFaceMuteToggled();
var mouseDown = {};
function onMouseDown(event) {
if (event.isLeftButton) {
mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
}
if (event.isRightButton) {
mouseDown.pos = { x: event.x, y: event.y };
}
}
function onMouseUp(event) {
if (event.isLeftButton) {
var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
if (overlay && overlay === mouseDown.overlay && overlay.onClick) {
overlay.onClick(event);
}
}
if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) {
panel.setProperties({
visible: !panel.visible,
offsetRotation: {
bind: "quat",
value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 })
}
});
}
mouseDown = {};
}
function onScriptEnd(event) {
panel.destroy();
}
Controller.mousePressEvent.connect(onMouseDown);
Controller.mouseReleaseEvent.connect(onMouseUp);
AudioDevice.muteToggled.connect(onMicMuteToggled);
FaceTracker.muteToggled.connect(onFaceMuteToggled);
Script.scriptEnding.connect(onScriptEnd);

View file

@ -0,0 +1,169 @@
//
// floatingUI.js
// examples/example/ui
//
// Created by Alexander Otavka
// 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
//
Script.include([
"../../libraries/globals.js",
"../../libraries/overlayManager.js",
]);
var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg";
var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg";
var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg";
var mainPanel = new FloatingUIPanel({
offsetRotation: {
bind: "quat",
value: { w: 1, x: 0, y: 0, z: 0 }
},
offsetPosition: { x: 0, y: 0.4, z: 1 }
});
var bluePanel = mainPanel.addChild(new FloatingUIPanel ({
offsetPosition: { x: 0.1, y: 0.1, z: -0.2 }
}));
var mainPanelBackground = new BillboardOverlay({
url: BG_IMAGE_URL,
dimensions: {
x: 0.5,
y: 0.5,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0,
y: 0,
z: 0.001
}
});
var bluePanelBackground = mainPanelBackground.clone();
bluePanelBackground.dimensions = {
x: 0.3,
y: 0.3
};
mainPanel.addChild(mainPanelBackground);
bluePanel.addChild(bluePanelBackground);
var redDot = mainPanel.addChild(new BillboardOverlay({
url: RED_DOT_IMAGE_URL,
dimensions: {
x: 0.1,
y: 0.1,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: -0.15,
y: -0.15,
z: 0
}
}));
var redDot2 = mainPanel.addChild(new BillboardOverlay({
url: RED_DOT_IMAGE_URL,
dimensions: {
x: 0.1,
y: 0.1,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: -0.155,
y: 0.005,
z: 0
}
}));
var blueSquare = bluePanel.addChild(new BillboardOverlay({
url: BLUE_SQUARE_IMAGE_URL,
dimensions: {
x: 0.1,
y: 0.1,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.055,
y: -0.055,
z: 0
}
}));
var blueSquare2 = bluePanel.addChild(new BillboardOverlay({
url: BLUE_SQUARE_IMAGE_URL,
dimensions: {
x: 0.1,
y: 0.1,
},
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.055,
y: 0.055,
z: 0
}
}));
var blueSquare3 = blueSquare2.clone();
blueSquare3.offsetPosition = {
x: -0.055,
y: 0.055,
z: 0
};
var mouseDown = {};
function onMouseDown(event) {
if (event.isLeftButton) {
mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
}
if (event.isRightButton) {
mouseDown.pos = { x: event.x, y: event.y };
}
}
function onMouseUp(event) {
if (event.isLeftButton) {
var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
if (overlay === mouseDown.overlay) {
if (overlay.attachedPanel === bluePanel) {
overlay.destroy();
} else if (overlay) {
var oldPos = overlay.offsetPosition;
var newPos = {
x: Number(oldPos.x),
y: Number(oldPos.y),
z: Number(oldPos.z) + 0.1
};
overlay.offsetPosition = newPos;
}
}
}
if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) {
mainPanel.visible = !mainPanel.visible;
}
}
function onScriptEnd() {
mainPanel.destroy();
}
Controller.mousePressEvent.connect(onMouseDown);
Controller.mouseReleaseEvent.connect(onMouseUp);
Script.scriptEnding.connect(onScriptEnd);

78
examples/gridTest.js Normal file
View file

@ -0,0 +1,78 @@
//
// Created by Philip Rosedale on July 28, 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
//
// Creates a rectangular grid of objects, starting at the origin and proceeding along the X/Z plane.
// Useful for testing the rendering, LOD, and octree storage aspects of the system.
//
// Note that when creating things quickly, the entity server will ignore data if we send updates too quickly.
// like Internet MTU, these rates are set by th domain operator, so in this script there is a RATE_PER_SECOND
// variable letting you set this speed. If entities are missing from the grid after a relog, this number
// being too high may be the reason.
var SIZE = 10.0;
var SEPARATION = 20.0;
var ROWS_X = 30;
var ROWS_Z = 30;
var TYPE = "Sphere"; // Right now this can be "Box" or "Model" or "Sphere"
var MODEL_URL = "https://hifi-public.s3.amazonaws.com/models/props/LowPolyIsland/CypressTreeGroup.fbx";
var MODEL_DIMENSION = { x: 33, y: 16, z: 49 };
var RATE_PER_SECOND = 1000; // The entity server will drop data if we create things too fast.
var SCRIPT_INTERVAL = 100;
var LIFETIME = 600; // By default, these entities will live in the server for 10 minutes
var addRandom = false;
var x = 0;
var z = 0;
var totalCreated = 0;
Script.setInterval(function () {
if (!Entities.serversExist() || !Entities.canRez()) {
return;
}
var numToCreate = RATE_PER_SECOND * (SCRIPT_INTERVAL / 1000.0);
for (var i = 0; i < numToCreate; i++) {
var position = { x: SIZE + (x * SEPARATION), y: SIZE, z: SIZE + (z * SEPARATION) };
if (TYPE == "Model") {
Entities.addEntity({
type: TYPE,
name: "gridTest",
modelURL: MODEL_URL,
position: position,
dimensions: MODEL_DIMENSION,
ignoreCollisions: true,
collisionsWillMove: false,
lifetime: LIFETIME
});
} else {
Entities.addEntity({
type: TYPE,
name: "gridTest",
position: position,
dimensions: { x: SIZE, y: SIZE, z: SIZE },
color: { red: x / ROWS_X * 255, green: 50, blue: z / ROWS_Z * 255 },
ignoreCollisions: true,
collisionsWillMove: false,
lifetime: LIFETIME
});
}
totalCreated++;
x++;
if (x == ROWS_X) {
x = 0;
z++;
print("Created: " + totalCreated);
}
if (z == ROWS_Z) {
Script.stop();
}
}
}, SCRIPT_INTERVAL);

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var MOVE_DISTANCE = 10.0;
var MOVE_DISTANCE = 2.0;
var PITCH_INCREMENT = 0.5; // degrees
var pitchChange = 0; // degrees
var YAW_INCREMENT = 0.5; // degrees

View file

@ -0,0 +1,455 @@
//
// overlayManager.js
// examples/libraries
//
// Created by Zander Otavka on 7/24/15
// Copyright 2015 High Fidelity, Inc.
//
// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods.
// Instead of:
//
// var billboard = Overlays.addOverlay("billboard", { visible: false });
// ...
// Overlays.editOverlay(billboard, { visible: true });
// ...
// Overlays.deleteOverlay(billboard);
//
// You can now do:
//
// var billboard = new BillboardOverlay({ visible: false });
// ...
// billboard.visible = true;
// ...
// billboard.destroy();
//
// See more on usage below.
//
// Note that including this file will delete Overlays from the global scope. All the
// functionality of Overlays is represented here, just better. If you try to use Overlays in
// tandem, there may be performance problems or nasty surprises.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
// Delete `Overlays` from the global scope.
var Overlays = this.Overlays;
delete this.Overlays;
var overlays = {};
var panels = {};
var overlayTypes;
var Overlay, Overlay2D, Base3DOverlay, Planar3DOverlay, Volume3DOverlay;
//
// Create a new JavaScript object for an overlay of given ID.
//
function makeOverlayFromId(id) {
var type = Overlays.getOverlayType(id);
if (!type) {
return null;
}
var overlay = new overlayTypes[type]();
overlay._id = id;
var panelID = Overlays.getAttachedPanel(id)
if (panelID && panelID in panels) {
panels[panelID].addChild(overlay);
}
overlays[id] = overlay;
return overlay;
}
//
// Get or create an overlay object from the id.
//
// @param knownOverlaysOnly (Optional: Boolean)
// If true, a new object will not be created.
// @param searchList (Optional: Object)
// Map of overlay id's and overlay objects. Can be generated with
// `OverlayManager.makeSearchList`.
//
function findOverlay(id, knownOverlaysOnly, searchList) {
if (id > 0) {
knownOverlaysOnly = Boolean(knownOverlaysOnly) || Boolean(searchList);
searchList = searchList || overlays;
var foundOverlay = searchList[id];
if (foundOverlay) {
return foundOverlay;
}
if (!knownOverlaysOnly) {
return makeOverlayFromId(id);
}
}
return null;
}
//
// Perform global scoped operations on overlays, such as finding by ray intersection.
//
OverlayManager = {
findOnRay: function(pickRay, knownOverlaysOnly, searchList) {
var rayPickResult = Overlays.findRayIntersection(pickRay);
print("raypick " + rayPickResult.overlayID);
if (rayPickResult.intersects) {
return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList);
}
return null;
},
findAtPoint: function(point, knownOverlaysOnly, searchList) {
var foundID = Overlays.getOverlayAtPoint(point);
print("at point " + foundID);
if (foundID) {
return findOverlay(foundID, knownOverlaysOnly, searchList);
} else {
var pickRay = Camera.computePickRay(point.x, point.y);
return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList);
}
},
makeSearchList: function(overlayArray) {
var searchList = {};
overlayArray.forEach(function(overlay){
searchList[overlay._id] = overlay;
});
return searchList;
}
};
//
// Object oriented abstraction layer for overlays.
//
// Usage:
// // Create an overlay
// var billboard = new BillboardOverlay({
// visible: true,
// isFacingAvatar: true,
// ignoreRayIntersections: false
// });
//
// // Get a property
// var isVisible = billboard.visible;
//
// // Set a single property
// billboard.position = { x: 1, y: 3, z: 2 };
//
// // Set multiple properties at the same time
// billboard.setProperties({
// url: "http://images.com/overlayImage.jpg",
// dimensions: { x: 2, y: 2 }
// });
//
// // Clone an overlay
// var clonedBillboard = billboard.clone();
//
// // Remove an overlay from the world
// billboard.destroy();
//
// // Remember, there is a poor orphaned JavaScript object left behind. You should
// // remove any references to it so you don't accidentally try to modify an overlay that
// // isn't there.
// billboard = undefined;
//
(function() {
var ABSTRACT = null;
overlayTypes = {};
function generateOverlayClass(superclass, type, properties) {
var that;
if (type == ABSTRACT) {
that = function(type, params) {
superclass.call(this, type, params);
};
} else {
that = function(params) {
superclass.call(this, type, params);
};
overlayTypes[type] = that;
}
that.prototype = new superclass();
that.prototype.constructor = that;
properties.forEach(function(prop) {
Object.defineProperty(that.prototype, prop, {
get: function() {
return Overlays.getProperty(this._id, prop);
},
set: function(newValue) {
var keyValuePair = {};
keyValuePair[prop] = newValue;
this.setProperties(keyValuePair);
},
configurable: false
});
});
return that;
}
// Supports multiple inheritance of properties. Just `concat` them onto the end of the
// properties list.
var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "facingRotation"];
Overlay = (function() {
var that = function(type, params) {
if (type && params) {
this._id = Overlays.addOverlay(type, params);
overlays[this._id] = this;
} else {
this._id = 0;
}
this._attachedPanelPointer = null;
};
that.prototype.constructor = that;
Object.defineProperty(that.prototype, "isLoaded", {
get: function() {
return Overlays.isLoaded(this._id);
}
});
Object.defineProperty(that.prototype, "attachedPanel", {
get: function() {
return this._attachedPanelPointer;
}
});
that.prototype.getTextSize = function(text) {
return Overlays.textSize(this._id, text);
};
that.prototype.setProperties = function(properties) {
Overlays.editOverlay(this._id, properties);
};
that.prototype.clone = function() {
return makeOverlayFromId(Overlays.cloneOverlay(this._id));
};
that.prototype.destroy = function() {
Overlays.deleteOverlay(this._id);
};
return generateOverlayClass(that, ABSTRACT, [
"alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse",
"alphaPulse", "colorPulse", "visible", "anchor"
]);
})();
Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [
"bounds", "x", "y", "width", "height"
]);
Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [
"position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine",
"ignoreRayIntersection", "drawInFront", "drawOnHUD"
]);
Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
"dimensions"
]);
Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
"dimensions"
]);
generateOverlayClass(Overlay2D, "image", [
"subImage", "imageURL"
]);
generateOverlayClass(Overlay2D, "text", [
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
]);
generateOverlayClass(Planar3DOverlay, "text3d", [
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
"rightMargin", "bottomMargin", "isFacingAvatar"
]);
generateOverlayClass(Volume3DOverlay, "cube", [
"borderSize"
]);
generateOverlayClass(Volume3DOverlay, "sphere", [
]);
generateOverlayClass(Planar3DOverlay, "circle3d", [
"startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks",
"majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength",
"minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor"
]);
generateOverlayClass(Planar3DOverlay, "rectangle3d", [
]);
generateOverlayClass(Base3DOverlay, "line3d", [
"start", "end"
]);
generateOverlayClass(Planar3DOverlay, "grid", [
"minorGridWidth", "majorGridEvery"
]);
generateOverlayClass(Volume3DOverlay, "localmodels", [
]);
generateOverlayClass(Volume3DOverlay, "model", [
"url", "dimensions", "textures"
]);
generateOverlayClass(Planar3DOverlay, "billboard", [
"url", "subImage", "isFacingAvatar"
].concat(PANEL_ATTACHABLE_FIELDS));
})();
ImageOverlay = overlayTypes["image"];
TextOverlay = overlayTypes["text"];
Text3DOverlay = overlayTypes["text3d"];
Cube3DOverlay = overlayTypes["cube"];
Sphere3DOverlay = overlayTypes["sphere"];
Circle3DOverlay = overlayTypes["circle3d"];
Rectangle3DOverlay = overlayTypes["rectangle3d"];
Line3DOverlay = overlayTypes["line3d"];
Grid3DOverlay = overlayTypes["grid"];
LocalModelsOverlay = overlayTypes["localmodels"];
ModelOverlay = overlayTypes["model"];
BillboardOverlay = overlayTypes["billboard"];
//
// Object oriented abstraction layer for panels.
//
FloatingUIPanel = (function() {
var that = function(params) {
this._id = Overlays.addPanel(params);
this._children = [];
this._visible = Boolean(params.visible);
panels[this._id] = this;
this._attachedPanelPointer = null;
};
that.prototype.constructor = that;
var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"];
FIELDS.forEach(function(prop) {
Object.defineProperty(that.prototype, prop, {
get: function() {
return Overlays.getPanelProperty(this._id, prop);
},
set: function(newValue) {
var keyValuePair = {};
keyValuePair[prop] = newValue;
this.setProperties(keyValuePair);
},
configurable: false
});
});
var PSEUDO_FIELDS = [];
PSEUDO_FIELDS.push("children");
Object.defineProperty(that.prototype, "children", {
get: function() {
return this._children.slice();
}
});
PSEUDO_FIELDS.push("visible");
Object.defineProperty(that.prototype, "visible", {
get: function() {
return this._visible;
},
set: function(visible) {
this._visible = visible;
this._children.forEach(function(child) {
child.visible = visible;
});
}
});
that.prototype.addChild = function(child) {
if (child instanceof Overlay) {
Overlays.setAttachedPanel(child._id, this._id);
} else if (child instanceof FloatingUIPanel) {
child.setProperties({
anchorPosition: {
bind: "panel",
value: this._id
},
offsetRotation: {
bind: "panel",
value: this._id
}
});
}
child._attachedPanelPointer = this;
child.visible = this.visible;
this._children.push(child);
return child;
};
that.prototype.removeChild = function(child) {
var i = this._children.indexOf(child);
if (i >= 0) {
if (child instanceof Overlay) {
Overlays.setAttachedPanel(child._id, 0);
} else if (child instanceof FloatingUIPanel) {
child.setProperties({
anchorPosition: {
bind: "myAvatar"
},
offsetRotation: {
bind: "myAvatar"
}
});
}
child._attachedPanelPointer = null;
this._children.splice(i, 1);
}
};
that.prototype.setProperties = function(properties) {
for (var i in PSEUDO_FIELDS) {
if (properties[PSEUDO_FIELDS[i]] !== undefined) {
this[PSEUDO_FIELDS[i]] = properties[PSEUDO_FIELDS[i]];
}
}
Overlays.editPanel(this._id, properties);
};
that.prototype.destroy = function() {
Overlays.deletePanel(this._id);
};
return that;
})();
function onOverlayDeleted(id) {
if (id in overlays) {
if (overlays[id]._attachedPanelPointer) {
overlays[id]._attachedPanelPointer.removeChild(overlays[id]);
}
delete overlays[id];
}
}
function onPanelDeleted(id) {
if (id in panels) {
panels[id]._children.forEach(function(child) {
print(JSON.stringify(child.destroy));
child.destroy();
});
delete panels[id];
}
}
Overlays.overlayDeleted.connect(onOverlayDeleted);
Overlays.panelDeleted.connect(onPanelDeleted);
})();

View file

@ -1,7 +1,17 @@
/**
* OverlayGroup provides a way to create composite overlays and control their
* position relative to a settable rootPosition and rootRotation.
*/
//
// overlayUtils.js
// examples/libraries
//
// Copyright 2015 High Fidelity, Inc.
//
//
// DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel.
//
// OverlayGroup provides a way to create composite overlays and control their
// position relative to a settable rootPosition and rootRotation.
//
OverlayGroup = function(opts) {
var that = {};
@ -59,6 +69,6 @@ OverlayGroup = function(opts) {
}
overlays = {};
}
return that;
};

View file

@ -1,3 +1,3 @@
The Mac version does not require any files. The 3D Connexion driver should be installed from http://www.3dconnexion.eu/service/drivers.html
For Windows the provided header file is required: Inc/I3dMouseParams.h
For Windows the provided header file is required: include/I3dMouseParams.h

View file

@ -0,0 +1,64 @@
name = defaultAvatar_full
type = body+head
scale = 1
filename = defaultAvatar_full/defaultAvatar_full.fbx
texdir = defaultAvatar_full/textures
joint = jointNeck = Head
joint = jointLeftHand = LeftHand
joint = jointRoot = Hips
joint = jointHead = HeadTop_End
joint = jointRightHand = RightHand
joint = jointLean = Spine
freeJoint = LeftArm
freeJoint = LeftForeArm
freeJoint = RightArm
freeJoint = RightForeArm
jointIndex = LeftHand = 35
jointIndex = Reye = 3
jointIndex = Hips = 10
jointIndex = LeftHandIndex1 = 36
jointIndex = LeftHandIndex2 = 37
jointIndex = LeftHandIndex3 = 38
jointIndex = LeftHandIndex4 = 39
jointIndex = LeftShoulder = 32
jointIndex = RightLeg = 12
jointIndex = Grp_blendshapes = 0
jointIndex = Leye = 4
jointIndex = headphone = 8
jointIndex = RightForeArm = 26
jointIndex = Spine = 21
jointIndex = LeftFoot = 18
jointIndex = RightToeBase = 14
jointIndex = face = 1
jointIndex = LeftToe_End = 20
jointIndex = Spine1 = 22
jointIndex = body = 9
jointIndex = Spine2 = 23
jointIndex = RightUpLeg = 11
jointIndex = top1 = 7
jointIndex = Neck = 40
jointIndex = HeadTop_End = 42
jointIndex = RightShoulder = 24
jointIndex = RightArm = 25
jointIndex = Head = 41
jointIndex = LeftLeg = 17
jointIndex = LeftForeArm = 34
jointIndex = hair = 6
jointIndex = RightHand = 27
jointIndex = LeftToeBase = 19
jointIndex = LeftUpLeg = 16
jointIndex = mouth = 2
jointIndex = RightFoot = 13
jointIndex = LeftArm = 33
jointIndex = shield = 5
jointIndex = RightHandIndex1 = 28
jointIndex = RightHandIndex2 = 29
jointIndex = RightToe_End = 15
jointIndex = RightHandIndex3 = 30
jointIndex = RightHandIndex4 = 31
ry = 0
rz = 0
tx = 0
ty = 0
tz = 0
rx = 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -660,7 +660,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_myAvatar->updateStandingHMDModeFromMenu();
// the 3Dconnexion device wants to be initiliazed after a window is displayed.
ConnexionClient::init();
ConnexionClient::getInstance().init();
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
@ -779,7 +779,7 @@ Application::~Application() {
Leapmotion::destroy();
RealSense::destroy();
ConnexionClient::destroy();
ConnexionClient::getInstance().destroy();
qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
}
@ -936,14 +936,15 @@ void Application::paintGL() {
{
float ratio = ((float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale());
auto mirrorViewport = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio);
auto mirrorViewportDest = mirrorViewport;
// Flip the src and destination rect horizontally to do the mirror
auto mirrorRect = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio);
auto mirrorRectDest = glm::ivec4(mirrorRect.z, mirrorRect.y, mirrorRect.x, mirrorRect.w);
auto selfieFbo = DependencyManager::get<FramebufferCache>()->getSelfieFramebuffer();
gpu::Batch batch;
batch.setFramebuffer(selfieFbo);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
batch.blit(primaryFbo, mirrorViewport, selfieFbo, mirrorViewportDest);
batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest);
batch.setFramebuffer(nullptr);
renderArgs._context->render(batch);
}
@ -2047,6 +2048,7 @@ void Application::setActiveFaceTracker() {
#ifdef HAVE_DDE
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
Menu::getInstance()->getActionForOption(MenuOption::BinaryEyelidControl)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::CoupleEyelids)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setVisible(isUsingDDE);
@ -3762,10 +3764,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue);

View file

@ -50,6 +50,7 @@
#include "avatar/MyAvatar.h"
#include <input-plugins/KeyboardMouseDevice.h>
#include "scripting/ControllerScriptingInterface.h"
#include "scripting/DialogsManagerScriptingInterface.h"
#include "scripting/WebWindowClass.h"
#include "ui/AudioStatsDialog.h"
#include "ui/BandwidthDialog.h"
@ -69,6 +70,8 @@
#include "DisplayPlugins.h"
#include "InputPlugins.h"
#include "gpu/Context.h"
#include "render/Engine.h"
class QGLWidget;
@ -670,6 +673,7 @@ private:
bool _overlayEnabled = true;
QRect _savedGeometry;
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
};
#endif // hifi_Application_h

View file

@ -414,6 +414,8 @@ Menu::Menu() {
faceTrackingMenu->addSeparator();
QAction* binaryEyelidControl = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::BinaryEyelidControl, 0, true);
binaryEyelidControl->setVisible(true); // DDE face tracking is on by default
QAction* coupleEyelids = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::CoupleEyelids, 0, true);
coupleEyelids->setVisible(true); // DDE face tracking is on by default
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
useAudioForMouth->setVisible(true); // DDE face tracking is on by default
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);

View file

@ -164,6 +164,7 @@ namespace MenuOption {
const QString ControlWithSpeech = "Control With Speech";
const QString CopyAddress = "Copy Address to Clipboard";
const QString CopyPath = "Copy Path to Clipboard";
const QString CoupleEyelids = "Couple Eyelids";
const QString DebugAmbientOcclusion = "Debug Ambient Occlusion";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DeleteBookmark = "Delete Bookmark...";

View file

@ -137,7 +137,7 @@ void AudioScope::render(RenderArgs* renderArgs, int width, int height) {
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch._glLineWidth(1.0f); // default
geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground);
geometryCache->renderGrid(batch, x, y, w, h, gridRows, gridCols, gridColor, _audioScopeGrid);
renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput);

View file

@ -45,6 +45,7 @@
#include "Util.h"
#include "world.h"
#include "InterfaceLogging.h"
#include "EntityRig.h"
using namespace std;
@ -75,9 +76,9 @@ namespace render {
}
}
Avatar::Avatar() :
Avatar::Avatar(RigPointer rig) :
AvatarData(),
_skeletonModel(this),
_skeletonModel(this, nullptr, rig),
_skeletonOffset(0.0f),
_bodyYawDelta(0.0f),
_positionDeltaAccumulator(0.0f),
@ -430,15 +431,17 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
}
}
/*
// TODO: re-implement these when we have more detailed avatar collision shapes
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderSkeleton) {
_skeletonModel.renderJointCollisionShapes(0.7f);
}
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
if (renderHead && shouldRenderHead(renderArgs)) {
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
}
*/
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderBounding && shouldRenderHead(renderArgs)) {
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
}
@ -793,33 +796,6 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
}
bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {
bool hit = _skeletonModel.findRayIntersection(intersection);
hit = getHead()->getFaceModel().findRayIntersection(intersection) || hit;
return hit;
}
bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) {
return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
// TODO: Andrew to fix: Temporarily disabling collisions against the head
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
}
bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
return _skeletonModel.findPlaneCollisions(plane, collisions) ||
getHead()->getFaceModel().findPlaneCollisions(plane, collisions);
}
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
// TODO: Andrew to fix: also collide against _skeleton
//bool collided = _skeletonModel.findCollisions(shapes, collisions);
Model& headModel = getHead()->getFaceModel();
//collided = headModel.findCollisions(shapes, collisions) || collided;
bool collided = headModel.findCollisions(shapes, collisions);
return collided;
}
void Avatar::setSkeletonOffset(const glm::vec3& offset) {
const float MAX_OFFSET_LENGTH = _scale * 0.5f;
float offsetLength = glm::length(offset);
@ -843,7 +819,7 @@ QVector<glm::quat> Avatar::getJointRotations() const {
}
QVector<glm::quat> jointRotations(_skeletonModel.getJointStateCount());
for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) {
_skeletonModel.getJointState(i, jointRotations[i]);
_skeletonModel.getJointRotation(i, jointRotations[i]);
}
return jointRotations;
}
@ -853,7 +829,7 @@ glm::quat Avatar::getJointRotation(int index) const {
return AvatarData::getJointRotation(index);
}
glm::quat rotation;
_skeletonModel.getJointState(index, rotation);
_skeletonModel.getJointRotation(index, rotation);
return rotation;
}
@ -960,8 +936,14 @@ void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL);
const QUrl DEFAULT_FULL_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst");
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst");
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
if (isMyAvatar()) {
_skeletonModel.setURL(_skeletonModelURL,
getUseFullAvatar() ? DEFAULT_FULL_MODEL_URL : DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
} else {
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
}
}
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
@ -977,7 +959,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (_unusedAttachments.size() > 0) {
model = _unusedAttachments.takeFirst();
} else {
model = new Model(this);
model = new Model(std::make_shared<EntityRig>(), this);
}
model->init();
_attachmentModels.append(model);
@ -1139,9 +1121,8 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
// virtual
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight());
shapeInfo.setOffset(_skeletonModel.getBoundingShapeOffset());
shapeInfo.setCapsuleY(_skeletonModel.getBoundingCapsuleRadius(), 0.5f * _skeletonModel.getBoundingCapsuleHeight());
shapeInfo.setOffset(_skeletonModel.getBoundingCapsuleOffset());
}
// virtual

View file

@ -72,7 +72,7 @@ class Avatar : public AvatarData {
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
public:
Avatar();
Avatar(RigPointer rig = nullptr);
~Avatar();
typedef render::Payload<AvatarData> Payload;
@ -110,26 +110,6 @@ public:
/// Returns the distance to use as a LOD parameter.
float getLODDistance() const;
bool findRayIntersection(RayIntersectionInfo& intersection) const;
/// \param shapes list of shapes to collide against avatar
/// \param collisions list to store collision results
/// \return true if at least one shape collided with avatar
bool findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions);
/// Checks for penetration between the a sphere and the avatar's models.
/// \param penetratorCenter the center of the penetration test sphere
/// \param penetratorRadius the radius of the penetration test sphere
/// \param collisions[out] a list to which collisions get appended
/// \return whether or not the sphere penetrated
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions);
/// Checks for penetration between the described plane and the avatar.
/// \param plane the penetration plane
/// \param collisions[out] a list to which collisions get appended
/// \return whether or not the plane penetrated
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
virtual bool isMyAvatar() const { return false; }
virtual QVector<glm::quat> getJointRotations() const;
@ -170,6 +150,7 @@ public:
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
virtual bool getUseFullAvatar() const { return false; }
/// Scales a world space position vector relative to the avatar position and scale
/// \param vector position to be scaled. Will store the result
@ -186,10 +167,8 @@ public:
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
friend class AvatarManager;
signals:
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
AvatarMotionState* getMotionState() { return _motionState; }
protected:
SkeletonModel _skeletonModel;
@ -218,7 +197,7 @@ protected:
glm::vec3 _worldUpDirection;
float _stringLength;
bool _moving; ///< set when position is changing
// protected methods...
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
@ -243,7 +222,7 @@ protected:
virtual void updateJointMappings();
render::ItemID _renderItemID;
private:
bool _initialized;
NetworkTexturePointer _billboardTexture;
@ -251,9 +230,9 @@ private:
bool _isLookAtTarget;
void renderBillboard(RenderArgs* renderArgs);
float getBillboardSize() const;
static int _jointConesID;
int _voiceSphereID;

View file

@ -35,6 +35,7 @@
#include "Menu.h"
#include "MyAvatar.h"
#include "SceneScriptingInterface.h"
#include "AvatarRig.h"
// 70 times per second - target is 60hz, but this helps account for any small deviations
// in the update loop
@ -65,7 +66,7 @@ AvatarManager::AvatarManager(QObject* parent) :
{
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
_myAvatar = std::make_shared<MyAvatar>();
_myAvatar = std::make_shared<MyAvatar>(std::make_shared<AvatarRig>());
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
@ -160,7 +161,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
}
AvatarSharedPointer AvatarManager::newSharedAvatar() {
return AvatarSharedPointer(std::make_shared<Avatar>());
return AvatarSharedPointer(std::make_shared<Avatar>(std::make_shared<AvatarRig>()));
}
// virtual
@ -178,11 +179,11 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
// protected
void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
AvatarMotionState* motionState= rawPointer->_motionState;
AvatarMotionState* motionState = rawPointer->getMotionState();
if (motionState) {
// clean up physics stuff
motionState->clearObjectBackPointer();
rawPointer->_motionState = nullptr;
rawPointer->setMotionState(nullptr);
_avatarMotionStates.remove(motionState);
_motionStatesToAdd.remove(motionState);
_motionStatesToDelete.push_back(motionState);
@ -306,7 +307,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
AvatarHash::iterator avatarItr = _avatarHash.find(id);
if (avatarItr != _avatarHash.end()) {
auto avatar = std::static_pointer_cast<Avatar>(avatarItr.value());
AvatarMotionState* motionState = avatar->_motionState;
AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) {
motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE);
} else {
@ -315,7 +316,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
if (shape) {
AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape);
avatar->_motionState = motionState;
avatar->setMotionState(motionState);
_motionStatesToAdd.insert(motionState);
_avatarMotionStates.insert(motionState);
}

View file

@ -16,13 +16,16 @@
#include "Head.h"
#include "Menu.h"
FaceModel::FaceModel(Head* owningHead) :
FaceModel::FaceModel(Head* owningHead, RigPointer rig) :
Model(rig, nullptr),
_owningHead(owningHead)
{
assert(_rig);
}
void FaceModel::simulate(float deltaTime, bool fullUpdate) {
updateGeometry();
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
glm::vec3 neckPosition;
if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) {
@ -35,67 +38,73 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
}
setRotation(neckParentRotation);
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale());
setPupilDilation(_owningHead->getPupilDilation());
setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients());
// FIXME - this is very expensive, we shouldn't do it if we don't have to
//invalidCalculatedMeshBoxes();
if (isActive()) {
setOffset(-_geometry->getFBXGeometry().neckPivot);
for (int i = 0; i < _rig->getJointStateCount(); i++) {
maybeUpdateNeckAndEyeRotation(i);
}
Model::simulateInternal(deltaTime);
}
}
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) {
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
_owningHead->getTorsoTwist(),
_owningHead->getFinalLeanSideways()));
pitchYawRoll -= lean;
state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
* joint.rotation, DEFAULT_PRIORITY);
_rig->setJointRotationInConstrainedFrame(index,
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
* joint.rotation, DEFAULT_PRIORITY);
}
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) {
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) {
// likewise with the eye joints
// NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual.
glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation();
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f));
glm::quat between = rotationBetween(front, lookAt);
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation, DEFAULT_PRIORITY);
_rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between),
-MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation, DEFAULT_PRIORITY);
}
void FaceModel::updateJointState(int index) {
JointState& state = _jointStates[index];
void FaceModel::maybeUpdateNeckAndEyeRotation(int index) {
const JointState& state = _rig->getJointState(index);
const FBXJoint& joint = state.getFBXJoint();
const FBXGeometry& geometry = _geometry->getFBXGeometry();
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) {
const JointState& parentState = _rig->getJointState(joint.parentIndex);
if (index == geometry.neckJointIndex) {
maybeUpdateNeckRotation(parentState, joint, state);
maybeUpdateNeckRotation(parentState, joint, index);
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
maybeUpdateEyeRotation(this, parentState, joint, state);
maybeUpdateEyeRotation(this, parentState, joint, index);
}
}
Model::updateJointState(index);
}
bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {

View file

@ -19,23 +19,23 @@ class Head;
/// A face formed from a linear mix of blendshapes according to a set of coefficients.
class FaceModel : public Model {
Q_OBJECT
public:
FaceModel(Head* owningHead);
FaceModel(Head* owningHead, RigPointer rig);
virtual void simulate(float deltaTime, bool fullUpdate = true);
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state);
virtual void updateJointState(int index);
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index);
void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index);
void maybeUpdateNeckAndEyeRotation(int index);
/// Retrieve the positions of up to two eye meshes.
/// \return whether or not both eye meshes were found
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
private:
Head* _owningHead;
};

View file

@ -40,65 +40,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
}
}
// We create a static CollisionList that is recycled for each collision test.
const float MAX_COLLISIONS_PER_AVATAR = 32;
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
if (!avatar || avatar == _owningAvatar) {
// don't collide hands against ourself (that is done elsewhere)
return;
}
const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel();
int jointIndices[2];
jointIndices[0] = skeletonModel.getLeftHandJointIndex();
jointIndices[1] = skeletonModel.getRightHandJointIndex();
for (size_t i = 0; i < 2; i++) {
int jointIndex = jointIndices[i];
if (jointIndex < 0) {
continue;
}
handCollisions.clear();
QVector<const Shape*> shapes;
skeletonModel.getHandShapes(jointIndex, shapes);
if (avatar->findCollisions(shapes, handCollisions)) {
glm::vec3 totalPenetration(0.0f);
glm::vec3 averageContactPoint;
for (int j = 0; j < handCollisions.size(); ++j) {
CollisionInfo* collision = handCollisions.getCollision(j);
totalPenetration += collision->_penetration;
averageContactPoint += collision->_contactPoint;
}
if (isMyHand) {
// our hand against other avatar
// TODO: resolve this penetration when we don't think the other avatar will yield
//palm.addToPenetration(averagePenetration);
} else {
// someone else's hand against MyAvatar
// TODO: submit collision info to MyAvatar which should lean accordingly
averageContactPoint /= (float)handCollisions.size();
avatar->applyCollision(averageContactPoint, totalPenetration);
CollisionInfo collision;
collision._penetration = totalPenetration;
collision._contactPoint = averageContactPoint;
emit avatar->collisionWithAvatar(avatar->getSessionUUID(), _owningAvatar->getSessionUUID(), collision);
}
}
}
}
void Hand::resolvePenetrations() {
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
palm.resolvePenetrations();
}
}
void Hand::render(RenderArgs* renderArgs, bool isMine) {
gpu::Batch& batch = *renderArgs->_batch;
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&

View file

@ -29,10 +29,6 @@
class Avatar;
const float HAND_PADDLE_OFFSET = 0.1f;
const float HAND_PADDLE_THICKNESS = 0.01f;
const float HAND_PADDLE_RADIUS = 0.15f;
class Hand : public HandData {
public:
Hand(Avatar* owningAvatar);
@ -40,10 +36,6 @@ public:
void simulate(float deltaTime, bool isMine);
void render(RenderArgs* renderArgs, bool isMine);
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
void resolvePenetrations();
private:
// disallow copies of the Hand, copy of owning Avatar is disallowed too
Hand(const Hand&);

View file

@ -23,7 +23,7 @@
#include "Util.h"
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "InterfaceLogging.h"
#include "AvatarRig.h"
using namespace std;
@ -58,11 +58,10 @@ Head::Head(Avatar* owningAvatar) :
_isLookingAtMe(false),
_lookingAtMeStarted(0),
_wasLastLookingAtMe(0),
_faceModel(this),
_faceModel(this, std::make_shared<AvatarRig>()),
_leftEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID()),
_rightEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID())
{
}
void Head::init() {
@ -407,7 +406,7 @@ void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition
auto& batch = *renderArgs->_batch;
auto transform = Transform{};
batch.setModelTransform(transform);
batch._glLineWidth(2.0f);
// FIXME: THe line width of 2.0f is not supported anymore, we ll need a workaround
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
deferredLighting->bindSimpleProgram(batch);

View file

@ -107,7 +107,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
EntityItemPointer item = _tree->findEntityByID(_entityID);
const Model* model = getModel(item);
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
if (isValid() && model != NULL && _jointIndex < (uint32_t)(model->getJointStateCount())) {
_lastRefDimension = item->getDimensions();
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);

View file

@ -29,7 +29,6 @@
#include <udt/PacketHeaders.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <ShapeCollider.h>
#include <SharedUtil.h>
#include <TextRenderer3D.h>
#include <UserActivityLogger.h>
@ -47,9 +46,6 @@
#include "Util.h"
#include "InterfaceLogging.h"
#include "gpu/GLBackend.h"
using namespace std;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
@ -77,8 +73,8 @@ const float MyAvatar::ZOOM_MIN = 0.5f;
const float MyAvatar::ZOOM_MAX = 25.0f;
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
MyAvatar::MyAvatar() :
Avatar(),
MyAvatar::MyAvatar(RigPointer rig) :
Avatar(rig),
_gravity(0.0f, 0.0f, 0.0f),
_wasPushing(false),
_isPushing(false),
@ -100,8 +96,6 @@ MyAvatar::MyAvatar() :
_eyeContactTarget(LEFT_EYE),
_realWorldFieldOfView("realWorldFieldOfView",
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
_firstPersonSkeletonModel(this),
_prevShouldDrawHead(true),
_hmdSensorMatrix(),
_hmdSensorOrientation(),
_hmdSensorPosition(),
@ -110,17 +104,14 @@ MyAvatar::MyAvatar() :
_standingHMDSensorMode(false),
_goToPending(false),
_goToPosition(),
_goToOrientation()
_goToOrientation(),
_rig(rig),
_prevShouldDrawHead(true)
{
_firstPersonSkeletonModel.setIsFirstPerson(true);
ShapeCollider::initDispatchTable();
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = 0.0f;
}
_skeletonModel.setEnableShapes(true);
// connect to AddressManager signal for location jumps
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
this, &MyAvatar::goToLocation);
@ -147,7 +138,6 @@ QByteArray MyAvatar::toByteArray() {
void MyAvatar::reset() {
_skeletonModel.reset();
_firstPersonSkeletonModel.reset();
getHead()->reset();
_targetVelocity = glm::vec3(0.0f);
@ -211,7 +201,6 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("skeleton");
_skeletonModel.simulate(deltaTime);
_firstPersonSkeletonModel.simulate(deltaTime);
}
if (!_skeletonModel.hasSkeleton()) {
@ -227,10 +216,10 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("joints");
// copy out the skeleton joints from the model
_jointData.resize(_skeletonModel.getJointStateCount());
_jointData.resize(_rig->getJointStateCount());
for (int i = 0; i < _jointData.size(); i++) {
JointData& data = _jointData[i];
data.valid = _skeletonModel.getJointState(i, data.rotation);
data.valid = _rig->getJointStateRotation(i, data.rotation);
}
}
@ -547,17 +536,6 @@ void MyAvatar::loadLastRecording() {
_player->loadRecording(_recorder->getRecording());
}
AnimationHandlePointer MyAvatar::addAnimationHandle() {
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
_animationHandles.append(handle);
return handle;
}
void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) {
handle->stop();
_animationHandles.removeOne(handle);
}
void MyAvatar::startAnimation(const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
if (QThread::currentThread() != thread()) {
@ -566,16 +544,7 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority,
Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
return;
}
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
handle->setURL(url);
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints);
handle->start();
_rig->startAnimation(url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints);
}
void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
@ -586,25 +555,7 @@ void MyAvatar::startAnimationByRole(const QString& role, const QString& url, flo
Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
return;
}
// check for a configured animation for the role
foreach (const AnimationHandlePointer& handle, _animationHandles) {
if (handle->getRole() == role) {
handle->start();
return;
}
}
// no joy; use the parameters provided
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
handle->setRole(role);
handle->setURL(url);
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints);
handle->start();
_rig->startAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints);
}
void MyAvatar::stopAnimationByRole(const QString& role) {
@ -612,11 +563,7 @@ void MyAvatar::stopAnimationByRole(const QString& role) {
QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role));
return;
}
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
if (handle->getRole() == role) {
handle->stop();
}
}
_rig->stopAnimationByRole(role);
}
void MyAvatar::stopAnimation(const QString& url) {
@ -624,11 +571,7 @@ void MyAvatar::stopAnimation(const QString& url) {
QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url));
return;
}
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
if (handle->getURL() == url) {
handle->stop();
}
}
_rig->stopAnimation(url);
}
AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) {
@ -639,7 +582,7 @@ AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) {
Q_ARG(const QString&, role));
return result;
}
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) {
if (handle->getRole() == role) {
result = handle->getAnimationDetails();
break;
@ -656,7 +599,7 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) {
Q_ARG(const QString&, url));
return result;
}
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) {
if (handle->getURL() == url) {
result = handle->getAnimationDetails();
break;
@ -702,9 +645,10 @@ void MyAvatar::saveData() {
settings.endArray();
settings.beginWriteArray("animationHandles");
for (int i = 0; i < _animationHandles.size(); i++) {
auto animationHandles = _rig->getAnimationHandles();
for (int i = 0; i < animationHandles.size(); i++) {
settings.setArrayIndex(i);
const AnimationHandlePointer& pointer = _animationHandles.at(i);
const AnimationHandlePointer& pointer = animationHandles.at(i);
settings.setValue("role", pointer->getRole());
settings.setValue("url", pointer->getURL());
settings.setValue("fps", pointer->getFPS());
@ -732,6 +676,12 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) {
return value;
}
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
Settings settings;
settings.setValue("enableRig", isEnabled);
_rig->setEnableRig(isEnabled);
}
void MyAvatar::loadData() {
Settings settings;
settings.beginGroup("Avatar");
@ -822,25 +772,19 @@ void MyAvatar::loadData() {
setAttachmentData(attachmentData);
int animationCount = settings.beginReadArray("animationHandles");
while (_animationHandles.size() > animationCount) {
_animationHandles.takeLast()->stop();
}
while (_animationHandles.size() < animationCount) {
addAnimationHandle();
}
_rig->deleteAnimations();
for (int i = 0; i < animationCount; i++) {
settings.setArrayIndex(i);
const AnimationHandlePointer& handle = _animationHandles.at(i);
handle->setRole(settings.value("role", "idle").toString());
handle->setURL(settings.value("url").toUrl());
handle->setFPS(loadSetting(settings, "fps", 30.0f));
handle->setPriority(loadSetting(settings, "priority", 1.0f));
handle->setLoop(settings.value("loop", true).toBool());
handle->setHold(settings.value("hold", false).toBool());
handle->setFirstFrame(settings.value("firstFrame", 0.0f).toFloat());
handle->setLastFrame(settings.value("lastFrame", INT_MAX).toFloat());
handle->setMaskedJoints(settings.value("maskedJoints").toStringList());
handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
_rig->addAnimationByRole(settings.value("role", "idle").toString(),
settings.value("url").toString(),
loadSetting(settings, "fps", 30.0f),
loadSetting(settings, "priority", 1.0f),
settings.value("loop", true).toBool(),
settings.value("hold", false).toBool(),
settings.value("firstFrame", 0.0f).toFloat(),
settings.value("lastFrame", INT_MAX).toFloat(),
settings.value("maskedJoints").toStringList(),
settings.value("startAutomatically", true).toBool());
}
settings.endArray();
@ -848,6 +792,7 @@ void MyAvatar::loadData() {
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
settings.endGroup();
_rig->setEnableRig(settings.value("enableRig").toBool());
}
void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
@ -1015,15 +960,15 @@ void MyAvatar::setJointRotations(QVector<glm::quat> jointRotations) {
void MyAvatar::setJointData(int index, const glm::quat& rotation) {
if (QThread::currentThread() == thread()) {
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
_skeletonModel.setJointState(index, true, rotation, SCRIPT_PRIORITY);
_rig->setJointState(index, true, rotation, SCRIPT_PRIORITY);
}
}
void MyAvatar::clearJointData(int index) {
if (QThread::currentThread() == thread()) {
// HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority
_skeletonModel.setJointState(index, false, glm::quat(), 0.0f);
_skeletonModel.clearJointAnimationPriority(index);
_rig->setJointState(index, false, glm::quat(), 0.0f);
_rig->clearJointAnimationPriority(index);
}
}
@ -1034,7 +979,7 @@ void MyAvatar::clearJointsData() {
void MyAvatar::clearJointAnimationPriorities() {
int numStates = _skeletonModel.getJointStateCount();
for (int i = 0; i < numStates; ++i) {
_skeletonModel.clearJointAnimationPriority(i);
_rig->clearJointAnimationPriority(i);
}
}
@ -1074,19 +1019,8 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
Avatar::setSkeletonModelURL(skeletonModelURL);
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
_billboardValid = false;
if (_useFullAvatar) {
_skeletonModel.setVisibleInScene(_prevShouldDrawHead, scene);
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst");
_firstPersonSkeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
_firstPersonSkeletonModel.setVisibleInScene(!_prevShouldDrawHead, scene);
} else {
_skeletonModel.setVisibleInScene(true, scene);
_firstPersonSkeletonModel.setVisibleInScene(false, scene);
_firstPersonSkeletonModel.reset();
}
_skeletonModel.setVisibleInScene(true, scene);
_headBoneSet.clear();
}
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
@ -1197,11 +1131,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
void MyAvatar::rebuildSkeletonBody() {
// compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius();
float height = 2.0f * (capsule.getHalfHeight() + radius);
float radius = _skeletonModel.getBoundingCapsuleRadius();
float height = _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius;
glm::vec3 corner(-radius, -0.5f * height, -radius);
corner += _skeletonModel.getBoundingShapeOffset();
corner += _skeletonModel.getBoundingCapsuleOffset();
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
_characterController.setLocalBoundingBox(corner, scale);
}
@ -1293,29 +1226,48 @@ void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene
}
}
void MyAvatar::initHeadBones() {
int neckJointIndex = -1;
if (_skeletonModel.getGeometry()) {
neckJointIndex = _skeletonModel.getGeometry()->getFBXGeometry().neckJointIndex;
}
if (neckJointIndex == -1) {
return;
}
_headBoneSet.clear();
std::queue<int> q;
q.push(neckJointIndex);
_headBoneSet.insert(neckJointIndex);
// fbxJoints only hold links to parents not children, so we have to do a bit of extra work here.
while (q.size() > 0) {
int jointIndex = q.front();
for (int i = 0; i < _skeletonModel.getJointStateCount(); i++) {
if (jointIndex == _skeletonModel.getParentJointIndex(i)) {
_headBoneSet.insert(i);
q.push(i);
}
}
q.pop();
}
}
void MyAvatar::preRender(RenderArgs* renderArgs) {
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
const bool shouldDrawHead = shouldRenderHead(renderArgs);
_skeletonModel.initWhenReady(scene);
if (_useFullAvatar) {
_firstPersonSkeletonModel.initWhenReady(scene);
if (_skeletonModel.initWhenReady(scene)) {
initHeadBones();
_skeletonModel.setCauterizeBoneSet(_headBoneSet);
}
if (shouldDrawHead != _prevShouldDrawHead) {
if (_useFullAvatar) {
if (shouldDrawHead) {
_skeletonModel.setVisibleInScene(true, scene);
_firstPersonSkeletonModel.setVisibleInScene(false, scene);
} else {
_skeletonModel.setVisibleInScene(false, scene);
_firstPersonSkeletonModel.setVisibleInScene(true, scene);
}
_skeletonModel.setCauterizeBones(!shouldDrawHead);
} else {
getHead()->getFaceModel().setVisibleInScene(shouldDrawHead, scene);
}
}
_prevShouldDrawHead = shouldDrawHead;
}

View file

@ -14,6 +14,7 @@
#include <SettingHandle.h>
#include <DynamicCharacterController.h>
#include <Rig.h>
#include "Avatar.h"
@ -35,14 +36,14 @@ class MyAvatar : public Avatar {
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
public:
MyAvatar();
MyAvatar(RigPointer rig);
~MyAvatar();
QByteArray toByteArray();
void reset();
void update(float deltaTime);
void simulate(float deltaTime);
void preRender(RenderArgs* renderArgs);
void updateFromTrackers(float deltaTime);
const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
@ -59,28 +60,21 @@ public:
// This is so the correct camera can be used for rendering.
void updateSensorToWorldMatrix();
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
// setters
void setLeanScale(float scale) { _leanScale = scale; }
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); }
// getters
float getLeanScale() const { return _leanScale; }
Q_INVOKABLE glm::vec3 getDefaultEyePosition() const;
bool getShouldRenderLocally() const { return _shouldRender; }
float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); }
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
AnimationHandlePointer addAnimationHandle();
void removeAnimationHandle(const AnimationHandlePointer& handle);
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _rig->getAnimationHandles(); }
AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); }
void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); }
/// Allows scripts to run animations.
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
bool hold = false, float firstFrame = 0.0f,
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
/// Stops an animation as identified by a URL.
Q_INVOKABLE void stopAnimation(const QString& url);
@ -88,14 +82,14 @@ public:
/// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom
/// animation for the role.
Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
/// Stops an animation identified by its role.
Q_INVOKABLE void stopAnimationByRole(const QString& role);
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
void clearJointAnimationPriorities();
Q_INVOKABLE void setEnableRigAnimations(bool isEnabled);
// get/set avatar data
void saveData();
@ -107,16 +101,10 @@ public:
// Set what driving keys are being pressed to control thrust levels
void clearDriveKeys();
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
void relayDriveKeysToCharacterController();
bool isMyAvatar() const { return true; }
eyeContactTarget getEyeContactTarget();
virtual int parseDataFromBuffer(const QByteArray& buffer);
static void sendKillAvatar();
Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; }
@ -129,6 +117,7 @@ public:
Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); }
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
void updateLookAtTargetAvatar();
void clearLookAtTargetAvatar();
@ -141,9 +130,10 @@ public:
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString());
Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString());
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName = QString(), const QString& bodyName = QString());
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL,
const QString& headName = QString(), const QString& bodyName = QString());
Q_INVOKABLE bool getUseFullAvatar() const { return _useFullAvatar; }
Q_INVOKABLE virtual bool getUseFullAvatar() const { return _useFullAvatar; }
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
Q_INVOKABLE const QUrl& getHeadURLFromPreferences() const { return _headURLFromPreferences; }
Q_INVOKABLE const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; }
@ -156,35 +146,17 @@ public:
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
virtual glm::vec3 getSkeletonPosition() const;
void updateLocalAABox();
DynamicCharacterController* getCharacterController() { return &_characterController; }
void clearJointAnimationPriorities();
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
QString getScriptedMotorFrame() const;
void setScriptedMotorVelocity(const glm::vec3& velocity);
void setScriptedMotorTimescale(float timescale);
void setScriptedMotorFrame(QString frame);
const QString& getCollisionSoundURL() {return _collisionSoundURL; }
void setCollisionSoundURL(const QString& url);
void clearScriptableSettings();
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
bool allowDuplicates = false, bool useSaved = true);
/// Renders a laser pointer for UI picking
void renderLaserPointers(gpu::Batch& batch);
glm::vec3 getLaserPointerTipPosition(const PalmData* palm);
const RecorderPointer getRecorder() const { return _recorder; }
const PlayerPointer getPlayer() const { return _player; }
glm::vec3 getLaserPointerTipPosition(const PalmData* palm);
float getBoomLength() const { return _boomLength; }
void setBoomLength(float boomLength) { _boomLength = boomLength; }
@ -230,8 +202,6 @@ public slots:
virtual void rebuildSkeletonBody();
signals:
void transformChanged();
void newCollisionSoundURL(const QUrl& url);
@ -241,6 +211,32 @@ private:
glm::vec3 getWorldBodyPosition() const;
glm::quat getWorldBodyOrientation() const;
QByteArray toByteArray();
void simulate(float deltaTime);
void updateFromTrackers(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
bool getShouldRenderLocally() const { return _shouldRender; }
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
bool isMyAvatar() const { return true; }
virtual int parseDataFromBuffer(const QByteArray& buffer);
virtual glm::vec3 getSkeletonPosition() const;
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
QString getScriptedMotorFrame() const;
void setScriptedMotorVelocity(const glm::vec3& velocity);
void setScriptedMotorTimescale(float timescale);
void setScriptedMotorFrame(QString frame);
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
bool allowDuplicates = false, bool useSaved = true);
void renderLaserPointers(gpu::Batch& batch);
const RecorderPointer getRecorder() const { return _recorder; }
const PlayerPointer getPlayer() const { return _player; }
bool cameraInsideHead() const;
@ -282,10 +278,6 @@ private:
bool _billboardValid;
float _oculusYawOffset;
QList<AnimationHandlePointer> _animationHandles;
bool _feetTouchFloor;
eyeContactTarget _eyeContactTarget;
RecorderPointer _recorder;
@ -301,6 +293,7 @@ private:
void updatePosition(float deltaTime);
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
void maybeUpdateBillboard();
void initHeadBones();
// Avatar Preferences
bool _useFullAvatar = false;
@ -312,8 +305,7 @@ private:
QString _bodyModelName;
QString _fullAvatarModelName;
// used for rendering when in first person view or when in an HMD.
SkeletonModel _firstPersonSkeletonModel;
RigPointer _rig;
bool _prevShouldDrawHead;
// cache of the current HMD sensor position and orientation
@ -334,6 +326,8 @@ private:
bool _goToPending;
glm::vec3 _goToPosition;
glm::quat _goToOrientation;
std::unordered_set<int> _headBoneSet;
};
#endif // hifi_MyAvatar_h

View file

@ -12,9 +12,7 @@
#include <glm/gtx/transform.hpp>
#include <QMultiMap>
#include <CapsuleShape.h>
#include <DeferredLightingEffect.h>
#include <SphereShape.h>
#include "Application.h"
#include "Avatar.h"
@ -24,29 +22,47 @@
#include "Util.h"
#include "InterfaceLogging.h"
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
Model(parent),
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
Model(rig, parent),
_triangleFanID(DependencyManager::get<GeometryCache>()->allocateID()),
_owningAvatar(owningAvatar),
_boundingShape(),
_boundingShapeLocalOffset(0.0f),
_boundingCapsuleLocalOffset(0.0f),
_boundingCapsuleRadius(0.0f),
_boundingCapsuleHeight(0.0f),
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)),
_headClipDistance(DEFAULT_NEAR_CLIP),
_isFirstPerson(false)
_headClipDistance(DEFAULT_NEAR_CLIP)
{
assert(_rig);
assert(_owningAvatar);
_enableShapes = true;
}
SkeletonModel::~SkeletonModel() {
}
void SkeletonModel::initJointStates(QVector<JointState> states) {
Model::initJointStates(states);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
int rootJointIndex = geometry.rootJointIndex;
int leftHandJointIndex = geometry.leftHandJointIndex;
int leftElbowJointIndex = leftHandJointIndex >= 0 ? geometry.joints.at(leftHandJointIndex).parentIndex : -1;
int leftShoulderJointIndex = leftElbowJointIndex >= 0 ? geometry.joints.at(leftElbowJointIndex).parentIndex : -1;
int rightHandJointIndex = geometry.rightHandJointIndex;
int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1;
int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1;
_boundingRadius = _rig->initJointStates(states, parentTransform,
rootJointIndex,
leftHandJointIndex,
leftElbowJointIndex,
leftShoulderJointIndex,
rightHandJointIndex,
rightElbowJointIndex,
rightShoulderJointIndex);
// Determine the default eye position for avatar scale = 1.0
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
if (0 <= headJointIndex && headJointIndex < _jointStates.size()) {
if (0 <= headJointIndex && headJointIndex < _rig->getJointStateCount()) {
glm::vec3 leftEyePosition, rightEyePosition;
getEyeModelPositions(leftEyePosition, rightEyePosition);
@ -66,14 +82,11 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
// the SkeletonModel override of updateJointState() will clear the translation part
// of its root joint and we need that done before we try to build shapes hence we
// recompute all joint transforms at this time.
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i);
for (int i = 0; i < _rig->getJointStateCount(); i++) {
_rig->updateJointState(i, parentTransform);
}
clearShapes();
if (_enableShapes) {
buildShapes();
}
buildShapes();
Extents meshExtents = getMeshExtents();
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
@ -84,7 +97,31 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
}
const float PALM_PRIORITY = DEFAULT_PRIORITY;
const float LEAN_PRIORITY = DEFAULT_PRIORITY;
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (_owningAvatar->isMyAvatar()) {
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
}
Model::updateRig(deltaTime, parentTransform);
if (_owningAvatar->isMyAvatar()) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
Rig::HeadParameters params;
params.leanSideways = _owningAvatar->getHead()->getFinalLeanSideways();
params.leanForward = _owningAvatar->getHead()->getFinalLeanSideways();
params.torsoTwist = _owningAvatar->getHead()->getTorsoTwist();
params.localHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInLocalFrame();
params.worldHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInWorldFrame();
params.eyeLookAt = _owningAvatar->getHead()->getCorrectedLookAtPosition();
params.eyeSaccade = _owningAvatar->getHead()->getSaccade();
params.leanJointIndex = geometry.leanJointIndex;
params.neckJointIndex = geometry.neckJointIndex;
params.leftEyeJointIndex = geometry.leftEyeJointIndex;
params.rightEyeJointIndex = geometry.rightEyeJointIndex;
_rig->updateFromHeadParameters(params);
}
}
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
setTranslation(_owningAvatar->getSkeletonPosition());
@ -141,49 +178,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
}
}
if (_isFirstPerson) {
cauterizeHead();
updateClusterMatrices();
}
_boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);
}
void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const {
if (jointIndex < 0 || jointIndex >= int(_shapes.size())) {
return;
}
if (jointIndex == getLeftHandJointIndex()
|| jointIndex == getRightHandJointIndex()) {
// get all shapes that have this hand as an ancestor in the skeleton heirarchy
const FBXGeometry& geometry = _geometry->getFBXGeometry();
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
int parentIndex = joint.parentIndex;
Shape* shape = _shapes[i];
if (i == jointIndex) {
// this shape is the hand
if (shape) {
shapes.push_back(shape);
}
if (parentIndex != -1 && _shapes[parentIndex]) {
// also add the forearm
shapes.push_back(_shapes[parentIndex]);
}
} else if (shape) {
while (parentIndex != -1) {
if (parentIndex == jointIndex) {
// this shape is a child of the hand
shapes.push_back(shape);
break;
}
parentIndex = geometry.joints[parentIndex].parentIndex;
}
}
}
}
}
void SkeletonModel::renderIKConstraints(gpu::Batch& batch) {
@ -202,7 +196,7 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
}
void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}
// NOTE: 'position' is in model-frame
@ -217,16 +211,20 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
if (forearmLength < EPSILON) {
return;
}
JointState& state = _jointStates[jointIndex];
glm::quat handRotation = state.getRotation();
glm::quat handRotation;
if (!_rig->getJointStateRotation(jointIndex, handRotation)) {
return;
}
// align hand with forearm
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
state.applyRotationDelta(rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY);
_rig->applyJointRotationDelta(jointIndex,
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
true, PALM_PRIORITY);
}
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
@ -246,85 +244,36 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation;
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
setHandPosition(jointIndex, palmPosition, palmRotation);
_rig->setHandPosition(jointIndex, palmPosition, palmRotation, extractUniformScale(_scale), PALM_PRIORITY);
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
setJointPosition(parentJointIndex, palmPosition + forearm,
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
JointState& parentState = _jointStates[parentJointIndex];
parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY);
_rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY);
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
_jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY);
_rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY);
} else {
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
}
}
void SkeletonModel::updateJointState(int index) {
if (index < 0 && index >= _jointStates.size()) {
return; // bail
}
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (index == geometry.leanJointIndex) {
maybeUpdateLeanRotation(parentState, state);
} else if (index == geometry.neckJointIndex) {
maybeUpdateNeckRotation(parentState, joint, state);
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
maybeUpdateEyeRotation(parentState, joint, state);
}
}
Model::updateJointState(index);
if (index == _geometry->getFBXGeometry().rootJointIndex) {
state.clearTransformTranslation();
}
}
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) {
if (!_owningAvatar->isMyAvatar()) {
return;
}
// get the rotation axes in joint space and use them to adjust the rotation
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
state.setRotationInConstrainedFrame(
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
* glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis)
* state.getFBXJoint().rotation, LEAN_PRIORITY);
}
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state);
}
void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
_owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, state);
}
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const float BASE_DIRECTION_SIZE = 0.3f;
float directionSize = BASE_DIRECTION_SIZE * extractUniformScale(_scale);
batch._glLineWidth(3.0f);
// FIXME: THe line width of 3.0f is not supported anymore, we ll need a workaround
do {
const FBXJoint& joint = geometry.joints.at(jointIndex);
const JointState& jointState = _jointStates.at(jointIndex);
const JointState& jointState = _rig->getJointState(jointIndex);
glm::vec3 position = _rotation * jointState.getPosition() + _translation;
glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _rotation * _jointStates.at(joint.parentIndex).getRotation();
glm::quat parentRotation = (joint.parentIndex == -1) ?
_rotation :
_rotation * _rig->getJointState(joint.parentIndex).getRotation();
float fanScale = directionSize * 0.75f;
Transform transform = Transform();
@ -401,72 +350,6 @@ void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointInde
geometryCache->renderLine(batch, position, pFront, blue, jointLineIDs._front);
}
void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation) {
// this algorithm is from sample code from sixense
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int elbowJointIndex = geometry.joints.at(jointIndex).parentIndex;
if (elbowJointIndex == -1) {
return;
}
int shoulderJointIndex = geometry.joints.at(elbowJointIndex).parentIndex;
glm::vec3 shoulderPosition;
if (!getJointPosition(shoulderJointIndex, shoulderPosition)) {
return;
}
// precomputed lengths
float scale = extractUniformScale(_scale);
float upperArmLength = geometry.joints.at(elbowJointIndex).distanceToParent * scale;
float lowerArmLength = geometry.joints.at(jointIndex).distanceToParent * scale;
// first set wrist position
glm::vec3 wristPosition = position;
glm::vec3 shoulderToWrist = wristPosition - shoulderPosition;
float distanceToWrist = glm::length(shoulderToWrist);
// prevent gimbal lock
if (distanceToWrist > upperArmLength + lowerArmLength - EPSILON) {
distanceToWrist = upperArmLength + lowerArmLength - EPSILON;
shoulderToWrist = glm::normalize(shoulderToWrist) * distanceToWrist;
wristPosition = shoulderPosition + shoulderToWrist;
}
// cosine of angle from upper arm to hand vector
float cosA = (upperArmLength * upperArmLength + distanceToWrist * distanceToWrist - lowerArmLength * lowerArmLength) /
(2 * upperArmLength * distanceToWrist);
float mid = upperArmLength * cosA;
float height = sqrt(upperArmLength * upperArmLength + mid * mid - 2 * upperArmLength * mid * cosA);
// direction of the elbow
glm::vec3 handNormal = glm::cross(rotation * glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow rotating with wrist
glm::vec3 relaxedNormal = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow pointing straight down
const float NORMAL_WEIGHT = 0.5f;
glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT);
bool rightHand = (jointIndex == geometry.rightHandJointIndex);
if (rightHand ? (finalNormal.y > 0.0f) : (finalNormal.y < 0.0f)) {
finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis)
}
glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal));
// ik solution
glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height;
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
JointState& shoulderState = _jointStates[shoulderJointIndex];
shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY);
JointState& elbowState = _jointStates[elbowJointIndex];
elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY);
JointState& handState = _jointStates[jointIndex];
handState.setRotationInBindFrame(rotation, PALM_PRIORITY);
}
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
return getJointPositionInWorldFrame(getLeftHandJointIndex(), position);
}
@ -523,7 +406,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP
glm::quat worldFrameRotation;
bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation);
if (success) {
neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation;
neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation;
}
return success;
}
@ -574,7 +457,7 @@ float VERY_BIG_MASS = 1.0e6f;
// virtual
void SkeletonModel::buildShapes() {
if (_geometry == NULL || _jointStates.isEmpty()) {
if (_geometry == NULL || _rig->jointStatesEmpty()) {
return;
}
@ -583,46 +466,12 @@ void SkeletonModel::buildShapes() {
// rootJointIndex == -1 if the avatar model has no skeleton
return;
}
float uniformScale = extractUniformScale(_scale);
const int numStates = _jointStates.size();
for (int i = 0; i < numStates; i++) {
JointState& state = _jointStates[i];
const FBXJoint& joint = state.getFBXJoint();
float radius = uniformScale * joint.boneRadius;
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
Shape::Type type = joint.shapeType;
int parentIndex = joint.parentIndex;
if (parentIndex == -1 || radius < EPSILON) {
type = INVALID_SHAPE;
} else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) {
// this shape is forced to be a sphere
type = SPHERE_SHAPE;
}
Shape* shape = NULL;
if (type == SPHERE_SHAPE) {
shape = new SphereShape(radius);
shape->setEntity(this);
} else if (type == CAPSULE_SHAPE) {
assert(parentIndex != -1);
shape = new CapsuleShape(radius, halfHeight);
shape->setEntity(this);
}
if (shape && parentIndex != -1) {
// always disable collisions between joint and its parent
disableCollisions(i, parentIndex);
}
_shapes.push_back(shape);
}
// This method moves the shapes to their default positions in Model frame.
computeBoundingShape(geometry);
}
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
// compute default joint transforms
int numStates = _jointStates.size();
assert(numStates == _shapes.size());
int numStates = _rig->getJointStateCount();
QVector<glm::mat4> transforms;
transforms.fill(glm::mat4(), numStates);
@ -632,190 +481,69 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
totalExtents.addPoint(glm::vec3(0.0f));
for (int i = 0; i < numStates; i++) {
// compute the default transform of this joint
JointState& state = _jointStates[i];
const JointState& state = _rig->getJointState(i);
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
transforms[i] = _jointStates[i].getTransform();
transforms[i] = _rig->getJointTransform(i);
} else {
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
* joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform;
}
// Each joint contributes its point to the bounding box
// Each joint contributes a sphere at its position
glm::vec3 axis(joint.boneRadius);
glm::vec3 jointPosition = extractTranslation(transforms[i]);
totalExtents.addPoint(jointPosition);
Shape* shape = _shapes[i];
if (!shape) {
continue;
}
// Each joint with a shape contributes to the totalExtents: a box
// that contains the sphere centered at the end of the joint with radius of the bone.
// TODO: skip hand and arm shapes for bounding box calculation
int type = shape->getType();
if (type == CAPSULE_SHAPE) {
// add the two furthest surface points of the capsule
CapsuleShape* capsule = static_cast<CapsuleShape*>(shape);
float radius = capsule->getRadius();
glm::vec3 axis(radius);
Extents shapeExtents;
shapeExtents.reset();
shapeExtents.addPoint(jointPosition + axis);
shapeExtents.addPoint(jointPosition - axis);
totalExtents.addExtents(shapeExtents);
} else if (type == SPHERE_SHAPE) {
float radius = shape->getBoundingRadius();
glm::vec3 axis(radius);
Extents shapeExtents;
shapeExtents.reset();
shapeExtents.addPoint(jointPosition + axis);
shapeExtents.addPoint(jointPosition - axis);
totalExtents.addExtents(shapeExtents);
}
totalExtents.addPoint(jointPosition + axis);
totalExtents.addPoint(jointPosition - axis);
}
// compute bounding shape parameters
// NOTE: we assume that the longest side of totalExtents is the yAxis...
glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum;
// ... and assume the radius is half the RMS of the X and Z sides:
float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
_boundingShape.setRadius(capsuleRadius);
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
_boundingCapsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
_boundingCapsuleHeight = diagonal.y - 2.0f * _boundingCapsuleRadius;
glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition();
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
_boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
_boundingRadius = 0.5f * glm::length(diagonal);
}
void SkeletonModel::resetShapePositionsToDefaultPose() {
// DEBUG method.
// Moves shapes to the joint default locations for debug visibility into
// how the bounding shape is computed.
if (!_geometry || _shapes.isEmpty()) {
// geometry or joints have not yet been created
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (geometry.joints.isEmpty()) {
return;
}
// The shapes are moved to their default positions in computeBoundingShape().
computeBoundingShape(geometry);
// Then we move them into world frame for rendering at the Model's location.
for (int i = 0; i < _shapes.size(); i++) {
Shape* shape = _shapes[i];
if (shape) {
shape->setTranslation(_translation + _rotation * shape->getTranslation());
shape->setRotation(_rotation * shape->getRotation());
}
}
_boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);
}
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {
const int BALL_SUBDIVISIONS = 10;
#if 0
if (_shapes.isEmpty()) {
// the bounding shape has not been properly computed
// so no need to render it
return;
}
auto geometryCache = DependencyManager::get<GeometryCache>();
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
Transform transform; // = Transform();
// draw a blue sphere at the capsule end point
glm::vec3 endPoint;
_boundingShape.getEndPoint(endPoint);
endPoint = endPoint + _translation;
transform.setTranslation(endPoint);
// draw a blue sphere at the capsule top point
glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f);
transform.setTranslation(topPoint);
batch.setModelTransform(transform);
deferredLighting->bindSimpleProgram(batch);
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
// draw a yellow sphere at the capsule start point
glm::vec3 startPoint;
_boundingShape.getStartPoint(startPoint);
startPoint = startPoint + _translation;
glm::vec3 axis = endPoint - startPoint;
transform.setTranslation(startPoint);
// draw a yellow sphere at the capsule bottom point
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f);
glm::vec3 axis = topPoint - bottomPoint;
transform.setTranslation(bottomPoint);
batch.setModelTransform(transform);
deferredLighting->bindSimpleProgram(batch);
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
glm::vec4(0.8f, 0.8f, 0.6f, alpha));
// draw a green cylinder between the two points
glm::vec3 origin(0.0f);
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(),
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingCapsuleRadius, _boundingCapsuleRadius,
glm::vec4(0.6f, 0.8f, 0.6f, alpha));
#endif
}
bool SkeletonModel::hasSkeleton() {
return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false;
}
void SkeletonModel::initHeadBones() {
_headBones.clear();
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
const int neckJointIndex = fbxGeometry.neckJointIndex;
std::queue<int> q;
q.push(neckJointIndex);
_headBones.push_back(neckJointIndex);
// fbxJoints only hold links to parents not children, so we have to do a bit of extra work here.
while (q.size() > 0) {
int jointIndex = q.front();
for (int i = 0; i < fbxGeometry.joints.size(); i++) {
const FBXJoint& fbxJoint = fbxGeometry.joints[i];
if (jointIndex == fbxJoint.parentIndex) {
_headBones.push_back(i);
q.push(i);
}
}
q.pop();
}
}
void SkeletonModel::invalidateHeadBones() {
_headBones.clear();
}
void SkeletonModel::cauterizeHead() {
if (isActive()) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const int neckJointIndex = geometry.neckJointIndex;
if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) {
// lazy init of headBones
if (_headBones.size() == 0) {
initHeadBones();
}
// preserve the translation for the neck
glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3];
glm::vec4 zero(0, 0, 0, 0);
for (const int &i : _headBones) {
JointState& joint = _jointStates[i];
glm::mat4 newXform(zero, zero, zero, trans);
joint.setTransform(newXform);
joint.setVisibleTransform(newXform);
}
}
}
}
void SkeletonModel::onInvalidate() {
invalidateHeadBones();
}

View file

@ -13,7 +13,6 @@
#define hifi_SkeletonModel_h
#include <CapsuleShape.h>
#include <Model.h>
class Avatar;
@ -22,73 +21,71 @@ class MuscleConstraint;
/// A skeleton loaded from a model.
class SkeletonModel : public Model {
Q_OBJECT
public:
SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL);
SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr);
~SkeletonModel();
virtual void initJointStates(QVector<JointState> states);
void simulate(float deltaTime, bool fullUpdate = true);
/// \param jointIndex index of hand joint
/// \param shapes[out] list in which is stored pointers to hand shapes
void getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const;
virtual void simulate(float deltaTime, bool fullUpdate = true);
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
void renderIKConstraints(gpu::Batch& batch);
/// Returns the index of the left hand joint, or -1 if not found.
int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; }
/// Returns the index of the right hand joint, or -1 if not found.
int getRightHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().rightHandJointIndex : -1; }
/// Retrieve the position of the left hand
/// \return true whether or not the position was found
bool getLeftHandPosition(glm::vec3& position) const;
/// Retrieve the position of the right hand
/// \return true whether or not the position was found
bool getRightHandPosition(glm::vec3& position) const;
/// Restores some fraction of the default position of the left hand.
/// \param fraction the fraction of the default position to restore
/// \return whether or not the left hand joint was found
bool restoreLeftHandPosition(float fraction = 1.0f, float priority = 1.0f);
/// Gets the position of the left shoulder.
/// \return whether or not the left shoulder joint was found
bool getLeftShoulderPosition(glm::vec3& position) const;
/// Returns the extended length from the left hand to its last free ancestor.
float getLeftArmLength() const;
/// Restores some fraction of the default position of the right hand.
/// \param fraction the fraction of the default position to restore
/// \return whether or not the right hand joint was found
bool restoreRightHandPosition(float fraction = 1.0f, float priority = 1.0f);
/// Gets the position of the right shoulder.
/// \return whether or not the right shoulder joint was found
bool getRightShoulderPosition(glm::vec3& position) const;
/// Returns the extended length from the right hand to its first free ancestor.
float getRightArmLength() const;
/// Returns the position of the head joint.
/// \return whether or not the head was found
bool getHeadPosition(glm::vec3& headPosition) const;
/// Returns the position of the neck joint.
/// \return whether or not the neck was found
bool getNeckPosition(glm::vec3& neckPosition) const;
bool getLocalNeckPosition(glm::vec3& neckPosition) const;
/// Returns the rotation of the neck joint's parent from default orientation
/// \return whether or not the neck was found
bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const;
/// Retrieve the positions of up to two eye meshes.
/// \return whether or not both eye meshes were found
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
@ -99,19 +96,14 @@ public:
void computeBoundingShape(const FBXGeometry& geometry);
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; }
void resetShapePositionsToDefaultPose(); // DEBUG method
float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; }
float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; }
const glm::vec3 getBoundingCapsuleOffset() const { return _boundingCapsuleLocalOffset; }
bool hasSkeleton();
float getHeadClipDistance() const { return _headClipDistance; }
void setIsFirstPerson(bool value) { _isFirstPerson = value; }
bool getIsFirstPerson() const { return _isFirstPerson; }
virtual void onInvalidate() override;
signals:
@ -125,26 +117,14 @@ protected:
/// \param jointIndex index of joint in model
/// \param position position of joint in model-frame
void applyHandPosition(int jointIndex, const glm::vec3& position);
void applyPalmData(int jointIndex, PalmData& palm);
/// Updates the state of the joint at the specified index.
virtual void updateJointState(int index);
void maybeUpdateLeanRotation(const JointState& parentState, JointState& state);
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
void cauterizeHead();
void initHeadBones();
void invalidateHeadBones();
private:
void renderJointConstraints(gpu::Batch& batch, int jointIndex);
void renderOrientationDirections(gpu::Batch& batch, int jointIndex,
void renderOrientationDirections(gpu::Batch& batch, int jointIndex,
glm::vec3 position, const glm::quat& orientation, float size);
struct OrientationLineIDs {
int _up;
int _front;
@ -153,24 +133,17 @@ private:
QHash<int, OrientationLineIDs> _jointOrientationLines;
int _triangleFanID;
/// \param jointIndex index of joint in model
/// \param position position of joint in model-frame
/// \param rotation rotation of joint in model-frame
void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation);
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
Avatar* _owningAvatar;
CapsuleShape _boundingShape;
glm::vec3 _boundingShapeLocalOffset;
glm::vec3 _boundingCapsuleLocalOffset;
float _boundingCapsuleRadius;
float _boundingCapsuleHeight;
glm::vec3 _defaultEyeModelPosition;
float _headClipDistance; // Near clip distance to use if no separate head model
bool _isFirstPerson;
std::vector<int> _headBones;
};
#endif // hifi_SkeletonModel_h

File diff suppressed because it is too large Load diff

View file

@ -24,9 +24,9 @@ class ConnexionClient : public QObject {
Q_OBJECT
public:
static ConnexionClient& getInstance();
static void init() {};
static void destroy() {};
static bool Is3dmouseAttached() { return false; };
void init() {};
void destroy() {};
bool Is3dmouseAttached() { return false; };
public slots:
void toggleConnexion(bool shouldEnable) {};
};
@ -34,7 +34,7 @@ public slots:
#ifdef HAVE_3DCONNEXIONCLIENT
// the windows connexion rawinput
#ifdef _WIN32
#ifdef Q_OS_WIN
#include "I3dMouseParams.h"
#include <QAbstractNativeEventFilter>
@ -46,7 +46,6 @@ public slots:
class MouseParameters : public I3dMouseParam {
public:
MouseParameters();
~MouseParameters();
// I3dmouseSensor interface
bool IsPanZoom() const;
@ -87,13 +86,12 @@ private:
class ConnexionClient : public QObject, public QAbstractNativeEventFilter {
Q_OBJECT
public:
ConnexionClient();
~ConnexionClient();
ConnexionClient() {};
static ConnexionClient& getInstance();
static void init();
static void destroy();
static bool Is3dmouseAttached();
void init();
void destroy();
bool Is3dmouseAttached();
ConnexionClient* client;
@ -107,7 +105,7 @@ public:
virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) Q_DECL_OVERRIDE
{
MSG* msg = static_cast< MSG * >(message);
return ConnexionClient::RawInputEventFilter(message, result);
return RawInputEventFilter(message, result);
}
public slots:
@ -121,7 +119,7 @@ signals:
private:
bool InitializeRawInput(HWND hwndTarget);
static bool RawInputEventFilter(void* msg, long* result);
bool RawInputEventFilter(void* msg, long* result);
void OnRawInput(UINT nInputCode, HRAWINPUT hRawInput);
UINT GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader);
@ -166,9 +164,9 @@ class ConnexionClient : public QObject {
Q_OBJECT
public:
static ConnexionClient& getInstance();
static void init();
static void destroy();
static bool Is3dmouseAttached();
void init();
void destroy();
bool Is3dmouseAttached();
public slots:
void toggleConnexion(bool shouldEnable);
};

View file

@ -564,6 +564,13 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
eyeCoefficients[1] = _filteredEyeBlinks[1];
}
// Couple eyelid values if configured - use the most "open" value for both
if (Menu::getInstance()->isOptionChecked(MenuOption::CoupleEyelids)) {
float eyeCoefficient = std::min(eyeCoefficients[0], eyeCoefficients[1]);
eyeCoefficients[0] = eyeCoefficient;
eyeCoefficients[1] = eyeCoefficient;
}
// Use EyeBlink values to control both EyeBlink and EyeOpen
if (eyeCoefficients[0] > 0) {
_coefficients[_leftBlinkIndex] = eyeCoefficients[0];

View file

@ -47,7 +47,6 @@ public:
bool isMuted() const { return _isMuted; }
void setIsMuted(bool isMuted) { _isMuted = isMuted; }
void toggleMute();
static float getEyeDeflection() { return _eyeDeflection.get(); }
static void setEyeDeflection(float eyeDeflection);
@ -57,6 +56,8 @@ signals:
public slots:
virtual void setEnabled(bool enabled) = 0;
void toggleMute();
bool getMuted() { return _isMuted; }
protected:
virtual ~FaceTracker() {};

View file

@ -25,7 +25,8 @@
ControllerScriptingInterface::ControllerScriptingInterface() :
_mouseCaptured(false),
_touchCaptured(false),
_wheelCaptured(false)
_wheelCaptured(false),
_actionsCaptured(false)
{
}

View file

@ -0,0 +1,26 @@
//
// DialogsManagerScriptingInterface.cpp
// interface/src/scripting
//
// Created by Zander Otavka on 7/17/15.
// 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 "DialogsManagerScriptingInterface.h"
#include <DependencyManager.h>
#include "ui/DialogsManager.h"
DialogsManagerScriptingInterface::DialogsManagerScriptingInterface() {
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarToggled,
this, &DialogsManagerScriptingInterface::addressBarToggled);
}
void DialogsManagerScriptingInterface::toggleAddressBar() {
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
"toggleAddressBar", Qt::QueuedConnection);
}

View file

@ -0,0 +1,29 @@
//
// DialogsManagerScriptingInterface.h
// interface/src/scripting
//
// Created by Zander Otavka on 7/17/15.
// 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
//
#ifndef hifi_DialogsManagerScriptInterface_h
#define hifi_DialogsManagerScriptInterface_h
#include <QObject>
class DialogsManagerScriptingInterface : public QObject {
Q_OBJECT
public:
DialogsManagerScriptingInterface();
public slots:
void toggleAddressBar();
signals:
void addressBarToggled();
};
#endif

View file

@ -156,7 +156,7 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
buttons->addWidget(remove);
connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle()));
_stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool)));
_stop->connect(_handle.get(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool)));
_stop->setEnabled(_handle->isRunning());
}

View file

@ -14,7 +14,6 @@
#include <avatar/AvatarManager.h>
#include <DeferredLightingEffect.h>
#include <GLMHelpers.h>
#include <gpu/GLBackend.h>
#include <gpu/GLBackendShared.h>
#include <FramebufferCache.h>
#include <GLMHelpers.h>
@ -32,7 +31,6 @@
#include "ui/AvatarInputs.h"
const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
static const float ORTHO_NEAR_CLIP = -1000.0f;
static const float ORTHO_FAR_CLIP = 1000.0f;
@ -137,8 +135,7 @@ void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) {
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch._glLineWidth(1.0f); // default
// Render the audio scope
DependencyManager::get<AudioScope>()->render(renderArgs, width, height);
}
@ -157,8 +154,7 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch._glLineWidth(1.0f); // default
// Render all of the Script based "HUD" aka 2D overlays.
// note: we call them HUD, as opposed to 2D, only because there are some cases of 3D HUD overlays, like the
// cameral controls for the edit.js
@ -195,6 +191,7 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
glm::vec2 texCoordMinCorner(0.0f, 0.0f);
glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight()));
geometryCache->useSimpleDrawPipeline(batch, true);
batch.setResourceTexture(0, selfieTexture);
geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
@ -247,7 +244,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
batch._glLineWidth(CONNECTION_STATUS_BORDER_LINE_WIDTH);
// FIXME: THe line width of CONNECTION_STATUS_BORDER_LINE_WIDTH is not supported anymore, we ll need a workaround
// TODO animate the disconnect border for some excitement while not connected?
//double usecs = usecTimestampNow();

View file

@ -36,6 +36,7 @@
void DialogsManager::toggleAddressBar() {
AddressBarDialog::toggle();
emit addressBarToggled();
}
void DialogsManager::toggleDiskCacheEditor() {

View file

@ -72,6 +72,9 @@ public slots:
// Application Update
void showUpdateDialog();
signals:
void addressBarToggled();
private slots:
void toggleToolWindow();
void hmdToolsClosed();

View file

@ -13,19 +13,25 @@
#include <QScriptValue>
#include <DeferredLightingEffect.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <gpu/Batch.h>
#include <GLMHelpers.h>
#include "Application.h"
#include "GeometryUtil.h"
QString const BillboardOverlay::TYPE = "billboard";
BillboardOverlay::BillboardOverlay() {
_isLoaded = false;
}
BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) :
Planar3DOverlay(billboardOverlay),
PanelAttachable(billboardOverlay),
_url(billboardOverlay->_url),
_texture(billboardOverlay->_texture),
_fromImage(billboardOverlay->_fromImage),
@ -33,6 +39,19 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) :
{
}
void BillboardOverlay::setTransforms(Transform& transform) {
PanelAttachable::setTransforms(transform);
if (_isFacingAvatar) {
glm::quat rotation = Application::getInstance()->getCamera()->getOrientation();
rotation *= glm::angleAxis(glm::pi<float>(), IDENTITY_UP);
setRotation(rotation);
}
}
void BillboardOverlay::update(float deltatime) {
setTransforms(_transform);
}
void BillboardOverlay::render(RenderArgs* args) {
if (!_texture) {
_isLoaded = true;
@ -43,15 +62,8 @@ void BillboardOverlay::render(RenderArgs* args) {
return;
}
glm::quat rotation;
if (_isFacingAvatar) {
// rotate about vertical to face the camera
rotation = args->_viewFrustum->getOrientation();
rotation *= glm::angleAxis(glm::pi<float>(), IDENTITY_UP);
rotation *= getRotation();
} else {
rotation = getRotation();
}
Q_ASSERT(args->_batch);
auto batch = args->_batch;
float imageWidth = _texture->getWidth();
float imageHeight = _texture->getHeight();
@ -86,25 +98,25 @@ void BillboardOverlay::render(RenderArgs* args) {
xColor color = getColor();
float alpha = getAlpha();
auto batch = args->_batch;
setTransforms(_transform);
Transform transform = _transform;
transform.postScale(glm::vec3(getDimensions(), 1.0f));
if (batch) {
Transform transform = _transform;
transform.postScale(glm::vec3(getDimensions(), 1.0f));
transform.setRotation(rotation);
batch->setModelTransform(transform);
batch->setResourceTexture(0, _texture->getGPUTexture());
DependencyManager::get<GeometryCache>()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
batch->setModelTransform(transform);
batch->setResourceTexture(0, _texture->getGPUTexture());
batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
}
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, true, false, true);
DependencyManager::get<GeometryCache>()->renderQuad(
*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)
);
batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
}
void BillboardOverlay::setProperties(const QScriptValue &properties) {
Planar3DOverlay::setProperties(properties);
PanelAttachable::setProperties(properties);
QScriptValue urlValue = properties.property("url");
if (urlValue.isValid()) {
@ -161,7 +173,14 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) {
if (property == "isFacingAvatar") {
return _isFacingAvatar;
}
if (property == "offsetPosition") {
return vec3toScriptValue(_scriptEngine, getOffsetPosition());
}
QScriptValue value = PanelAttachable::getProperty(_scriptEngine, property);
if (value.isValid()) {
return value;
}
return Planar3DOverlay::getProperty(property);
}
@ -175,15 +194,10 @@ void BillboardOverlay::setBillboardURL(const QString& url) {
}
bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face) {
float& distance, BoxFace& face) {
if (_texture && _texture->isLoaded()) {
glm::quat rotation = getRotation();
if (_isFacingAvatar) {
// rotate about vertical to face the camera
rotation = Application::getInstance()->getCamera()->getRotation();
rotation *= glm::angleAxis(glm::pi<float>(), glm::vec3(0.0f, 1.0f, 0.0f));
}
// Make sure position and rotation is updated.
setTransforms(_transform);
// Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale.
bool isNull = _fromImage.isNull();
@ -192,7 +206,7 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v
float maxSize = glm::max(width, height);
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
return findRayRectangleIntersection(origin, direction, rotation, getPosition(), dimensions, distance);
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance);
}
return false;

View file

@ -15,15 +15,21 @@
#include <TextureCache.h>
#include "Planar3DOverlay.h"
#include "PanelAttachable.h"
class BillboardOverlay : public Planar3DOverlay {
class BillboardOverlay : public Planar3DOverlay, public PanelAttachable {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
BillboardOverlay();
BillboardOverlay(const BillboardOverlay* billboardOverlay);
virtual void render(RenderArgs* args);
virtual void update(float deltatime);
// setters
void setURL(const QString& url);
void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; }
@ -36,9 +42,12 @@ public:
virtual BillboardOverlay* createClone() const;
protected:
virtual void setTransforms(Transform& transform);
private:
void setBillboardURL(const QString& url);
QString _url;
NetworkTexturePointer _texture;

View file

@ -15,6 +15,8 @@
#include <RegisteredMetaTypes.h>
QString const Circle3DOverlay::TYPE = "circle3d";
Circle3DOverlay::Circle3DOverlay() :
_startAt(0.0f),
_endAt(360.0f),
@ -99,8 +101,9 @@ void Circle3DOverlay::render(RenderArgs* args) {
Q_ASSERT(args->_batch);
auto& batch = *args->_batch;
batch._glLineWidth(_lineWidth);
// FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround
auto transform = _transform;
transform.postScale(glm::vec3(getDimensions(), 1.0f));
batch.setModelTransform(transform);

View file

@ -18,6 +18,9 @@ class Circle3DOverlay : public Planar3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
Circle3DOverlay();
Circle3DOverlay(const Circle3DOverlay* circle3DOverlay);

View file

@ -19,6 +19,8 @@
#include <GeometryCache.h>
#include <DependencyManager.h>
QString const Cube3DOverlay::TYPE = "cube";
Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) :
Volume3DOverlay(cube3DOverlay)
{

View file

@ -17,6 +17,9 @@ class Cube3DOverlay : public Volume3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
Cube3DOverlay() {}
Cube3DOverlay(const Cube3DOverlay* cube3DOverlay);

View file

@ -0,0 +1,197 @@
//
// FloatingUIPanel.cpp
// interface/src/ui/overlays
//
// Created by Zander Otavka on 7/2/15.
// Copyright 2014 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 "FloatingUIPanel.h"
#include <QVariant>
#include <RegisteredMetaTypes.h>
#include <DependencyManager.h>
#include "avatar/AvatarManager.h"
#include "avatar/MyAvatar.h"
#include "Application.h"
#include "Base3DOverlay.h"
std::function<glm::vec3()> const FloatingUIPanel::AVATAR_POSITION = []() -> glm::vec3 {
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition();
};
std::function<glm::quat()> const FloatingUIPanel::AVATAR_ORIENTATION = []() -> glm::quat {
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getOrientation() *
glm::angleAxis(glm::pi<float>(), IDENTITY_UP);
};
glm::vec3 FloatingUIPanel::getPosition() const {
return getOffsetRotation() * getOffsetPosition() + getAnchorPosition();
}
glm::quat FloatingUIPanel::getRotation() const {
return getOffsetRotation() * getFacingRotation();
}
void FloatingUIPanel::setAnchorPosition(const glm::vec3& position) {
setAnchorPosition([position]() -> glm::vec3 {
return position;
});
}
void FloatingUIPanel::setOffsetRotation(const glm::quat& rotation) {
setOffsetRotation([rotation]() -> glm::quat {
return rotation;
});
}
void FloatingUIPanel::addChild(unsigned int childId) {
if (!_children.contains(childId)) {
_children.append(childId);
}
}
void FloatingUIPanel::removeChild(unsigned int childId) {
if (_children.contains(childId)) {
_children.removeOne(childId);
}
}
QScriptValue FloatingUIPanel::getProperty(const QString &property) {
if (property == "anchorPosition") {
return vec3toScriptValue(_scriptEngine, getAnchorPosition());
}
if (property == "offsetRotation") {
return quatToScriptValue(_scriptEngine, getOffsetRotation());
}
if (property == "offsetPosition") {
return vec3toScriptValue(_scriptEngine, getOffsetPosition());
}
if (property == "facingRotation") {
return quatToScriptValue(_scriptEngine, getFacingRotation());
}
return QScriptValue();
}
void FloatingUIPanel::setProperties(const QScriptValue &properties) {
QScriptValue anchor = properties.property("anchorPosition");
if (anchor.isValid()) {
QScriptValue bindType = anchor.property("bind");
QScriptValue value = anchor.property("value");
if (bindType.isValid()) {
QString bindTypeString = bindType.toVariant().toString();
if (bindTypeString == "myAvatar") {
setAnchorPosition(AVATAR_POSITION);
} else if (value.isValid()) {
if (bindTypeString == "overlay") {
Overlay::Pointer overlay = Application::getInstance()->getOverlays()
.getOverlay(value.toVariant().toUInt());
if (overlay->is3D()) {
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
setAnchorPosition([&overlay3D]() -> glm::vec3 {
return overlay3D->getPosition();
});
}
} else if (bindTypeString == "panel") {
FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays()
.getPanel(value.toVariant().toUInt());
setAnchorPosition([panel]() -> glm::vec3 {
return panel->getPosition();
});
} else if (bindTypeString == "vec3") {
QScriptValue x = value.property("x");
QScriptValue y = value.property("y");
QScriptValue z = value.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newPosition;
newPosition.x = x.toVariant().toFloat();
newPosition.y = y.toVariant().toFloat();
newPosition.z = z.toVariant().toFloat();
setAnchorPosition(newPosition);
}
}
}
}
}
QScriptValue offsetRotation = properties.property("offsetRotation");
if (offsetRotation.isValid()) {
QScriptValue bindType = offsetRotation.property("bind");
QScriptValue value = offsetRotation.property("value");
if (bindType.isValid()) {
QString bindTypeString = bindType.toVariant().toString();
if (bindTypeString == "myAvatar") {
setOffsetRotation(AVATAR_ORIENTATION);
} else if (value.isValid()) {
if (bindTypeString == "overlay") {
Overlay::Pointer overlay = Application::getInstance()->getOverlays()
.getOverlay(value.toVariant().toUInt());
if (overlay->is3D()) {
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
setOffsetRotation([&overlay3D]() -> glm::quat {
return overlay3D->getRotation();
});
}
} else if (bindTypeString == "panel") {
FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays()
.getPanel(value.toVariant().toUInt());
setOffsetRotation([panel]() -> glm::quat {
return panel->getRotation();
});
} else if (bindTypeString == "quat") {
QScriptValue x = value.property("x");
QScriptValue y = value.property("y");
QScriptValue z = value.property("z");
QScriptValue w = value.property("w");
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
glm::quat newRotation;
newRotation.x = x.toVariant().toFloat();
newRotation.y = y.toVariant().toFloat();
newRotation.z = z.toVariant().toFloat();
newRotation.w = w.toVariant().toFloat();
setOffsetRotation(newRotation);
}
}
}
}
}
QScriptValue offsetPosition = properties.property("offsetPosition");
if (offsetPosition.isValid()) {
QScriptValue x = offsetPosition.property("x");
QScriptValue y = offsetPosition.property("y");
QScriptValue z = offsetPosition.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newPosition;
newPosition.x = x.toVariant().toFloat();
newPosition.y = y.toVariant().toFloat();
newPosition.z = z.toVariant().toFloat();
setOffsetPosition(newPosition);
}
}
QScriptValue facingRotation = properties.property("facingRotation");
if (facingRotation.isValid()) {
QScriptValue x = facingRotation.property("x");
QScriptValue y = facingRotation.property("y");
QScriptValue z = facingRotation.property("z");
QScriptValue w = facingRotation.property("w");
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
glm::quat newRotation;
newRotation.x = x.toVariant().toFloat();
newRotation.y = y.toVariant().toFloat();
newRotation.z = z.toVariant().toFloat();
newRotation.w = w.toVariant().toFloat();
setFacingRotation(newRotation);
}
}
}

View file

@ -0,0 +1,63 @@
//
// FloatingUIPanel.h
// interface/src/ui/overlays
//
// Created by Zander Otavka on 7/2/15.
// Copyright 2014 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_FloatingUIPanel_h
#define hifi_FloatingUIPanel_h
#include <functional>
#include <memory>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QScriptValue>
class FloatingUIPanel : public QObject {
Q_OBJECT
public:
typedef std::shared_ptr<FloatingUIPanel> Pointer;
void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; }
glm::vec3 getAnchorPosition() const { return _anchorPosition(); }
glm::quat getOffsetRotation() const { return _offsetRotation(); }
glm::vec3 getOffsetPosition() const { return _offsetPosition; }
glm::quat getFacingRotation() const { return _facingRotation; }
glm::vec3 getPosition() const;
glm::quat getRotation() const;
void setAnchorPosition(const std::function<glm::vec3()>& func) { _anchorPosition = func; }
void setAnchorPosition(const glm::vec3& position);
void setOffsetRotation(const std::function<glm::quat()>& func) { _offsetRotation = func; }
void setOffsetRotation(const glm::quat& rotation);
void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; }
void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; }
const QList<unsigned int>& getChildren() { return _children; }
void addChild(unsigned int childId);
void removeChild(unsigned int childId);
unsigned int popLastChild() { return _children.takeLast(); }
QScriptValue getProperty(const QString& property);
void setProperties(const QScriptValue& properties);
private:
static std::function<glm::vec3()> const AVATAR_POSITION;
static std::function<glm::quat()> const AVATAR_ORIENTATION;
std::function<glm::vec3()> _anchorPosition{AVATAR_POSITION};
std::function<glm::quat()> _offsetRotation{AVATAR_ORIENTATION};
glm::vec3 _offsetPosition{0, 0, 0};
glm::quat _facingRotation{1, 0, 0, 0};
QScriptEngine* _scriptEngine;
QList<unsigned int> _children;
};
#endif // hifi_FloatingUIPanel_h

View file

@ -18,6 +18,9 @@
#include <PathUtils.h>
#include <ViewFrustum.h>
QString const Grid3DOverlay::TYPE = "grid";
Grid3DOverlay::Grid3DOverlay() :
_minorGridWidth(1.0),
_majorGridEvery(5) {
@ -57,7 +60,6 @@ void Grid3DOverlay::render(RenderArgs* args) {
// Minor grid
{
batch->_glLineWidth(1.0f);
auto position = glm::vec3(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2),
getPosition().z);
@ -73,7 +75,6 @@ void Grid3DOverlay::render(RenderArgs* args) {
// Major grid
{
batch->_glLineWidth(4.0f);
spacing *= _majorGridEvery;
auto position = glm::vec3(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2),
@ -83,6 +84,8 @@ void Grid3DOverlay::render(RenderArgs* args) {
transform.setTranslation(position);
transform.setScale(scale);
// FIXME: THe line width of 4.0f is not supported anymore, we ll need a workaround
batch->setModelTransform(transform);
DependencyManager::get<GeometryCache>()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor);

View file

@ -18,6 +18,9 @@ class Grid3DOverlay : public Planar3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
Grid3DOverlay();
Grid3DOverlay(const Grid3DOverlay* grid3DOverlay);

View file

@ -16,6 +16,9 @@
#include <gpu/StandardShaderLib.h>
#include <RegisteredMetaTypes.h>
QString const ImageOverlay::TYPE = "image";
ImageOverlay::ImageOverlay() :
_imageURL(),
_renderImage(false),

View file

@ -24,6 +24,9 @@ class ImageOverlay : public Overlay2D {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
ImageOverlay();
ImageOverlay(const ImageOverlay* imageOverlay);

View file

@ -13,6 +13,9 @@
#include <GeometryCache.h>
#include <RegisteredMetaTypes.h>
QString const Line3DOverlay::TYPE = "line3d";
Line3DOverlay::Line3DOverlay() :
_geometryCacheID(DependencyManager::get<GeometryCache>()->allocateID())
{

View file

@ -17,6 +17,9 @@ class Line3DOverlay : public Base3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
Line3DOverlay();
Line3DOverlay(const Line3DOverlay* line3DOverlay);
~Line3DOverlay();

View file

@ -14,6 +14,9 @@
#include <EntityTreeRenderer.h>
#include <gpu/Batch.h>
QString const LocalModelsOverlay::TYPE = "localmodels";
LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) :
Volume3DOverlay(),
_entityTreeRenderer(entityTreeRenderer) {

View file

@ -19,6 +19,9 @@ class EntityTreeRenderer;
class LocalModelsOverlay : public Volume3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer);
LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay);

View file

@ -10,11 +10,15 @@
//
#include "ModelOverlay.h"
#include "EntityRig.h"
#include "Application.h"
QString const ModelOverlay::TYPE = "model";
ModelOverlay::ModelOverlay()
: _model(),
: _model(std::make_shared<EntityRig>()),
_modelTextures(QVariantMap()),
_updateModel(false)
{
@ -24,7 +28,7 @@ ModelOverlay::ModelOverlay()
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
Volume3DOverlay(modelOverlay),
_model(),
_model(std::make_shared<EntityRig>()),
_modelTextures(QVariantMap()),
_url(modelOverlay->_url),
_updateModel(false)

View file

@ -19,6 +19,9 @@
class ModelOverlay : public Volume3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
ModelOverlay();
ModelOverlay(const ModelOverlay* modelOverlay);

View file

@ -110,7 +110,8 @@ void Overlay::setProperties(const QScriptValue& properties) {
}
if (properties.property("visible").isValid()) {
setVisible(properties.property("visible").toVariant().toBool());
bool visible = properties.property("visible").toVariant().toBool();
setVisible(visible);
}
if (properties.property("anchor").isValid()) {

View file

@ -44,6 +44,7 @@ public:
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
// getters
virtual QString getType() const = 0;
virtual bool is3D() const = 0;
bool isLoaded() { return _isLoaded; }
bool getVisible() const { return _visible; }

View file

@ -15,7 +15,6 @@
#include <limits>
#include <render/Scene.h>
#include <gpu/GLBackend.h>
#include <RegisteredMetaTypes.h>
#include "Application.h"
@ -48,6 +47,7 @@ Overlays::~Overlays() {
}
_overlaysHUD.clear();
_overlaysWorld.clear();
_panels.clear();
}
cleanupOverlaysToDelete();
@ -118,12 +118,12 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
batch.setProjectionTransform(legacyProjection);
batch.setModelTransform(Transform());
batch.setViewTransform(Transform());
batch._glLineWidth(1.0f); // default
thisOverlay->render(renderArgs);
}
}
<<<<<<< HEAD
void Overlays::disable() {
QWriteLocker lock(&_lock);
_enabled = false;
@ -132,87 +132,86 @@ void Overlays::disable() {
void Overlays::enable() {
QWriteLocker lock(&_lock);
_enabled = true;
=======
Overlay::Pointer Overlays::getOverlay(unsigned int id) const {
if (_overlaysHUD.contains(id)) {
return _overlaysHUD[id];
}
if (_overlaysWorld.contains(id)) {
return _overlaysWorld[id];
}
return nullptr;
>>>>>>> master
}
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
unsigned int thisID = 0;
Overlay* thisOverlay = NULL;
bool created = true;
if (type == "image") {
thisOverlay = new ImageOverlay();
} else if (type == "text") {
thisOverlay = new TextOverlay();
} else if (type == "text3d") {
thisOverlay = new Text3DOverlay();
} else if (type == "cube") {
thisOverlay = new Cube3DOverlay();
} else if (type == "sphere") {
thisOverlay = new Sphere3DOverlay();
} else if (type == "circle3d") {
thisOverlay = new Circle3DOverlay();
} else if (type == "rectangle3d") {
thisOverlay = new Rectangle3DOverlay();
} else if (type == "line3d") {
thisOverlay = new Line3DOverlay();
} else if (type == "grid") {
thisOverlay = new Grid3DOverlay();
} else if (type == "localmodels") {
thisOverlay = new LocalModelsOverlay(Application::getInstance()->getEntityClipboardRenderer());
} else if (type == "model") {
thisOverlay = new ModelOverlay();
} else if (type == "billboard") {
thisOverlay = new BillboardOverlay();
} else {
created = false;
Overlay::Pointer thisOverlay = nullptr;
if (type == ImageOverlay::TYPE) {
thisOverlay = std::make_shared<ImageOverlay>();
} else if (type == TextOverlay::TYPE) {
thisOverlay = std::make_shared<TextOverlay>();
} else if (type == Text3DOverlay::TYPE) {
thisOverlay = std::make_shared<Text3DOverlay>();
} else if (type == Cube3DOverlay::TYPE) {
thisOverlay = std::make_shared<Cube3DOverlay>();
} else if (type == Sphere3DOverlay::TYPE) {
thisOverlay = std::make_shared<Sphere3DOverlay>();
} else if (type == Circle3DOverlay::TYPE) {
thisOverlay = std::make_shared<Circle3DOverlay>();
} else if (type == Rectangle3DOverlay::TYPE) {
thisOverlay = std::make_shared<Rectangle3DOverlay>();
} else if (type == Line3DOverlay::TYPE) {
thisOverlay = std::make_shared<Line3DOverlay>();
} else if (type == Grid3DOverlay::TYPE) {
thisOverlay = std::make_shared<Grid3DOverlay>();
} else if (type == LocalModelsOverlay::TYPE) {
thisOverlay = std::make_shared<LocalModelsOverlay>(Application::getInstance()->getEntityClipboardRenderer());
} else if (type == ModelOverlay::TYPE) {
thisOverlay = std::make_shared<ModelOverlay>();
} else if (type == BillboardOverlay::TYPE) {
thisOverlay = std::make_shared<BillboardOverlay>();
}
if (created) {
if (thisOverlay) {
thisOverlay->setProperties(properties);
thisID = addOverlay(thisOverlay);
return addOverlay(thisOverlay);
}
return thisID;
return 0;
}
unsigned int Overlays::addOverlay(Overlay* overlay) {
Overlay::Pointer overlayPointer(overlay);
unsigned int Overlays::addOverlay(Overlay::Pointer overlay) {
overlay->init(_scriptEngine);
QWriteLocker lock(&_lock);
unsigned int thisID = _nextOverlayID;
_nextOverlayID++;
if (overlay->is3D()) {
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(overlay);
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
if (overlay3D->getDrawOnHUD()) {
_overlaysHUD[thisID] = overlayPointer;
_overlaysHUD[thisID] = overlay;
} else {
_overlaysWorld[thisID] = overlayPointer;
_overlaysWorld[thisID] = overlay;
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
render::PendingChanges pendingChanges;
overlayPointer->addToScene(overlayPointer, scene, pendingChanges);
overlay->addToScene(overlay, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
}
} else {
_overlaysHUD[thisID] = overlayPointer;
_overlaysHUD[thisID] = overlay;
}
return thisID;
}
unsigned int Overlays::cloneOverlay(unsigned int id) {
Overlay::Pointer thisOverlay = NULL;
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
} else if (_overlaysWorld.contains(id)) {
thisOverlay = _overlaysWorld[id];
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
return addOverlay(thisOverlay->createClone());
return addOverlay(Overlay::Pointer(thisOverlay->createClone()));
}
return 0; // Not found
@ -220,14 +219,8 @@ unsigned int Overlays::cloneOverlay(unsigned int id) {
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
QWriteLocker lock(&_lock);
Overlay::Pointer thisOverlay;
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
} else if (_overlaysWorld.contains(id)) {
thisOverlay = _overlaysWorld[id];
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) {
if (thisOverlay->is3D()) {
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(thisOverlay);
@ -268,8 +261,51 @@ void Overlays::deleteOverlay(unsigned int id) {
}
}
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete);
if (attachable && attachable->getAttachedPanel()) {
attachable->getAttachedPanel()->removeChild(id);
attachable->setAttachedPanel(nullptr);
}
QWriteLocker lock(&_deleteLock);
_overlaysToDelete.push_back(overlayToDelete);
emit overlayDeleted(id);
}
QString Overlays::getOverlayType(unsigned int overlayId) const {
Overlay::Pointer overlay = getOverlay(overlayId);
if (overlay) {
return overlay->getType();
}
return "";
}
unsigned int Overlays::getAttachedPanel(unsigned int childId) const {
Overlay::Pointer overlay = getOverlay(childId);
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
if (attachable) {
return _panels.key(attachable->getAttachedPanel());
}
return 0;
}
void Overlays::setAttachedPanel(unsigned int childId, unsigned int panelId) {
Overlay::Pointer overlay = getOverlay(childId);
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
if (attachable) {
if (_panels.contains(panelId)) {
auto panel = _panels[panelId];
panel->addChild(childId);
attachable->setAttachedPanel(panel);
} else {
auto panel = attachable->getAttachedPanel();
if (panel) {
panel->removeChild(childId);
attachable->setAttachedPanel(nullptr);
}
}
}
}
unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
@ -315,13 +351,8 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) {
OverlayPropertyResult result;
Overlay::Pointer thisOverlay;
Overlay::Pointer thisOverlay = getOverlay(id);
QReadLocker lock(&_lock);
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
} else if (_overlaysWorld.contains(id)) {
thisOverlay = _overlaysWorld[id];
}
if (thisOverlay) {
result.value = thisOverlay->getProperty(property);
}
@ -469,12 +500,8 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
bool Overlays::isLoaded(unsigned int id) {
QReadLocker lock(&_lock);
Overlay::Pointer thisOverlay = NULL;
if (_overlaysHUD.contains(id)) {
thisOverlay = _overlaysHUD[id];
} else if (_overlaysWorld.contains(id)) {
thisOverlay = _overlaysWorld[id];
} else {
Overlay::Pointer thisOverlay = getOverlay(id);
if (!thisOverlay) {
return false; // not found
}
return thisOverlay->isLoaded();
@ -496,3 +523,56 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const {
}
return QSizeF(0.0f, 0.0f);
}
unsigned int Overlays::addPanel(FloatingUIPanel::Pointer panel) {
QWriteLocker lock(&_lock);
unsigned int thisID = _nextOverlayID;
_nextOverlayID++;
_panels[thisID] = panel;
return thisID;
}
unsigned int Overlays::addPanel(const QScriptValue& properties) {
FloatingUIPanel::Pointer panel = std::make_shared<FloatingUIPanel>();
panel->init(_scriptEngine);
panel->setProperties(properties);
return addPanel(panel);
}
void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) {
if (_panels.contains(panelId)) {
_panels[panelId]->setProperties(properties);
}
}
OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QString& property) {
OverlayPropertyResult result;
if (_panels.contains(panelId)) {
FloatingUIPanel::Pointer thisPanel = _panels[panelId];
QReadLocker lock(&_lock);
result.value = thisPanel->getProperty(property);
}
return result;
}
void Overlays::deletePanel(unsigned int panelId) {
FloatingUIPanel::Pointer panelToDelete;
{
QWriteLocker lock(&_lock);
if (_panels.contains(panelId)) {
panelToDelete = _panels.take(panelId);
} else {
return;
}
}
while (!panelToDelete->getChildren().isEmpty()) {
deleteOverlay(panelToDelete->popLastChild());
}
emit panelDeleted(panelId);
}

View file

@ -2,8 +2,14 @@
// Overlays.h
// interface/src/ui/overlays
//
// Modified by Zander Otavka on 7/15/15
// Copyright 2014 High Fidelity, Inc.
//
// Exposes methods for managing `Overlay`s and `FloatingUIPanel`s to scripts.
//
// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the
// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
@ -16,6 +22,9 @@
#include "Overlay.h"
#include "FloatingUIPanel.h"
#include "PanelAttachable.h"
class PickRay;
class OverlayPropertyResult {
@ -59,12 +68,16 @@ public:
void disable();
void enable();
Overlay::Pointer getOverlay(unsigned int id) const;
FloatingUIPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; }
public slots:
/// adds an overlay with the specific properties
unsigned int addOverlay(const QString& type, const QScriptValue& properties);
/// adds an overlay that's already been created
unsigned int addOverlay(Overlay* overlay);
unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
unsigned int addOverlay(Overlay::Pointer overlay);
/// clones an existing overlay
unsigned int cloneOverlay(unsigned int id);
@ -76,6 +89,12 @@ public slots:
/// deletes a particle
void deleteOverlay(unsigned int id);
/// get the string type of the overlay used in addOverlay
QString getOverlayType(unsigned int overlayId) const;
unsigned int getAttachedPanel(unsigned int childId) const;
void setAttachedPanel(unsigned int childId, unsigned int panelId);
/// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point
unsigned int getOverlayAtPoint(const glm::vec2& point);
@ -92,12 +111,35 @@ public slots:
/// overlay; in meters if it is a 3D text overlay
QSizeF textSize(unsigned int id, const QString& text) const;
/// adds a panel that has already been created
unsigned int addPanel(FloatingUIPanel::Pointer panel);
/// creates and adds a panel based on a set of properties
unsigned int addPanel(const QScriptValue& properties);
/// edit the properties of a panel
void editPanel(unsigned int panelId, const QScriptValue& properties);
/// get a property of a panel
OverlayPropertyResult getPanelProperty(unsigned int panelId, const QString& property);
/// deletes a panel and all child overlays
void deletePanel(unsigned int panelId);
signals:
void overlayDeleted(unsigned int id);
void panelDeleted(unsigned int id);
private:
void cleanupOverlaysToDelete();
QMap<unsigned int, Overlay::Pointer> _overlaysHUD;
QMap<unsigned int, Overlay::Pointer> _overlaysWorld;
QMap<unsigned int, FloatingUIPanel::Pointer> _panels;
QList<Overlay::Pointer> _overlaysToDelete;
unsigned int _nextOverlayID;
QReadWriteLock _lock;
QReadWriteLock _deleteLock;
QScriptEngine* _scriptEngine;

View file

@ -0,0 +1,80 @@
//
// PanelAttachable.cpp
// hifi
//
// Created by Zander Otavka on 7/15/15.
// 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 "PanelAttachable.h"
#include <RegisteredMetaTypes.h>
PanelAttachable::PanelAttachable() :
_attachedPanel(nullptr),
_facingRotation(1, 0, 0, 0)
{
}
PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) :
_attachedPanel(panelAttachable->_attachedPanel),
_offsetPosition(panelAttachable->_offsetPosition),
_facingRotation(panelAttachable->_facingRotation)
{
}
void PanelAttachable::setTransforms(Transform& transform) {
if (getAttachedPanel()) {
transform.setTranslation(getAttachedPanel()->getAnchorPosition());
transform.setRotation(getAttachedPanel()->getOffsetRotation());
transform.postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition());
transform.postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation());
}
}
QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QString &property) {
if (property == "offsetPosition") {
return vec3toScriptValue(scriptEngine, getOffsetPosition());
}
if (property == "facingRotation") {
return quatToScriptValue(scriptEngine, getFacingRotation());
}
return QScriptValue();
}
void PanelAttachable::setProperties(const QScriptValue &properties) {
QScriptValue offsetPosition = properties.property("offsetPosition");
if (offsetPosition.isValid()) {
QScriptValue x = offsetPosition.property("x");
QScriptValue y = offsetPosition.property("y");
QScriptValue z = offsetPosition.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newPosition;
newPosition.x = x.toVariant().toFloat();
newPosition.y = y.toVariant().toFloat();
newPosition.z = z.toVariant().toFloat();
setOffsetPosition(newPosition);
}
}
QScriptValue facingRotation = properties.property("facingRotation");
if (facingRotation.isValid()) {
QScriptValue x = facingRotation.property("x");
QScriptValue y = facingRotation.property("y");
QScriptValue z = facingRotation.property("z");
QScriptValue w = facingRotation.property("w");
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
glm::quat newRotation;
newRotation.x = x.toVariant().toFloat();
newRotation.y = y.toVariant().toFloat();
newRotation.z = z.toVariant().toFloat();
newRotation.w = w.toVariant().toFloat();
setFacingRotation(newRotation);
}
}
}

View file

@ -0,0 +1,45 @@
//
// PanelAttachable.h
// interface/src/ui/overlays
//
// Created by Zander Otavka on 7/1/15.
// 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
//
#ifndef hifi_PanelAttachable_h
#define hifi_PanelAttachable_h
#include "FloatingUIPanel.h"
#include <glm/glm.hpp>
#include <Transform.h>
class PanelAttachable {
public:
PanelAttachable();
PanelAttachable(const PanelAttachable* panelAttachable);
FloatingUIPanel::Pointer getAttachedPanel() const { return _attachedPanel; }
glm::vec3 getOffsetPosition() const { return _offsetPosition; }
glm::quat getFacingRotation() const { return _facingRotation; }
void setAttachedPanel(FloatingUIPanel::Pointer panel) { _attachedPanel = panel; }
void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; }
void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; }
QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property);
void setProperties(const QScriptValue& properties);
protected:
virtual void setTransforms(Transform& transform);
private:
FloatingUIPanel::Pointer _attachedPanel;
glm::vec3 _offsetPosition;
glm::quat _facingRotation;
};
#endif // hifi_PanelAttachable_h

View file

@ -14,8 +14,15 @@
#include <GeometryUtil.h>
#include <RegisteredMetaTypes.h>
Planar3DOverlay::Planar3DOverlay() :
Base3DOverlay(),
_dimensions{1.0f, 1.0f}
{
}
Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) :
Base3DOverlay(planar3DOverlay)
Base3DOverlay(planar3DOverlay),
_dimensions(planar3DOverlay->_dimensions)
{
}

View file

@ -17,7 +17,7 @@ class Planar3DOverlay : public Base3DOverlay {
Q_OBJECT
public:
Planar3DOverlay() {}
Planar3DOverlay();
Planar3DOverlay(const Planar3DOverlay* planar3DOverlay);
AABox getBounds() const;
@ -32,7 +32,7 @@ public:
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
protected:
glm::vec2 _dimensions{1.0f, 1.0f};
glm::vec2 _dimensions;
};

View file

@ -13,6 +13,9 @@
#include <GeometryCache.h>
#include <SharedUtil.h>
QString const Rectangle3DOverlay::TYPE = "rectangle3d";
Rectangle3DOverlay::Rectangle3DOverlay() :
_geometryCacheID(DependencyManager::get<GeometryCache>()->allocateID())
{

View file

@ -17,6 +17,9 @@ class Rectangle3DOverlay : public Planar3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
Rectangle3DOverlay();
Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay);
~Rectangle3DOverlay();

View file

@ -15,6 +15,8 @@
#include <gpu/Batch.h>
#include <SharedUtil.h>
QString const Sphere3DOverlay::TYPE = "sphere";
Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
Volume3DOverlay(Sphere3DOverlay)
{

View file

@ -17,6 +17,9 @@ class Sphere3DOverlay : public Volume3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
Sphere3DOverlay() {}
Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay);

View file

@ -23,6 +23,8 @@ const int FIXED_FONT_POINT_SIZE = 40;
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation
const float LINE_SCALE_RATIO = 1.2f;
QString const Text3DOverlay::TYPE = "text3d";
Text3DOverlay::Text3DOverlay() :
_backgroundColor(DEFAULT_BACKGROUND_COLOR),
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),

View file

@ -21,6 +21,9 @@ class Text3DOverlay : public Planar3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
Text3DOverlay();
Text3DOverlay(const Text3DOverlay* text3DOverlay);
~Text3DOverlay();

View file

@ -15,7 +15,6 @@
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <GLMHelpers.h>
#include <gpu/GLBackend.h>
#include <OffscreenUi.h>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h>
@ -78,6 +77,8 @@ QString toQmlColor(const glm::vec4& v) {
arg((int)(v.b * 255), 2, 16, QChar('0'));
}
QString const TextOverlay::TYPE = "text";
TextOverlay::TextOverlay() :
_backgroundColor(DEFAULT_BACKGROUND_COLOR),
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),

View file

@ -29,6 +29,9 @@ class TextOverlay : public Overlay2D {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const { return TYPE; }
TextOverlay();
TextOverlay(const TextOverlay* textOverlay);
~TextOverlay();

View file

@ -1,6 +1,6 @@
//
// AnimationCache.cpp
// libraries/script-engine/src/
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 4/14/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.

View file

@ -1,6 +1,6 @@
//
// AnimationCache.h
// libraries/script-engine/src/
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 4/14/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.

View file

@ -1,6 +1,6 @@
//
// AnimationHandle.cpp
// interface/src/renderer
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 10/18/13.
// Copyright 2013 High Fidelity, Inc.
@ -10,7 +10,7 @@
//
#include "AnimationHandle.h"
#include "Model.h"
void AnimationHandle::setURL(const QUrl& url) {
if (_url != url) {
@ -20,28 +20,17 @@ void AnimationHandle::setURL(const QUrl& url) {
}
}
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
if (handle->getPriority() > (*it)->getPriority()) {
handles.insert(it, handle);
return;
}
}
handles.append(handle);
}
void AnimationHandle::setPriority(float priority) {
if (_priority == priority) {
return;
}
if (isRunning()) {
_model->_runningAnimations.removeOne(_self);
_rig->removeRunningAnimation(getAnimationHandlePointer());
if (priority < _priority) {
replaceMatchingPriorities(priority);
}
_priority = priority;
insertSorted(_model->_runningAnimations, _self);
_rig->addRunningAnimation(getAnimationHandlePointer());
} else {
_priority = priority;
}
@ -60,7 +49,7 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
_jointMappings.clear();
}
void AnimationHandle::setRunning(bool running) {
void AnimationHandle::setRunning(bool running, bool doRestoreJoints) {
if (running && isRunning()) {
// if we're already running, this is the same as a restart
setFrameIndex(getFirstFrame());
@ -68,21 +57,25 @@ void AnimationHandle::setRunning(bool running) {
}
_animationLoop.setRunning(running);
if (isRunning()) {
if (!_model->_runningAnimations.contains(_self)) {
insertSorted(_model->_runningAnimations, _self);
if (!_rig->isRunningAnimation(getAnimationHandlePointer())) {
_rig->addRunningAnimation(getAnimationHandlePointer());
}
} else {
_model->_runningAnimations.removeOne(_self);
restoreJoints();
_rig->removeRunningAnimation(getAnimationHandlePointer());
if (doRestoreJoints) {
restoreJoints();
}
replaceMatchingPriorities(0.0f);
}
emit runningChanged(isRunning());
}
AnimationHandle::AnimationHandle(Model* model) :
QObject(model),
_model(model),
_priority(1.0f)
AnimationHandle::AnimationHandle(RigPointer rig) :
QObject(rig.get()),
_rig(rig),
_priority(1.0f),
_fade(0.0f),
_fadePerSecond(0.0f)
{
}
@ -110,42 +103,61 @@ void AnimationHandle::setAnimationDetails(const AnimationDetails& details) {
}
void AnimationHandle::setJointMappings(QVector<int> jointMappings) {
_jointMappings = jointMappings;
}
QVector<int> AnimationHandle::getJointMappings() {
if (_jointMappings.isEmpty()) {
QVector<FBXJoint> animationJoints = _animation->getGeometry().joints;
for (int i = 0; i < animationJoints.count(); i++) {
_jointMappings.append(_rig->indexOfJoint(animationJoints.at(i).name));
}
}
return _jointMappings;
}
void AnimationHandle::simulate(float deltaTime) {
if (!_animation || !_animation->isLoaded()) {
return;
}
_animationLoop.simulate(deltaTime);
// update the joint mappings if necessary/possible
if (_jointMappings.isEmpty()) {
if (_model && _model->isActive()) {
_jointMappings = _model->getGeometry()->getJointMappings(_animation);
}
if (_jointMappings.isEmpty()) {
return;
}
if (!_maskedJoints.isEmpty()) {
const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry();
for (int i = 0; i < _jointMappings.size(); i++) {
int& mapping = _jointMappings[i];
if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) {
mapping = -1;
}
}
}
if (getJointMappings().isEmpty()) {
qDebug() << "AnimationHandle::simulate -- _jointMappings.isEmpty()";
return;
}
// // update the joint mappings if necessary/possible
// if (_jointMappings.isEmpty()) {
// if (_model && _model->isActive()) {
// _jointMappings = _model->getGeometry()->getJointMappings(_animation);
// }
// if (_jointMappings.isEmpty()) {
// return;
// }
// if (!_maskedJoints.isEmpty()) {
// const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry();
// for (int i = 0; i < _jointMappings.size(); i++) {
// int& mapping = _jointMappings[i];
// if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) {
// mapping = -1;
// }
// }
// }
// }
const FBXGeometry& animationGeometry = _animation->getGeometry();
if (animationGeometry.animationFrames.isEmpty()) {
stop();
return;
}
if (_animationLoop.getMaxFrameIndexHint() != animationGeometry.animationFrames.size()) {
_animationLoop.setMaxFrameIndexHint(animationGeometry.animationFrames.size());
}
// blend between the closest two frames
applyFrame(getFrameIndex());
}
@ -154,17 +166,23 @@ void AnimationHandle::applyFrame(float frameIndex) {
if (!_animation || !_animation->isLoaded()) {
return;
}
const FBXGeometry& animationGeometry = _animation->getGeometry();
int frameCount = animationGeometry.animationFrames.size();
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
float frameFraction = glm::fract(frameIndex);
assert(_rig->getJointStateCount() >= _jointMappings.size());
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
JointState& state = _model->_jointStates[mapping];
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority);
_rig->setJointRotationInConstrainedFrame(mapping,
safeMix(floorFrame.rotations.at(i),
ceilFrame.rotations.at(i),
frameFraction),
_priority,
false,
_mix);
}
}
}
@ -173,9 +191,8 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
JointState& state = _model->_jointStates[mapping];
if (_priority == state._animationPriority) {
state._animationPriority = newPriority;
if (_priority == _rig->getJointAnimatinoPriority(mapping)) {
_rig->setJointAnimatinoPriority(mapping, newPriority);
}
}
}
@ -185,9 +202,7 @@ void AnimationHandle::restoreJoints() {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
JointState& state = _model->_jointStates[mapping];
state.restoreRotation(1.0f, state._animationPriority);
_rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping));
}
}
}

View file

@ -1,6 +1,6 @@
//
// AnimationHandle.h
// interface/src/renderer
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 10/18/13.
// Copyright 2013 High Fidelity, Inc.
@ -18,22 +18,43 @@
#include <QUrl>
#include <QVector>
#include <AnimationCache.h>
#include <AnimationLoop.h>
#include "AnimationCache.h"
#include "AnimationLoop.h"
#include "Rig.h"
class AnimationHandle;
class Model;
typedef QSharedPointer<AnimationHandle> AnimationHandlePointer;
typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
typedef std::weak_ptr<AnimationHandle> WeakAnimationHandlePointer;
inline uint qHash(const std::shared_ptr<AnimationHandle>& a, uint seed) {
// return qHash(a.get(), seed);
AnimationHandle* strongRef = a ? a.get() : nullptr;
return qHash(strongRef, seed);
}
inline uint qHash(const std::weak_ptr<AnimationHandle>& a, uint seed) {
AnimationHandlePointer strongPointer = a.lock();
AnimationHandle* strongRef = strongPointer ? strongPointer.get() : nullptr;
return qHash(strongRef, seed);
}
/// Represents a handle to a model animation.
class AnimationHandle : public QObject {
// inline uint qHash(const WeakAnimationHandlePointer& handle, uint seed) {
// return qHash(handle.data(), seed);
// }
/// Represents a handle to a model animation. I.e., an Animation in use by a given Rig.
class AnimationHandle : public QObject, public std::enable_shared_from_this<AnimationHandle> {
Q_OBJECT
public:
AnimationHandle(RigPointer rig);
AnimationHandlePointer getAnimationHandlePointer() { return shared_from_this(); }
void setRole(const QString& role) { _role = role; }
const QString& getRole() const { return _role; }
@ -42,30 +63,35 @@ public:
void setPriority(float priority);
float getPriority() const { return _priority; }
void setMix(float mix) { _mix = mix; }
void setFade(float fade) { _fade = fade; }
float getFade() const { return _fade; }
void setFadePerSecond(float fadePerSecond) { _fadePerSecond = fadePerSecond; }
float getFadePerSecond() const { return _fadePerSecond; }
void setMaskedJoints(const QStringList& maskedJoints);
const QStringList& getMaskedJoints() const { return _maskedJoints; }
void setFPS(float fps) { _animationLoop.setFPS(fps); }
float getFPS() const { return _animationLoop.getFPS(); }
void setLoop(bool loop) { _animationLoop.setLoop(loop); }
bool getLoop() const { return _animationLoop.getLoop(); }
void setHold(bool hold) { _animationLoop.setHold(hold); }
bool getHold() const { return _animationLoop.getHold(); }
void setStartAutomatically(bool startAutomatically);
bool getStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
void setFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
float getFirstFrame() const { return _animationLoop.getFirstFrame(); }
void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
float getLastFrame() const { return _animationLoop.getLastFrame(); }
void setRunning(bool running);
void setRunning(bool running, bool restoreJoints = true);
bool isRunning() const { return _animationLoop.isRunning(); }
void setFrameIndex(float frameIndex) { _animationLoop.setFrameIndex(frameIndex); }
@ -74,39 +100,40 @@ public:
AnimationDetails getAnimationDetails() const;
void setAnimationDetails(const AnimationDetails& details);
void setJointMappings(QVector<int> jointMappings);
QVector<int> getJointMappings(); // computing if necessary
void simulate(float deltaTime);
void applyFrame(float frameIndex);
void replaceMatchingPriorities(float newPriority);
void restoreJoints();
void clearJoints() { _jointMappings.clear(); }
signals:
void runningChanged(bool running);
public slots:
void start() { setRunning(true); }
void stop() { setRunning(false); }
void stop() { setRunning(false); _fadePerSecond = _fade = 0.0f; }
private:
friend class Model;
AnimationHandle(Model* model);
void simulate(float deltaTime);
void applyFrame(float frameIndex);
void replaceMatchingPriorities(float newPriority);
void restoreJoints();
void clearJoints() { _jointMappings.clear(); }
Model* _model;
WeakAnimationHandlePointer _self;
RigPointer _rig;
AnimationPointer _animation;
QString _role;
QUrl _url;
float _priority;
float _mix; // How much of this animation to blend against what is already there. 1.0 sets to just this animation.
float _fade; // How far are we into full strength. 0.0 uses none of this animation, 1.0 (the max) is as much as possible.
float _fadePerSecond; // How fast should _fade change? +1.0 means _fade is increasing to 1.0 in 1 second. Negative is fading out.
QStringList _maskedJoints;
QVector<int> _jointMappings;
AnimationLoop _animationLoop;
static QHash<QWeakPointer<Animation>, QVector<int>> _jointMappingsCache;
};

View file

@ -0,0 +1,14 @@
//
// AnimationLogging.cpp
// libraries/audio/src
//
// Created by Howard Stearns on 7/24/15.
// Copyright 2014 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 "AnimationLogging.h"
Q_LOGGING_CATEGORY(animation, "hifi.animation")

View file

@ -0,0 +1,18 @@
//
// AnimationLogging.h
// libraries/animation/src/
//
// Created by Howard Stearns on 7/24/15.
//
// 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_AnimationLogging_h
#define hifi_AnimationLogging_h
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(animation)
#endif

View file

@ -1,6 +1,6 @@
//
// AnimationLoop.cpp
// libraries/animation
// libraries/animation/src/
//
// Created by Brad Hefta-Gaub on 11/12/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.

View file

@ -1,6 +1,6 @@
//
// AnimationLoop.h
// libraries/script-engine/src/
// libraries/animation/src/
//
// Created by Brad Hefta-Gaub on 11/12/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.

View file

@ -1,6 +1,6 @@
//
// AnimationObject.cpp
// libraries/script-engine/src/
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 4/17/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.

View file

@ -1,6 +1,6 @@
//
// AnimationObject.h
// libraries/script-engine/src/
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 4/17/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.

View file

@ -0,0 +1,100 @@
//
// AvatarRig.cpp
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// 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 "AvatarRig.h"
/// Updates the state of the joint at the specified index.
void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
if (index < 0 && index >= _jointStates.size()) {
return; // bail
}
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
// compute model transforms
if (index == _rootJointIndex) {
// we always zero-out the translation part of an avatar's root join-transform.
state.computeTransform(parentTransform);
clearJointTransformTranslation(index);
} else {
// guard against out-of-bounds access to _jointStates
int parentIndex = joint.parentIndex;
if (parentIndex >= 0 && parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(parentIndex);
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
}
}
}
void AvatarRig::setHandPosition(int jointIndex,
const glm::vec3& position, const glm::quat& rotation,
float scale, float priority) {
bool rightHand = (jointIndex == _rightHandJointIndex);
int elbowJointIndex = rightHand ? _rightElbowJointIndex : _leftElbowJointIndex;
int shoulderJointIndex = rightHand ? _rightShoulderJointIndex : _leftShoulderJointIndex;
// this algorithm is from sample code from sixense
if (elbowJointIndex == -1 || shoulderJointIndex == -1) {
return;
}
glm::vec3 shoulderPosition;
if (!getJointPosition(shoulderJointIndex, shoulderPosition)) {
return;
}
// precomputed lengths
float upperArmLength = _jointStates[elbowJointIndex].getFBXJoint().distanceToParent * scale;
float lowerArmLength = _jointStates[jointIndex].getFBXJoint().distanceToParent * scale;
// first set wrist position
glm::vec3 wristPosition = position;
glm::vec3 shoulderToWrist = wristPosition - shoulderPosition;
float distanceToWrist = glm::length(shoulderToWrist);
// prevent gimbal lock
if (distanceToWrist > upperArmLength + lowerArmLength - EPSILON) {
distanceToWrist = upperArmLength + lowerArmLength - EPSILON;
shoulderToWrist = glm::normalize(shoulderToWrist) * distanceToWrist;
wristPosition = shoulderPosition + shoulderToWrist;
}
// cosine of angle from upper arm to hand vector
float cosA = (upperArmLength * upperArmLength + distanceToWrist * distanceToWrist - lowerArmLength * lowerArmLength) /
(2 * upperArmLength * distanceToWrist);
float mid = upperArmLength * cosA;
float height = sqrt(upperArmLength * upperArmLength + mid * mid - 2 * upperArmLength * mid * cosA);
// direction of the elbow
glm::vec3 handNormal = glm::cross(rotation * glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow rotating with wrist
glm::vec3 relaxedNormal = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow pointing straight down
const float NORMAL_WEIGHT = 0.5f;
glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT);
if (rightHand ? (finalNormal.y > 0.0f) : (finalNormal.y < 0.0f)) {
finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis)
}
glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal));
// ik solution
glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height;
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, priority);
setJointRotationInBindFrame(elbowJointIndex,
rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) *
shoulderRotation, priority);
setJointRotationInBindFrame(jointIndex, rotation, priority);
}

View file

@ -0,0 +1,29 @@
//
// AvatarRig.h
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// 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
//
#ifndef hifi_AvatarRig_h
#define hifi_AvatarRig_h
#include <QObject>
#include "Rig.h"
class AvatarRig : public Rig {
Q_OBJECT
public:
~AvatarRig() {}
virtual void updateJointState(int index, glm::mat4 parentTransform);
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
float scale, float priority);
};
#endif // hifi_AvatarRig_h

View file

@ -0,0 +1,30 @@
//
// EntityRig.cpp
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// 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 "EntityRig.h"
/// Updates the state of the joint at the specified index.
void EntityRig::updateJointState(int index, glm::mat4 parentTransform) {
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
// compute model transforms
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
state.computeTransform(parentTransform);
} else {
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(parentIndex);
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
}
}
}

View file

@ -0,0 +1,29 @@
//
// EntityRig.h
// libraries/animation/src/
//
// Created by SethAlves on 2015-7-22.
// 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
//
#ifndef hifi_EntityRig_h
#define hifi_EntityRig_h
#include <QObject>
#include "Rig.h"
class EntityRig : public Rig {
Q_OBJECT
public:
~EntityRig() {}
virtual void updateJointState(int index, glm::mat4 parentTransform);
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
float scale, float priority) {}
};
#endif // hifi_EntityRig_h

View file

@ -1,6 +1,6 @@
//
// JointState.cpp
// interface/src/renderer
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 10/18/13.
// Copyright 2013 High Fidelity, Inc.
@ -232,12 +232,13 @@ glm::quat JointState::computeVisibleParentRotation() const {
return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation);
}
void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain) {
void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain, float mix) {
if (priority >= _animationPriority || _animationPriority == 0.0f) {
if (constrain && _constraint) {
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
}
setRotationInConstrainedFrameInternal(targetRotation);
auto rotation = (mix == 1.0f) ? targetRotation : safeMix(getRotationInConstrainedFrame(), targetRotation, mix);
setRotationInConstrainedFrameInternal(rotation);
_animationPriority = priority;
}
}

View file

@ -1,6 +1,6 @@
//
// JointState.h
// interface/src/renderer
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 10/18/13.
// Copyright 2013 High Fidelity, Inc.
@ -84,7 +84,7 @@ public:
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false);
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f);
void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation);
const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; }
const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; }

View file

@ -0,0 +1,845 @@
//
// Rig.cpp
// libraries/animation/src/
//
// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/vector_angle.hpp>
#include <queue>
#include "AnimationHandle.h"
#include "AnimationLogging.h"
#include "Rig.h"
void Rig::HeadParameters::dump() const {
qCDebug(animation, "HeadParameters =");
qCDebug(animation, " leanSideways = %0.5f", leanSideways);
qCDebug(animation, " leanForward = %0.5f", leanForward);
qCDebug(animation, " torsoTwist = %0.5f", torsoTwist);
glm::vec3 axis = glm::axis(localHeadOrientation);
float theta = glm::angle(localHeadOrientation);
qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
axis = glm::axis(worldHeadOrientation);
theta = glm::angle(worldHeadOrientation);
qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", eyeLookAt.x, eyeLookAt.y, eyeLookAt.z);
qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", eyeSaccade.x, eyeSaccade.y, eyeSaccade.z);
qCDebug(animation, " leanJointIndex = %.d", leanJointIndex);
qCDebug(animation, " neckJointIndex = %.d", neckJointIndex);
qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex);
qCDebug(animation, " rightEyeJointIndex = %.d", rightEyeJointIndex);
}
void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
if (handle->getPriority() > (*it)->getPriority()) {
handles.insert(it, handle);
return;
}
}
handles.append(handle);
}
AnimationHandlePointer Rig::createAnimationHandle() {
AnimationHandlePointer handle(new AnimationHandle(getRigPointer()));
_animationHandles.append(handle);
return handle;
}
void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) {
handle->stop();
// FIXME? Do we need to also animationHandle->clearJoints()? deleteAnimations(), below, was first written to do so, but did not first stop it.
_animationHandles.removeOne(handle);
}
void Rig::startAnimation(const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
// This is different than startAnimationByRole, in which we use the existing values if the animation already exists.
// Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted).
AnimationHandlePointer handle = nullptr;
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
if (candidate->getURL() == url) {
handle = candidate;
}
}
if (!handle) {
handle = createAnimationHandle();
handle->setURL(url);
}
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints);
handle->start();
}
AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) {
// check for a configured animation for the role
//qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically;
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
if (candidate->getRole() == role) {
if (startAutomatically) {
candidate->start();
}
return candidate;
}
}
AnimationHandlePointer handle = createAnimationHandle();
QString standard = "";
if (url.isEmpty()) { // Default animations for fight club
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/";
if (role == "walk") {
standard = base + "support/FightClubBotTest1/Animations/standard_walk.fbx";
lastFrame = 60;
} else if (role == "leftTurn") {
standard = base + "support/FightClubBotTest1/Animations/left_turn_noHipRotation.fbx";
lastFrame = 29;
} else if (role == "rightTurn") {
standard = base + "support/FightClubBotTest1/Animations/right_turn_noHipRotation.fbx";
lastFrame = 31;
} else if (role == "leftStrafe") {
standard = base + "animations/fightclub_bot_anims/side_step_left_inPlace.fbx";
lastFrame = 31;
} else if (role == "rightStrafe") {
standard = base + "animations/fightclub_bot_anims/side_step_right_inPlace.fbx";
lastFrame = 31;
} else if (role == "idle") {
standard = base + "support/FightClubBotTest1/Animations/standard_idle.fbx";
fps = 25.0f;
}
if (!standard.isEmpty()) {
loop = true;
}
}
handle->setRole(role);
handle->setURL(url.isEmpty() ? standard : url);
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints);
if (startAutomatically) {
handle->start();
}
return handle;
}
void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
AnimationHandlePointer handle = addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true);
handle->setFadePerSecond(1.0f); // For now. Could be individualized later.
}
void Rig::stopAnimationByRole(const QString& role) {
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
if (handle->getRole() == role) {
handle->setFadePerSecond(-1.0f); // For now. Could be individualized later.
}
}
}
void Rig::stopAnimation(const QString& url) {
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
if (handle->getURL() == url) {
handle->stop();
}
}
}
bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.removeOne(animationHandle);
}
void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
insertSorted(_runningAnimations, animationHandle);
}
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.contains(animationHandle);
}
bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this
for (auto animation : _runningAnimations) {
if ((animation->getRole() == role) && (animation->getFadePerSecond() >= 0.0f)) { // Don't count those being faded out
return true;
}
}
return false;
}
void Rig::deleteAnimations() {
for (auto animation : _animationHandles) {
removeAnimationHandle(animation);
}
_animationHandles.clear();
}
float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
int rootJointIndex,
int leftHandJointIndex,
int leftElbowJointIndex,
int leftShoulderJointIndex,
int rightHandJointIndex,
int rightElbowJointIndex,
int rightShoulderJointIndex) {
_jointStates = states;
_rootJointIndex = rootJointIndex;
_leftHandJointIndex = leftHandJointIndex;
_leftElbowJointIndex = leftElbowJointIndex;
_leftShoulderJointIndex = leftShoulderJointIndex;
_rightHandJointIndex = rightHandJointIndex;
_rightElbowJointIndex = rightElbowJointIndex;
_rightShoulderJointIndex = rightShoulderJointIndex;
initJointTransforms(parentTransform);
int numStates = _jointStates.size();
float radius = 0.0f;
for (int i = 0; i < numStates; ++i) {
float distance = glm::length(_jointStates[i].getPosition());
if (distance > radius) {
radius = distance;
}
_jointStates[i].buildConstraint();
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
}
return radius;
}
// We could build and cache a dictionary, too....
// Should we be using .fst mapping instead/also?
int Rig::indexOfJoint(const QString& jointName) {
for (int i = 0; i < _jointStates.count(); i++) {
if (_jointStates[i].getFBXJoint().name == jointName) {
return i;
}
}
return -1;
}
void Rig::initJointTransforms(glm::mat4 parentTransform) {
// compute model transforms
int numStates = _jointStates.size();
for (int i = 0; i < numStates; ++i) {
JointState& state = _jointStates[i];
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
state.initTransform(parentTransform);
} else {
const JointState& parentState = _jointStates.at(parentIndex);
state.initTransform(parentState.getTransform());
}
}
}
void Rig::clearJointTransformTranslation(int jointIndex) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return;
}
_jointStates[jointIndex].clearTransformTranslation();
}
void Rig::reset(const QVector<FBXJoint>& fbxJoints) {
if (_jointStates.isEmpty()) {
return;
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f);
}
}
JointState Rig::getJointState(int jointIndex) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return JointState();
}
return _jointStates[jointIndex];
}
bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
if (index == -1 || index >= _jointStates.size()) {
return false;
}
const JointState& state = _jointStates.at(index);
rotation = state.getRotationInConstrainedFrame();
return !state.rotationIsDefault(rotation);
}
bool Rig::getVisibleJointState(int index, glm::quat& rotation) const {
if (index == -1 || index >= _jointStates.size()) {
return false;
}
const JointState& state = _jointStates.at(index);
rotation = state.getVisibleRotationInConstrainedFrame();
return !state.rotationIsDefault(rotation);
}
void Rig::clearJointState(int index) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
}
}
void Rig::clearJointStates() {
_jointStates.clear();
}
void Rig::clearJointAnimationPriority(int index) {
if (index != -1 && index < _jointStates.size()) {
_jointStates[index]._animationPriority = 0.0f;
}
}
float Rig::getJointAnimatinoPriority(int index) {
if (index != -1 && index < _jointStates.size()) {
return _jointStates[index]._animationPriority;
}
return 0.0f;
}
void Rig::setJointAnimatinoPriority(int index, float newPriority) {
if (index != -1 && index < _jointStates.size()) {
_jointStates[index]._animationPriority = newPriority;
}
}
void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
if (valid) {
state.setRotationInConstrainedFrame(rotation, priority);
} else {
state.restoreRotation(1.0f, priority);
}
}
}
void Rig::restoreJointRotation(int index, float fraction, float priority) {
if (index != -1 && index < _jointStates.size()) {
_jointStates[index].restoreRotation(fraction, priority);
}
}
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
glm::vec3 translation, glm::quat rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in world-frame
position = translation + rotation * _jointStates[jointIndex].getPosition();
return true;
}
bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in model-frame
position = extractTranslation(_jointStates[jointIndex].getTransform());
return true;
}
bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
result = rotation * _jointStates[jointIndex].getRotation();
return true;
}
bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
rotation = _jointStates[jointIndex].getRotation();
return true;
}
bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
result = rotation * _jointStates[jointIndex].getRotation();
return true;
}
bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
glm::vec3 translation, glm::quat rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
// position is in world-frame
position = translation + rotation * _jointStates[jointIndex].getVisiblePosition();
return true;
}
bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false;
}
result = rotation * _jointStates[jointIndex].getVisibleRotation();
return true;
}
glm::mat4 Rig::getJointTransform(int jointIndex) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return glm::mat4();
}
return _jointStates[jointIndex].getTransform();
}
glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return glm::mat4();
}
return _jointStates[jointIndex].getVisibleTransform();
}
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
if (!_enableRig) {
return;
}
bool isMoving = false;
glm::vec3 front = worldRotation * IDENTITY_FRONT;
float forwardSpeed = glm::dot(worldVelocity, front);
float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT);
float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
auto updateRole = [&](const QString& role, bool isOn) {
isMoving = isMoving || isOn;
if (isOn) {
if (!isRunningRole(role)) {
qCDebug(animation) << "Rig STARTING" << role;
startAnimationByRole(role);
}
} else {
if (isRunningRole(role)) {
qCDebug(animation) << "Rig stopping" << role;
stopAnimationByRole(role);
}
}
};
updateRole("walk", std::abs(forwardSpeed) > 0.01f);
bool isTurning = std::abs(rightTurningSpeed) > 0.5f;
updateRole("rightTurn", isTurning && (rightTurningSpeed > 0));
updateRole("leftTurn", isTurning && (rightTurningSpeed < 0));
bool isStrafing = std::abs(rightLateralSpeed) > 0.01f;
updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f));
updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f));
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
_lastFront = front;
_lastPosition = worldPosition;
}
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
// First normalize the fades so that they sum to 1.0.
// update the fade data in each animation (not normalized as they are an independent propert of animation)
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
float fadePerSecond = handle->getFadePerSecond();
float fade = handle->getFade();
if (fadePerSecond != 0.0f) {
fade += fadePerSecond * deltaTime;
if ((0.0f >= fade) || (fade >= 1.0f)) {
fade = glm::clamp(fade, 0.0f, 1.0f);
handle->setFadePerSecond(0.0f);
}
handle->setFade(fade);
if (fade <= 0.0f) { // stop any finished animations now
handle->setRunning(false, false); // but do not restore joints as it causes a flicker
}
}
}
// sum the remaining fade data
float fadeTotal = 0.0f;
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
fadeTotal += handle->getFade();
}
float fadeSumSoFar = 0.0f;
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
handle->setPriority(1.0f);
float normalizedFade = handle->getFade() / fadeTotal;
// simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step.
// i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result.
// The formula here for mix is based on the idea that, at each step:
// fadeSum is to normalizedFade, as (1 - mix) is to mix
// i.e., fadeSumSoFar/normalizedFade = (1 - mix)/mix
// Then we solve for mix.
// Sanity check: For the first animation, fadeSum = 0, and the mix will always be 1.
// Sanity check: For equal blending, the formula is equivalent to mix = 1 / nAnimationsSoFar++
float mix = 1.0f / ((fadeSumSoFar / normalizedFade) + 1.0f);
assert((0.0f <= mix) && (mix <= 1.0f));
fadeSumSoFar += normalizedFade;
handle->setMix(mix);
handle->simulate(deltaTime);
}
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i, parentTransform);
}
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].resetTransformChanged();
}
}
bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
if (freeLineage.isEmpty()) {
return false;
}
if (lastFreeIndex == -1) {
lastFreeIndex = freeLineage.last();
}
// this is a cyclic coordinate descent algorithm: see
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
const int ITERATION_COUNT = 1;
glm::vec3 worldAlignment = alignment;
for (int i = 0; i < ITERATION_COUNT; i++) {
// first, try to rotate the end effector as close as possible to the target rotation, if any
glm::quat endRotation;
if (useRotation) {
JointState& state = _jointStates[jointIndex];
state.setRotationInBindFrame(rotation, priority);
endRotation = state.getRotationInBindFrame();
}
// then, we go from the joint upwards, rotating the end as close as possible to the target
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform());
for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) {
int index = freeLineage.at(j);
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (!(joint.isFree || allIntermediatesFree)) {
continue;
}
glm::vec3 jointPosition = extractTranslation(state.getTransform());
glm::vec3 jointVector = endPosition - jointPosition;
glm::quat oldCombinedRotation = state.getRotation();
glm::quat combinedDelta;
float combinedWeight;
if (useRotation) {
combinedDelta = safeMix(rotation * glm::inverse(endRotation),
rotationBetween(jointVector, position - jointPosition), 0.5f);
combinedWeight = 2.0f;
} else {
combinedDelta = rotationBetween(jointVector, position - jointPosition);
combinedWeight = 1.0f;
}
if (alignment != glm::vec3() && j > 1) {
jointVector = endPosition - jointPosition;
glm::vec3 positionSum;
for (int k = j - 1; k > 0; k--) {
int index = freeLineage.at(k);
updateJointState(index, parentTransform);
positionSum += extractTranslation(_jointStates.at(index).getTransform());
}
glm::vec3 projectedCenterOfMass = glm::cross(jointVector,
glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector));
glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector));
const float LENGTH_EPSILON = 0.001f;
if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) {
combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment),
1.0f / (combinedWeight + 1.0f));
}
}
state.applyRotationDelta(combinedDelta, true, priority);
glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation);
endPosition = actualDelta * jointVector + jointPosition;
if (useRotation) {
endRotation = actualDelta * endRotation;
}
}
}
// now update the joint states from the top
for (int j = freeLineage.size() - 1; j >= 0; j--) {
updateJointState(freeLineage.at(j), parentTransform);
}
return true;
}
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
// NOTE: targetRotation is from bind- to model-frame
if (endIndex == -1 || _jointStates.isEmpty()) {
return;
}
if (freeLineage.isEmpty()) {
return;
}
int numFree = freeLineage.size();
// store and remember topmost parent transform
glm::mat4 topParentTransform;
{
int index = freeLineage.last();
const JointState& state = _jointStates.at(index);
const FBXJoint& joint = state.getFBXJoint();
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
topParentTransform = parentTransform;
} else {
topParentTransform = _jointStates[parentIndex].getTransform();
}
}
// this is a cyclic coordinate descent algorithm: see
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
// keep track of the position of the end-effector
JointState& endState = _jointStates[endIndex];
glm::vec3 endPosition = endState.getPosition();
float distanceToGo = glm::distance(targetPosition, endPosition);
const int MAX_ITERATION_COUNT = 2;
const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
int numIterations = 0;
do {
++numIterations;
// moving up, rotate each free joint to get endPosition closer to target
for (int j = 1; j < numFree; j++) {
int nextIndex = freeLineage.at(j);
JointState& nextState = _jointStates[nextIndex];
FBXJoint nextJoint = nextState.getFBXJoint();
if (! nextJoint.isFree) {
continue;
}
glm::vec3 pivot = nextState.getPosition();
glm::vec3 leverArm = endPosition - pivot;
float leverLength = glm::length(leverArm);
if (leverLength < EPSILON) {
continue;
}
glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot);
// We want to mix the shortest rotation with one that will pull the system down with gravity
// so that limbs don't float unrealistically. To do this we compute a simplified center of mass
// where each joint has unit mass and we don't bother averaging it because we only need direction.
if (j > 1) {
glm::vec3 centerOfMass(0.0f);
for (int k = 0; k < j; ++k) {
int massIndex = freeLineage.at(k);
centerOfMass += _jointStates[massIndex].getPosition() - pivot;
}
// the gravitational effect is a rotation that tends to align the two cross products
const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f);
glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm),
glm::cross(worldAlignment, leverArm));
float gravityAngle = glm::angle(gravityDelta);
const float MIN_GRAVITY_ANGLE = 0.1f;
float mixFactor = 0.5f;
if (gravityAngle < MIN_GRAVITY_ANGLE) {
// the final rotation is a mix of the two
mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
}
deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
}
// Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
// in the process. This provides stability to the IK solution for most models.
glm::quat oldNextRotation = nextState.getRotation();
float mixFactor = 0.03f;
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
// measure the result of the rotation which may have been modified by
// blending and constraints
glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation);
endPosition = pivot + actualDelta * leverArm;
}
// recompute transforms from the top down
glm::mat4 currentParentTransform = topParentTransform;
for (int j = numFree - 1; j >= 0; --j) {
JointState& freeState = _jointStates[freeLineage.at(j)];
freeState.computeTransform(currentParentTransform);
currentParentTransform = freeState.getTransform();
}
// measure our success
endPosition = endState.getPosition();
distanceToGo = glm::distance(targetPosition, endPosition);
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
// set final rotation of the end joint
endState.setRotationInBindFrame(targetRotation, priority, true);
}
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
foreach (int index, freeLineage) {
JointState& state = _jointStates[index];
state.restoreRotation(fraction, priority);
}
return true;
}
float Rig::getLimbLength(int jointIndex, const QVector<int>& freeLineage,
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return 0.0f;
}
float length = 0.0f;
float lengthScale = (scale.x + scale.y + scale.z) / 3.0f;
for (int i = freeLineage.size() - 2; i >= 0; i--) {
length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale;
}
return length;
}
glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
state.setRotationInBindFrame(rotation, priority, constrain);
endRotation = state.getRotationInBindFrame();
return endRotation;
}
glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return glm::vec3();
}
return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame();
}
glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain, float mix) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
state.setRotationInConstrainedFrame(targetRotation, priority, constrain, mix);
endRotation = state.getRotationInConstrainedFrame();
return endRotation;
}
void Rig::updateVisibleJointStates() {
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].slaveVisibleTransform();
}
}
void Rig::setJointTransform(int jointIndex, glm::mat4 newTransform) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return;
}
_jointStates[jointIndex].setTransform(newTransform);
}
void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return;
}
_jointStates[jointIndex].setVisibleTransform(newTransform);
}
void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return;
}
_jointStates[jointIndex].applyRotationDelta(delta, constrain, priority);
}
glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return glm::quat();
}
return _jointStates[jointIndex].getDefaultRotationInParentFrame();
}
void Rig::updateFromHeadParameters(const HeadParameters& params) {
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
updateEyeJoint(params.leftEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
updateEyeJoint(params.rightEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
}
void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
// get the rotation axes in joint space and use them to adjust the rotation
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index));
setJointRotationInConstrainedFrame(index,
glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * zAxis) *
glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * xAxis) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * yAxis) *
getJointState(index).getFBXJoint().rotation, DEFAULT_PRIORITY);
}
}
void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
auto joint = _jointStates[index].getFBXJoint();
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 axes = glm::mat3_cast(glm::quat());
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation);
glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways));
pitchYawRoll -= lean;
setJointRotationInConstrainedFrame(index,
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) *
glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) *
glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) *
joint.rotation, DEFAULT_PRIORITY);
}
}
void Rig::updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
auto joint = _jointStates[index].getFBXJoint();
// NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual.
glm::mat4 inverse = glm::inverse(parentState.getTransform() *
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAtDelta = lookAt;
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f));
glm::quat between = rotationBetween(front, lookAt);
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
float angle = glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE);
glm::quat rot = glm::angleAxis(angle, glm::axis(between));
setJointRotationInConstrainedFrame(index, rot * joint.rotation, DEFAULT_PRIORITY);
}
}

View file

@ -0,0 +1,187 @@
//
// Rig.h
// libraries/animation/src/
//
// Produces animation data and hip placement for the current timestamp.
//
// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/*
Things we want to be able to do, that I think we cannot do now:
* Stop an animation at a given time so that it can be examined visually or in a test harness. (I think we can already stop animation and set frame to a computed float? But does that move the bones?)
* Play two animations, blending between them. (Current structure just has one, under script control.)
* Fade in an animation over another.
* Apply IK, lean, head pointing or other overrides relative to previous position.
All of this depends on coordinated state.
TBD:
- What are responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop?
Is there common/copied code (e.g., ScriptableAvatar::update)?
- How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object
physics?
- Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the
system choosing randomly?
- Distribute some doc from here to the right files if it turns out to be correct:
- AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything
equivalent to editEntity.
But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject
is to AnimationPointer?
*/
#ifndef __hifi__Rig__
#define __hifi__Rig__
#include <QObject>
#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS
class AnimationHandle;
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
class Rig;
typedef std::shared_ptr<Rig> RigPointer;
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
public:
struct HeadParameters {
float leanSideways = 0.0f; // degrees
float leanForward = 0.0f; // degrees
float torsoTwist = 0.0f; // degrees
glm::quat localHeadOrientation = glm::quat();
glm::quat worldHeadOrientation = glm::quat();
glm::vec3 eyeLookAt = glm::vec3(); // world space
glm::vec3 eyeSaccade = glm::vec3(); // world space
int leanJointIndex = -1;
int neckJointIndex = -1;
int leftEyeJointIndex = -1;
int rightEyeJointIndex = -1;
void dump() const;
};
virtual ~Rig() {}
RigPointer getRigPointer() { return shared_from_this(); }
AnimationHandlePointer createAnimationHandle();
void removeAnimationHandle(const AnimationHandlePointer& handle);
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
void addRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation.
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
void deleteAnimations();
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
void stopAnimation(const QString& url);
void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
void stopAnimationByRole(const QString& role);
AnimationHandlePointer addAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false);
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
int rootJointIndex,
int leftHandJointIndex,
int leftElbowJointIndex,
int leftShoulderJointIndex,
int rightHandJointIndex,
int rightElbowJointIndex,
int rightShoulderJointIndex);
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
int getJointStateCount() const { return _jointStates.size(); }
int indexOfJoint(const QString& jointName) ;
void initJointTransforms(glm::mat4 parentTransform);
void clearJointTransformTranslation(int jointIndex);
void reset(const QVector<FBXJoint>& fbxJoints);
bool getJointStateRotation(int index, glm::quat& rotation) const;
void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority);
JointState getJointState(int jointIndex) const; // XXX
bool getVisibleJointState(int index, glm::quat& rotation) const;
void clearJointState(int index);
void clearJointStates();
void clearJointAnimationPriority(int index);
float getJointAnimatinoPriority(int index);
void setJointAnimatinoPriority(int index, float newPriority);
void setJointState(int index, bool valid, const glm::quat& rotation, float priority);
void restoreJointRotation(int index, float fraction, float priority);
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
glm::vec3 translation, glm::quat rotation) const;
bool getJointPosition(int jointIndex, glm::vec3& position) const;
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
glm::vec3 translation, glm::quat rotation) const;
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const;
glm::mat4 getJointTransform(int jointIndex) const;
void setJointTransform(int jointIndex, glm::mat4 newTransform);
glm::mat4 getJointVisibleTransform(int jointIndex) const;
void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform);
// Start or stop animations as needed.
void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation);
// Regardless of who started the animations or how many, update the joints.
void updateAnimations(float deltaTime, glm::mat4 parentTransform);
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform);
void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
const QVector<int>& freeLineage, glm::mat4 parentTransform);
bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage);
float getLimbLength(int jointIndex, const QVector<int>& freeLineage,
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const;
glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false);
glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex);
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
float priority, bool constrain = false, float mix = 1.0f);
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
void updateVisibleJointStates();
virtual void updateJointState(int index, glm::mat4 parentTransform) = 0;
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
void updateFromHeadParameters(const HeadParameters& params);
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
float scale, float priority) = 0;
protected:
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
void updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist);
void updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
QVector<JointState> _jointStates;
int _rootJointIndex = -1;
int _leftHandJointIndex = -1;
int _leftElbowJointIndex = -1;
int _leftShoulderJointIndex = -1;
int _rightHandJointIndex = -1;
int _rightElbowJointIndex = -1;
int _rightShoulderJointIndex = -1;
QList<AnimationHandlePointer> _animationHandles;
QList<AnimationHandlePointer> _runningAnimations;
bool _enableRig;
glm::vec3 _lastFront;
glm::vec3 _lastPosition;
};
#endif /* defined(__hifi__Rig__) */

Some files were not shown because too many files have changed in this diff Show more