Merge branch 'master' of https://github.com/highfidelity/hifi into domain-tunnel

This commit is contained in:
Stephen Birarda 2014-10-14 09:26:11 -07:00
commit 20f97792bf
31 changed files with 1438 additions and 883 deletions

View 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)

View file

@ -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)");

View file

@ -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);

View file

@ -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;

View file

@ -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() {

View file

@ -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)

View 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);
}

View 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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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");

View file

@ -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...";

View file

@ -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 };

View file

@ -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.

View file

@ -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;
}

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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());
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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

View file

@ -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: