merged from master

This commit is contained in:
Jose Carlos 2014-02-19 23:01:13 +01:00
commit 828508bbf1
38 changed files with 949 additions and 296 deletions

6
.gitignore vendored
View file

@ -39,5 +39,11 @@ interface/external/Leap/util/
interface/external/Sixense/include/ interface/external/Sixense/include/
interface/external/Sixense/lib/ interface/external/Sixense/lib/
# Ignore Visage
interface/external/visage/dependencies/
interface/external/visage/include/
interface/external/visage/lib/
interface/resources/visage/
# Ignore interfaceCache for Linux users # Ignore interfaceCache for Linux users
interface/interfaceCache/ interface/interfaceCache/

View file

@ -0,0 +1,76 @@
# Try to find the Visage controller library
#
# You must provide a VISAGE_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# VISAGE_FOUND - system found Visage
# VISAGE_INCLUDE_DIRS - the Visage include directory
# VISAGE_LIBRARIES - Link this to use Visage
#
# Created on 2/11/2014 by Andrzej Kapolka
# Copyright (c) 2014 High Fidelity
#
if (VISAGE_LIBRARIES AND VISAGE_INCLUDE_DIRS)
# in cache already
set(VISAGE_FOUND TRUE)
else (VISAGE_LIBRARIES AND VISAGE_INCLUDE_DIRS)
find_path(VISAGE_INCLUDE_DIR VisageTracker2.h ${VISAGE_ROOT_DIR}/include)
if (APPLE)
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h /usr/include/libxml2)
find_path(VISAGE_OPENCV_INCLUDE_DIR cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/include)
find_path(VISAGE_OPENCV2_INCLUDE_DIR opencv.hpp ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/include/opencv2)
if (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
set(VISAGE_INCLUDE_DIRS
"${VISAGE_INCLUDE_DIR};${VISAGE_XML_INCLUDE_DIR};${VISAGE_OPENCV_INCLUDE_DIR};${VISAGE_OPENCV2_INCLUDE_DIR}"
CACHE INTERNAL "Visage include dirs")
endif (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
find_library(VISAGE_CORE_LIBRARY libvscore.a ${VISAGE_ROOT_DIR}/lib)
find_library(VISAGE_VISION_LIBRARY libvsvision.a ${VISAGE_ROOT_DIR}/lib)
find_library(VISAGE_OPENCV_LIBRARY libOpenCV.a ${VISAGE_ROOT_DIR}/dependencies/OpenCV_MacOSX/lib)
if (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY};${VISAGE_VISION_LIBRARY};${VISAGE_OPENCV_LIBRARY}"
CACHE INTERNAL "Visage libraries")
endif (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
elseif (WIN32)
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h ${VISAGE_ROOT_DIR}/dependencies/libxml2/include)
find_path(VISAGE_OPENCV_INCLUDE_DIR opencv/cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV/include)
find_path(VISAGE_OPENCV2_INCLUDE_DIR cv.h ${VISAGE_ROOT_DIR}/dependencies/OpenCV/include/opencv)
if (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
set(VISAGE_INCLUDE_DIRS
"${VISAGE_INCLUDE_DIR};${VISAGE_XML_INCLUDE_DIR};${VISAGE_OPENCV_INCLUDE_DIR};${VISAGE_OPENCV2_INCLUDE_DIR}"
CACHE INTERNAL "Visage include dirs")
endif (VISAGE_INCLUDE_DIR AND VISAGE_XML_INCLUDE_DIR AND VISAGE_OPENCV_INCLUDE_DIR AND VISAGE_OPENCV2_INCLUDE_DIR)
find_library(VISAGE_CORE_LIBRARY vscore.lib ${VISAGE_ROOT_DIR}/lib)
find_library(VISAGE_VISION_LIBRARY vsvision.lib ${VISAGE_ROOT_DIR}/lib)
find_library(VISAGE_OPENCV_LIBRARY opencv_core243.lib ${VISAGE_ROOT_DIR}/dependencies/OpenCV/lib)
if (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY};${VISAGE_VISION_LIBRARY};${VISAGE_OPENCV_LIBRARY}"
CACHE INTERNAL "Visage libraries")
endif (VISAGE_CORE_LIBRARY AND VISAGE_VISION_LIBRARY AND VISAGE_OPENCV_LIBRARY)
endif ()
if (VISAGE_INCLUDE_DIRS AND VISAGE_LIBRARIES)
set(VISAGE_FOUND TRUE)
endif (VISAGE_INCLUDE_DIRS AND VISAGE_LIBRARIES)
if (VISAGE_FOUND)
if (NOT VISAGE_FIND_QUIETLY)
message(STATUS "Found Visage: ${VISAGE_LIBRARIES}")
endif (NOT VISAGE_FIND_QUIETLY)
else (VISAGE_FOUND)
if (VISAGE_FIND_REQUIRED)
message(FATAL_ERROR "Could not find Visage")
endif (VISAGE_FIND_REQUIRED)
endif (VISAGE_FOUND)
# show the VISAGE_INCLUDE_DIRS and VISAGE_LIBRARIES variables only in the advanced view
mark_as_advanced(VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES)
endif (VISAGE_LIBRARIES AND VISAGE_INCLUDE_DIRS)

View file

@ -16,14 +16,7 @@
// Click and drag to create more new voxels in the same direction // Click and drag to create more new voxels in the same direction
// //
function vLength(v) { var windowDimensions = Controller.getViewportDimensions();
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
function vMinus(a, b) {
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
return rval;
}
var NEW_VOXEL_SIZE = 1.0; var NEW_VOXEL_SIZE = 1.0;
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0; var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
@ -76,6 +69,52 @@ var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-publ
var audioOptions = new AudioInjectionOptions(); var audioOptions = new AudioInjectionOptions();
audioOptions.volume = 0.5; audioOptions.volume = 0.5;
var editToolsOn = false; // starts out off
var voxelPreview = Overlays.addOverlay("cube", {
position: { x: 0, y: 0, z: 0},
size: 1,
color: { red: 255, green: 0, blue: 0},
alpha: 1,
solid: false,
visible: false,
lineWidth: 4
});
// These will be our "overlay IDs"
var swatches = new Array();
var swatchHeight = 54;
var swatchWidth = 31;
var swatchesWidth = swatchWidth * numColors;
var swatchesX = (windowDimensions.x - swatchesWidth) / 2;
var swatchesY = windowDimensions.y - swatchHeight;
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
// location so that it displays the "selected" marker
for (s = 0; s < numColors; s++) {
var imageFromX = 12 + (s * 27);
var imageFromY = 0;
if (s == whichColor) {
imageFromY = 55;
}
var swatchX = swatchesX + (30 * s);
swatches[s] = Overlays.addOverlay("image", {
x: swatchX,
y: swatchesY,
width: swatchWidth,
height: swatchHeight,
subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight },
imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg",
color: colors[s],
alpha: 1,
visible: editToolsOn
});
}
function setAudioPosition() { function setAudioPosition() {
var camera = Camera.getPosition(); var camera = Camera.getPosition();
var forwardVector = Quat.getFront(MyAvatar.orientation); var forwardVector = Quat.getFront(MyAvatar.orientation);
@ -101,7 +140,141 @@ function fixEulerAngles(eulers) {
return rVal; return rVal;
} }
var trackLastMouseX = 0;
var trackLastMouseY = 0;
var trackAsDelete = false;
var trackAsRecolor = false;
function showPreviewVoxel() {
if (editToolsOn) {
var voxelColor;
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
var intersection = Voxels.findRayIntersection(pickRay);
if (whichColor == -1) {
// Copy mode - use clicked voxel color
voxelColor = { red: intersection.voxel.red,
green: intersection.voxel.green,
blue: intersection.voxel.blue };
} else {
voxelColor = { red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
}
var guidePosition;
if (trackAsDelete) {
guidePosition = { x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z };
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s,
visible: true,
color: { red: 255, green: 0, blue: 0 },
solid: false,
alpha: 1
});
} else if (trackAsRecolor) {
guidePosition = { x: intersection.voxel.x - 0.001,
y: intersection.voxel.y - 0.001,
z: intersection.voxel.z - 0.001 };
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s + 0.002,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.8
});
} else if (!isExtruding) {
guidePosition = { x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z };
if (intersection.face == "MIN_X_FACE") {
guidePosition.x -= intersection.voxel.s;
} else if (intersection.face == "MAX_X_FACE") {
guidePosition.x += intersection.voxel.s;
} else if (intersection.face == "MIN_Y_FACE") {
guidePosition.y -= intersection.voxel.s;
} else if (intersection.face == "MAX_Y_FACE") {
guidePosition.y += intersection.voxel.s;
} else if (intersection.face == "MIN_Z_FACE") {
guidePosition.z -= intersection.voxel.s;
} else if (intersection.face == "MAX_Z_FACE") {
guidePosition.z += intersection.voxel.s;
}
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.7
});
} else if (isExtruding) {
Overlays.editOverlay(voxelPreview, { visible: false });
}
} else {
Overlays.editOverlay(voxelPreview, { visible: false });
}
}
function trackMouseEvent(event) {
trackLastMouseX = event.x;
trackLastMouseY = event.y;
trackAsDelete = event.isControl;
trackAsRecolor = event.isShifted;
showPreviewVoxel();
}
function trackKeyPressEvent(event) {
if (event.text == "CONTROL") {
trackAsDelete = true;
showPreviewVoxel();
}
if (event.text == "SHIFT") {
trackAsRecolor = true;
}
showPreviewVoxel();
}
function trackKeyReleaseEvent(event) {
if (event.text == "CONTROL") {
trackAsDelete = false;
showPreviewVoxel();
}
if (event.text == "SHIFT") {
trackAsRecolor = false;
}
// on TAB release, toggle our tool state
if (event.text == "TAB") {
editToolsOn = !editToolsOn;
moveTools();
Audio.playSound(clickSound, audioOptions);
}
showPreviewVoxel();
}
function mousePressEvent(event) { function mousePressEvent(event) {
// if our tools are off, then don't do anything
if (!editToolsOn) {
return;
}
if (event.isRightButton) {
// debugging of right button click on mac...
print(">>>> RIGHT BUTTON <<<<<");
}
trackMouseEvent(event); // used by preview support
mouseX = event.x; mouseX = event.x;
mouseY = event.y; mouseY = event.y;
var pickRay = Camera.computePickRay(event.x, event.y); var pickRay = Camera.computePickRay(event.x, event.y);
@ -118,16 +291,17 @@ function mousePressEvent(event) {
// get position for initial azimuth, elevation // get position for initial azimuth, elevation
orbitCenter = intersection.intersection; orbitCenter = intersection.intersection;
var orbitVector = Vec3.subtract(cameraPosition, orbitCenter); var orbitVector = Vec3.subtract(cameraPosition, orbitCenter);
orbitRadius = vLength(orbitVector); orbitRadius = Vec3.length(orbitVector);
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
} else if (event.isRightButton || event.isControl) { } else if (trackAsDelete || event.isRightButton) {
// Delete voxel // Delete voxel
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s); Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
Audio.playSound(deleteSound, audioOptions); Audio.playSound(deleteSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (event.isShifted) { } else if (trackAsRecolor) {
// Recolor Voxel // Recolor Voxel
Voxels.setVoxel(intersection.voxel.x, Voxels.setVoxel(intersection.voxel.x,
intersection.voxel.y, intersection.voxel.y,
@ -135,6 +309,7 @@ function mousePressEvent(event) {
intersection.voxel.s, intersection.voxel.s,
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue); colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
Audio.playSound(changeColorSound, audioOptions); Audio.playSound(changeColorSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
} else { } else {
// Add voxel on face // Add voxel on face
if (whichColor == -1) { if (whichColor == -1) {
@ -178,6 +353,7 @@ function mousePressEvent(event) {
lastVoxelScale = newVoxel.s; lastVoxelScale = newVoxel.s;
Audio.playSound(addSound, audioOptions); Audio.playSound(addSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
dragStart = { x: event.x, y: event.y }; dragStart = { x: event.x, y: event.y };
isAdding = true; isAdding = true;
} }
@ -185,6 +361,8 @@ function mousePressEvent(event) {
} }
function keyPressEvent(event) { function keyPressEvent(event) {
// if our tools are off, then don't do anything
if (editToolsOn) {
key_alt = event.isAlt; key_alt = event.isAlt;
key_shift = event.isShifted; key_shift = event.isShifted;
var nVal = parseInt(event.text); var nVal = parseInt(event.text);
@ -192,10 +370,12 @@ function keyPressEvent(event) {
print("Color = Copy"); print("Color = Copy");
whichColor = -1; whichColor = -1;
Audio.playSound(clickSound, audioOptions); Audio.playSound(clickSound, audioOptions);
moveTools();
} else if ((nVal > 0) && (nVal <= numColors)) { } else if ((nVal > 0) && (nVal <= numColors)) {
whichColor = nVal - 1; whichColor = nVal - 1;
print("Color = " + (whichColor + 1)); print("Color = " + (whichColor + 1));
Audio.playSound(clickSound, audioOptions); Audio.playSound(clickSound, audioOptions);
moveTools();
} else if (event.text == "9") { } else if (event.text == "9") {
// Create a brand new 1 meter voxel in front of your avatar // Create a brand new 1 meter voxel in front of your avatar
var color = whichColor; var color = whichColor;
@ -212,15 +392,21 @@ function keyPressEvent(event) {
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue); Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
setAudioPosition(); setAudioPosition();
Audio.playSound(addSound, audioOptions); Audio.playSound(addSound, audioOptions);
} else if (event.text == " ") { }
}
// do this even if not in edit tools
if (event.text == " ") {
// Reset my orientation! // Reset my orientation!
var orientation = { x:0, y:0, z:0, w:1 }; var orientation = { x:0, y:0, z:0, w:1 };
Camera.setOrientation(orientation); Camera.setOrientation(orientation);
MyAvatar.orientation = orientation; MyAvatar.orientation = orientation;
} }
trackKeyPressEvent(event); // used by preview support
} }
function keyReleaseEvent(event) { function keyReleaseEvent(event) {
trackKeyReleaseEvent(event); // used by preview support
key_alt = false; key_alt = false;
key_shift = false; key_shift = false;
} }
@ -248,7 +434,7 @@ function mouseMoveEvent(event) {
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x, var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
y: pickRay.origin.y - lastVoxelPosition.y, y: pickRay.origin.y - lastVoxelPosition.y,
z: pickRay.origin.z - lastVoxelPosition.z }; z: pickRay.origin.z - lastVoxelPosition.z };
var distance = vLength(lastVoxelDistance); var distance = Vec3.length(lastVoxelDistance);
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance }; var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
mouseSpot.x += pickRay.origin.x; mouseSpot.x += pickRay.origin.x;
mouseSpot.y += pickRay.origin.y; mouseSpot.y += pickRay.origin.y;
@ -279,9 +465,17 @@ function mouseMoveEvent(event) {
} }
} }
} }
// update the add voxel/delete voxel overlay preview
trackMouseEvent(event);
} }
function mouseReleaseEvent(event) { function mouseReleaseEvent(event) {
// if our tools are off, then don't do anything
if (!editToolsOn) {
return;
}
if (isOrbiting) { if (isOrbiting) {
var cameraOrientation = Camera.getOrientation(); var cameraOrientation = Camera.getOrientation();
var eulers = Quat.safeEulerAngles(cameraOrientation); var eulers = Quat.safeEulerAngles(cameraOrientation);
@ -296,6 +490,41 @@ function mouseReleaseEvent(event) {
isExtruding = false; isExtruding = false;
} }
function moveTools() {
swatchesX = (windowDimensions.x - swatchesWidth) / 2;
swatchesY = windowDimensions.y - swatchHeight;
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
// location so that it displays the "selected" marker
for (s = 0; s < numColors; s++) {
var imageFromX = 12 + (s * 27);
var imageFromY = 0;
if (s == whichColor) {
imageFromY = 55;
}
var swatchX = swatchesX + ((swatchWidth - 1) * s);
Overlays.editOverlay(swatches[s], {
x: swatchX,
y: swatchesY,
subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight },
color: colors[s],
alpha: 1,
visible: editToolsOn
});
}
}
function update() {
var newWindowDimensions = Controller.getViewportDimensions();
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
print("window resized...");
moveTools();
}
}
Controller.mousePressEvent.connect(mousePressEvent); Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent);
@ -303,5 +532,15 @@ Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent);
function scriptEnding() { function scriptEnding() {
Overlays.deleteOverlay(voxelPreview);
for (s = 0; s < numColors; s++) {
Overlays.deleteOverlay(swatches[s]);
}
} }
Script.scriptEnding.connect(scriptEnding); Script.scriptEnding.connect(scriptEnding);
Script.willSendVisualDataCallback.connect(update);

View file

@ -11,6 +11,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake
set(FACESHIFT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift) set(FACESHIFT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift)
set(LIBOVR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibOVR) set(LIBOVR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibOVR)
set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense) set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense)
set(VISAGE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/visage)
if (DEFINED ENV{JOB_ID}) if (DEFINED ENV{JOB_ID})
set(BUILD_SEQ $ENV{JOB_ID}) set(BUILD_SEQ $ENV{JOB_ID})
@ -138,9 +139,10 @@ find_package(Faceshift)
find_package(GLM REQUIRED) find_package(GLM REQUIRED)
find_package(LibOVR) find_package(LibOVR)
find_package(Sixense) find_package(Sixense)
find_package(Visage)
find_package(ZLIB) find_package(ZLIB)
# likewise with Sixense library for Razer Hydra # include the Sixense library for Razer Hydra if available
if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE) if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
add_definitions(-DHAVE_SIXENSE) add_definitions(-DHAVE_SIXENSE)
include_directories(SYSTEM ${SIXENSE_INCLUDE_DIRS}) include_directories(SYSTEM ${SIXENSE_INCLUDE_DIRS})
@ -150,6 +152,21 @@ if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE) endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
# likewise with Visage library for webcam feature tracking
if (VISAGE_FOUND AND NOT DISABLE_VISAGE)
add_definitions(-DHAVE_VISAGE -DVISAGE_STATIC)
include_directories(SYSTEM ${VISAGE_INCLUDE_DIRS})
if (APPLE)
add_definitions(-DMAC_OS_X)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment -isystem ${VISAGE_INCLUDE_DIRS}")
find_library(AVFoundation AVFoundation)
find_library(CoreMedia CoreMedia)
find_library(NEW_STD_LIBRARY libc++.dylib /usr/lib/)
target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY})
endif (APPLE)
target_link_libraries(${TARGET_NAME} ${VISAGE_LIBRARIES})
endif (VISAGE_FOUND AND NOT DISABLE_VISAGE)
# and with LibOVR for Oculus Rift # and with LibOVR for Oculus Rift
if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
add_definitions(-DHAVE_LIBOVR) add_definitions(-DHAVE_LIBOVR)

14
interface/external/visage/readme.txt vendored Normal file
View file

@ -0,0 +1,14 @@
Instructions for adding the Visage driver to Interface
Andrzej Kapolka, February 11, 2014
1. Copy the Visage sdk folders (lib, include, dependencies) into the interface/external/visage folder.
This readme.txt should be there as well.
2. Copy the Visage configuration data folder (visageSDK-MacOS/Samples/MacOSX/data/) to interface/resources/visage
(i.e., so that interface/resources/visage/candide3.wfm is accessible)
3. Copy the Visage license file to interface/resources/visage/license.vlc.
4. Delete your build directory, run cmake and build, and you should be all set.

View file

@ -450,22 +450,22 @@ void Application::paintGL() {
_myCamera.setUpShift(0.0f); _myCamera.setUpShift(0.0f);
_myCamera.setDistance(0.0f); _myCamera.setDistance(0.0f);
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing _myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition()); _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead().getOrientation()); _myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay _myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
_myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition()); _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation()); _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing _myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition()); _myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
_myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation()); _myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_myCamera.setTightness(0.0f); _myCamera.setTightness(0.0f);
float headHeight = _myAvatar->getHead().calculateAverageEyePosition().y - _myAvatar->getPosition().y; float headHeight = _myAvatar->getHead()->calculateAverageEyePosition().y - _myAvatar->getPosition().y;
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale()); _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale());
_myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0)); _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0));
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
@ -529,14 +529,14 @@ void Application::paintGL() {
_mirrorCamera.setTargetPosition(_myAvatar->getChestPosition()); _mirrorCamera.setTargetPosition(_myAvatar->getChestPosition());
} else { // HEAD zoom level } else { // HEAD zoom level
_mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); _mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead().getFaceModel().isActive()) { if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) {
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the // as a hack until we have a better way of dealing with coordinate precision issues, reposition the
// face/body so that the average eye position lies at the origin // face/body so that the average eye position lies at the origin
eyeRelativeCamera = true; eyeRelativeCamera = true;
_mirrorCamera.setTargetPosition(glm::vec3()); _mirrorCamera.setTargetPosition(glm::vec3());
} else { } else {
_mirrorCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition()); _mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
} }
} }
@ -558,26 +558,26 @@ void Application::paintGL() {
if (eyeRelativeCamera) { if (eyeRelativeCamera) {
// save absolute translations // save absolute translations
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation(); glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead().getFaceModel().getTranslation(); glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
// get the eye positions relative to the neck and use them to set the face translation // get the eye positions relative to the neck and use them to set the face translation
glm::vec3 leftEyePosition, rightEyePosition; glm::vec3 leftEyePosition, rightEyePosition;
_myAvatar->getHead().getFaceModel().setTranslation(glm::vec3()); _myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
_myAvatar->getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); _myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition);
_myAvatar->getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); _myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f);
// get the neck position relative to the body and use it to set the skeleton translation // get the neck position relative to the body and use it to set the skeleton translation
glm::vec3 neckPosition; glm::vec3 neckPosition;
_myAvatar->getSkeletonModel().setTranslation(glm::vec3()); _myAvatar->getSkeletonModel().setTranslation(glm::vec3());
_myAvatar->getSkeletonModel().getNeckPosition(neckPosition); _myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead().getFaceModel().getTranslation() - _myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
neckPosition); neckPosition);
displaySide(_mirrorCamera, true); displaySide(_mirrorCamera, true);
// restore absolute translations // restore absolute translations
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation); _myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
_myAvatar->getHead().getFaceModel().setTranslation(absoluteFaceTranslation); _myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
} else { } else {
displaySide(_mirrorCamera, true); displaySide(_mirrorCamera, true);
} }
@ -1867,7 +1867,7 @@ void Application::init() {
// TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager // TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager
_avatarManager.init(); _avatarManager.init();
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftPeriod(1.0f);
_mirrorCamera.setMode(CAMERA_MODE_MIRROR); _mirrorCamera.setMode(CAMERA_MODE_MIRROR);
_mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT);
@ -1978,8 +1978,8 @@ const float MAX_VOXEL_EDIT_DISTANCE = 50.0f;
const float HEAD_SPHERE_RADIUS = 0.07f; const float HEAD_SPHERE_RADIUS = 0.07f;
bool Application::isLookingAtMyAvatar(Avatar* avatar) { bool Application::isLookingAtMyAvatar(Avatar* avatar) {
glm::vec3 theirLookat = avatar->getHead().getLookAtPosition(); glm::vec3 theirLookat = avatar->getHead()->getLookAtPosition();
glm::vec3 myHeadPosition = _myAvatar->getHead().getPosition(); glm::vec3 myHeadPosition = _myAvatar->getHead()->getPosition();
if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) { if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) {
return true; return true;
@ -2040,10 +2040,19 @@ void Application::updateFaceshift() {
// Copy angular velocity if measured by faceshift, to the head // Copy angular velocity if measured by faceshift, to the head
if (_faceshift.isActive()) { if (_faceshift.isActive()) {
_myAvatar->getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity()); _myAvatar->getHead()->setAngularVelocity(_faceshift.getHeadAngularVelocity());
} }
} }
void Application::updateVisage() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateVisage()");
// Update Visage
_visage.update();
}
void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) { void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
@ -2057,23 +2066,35 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
float distance = TREE_SCALE; float distance = TREE_SCALE;
if (_myAvatar->getLookAtTargetAvatar()) { if (_myAvatar->getLookAtTargetAvatar()) {
distance = glm::distance(_mouseRayOrigin, distance = glm::distance(_mouseRayOrigin,
static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead().calculateAverageEyePosition()); static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition());
} else if (_isHoverVoxel) { } else if (_isHoverVoxel) {
distance = glm::distance(_mouseRayOrigin, getMouseVoxelWorldCoordinates(_hoverVoxel)); distance = glm::distance(_mouseRayOrigin, getMouseVoxelWorldCoordinates(_hoverVoxel));
} }
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * distance; lookAtSpot = _mouseRayOrigin + _mouseRayDirection * distance;
} }
bool trackerActive = false;
float eyePitch, eyeYaw;
if (_faceshift.isActive()) { if (_faceshift.isActive()) {
eyePitch = _faceshift.getEstimatedEyePitch();
eyeYaw = _faceshift.getEstimatedEyeYaw();
trackerActive = true;
} else if (_visage.isActive()) {
eyePitch = _visage.getEstimatedEyePitch();
eyeYaw = _visage.getEstimatedEyeYaw();
trackerActive = true;
}
if (trackerActive) {
// deflect using Faceshift gaze data // deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar->getHead().calculateAverageEyePosition(); glm::vec3 origin = _myAvatar->getHead()->calculateAverageEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f; float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
float deflection = Menu::getInstance()->getFaceshiftEyeDeflection(); float deflection = Menu::getInstance()->getFaceshiftEyeDeflection();
lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3( lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
_faceshift.getEstimatedEyePitch() * pitchSign * deflection, _faceshift.getEstimatedEyeYaw() * deflection, 0.0f))) * eyePitch * pitchSign * deflection, eyeYaw * deflection, 0.0f))) *
glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin); glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin);
} }
_myAvatar->getHead().setLookAtPosition(lookAtSpot); _myAvatar->getHead()->setLookAtPosition(lookAtSpot);
} }
void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) { void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) {
@ -2229,17 +2250,17 @@ void Application::cameraMenuChanged() {
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
_myCamera.setMode(CAMERA_MODE_MIRROR); _myCamera.setMode(CAMERA_MODE_MIRROR);
_myCamera.setModeShiftRate(100.0f); _myCamera.setModeShiftPeriod(0.00f);
} }
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { } else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftPeriod(1.0f);
} }
} else { } else {
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON); _myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
_myCamera.setModeShiftRate(1.0f); _myCamera.setModeShiftPeriod(1.0f);
} }
} }
} }
@ -2321,6 +2342,7 @@ void Application::update(float deltaTime) {
glm::vec3 lookAtSpot; glm::vec3 lookAtSpot;
updateFaceshift(); updateFaceshift();
updateVisage();
_myAvatar->updateLookAtTargetAvatar(lookAtSpot); _myAvatar->updateLookAtTargetAvatar(lookAtSpot);
updateMyAvatarLookAtPosition(lookAtSpot); updateMyAvatarLookAtPosition(lookAtSpot);
@ -3833,6 +3855,7 @@ void Application::resetSensors() {
_mouseY = _glWidget->height() / 2; _mouseY = _glWidget->height() / 2;
_faceshift.reset(); _faceshift.reset();
_visage.reset();
if (OculusManager::isConnected()) { if (OculusManager::isConnected()) {
OculusManager::reset(); OculusManager::reset();

View file

@ -54,6 +54,7 @@
#include "avatar/Profile.h" #include "avatar/Profile.h"
#include "devices/Faceshift.h" #include "devices/Faceshift.h"
#include "devices/SixenseManager.h" #include "devices/SixenseManager.h"
#include "devices/Visage.h"
#include "renderer/AmbientOcclusionEffect.h" #include "renderer/AmbientOcclusionEffect.h"
#include "renderer/GeometryCache.h" #include "renderer/GeometryCache.h"
#include "renderer/GlowEffect.h" #include "renderer/GlowEffect.h"
@ -160,6 +161,7 @@ public:
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
Faceshift* getFaceshift() { return &_faceshift; } Faceshift* getFaceshift() { return &_faceshift; }
Visage* getVisage() { return &_visage; }
SixenseManager* getSixenseManager() { return &_sixenseManager; } SixenseManager* getSixenseManager() { return &_sixenseManager; }
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
QSettings* getSettings() { return _settings; } QSettings* getSettings() { return _settings; }
@ -286,6 +288,7 @@ private:
// Various helper functions called during update() // Various helper functions called during update()
void updateMouseRay(); void updateMouseRay();
void updateFaceshift(); void updateFaceshift();
void updateVisage();
void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot); void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot);
void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face); void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face);
void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face); void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face);
@ -385,6 +388,7 @@ private:
Profile _profile; // The data-server linked profile for this user Profile _profile; // The data-server linked profile for this user
Faceshift _faceshift; Faceshift _faceshift;
Visage _visage;
SixenseManager _sixenseManager; SixenseManager _sixenseManager;
QStringList _activeScripts; QStringList _activeScripts;

View file

@ -467,8 +467,8 @@ void Audio::handleAudioInput() {
if (audioMixer && audioMixer->getActiveSocket()) { if (audioMixer && audioMixer->getActiveSocket()) {
MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar();
glm::vec3 headPosition = interfaceAvatar->getHead().getPosition(); glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition();
glm::quat headOrientation = interfaceAvatar->getHead().getOrientation(); glm::quat headOrientation = interfaceAvatar->getHead()->getOrientation();
// we need the amount of bytes in the buffer + 1 for type // we need the amount of bytes in the buffer + 1 for type
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte // + 12 for 3 floats for position + float for bearing + 1 attenuation byte

View file

@ -15,8 +15,6 @@
#include "Menu.h" #include "Menu.h"
#include "Util.h" #include "Util.h"
const float CAMERA_MINIMUM_MODE_SHIFT_RATE = 0.5f;
const float CAMERA_FIRST_PERSON_MODE_UP_SHIFT = 0.0f; const float CAMERA_FIRST_PERSON_MODE_UP_SHIFT = 0.0f;
const float CAMERA_FIRST_PERSON_MODE_DISTANCE = 0.0f; const float CAMERA_FIRST_PERSON_MODE_DISTANCE = 0.0f;
const float CAMERA_FIRST_PERSON_MODE_TIGHTNESS = 100.0f; const float CAMERA_FIRST_PERSON_MODE_TIGHTNESS = 100.0f;
@ -57,7 +55,7 @@ Camera::Camera() :
_newTightness(0.0f), _newTightness(0.0f),
_modeShift(1.0f), _modeShift(1.0f),
_linearModeShift(0.0f), _linearModeShift(0.0f),
_modeShiftRate(1.0f), _modeShiftPeriod(1.0f),
_scale(1.0f), _scale(1.0f),
_lookingAt(0.0f, 0.0f, 0.0f), _lookingAt(0.0f, 0.0f, 0.0f),
_isKeepLookingAt(false) _isKeepLookingAt(false)
@ -75,18 +73,18 @@ void Camera::update(float deltaTime) {
// use iterative forces to keep the camera at the desired position and angle // use iterative forces to keep the camera at the desired position and angle
void Camera::updateFollowMode(float deltaTime) { void Camera::updateFollowMode(float deltaTime) {
if (_linearModeShift < 1.0f) { if (_linearModeShift < 1.0f) {
_linearModeShift += _modeShiftRate * deltaTime; _linearModeShift += deltaTime / _modeShiftPeriod;
_modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PIE );
_upShift = _previousUpShift * (1.0f - _modeShift) + _newUpShift * _modeShift;
_distance = _previousDistance * (1.0f - _modeShift) + _newDistance * _modeShift;
_tightness = _previousTightness * (1.0f - _modeShift) + _newTightness * _modeShift;
if (_needsToInitialize || _linearModeShift > 1.0f) { if (_needsToInitialize || _linearModeShift > 1.0f) {
_linearModeShift = 1.0f; _linearModeShift = 1.0f;
_modeShift = 1.0f; _modeShift = 1.0f;
_upShift = _newUpShift; _upShift = _newUpShift;
_distance = _newDistance; _distance = _newDistance;
_tightness = _newTightness; _tightness = _newTightness;
} else {
_modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PIE );
_upShift = _previousUpShift * (1.0f - _modeShift) + _newUpShift * _modeShift;
_distance = _previousDistance * (1.0f - _modeShift) + _newDistance * _modeShift;
_tightness = _previousTightness * (1.0f - _modeShift) + _newTightness * _modeShift;
} }
} }
@ -121,13 +119,10 @@ float Camera::getFarClip() const {
: std::numeric_limits<int16_t>::max() - 1; : std::numeric_limits<int16_t>::max() - 1;
} }
void Camera::setModeShiftRate ( float rate ) { void Camera::setModeShiftPeriod (float period) {
const float MIN_PERIOD = 0.001f;
_modeShiftRate = rate; const float MAX_PERIOD = 3.0f;
_modeShiftPeriod = glm::clamp(period, MIN_PERIOD, MAX_PERIOD);
if (_modeShiftRate < CAMERA_MINIMUM_MODE_SHIFT_RATE ) {
_modeShiftRate = CAMERA_MINIMUM_MODE_SHIFT_RATE;
}
} }
void Camera::setMode(CameraMode m) { void Camera::setMode(CameraMode m) {
@ -307,7 +302,8 @@ void CameraScriptableObject::setMode(const QString& mode) {
} }
if (currentMode != targetMode) { if (currentMode != targetMode) {
_camera->setMode(targetMode); _camera->setMode(targetMode);
_camera->setModeShiftRate(10.0f); const float DEFAULT_MODE_SHIFT_PERIOD = 0.5f; // half second
_camera->setModeShiftPeriod(DEFAULT_MODE_SHIFT_PERIOD);
} }
} }

View file

@ -43,7 +43,7 @@ public:
void setTargetRotation(const glm::quat& rotation); void setTargetRotation(const glm::quat& rotation);
void setMode(CameraMode m); void setMode(CameraMode m);
void setModeShiftRate(float r); void setModeShiftPeriod(float r);
void setFieldOfView(float f); void setFieldOfView(float f);
void setAspectRatio(float a); void setAspectRatio(float a);
void setNearClip(float n); void setNearClip(float n);
@ -109,7 +109,7 @@ private:
float _newTightness; float _newTightness;
float _modeShift; float _modeShift;
float _linearModeShift; float _linearModeShift;
float _modeShiftRate; float _modeShiftPeriod;
float _scale; float _scale;
glm::vec3 _lookingAt; glm::vec3 _lookingAt;

View file

@ -250,3 +250,7 @@ void ControllerScriptingInterface::releaseJoystick(int joystickIndex) {
} }
} }
glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
QGLWidget* widget = Application::getInstance()->getGLWidget();
return glm::vec2(widget->width(), widget->height());
}

View file

@ -74,6 +74,8 @@ public slots:
virtual void captureJoystick(int joystickIndex); virtual void captureJoystick(int joystickIndex);
virtual void releaseJoystick(int joystickIndex); virtual void releaseJoystick(int joystickIndex);
virtual glm::vec2 getViewportDimensions() const;
private: private:
const PalmData* getPrimaryPalm() const; const PalmData* getPrimaryPalm() const;
const PalmData* getPalm(int palmIndex) const; const PalmData* getPalm(int palmIndex) const;

View file

@ -327,7 +327,8 @@ Menu::Menu() :
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionProxies);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionProxies);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
@ -772,7 +773,7 @@ void Menu::editPreferences() {
QFormLayout* form = new QFormLayout(); QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1); layout->addLayout(form, 1);
QString faceURLString = applicationInstance->getAvatar()->getHead().getFaceModel().getURL().toString(); QString faceURLString = applicationInstance->getAvatar()->getHead()->getFaceModel().getURL().toString();
QLineEdit* faceURLEdit = new QLineEdit(faceURLString); QLineEdit* faceURLEdit = new QLineEdit(faceURLString);
faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH); faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
faceURLEdit->setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString()); faceURLEdit->setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString());
@ -790,7 +791,7 @@ void Menu::editPreferences() {
form->addRow("Display name:", displayNameEdit); form->addRow("Display name:", displayNameEdit);
QSlider* pupilDilation = new QSlider(Qt::Horizontal); QSlider* pupilDilation = new QSlider(Qt::Horizontal);
pupilDilation->setValue(applicationInstance->getAvatar()->getHead().getPupilDilation() * pupilDilation->maximum()); pupilDilation->setValue(applicationInstance->getAvatar()->getHead()->getPupilDilation() * pupilDilation->maximum());
form->addRow("Pupil Dilation:", pupilDilation); form->addRow("Pupil Dilation:", pupilDilation);
QSlider* faceshiftEyeDeflection = new QSlider(Qt::Horizontal); QSlider* faceshiftEyeDeflection = new QSlider(Qt::Horizontal);
@ -873,7 +874,7 @@ void Menu::editPreferences() {
applicationInstance->getAvatar()->sendIdentityPacket(); applicationInstance->getAvatar()->sendIdentityPacket();
} }
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum()); applicationInstance->getAvatar()->getHead()->setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
_maxVoxels = maxVoxels->value(); _maxVoxels = maxVoxels->value();
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels); applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);

View file

@ -185,7 +185,6 @@ namespace MenuOption {
const QString Bandwidth = "Bandwidth Display"; const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details"; const QString BandwidthDetails = "Bandwidth Details";
const QString ChatCircling = "Chat Circling"; const QString ChatCircling = "Chat Circling";
const QString CollisionProxies = "Collision Proxies";
const QString Collisions = "Collisions"; const QString Collisions = "Collisions";
const QString CollideWithAvatars = "Collide With Avatars"; const QString CollideWithAvatars = "Collide With Avatars";
const QString CollideWithParticles = "Collide With Particles"; const QString CollideWithParticles = "Collide With Particles";
@ -267,6 +266,8 @@ namespace MenuOption {
const QString Preferences = "Preferences..."; const QString Preferences = "Preferences...";
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors"; const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";
const QString ReloadAllScripts = "Reload All Scripts"; const QString ReloadAllScripts = "Reload All Scripts";
const QString RenderSkeletonCollisionProxies = "Skeleton Collision Proxies";
const QString RenderHeadCollisionProxies = "Head Collision Proxies";
const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSwatchColors = "Reset Swatch Colors"; const QString ResetSwatchColors = "Reset Swatch Colors";
const QString RunTimingTests = "Run Timing Tests"; const QString RunTimingTests = "Run Timing Tests";

View file

@ -2201,7 +2201,6 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// ok, now do some processing for this node... // ok, now do some processing for this node...
switch (inFrustum) { switch (inFrustum) {
case ViewFrustum::OUTSIDE: { case ViewFrustum::OUTSIDE: {
// If this node is outside the current view, then we might want to hide it... unless it was previously OUTSIDE, // If this node is outside the current view, then we might want to hide it... unless it was previously OUTSIDE,
// if it was previously outside, then we can safely assume it's already hidden, and we can also safely assume // if it was previously outside, then we can safely assume it's already hidden, and we can also safely assume
// that all of it's children are outside both of our views, in which case we can just stop recursing... // that all of it's children are outside both of our views, in which case we can just stop recursing...
@ -2215,12 +2214,10 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse // we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse
// the children and simply mark them as hidden // the children and simply mark them as hidden
args->tree->recurseNodeWithOperation(voxel, hideAllSubTreeOperation, args ); args->tree->recurseNodeWithOperation(voxel, hideAllSubTreeOperation, args );
return false; return false;
} break; } break;
case ViewFrustum::INSIDE: { case ViewFrustum::INSIDE: {
// If this node is INSIDE the current view, then we might want to show it... unless it was previously INSIDE, // If this node is INSIDE the current view, then we might want to show it... unless it was previously INSIDE,
// if it was previously INSIDE, then we can safely assume it's already shown, and we can also safely assume // if it was previously INSIDE, then we can safely assume it's already shown, and we can also safely assume
// that all of it's children are INSIDE both of our views, in which case we can just stop recursing... // that all of it's children are INSIDE both of our views, in which case we can just stop recursing...
@ -2234,12 +2231,10 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse // we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse
// the children and simply mark them as visible (as appropriate based on LOD) // the children and simply mark them as visible (as appropriate based on LOD)
args->tree->recurseNodeWithOperation(voxel, showAllSubTreeOperation, args); args->tree->recurseNodeWithOperation(voxel, showAllSubTreeOperation, args);
return false; return false;
} break; } break;
case ViewFrustum::INTERSECT: { case ViewFrustum::INTERSECT: {
args->nodesScanned++; args->nodesScanned++;
// If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE // If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE
// the last known view, in which case it will already be visible, and we know that all it's children are also // the last known view, in which case it will already be visible, and we know that all it's children are also
// previously INSIDE and visible. So in this case stop recursing // previously INSIDE and visible. So in this case stop recursing
@ -2253,8 +2248,15 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// if the child node INTERSECTs the view, then we want to check to see if it thinks it should render // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render
// if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from // if it should render but is missing it's VBO index, then we want to flip it on, and we can stop recursing from
// here because we know will block any children anyway // here because we know will block any children anyway
float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale();
int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust();
bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, voxelSizeScale, boundaryLevelAdjust);
voxel->setShouldRender(shouldRender);
if (voxel->getShouldRender() && !voxel->isKnownBufferIndex()) { if (voxel->getShouldRender() && !voxel->isKnownBufferIndex()) {
voxel->setDirtyBit(); // will this make it draw? voxel->setDirtyBit(); // will this make it draw?
voxel->markWithChangedTime(); // both are needed to force redraw
args->nodesShown++; args->nodesShown++;
return false; return false;
} }
@ -2267,7 +2269,6 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
} break; } break;
} // switch } // switch
return true; // keep going! return true; // keep going!
} }

View file

@ -62,8 +62,6 @@ const float DISPLAYNAME_ALPHA = 0.95f;
Avatar::Avatar() : Avatar::Avatar() :
AvatarData(), AvatarData(),
_head(this),
_hand(this),
_skeletonModel(this), _skeletonModel(this),
_bodyYawDelta(0.0f), _bodyYawDelta(0.0f),
_mode(AVATAR_MODE_STANDING), _mode(AVATAR_MODE_STANDING),
@ -84,18 +82,16 @@ Avatar::Avatar() :
moveToThread(Application::getInstance()->thread()); moveToThread(Application::getInstance()->thread());
// give the pointer to our head to inherited _headData variable from AvatarData // give the pointer to our head to inherited _headData variable from AvatarData
_headData = &_head; _headData = static_cast<HeadData*>(new Head(this));
_handData = &_hand; _handData = static_cast<HandData*>(new Hand(this));
} }
Avatar::~Avatar() { Avatar::~Avatar() {
_headData = NULL;
_handData = NULL;
} }
void Avatar::init() { void Avatar::init() {
_head.init(); getHead()->init();
_hand.init(); getHand()->init();
_skeletonModel.init(); _skeletonModel.init();
_initialized = true; _initialized = true;
} }
@ -118,16 +114,17 @@ void Avatar::simulate(float deltaTime) {
// copy velocity so we can use it later for acceleration // copy velocity so we can use it later for acceleration
glm::vec3 oldVelocity = getVelocity(); glm::vec3 oldVelocity = getVelocity();
_hand.simulate(deltaTime, false); getHand()->simulate(deltaTime, false);
_skeletonModel.simulate(deltaTime); _skeletonModel.simulate(deltaTime);
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); Head* head = getHead();
head->setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
glm::vec3 headPosition; glm::vec3 headPosition;
if (!_skeletonModel.getHeadPosition(headPosition)) { if (!_skeletonModel.getHeadPosition(headPosition)) {
headPosition = _position; headPosition = _position;
} }
_head.setPosition(headPosition); head->setPosition(headPosition);
_head.setScale(_scale); head->setScale(_scale);
_head.simulate(deltaTime, false); getHead()->simulate(deltaTime, false);
// use speed and angular velocity to determine walking vs. standing // use speed and angular velocity to determine walking vs. standing
if (_speed + fabs(_bodyYawDelta) > 0.2) { if (_speed + fabs(_bodyYawDelta) > 0.2) {
@ -195,11 +192,12 @@ void Avatar::render(bool forceRenderHead) {
Glower glower(_moving && lengthToTarget > GLOW_DISTANCE ? 1.0f : 0.0f); Glower glower(_moving && lengthToTarget > GLOW_DISTANCE ? 1.0f : 0.0f);
// render body // render body
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) {
_skeletonModel.renderCollisionProxies(1.f); _skeletonModel.renderCollisionProxies(0.7f);
//_head.getFaceModel().renderCollisionProxies(0.5f); }
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionProxies)) {
getHead()->getFaceModel().renderCollisionProxies(0.7f);
} }
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
renderBody(forceRenderHead); renderBody(forceRenderHead);
} }
@ -207,7 +205,7 @@ void Avatar::render(bool forceRenderHead) {
// render sphere when far away // render sphere when far away
const float MAX_ANGLE = 10.f; const float MAX_ANGLE = 10.f;
float height = getSkeletonHeight(); float height = getSkeletonHeight();
glm::vec3 delta = height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f; glm::vec3 delta = height * (getHead()->getCameraOrientation() * IDENTITY_UP) / 2.f;
float angle = abs(angleBetween(toTarget + delta, toTarget - delta)); float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
if (angle < MAX_ANGLE) { if (angle < MAX_ANGLE) {
@ -215,7 +213,7 @@ void Avatar::render(bool forceRenderHead) {
glPushMatrix(); glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z); glTranslatef(_position.x, _position.y, _position.z);
glScalef(height / 2.f, height / 2.f, height / 2.f); glScalef(height / 2.f, height / 2.f, height / 2.f);
glutSolidSphere(1.2f + _head.getAverageLoudness() * .0005f, 20, 20); glutSolidSphere(1.2f + getHead()->getAverageLoudness() * .0005f, 20, 20);
glPopMatrix(); glPopMatrix();
} }
} }
@ -231,7 +229,7 @@ void Avatar::render(bool forceRenderHead) {
} }
glPushMatrix(); glPushMatrix();
glm::vec3 chatPosition = getHead().getEyePosition() + getBodyUpDirection() * CHAT_MESSAGE_HEIGHT * _scale; glm::vec3 chatPosition = getHead()->getEyePosition() + getBodyUpDirection() * CHAT_MESSAGE_HEIGHT * _scale;
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z); glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation(); glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
glm::vec3 chatAxis = glm::axis(chatRotation); glm::vec3 chatAxis = glm::axis(chatRotation);
@ -288,9 +286,9 @@ void Avatar::renderBody(bool forceRenderHead) {
//printf("Render other at %.3f, %.2f, %.2f\n", pos.x, pos.y, pos.z); //printf("Render other at %.3f, %.2f, %.2f\n", pos.x, pos.y, pos.z);
_skeletonModel.render(1.0f); _skeletonModel.render(1.0f);
if (forceRenderHead) { if (forceRenderHead) {
_head.render(1.0f); getHead()->render(1.0f);
} }
_hand.render(false); getHand()->render(false);
} }
void Avatar::renderDisplayName() { void Avatar::renderDisplayName() {
@ -386,7 +384,7 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
if (_skeletonModel.findRayIntersection(origin, direction, modelDistance)) { if (_skeletonModel.findRayIntersection(origin, direction, modelDistance)) {
minDistance = qMin(minDistance, modelDistance); minDistance = qMin(minDistance, modelDistance);
} }
if (_head.getFaceModel().findRayIntersection(origin, direction, modelDistance)) { if (getHead()->getFaceModel().findRayIntersection(origin, direction, modelDistance)) {
minDistance = qMin(minDistance, modelDistance); minDistance = qMin(minDistance, modelDistance);
} }
if (minDistance < FLT_MAX) { if (minDistance < FLT_MAX) {
@ -401,7 +399,7 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet
// Temporarily disabling collisions against the skeleton because the collision proxies up // Temporarily disabling collisions against the skeleton because the collision proxies up
// near the neck are bad and prevent the hand from hitting the face. // near the neck are bad and prevent the hand from hitting the face.
//return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions, 1.0f, skeletonSkipIndex); //return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions, 1.0f, skeletonSkipIndex);
return _head.getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
} }
bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
@ -480,7 +478,7 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
void Avatar::setFaceModelURL(const QUrl &faceModelURL) { void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
AvatarData::setFaceModelURL(faceModelURL); AvatarData::setFaceModelURL(faceModelURL);
const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst"); const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst");
_head.getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL); getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL);
} }
void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) { void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) {
@ -605,7 +603,7 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
return false; return false;
//return _skeletonModel.collisionHitsMoveableJoint(collision); //return _skeletonModel.collisionHitsMoveableJoint(collision);
} }
if (model == &(_head.getFaceModel())) { if (model == &(getHead()->getFaceModel())) {
// ATM we always handle MODEL_COLLISIONS against the face. // ATM we always handle MODEL_COLLISIONS against the face.
return true; return true;
} }
@ -618,8 +616,8 @@ void Avatar::applyCollision(CollisionInfo& collision) {
} }
// TODO: make skeleton also respond to collisions // TODO: make skeleton also respond to collisions
Model* model = static_cast<Model*>(collision._data); Model* model = static_cast<Model*>(collision._data);
if (model == &(_head.getFaceModel())) { if (model == &(getHead()->getFaceModel())) {
_head.applyCollision(collision); getHead()->applyCollision(collision);
} }
} }
@ -628,7 +626,7 @@ float Avatar::getPelvisFloatingHeight() const {
} }
float Avatar::getPelvisToHeadLength() const { float Avatar::getPelvisToHeadLength() const {
return glm::distance(_position, _head.getPosition()); return glm::distance(_position, getHead()->getPosition());
} }
void Avatar::setShowDisplayName(bool showDisplayName) { void Avatar::setShowDisplayName(bool showDisplayName) {

View file

@ -74,7 +74,7 @@ public:
void render(bool forceRenderHead); void render(bool forceRenderHead);
//setters //setters
void setDisplayingLookatVectors(bool displayingLookatVectors) { _head.setRenderLookatVectors(displayingLookatVectors); } void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction); void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
//getters //getters
@ -83,8 +83,9 @@ public:
glm::vec3 getChestPosition() const; glm::vec3 getChestPosition() const;
float getScale() const { return _scale; } float getScale() const { return _scale; }
const glm::vec3& getVelocity() const { return _velocity; } const glm::vec3& getVelocity() const { return _velocity; }
Head& getHead() { return _head; } const Head* getHead() const { return static_cast<const Head*>(_headData); }
Hand& getHand() { return _hand; } Head* getHead() { return static_cast<Head*>(_headData); }
Hand* getHand() { return static_cast<Hand*>(_handData); }
glm::quat getWorldAlignedOrientation() const; glm::quat getWorldAlignedOrientation() const;
Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); } Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); }
@ -134,8 +135,6 @@ public slots:
void updateCollisionFlags(); void updateCollisionFlags();
protected: protected:
Head _head;
Hand _hand;
SkeletonModel _skeletonModel; SkeletonModel _skeletonModel;
float _bodyYawDelta; float _bodyYawDelta;
AvatarMode _mode; AvatarMode _mode;

View file

@ -48,9 +48,6 @@ bool FaceModel::render(float alpha) {
if (!Model::render(alpha)) { if (!Model::render(alpha)) {
return false; return false;
} }
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
renderCollisionProxies(alpha);
}
return true; return true;
} }

View file

@ -125,31 +125,16 @@ void Hand::simulate(float deltaTime, bool isMine) {
} }
} }
// We create a static CollisionList that is recycled for each collision test. void Hand::playSlaps(PalmData& palm, Avatar* avatar)
const float MAX_COLLISIONS_PER_AVATAR = 32; {
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
if (!avatar || avatar == _owningAvatar) {
// don't collide with our own hands (that is done elsewhere)
return;
}
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
for (size_t i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
if (!palm.isActive()) {
continue;
}
glm::vec3 totalPenetration;
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
// Check for palm collisions // Check for palm collisions
glm::vec3 myPalmPosition = palm.getPosition(); glm::vec3 myPalmPosition = palm.getPosition();
float palmCollisionDistance = 0.1f; float palmCollisionDistance = 0.1f;
bool wasColliding = palm.getIsCollidingWithPalm(); bool wasColliding = palm.getIsCollidingWithPalm();
palm.setIsCollidingWithPalm(false); palm.setIsCollidingWithPalm(false);
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound // If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) { for (size_t j = 0; j < avatar->getHand()->getNumPalms(); j++) {
PalmData& otherPalm = avatar->getHand().getPalms()[j]; PalmData& otherPalm = avatar->getHand()->getPalms()[j];
if (!otherPalm.isActive()) { if (!otherPalm.isActive()) {
continue; continue;
} }
@ -174,6 +159,27 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
} }
} }
} }
// We create a static CollisionList that is recycled for each collision test.
const float MAX_COLLISIONS_PER_AVATAR = 32;
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
if (!avatar || avatar == _owningAvatar) {
// don't collide with our own hands (that is done elsewhere)
return;
}
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
for (size_t i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i];
if (!palm.isActive()) {
continue;
}
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
playSlaps(palm, avatar);
}
glm::vec3 totalPenetration;
handCollisions.clear(); handCollisions.clear();
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions)) { if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions)) {
for (int j = 0; j < handCollisions.size(); ++j) { for (int j = 0; j < handCollisions.size(); ++j) {
@ -307,7 +313,8 @@ void Hand::render(bool isMine) {
_buckyBalls.render(); _buckyBalls.render();
} }
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) {
// draw a green sphere at hand joint location, which is actually near the wrist)
for (size_t i = 0; i < getNumPalms(); i++) { for (size_t i = 0; i < getNumPalms(); i++) {
PalmData& palm = getPalms()[i]; PalmData& palm = getPalms()[i];
if (!palm.isActive()) { if (!palm.isActive()) {

View file

@ -93,6 +93,8 @@ private:
void calculateGeometry(); void calculateGeometry();
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime); void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
void playSlaps(PalmData& palm, Avatar* avatar);
}; };
#endif #endif

View file

@ -62,24 +62,20 @@ void Head::simulate(float deltaTime, bool isMine) {
// Update audio trailing average for rendering facial animations // Update audio trailing average for rendering facial animations
Faceshift* faceshift = Application::getInstance()->getFaceshift(); Faceshift* faceshift = Application::getInstance()->getFaceshift();
Visage* visage = Application::getInstance()->getVisage();
if (isMine) { if (isMine) {
_isFaceshiftConnected = faceshift->isActive(); _isFaceshiftConnected = false;
if (faceshift->isActive()) {
_blendshapeCoefficients = faceshift->getBlendshapeCoefficients();
_isFaceshiftConnected = true;
} else if (visage->isActive()) {
_blendshapeCoefficients = visage->getBlendshapeCoefficients();
_isFaceshiftConnected = true;
}
} }
if (isMine && faceshift->isActive()) { if (!_isFaceshiftConnected) {
const float EYE_OPEN_SCALE = 0.5f;
_leftEyeBlink = faceshift->getLeftBlink() - EYE_OPEN_SCALE * faceshift->getLeftEyeOpen();
_rightEyeBlink = faceshift->getRightBlink() - EYE_OPEN_SCALE * faceshift->getRightEyeOpen();
// set these values based on how they'll be used. if we use faceshift in the long term, we'll want a complete
// mapping between their blendshape coefficients and our avatar features
const float MOUTH_SIZE_SCALE = 2500.0f;
_averageLoudness = faceshift->getMouthSize() * faceshift->getMouthSize() * MOUTH_SIZE_SCALE;
const float BROW_HEIGHT_SCALE = 0.005f;
_browAudioLift = faceshift->getBrowUpCenter() * BROW_HEIGHT_SCALE;
_blendshapeCoefficients = faceshift->getBlendshapeCoefficients();
} else if (!_isFaceshiftConnected) {
// Update eye saccades // Update eye saccades
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f; const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
const float AVERAGE_SACCADE_INTERVAL = 4.0f; const float AVERAGE_SACCADE_INTERVAL = 4.0f;

View file

@ -53,7 +53,6 @@ MyAvatar::MyAvatar() :
_elapsedTimeSinceCollision(0.0f), _elapsedTimeSinceCollision(0.0f),
_lastCollisionPosition(0, 0, 0), _lastCollisionPosition(0, 0, 0),
_speedBrakes(false), _speedBrakes(false),
_isCollisionsOn(true),
_isThrustOn(false), _isThrustOn(false),
_thrustMultiplier(1.0f), _thrustMultiplier(1.0f),
_moveTarget(0,0,0), _moveTarget(0,0,0),
@ -73,8 +72,8 @@ void MyAvatar::reset() {
// TODO? resurrect headMouse stuff? // TODO? resurrect headMouse stuff?
//_headMouseX = _glWidget->width() / 2; //_headMouseX = _glWidget->width() / 2;
//_headMouseY = _glWidget->height() / 2; //_headMouseY = _glWidget->height() / 2;
_head.reset(); getHead()->reset();
_hand.reset(); getHand()->reset();
setVelocity(glm::vec3(0,0,0)); setVelocity(glm::vec3(0,0,0));
setThrust(glm::vec3(0,0,0)); setThrust(glm::vec3(0,0,0));
@ -131,19 +130,20 @@ void MyAvatar::update(float deltaTime) {
//_headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height()); //_headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height());
} }
Head* head = getHead();
if (OculusManager::isConnected()) { if (OculusManager::isConnected()) {
float yaw, pitch, roll; float yaw, pitch, roll;
OculusManager::getEulerAngles(yaw, pitch, roll); OculusManager::getEulerAngles(yaw, pitch, roll);
_head.setYaw(yaw); head->setYaw(yaw);
_head.setPitch(pitch); head->setPitch(pitch);
_head.setRoll(roll); head->setRoll(roll);
} }
// Get audio loudness data from audio input device // Get audio loudness data from audio input device
Audio* audio = Application::getInstance()->getAudio(); Audio* audio = Application::getInstance()->getAudio();
_head.setAudioLoudness(audio->getLastInputLoudness()); head->setAudioLoudness(audio->getLastInputLoudness());
_head.setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) {
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition())); setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
@ -267,7 +267,7 @@ void MyAvatar::simulate(float deltaTime) {
if (!Application::getInstance()->getFaceshift()->isActive() && OculusManager::isConnected() && if (!Application::getInstance()->getFaceshift()->isActive() && OculusManager::isConnected() &&
fabsf(forwardAcceleration) > OCULUS_ACCELERATION_PULL_THRESHOLD && fabsf(forwardAcceleration) > OCULUS_ACCELERATION_PULL_THRESHOLD &&
fabs(_head.getYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) { fabs(getHead()->getYaw()) > OCULUS_YAW_OFFSET_THRESHOLD) {
// if we're wearing the oculus // if we're wearing the oculus
// and this acceleration is above the pull threshold // and this acceleration is above the pull threshold
@ -277,7 +277,7 @@ void MyAvatar::simulate(float deltaTime) {
_bodyYaw = getAbsoluteHeadYaw(); _bodyYaw = getAbsoluteHeadYaw();
// set the head yaw to zero for this draw // set the head yaw to zero for this draw
_head.setYaw(0); getHead()->setYaw(0);
// correct the oculus yaw offset // correct the oculus yaw offset
OculusManager::updateYawOffset(); OculusManager::updateYawOffset();
@ -315,17 +315,20 @@ void MyAvatar::simulate(float deltaTime) {
_position += _velocity * deltaTime; _position += _velocity * deltaTime;
// update avatar skeleton and simulate hand and head // update avatar skeleton and simulate hand and head
_hand.collideAgainstOurself(); getHand()->collideAgainstOurself();
_hand.simulate(deltaTime, true); getHand()->simulate(deltaTime, true);
_skeletonModel.simulate(deltaTime); _skeletonModel.simulate(deltaTime);
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
Head* head = getHead();
head->setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
glm::vec3 headPosition; glm::vec3 headPosition;
if (!_skeletonModel.getHeadPosition(headPosition)) { if (!_skeletonModel.getHeadPosition(headPosition)) {
headPosition = _position; headPosition = _position;
} }
_head.setPosition(headPosition); head->setPosition(headPosition);
_head.setScale(_scale); head->setScale(_scale);
_head.simulate(deltaTime, true); head->simulate(deltaTime, true);
// Zero thrust out now that we've added it to velocity in this frame // Zero thrust out now that we've added it to velocity in this frame
_thrust = glm::vec3(0, 0, 0); _thrust = glm::vec3(0, 0, 0);
@ -337,22 +340,34 @@ const float MAX_PITCH = 90.0f;
// Update avatar head rotation with sensor data // Update avatar head rotation with sensor data
void MyAvatar::updateFromGyros(float deltaTime) { void MyAvatar::updateFromGyros(float deltaTime) {
Faceshift* faceshift = Application::getInstance()->getFaceshift(); Faceshift* faceshift = Application::getInstance()->getFaceshift();
Visage* visage = Application::getInstance()->getVisage();
glm::vec3 estimatedPosition, estimatedRotation; glm::vec3 estimatedPosition, estimatedRotation;
bool trackerActive = false;
if (faceshift->isActive()) { if (faceshift->isActive()) {
estimatedPosition = faceshift->getHeadTranslation(); estimatedPosition = faceshift->getHeadTranslation();
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation()); estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
trackerActive = true;
} else if (visage->isActive()) {
estimatedPosition = visage->getHeadTranslation();
estimatedRotation = safeEulerAngles(visage->getHeadRotation());
trackerActive = true;
}
Head* head = getHead();
if (trackerActive) {
// Rotate the body if the head is turned beyond the screen // Rotate the body if the head is turned beyond the screen
if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) {
const float FACESHIFT_YAW_TURN_SENSITIVITY = 0.5f; const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f;
const float FACESHIFT_MIN_YAW_TURN = 15.f; const float TRACKER_MIN_YAW_TURN = 15.f;
const float FACESHIFT_MAX_YAW_TURN = 50.f; const float TRACKER_MAX_YAW_TURN = 50.f;
if ( (fabs(estimatedRotation.y) > FACESHIFT_MIN_YAW_TURN) && if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) &&
(fabs(estimatedRotation.y) < FACESHIFT_MAX_YAW_TURN) ) { (fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) {
if (estimatedRotation.y > 0.f) { if (estimatedRotation.y > 0.f) {
_bodyYawDelta += (estimatedRotation.y - FACESHIFT_MIN_YAW_TURN) * FACESHIFT_YAW_TURN_SENSITIVITY; _bodyYawDelta += (estimatedRotation.y - TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY;
} else { } else {
_bodyYawDelta += (estimatedRotation.y + FACESHIFT_MIN_YAW_TURN) * FACESHIFT_YAW_TURN_SENSITIVITY; _bodyYawDelta += (estimatedRotation.y + TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY;
} }
} }
} }
@ -360,10 +375,10 @@ void MyAvatar::updateFromGyros(float deltaTime) {
// restore rotation, lean to neutral positions // restore rotation, lean to neutral positions
const float RESTORE_PERIOD = 1.f; // seconds const float RESTORE_PERIOD = 1.f; // seconds
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f); float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
_head.setYaw(glm::mix(_head.getYaw(), 0.0f, restorePercentage)); head->setYaw(glm::mix(head->getYaw(), 0.0f, restorePercentage));
_head.setRoll(glm::mix(_head.getRoll(), 0.0f, restorePercentage)); head->setRoll(glm::mix(head->getRoll(), 0.0f, restorePercentage));
_head.setLeanSideways(glm::mix(_head.getLeanSideways(), 0.0f, restorePercentage)); head->setLeanSideways(glm::mix(head->getLeanSideways(), 0.0f, restorePercentage));
_head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, restorePercentage)); head->setLeanForward(glm::mix(head->getLeanForward(), 0.0f, restorePercentage));
return; return;
} }
@ -372,17 +387,17 @@ void MyAvatar::updateFromGyros(float deltaTime) {
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f; const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f; const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f; const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
_head.tweakPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY); head->tweakPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
_head.tweakYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY); head->tweakYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
_head.tweakRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY); head->tweakRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
// Update torso lean distance based on accelerometer data // Update torso lean distance based on accelerometer data
const float TORSO_LENGTH = 0.5f; const float TORSO_LENGTH = 0.5f;
glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f); glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f);
const float MAX_LEAN = 45.0f; const float MAX_LEAN = 45.0f;
_head.setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)), head->setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN)); -MAX_LEAN, MAX_LEAN));
_head.setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)), head->setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN)); -MAX_LEAN, MAX_LEAN));
// if Faceshift drive is enabled, set the avatar drive based on the head position // if Faceshift drive is enabled, set the avatar drive based on the head position
@ -391,11 +406,11 @@ void MyAvatar::updateFromGyros(float deltaTime) {
} }
// Move with Lean by applying thrust proportional to leaning // Move with Lean by applying thrust proportional to leaning
glm::quat orientation = _head.getCameraOrientation(); glm::quat orientation = head->getCameraOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT; glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT; glm::vec3 right = orientation * IDENTITY_RIGHT;
float leanForward = _head.getLeanForward(); float leanForward = head->getLeanForward();
float leanSideways = _head.getLeanSideways(); float leanSideways = head->getLeanSideways();
// Degrees of 'dead zone' when leaning, and amount of acceleration to apply to lean angle // Degrees of 'dead zone' when leaning, and amount of acceleration to apply to lean angle
const float LEAN_FWD_DEAD_ZONE = 15.f; const float LEAN_FWD_DEAD_ZONE = 15.f;
@ -426,7 +441,7 @@ static TextRenderer* textRenderer() {
void MyAvatar::renderDebugBodyPoints() { void MyAvatar::renderDebugBodyPoints() {
glm::vec3 torsoPosition(getPosition()); glm::vec3 torsoPosition(getPosition());
glm::vec3 headPosition(getHead().getEyePosition()); glm::vec3 headPosition(getHead()->getEyePosition());
float torsoToHead = glm::length(headPosition - torsoPosition); float torsoToHead = glm::length(headPosition - torsoPosition);
glm::vec3 position; glm::vec3 position;
printf("head-above-torso %.2f, scale = %0.2f\n", torsoToHead, getScale()); printf("head-above-torso %.2f, scale = %0.2f\n", torsoToHead, getScale());
@ -452,7 +467,10 @@ void MyAvatar::renderDebugBodyPoints() {
void MyAvatar::render(bool forceRenderHead) { void MyAvatar::render(bool forceRenderHead) {
// render body // render body
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) {
_skeletonModel.renderCollisionProxies(1.f);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionProxies)) {
_skeletonModel.renderCollisionProxies(1.f); _skeletonModel.renderCollisionProxies(1.f);
} }
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
@ -469,7 +487,7 @@ void MyAvatar::render(bool forceRenderHead) {
} }
glPushMatrix(); glPushMatrix();
glm::vec3 chatPosition = getHead().getEyePosition() + getBodyUpDirection() * CHAT_MESSAGE_HEIGHT * _scale; glm::vec3 chatPosition = getHead()->getEyePosition() + getBodyUpDirection() * CHAT_MESSAGE_HEIGHT * _scale;
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z); glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation(); glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
glm::vec3 chatAxis = glm::axis(chatRotation); glm::vec3 chatAxis = glm::axis(chatRotation);
@ -579,13 +597,13 @@ void MyAvatar::saveData(QSettings* settings) {
settings->setValue("bodyPitch", _bodyPitch); settings->setValue("bodyPitch", _bodyPitch);
settings->setValue("bodyRoll", _bodyRoll); settings->setValue("bodyRoll", _bodyRoll);
settings->setValue("headPitch", _head.getPitch()); settings->setValue("headPitch", getHead()->getPitch());
settings->setValue("position_x", _position.x); settings->setValue("position_x", _position.x);
settings->setValue("position_y", _position.y); settings->setValue("position_y", _position.y);
settings->setValue("position_z", _position.z); settings->setValue("position_z", _position.z);
settings->setValue("pupilDilation", _head.getPupilDilation()); settings->setValue("pupilDilation", getHead()->getPupilDilation());
settings->setValue("leanScale", _leanScale); settings->setValue("leanScale", _leanScale);
settings->setValue("scale", _targetScale); settings->setValue("scale", _targetScale);
@ -605,13 +623,13 @@ void MyAvatar::loadData(QSettings* settings) {
_bodyPitch = loadSetting(settings, "bodyPitch", 0.0f); _bodyPitch = loadSetting(settings, "bodyPitch", 0.0f);
_bodyRoll = loadSetting(settings, "bodyRoll", 0.0f); _bodyRoll = loadSetting(settings, "bodyRoll", 0.0f);
_head.setPitch(loadSetting(settings, "headPitch", 0.0f)); getHead()->setPitch(loadSetting(settings, "headPitch", 0.0f));
_position.x = loadSetting(settings, "position_x", 0.0f); _position.x = loadSetting(settings, "position_x", 0.0f);
_position.y = loadSetting(settings, "position_y", 0.0f); _position.y = loadSetting(settings, "position_y", 0.0f);
_position.z = loadSetting(settings, "position_z", 0.0f); _position.z = loadSetting(settings, "position_z", 0.0f);
_head.setPupilDilation(loadSetting(settings, "pupilDilation", 0.0f)); getHead()->setPupilDilation(loadSetting(settings, "pupilDilation", 0.0f));
_leanScale = loadSetting(settings, "leanScale", 0.05f); _leanScale = loadSetting(settings, "leanScale", 0.05f);
_targetScale = loadSetting(settings, "scale", 1.0f); _targetScale = loadSetting(settings, "scale", 1.0f);
@ -647,9 +665,9 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
setOrientation(orientation); setOrientation(orientation);
// then vertically // then vertically
float oldPitch = _head.getPitch(); float oldPitch = getHead()->getPitch();
_head.setPitch(oldPitch + deltaY * -ANGULAR_SCALE); getHead()->setPitch(oldPitch + deltaY * -ANGULAR_SCALE);
rotation = glm::angleAxis(_head.getPitch() - oldPitch, orientation * IDENTITY_RIGHT); rotation = glm::angleAxis(getHead()->getPitch() - oldPitch, orientation * IDENTITY_RIGHT);
setPosition(position + rotation * (getPosition() - position)); setPosition(position + rotation * (getPosition() - position));
} }
@ -669,8 +687,8 @@ void MyAvatar::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
float distance; float distance;
if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) { if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) {
// rescale to compensate for head embiggening // rescale to compensate for head embiggening
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * eyePosition = (avatar->getHead()->calculateAverageEyePosition() - avatar->getHead()->getScalePivot()) *
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); (avatar->getScale() / avatar->getHead()->getScale()) + avatar->getHead()->getScalePivot();
_lookAtTargetAvatar = avatarPointer; _lookAtTargetAvatar = avatarPointer;
return; return;
} else { } else {
@ -686,7 +704,8 @@ void MyAvatar::clearLookAtTargetAvatar() {
} }
float MyAvatar::getAbsoluteHeadYaw() const { float MyAvatar::getAbsoluteHeadYaw() const {
return glm::yaw(_head.getOrientation()); const Head* head = static_cast<const Head*>(_headData);
return glm::yaw(head->getOrientation());
} }
glm::vec3 MyAvatar::getUprightHeadPosition() const { glm::vec3 MyAvatar::getUprightHeadPosition() const {
@ -700,17 +719,17 @@ void MyAvatar::renderBody(bool forceRenderHead) {
// Render head so long as the camera isn't inside it // Render head so long as the camera isn't inside it
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.10f; const float RENDER_HEAD_CUTOFF_DISTANCE = 0.10f;
Camera* myCamera = Application::getInstance()->getCamera(); Camera* myCamera = Application::getInstance()->getCamera();
if (forceRenderHead || (glm::length(myCamera->getPosition() - _head.calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE)) { if (forceRenderHead || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE)) {
_head.render(1.0f); getHead()->render(1.0f);
} }
_hand.render(true); getHand()->render(true);
} }
void MyAvatar::updateThrust(float deltaTime) { void MyAvatar::updateThrust(float deltaTime) {
// //
// Gather thrust information from keyboard and sensors to apply to avatar motion // Gather thrust information from keyboard and sensors to apply to avatar motion
// //
glm::quat orientation = getHead().getCameraOrientation(); glm::quat orientation = getHead()->getCameraOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT; glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT; glm::vec3 right = orientation * IDENTITY_RIGHT;
glm::vec3 up = orientation * IDENTITY_UP; glm::vec3 up = orientation * IDENTITY_UP;
@ -731,7 +750,7 @@ void MyAvatar::updateThrust(float deltaTime) {
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up; _thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime; _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime;
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime; _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime;
_head.setPitch(_head.getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime); getHead()->setPitch(getHead()->getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime);
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds // If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
@ -996,10 +1015,10 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
} }
// collide our hands against them // collide our hands against them
_hand.collideAgainstAvatar(avatar, true); getHand()->collideAgainstAvatar(avatar, true);
// collide their hands against us // collide their hands against us
avatar->getHand().collideAgainstAvatar(this, false); avatar->getHand()->collideAgainstAvatar(this, false);
} }
} }
} }
@ -1114,7 +1133,7 @@ void MyAvatar::updateChatCircle(float deltaTime) {
void MyAvatar::setGravity(glm::vec3 gravity) { void MyAvatar::setGravity(glm::vec3 gravity) {
_gravity = gravity; _gravity = gravity;
_head.setGravity(_gravity); getHead()->setGravity(_gravity);
// use the gravity to determine the new world up direction, if possible // use the gravity to determine the new world up direction, if possible
float gravityLength = glm::length(gravity); float gravityLength = glm::length(gravity);

View file

@ -86,7 +86,6 @@ public:
public slots: public slots:
void goHome(); void goHome();
void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; }
void increaseSize(); void increaseSize();
void decreaseSize(); void decreaseSize();
void resetSize(); void resetSize();
@ -110,7 +109,6 @@ private:
float _elapsedTimeSinceCollision; float _elapsedTimeSinceCollision;
glm::vec3 _lastCollisionPosition; glm::vec3 _lastCollisionPosition;
bool _speedBrakes; bool _speedBrakes;
bool _isCollisionsOn;
bool _isThrustOn; bool _isThrustOn;
float _thrustMultiplier; float _thrustMultiplier;
glm::vec3 _moveTarget; glm::vec3 _moveTarget;

View file

@ -8,10 +8,9 @@
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include <HandData.h>
#include "Application.h" #include "Application.h"
#include "Avatar.h" #include "Avatar.h"
#include "Hand.h"
#include "Menu.h" #include "Menu.h"
#include "SkeletonModel.h" #include "SkeletonModel.h"
@ -33,8 +32,8 @@ void SkeletonModel::simulate(float deltaTime) {
// find the left and rightmost active Leap palms // find the left and rightmost active Leap palms
int leftPalmIndex, rightPalmIndex; int leftPalmIndex, rightPalmIndex;
HandData& hand = _owningAvatar->getHand(); Hand* hand = _owningAvatar->getHand();
hand.getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
const float HAND_RESTORATION_PERIOD = 1.f; // seconds const float HAND_RESTORATION_PERIOD = 1.f; // seconds
float handRestorePercent = glm::clamp(deltaTime / HAND_RESTORATION_PERIOD, 0.f, 1.f); float handRestorePercent = glm::clamp(deltaTime / HAND_RESTORATION_PERIOD, 0.f, 1.f);
@ -52,14 +51,14 @@ void SkeletonModel::simulate(float deltaTime) {
} else if (leftPalmIndex == rightPalmIndex) { } else if (leftPalmIndex == rightPalmIndex) {
// right hand only // right hand only
applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices,
hand.getPalms()[leftPalmIndex]); hand->getPalms()[leftPalmIndex]);
restoreLeftHandPosition(handRestorePercent); restoreLeftHandPosition(handRestorePercent);
} else { } else {
applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices,
hand.getPalms()[leftPalmIndex]); hand->getPalms()[leftPalmIndex]);
applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices,
hand.getPalms()[rightPalmIndex]); hand->getPalms()[rightPalmIndex]);
} }
} }
@ -182,8 +181,8 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
glm::mat3 axes = glm::mat3_cast(_rotation); glm::mat3 axes = glm::mat3_cast(_rotation);
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) * glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation))); joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
state.rotation = glm::angleAxis(-_owningAvatar->getHead().getLeanSideways(), glm::normalize(inverse * axes[2])) * state.rotation = glm::angleAxis(-_owningAvatar->getHead()->getLeanSideways(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(-_owningAvatar->getHead().getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation; glm::angleAxis(-_owningAvatar->getHead()->getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation;
} }
void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) { void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) {

View file

@ -45,7 +45,7 @@ void SixenseManager::update(float deltaTime) {
return; return;
} }
MyAvatar* avatar = Application::getInstance()->getAvatar(); MyAvatar* avatar = Application::getInstance()->getAvatar();
Hand& hand = avatar->getHand(); Hand* hand = avatar->getHand();
int maxControllers = sixenseGetMaxControllers(); int maxControllers = sixenseGetMaxControllers();
for (int i = 0; i < maxControllers; i++) { for (int i = 0; i < maxControllers; i++) {
@ -60,16 +60,16 @@ void SixenseManager::update(float deltaTime) {
// Either find a palm matching the sixense controller, or make a new one // Either find a palm matching the sixense controller, or make a new one
PalmData* palm; PalmData* palm;
bool foundHand = false; bool foundHand = false;
for (int j = 0; j < hand.getNumPalms(); j++) { for (int j = 0; j < hand->getNumPalms(); j++) {
if (hand.getPalms()[j].getSixenseID() == data.controller_index) { if (hand->getPalms()[j].getSixenseID() == data.controller_index) {
palm = &hand.getPalms()[j]; palm = &(hand->getPalms()[j]);
foundHand = true; foundHand = true;
} }
} }
if (!foundHand) { if (!foundHand) {
PalmData newPalm(&hand); PalmData newPalm(hand);
hand.getPalms().push_back(newPalm); hand->getPalms().push_back(newPalm);
palm = &hand.getPalms()[hand.getNumPalms() - 1]; palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
palm->setSixenseID(data.controller_index); palm->setSixenseID(data.controller_index);
printf("Found new Sixense controller, ID %i\n", data.controller_index); printf("Found new Sixense controller, ID %i\n", data.controller_index);
} }
@ -107,7 +107,7 @@ void SixenseManager::update(float deltaTime) {
} }
// initialize the "finger" based on the direction // initialize the "finger" based on the direction
FingerData finger(palm, &hand); FingerData finger(palm, hand);
finger.setActive(true); finger.setActive(true);
finger.setRawRootPosition(position); finger.setRawRootPosition(position);
const float FINGER_LENGTH = 300.0f; // Millimeters const float FINGER_LENGTH = 300.0f; // Millimeters
@ -130,7 +130,7 @@ void SixenseManager::update(float deltaTime) {
// if the controllers haven't been moved in a while, disable // if the controllers haven't been moved in a while, disable
const int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; const int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000;
if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) { if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) {
for (vector<PalmData>::iterator it = hand.getPalms().begin(); it != hand.getPalms().end(); it++) { for (vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
it->setActive(false); it->setActive(false);
} }
} }

View file

@ -0,0 +1,166 @@
//
// Visage.cpp
// interface
//
// Created by Andrzej Kapolka on 2/11/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#include <QHash>
#include <SharedUtil.h>
#ifdef HAVE_VISAGE
#include <VisageTracker2.h>
#endif
#include "Visage.h"
#include "renderer/FBXReader.h"
namespace VisageSDK {
#ifdef WIN32
void __declspec(dllimport) initializeLicenseManager(char* licenseKeyFileName);
#else
void initializeLicenseManager(char* licenseKeyFileName);
#endif
}
using namespace VisageSDK;
const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f);
Visage::Visage() :
#ifdef HAVE_VISAGE
_leftInnerBrowIndex(0),
_rightInnerBrowIndex(0),
#endif
_active(false),
_headOrigin(DEFAULT_HEAD_ORIGIN),
_estimatedEyePitch(0.0f),
_estimatedEyeYaw(0.0f) {
#ifdef HAVE_VISAGE
switchToResourcesParentIfRequired();
QByteArray licensePath = "resources/visage/license.vlc";
initializeLicenseManager(licensePath.data());
_tracker = new VisageTracker2("resources/visage/Facial Features Tracker - Asymmetric.cfg");
if (_tracker->trackFromCam()) {
_data = new FaceData();
} else {
delete _tracker;
_tracker = NULL;
}
#endif
}
Visage::~Visage() {
#ifdef HAVE_VISAGE
if (_tracker) {
_tracker->stop();
delete _tracker;
delete _data;
}
#endif
}
#ifdef HAVE_VISAGE
static int leftEyeBlinkIndex = 0;
static int rightEyeBlinkIndex = 1;
static int centerBrowIndex = 16;
static QHash<QByteArray, int> createBlendshapeIndices() {
QHash<QByteArray, QByteArray> blendshapeMap;
blendshapeMap.insert("Sneer", "au_nose_wrinkler");
blendshapeMap.insert("JawFwd", "au_jaw_z_push");
blendshapeMap.insert("JawLeft", "au_jaw_x_push");
blendshapeMap.insert("JawOpen", "au_jaw_drop");
blendshapeMap.insert("LipsLowerDown", "au_lower_lip_drop");
blendshapeMap.insert("LipsUpperUp", "au_upper_lip_raiser");
blendshapeMap.insert("LipsStretch_R", "au_lip_stretcher_left");
blendshapeMap.insert("BrowsU_R", "au_left_outer_brow_raiser");
blendshapeMap.insert("BrowsD_R", "au_left_brow_lowerer");
blendshapeMap.insert("LipsStretch_L", "au_lip_stretcher_right");
blendshapeMap.insert("BrowsU_L", "au_right_outer_brow_raiser");
blendshapeMap.insert("BrowsD_L", "au_right_brow_lowerer");
QHash<QByteArray, int> blendshapeIndices;
for (int i = 0;; i++) {
QByteArray blendshape = FACESHIFT_BLENDSHAPES[i];
if (blendshape.isEmpty()) {
break;
}
if (blendshape == "EyeBlink_L") {
leftEyeBlinkIndex = i;
} else if (blendshape == "EyeBlink_R") {
rightEyeBlinkIndex = i;
} else if (blendshape == "BrowsU_C") {
centerBrowIndex = i;
}
QByteArray mapping = blendshapeMap.value(blendshape);
if (!mapping.isEmpty()) {
blendshapeIndices.insert(mapping, i + 1);
}
}
return blendshapeIndices;
}
static const QHash<QByteArray, int>& getBlendshapeIndices() {
static QHash<QByteArray, int> blendshapeIndices = createBlendshapeIndices();
return blendshapeIndices;
}
#endif
const float TRANSLATION_SCALE = 20.0f;
void Visage::update() {
#ifdef HAVE_VISAGE
_active = (_tracker && _tracker->getTrackingData(_data) == TRACK_STAT_OK);
if (!_active) {
return;
}
_headRotation = glm::quat(glm::vec3(-_data->faceRotation[0], -_data->faceRotation[1], _data->faceRotation[2]));
_headTranslation = (glm::vec3(_data->faceTranslation[0], _data->faceTranslation[1], _data->faceTranslation[2]) -
_headOrigin) * TRANSLATION_SCALE;
_estimatedEyePitch = glm::degrees(-_data->gazeDirection[1]);
_estimatedEyeYaw = glm::degrees(-_data->gazeDirection[0]);
if (_blendshapeIndices.isEmpty()) {
_blendshapeIndices.resize(_data->actionUnitCount);
int maxIndex = -1;
for (int i = 0; i < _data->actionUnitCount; i++) {
QByteArray name = _data->actionUnitsNames[i];
if (name == "au_left_inner_brow_raiser") {
_leftInnerBrowIndex = i;
} else if (name == "au_right_inner_brow_raiser") {
_rightInnerBrowIndex = i;
}
int index = getBlendshapeIndices().value(name) - 1;
maxIndex = qMax(maxIndex, _blendshapeIndices[i] = index);
}
_blendshapeCoefficients.resize(maxIndex + 1);
}
qFill(_blendshapeCoefficients.begin(), _blendshapeCoefficients.end(), 0.0f);
for (int i = 0; i < _data->actionUnitCount; i++) {
if (!_data->actionUnitsUsed[i]) {
continue;
}
int index = _blendshapeIndices.at(i);
if (index != -1) {
_blendshapeCoefficients[index] = _data->actionUnits[i];
}
}
_blendshapeCoefficients[leftEyeBlinkIndex] = 1.0f - _data->eyeClosure[1];
_blendshapeCoefficients[rightEyeBlinkIndex] = 1.0f - _data->eyeClosure[0];
_blendshapeCoefficients[centerBrowIndex] = (_data->actionUnits[_leftInnerBrowIndex] +
_data->actionUnits[_rightInnerBrowIndex]) * 0.5f;
#endif
}
void Visage::reset() {
_headOrigin += _headTranslation / TRANSLATION_SCALE;
}

View file

@ -0,0 +1,66 @@
//
// Visage.h
// interface
//
// Created by Andrzej Kapolka on 2/11/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__Visage__
#define __interface__Visage__
#include <vector>
#include <QVector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
namespace VisageSDK {
class VisageTracker2;
struct FaceData;
}
/// Handles input from the Visage webcam feature tracking software.
class Visage {
public:
Visage();
~Visage();
bool isActive() const { return _active; }
const glm::quat& getHeadRotation() const { return _headRotation; }
const glm::vec3& getHeadTranslation() const { return _headTranslation; }
float getEstimatedEyePitch() const { return _estimatedEyePitch; }
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
void update();
void reset();
private:
#ifdef HAVE_VISAGE
VisageSDK::VisageTracker2* _tracker;
VisageSDK::FaceData* _data;
int _leftInnerBrowIndex;
int _rightInnerBrowIndex;
QVector<int> _blendshapeIndices;
#endif
bool _active;
glm::quat _headRotation;
glm::vec3 _headTranslation;
glm::vec3 _headOrigin;
float _estimatedEyePitch;
float _estimatedEyeYaw;
std::vector<float> _blendshapeCoefficients;
};
#endif /* defined(__interface__Visage__) */

View file

@ -1405,6 +1405,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
cluster.jointIndex = 0; cluster.jointIndex = 0;
} }
extracted.mesh.clusters.append(cluster); extracted.mesh.clusters.append(cluster);
// BUG: joints that fall into this context do not get their bindTransform and
// inverseBindRotation data members properly set. This causes bad boneRadius
// and boneLength calculations for collision proxies. Affected joints are usually:
// hair, teeth, tongue. I tried to figure out how to fix this but was going
// crosseyed trying to understand FBX so I gave up for the time being -- Andrew.
} }
// whether we're skinned depends on how many clusters are attached // whether we're skinned depends on how many clusters are attached

View file

@ -21,6 +21,9 @@ class FBXNode;
typedef QList<FBXNode> FBXNodeList; typedef QList<FBXNode> FBXNodeList;
/// The names of the blendshapes expected by Faceshift, terminated with an empty string.
extern const char* FACESHIFT_BLENDSHAPES[];
/// A node within an FBX document. /// A node within an FBX document.
class FBXNode { class FBXNode {
public: public:

View file

@ -421,6 +421,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback) {
_dilatedTextures.clear(); _dilatedTextures.clear();
_lodHysteresis = NetworkGeometry::NO_HYSTERESIS; _lodHysteresis = NetworkGeometry::NO_HYSTERESIS;
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
_baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback); _baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback);
} }

View file

@ -227,7 +227,7 @@ private:
void deleteGeometry(); void deleteGeometry();
void renderMeshes(float alpha, bool translucent); void renderMeshes(float alpha, bool translucent);
QSharedPointer<NetworkGeometry> _baseGeometry; QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
float _lodHysteresis; float _lodHysteresis;
float _pupilDilation; float _pupilDilation;

View file

@ -64,7 +64,7 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar)
QImage shot = widget->grabFrameBuffer(); QImage shot = widget->grabFrameBuffer();
glm::vec3 location = avatar->getPosition(); glm::vec3 location = avatar->getPosition();
glm::quat orientation = avatar->getHead().getOrientation(); glm::quat orientation = avatar->getHead()->getOrientation();
// add metadata // add metadata
shot.setText(LOCATION_X, QString::number(location.x)); shot.setText(LOCATION_X, QString::number(location.x));

View file

@ -66,10 +66,6 @@ QByteArray AvatarData::toByteArray() {
if (!_headData) { if (!_headData) {
_headData = new HeadData(this); _headData = new HeadData(this);
} }
// lazily allocate memory for HandData in case we're not an Avatar instance
if (!_handData) {
_handData = new HandData(this);
}
QByteArray avatarDataByteArray; QByteArray avatarDataByteArray;
avatarDataByteArray.resize(MAX_PACKET_SIZE); avatarDataByteArray.resize(MAX_PACKET_SIZE);
@ -155,8 +151,8 @@ QByteArray AvatarData::toByteArray() {
// pupil dilation // pupil dilation
destinationBuffer += packFloatToByte(destinationBuffer, _headData->_pupilDilation, 1.0f); destinationBuffer += packFloatToByte(destinationBuffer, _headData->_pupilDilation, 1.0f);
// leap hand data // hand data
destinationBuffer += _handData->encodeRemoteData(destinationBuffer); destinationBuffer += HandData::encodeData(_handData, destinationBuffer);
return avatarDataByteArray.left(destinationBuffer - startPosition); return avatarDataByteArray.left(destinationBuffer - startPosition);
} }
@ -262,7 +258,7 @@ int AvatarData::parseData(const QByteArray& packet) {
// pupil dilation // pupil dilation
sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f);
// leap hand data // hand data
if (sourceBuffer - startPosition < packet.size()) { if (sourceBuffer - startPosition < packet.size()) {
// check passed, bytes match // check passed, bytes match
sourceBuffer += _handData->decodeRemoteData(packet.mid(sourceBuffer - startPosition)); sourceBuffer += _handData->decodeRemoteData(packet.mid(sourceBuffer - startPosition));

View file

@ -139,9 +139,6 @@ public:
const HeadData* getHeadData() const { return _headData; } const HeadData* getHeadData() const { return _headData; }
const HandData* getHandData() const { return _handData; } const HandData* getHandData() const { return _handData; }
void setHeadData(HeadData* headData) { _headData = headData; }
void setHandData(HandData* handData) { _handData = handData; }
virtual const glm::vec3& getVelocity() const { return vec3Zero; } virtual const glm::vec3& getVelocity() const { return vec3Zero; }
virtual bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { virtual bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {

View file

@ -113,17 +113,30 @@ _owningHandData(owningHandData)
setTrailLength(standardTrailLength); setTrailLength(standardTrailLength);
} }
// static
int HandData::encodeData(HandData* hand, unsigned char* destinationBuffer) {
if (hand) {
return hand->encodeRemoteData(destinationBuffer);
}
// else encode empty data:
// One byte for zero hands
// One byte for error checking.
*destinationBuffer = 0;
*(destinationBuffer + 1) = 1;
return 2;
}
int HandData::encodeRemoteData(unsigned char* destinationBuffer) { int HandData::encodeRemoteData(unsigned char* destinationBuffer) {
const unsigned char* startPosition = destinationBuffer; const unsigned char* startPosition = destinationBuffer;
unsigned int numHands = 0; unsigned int numPalms = 0;
for (unsigned int handIndex = 0; handIndex < getNumPalms(); ++handIndex) { for (unsigned int handIndex = 0; handIndex < getNumPalms(); ++handIndex) {
PalmData& palm = getPalms()[handIndex]; PalmData& palm = getPalms()[handIndex];
if (palm.isActive()) { if (palm.isActive()) {
numHands++; numPalms++;
} }
} }
*destinationBuffer++ = numHands; *destinationBuffer++ = numPalms;
for (unsigned int handIndex = 0; handIndex < getNumPalms(); ++handIndex) { for (unsigned int handIndex = 0; handIndex < getNumPalms(); ++handIndex) {
PalmData& palm = getPalms()[handIndex]; PalmData& palm = getPalms()[handIndex];
@ -162,9 +175,9 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) {
int HandData::decodeRemoteData(const QByteArray& dataByteArray) { int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
const unsigned char* startPosition; const unsigned char* startPosition;
const unsigned char* sourceBuffer = startPosition = reinterpret_cast<const unsigned char*>(dataByteArray.data()); const unsigned char* sourceBuffer = startPosition = reinterpret_cast<const unsigned char*>(dataByteArray.data());
unsigned int numHands = *sourceBuffer++; unsigned int numPalms = *sourceBuffer++;
for (unsigned int handIndex = 0; handIndex < numHands; ++handIndex) { for (unsigned int handIndex = 0; handIndex < numPalms; ++handIndex) {
if (handIndex >= getNumPalms()) if (handIndex >= getNumPalms())
addNewPalm(); addNewPalm();
PalmData& palm = getPalms()[handIndex]; PalmData& palm = getPalms()[handIndex];
@ -203,7 +216,7 @@ int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
} }
} }
// Turn off any hands which weren't used. // Turn off any hands which weren't used.
for (unsigned int handIndex = numHands; handIndex < getNumPalms(); ++handIndex) { for (unsigned int handIndex = numPalms; handIndex < getNumPalms(); ++handIndex) {
PalmData& palm = getPalms()[handIndex]; PalmData& palm = getPalms()[handIndex];
palm.setActive(false); palm.setActive(false);
} }

View file

@ -71,6 +71,8 @@ public:
void setFingerTrailLength(unsigned int length); void setFingerTrailLength(unsigned int length);
void updateFingerTrails(); void updateFingerTrails();
static int encodeData(HandData* hand, unsigned char* destinationBuffer);
// Use these for sending and receiving hand data // Use these for sending and receiving hand data
int encodeRemoteData(unsigned char* destinationBuffer); int encodeRemoteData(unsigned char* destinationBuffer);
int decodeRemoteData(const QByteArray& dataByteArray); int decodeRemoteData(const QByteArray& dataByteArray);

View file

@ -52,6 +52,10 @@ public slots:
virtual void captureWheelEvents() = 0; virtual void captureWheelEvents() = 0;
virtual void releaseWheelEvents() = 0; virtual void releaseWheelEvents() = 0;
virtual void captureJoystick(int joystickIndex) = 0;
virtual void releaseJoystick(int joystickIndex) = 0;
virtual glm::vec2 getViewportDimensions() const = 0;
signals: signals:
void keyPressEvent(const KeyEvent& event); void keyPressEvent(const KeyEvent& event);

View file

@ -102,6 +102,8 @@ KeyEvent::KeyEvent(const QKeyEvent& event) {
text = "END"; text = "END";
} else if (key == Qt::Key_Help) { } else if (key == Qt::Key_Help) {
text = "HELP"; text = "HELP";
} else if (key == Qt::Key_CapsLock) {
text = "CAPS LOCK";
} }
} }
@ -208,6 +210,8 @@ void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event) {
event.key = Qt::Key_End; event.key = Qt::Key_End;
} else if (event.text.toUpper() == "HELP") { } else if (event.text.toUpper() == "HELP") {
event.key = Qt::Key_Help; event.key = Qt::Key_Help;
} else if (event.text.toUpper() == "CAPS LOCK") {
event.key = Qt::Key_CapsLock;
} else { } else {
event.key = event.text.at(0).unicode(); event.key = event.text.at(0).unicode();
} }
@ -258,10 +262,17 @@ MouseEvent::MouseEvent() :
}; };
MouseEvent::MouseEvent(const QMouseEvent& event) { MouseEvent::MouseEvent(const QMouseEvent& event) :
x = event.x(); x(event.x()),
y = event.y(); y(event.y()),
isLeftButton(event.buttons().testFlag(Qt::LeftButton)),
isRightButton(event.buttons().testFlag(Qt::RightButton)),
isMiddleButton(event.buttons().testFlag(Qt::MiddleButton)),
isShifted(event.modifiers().testFlag(Qt::ShiftModifier)),
isControl(event.modifiers().testFlag(Qt::ControlModifier)),
isMeta(event.modifiers().testFlag(Qt::MetaModifier)),
isAlt(event.modifiers().testFlag(Qt::AltModifier))
{
// single button that caused the event // single button that caused the event
switch (event.button()) { switch (event.button()) {
case Qt::LeftButton: case Qt::LeftButton:
@ -280,16 +291,6 @@ MouseEvent::MouseEvent(const QMouseEvent& event) {
button = "NONE"; button = "NONE";
break; break;
} }
// button pressed state
isLeftButton = isLeftButton || (event.buttons().testFlag(Qt::LeftButton));
isRightButton = isRightButton || (event.buttons().testFlag(Qt::RightButton));
isMiddleButton = isMiddleButton || (event.buttons().testFlag(Qt::MiddleButton));
// keyboard modifiers
isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
isMeta = event.modifiers().testFlag(Qt::MetaModifier);
isControl = event.modifiers().testFlag(Qt::ControlModifier);
isAlt = event.modifiers().testFlag(Qt::AltModifier);
} }
QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) { QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) {