mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into domain-tunnel
This commit is contained in:
commit
20f97792bf
31 changed files with 1438 additions and 883 deletions
249
cmake/modules/FindSDL2.cmake
Normal file
249
cmake/modules/FindSDL2.cmake
Normal file
|
@ -0,0 +1,249 @@
|
|||
# Locate SDL2 library
|
||||
# This module defines
|
||||
# SDL2_LIBRARY, the name of the library to link against
|
||||
# SDL2_FOUND, if false, do not try to link to SDL2
|
||||
# SDL2_INCLUDE_DIR, where to find SDL.h
|
||||
#
|
||||
# This module responds to the the flag:
|
||||
# SDL2_BUILDING_LIBRARY
|
||||
# If this is defined, then no SDL2_main will be linked in because
|
||||
# only applications need main().
|
||||
# Otherwise, it is assumed you are building an application and this
|
||||
# module will attempt to locate and set the the proper link flags
|
||||
# as part of the returned SDL2_LIBRARY variable.
|
||||
#
|
||||
# Don't forget to include SDL2main.h and SDL2main.m your project for the
|
||||
# OS X framework based version. (Other versions link to -lSDL2main which
|
||||
# this module will try to find on your behalf.) Also for OS X, this
|
||||
# module will automatically add the -framework Cocoa on your behalf.
|
||||
#
|
||||
#
|
||||
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
|
||||
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
|
||||
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
|
||||
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
|
||||
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
|
||||
# as appropriate. These values are used to generate the final SDL2_LIBRARY
|
||||
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
|
||||
#
|
||||
#
|
||||
# $SDL2 is an environment variable that would
|
||||
# correspond to the ./configure --prefix=$SDL2
|
||||
# used in building SDL2.
|
||||
# l.e.galup 9-20-02
|
||||
#
|
||||
# Modified by Eric Wing.
|
||||
# Added code to assist with automated building by using environmental variables
|
||||
# and providing a more controlled/consistent search behavior.
|
||||
# Added new modifications to recognize OS X frameworks and
|
||||
# additional Unix paths (FreeBSD, etc).
|
||||
# Also corrected the header search path to follow "proper" SDL2 guidelines.
|
||||
# Added a search for SDL2main which is needed by some platforms.
|
||||
# Added a search for threads which is needed by some platforms.
|
||||
# Added needed compile switches for MinGW.
|
||||
#
|
||||
# On OSX, this will prefer the Framework version (if found) over others.
|
||||
# People will have to manually change the cache values of
|
||||
# SDL2_LIBRARY to override this selection or set the CMake environment
|
||||
# CMAKE_INCLUDE_PATH to modify the search paths.
|
||||
#
|
||||
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
|
||||
# This needed to change because "proper" SDL2 convention
|
||||
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
|
||||
# reasons because not all systems place things in SDL2/ (see FreeBSD).
|
||||
#
|
||||
# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake
|
||||
# module with the minor edit of changing "SDL" to "SDL2" where necessary. This
|
||||
# was not created for redistribution, and exists temporarily pending official
|
||||
# SDL2 CMake modules.
|
||||
#
|
||||
# Note that on windows this will only search for the 32bit libraries, to search
|
||||
# for 64bit change x86/i686-w64 to x64/x86_64-w64
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2003-2009 Kitware, Inc.
|
||||
#
|
||||
# CMake - Cross Platform Makefile Generator
|
||||
# Copyright 2000-2014 Kitware, Inc.
|
||||
# Copyright 2000-2011 Insight Software Consortium
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
|
||||
# nor the names of their contributors may be used to endorse or promote
|
||||
# products derived from this software without specific prior written
|
||||
# permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES include/SDL2 include SDL2
|
||||
i686-w64-mingw32/include/SDL2
|
||||
x86_64-w64-mingw32/include/SDL2
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/include/SDL2
|
||||
/usr/include/SDL2
|
||||
/sw # Fink
|
||||
/opt/local # DarwinPorts
|
||||
/opt/csw # Blastwave
|
||||
/opt
|
||||
)
|
||||
|
||||
# Lookup the 64 bit libs on x64
|
||||
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
lib/x64
|
||||
x86_64-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
# On 32bit build find the 32bit libs
|
||||
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib
|
||||
lib/x86
|
||||
i686-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
# Non-OS X framework versions expect you to also dynamically link to
|
||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
||||
# seem to provide SDL2main for compatibility even though they don't
|
||||
# necessarily need it.
|
||||
# Lookup the 64 bit libs on x64
|
||||
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
lib/x64
|
||||
x86_64-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
# On 32bit build find the 32bit libs
|
||||
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2}
|
||||
PATH_SUFFIXES lib
|
||||
lib/x86
|
||||
i686-w64-mingw32/lib
|
||||
PATHS
|
||||
/sw
|
||||
/opt/local
|
||||
/opt/csw
|
||||
/opt
|
||||
)
|
||||
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# SDL2 may require threads on your system.
|
||||
# The Apple build may not need an explicit flag because one of the
|
||||
# frameworks may already provide it.
|
||||
# But for non-OSX systems, I will use the CMake Threads package.
|
||||
IF(NOT APPLE)
|
||||
FIND_PACKAGE(Threads)
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# MinGW needs an additional library, mwindows
|
||||
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
|
||||
# (Actually on second look, I think it only needs one of the m* libraries.)
|
||||
IF(MINGW)
|
||||
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
|
||||
ENDIF(MINGW)
|
||||
|
||||
SET(SDL2_FOUND "NO")
|
||||
IF(SDL2_LIBRARY_TEMP)
|
||||
# For SDL2main
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(SDL2MAIN_LIBRARY)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(SDL2MAIN_LIBRARY)
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
|
||||
# CMake doesn't display the -framework Cocoa string in the UI even
|
||||
# though it actually is there if I modify a pre-used variable.
|
||||
# I think it has something to do with the CACHE STRING.
|
||||
# So I use a temporary variable until the end so I can set the
|
||||
# "real" variable in one-shot.
|
||||
IF(APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
|
||||
ENDIF(APPLE)
|
||||
|
||||
# For threads, as mentioned Apple doesn't need this.
|
||||
# In fact, there seems to be a problem if I used the Threads package
|
||||
# and try using this line, so I'm just skipping it entirely for OS X.
|
||||
IF(NOT APPLE)
|
||||
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
# For MinGW library
|
||||
IF(MINGW)
|
||||
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
|
||||
ENDIF(MINGW)
|
||||
|
||||
# Set the final string here so the GUI reflects the final state.
|
||||
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
|
||||
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
|
||||
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
|
||||
|
||||
SET(SDL2_FOUND "YES")
|
||||
ENDIF(SDL2_LIBRARY_TEMP)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)
|
|
@ -1312,7 +1312,6 @@ var toolBar = (function () {
|
|||
if (clickedOverlay === loadFileMenuItem) {
|
||||
toggleNewModelButton(false);
|
||||
|
||||
// TODO BUG: this is bug, if the user has never uploaded a model, this will throw an JS exception
|
||||
file = Window.browse("Select your model file ...",
|
||||
Settings.getValue("LastModelUploadLocation").path(),
|
||||
"Model files (*.fst *.fbx)");
|
||||
|
|
|
@ -9,72 +9,23 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// TODO Update to work with any controller that is plugged in.
|
||||
var CONTROLLER_NAMES = [
|
||||
"Wireless 360 Controller",
|
||||
"Controller (XBOX 360 For Windows)",
|
||||
"Controller", // Wired 360 controller
|
||||
]
|
||||
var gamepads = {};
|
||||
|
||||
for (var i = 0; i < CONTROLLER_NAMES.length; i++) {
|
||||
gamepad = Joysticks.joystickWithName(CONTROLLER_NAMES[i]);
|
||||
if (gamepad) {
|
||||
print("Found controller: " + CONTROLLER_NAMES[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Function -> button/axis mappings
|
||||
var AXIS_STRAFE = Joysticks.AXIS_LEFT_X;
|
||||
var AXIS_FORWARD = Joysticks.AXIS_LEFT_Y;
|
||||
var AXIS_ROTATE = Joysticks.AXIS_RIGHT_X;
|
||||
|
||||
if (!gamepad) {
|
||||
print("No gamepad found.");
|
||||
}
|
||||
var BUTTON_TURN_AROUND = Joysticks.BUTTON_RIGHT_STICK;
|
||||
|
||||
// Controller axis/button mappings
|
||||
var GAMEPAD = {
|
||||
AXES: {
|
||||
LEFT_JOYSTICK_X: 0,
|
||||
LEFT_JOYSTICK_Y: 1,
|
||||
var BUTTON_FLY_UP = Joysticks.BUTTON_RIGHT_SHOULDER;
|
||||
var BUTTON_FLY_DOWN = Joysticks.BUTTON_LEFT_SHOULDER;
|
||||
var BUTTON_WARP = Joysticks.BUTTON_FACE_BOTTOM;
|
||||
|
||||
RIGHT_JOYSTICK_X: 2,
|
||||
RIGHT_JOYSTICK_Y: 3,
|
||||
|
||||
LEFT_TRIGGER: 4,
|
||||
RIGHT_TRIGGER: 5,
|
||||
},
|
||||
BUTTONS: {
|
||||
DPAD_UP: 0,
|
||||
DPAD_DOWN: 1,
|
||||
DPAD_LEFT: 2,
|
||||
DPAD_RIGHT: 3,
|
||||
|
||||
LEFT_JOYSTICK: 6,
|
||||
RIGHT_JOYSTICK: 7,
|
||||
|
||||
LEFT_BUMPER: 8,
|
||||
RIGHT_BUMPER: 9,
|
||||
|
||||
// Face buttons, ABXY on an XBOX controller
|
||||
FACE_BOTTOM: 11,
|
||||
FACE_RIGHT: 12,
|
||||
FACE_LEFT: 13,
|
||||
FACE_TOP: 14,
|
||||
}
|
||||
}
|
||||
|
||||
// Button/axis mappings
|
||||
var AXIS_STRAFE = GAMEPAD.AXES.LEFT_JOYSTICK_X;
|
||||
var AXIS_FORWARD = GAMEPAD.AXES.LEFT_JOYSTICK_Y;
|
||||
var AXIS_ROTATE = GAMEPAD.AXES.RIGHT_JOYSTICK_X;
|
||||
|
||||
var BUTTON_TURN_AROUND = GAMEPAD.BUTTONS.RIGHT_JOYSTICK;
|
||||
|
||||
var BUTTON_FLY_UP = GAMEPAD.BUTTONS.RIGHT_BUMPER;
|
||||
var BUTTON_FLY_DOWN = GAMEPAD.BUTTONS.LEFT_BUMPER
|
||||
var BUTTON_WARP = GAMEPAD.BUTTONS.FACE_BOTTOM;
|
||||
|
||||
var BUTTON_WARP_FORWARD = GAMEPAD.BUTTONS.DPAD_UP;
|
||||
var BUTTON_WARP_BACKWARD = GAMEPAD.BUTTONS.DPAD_DOWN;
|
||||
var BUTTON_WARP_LEFT = GAMEPAD.BUTTONS.DPAD_LEFT;
|
||||
var BUTTON_WARP_RIGHT = GAMEPAD.BUTTONS.DPAD_RIGHT;
|
||||
var BUTTON_WARP_FORWARD = Joysticks.BUTTON_DPAD_UP;
|
||||
var BUTTON_WARP_BACKWARD = Joysticks.BUTTON_DPAD_DOWN;
|
||||
var BUTTON_WARP_LEFT = Joysticks.BUTTON_DPAD_LEFT;
|
||||
var BUTTON_WARP_RIGHT = Joysticks.BUTTON_DPAD_RIGHT;
|
||||
|
||||
// Distance in meters to warp via BUTTON_WARP_*
|
||||
var WARP_DISTANCE = 1;
|
||||
|
@ -272,9 +223,27 @@ function update(dt) {
|
|||
updateWarp();
|
||||
}
|
||||
|
||||
if (gamepad) {
|
||||
function addJoystick(gamepad) {
|
||||
gamepad.axisValueChanged.connect(reportAxisValue);
|
||||
gamepad.buttonStateChanged.connect(reportButtonValue);
|
||||
|
||||
Script.update.connect(update);
|
||||
gamepads[gamepad.instanceId] = gamepad;
|
||||
|
||||
print("Added gamepad: " + gamepad.name + " (" + gamepad.instanceId + ")");
|
||||
}
|
||||
|
||||
function removeJoystick(gamepad) {
|
||||
delete gamepads[gamepad.instanceId]
|
||||
|
||||
print("Removed gamepad: " + gamepad.name + " (" + gamepad.instanceId + ")");
|
||||
}
|
||||
|
||||
var allJoysticks = Joysticks.getAllJoysticks();
|
||||
for (var i = 0; i < allJoysticks.length; i++) {
|
||||
addJoystick(allJoysticks[i]);
|
||||
}
|
||||
|
||||
Joysticks.joystickAdded.connect(addJoystick);
|
||||
Joysticks.joystickRemoved.connect(removeJoystick);
|
||||
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -16,6 +16,8 @@ Script.include("libraries/globals.js");
|
|||
SelectionDisplay = (function () {
|
||||
var that = {};
|
||||
|
||||
var MINIMUM_DIMENSION = 0.001;
|
||||
|
||||
var mode = "UNKNOWN";
|
||||
var overlayNames = new Array();
|
||||
var lastAvatarPosition = MyAvatar.position;
|
||||
|
@ -58,9 +60,17 @@ SelectionDisplay = (function () {
|
|||
var rotateHandleColor = { red: 0, green: 0, blue: 0 };
|
||||
var rotateHandleAlpha = 0.7;
|
||||
|
||||
var highlightedHandleColor = { red: 255, green: 0, blue: 0 };
|
||||
var highlightedHandleAlpha = 0.7;
|
||||
|
||||
var previousHandle = false;
|
||||
var previousHandleColor;
|
||||
var previousHandleAlpha;
|
||||
|
||||
var grabberSizeCorner = 0.025;
|
||||
var grabberSizeEdge = 0.015;
|
||||
var grabberSizeFace = 0.025;
|
||||
var grabberAlpha = 1;
|
||||
var grabberColorCorner = { red: 120, green: 120, blue: 120 };
|
||||
var grabberColorEdge = { red: 0, green: 0, blue: 0 };
|
||||
var grabberColorFace = { red: 120, green: 120, blue: 120 };
|
||||
|
@ -260,6 +270,9 @@ SelectionDisplay = (function () {
|
|||
visible: false,
|
||||
rotation: yawOverlayRotation,
|
||||
ignoreRayIntersection: true, // always ignore this
|
||||
hasTickMarks: true,
|
||||
majorTickMarksColor: { red: 0, green: 0, blue: 0 },
|
||||
minorTickMarksColor: { red: 0, green: 0, blue: 0 },
|
||||
});
|
||||
|
||||
var yawHandle = Overlays.addOverlay("billboard", {
|
||||
|
@ -393,14 +406,8 @@ SelectionDisplay = (function () {
|
|||
|
||||
that.highlightSelectable = function(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
var center = { x: properties.position.x, y: properties.position.y, z: properties.position.z };
|
||||
Overlays.editOverlay(highlightBox,
|
||||
{
|
||||
visible: true,
|
||||
position: center,
|
||||
dimensions: properties.dimensions,
|
||||
rotation: properties.rotation,
|
||||
});
|
||||
Overlays.editOverlay(highlightBox, { visible: true, position: properties.boundingBox.center,
|
||||
dimensions: properties.boundingBox.dimensions });
|
||||
};
|
||||
|
||||
that.unhighlightSelectable = function(entityID) {
|
||||
|
@ -642,8 +649,8 @@ SelectionDisplay = (function () {
|
|||
|
||||
Overlays.editOverlay(highlightBox, { visible: false });
|
||||
|
||||
Overlays.editOverlay(selectionBox, { visible: selectionBoxVisible, position: objectCenter, dimensions: properties.dimensions,
|
||||
rotation: properties.rotation,});
|
||||
Overlays.editOverlay(selectionBox, { visible: selectionBoxVisible, position: properties.boundingBox.center,
|
||||
dimensions: properties.boundingBox.dimensions });
|
||||
|
||||
|
||||
Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: boundsCenter.x, y: top + grabberMoveUpOffset, z: boundsCenter.z } });
|
||||
|
@ -889,6 +896,11 @@ SelectionDisplay = (function () {
|
|||
// dimensions changes by: (oldNEAR - newNEAR)
|
||||
var changeInDimensions = { x: 0, y: 0, z: (oldNEAR - newNEAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: 0, y: 0, z: (oldNEAR - newNEAR) * -0.5 };
|
||||
var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition);
|
||||
var wantDebug = false;
|
||||
|
@ -934,6 +946,11 @@ SelectionDisplay = (function () {
|
|||
var newFAR = oldFAR + vector.z;
|
||||
var changeInDimensions = { x: 0, y: 0, z: (newFAR - oldFAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: 0, y: 0, z: (newFAR - oldFAR) * 0.5 };
|
||||
var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition);
|
||||
var wantDebug = false;
|
||||
|
@ -979,6 +996,11 @@ SelectionDisplay = (function () {
|
|||
var newTOP = oldTOP + vector.y;
|
||||
var changeInDimensions = { x: 0, y: (newTOP - oldTOP), z: 0 };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: 0, y: (newTOP - oldTOP) * 0.5, z: 0 };
|
||||
var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition);
|
||||
var wantDebug = false;
|
||||
|
@ -1023,6 +1045,11 @@ SelectionDisplay = (function () {
|
|||
var newBOTTOM = oldBOTTOM + vector.y;
|
||||
var changeInDimensions = { x: 0, y: (oldBOTTOM - newBOTTOM), z: 0 };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: 0, y: (oldBOTTOM - newBOTTOM) * -0.5, z: 0 };
|
||||
var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition);
|
||||
var wantDebug = false;
|
||||
|
@ -1067,6 +1094,11 @@ SelectionDisplay = (function () {
|
|||
var newRIGHT = oldRIGHT + vector.x;
|
||||
var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: 0 , z: 0 };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, y: 0, z: 0 };
|
||||
var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition);
|
||||
var wantDebug = false;
|
||||
|
@ -1111,6 +1143,11 @@ SelectionDisplay = (function () {
|
|||
var newLEFT = oldLEFT + vector.x;
|
||||
var changeInDimensions = { x: (oldLEFT - newLEFT), y: 0, z: 0 };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (oldLEFT - newLEFT) * -0.5, y: 0, z: 0 };
|
||||
var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition);
|
||||
var wantDebug = false;
|
||||
|
@ -1163,6 +1200,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newBOTTOM - oldBOTTOM) , z: (newNEAR - oldNEAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5,
|
||||
y: (newBOTTOM - oldBOTTOM) * -0.5,
|
||||
z: (newNEAR - oldNEAR) * -0.5 };
|
||||
|
@ -1217,6 +1267,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newBOTTOM - oldBOTTOM) , z: (newNEAR - oldNEAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5,
|
||||
y: (newBOTTOM - oldBOTTOM) * -0.5,
|
||||
z: (newNEAR - oldNEAR) * -0.5 };
|
||||
|
@ -1271,6 +1334,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newTOP - oldTOP) , z: (newNEAR - oldNEAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5,
|
||||
y: (newTOP - oldTOP) * 0.5,
|
||||
z: (newNEAR - oldNEAR) * -0.5 };
|
||||
|
@ -1325,6 +1401,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newTOP - oldTOP) , z: (newNEAR - oldNEAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5,
|
||||
y: (newTOP - oldTOP) * 0.5,
|
||||
z: (newNEAR - oldNEAR) * -0.5 };
|
||||
|
@ -1379,6 +1468,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newBOTTOM - oldBOTTOM) , z: (newFAR - oldFAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5,
|
||||
y: (newBOTTOM - oldBOTTOM) * -0.5,
|
||||
z: (newFAR - oldFAR) * 0.5 };
|
||||
|
@ -1433,6 +1535,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newBOTTOM - oldBOTTOM) , z: (newFAR - oldFAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5,
|
||||
y: (newBOTTOM - oldBOTTOM) * -0.5,
|
||||
z: (newFAR - oldFAR) * 0.5 };
|
||||
|
@ -1487,6 +1602,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newTOP - oldTOP) , z: (newFAR - oldFAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5,
|
||||
y: (newTOP - oldTOP) * 0.5,
|
||||
z: (newFAR - oldFAR) * 0.5 };
|
||||
|
@ -1541,6 +1669,19 @@ SelectionDisplay = (function () {
|
|||
|
||||
var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newTOP - oldTOP) , z: (newFAR - oldFAR) };
|
||||
var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions);
|
||||
|
||||
if (newDimensions.x < MINIMUM_DIMENSION) {
|
||||
newDimensions.x = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.y < MINIMUM_DIMENSION) {
|
||||
newDimensions.y = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
if (newDimensions.z < MINIMUM_DIMENSION) {
|
||||
newDimensions.z = MINIMUM_DIMENSION;
|
||||
}
|
||||
|
||||
var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5,
|
||||
y: (newTOP - oldTOP) * 0.5,
|
||||
z: (newFAR - oldFAR) * 0.5 };
|
||||
|
@ -1573,6 +1714,13 @@ SelectionDisplay = (function () {
|
|||
if (!entitySelected || mode !== "ROTATE_YAW") {
|
||||
return; // not allowed
|
||||
}
|
||||
|
||||
var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems");
|
||||
|
||||
if (debug) {
|
||||
print("rotateYaw()...");
|
||||
print(" event.x,y:" + event.x + "," + event.y);
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false});
|
||||
|
@ -1583,7 +1731,14 @@ SelectionDisplay = (function () {
|
|||
Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true });
|
||||
|
||||
var result = Overlays.findRayIntersection(pickRay);
|
||||
|
||||
if (debug) {
|
||||
print(" findRayIntersection() .... result.intersects:" + result.intersects);
|
||||
}
|
||||
|
||||
if (result.intersects) {
|
||||
|
||||
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var center = yawCenter;
|
||||
var zero = yawZero;
|
||||
|
@ -1599,7 +1754,11 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
|
||||
// for debugging
|
||||
//Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection });
|
||||
if (debug) {
|
||||
Vec3.print(" result.intersection:",result.intersection);
|
||||
Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection });
|
||||
print(" angleFromZero:" + angleFromZero);
|
||||
}
|
||||
|
||||
var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 });
|
||||
var newRotation = Quat.multiply(yawChange, originalRotation);
|
||||
|
@ -1620,11 +1779,15 @@ SelectionDisplay = (function () {
|
|||
if (snapToInner) {
|
||||
Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 });
|
||||
Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius,
|
||||
majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0,
|
||||
majorTickMarksLength: -0.25, minorTickMarksLength: 0, });
|
||||
} else {
|
||||
Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 });
|
||||
Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius,
|
||||
majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
|
||||
majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1634,6 +1797,13 @@ SelectionDisplay = (function () {
|
|||
if (!entitySelected || mode !== "ROTATE_PITCH") {
|
||||
return; // not allowed
|
||||
}
|
||||
var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems");
|
||||
|
||||
if (debug) {
|
||||
print("rotatePitch()...");
|
||||
print(" event.x,y:" + event.x + "," + event.y);
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false});
|
||||
Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false });
|
||||
|
@ -1642,7 +1812,11 @@ SelectionDisplay = (function () {
|
|||
Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true });
|
||||
var result = Overlays.findRayIntersection(pickRay);
|
||||
|
||||
|
||||
if (debug) {
|
||||
print(" findRayIntersection() .... result.intersects:" + result.intersects);
|
||||
}
|
||||
|
||||
if (result.intersects) {
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var center = pitchCenter;
|
||||
|
@ -1659,7 +1833,11 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
|
||||
// for debugging
|
||||
//Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection });
|
||||
if (debug) {
|
||||
Vec3.print(" result.intersection:",result.intersection);
|
||||
Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection });
|
||||
print(" angleFromZero:" + angleFromZero);
|
||||
}
|
||||
|
||||
var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 });
|
||||
var newRotation = Quat.multiply(pitchChange, originalRotation);
|
||||
|
@ -1680,11 +1858,15 @@ SelectionDisplay = (function () {
|
|||
if (snapToInner) {
|
||||
Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 });
|
||||
Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius,
|
||||
majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0,
|
||||
majorTickMarksLength: -0.25, minorTickMarksLength: 0, });
|
||||
} else {
|
||||
Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 });
|
||||
Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius,
|
||||
majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
|
||||
majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1693,6 +1875,13 @@ SelectionDisplay = (function () {
|
|||
if (!entitySelected || mode !== "ROTATE_ROLL") {
|
||||
return; // not allowed
|
||||
}
|
||||
var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems");
|
||||
|
||||
if (debug) {
|
||||
print("rotateRoll()...");
|
||||
print(" event.x,y:" + event.x + "," + event.y);
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false});
|
||||
Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false });
|
||||
|
@ -1701,6 +1890,11 @@ SelectionDisplay = (function () {
|
|||
Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true });
|
||||
var result = Overlays.findRayIntersection(pickRay);
|
||||
|
||||
if (debug) {
|
||||
print(" findRayIntersection() .... result.intersects:" + result.intersects);
|
||||
}
|
||||
|
||||
if (result.intersects) {
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var center = rollCenter;
|
||||
|
@ -1717,7 +1911,11 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
|
||||
// for debugging
|
||||
//Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection });
|
||||
if (debug) {
|
||||
Vec3.print(" result.intersection:",result.intersection);
|
||||
Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection });
|
||||
print(" angleFromZero:" + angleFromZero);
|
||||
}
|
||||
|
||||
var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero });
|
||||
var newRotation = Quat.multiply(rollChange, originalRotation);
|
||||
|
@ -1738,11 +1936,15 @@ SelectionDisplay = (function () {
|
|||
if (snapToInner) {
|
||||
Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 });
|
||||
Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius,
|
||||
majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0,
|
||||
majorTickMarksLength: -0.25, minorTickMarksLength: 0, });
|
||||
} else {
|
||||
Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 });
|
||||
Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius });
|
||||
Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius,
|
||||
majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
|
||||
majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1755,6 +1957,7 @@ SelectionDisplay = (function () {
|
|||
};
|
||||
|
||||
that.mousePressEvent = function(event) {
|
||||
|
||||
var somethingClicked = false;
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
|
@ -1978,8 +2181,11 @@ SelectionDisplay = (function () {
|
|||
Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 0 });
|
||||
|
||||
// for debugging
|
||||
//Overlays.editOverlay(rotateZeroOverlay, { visible: true, start: overlayCenter, end: result.intersection });
|
||||
//Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: overlayCenter, end: result.intersection });
|
||||
var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems");
|
||||
if (debug) {
|
||||
Overlays.editOverlay(rotateZeroOverlay, { visible: true, start: overlayCenter, end: result.intersection });
|
||||
Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: overlayCenter, end: result.intersection });
|
||||
}
|
||||
|
||||
Overlays.editOverlay(yawHandle, { visible: false });
|
||||
Overlays.editOverlay(pitchHandle, { visible: false });
|
||||
|
@ -2055,6 +2261,8 @@ SelectionDisplay = (function () {
|
|||
Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false });
|
||||
Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false });
|
||||
Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false });
|
||||
|
||||
return somethingClicked;
|
||||
};
|
||||
|
||||
that.mouseMoveEvent = function(event) {
|
||||
|
@ -2120,9 +2328,99 @@ SelectionDisplay = (function () {
|
|||
that.stretchLEFT(event);
|
||||
break;
|
||||
default:
|
||||
// nothing to do by default
|
||||
break;
|
||||
// if not in any specific mode, then just look for handles to highlight...
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var result = Overlays.findRayIntersection(pickRay);
|
||||
var pickedColor;
|
||||
var pickedAlpha;
|
||||
var highlightNeeded = false;
|
||||
|
||||
if (result.intersects) {
|
||||
switch(result.overlayID) {
|
||||
case yawHandle:
|
||||
case pitchHandle:
|
||||
case rollHandle:
|
||||
pickedColor = rotateHandleColor;
|
||||
pickedAlpha = rotateHandleAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberMoveUp:
|
||||
pickedColor = rotateHandleColor;
|
||||
pickedAlpha = rotateHandleAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberLBN:
|
||||
case grabberLBF:
|
||||
case grabberRBN:
|
||||
case grabberRBF:
|
||||
case grabberLTN:
|
||||
case grabberLTF:
|
||||
case grabberRTN:
|
||||
case grabberRTF:
|
||||
pickedColor = grabberColorCorner;
|
||||
pickedAlpha = grabberAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberTOP:
|
||||
case grabberBOTTOM:
|
||||
case grabberLEFT:
|
||||
case grabberRIGHT:
|
||||
case grabberNEAR:
|
||||
case grabberFAR:
|
||||
pickedColor = grabberColorFace;
|
||||
pickedAlpha = grabberAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
case grabberEdgeTR:
|
||||
case grabberEdgeTL:
|
||||
case grabberEdgeTF:
|
||||
case grabberEdgeTN:
|
||||
case grabberEdgeBR:
|
||||
case grabberEdgeBL:
|
||||
case grabberEdgeBF:
|
||||
case grabberEdgeBN:
|
||||
case grabberEdgeNR:
|
||||
case grabberEdgeNL:
|
||||
case grabberEdgeFR:
|
||||
case grabberEdgeFL:
|
||||
pickedColor = grabberColorEdge;
|
||||
pickedAlpha = grabberAlpha;
|
||||
highlightNeeded = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (previousHandle) {
|
||||
Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha });
|
||||
previousHandle = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (highlightNeeded) {
|
||||
if (previousHandle) {
|
||||
Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha });
|
||||
previousHandle = false;
|
||||
}
|
||||
Overlays.editOverlay(result.overlayID, { color: highlightedHandleColor, alpha: highlightedHandleAlpha });
|
||||
previousHandle = result.overlayID;
|
||||
previousHandleColor = pickedColor;
|
||||
previousHandleAlpha = pickedAlpha;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (previousHandle) {
|
||||
Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha });
|
||||
previousHandle = false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
that.mouseReleaseEvent = function(event) {
|
||||
|
@ -2156,8 +2454,9 @@ SelectionDisplay = (function () {
|
|||
|
||||
};
|
||||
|
||||
Controller.mousePressEvent.connect(that.mousePressEvent);
|
||||
Controller.mouseMoveEvent.connect(that.mouseMoveEvent);
|
||||
// NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these:
|
||||
// Controller.mousePressEvent.connect(that.mousePressEvent);
|
||||
// Controller.mouseMoveEvent.connect(that.mouseMoveEvent);
|
||||
Controller.mouseReleaseEvent.connect(that.mouseReleaseEvent);
|
||||
|
||||
return that;
|
||||
|
|
|
@ -238,7 +238,6 @@ var toolBar = (function () {
|
|||
if (clickedOverlay === loadFileMenuItem) {
|
||||
toggleNewModelButton(false);
|
||||
|
||||
// TODO BUG: this is bug, if the user has never uploaded a model, this will throw an JS exception
|
||||
file = Window.browse("Select your model file ...",
|
||||
Settings.getValue("LastModelUploadLocation").path(),
|
||||
"Model files (*.fst *.fbx)");
|
||||
|
@ -338,13 +337,15 @@ function rayPlaneIntersection(pickRay, point, normal) {
|
|||
|
||||
function mousePressEvent(event) {
|
||||
mouseLastPosition = { x: event.x, y: event.y };
|
||||
entitySelected = false;
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
|
||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
|
||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
||||
// Event handled; do nothing.
|
||||
return;
|
||||
} else {
|
||||
entitySelected = false;
|
||||
selectionDisplay.unselectAll();
|
||||
|
||||
// If we aren't active and didn't click on an overlay: quit
|
||||
if (!isActive) {
|
||||
return;
|
||||
|
@ -440,34 +441,36 @@ function mouseMoveEvent(event) {
|
|||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// allow the selectionDisplay to handle the event first, if it doesn't handle it, then do our own thing
|
||||
if (selectionDisplay.mouseMoveEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
if (!entitySelected) {
|
||||
var entityIntersection = Entities.findRayIntersection(pickRay);
|
||||
if (entityIntersection.accurate) {
|
||||
if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) {
|
||||
selectionDisplay.unhighlightSelectable(highlightedEntityID);
|
||||
highlightedEntityID = { id: -1, isKnownID: false };
|
||||
}
|
||||
|
||||
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
|
||||
entityIntersection.properties.position)) * 180 / 3.14;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (entityIntersection.entityID.isKnownID && sizeOK) {
|
||||
if (wantEntityGlow) {
|
||||
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
|
||||
}
|
||||
highlightedEntityID = entityIntersection.entityID;
|
||||
selectionDisplay.highlightSelectable(entityIntersection.entityID);
|
||||
}
|
||||
|
||||
var entityIntersection = Entities.findRayIntersection(pickRay);
|
||||
if (entityIntersection.accurate) {
|
||||
if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) {
|
||||
selectionDisplay.unhighlightSelectable(highlightedEntityID);
|
||||
highlightedEntityID = { id: -1, isKnownID: false };
|
||||
}
|
||||
return;
|
||||
|
||||
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
|
||||
entityIntersection.properties.position)) * 180 / 3.14;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (entityIntersection.entityID.isKnownID && sizeOK) {
|
||||
if (wantEntityGlow) {
|
||||
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
|
||||
}
|
||||
highlightedEntityID = entityIntersection.entityID;
|
||||
selectionDisplay.highlightSelectable(entityIntersection.entityID);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,6 +517,7 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
||||
Menu.addMenuItem({ menuName: "Developer", menuItemName: "Debug Ryans Rotation Problems", isCheckable: true });
|
||||
}
|
||||
|
||||
setupModelMenus(); // do this when first running our script.
|
||||
|
@ -533,6 +537,7 @@ function cleanupModelMenus() {
|
|||
Menu.removeSeparator("File", "Models");
|
||||
Menu.removeMenuItem("File", "Export Models");
|
||||
Menu.removeMenuItem("File", "Import Models");
|
||||
Menu.removeMenuItem("Developer", "Debug Ryans Rotation Problems");
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
|
|
|
@ -2,7 +2,7 @@ set(TARGET_NAME interface)
|
|||
project(${TARGET_NAME})
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL")
|
||||
set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL2")
|
||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
|
||||
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
|
||||
|
|
32
interface/resources/shaders/metavoxel_voxel_cursor.frag
Normal file
32
interface/resources/shaders/metavoxel_voxel_cursor.frag
Normal file
|
@ -0,0 +1,32 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_voxel_cursor.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/10/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
// the inner radius of the outline, squared
|
||||
const float SQUARED_OUTLINE_INNER_RADIUS = 0.81;
|
||||
|
||||
// the outer radius of the outline, squared
|
||||
const float SQUARED_OUTLINE_OUTER_RADIUS = 1.0;
|
||||
|
||||
// the inner radius of the inset, squared
|
||||
const float SQUARED_INSET_INNER_RADIUS = 0.855625;
|
||||
|
||||
// the outer radius of the inset, squared
|
||||
const float SQUARED_INSET_OUTER_RADIUS = 0.950625;
|
||||
|
||||
void main(void) {
|
||||
// use the distance to compute the ring color, then multiply it by the varying color
|
||||
float squaredDistance = dot(gl_TexCoord[0].stp, gl_TexCoord[0].stp);
|
||||
float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS);
|
||||
float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS);
|
||||
gl_FragColor = gl_Color * vec4(white, white, white, alpha);
|
||||
}
|
25
interface/resources/shaders/metavoxel_voxel_cursor.vert
Normal file
25
interface/resources/shaders/metavoxel_voxel_cursor.vert
Normal file
|
@ -0,0 +1,25 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_voxel_cursor.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/10/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
void main(void) {
|
||||
// compute the view space coordinates
|
||||
vec4 viewPosition = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = gl_ProjectionMatrix * viewPosition;
|
||||
|
||||
// generate the texture coordinates from the view position
|
||||
gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]),
|
||||
dot(viewPosition, gl_EyePlaneR[4]), 1.0);
|
||||
|
||||
// copy the color for interpolation
|
||||
gl_FrontColor = gl_Color;
|
||||
}
|
|
@ -186,8 +186,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// set the associated application properties
|
||||
applicationInfo.beginGroup("INFO");
|
||||
|
||||
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
|
||||
|
||||
setApplicationName(applicationInfo.value("name").toString());
|
||||
setApplicationVersion(BUILD_VERSION);
|
||||
setOrganizationName(applicationInfo.value("organizationName").toString());
|
||||
|
@ -206,6 +204,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
|
@ -1546,10 +1546,9 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox
|
|||
|
||||
FaceTracker* Application::getActiveFaceTracker() {
|
||||
return (_dde.isActive() ? static_cast<FaceTracker*>(&_dde) :
|
||||
(_cara.isActive() ? static_cast<FaceTracker*>(&_cara) :
|
||||
(_faceshift.isActive() ? static_cast<FaceTracker*>(&_faceshift) :
|
||||
(_faceplus.isActive() ? static_cast<FaceTracker*>(&_faceplus) :
|
||||
(_visage.isActive() ? static_cast<FaceTracker*>(&_visage) : NULL)))));
|
||||
(_visage.isActive() ? static_cast<FaceTracker*>(&_visage) : NULL))));
|
||||
}
|
||||
|
||||
struct SendVoxelsOperationArgs {
|
||||
|
@ -2008,19 +2007,6 @@ void Application::updateDDE() {
|
|||
_dde.update();
|
||||
}
|
||||
|
||||
void Application::updateCara() {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateCara()");
|
||||
|
||||
// Update Cara
|
||||
_cara.update();
|
||||
|
||||
// Copy angular velocity if measured by cara, to the head
|
||||
if (_cara.isActive()) {
|
||||
_myAvatar->getHead()->setAngularVelocity(_cara.getHeadAngularVelocity());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateMyAvatarLookAtPosition() {
|
||||
PerformanceTimer perfTimer("lookAt");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
|
@ -2120,7 +2106,6 @@ void Application::updateMetavoxels(float deltaTime) {
|
|||
}
|
||||
|
||||
void Application::cameraMenuChanged() {
|
||||
float modeShiftPeriod = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? 0.0f : 1.0f;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
#include "devices/PrioVR.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
#include "devices/Visage.h"
|
||||
#include "devices/CaraFaceTracker.h"
|
||||
#include "devices/DdeFaceTracker.h"
|
||||
#include "entities/EntityTreeRenderer.h"
|
||||
#include "particles/ParticleTreeRenderer.h"
|
||||
|
@ -219,7 +218,6 @@ public:
|
|||
Faceshift* getFaceshift() { return &_faceshift; }
|
||||
Visage* getVisage() { return &_visage; }
|
||||
DdeFaceTracker* getDDE() { return &_dde; }
|
||||
CaraFaceTracker* getCara() { return &_cara; }
|
||||
FaceTracker* getActiveFaceTracker();
|
||||
PrioVR* getPrioVR() { return &_prioVR; }
|
||||
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
||||
|
@ -408,7 +406,6 @@ private:
|
|||
void updateFaceshift();
|
||||
void updateVisage();
|
||||
void updateDDE();
|
||||
void updateCara();
|
||||
void updateMyAvatarLookAtPosition();
|
||||
void updateThreads(float deltaTime);
|
||||
void updateMetavoxels(float deltaTime);
|
||||
|
@ -509,7 +506,6 @@ private:
|
|||
Faceplus _faceplus;
|
||||
Faceshift _faceshift;
|
||||
Visage _visage;
|
||||
CaraFaceTracker _cara;
|
||||
DdeFaceTracker _dde;
|
||||
|
||||
PrioVR _prioVR;
|
||||
|
|
|
@ -246,10 +246,16 @@ Menu::Menu() :
|
|||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
this,
|
||||
SLOT(toggleConsole()));
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
this,
|
||||
SLOT(toggleConsole()));
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ResetSensors,
|
||||
Qt::Key_Apostrophe,
|
||||
appInstance,
|
||||
SLOT(resetSensors()));
|
||||
|
||||
QMenu* avatarMenu = addMenu("Avatar");
|
||||
|
||||
|
|
|
@ -442,6 +442,7 @@ namespace MenuOption {
|
|||
const QString RenderLookAtVectors = "Show Look-at Vectors";
|
||||
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptEditor = "Script Editor...";
|
||||
|
|
|
@ -328,6 +328,49 @@ bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origi
|
|||
return true;
|
||||
}
|
||||
|
||||
class RayVoxelIntersectionVisitor : public RayIntersectionVisitor {
|
||||
public:
|
||||
|
||||
float intersectionDistance;
|
||||
|
||||
RayVoxelIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info, float distance);
|
||||
};
|
||||
|
||||
RayVoxelIntersectionVisitor::RayVoxelIntersectionVisitor(const glm::vec3& origin,
|
||||
const glm::vec3& direction, const MetavoxelLOD& lod) :
|
||||
RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), QVector<AttributePointer>(), lod),
|
||||
intersectionDistance(FLT_MAX) {
|
||||
}
|
||||
|
||||
int RayVoxelIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
|
||||
if (!info.isLeaf) {
|
||||
return _order;
|
||||
}
|
||||
const VoxelBuffer* buffer = static_cast<VoxelBuffer*>(
|
||||
info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
|
||||
if (!buffer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
glm::vec3 entry = ((_origin + distance * _direction) - info.minimum) / info.size;
|
||||
if (buffer->findFirstRayIntersection(entry, _origin, _direction, intersectionDistance)) {
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) {
|
||||
RayVoxelIntersectionVisitor visitor(origin, direction, getLOD());
|
||||
guideToAugmented(visitor);
|
||||
if (visitor.intersectionDistance == FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
distance = visitor.intersectionDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
class HeightfieldHeightVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
|
@ -411,10 +454,10 @@ float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) {
|
|||
return visitor.height;
|
||||
}
|
||||
|
||||
class HeightfieldCursorRenderVisitor : public MetavoxelVisitor {
|
||||
class CursorRenderVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldCursorRenderVisitor(const Box& bounds);
|
||||
CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
|
@ -423,13 +466,12 @@ private:
|
|||
Box _bounds;
|
||||
};
|
||||
|
||||
HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()),
|
||||
CursorRenderVisitor::CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute),
|
||||
_bounds(bounds) {
|
||||
}
|
||||
|
||||
int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) {
|
||||
int CursorRenderVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.getBounds().intersects(_bounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
@ -465,7 +507,8 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents));
|
||||
CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
|
||||
Box(position - extents, position + extents));
|
||||
guideToAugmented(visitor);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
|
||||
|
@ -478,6 +521,42 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r
|
|||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) {
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(-1.0f, -1.0f);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().bind();
|
||||
|
||||
glActiveTexture(GL_TEXTURE4);
|
||||
float scale = 1.0f / radius;
|
||||
glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x);
|
||||
glm::vec4 tCoefficients(0.0f, scale, 0.0f, -scale * position.y);
|
||||
glm::vec4 rCoefficients(0.0f, 0.0f, scale, -scale * position.z);
|
||||
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients);
|
||||
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients);
|
||||
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&rCoefficients);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(),
|
||||
Box(position - extents, position + extents));
|
||||
guideToAugmented(visitor);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release();
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) {
|
||||
glDeleteTextures(1, (GLuint*)&heightID);
|
||||
glDeleteTextures(1, (GLuint*)&colorID);
|
||||
|
@ -991,10 +1070,12 @@ void VoxelPoint::setNormal(const glm::vec3& normal) {
|
|||
}
|
||||
|
||||
VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
|
||||
const QVector<SharedObjectPointer>& materials) :
|
||||
const QMultiHash<QRgb, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials) :
|
||||
_vertices(vertices),
|
||||
_indices(indices),
|
||||
_hermite(hermite),
|
||||
_quadIndices(quadIndices),
|
||||
_size(size),
|
||||
_vertexCount(vertices.size()),
|
||||
_indexCount(indices.size()),
|
||||
_hermiteCount(hermite.size()),
|
||||
|
@ -1002,18 +1083,107 @@ VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>
|
|||
_materials(materials) {
|
||||
}
|
||||
|
||||
static bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance) {
|
||||
glm::vec3 firstSide = v0 - v1;
|
||||
glm::vec3 secondSide = v2 - v1;
|
||||
glm::vec3 normal = glm::cross(secondSide, firstSide);
|
||||
float dividend = glm::dot(normal, v1) - glm::dot(origin, normal);
|
||||
if (dividend > 0.0f) {
|
||||
return false; // origin below plane
|
||||
}
|
||||
float divisor = glm::dot(normal, direction);
|
||||
if (divisor > -EPSILON) {
|
||||
return false;
|
||||
}
|
||||
float t = dividend / divisor;
|
||||
glm::vec3 point = origin + direction * t;
|
||||
if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f &&
|
||||
glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f &&
|
||||
glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) {
|
||||
distance = t;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) const {
|
||||
float highest = _size - 1.0f;
|
||||
glm::vec3 position = entry * highest;
|
||||
glm::vec3 floors = glm::floor(position);
|
||||
int max = _size - 2;
|
||||
int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max);
|
||||
forever {
|
||||
for (QMultiHash<QRgb, int>::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1));
|
||||
it != _quadIndices.constEnd(); it++) {
|
||||
const int* indices = _indices.constData() + *it;
|
||||
if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex,
|
||||
_vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) ||
|
||||
findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex,
|
||||
_vertices.at(indices[2]).vertex, _vertices.at(indices[3]).vertex, distance)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
float xDistance = FLT_MAX, yDistance = FLT_MAX, zDistance = FLT_MAX;
|
||||
if (direction.x > 0.0f) {
|
||||
xDistance = (x + 1.0f - position.x) / direction.x;
|
||||
} else if (direction.x < 0.0f) {
|
||||
xDistance = (x - position.x) / direction.x;
|
||||
}
|
||||
if (direction.y > 0.0f) {
|
||||
yDistance = (y + 1.0f - position.y) / direction.y;
|
||||
} else if (direction.y < 0.0f) {
|
||||
yDistance = (y - position.y) / direction.y;
|
||||
}
|
||||
if (direction.z > 0.0f) {
|
||||
zDistance = (z + 1.0f - position.z) / direction.z;
|
||||
} else if (direction.z < 0.0f) {
|
||||
zDistance = (z - position.z) / direction.z;
|
||||
}
|
||||
float minimumDistance = qMin(xDistance, qMin(yDistance, zDistance));
|
||||
if (minimumDistance == xDistance) {
|
||||
if (direction.x > 0.0f) {
|
||||
if (x++ == max) {
|
||||
return false;
|
||||
}
|
||||
} else if (x-- == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (minimumDistance == yDistance) {
|
||||
if (direction.y > 0.0f) {
|
||||
if (y++ == max) {
|
||||
return false;
|
||||
}
|
||||
} else if (y-- == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (minimumDistance == zDistance) {
|
||||
if (direction.z > 0.0f) {
|
||||
if (z++ == max) {
|
||||
return false;
|
||||
}
|
||||
} else if (z-- == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
position += direction * minimumDistance;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoxelBuffer::render(bool cursor) {
|
||||
if (!_vertexBuffer.isCreated()) {
|
||||
_vertexBuffer.create();
|
||||
_vertexBuffer.bind();
|
||||
_vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint));
|
||||
_vertices.clear();
|
||||
|
||||
|
||||
_indexBuffer.create();
|
||||
_indexBuffer.bind();
|
||||
_indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int));
|
||||
_indices.clear();
|
||||
|
||||
|
||||
if (!_materials.isEmpty()) {
|
||||
_networkTextures.resize(_materials.size());
|
||||
for (int i = 0; i < _materials.size(); i++) {
|
||||
|
@ -1036,7 +1206,7 @@ void VoxelBuffer::render(bool cursor) {
|
|||
|
||||
glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
if (!_materials.isEmpty()) {
|
||||
if (!(_materials.isEmpty() || cursor)) {
|
||||
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
@ -1118,7 +1288,7 @@ void VoxelBuffer::render(bool cursor) {
|
|||
_vertexBuffer.release();
|
||||
_indexBuffer.release();
|
||||
|
||||
if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) {
|
||||
if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData) && !cursor) {
|
||||
if (!_hermiteBuffer.isCreated()) {
|
||||
_hermiteBuffer.create();
|
||||
_hermiteBuffer.bind();
|
||||
|
@ -1214,6 +1384,12 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
_baseVoxelProgram.link();
|
||||
|
||||
loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations);
|
||||
|
||||
_voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_voxel_cursor.vert");
|
||||
_voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_voxel_cursor.frag");
|
||||
_voxelCursorProgram.link();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1627,6 +1803,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
QVector<VoxelPoint> vertices;
|
||||
QVector<int> indices;
|
||||
QVector<glm::vec3> hermiteSegments;
|
||||
QMultiHash<QRgb, int> quadIndices;
|
||||
|
||||
// see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the
|
||||
// dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges
|
||||
|
@ -2091,6 +2268,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
// quads for each edge that includes a transition, using indices of previously generated vertices
|
||||
if (x != 0 && y != 0 && z != 0) {
|
||||
if (alpha0 != alpha1) {
|
||||
quadIndices.insert(qRgb(x, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x, y - 1, z), indices.size());
|
||||
quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size());
|
||||
quadIndices.insert(qRgb(x, y, z - 1), indices.size());
|
||||
indices.append(index.x);
|
||||
int index1 = lastLineIndices.at(x).x;
|
||||
int index2 = lastPlaneIndices.at((y - 1) * expanded + x).x;
|
||||
|
@ -2107,6 +2288,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
|
||||
if (alpha0 != alpha2) {
|
||||
quadIndices.insert(qRgb(x, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size());
|
||||
quadIndices.insert(qRgb(x, y, z - 1), indices.size());
|
||||
indices.append(index.y);
|
||||
int index1 = lastIndex.y;
|
||||
int index2 = lastPlaneIndices.at(y * expanded + x - 1).y;
|
||||
|
@ -2123,6 +2308,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
|
||||
if (alpha0 != alpha4) {
|
||||
quadIndices.insert(qRgb(x, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size());
|
||||
quadIndices.insert(qRgb(x, y - 1, z), indices.size());
|
||||
indices.append(index.z);
|
||||
int index1 = lastIndex.z;
|
||||
int index2 = lastLineIndices.at(x - 1).z;
|
||||
|
@ -2158,7 +2347,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
colorZ += area;
|
||||
}
|
||||
}
|
||||
buffer = new VoxelBuffer(vertices, indices, hermiteSegments,
|
||||
buffer = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, size,
|
||||
material ? material->getMaterials() : QVector<SharedObjectPointer>());
|
||||
}
|
||||
BufferDataPointer pointer(buffer);
|
||||
|
@ -2430,6 +2619,7 @@ ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
|
|||
ProgramObject DefaultMetavoxelRendererImplementation::_baseVoxelProgram;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_splatVoxelProgram;
|
||||
DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatVoxelLocations;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram;
|
||||
|
||||
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
||||
GLdouble coefficients[] = { x, y, z, w };
|
||||
|
|
|
@ -46,8 +46,12 @@ public:
|
|||
|
||||
void renderHeightfieldCursor(const glm::vec3& position, float radius);
|
||||
|
||||
void renderVoxelCursor(const glm::vec3& position, float radius);
|
||||
|
||||
bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
|
||||
|
||||
bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
|
||||
|
||||
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
|
||||
|
||||
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
|
||||
|
@ -241,7 +245,13 @@ class VoxelBuffer : public BufferData {
|
|||
public:
|
||||
|
||||
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
|
||||
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
|
||||
const QMultiHash<QRgb, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
|
||||
QVector<SharedObjectPointer>());
|
||||
|
||||
/// Finds the first intersection between the described ray and the voxel data.
|
||||
/// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1)
|
||||
bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) const;
|
||||
|
||||
virtual void render(bool cursor = false);
|
||||
|
||||
|
@ -250,6 +260,8 @@ private:
|
|||
QVector<VoxelPoint> _vertices;
|
||||
QVector<int> _indices;
|
||||
QVector<glm::vec3> _hermite;
|
||||
QMultiHash<QRgb, int> _quadIndices;
|
||||
int _size;
|
||||
int _vertexCount;
|
||||
int _indexCount;
|
||||
int _hermiteCount;
|
||||
|
@ -311,6 +323,8 @@ public:
|
|||
static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; }
|
||||
static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; }
|
||||
|
||||
static ProgramObject& getVoxelCursorProgram() { return _voxelCursorProgram; }
|
||||
|
||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||
|
||||
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||
|
@ -344,6 +358,8 @@ private:
|
|||
static ProgramObject _baseVoxelProgram;
|
||||
static ProgramObject _splatVoxelProgram;
|
||||
static SplatLocations _splatVoxelLocations;
|
||||
|
||||
static ProgramObject _voxelCursorProgram;
|
||||
};
|
||||
|
||||
/// Base class for spanner renderers; provides clipping.
|
||||
|
|
|
@ -1,455 +0,0 @@
|
|||
//
|
||||
// CaraFaceTracker.cpp
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Li Zuwei on 7/22/14.
|
||||
// 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 "CaraFaceTracker.h"
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
//qt
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#define PI M_PI
|
||||
#define RADTODEG(x) ( (x) * 180.0 / PI )
|
||||
#define DEGTORAD(x) ( (x) * PI / 180.0 )
|
||||
|
||||
static const QHostAddress CARA_FEATURE_POINT_SERVER_ADDR("127.0.0.1");
|
||||
static const quint16 CARA_FEATURE_POINT_SERVER_PORT = 36555;
|
||||
static QString sampleJson = "[{\"id\":1, \
|
||||
\"face\":{\"x\":248,\"y\":64,\"width\":278,\"height\":341}, \
|
||||
\"pose\":{\"roll\":2.62934,\"pitch\":-12.2318,\"yaw\":0.936743}, \
|
||||
\"feature_points\":[314,194,326,187,340,187,354,189,367,193,409,190,421,187,435,184,448,183,459,188, \
|
||||
388,207,389,223,390,240,391,257,377,266,384,267,392,268,399,266,407,264,331,209, \
|
||||
341,204,354,204,364,209,353,214,341,214,410,208,420,201,433,200,443,205,434,211, \
|
||||
421,211,362,294,372,290,383,287,393,289,404,286,415,289,426,291,418,300,407,306, \
|
||||
394,308,382,307,371,302,383,295,394,295,404,294,404,295,393,297,383,296], \
|
||||
\"classifiers\":{\"emotion\":{\"smi\":-0.368829,\"sur\":-1.33334,\"neg\":0.00235828,\"att\":1},\"blink\":1}}]";
|
||||
|
||||
static const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.0f);
|
||||
static const float TRANSLATION_SCALE = 1.0f;
|
||||
static const int NUM_BLENDSHAPE_COEFF = 30;
|
||||
static const int NUM_SMOOTHING_SAMPLES = 3;
|
||||
|
||||
struct CaraPerson {
|
||||
struct CaraPose {
|
||||
float roll, pitch, yaw;
|
||||
CaraPose() :
|
||||
roll(0.0f),
|
||||
pitch(0.0f),
|
||||
yaw(0.0f)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct CaraEmotion {
|
||||
float smile, surprise, negative, attention;
|
||||
CaraEmotion():
|
||||
smile(0.0f),
|
||||
surprise(0.0f),
|
||||
negative(0.0f),
|
||||
attention(0.0f)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
enum CaraBlink {
|
||||
BLINK_NOT_AVAILABLE,
|
||||
NO_BLINK,
|
||||
BLINK
|
||||
};
|
||||
|
||||
CaraPerson() :
|
||||
id(-1),
|
||||
blink(BLINK_NOT_AVAILABLE)
|
||||
{
|
||||
}
|
||||
|
||||
int id;
|
||||
CaraPose pose;
|
||||
CaraEmotion emotion;
|
||||
CaraBlink blink;
|
||||
|
||||
QString toString() {
|
||||
QString s = QString("id: %1, roll: %2, pitch: %3, yaw: %4, smi: %5, sur: %6, neg: %7, att: %8, blink: %9").
|
||||
arg(id).
|
||||
arg(pose.roll).
|
||||
arg(pose.pitch).
|
||||
arg(pose.yaw).
|
||||
arg(emotion.smile).
|
||||
arg(emotion.surprise).
|
||||
arg(emotion.negative).
|
||||
arg(emotion.attention).
|
||||
arg(blink);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
class CaraPacketDecoder {
|
||||
public:
|
||||
static CaraPerson extractOne(const QByteArray& buffer, QJsonParseError* jsonError) {
|
||||
CaraPerson person;
|
||||
QJsonDocument dom = QJsonDocument::fromJson(buffer, jsonError);
|
||||
|
||||
//check for errors
|
||||
if(jsonError->error == QJsonParseError::NoError) {
|
||||
//read the dom structure and populate the blend shapes and head poses
|
||||
//qDebug() << "[Info] Cara Face Tracker Packet Parsing Successful!";
|
||||
|
||||
//begin extracting the packet
|
||||
if(dom.isArray()) {
|
||||
QJsonArray people = dom.array();
|
||||
//extract the first person in the array
|
||||
if(people.size() > 0) {
|
||||
QJsonValue val = people.at(0);
|
||||
if(val.isObject()) {
|
||||
QJsonObject personDOM = val.toObject();
|
||||
person.id = extractId(personDOM);
|
||||
person.pose = extractPose(personDOM);
|
||||
|
||||
//extract the classifier outputs
|
||||
QJsonObject::const_iterator it = personDOM.constFind("classifiers");
|
||||
if(it != personDOM.constEnd()) {
|
||||
QJsonObject classifierDOM = (*it).toObject();
|
||||
person.emotion = extractEmotion(classifierDOM);
|
||||
person.blink = extractBlink(classifierDOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return person;
|
||||
}
|
||||
|
||||
private:
|
||||
static int extractId(const QJsonObject& person) {
|
||||
int id = -1;
|
||||
QJsonObject::const_iterator it = person.constFind("id");
|
||||
if(it != person.constEnd()) {
|
||||
id = (*it).toInt(-1);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static CaraPerson::CaraPose extractPose(const QJsonObject& person) {
|
||||
CaraPerson::CaraPose pose;
|
||||
QJsonObject::const_iterator it = person.constFind("pose");
|
||||
if(it != person.constEnd()) {
|
||||
QJsonObject poseDOM = (*it).toObject();
|
||||
|
||||
//look for the roll, pitch, yaw;
|
||||
QJsonObject::const_iterator poseIt = poseDOM.constFind("roll");
|
||||
QJsonObject::const_iterator poseEnd = poseDOM.constEnd();
|
||||
if(poseIt != poseEnd) {
|
||||
pose.roll = (float)(*poseIt).toDouble(0.0);
|
||||
}
|
||||
poseIt = poseDOM.constFind("pitch");
|
||||
if(poseIt != poseEnd) {
|
||||
pose.pitch = (float)(*poseIt).toDouble(0.0);
|
||||
}
|
||||
poseIt = poseDOM.constFind("yaw");
|
||||
if(poseIt != poseEnd) {
|
||||
pose.yaw = (float)(*poseIt).toDouble(0.0);
|
||||
}
|
||||
}
|
||||
return pose;
|
||||
}
|
||||
|
||||
static CaraPerson::CaraEmotion extractEmotion(const QJsonObject& classifiers) {
|
||||
CaraPerson::CaraEmotion emotion;
|
||||
QJsonObject::const_iterator it = classifiers.constFind("emotion");
|
||||
if(it != classifiers.constEnd()) {
|
||||
QJsonObject emotionDOM = (*it).toObject();
|
||||
|
||||
//look for smile, surprise, negative, attention responses
|
||||
QJsonObject::const_iterator emoEnd = emotionDOM.constEnd();
|
||||
QJsonObject::const_iterator emoIt = emotionDOM.constFind("smi");
|
||||
if(emoIt != emoEnd) {
|
||||
emotion.smile = (float)(*emoIt).toDouble(0.0);
|
||||
}
|
||||
emoIt = emotionDOM.constFind("sur");
|
||||
if(emoIt != emoEnd) {
|
||||
emotion.surprise = (float)(*emoIt).toDouble(0.0);
|
||||
}
|
||||
emoIt = emotionDOM.constFind("neg");
|
||||
if(emoIt != emoEnd) {
|
||||
emotion.negative = (float)(*emoIt).toDouble(0.0);
|
||||
}
|
||||
emoIt = emotionDOM.constFind("att");
|
||||
if(emoIt != emoEnd) {
|
||||
emotion.attention = (float)(*emoIt).toDouble(0.0);
|
||||
}
|
||||
}
|
||||
return emotion;
|
||||
}
|
||||
|
||||
static CaraPerson::CaraBlink extractBlink(const QJsonObject& classifiers) {
|
||||
CaraPerson::CaraBlink blink = CaraPerson::BLINK_NOT_AVAILABLE;
|
||||
QJsonObject::const_iterator it = classifiers.constFind("blink");
|
||||
if(it != classifiers.constEnd()) {
|
||||
int b = (*it).toInt(CaraPerson::BLINK_NOT_AVAILABLE);
|
||||
switch(b) {
|
||||
case CaraPerson::BLINK_NOT_AVAILABLE:
|
||||
blink = CaraPerson::BLINK_NOT_AVAILABLE;
|
||||
break;
|
||||
case CaraPerson::NO_BLINK:
|
||||
blink = CaraPerson::NO_BLINK;
|
||||
break;
|
||||
case CaraPerson::BLINK:
|
||||
blink = CaraPerson::BLINK;
|
||||
break;
|
||||
default:
|
||||
blink = CaraPerson::BLINK_NOT_AVAILABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return blink;
|
||||
}
|
||||
};
|
||||
|
||||
CaraFaceTracker::CaraFaceTracker() :
|
||||
_lastReceiveTimestamp(0),
|
||||
_pitchAverage(NUM_SMOOTHING_SAMPLES),
|
||||
_yawAverage(NUM_SMOOTHING_SAMPLES),
|
||||
_rollAverage(NUM_SMOOTHING_SAMPLES),
|
||||
_eyeGazeLeftPitch(0.0f),
|
||||
_eyeGazeLeftYaw(0.0f),
|
||||
_eyeGazeRightPitch(0.0f),
|
||||
_eyeGazeRightYaw(0),
|
||||
_leftBlinkIndex(0),
|
||||
_rightBlinkIndex(1),
|
||||
_leftEyeOpenIndex(8),
|
||||
_rightEyeOpenIndex(9),
|
||||
_browDownLeftIndex(14),
|
||||
_browDownRightIndex(15),
|
||||
_browUpCenterIndex(16),
|
||||
_browUpLeftIndex(17),
|
||||
_browUpRightIndex(18),
|
||||
_mouthSmileLeftIndex(28),
|
||||
_mouthSmileRightIndex(29),
|
||||
_jawOpenIndex(21)
|
||||
{
|
||||
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
||||
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError)));
|
||||
connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
|
||||
|
||||
bindTo(CARA_FEATURE_POINT_SERVER_PORT);
|
||||
|
||||
_headTranslation = DEFAULT_HEAD_ORIGIN;
|
||||
_blendshapeCoefficients.resize(NUM_BLENDSHAPE_COEFF);
|
||||
_blendshapeCoefficients.fill(0.0f);
|
||||
|
||||
//qDebug() << sampleJson;
|
||||
}
|
||||
|
||||
CaraFaceTracker::CaraFaceTracker(const QHostAddress& host, quint16 port) :
|
||||
_lastReceiveTimestamp(0),
|
||||
_pitchAverage(NUM_SMOOTHING_SAMPLES),
|
||||
_yawAverage(NUM_SMOOTHING_SAMPLES),
|
||||
_rollAverage(NUM_SMOOTHING_SAMPLES),
|
||||
_eyeGazeLeftPitch(0.0f),
|
||||
_eyeGazeLeftYaw(0.0f),
|
||||
_eyeGazeRightPitch(0.0f),
|
||||
_eyeGazeRightYaw(0.0f),
|
||||
_leftBlinkIndex(0),
|
||||
_rightBlinkIndex(1),
|
||||
_leftEyeOpenIndex(8),
|
||||
_rightEyeOpenIndex(9),
|
||||
_browDownLeftIndex(14),
|
||||
_browDownRightIndex(15),
|
||||
_browUpCenterIndex(16),
|
||||
_browUpLeftIndex(17),
|
||||
_browUpRightIndex(18),
|
||||
_mouthSmileLeftIndex(28),
|
||||
_mouthSmileRightIndex(29),
|
||||
_jawOpenIndex(21)
|
||||
{
|
||||
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
||||
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError)));
|
||||
connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
|
||||
|
||||
bindTo(host, port);
|
||||
|
||||
_headTranslation = DEFAULT_HEAD_ORIGIN * TRANSLATION_SCALE;
|
||||
_blendshapeCoefficients.resize(NUM_BLENDSHAPE_COEFF); //set the size of the blendshape coefficients
|
||||
_blendshapeCoefficients.fill(0.0f);
|
||||
}
|
||||
|
||||
CaraFaceTracker::~CaraFaceTracker() {
|
||||
if(_udpSocket.isOpen())
|
||||
_udpSocket.close();
|
||||
}
|
||||
|
||||
void CaraFaceTracker::init() {
|
||||
|
||||
}
|
||||
|
||||
void CaraFaceTracker::reset() {
|
||||
|
||||
}
|
||||
|
||||
void CaraFaceTracker::bindTo(quint16 port) {
|
||||
bindTo(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
void CaraFaceTracker::bindTo(const QHostAddress& host, quint16 port) {
|
||||
if(_udpSocket.isOpen()) {
|
||||
_udpSocket.close();
|
||||
}
|
||||
_udpSocket.bind(host, port);
|
||||
}
|
||||
|
||||
bool CaraFaceTracker::isActive() const {
|
||||
static const quint64 ACTIVE_TIMEOUT_USECS = 3000000; //3 secs
|
||||
return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS);
|
||||
}
|
||||
|
||||
void CaraFaceTracker::update() {
|
||||
// get the euler angles relative to the window
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3(
|
||||
(_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f)))));
|
||||
|
||||
//TODO: integrate when cara has eye gaze estimation
|
||||
|
||||
_estimatedEyePitch = eulers.x;
|
||||
_estimatedEyeYaw = eulers.y;
|
||||
}
|
||||
|
||||
//private slots and methods
|
||||
void CaraFaceTracker::socketErrorOccurred(QAbstractSocket::SocketError socketError) {
|
||||
qDebug() << "[Error] Cara Face Tracker Socket Error: " << _udpSocket.errorString();
|
||||
}
|
||||
|
||||
void CaraFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState) {
|
||||
QString state;
|
||||
switch(socketState) {
|
||||
case QAbstractSocket::BoundState:
|
||||
state = "Bounded";
|
||||
break;
|
||||
case QAbstractSocket::ClosingState:
|
||||
state = "Closing";
|
||||
break;
|
||||
case QAbstractSocket::ConnectedState:
|
||||
state = "Connected";
|
||||
break;
|
||||
case QAbstractSocket::ConnectingState:
|
||||
state = "Connecting";
|
||||
break;
|
||||
case QAbstractSocket::HostLookupState:
|
||||
state = "Host Lookup";
|
||||
break;
|
||||
case QAbstractSocket::ListeningState:
|
||||
state = "Listening";
|
||||
break;
|
||||
case QAbstractSocket::UnconnectedState:
|
||||
state = "Unconnected";
|
||||
break;
|
||||
}
|
||||
qDebug() << "[Info] Cara Face Tracker Socket: " << socketState;
|
||||
}
|
||||
|
||||
void CaraFaceTracker::readPendingDatagrams() {
|
||||
QByteArray buffer;
|
||||
while (_udpSocket.hasPendingDatagrams()) {
|
||||
buffer.resize(_udpSocket.pendingDatagramSize());
|
||||
_udpSocket.readDatagram(buffer.data(), buffer.size());
|
||||
decodePacket(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void CaraFaceTracker::decodePacket(const QByteArray& buffer) {
|
||||
//decode the incoming udp packet
|
||||
QJsonParseError jsonError;
|
||||
CaraPerson person = CaraPacketDecoder::extractOne(buffer, &jsonError);
|
||||
|
||||
if(jsonError.error == QJsonParseError::NoError) {
|
||||
|
||||
//do some noise filtering to the head poses
|
||||
//reduce the noise first by truncating to 1 dp
|
||||
person.pose.roll = glm::floor(person.pose.roll * 10) / 10;
|
||||
person.pose.pitch = glm::floor(person.pose.pitch * 10) / 10;
|
||||
person.pose.yaw = glm::floor(person.pose.yaw * 10) / 10;
|
||||
|
||||
//qDebug() << person.toString();
|
||||
|
||||
glm::quat newRotation(glm::vec3(DEGTORAD(person.pose.pitch), DEGTORAD(person.pose.yaw), DEGTORAD(person.pose.roll)));
|
||||
|
||||
// Compute angular velocity of the head
|
||||
glm::quat r = newRotation * glm::inverse(_headRotation);
|
||||
float theta = 2 * acos(r.w);
|
||||
if (theta > EPSILON) {
|
||||
float rMag = glm::length(glm::vec3(r.x, r.y, r.z));
|
||||
const float AVERAGE_CARA_FRAME_TIME = 0.04f;
|
||||
const float YAW_STANDARD_DEV_DEG = 2.5f;
|
||||
|
||||
_headAngularVelocity = theta / AVERAGE_CARA_FRAME_TIME * glm::vec3(r.x, r.y, r.z) / rMag;
|
||||
_pitchAverage.updateAverage(person.pose.pitch);
|
||||
_rollAverage.updateAverage(person.pose.roll);
|
||||
|
||||
//could use the angular velocity to detemine whether to update pitch and roll to further remove the noise.
|
||||
//use the angular velocity for roll and pitch, update if > THRESHOLD
|
||||
//if(glm::abs(_headAngularVelocity.x) > ANGULAR_VELOCITY_MIN) {
|
||||
// _pitchAverage.updateAverage(person.pose.pitch);
|
||||
//}
|
||||
|
||||
//if(glm::abs(_headAngularVelocity.z) > ANGULAR_VELOCITY_MIN) {
|
||||
// _rollAverage.updateAverage(person.pose.roll);;
|
||||
//}
|
||||
|
||||
//for yaw, the jitter is great, you can't use angular velocity because it swings too much
|
||||
//use the previous and current yaw, calculate the
|
||||
//abs difference and move it the difference is above the standard deviation which is around 2.5
|
||||
// > the standard deviation 2.5 deg, update the yaw smoothing average
|
||||
if(glm::abs(person.pose.yaw - _yawAverage.getAverage()) > YAW_STANDARD_DEV_DEG) {
|
||||
//qDebug() << "Yaw Diff: " << glm::abs(person.pose.yaw - _previousYaw);
|
||||
_yawAverage.updateAverage(person.pose.yaw);
|
||||
}
|
||||
|
||||
//set the new rotation
|
||||
newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage())));
|
||||
}
|
||||
else {
|
||||
//no change in position, use previous averages
|
||||
newRotation = glm::quat(glm::vec3(DEGTORAD(_pitchAverage.getAverage()), DEGTORAD(_yawAverage.getAverage()), DEGTORAD(-_rollAverage.getAverage())));
|
||||
_headAngularVelocity = glm::vec3(0,0,0);
|
||||
}
|
||||
|
||||
//update to new rotation angles
|
||||
_headRotation = newRotation;
|
||||
|
||||
//TODO: head translation, right now is 0
|
||||
|
||||
//Do Blendshapes, clip between 0.0f to 1.0f, neg should be ignored
|
||||
_blendshapeCoefficients[_leftBlinkIndex] = person.blink == CaraPerson::BLINK ? 1.0f : 0.0f;
|
||||
_blendshapeCoefficients[_rightBlinkIndex] = person.blink == CaraPerson::BLINK ? 1.0f : 0.0f;
|
||||
|
||||
//anger and surprised are mutually exclusive so we could try use this fact to determine
|
||||
//whether to down the brows or up the brows
|
||||
_blendshapeCoefficients[_browDownLeftIndex] = person.emotion.negative < 0.0f ? 0.0f : person.emotion.negative;
|
||||
_blendshapeCoefficients[_browDownRightIndex] = person.emotion.negative < 0.0f ? 0.0f : person.emotion.negative;
|
||||
_blendshapeCoefficients[_browUpCenterIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise;
|
||||
_blendshapeCoefficients[_browUpLeftIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise;
|
||||
_blendshapeCoefficients[_browUpRightIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise;
|
||||
_blendshapeCoefficients[_jawOpenIndex] = person.emotion.surprise < 0.0f ? 0.0f : person.emotion.surprise;
|
||||
_blendshapeCoefficients[_mouthSmileLeftIndex] = person.emotion.smile < 0.0f ? 0.0f : person.emotion.smile;
|
||||
_blendshapeCoefficients[_mouthSmileRightIndex] = person.emotion.smile < 0.0f ? 0.0f : person.emotion.smile;
|
||||
}
|
||||
else {
|
||||
qDebug() << "[Error] Cara Face Tracker Decode Error: " << jsonError.errorString();
|
||||
}
|
||||
|
||||
_lastReceiveTimestamp = usecTimestampNow();
|
||||
}
|
||||
|
||||
float CaraFaceTracker::getBlendshapeCoefficient(int index) const {
|
||||
return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f;
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
//
|
||||
// CaraFaceTracker.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Li Zuwei on 7/22/14.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_CaraFaceTracker_h
|
||||
#define hifi_CaraFaceTracker_h
|
||||
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include "FaceTracker.h"
|
||||
|
||||
/*!
|
||||
* \class CaraFaceTracker
|
||||
*
|
||||
* \brief Handles interaction with the Cara software,
|
||||
* which provides head position/orientation and facial features.
|
||||
* \details By default, opens a udp socket with IPV4_ANY_ADDR with port 36555.
|
||||
* User needs to run the Cara Face Detection UDP Client with the destination
|
||||
* host address (eg: 127.0.0.1 for localhost) and destination port 36555.
|
||||
**/
|
||||
|
||||
class CaraFaceTracker : public FaceTracker {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CaraFaceTracker();
|
||||
CaraFaceTracker(const QHostAddress& host, quint16 port);
|
||||
~CaraFaceTracker();
|
||||
|
||||
//initialization
|
||||
void init();
|
||||
void reset();
|
||||
|
||||
//sockets
|
||||
void bindTo(quint16 port);
|
||||
void bindTo(const QHostAddress& host, quint16 port);
|
||||
bool isActive() const;
|
||||
|
||||
//tracking
|
||||
void update();
|
||||
|
||||
//head angular velocity
|
||||
const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; }
|
||||
|
||||
//eye gaze
|
||||
float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; }
|
||||
float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; }
|
||||
|
||||
float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; }
|
||||
float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; }
|
||||
|
||||
//blend shapes
|
||||
float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); }
|
||||
float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); }
|
||||
float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); }
|
||||
float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); }
|
||||
|
||||
float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); }
|
||||
float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); }
|
||||
float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); }
|
||||
float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); }
|
||||
float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); }
|
||||
|
||||
float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); }
|
||||
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
|
||||
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
|
||||
|
||||
private slots:
|
||||
|
||||
//sockets
|
||||
void socketErrorOccurred(QAbstractSocket::SocketError socketError);
|
||||
void readPendingDatagrams();
|
||||
void socketStateChanged(QAbstractSocket::SocketState socketState);
|
||||
|
||||
private:
|
||||
void decodePacket(const QByteArray& buffer);
|
||||
float getBlendshapeCoefficient(int index) const;
|
||||
|
||||
// sockets
|
||||
QUdpSocket _udpSocket;
|
||||
quint64 _lastReceiveTimestamp;
|
||||
|
||||
//head tracking
|
||||
glm::vec3 _headAngularVelocity;
|
||||
|
||||
//pose average
|
||||
SimpleMovingAverage _pitchAverage;
|
||||
SimpleMovingAverage _yawAverage;
|
||||
SimpleMovingAverage _rollAverage;
|
||||
|
||||
// eye gaze degrees
|
||||
float _eyeGazeLeftPitch;
|
||||
float _eyeGazeLeftYaw;
|
||||
float _eyeGazeRightPitch;
|
||||
float _eyeGazeRightYaw;
|
||||
|
||||
//blend shapes
|
||||
int _leftBlinkIndex;
|
||||
int _rightBlinkIndex;
|
||||
int _leftEyeOpenIndex;
|
||||
int _rightEyeOpenIndex;
|
||||
|
||||
// Brows
|
||||
int _browDownLeftIndex;
|
||||
int _browDownRightIndex;
|
||||
int _browUpCenterIndex;
|
||||
int _browUpLeftIndex;
|
||||
int _browUpRightIndex;
|
||||
|
||||
int _mouthSmileLeftIndex;
|
||||
int _mouthSmileRightIndex;
|
||||
|
||||
int _jawOpenIndex;
|
||||
};
|
||||
|
||||
#endif //endif hifi_CaraFaceTracker_h
|
|
@ -15,13 +15,17 @@
|
|||
|
||||
#include "Joystick.h"
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
const float MAX_AXIS = 32768.0f;
|
||||
|
||||
Joystick::Joystick(const QString& name, SDL_Joystick* sdlJoystick) :
|
||||
#ifdef HAVE_SDL2
|
||||
|
||||
Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :
|
||||
_instanceId(instanceId),
|
||||
_name(name),
|
||||
_axes(QVector<float>(SDL_JoystickNumAxes(sdlJoystick))),
|
||||
_buttons(QVector<bool>(SDL_JoystickNumButtons(sdlJoystick))),
|
||||
_sdlJoystick(sdlJoystick)
|
||||
_sdlGameController(sdlGameController),
|
||||
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
|
||||
_axes(QVector<float>(SDL_JoystickNumAxes(_sdlJoystick))),
|
||||
_buttons(QVector<bool>(SDL_JoystickNumButtons(_sdlJoystick)))
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -33,30 +37,24 @@ Joystick::~Joystick() {
|
|||
}
|
||||
|
||||
void Joystick::closeJoystick() {
|
||||
#ifdef HAVE_SDL
|
||||
SDL_JoystickClose(_sdlJoystick);
|
||||
#ifdef HAVE_SDL2
|
||||
SDL_GameControllerClose(_sdlGameController);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Joystick::update() {
|
||||
#ifdef HAVE_SDL
|
||||
// update our current values, emit a signal when there is a change
|
||||
for (int j = 0; j < getNumAxes(); j++) {
|
||||
float newValue = glm::round(SDL_JoystickGetAxis(_sdlJoystick, j) + 0.5f) / std::numeric_limits<short>::max();
|
||||
|
||||
if (_axes[j] != newValue) {
|
||||
float oldValue = _axes[j];
|
||||
_axes[j] = newValue;
|
||||
emit axisValueChanged(j, newValue, oldValue);
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < getNumButtons(); j++) {
|
||||
bool newValue = SDL_JoystickGetButton(_sdlJoystick, j);
|
||||
if (_buttons[j] != newValue) {
|
||||
bool oldValue = _buttons[j];
|
||||
_buttons[j] = newValue;
|
||||
emit buttonStateChanged(j, newValue, oldValue);
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) {
|
||||
float oldValue = _axes[event.axis];
|
||||
float newValue = event.value / MAX_AXIS;
|
||||
_axes[event.axis] = newValue;
|
||||
emit axisValueChanged(event.axis, newValue, oldValue);
|
||||
}
|
||||
|
||||
void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
|
||||
bool oldValue = _buttons[event.button];
|
||||
bool newValue = event.state == SDL_PRESSED;
|
||||
_buttons[event.button] = newValue;
|
||||
emit buttonStateChanged(event.button, newValue, oldValue);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
#include <qobject.h>
|
||||
#include <qvector.h>
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#undef main
|
||||
#endif
|
||||
|
@ -24,6 +24,10 @@ class Joystick : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString name READ getName)
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
Q_PROPERTY(int instanceId READ getInstanceId)
|
||||
#endif
|
||||
|
||||
Q_PROPERTY(int numAxes READ getNumAxes)
|
||||
Q_PROPERTY(int numButtons READ getNumButtons)
|
||||
|
@ -31,19 +35,21 @@ public:
|
|||
Joystick();
|
||||
~Joystick();
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
Joystick(const QString& name, SDL_Joystick* sdlJoystick);
|
||||
#ifdef HAVE_SDL2
|
||||
Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
|
||||
#endif
|
||||
|
||||
void update();
|
||||
|
||||
void closeJoystick();
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
void setSDLJoystick(SDL_Joystick* sdlJoystick) { _sdlJoystick = sdlJoystick; }
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
void handleAxisEvent(const SDL_ControllerAxisEvent& event);
|
||||
void handleButtonEvent(const SDL_ControllerButtonEvent& event);
|
||||
#endif
|
||||
|
||||
const QString& getName() const { return _name; }
|
||||
#ifdef HAVE_SDL2
|
||||
int getInstanceId() const { return _instanceId; }
|
||||
#endif
|
||||
|
||||
const QVector<float>& getAxes() const { return _axes; }
|
||||
const QVector<bool>& getButtons() const { return _buttons; }
|
||||
|
@ -55,13 +61,15 @@ signals:
|
|||
void axisValueChanged(int axis, float newValue, float oldValue);
|
||||
void buttonStateChanged(int button, float newValue, float oldValue);
|
||||
private:
|
||||
#ifdef HAVE_SDL2
|
||||
SDL_GameController* _sdlGameController;
|
||||
SDL_Joystick* _sdlJoystick;
|
||||
SDL_JoystickID _instanceId;
|
||||
#endif
|
||||
|
||||
QString _name;
|
||||
QVector<float> _axes;
|
||||
QVector<bool> _buttons;
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
SDL_Joystick* _sdlJoystick;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // hifi_JoystickTracker_h
|
||||
#endif // hifi_JoystickTracker_h
|
||||
|
|
|
@ -78,6 +78,8 @@ void OculusManager::connect() {
|
|||
#ifdef HAVE_LIBOVR
|
||||
_calibrationState = UNCALIBRATED;
|
||||
|
||||
qDebug() << "Oculus SDK" << OVR_VERSION_STRING;
|
||||
|
||||
ovr_Initialize();
|
||||
|
||||
_ovrHmd = ovrHmd_Create(0);
|
||||
|
@ -282,6 +284,9 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) {
|
|||
_calibrationState = WAITING_FOR_ZERO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -44,16 +44,16 @@ void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) {
|
|||
}
|
||||
|
||||
Resource::Sysmem::Sysmem() :
|
||||
_data(NULL),
|
||||
_stamp(0),
|
||||
_size(0),
|
||||
_stamp(0)
|
||||
_data(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Resource::Sysmem::Sysmem(Size size, const Byte* bytes) :
|
||||
_data(NULL),
|
||||
_stamp(0),
|
||||
_size(0),
|
||||
_stamp(0)
|
||||
_data(NULL)
|
||||
{
|
||||
if (size > 0) {
|
||||
_size = allocateMemory(&_data, size);
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
//
|
||||
|
||||
#include <QtDebug>
|
||||
#include <QScriptValue>
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#undef main
|
||||
#endif
|
||||
|
@ -20,106 +21,106 @@
|
|||
|
||||
#include "JoystickScriptingInterface.h"
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
SDL_JoystickID getInstanceId(SDL_GameController* controller) {
|
||||
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
|
||||
return SDL_JoystickInstanceID(joystick);
|
||||
}
|
||||
#endif
|
||||
|
||||
JoystickScriptingInterface& JoystickScriptingInterface::getInstance() {
|
||||
static JoystickScriptingInterface sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
JoystickScriptingInterface::JoystickScriptingInterface() :
|
||||
#ifdef HAVE_SDL2
|
||||
_openJoysticks(),
|
||||
_availableDeviceNames(),
|
||||
#endif
|
||||
_isInitialized(false)
|
||||
{
|
||||
reset();
|
||||
#ifdef HAVE_SDL2
|
||||
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0);
|
||||
|
||||
if (initSuccess) {
|
||||
int joystickCount = SDL_NumJoysticks();
|
||||
|
||||
for (int i = 0; i < joystickCount; i++) {
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(i);
|
||||
SDL_JoystickID id = getInstanceId(controller);
|
||||
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
|
||||
_openJoysticks[id] = joystick;
|
||||
}
|
||||
|
||||
_isInitialized = true;
|
||||
} else {
|
||||
qDebug() << "Error initializing SDL";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JoystickScriptingInterface::~JoystickScriptingInterface() {
|
||||
#ifdef HAVE_SDL2
|
||||
qDeleteAll(_openJoysticks);
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
SDL_Quit();
|
||||
_isInitialized = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void JoystickScriptingInterface::reset() {
|
||||
#ifdef HAVE_SDL
|
||||
|
||||
if (_isInitialized) {
|
||||
_isInitialized = false;
|
||||
|
||||
// close all the open joysticks before we quit
|
||||
foreach(Joystick* openJoystick, _openJoysticks) {
|
||||
openJoystick->closeJoystick();
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
bool initSuccess = (SDL_Init(SDL_INIT_JOYSTICK) == 0);
|
||||
|
||||
if (initSuccess) {
|
||||
|
||||
int joystickCount = SDL_NumJoysticks();
|
||||
|
||||
for (int i = 0; i < joystickCount; i++) {
|
||||
_availableDeviceNames << SDL_JoystickName(i);
|
||||
}
|
||||
|
||||
foreach(const QString& joystickName, _openJoysticks.keys()) {
|
||||
_openJoysticks[joystickName]->setSDLJoystick(openSDLJoystickWithName(joystickName));
|
||||
}
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void JoystickScriptingInterface::update() {
|
||||
#ifdef HAVE_SDL
|
||||
if (_isInitialized) {
|
||||
PerformanceTimer perfTimer("JoystickScriptingInterface::update");
|
||||
SDL_JoystickUpdate();
|
||||
|
||||
foreach(Joystick* joystick, _openJoysticks) {
|
||||
joystick->update();
|
||||
}
|
||||
const QObjectList JoystickScriptingInterface::getAllJoysticks() const {
|
||||
QObjectList objectList;
|
||||
#ifdef HAVE_SDL2
|
||||
const QList<Joystick*> joystickList = _openJoysticks.values();
|
||||
for (int i = 0; i < joystickList.length(); i++) {
|
||||
objectList << joystickList[i];
|
||||
}
|
||||
#endif
|
||||
return objectList;
|
||||
}
|
||||
|
||||
Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) {
|
||||
Joystick* matchingJoystick = _openJoysticks.value(name);
|
||||
#ifdef HAVE_SDL
|
||||
if (!matchingJoystick) {
|
||||
SDL_Joystick* openSDLJoystick = openSDLJoystickWithName(name);
|
||||
|
||||
if (openSDLJoystick) {
|
||||
matchingJoystick = _openJoysticks.insert(name, new Joystick(name, openSDLJoystick)).value();
|
||||
} else {
|
||||
qDebug() << "No matching joystick found with name" << name << "- returning NULL pointer.";
|
||||
#ifdef HAVE_SDL2
|
||||
QMap<SDL_JoystickID, Joystick*>::iterator iter = _openJoysticks.begin();
|
||||
while (iter != _openJoysticks.end()) {
|
||||
if (iter.value()->getName() == name) {
|
||||
return iter.value();
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return matchingJoystick;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
|
||||
SDL_Joystick* JoystickScriptingInterface::openSDLJoystickWithName(const QString &name) {
|
||||
// we haven't opened a joystick with this name yet - enumerate our SDL devices and see if it exists
|
||||
int joystickCount = SDL_NumJoysticks();
|
||||
|
||||
for (int i = 0; i < joystickCount; i++) {
|
||||
if (SDL_JoystickName(i) == name) {
|
||||
return SDL_JoystickOpen(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void JoystickScriptingInterface::update() {
|
||||
#ifdef HAVE_SDL2
|
||||
if (_isInitialized) {
|
||||
PerformanceTimer perfTimer("JoystickScriptingInterface::update");
|
||||
SDL_GameControllerUpdate();
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_CONTROLLERAXISMOTION) {
|
||||
Joystick* joystick = _openJoysticks[event.caxis.which];
|
||||
if (joystick) {
|
||||
joystick->handleAxisEvent(event.caxis);
|
||||
}
|
||||
} else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) {
|
||||
Joystick* joystick = _openJoysticks[event.cbutton.which];
|
||||
if (joystick) {
|
||||
joystick->handleButtonEvent(event.cbutton);
|
||||
}
|
||||
} else if (event.type == SDL_CONTROLLERDEVICEADDED) {
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
|
||||
|
||||
SDL_JoystickID id = getInstanceId(controller);
|
||||
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
|
||||
_openJoysticks[id] = joystick;
|
||||
emit joystickAdded(joystick);
|
||||
} else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||
Joystick* joystick = _openJoysticks[event.cdevice.which];
|
||||
_openJoysticks.remove(event.cdevice.which);
|
||||
emit joystickRemoved(joystick);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -15,34 +15,100 @@
|
|||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
#include "devices/Joystick.h"
|
||||
|
||||
/// Handles joystick input through SDL.
|
||||
class JoystickScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QStringList availableJoystickNames READ getAvailableJoystickNames)
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
Q_PROPERTY(int AXIS_INVALID READ axisInvalid)
|
||||
Q_PROPERTY(int AXIS_LEFT_X READ axisLeftX)
|
||||
Q_PROPERTY(int AXIS_LEFT_Y READ axisLeftY)
|
||||
Q_PROPERTY(int AXIS_RIGHT_X READ axisRightX)
|
||||
Q_PROPERTY(int AXIS_RIGHT_Y READ axisRightY)
|
||||
Q_PROPERTY(int AXIS_TRIGGER_LEFT READ axisTriggerLeft)
|
||||
Q_PROPERTY(int AXIS_TRIGGER_RIGHT READ axisTriggerRight)
|
||||
Q_PROPERTY(int AXIS_MAX READ axisMax)
|
||||
|
||||
Q_PROPERTY(int BUTTON_INVALID READ buttonInvalid)
|
||||
Q_PROPERTY(int BUTTON_FACE_BOTTOM READ buttonFaceBottom)
|
||||
Q_PROPERTY(int BUTTON_FACE_RIGHT READ buttonFaceRight)
|
||||
Q_PROPERTY(int BUTTON_FACE_LEFT READ buttonFaceLeft)
|
||||
Q_PROPERTY(int BUTTON_FACE_TOP READ buttonFaceTop)
|
||||
Q_PROPERTY(int BUTTON_BACK READ buttonBack)
|
||||
Q_PROPERTY(int BUTTON_GUIDE READ buttonGuide)
|
||||
Q_PROPERTY(int BUTTON_START READ buttonStart)
|
||||
Q_PROPERTY(int BUTTON_LEFT_STICK READ buttonLeftStick)
|
||||
Q_PROPERTY(int BUTTON_RIGHT_STICK READ buttonRightStick)
|
||||
Q_PROPERTY(int BUTTON_LEFT_SHOULDER READ buttonLeftShoulder)
|
||||
Q_PROPERTY(int BUTTON_RIGHT_SHOULDER READ buttonRightShoulder)
|
||||
Q_PROPERTY(int BUTTON_DPAD_UP READ buttonDpadUp)
|
||||
Q_PROPERTY(int BUTTON_DPAD_DOWN READ buttonDpadDown)
|
||||
Q_PROPERTY(int BUTTON_DPAD_LEFT READ buttonDpadLeft)
|
||||
Q_PROPERTY(int BUTTON_DPAD_RIGHT READ buttonDpadRight)
|
||||
Q_PROPERTY(int BUTTON_MAX READ buttonMax)
|
||||
|
||||
Q_PROPERTY(int BUTTON_PRESSED READ buttonPressed)
|
||||
Q_PROPERTY(int BUTTON_RELEASED READ buttonRelease)
|
||||
#endif
|
||||
|
||||
public:
|
||||
static JoystickScriptingInterface& getInstance();
|
||||
|
||||
const QStringList& getAvailableJoystickNames() const { return _availableDeviceNames; }
|
||||
|
||||
|
||||
void update();
|
||||
|
||||
|
||||
public slots:
|
||||
Joystick* joystickWithName(const QString& name);
|
||||
void reset();
|
||||
|
||||
const QObjectList getAllJoysticks() const;
|
||||
|
||||
signals:
|
||||
void joystickAdded(Joystick* joystick);
|
||||
void joystickRemoved(Joystick* joystick);
|
||||
|
||||
private:
|
||||
#ifdef HAVE_SDL
|
||||
SDL_Joystick* openSDLJoystickWithName(const QString& name);
|
||||
#ifdef HAVE_SDL2
|
||||
int axisInvalid() const { return SDL_CONTROLLER_AXIS_INVALID; }
|
||||
int axisLeftX() const { return SDL_CONTROLLER_AXIS_LEFTX; }
|
||||
int axisLeftY() const { return SDL_CONTROLLER_AXIS_LEFTY; }
|
||||
int axisRightX() const { return SDL_CONTROLLER_AXIS_RIGHTX; }
|
||||
int axisRightY() const { return SDL_CONTROLLER_AXIS_RIGHTY; }
|
||||
int axisTriggerLeft() const { return SDL_CONTROLLER_AXIS_TRIGGERLEFT; }
|
||||
int axisTriggerRight() const { return SDL_CONTROLLER_AXIS_TRIGGERRIGHT; }
|
||||
int axisMax() const { return SDL_CONTROLLER_AXIS_MAX; }
|
||||
|
||||
int buttonInvalid() const { return SDL_CONTROLLER_BUTTON_INVALID; }
|
||||
int buttonFaceBottom() const { return SDL_CONTROLLER_BUTTON_A; }
|
||||
int buttonFaceRight() const { return SDL_CONTROLLER_BUTTON_B; }
|
||||
int buttonFaceLeft() const { return SDL_CONTROLLER_BUTTON_X; }
|
||||
int buttonFaceTop() const { return SDL_CONTROLLER_BUTTON_Y; }
|
||||
int buttonBack() const { return SDL_CONTROLLER_BUTTON_BACK; }
|
||||
int buttonGuide() const { return SDL_CONTROLLER_BUTTON_GUIDE; }
|
||||
int buttonStart() const { return SDL_CONTROLLER_BUTTON_START; }
|
||||
int buttonLeftStick() const { return SDL_CONTROLLER_BUTTON_LEFTSTICK; }
|
||||
int buttonRightStick() const { return SDL_CONTROLLER_BUTTON_RIGHTSTICK; }
|
||||
int buttonLeftShoulder() const { return SDL_CONTROLLER_BUTTON_LEFTSHOULDER; }
|
||||
int buttonRightShoulder() const { return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; }
|
||||
int buttonDpadUp() const { return SDL_CONTROLLER_BUTTON_DPAD_UP; }
|
||||
int buttonDpadDown() const { return SDL_CONTROLLER_BUTTON_DPAD_DOWN; }
|
||||
int buttonDpadLeft() const { return SDL_CONTROLLER_BUTTON_DPAD_LEFT; }
|
||||
int buttonDpadRight() const { return SDL_CONTROLLER_BUTTON_DPAD_RIGHT; }
|
||||
int buttonMax() const { return SDL_CONTROLLER_BUTTON_MAX; }
|
||||
|
||||
int buttonPressed() const { return SDL_PRESSED; }
|
||||
int buttonRelease() const { return SDL_RELEASED; }
|
||||
#endif
|
||||
|
||||
|
||||
JoystickScriptingInterface();
|
||||
~JoystickScriptingInterface();
|
||||
|
||||
QMap<QString, Joystick*> _openJoysticks;
|
||||
QStringList _availableDeviceNames;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
QMap<SDL_JoystickID, Joystick*> _openJoysticks;
|
||||
#endif
|
||||
bool _isInitialized;
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting) {
|
|||
QSettings* settings = Application::getInstance()->lockSettings();
|
||||
QVariant value = settings->value(setting);
|
||||
Application::getInstance()->unlockSettings();
|
||||
if (!value.isValid()) {
|
||||
value = "";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -29,6 +32,9 @@ QVariant SettingsScriptingInterface::getValue(const QString& setting, const QVar
|
|||
QSettings* settings = Application::getInstance()->lockSettings();
|
||||
QVariant value = settings->value(setting, defaultValue);
|
||||
Application::getInstance()->unlockSettings();
|
||||
if (!value.isValid()) {
|
||||
value = "";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
addTool(new EraseHeightfieldTool(this));
|
||||
addTool(new VoxelMaterialBoxTool(this));
|
||||
addTool(new VoxelMaterialSpannerTool(this));
|
||||
addTool(new VoxelMaterialBrushTool(this));
|
||||
|
||||
updateAttributes();
|
||||
|
||||
|
@ -1525,3 +1526,102 @@ void VoxelMaterialSpannerTool::updateTexture() {
|
|||
void VoxelMaterialSpannerTool::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
||||
VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) :
|
||||
MetavoxelTool(editor, name, false, true) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
widget->setLayout(_form = new QFormLayout());
|
||||
layout()->addWidget(widget);
|
||||
|
||||
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
|
||||
_radius->setSingleStep(0.01);
|
||||
_radius->setMaximum(FLT_MAX);
|
||||
_radius->setValue(0.25);
|
||||
}
|
||||
|
||||
bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("VoxelColorAttribute");
|
||||
}
|
||||
|
||||
void VoxelBrushTool::render() {
|
||||
if (Application::getInstance()->isMouseHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the intersection with the voxels
|
||||
glm::vec3 origin = Application::getInstance()->getMouseRayOrigin();
|
||||
glm::vec3 direction = Application::getInstance()->getMouseRayDirection();
|
||||
|
||||
float distance;
|
||||
if (!Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, distance)) {
|
||||
return;
|
||||
}
|
||||
Application::getInstance()->getMetavoxels()->renderVoxelCursor(
|
||||
_position = origin + distance * direction, _radius->value());
|
||||
}
|
||||
|
||||
bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) {
|
||||
if (event->type() == QEvent::Wheel) {
|
||||
float angle = static_cast<QWheelEvent*>(event)->angleDelta().y();
|
||||
const float ANGLE_SCALE = 1.0f / 1000.0f;
|
||||
_radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE));
|
||||
return true;
|
||||
|
||||
} else if (event->type() == QEvent::MouseButtonPress) {
|
||||
MetavoxelEditMessage message = { createEdit(static_cast<QMouseEvent*>(event)->button() == Qt::RightButton) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) :
|
||||
VoxelBrushTool(editor, "Material Brush") {
|
||||
|
||||
_form->addRow("Color:", _color = new QColorEditor(this));
|
||||
connect(_color, &QColorEditor::colorChanged, this, &VoxelMaterialBrushTool::clearTexture);
|
||||
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBrushTool::updateTexture);
|
||||
}
|
||||
|
||||
QVariant VoxelMaterialBrushTool::createEdit(bool alternate) {
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
||||
} else {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
if (static_cast<MaterialObject*>(material.data())->getDiffuse().isValid()) {
|
||||
_materialEditor->detachObject();
|
||||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), material, _color->getColor()));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::clearTexture() {
|
||||
_materialEditor->setObject(new MaterialObject());
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::updateTexture() {
|
||||
if (_texture) {
|
||||
_texture->disconnect(this);
|
||||
}
|
||||
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
|
||||
if (!material->getDiffuse().isValid()) {
|
||||
_texture.clear();
|
||||
return;
|
||||
}
|
||||
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
|
||||
if (_texture) {
|
||||
if (_texture->isLoaded()) {
|
||||
textureLoaded();
|
||||
} else {
|
||||
connect(_texture.data(), &Resource::loaded, this, &VoxelMaterialBrushTool::textureLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
|
|
@ -469,4 +469,53 @@ private:
|
|||
QSharedPointer<NetworkTexture> _texture;
|
||||
};
|
||||
|
||||
/// Base class for voxel brush tools.
|
||||
class VoxelBrushTool : public MetavoxelTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelBrushTool(MetavoxelEditor* editor, const QString& name);
|
||||
|
||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||
|
||||
virtual void render();
|
||||
|
||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate) = 0;
|
||||
|
||||
QFormLayout* _form;
|
||||
QDoubleSpinBox* _radius;
|
||||
|
||||
glm::vec3 _position;
|
||||
};
|
||||
|
||||
/// Allows texturing parts of the voxel field.
|
||||
class VoxelMaterialBrushTool : public VoxelBrushTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelMaterialBrushTool(MetavoxelEditor* editor);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private slots:
|
||||
|
||||
void clearTexture();
|
||||
void updateTexture();
|
||||
void textureLoaded();
|
||||
|
||||
private:
|
||||
|
||||
QColorEditor* _color;
|
||||
SharedObjectEditor* _materialEditor;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelEditor_h
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
|
||||
#include "BillboardOverlay.h"
|
||||
|
||||
BillboardOverlay::BillboardOverlay()
|
||||
: _fromImage(-1,-1,-1,-1),
|
||||
_scale(1.0f),
|
||||
_isFacingAvatar(true),
|
||||
_newTextureNeeded(true) {
|
||||
BillboardOverlay::BillboardOverlay() :
|
||||
_newTextureNeeded(true),
|
||||
_fromImage(-1,-1,-1,-1),
|
||||
_scale(1.0f),
|
||||
_isFacingAvatar(true)
|
||||
{
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,8 @@ void Circle3DOverlay::render() {
|
|||
xColor color = getMajorTickMarksColor();
|
||||
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
float angle = startAt;
|
||||
float tickMarkAngle = getMajorTickMarksAngle();
|
||||
float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
float tickMarkLength = getMajorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
||||
|
@ -183,7 +184,7 @@ void Circle3DOverlay::render() {
|
|||
glVertex2f(thisPointA.x, thisPointA.y);
|
||||
glVertex2f(thisPointB.x, thisPointB.y);
|
||||
|
||||
angle += getMajorTickMarksAngle();
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +194,8 @@ void Circle3DOverlay::render() {
|
|||
xColor color = getMinorTickMarksColor();
|
||||
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||
|
||||
float angle = startAt;
|
||||
float tickMarkAngle = getMinorTickMarksAngle();
|
||||
float angle = startAt - fmod(startAt, tickMarkAngle) + tickMarkAngle;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
float tickMarkLength = getMinorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
||||
|
@ -208,7 +210,7 @@ void Circle3DOverlay::render() {
|
|||
glVertex2f(thisPointA.x, thisPointA.y);
|
||||
glVertex2f(thisPointB.x, thisPointB.y);
|
||||
|
||||
angle += getMinorTickMarksAngle();
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,9 +179,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner());
|
||||
QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft());
|
||||
QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter());
|
||||
QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions());
|
||||
boundingBox.setProperty("brn", bottomRightNear);
|
||||
boundingBox.setProperty("tfl", topFarLeft);
|
||||
boundingBox.setProperty("center", center);
|
||||
boundingBox.setProperty("dimensions", boundingBoxDimensions);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(boundingBox, boundingBox); // gettable, but not settable
|
||||
|
||||
return properties;
|
||||
|
|
|
@ -848,3 +848,108 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject
|
|||
VoxelMaterialSpannerEditVisitor visitor(spanner, material, averageColor);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
position(position),
|
||||
radius(radius),
|
||||
material(material),
|
||||
averageColor(averageColor) {
|
||||
}
|
||||
|
||||
class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& color);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _position;
|
||||
float _radius;
|
||||
SharedObjectPointer _material;
|
||||
QColor _color;
|
||||
Box _bounds;
|
||||
};
|
||||
|
||||
PaintVoxelMaterialEditVisitor::PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& color) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
|
||||
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
|
||||
_position(position),
|
||||
_radius(radius),
|
||||
_material(material),
|
||||
_color(color) {
|
||||
|
||||
glm::vec3 extents(_radius, _radius, _radius);
|
||||
_bounds = Box(_position - extents, _position + extents);
|
||||
}
|
||||
|
||||
int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.getBounds().intersects(_bounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
|
||||
VoxelMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<VoxelMaterialDataPointer>();
|
||||
if (!(colorPointer && materialPointer && colorPointer->getSize() == materialPointer->getSize())) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
QVector<QRgb> colorContents = colorPointer->getContents();
|
||||
QByteArray materialContents = materialPointer->getContents();
|
||||
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
|
||||
|
||||
Box overlap = info.getBounds().getIntersection(_bounds);
|
||||
int size = colorPointer->getSize();
|
||||
int area = size * size;
|
||||
float scale = (size - 1.0f) / info.size;
|
||||
overlap.minimum = (overlap.minimum - info.minimum) * scale;
|
||||
overlap.maximum = (overlap.maximum - info.minimum) * scale;
|
||||
int minX = glm::ceil(overlap.minimum.x);
|
||||
int minY = glm::ceil(overlap.minimum.y);
|
||||
int minZ = glm::ceil(overlap.minimum.z);
|
||||
int sizeX = (int)overlap.maximum.x - minX + 1;
|
||||
int sizeY = (int)overlap.maximum.y - minY + 1;
|
||||
int sizeZ = (int)overlap.maximum.z - minZ + 1;
|
||||
|
||||
QRgb rgb = _color.rgba();
|
||||
float step = 1.0f / scale;
|
||||
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
|
||||
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
|
||||
QRgb* colorData = colorContents.data();
|
||||
uchar* materialData = (uchar*)materialContents.data();
|
||||
for (int destZ = minZ * area + minY * size + minX, endZ = destZ + sizeZ * area; destZ != endZ;
|
||||
destZ += area, position.z += step) {
|
||||
position.y = info.minimum.y + minY * step;
|
||||
for (int destY = destZ, endY = destY + sizeY * size; destY != endY; destY += size, position.y += step) {
|
||||
position.x = info.minimum.x + minX * step;
|
||||
for (int destX = destY, endX = destX + sizeX; destX != endX; destX++, position.x += step) {
|
||||
QRgb& color = colorData[destX];
|
||||
if (qAlpha(color) != 0 && glm::distance(position, _position) <= _radius) {
|
||||
color = rgb;
|
||||
materialData[destX] = materialIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, size));
|
||||
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
|
||||
encodeInline<VoxelColorDataPointer>(newColorPointer));
|
||||
|
||||
clearUnusedMaterials(materials, materialContents);
|
||||
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, size, materials));
|
||||
info.outputValues[1] = AttributeValue(_inputs.at(1), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
|
||||
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
PaintVoxelMaterialEditVisitor visitor(position, radius, material, averageColor);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
|
|
@ -261,4 +261,23 @@ public:
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit)
|
||||
|
||||
/// An edit that sets a region of a voxel material.
|
||||
class PaintVoxelMaterialEdit : public MetavoxelEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM glm::vec3 position;
|
||||
STREAM float radius;
|
||||
STREAM SharedObjectPointer material;
|
||||
STREAM QColor averageColor;
|
||||
|
||||
PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
||||
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(PaintVoxelMaterialEdit)
|
||||
|
||||
#endif // hifi_MetavoxelMessages_h
|
||||
|
|
|
@ -85,7 +85,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeAudioStreamStats:
|
||||
return 1;
|
||||
case PacketTypeMetavoxelData:
|
||||
return 5;
|
||||
return 6;
|
||||
case PacketTypeVoxelData:
|
||||
return VERSION_VOXELS_HAS_FILE_BREAKS;
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue