mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 09:13:38 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
e800c80d61
55 changed files with 2373 additions and 617 deletions
.gitignore
assignment-client/src/metavoxels
cmake/modules
examples
interface
CMakeLists.txt
external/visage
resources
src
Application.cppApplication.hAudio.cppCamera.cppCamera.hClipboardScriptingInterface.cppClipboardScriptingInterface.hControllerScriptingInterface.cppControllerScriptingInterface.hImportDialog.cppImportDialog.hMenu.cppMenu.hVoxelImporter.cppVoxelImporter.hVoxelSystem.cpp
avatar
Avatar.cppAvatar.hAvatarManager.cppFaceModel.cppHand.cppHand.hHead.cppMyAvatar.cppMyAvatar.hProfile.hSkeletonModel.cpp
devices
renderer
ui
libraries
avatars/src
script-engine/src
shared/src
voxels/src
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -39,5 +39,11 @@ interface/external/Leap/util/
|
|||
interface/external/Sixense/include/
|
||||
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
|
||||
interface/interfaceCache/
|
||||
|
|
|
@ -37,6 +37,9 @@ const char METAVOXEL_SERVER_LOGGING_NAME[] = "metavoxel-server";
|
|||
void MetavoxelServer::run() {
|
||||
commonInit(METAVOXEL_SERVER_LOGGING_NAME, NodeType::MetavoxelServer);
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||
|
||||
_lastSend = QDateTime::currentMSecsSinceEpoch();
|
||||
_sendTimer.start(SEND_INTERVAL);
|
||||
}
|
||||
|
|
76
cmake/modules/FindVisage.cmake
Normal file
76
cmake/modules/FindVisage.cmake
Normal 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)
|
151
examples/clipboardExample.js
Normal file
151
examples/clipboardExample.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
//
|
||||
// clipboardExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Clipboard class
|
||||
//
|
||||
//
|
||||
|
||||
var selectedVoxel = { x: 0, y: 0, z: 0, s: 0 };
|
||||
var selectedSize = 4;
|
||||
|
||||
function printKeyEvent(eventName, event) {
|
||||
print(eventName);
|
||||
print(" event.key=" + event.key);
|
||||
print(" event.text=" + event.text);
|
||||
print(" event.isShifted=" + event.isShifted);
|
||||
print(" event.isControl=" + event.isControl);
|
||||
print(" event.isMeta=" + event.isMeta);
|
||||
print(" event.isAlt=" + event.isAlt);
|
||||
print(" event.isKeypad=" + event.isKeypad);
|
||||
}
|
||||
|
||||
|
||||
function keyPressEvent(event) {
|
||||
var debug = false;
|
||||
if (debug) {
|
||||
printKeyEvent("keyPressEvent", event);
|
||||
}
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
var debug = false;
|
||||
if (debug) {
|
||||
printKeyEvent("keyReleaseEvent", event);
|
||||
}
|
||||
|
||||
// Note: this sample uses Alt+ as the key codes for these clipboard items
|
||||
if ((event.key == 199 || event.key == 67 || event.text == "C" || event.text == "c") && event.isAlt) {
|
||||
print("the Alt+C key was pressed");
|
||||
Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if ((event.key == 8776 || event.key == 88 || event.text == "X" || event.text == "x") && event.isAlt) {
|
||||
print("the Alt+X key was pressed");
|
||||
Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if ((event.key == 8730 || event.key == 86 || event.text == "V" || event.text == "v") && event.isAlt) {
|
||||
print("the Alt+V key was pressed");
|
||||
Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if (event.text == "DELETE" || event.text == "BACKSPACE") {
|
||||
print("the DELETE/BACKSPACE key was pressed");
|
||||
Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
|
||||
if ((event.text == "E" || event.text == "e") && event.isMeta) {
|
||||
print("the Ctl+E key was pressed");
|
||||
Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
||||
}
|
||||
if ((event.text == "I" || event.text == "i") && event.isMeta) {
|
||||
print("the Ctl+I key was pressed");
|
||||
Clipboard.importVoxels();
|
||||
}
|
||||
if ((event.key == 78 || event.text == "N" || event.text == "n") && event.isMeta) {
|
||||
print("the Ctl+N key was pressed, nudging to left 1 meter");
|
||||
Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
var selectCube = Overlays.addOverlay("cube", {
|
||||
position: { x: 0, y: 0, z: 0},
|
||||
size: selectedSize,
|
||||
color: { red: 255, green: 255, blue: 0},
|
||||
alpha: 1,
|
||||
solid: false,
|
||||
visible: false,
|
||||
lineWidth: 4
|
||||
});
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
var debug = false;
|
||||
if (debug) {
|
||||
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
|
||||
print("called Camera.computePickRay()");
|
||||
print("computePickRay origin=" + pickRay.origin.x + ", " + pickRay.origin.y + ", " + pickRay.origin.z);
|
||||
print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z);
|
||||
}
|
||||
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
|
||||
if (intersection.intersects) {
|
||||
if (debug) {
|
||||
print("intersection voxel.red/green/blue=" + intersection.voxel.red + ", "
|
||||
+ intersection.voxel.green + ", " + intersection.voxel.blue);
|
||||
print("intersection voxel.x/y/z/s=" + intersection.voxel.x + ", "
|
||||
+ intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s);
|
||||
print("intersection face=" + intersection.face);
|
||||
print("intersection distance=" + intersection.distance);
|
||||
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
|
||||
+ intersection.intersection.y + ", " + intersection.intersection.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var x = Math.floor(intersection.voxel.x / selectedSize) * selectedSize;
|
||||
var y = Math.floor(intersection.voxel.y / selectedSize) * selectedSize;
|
||||
var z = Math.floor(intersection.voxel.z / selectedSize) * selectedSize;
|
||||
selectedVoxel = { x: x, y: y, z: z, s: selectedSize };
|
||||
Overlays.editOverlay(selectCube, { position: selectedVoxel, size: selectedSize, visible: true } );
|
||||
} else {
|
||||
Overlays.editOverlay(selectCube, { visible: false } );
|
||||
selectedVoxel = { x: 0, y: 0, z: 0, s: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
||||
function wheelEvent(event) {
|
||||
var debug = false;
|
||||
if (debug) {
|
||||
print("wheelEvent");
|
||||
print(" event.x,y=" + event.x + ", " + event.y);
|
||||
print(" event.delta=" + event.delta);
|
||||
print(" event.orientation=" + event.orientation);
|
||||
print(" event.isLeftButton=" + event.isLeftButton);
|
||||
print(" event.isRightButton=" + event.isRightButton);
|
||||
print(" event.isMiddleButton=" + event.isMiddleButton);
|
||||
print(" event.isShifted=" + event.isShifted);
|
||||
print(" event.isControl=" + event.isControl);
|
||||
print(" event.isMeta=" + event.isMeta);
|
||||
print(" event.isAlt=" + event.isAlt);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.wheelEvent.connect(wheelEvent);
|
||||
|
||||
function scriptEnding() {
|
||||
Overlays.deleteOverlay(selectCube);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -16,14 +16,7 @@
|
|||
// Click and drag to create more new voxels in the same direction
|
||||
//
|
||||
|
||||
function vLength(v) {
|
||||
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 windowDimensions = Controller.getViewportDimensions();
|
||||
|
||||
var NEW_VOXEL_SIZE = 1.0;
|
||||
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
|
||||
|
@ -76,6 +69,233 @@ var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-publ
|
|||
var audioOptions = new AudioInjectionOptions();
|
||||
audioOptions.volume = 0.5;
|
||||
|
||||
var editToolsOn = false; // starts out off
|
||||
|
||||
|
||||
// previewAsVoxel - by default, we will preview adds/deletes/recolors as just 4 lines on the intersecting face. But if you
|
||||
// the preview to show a full voxel then set this to true and the voxel will be displayed for voxel editing
|
||||
var previewAsVoxel = false;
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
var linePreviewTop = Overlays.addOverlay("line3d", {
|
||||
position: { x: 0, y: 0, z: 0},
|
||||
end: { x: 0, y: 0, z: 0},
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
lineWidth: 1
|
||||
});
|
||||
|
||||
var linePreviewBottom = Overlays.addOverlay("line3d", {
|
||||
position: { x: 0, y: 0, z: 0},
|
||||
end: { x: 0, y: 0, z: 0},
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
lineWidth: 1
|
||||
});
|
||||
|
||||
var linePreviewLeft = Overlays.addOverlay("line3d", {
|
||||
position: { x: 0, y: 0, z: 0},
|
||||
end: { x: 0, y: 0, z: 0},
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
lineWidth: 1
|
||||
});
|
||||
|
||||
var linePreviewRight = Overlays.addOverlay("line3d", {
|
||||
position: { x: 0, y: 0, z: 0},
|
||||
end: { x: 0, y: 0, z: 0},
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
visible: false,
|
||||
lineWidth: 1
|
||||
});
|
||||
|
||||
|
||||
// these will be used below
|
||||
var sliderWidth = 158;
|
||||
var sliderHeight = 35;
|
||||
|
||||
// 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 + sliderWidth)) / 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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// These will be our tool palette overlays
|
||||
var numberOfTools = 5;
|
||||
var toolHeight = 40;
|
||||
var toolWidth = 62;
|
||||
var toolsHeight = toolHeight * numberOfTools;
|
||||
var toolsX = 0;
|
||||
var toolsY = (windowDimensions.y - toolsHeight) / 2;
|
||||
|
||||
var addToolAt = 0;
|
||||
var deleteToolAt = 1;
|
||||
var recolorToolAt = 2;
|
||||
var eyedropperToolAt = 3;
|
||||
var selectToolAt = 4;
|
||||
var toolSelectedColor = { red: 255, green: 255, blue: 255 };
|
||||
var notSelectedColor = { red: 128, green: 128, blue: 128 };
|
||||
|
||||
var addTool = Overlays.addOverlay("image", {
|
||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||
subImage: { x: 0, y: toolHeight * addToolAt, width: toolWidth, height: toolHeight },
|
||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
||||
color: toolSelectedColor,
|
||||
visible: false,
|
||||
alpha: 0.9
|
||||
});
|
||||
|
||||
var deleteTool = Overlays.addOverlay("image", {
|
||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||
subImage: { x: 0, y: toolHeight * deleteToolAt, width: toolWidth, height: toolHeight },
|
||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
||||
color: toolSelectedColor,
|
||||
visible: false,
|
||||
alpha: 0.9
|
||||
});
|
||||
|
||||
var recolorTool = Overlays.addOverlay("image", {
|
||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||
subImage: { x: 0, y: toolHeight * recolorToolAt, width: toolWidth, height: toolHeight },
|
||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
||||
color: toolSelectedColor,
|
||||
visible: false,
|
||||
alpha: 0.9
|
||||
});
|
||||
|
||||
var eyedropperTool = Overlays.addOverlay("image", {
|
||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||
subImage: { x: 0, y: toolHeight * eyedropperToolAt, width: toolWidth, height: toolHeight },
|
||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
||||
color: toolSelectedColor,
|
||||
visible: false,
|
||||
alpha: 0.9
|
||||
});
|
||||
|
||||
var selectTool = Overlays.addOverlay("image", {
|
||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||
subImage: { x: 0, y: toolHeight * selectToolAt, width: toolWidth, height: toolHeight },
|
||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
||||
color: toolSelectedColor,
|
||||
visible: false,
|
||||
alpha: 0.9
|
||||
});
|
||||
|
||||
|
||||
// This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to
|
||||
// move the slider
|
||||
|
||||
// see above...
|
||||
//var sliderWidth = 158;
|
||||
//var sliderHeight = 35;
|
||||
|
||||
var sliderX = swatchesX + swatchesWidth;
|
||||
var sliderY = windowDimensions.y - sliderHeight;
|
||||
var slider = Overlays.addOverlay("image", {
|
||||
// alternate form of expressing bounds
|
||||
bounds: { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight},
|
||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/slider.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
visible: false
|
||||
});
|
||||
|
||||
|
||||
// The slider is handled in the mouse event callbacks.
|
||||
var isMovingSlider = false;
|
||||
var thumbClickOffsetX = 0;
|
||||
|
||||
// This is the thumb of our slider
|
||||
var minThumbX = 30; // relative to the x of the slider
|
||||
var maxThumbX = minThumbX + 65;
|
||||
var thumbExtents = maxThumbX - minThumbX;
|
||||
var thumbX = (minThumbX + maxThumbX) / 2;
|
||||
var thumbY = sliderY + 9;
|
||||
var thumb = Overlays.addOverlay("image", {
|
||||
x: sliderX + thumbX,
|
||||
y: thumbY,
|
||||
width: 18,
|
||||
height: 17,
|
||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/thumb.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
visible: false
|
||||
});
|
||||
|
||||
var pointerVoxelScale = 0; // this is the voxel scale used for click to add or delete
|
||||
var pointerVoxelScaleSet = false; // if voxel scale has not yet been set, we use the intersection size
|
||||
|
||||
var pointerVoxelScaleSteps = 8; // the number of slider position steps
|
||||
var pointerVoxelScaleOriginStep = 3; // the position of slider for the 1 meter size voxel
|
||||
var pointerVoxelScaleMin = Math.pow(2, (1-pointerVoxelScaleOriginStep));
|
||||
var pointerVoxelScaleMax = Math.pow(2, (pointerVoxelScaleSteps-pointerVoxelScaleOriginStep));
|
||||
var thumbDeltaPerStep = thumbExtents / (pointerVoxelScaleSteps - 1);
|
||||
|
||||
function calcThumbFromScale(scale) {
|
||||
var scaleLog = Math.log(scale)/Math.log(2);
|
||||
var thumbStep = scaleLog + pointerVoxelScaleOriginStep;
|
||||
if (thumbStep < 1) {
|
||||
thumbStep = 1;
|
||||
}
|
||||
if (thumbStep > pointerVoxelScaleSteps) {
|
||||
thumbStep = pointerVoxelScaleSteps;
|
||||
}
|
||||
thumbX = (thumbDeltaPerStep * (thumbStep - 1)) + minThumbX;
|
||||
Overlays.editOverlay(thumb, { x: thumbX + sliderX } );
|
||||
}
|
||||
|
||||
function calcScaleFromThumb(newThumbX) {
|
||||
// newThumbX is the pixel location relative to start of slider,
|
||||
// we need to figure out the actual offset in the allowed slider area
|
||||
thumbAt = newThumbX - minThumbX;
|
||||
thumbStep = Math.floor((thumbAt/ thumbExtents) * (pointerVoxelScaleSteps-1)) + 1;
|
||||
pointerVoxelScale = Math.pow(2, (thumbStep-pointerVoxelScaleOriginStep));
|
||||
// now reset the display accordingly...
|
||||
calcThumbFromScale(pointerVoxelScale);
|
||||
|
||||
// if the user moved the thumb, then they are fixing the voxel scale
|
||||
pointerVoxelScaleSet = true;
|
||||
}
|
||||
|
||||
function setAudioPosition() {
|
||||
var camera = Camera.getPosition();
|
||||
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||
|
@ -101,13 +321,348 @@ function fixEulerAngles(eulers) {
|
|||
return rVal;
|
||||
}
|
||||
|
||||
var trackLastMouseX = 0;
|
||||
var trackLastMouseY = 0;
|
||||
var trackAsDelete = false;
|
||||
var trackAsRecolor = false;
|
||||
var trackAsEyedropper = false;
|
||||
var trackAsOrbit = false;
|
||||
|
||||
function calculateVoxelFromIntersection(intersection, operation) {
|
||||
//print("calculateVoxelFromIntersection() operation="+operation);
|
||||
var resultVoxel;
|
||||
|
||||
var voxelSize;
|
||||
if (pointerVoxelScaleSet) {
|
||||
voxelSize = pointerVoxelScale;
|
||||
} else {
|
||||
voxelSize = intersection.voxel.s;
|
||||
}
|
||||
|
||||
// first, calculate the enclosed voxel of size voxelSize that the intersection point falls inside of.
|
||||
// if you have a voxelSize that's smaller than the voxel you're intersecting, this calculation will result
|
||||
// in the subvoxel that the intersection point falls in
|
||||
var x = Math.floor(intersection.intersection.x / voxelSize) * voxelSize;
|
||||
var y = Math.floor(intersection.intersection.y / voxelSize) * voxelSize;
|
||||
var z = Math.floor(intersection.intersection.z / voxelSize) * voxelSize;
|
||||
resultVoxel = { x: x, y: y, z: z, s: voxelSize };
|
||||
highlightAt = { x: x, y: y, z: z, s: voxelSize };
|
||||
|
||||
// now we also want to calculate the "edge square" for the face for this voxel
|
||||
if (intersection.face == "MIN_X_FACE") {
|
||||
highlightAt.x = intersection.voxel.x;
|
||||
resultVoxel.x = intersection.voxel.x;
|
||||
if (operation == "add") {
|
||||
resultVoxel.x -= voxelSize;
|
||||
}
|
||||
|
||||
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
||||
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
|
||||
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
|
||||
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize };
|
||||
|
||||
} else if (intersection.face == "MAX_X_FACE") {
|
||||
highlightAt.x = intersection.voxel.x + intersection.voxel.s;
|
||||
resultVoxel.x = intersection.voxel.x + intersection.voxel.s;
|
||||
if (operation != "add") {
|
||||
resultVoxel.x -= voxelSize;
|
||||
}
|
||||
|
||||
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
||||
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
|
||||
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
|
||||
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize };
|
||||
|
||||
} else if (intersection.face == "MIN_Y_FACE") {
|
||||
|
||||
highlightAt.y = intersection.voxel.y;
|
||||
resultVoxel.y = intersection.voxel.y;
|
||||
|
||||
if (operation == "add") {
|
||||
resultVoxel.y -= voxelSize;
|
||||
}
|
||||
|
||||
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
||||
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
|
||||
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
|
||||
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize};
|
||||
|
||||
} else if (intersection.face == "MAX_Y_FACE") {
|
||||
|
||||
highlightAt.y = intersection.voxel.y + intersection.voxel.s;
|
||||
resultVoxel.y = intersection.voxel.y + intersection.voxel.s;
|
||||
if (operation != "add") {
|
||||
resultVoxel.y -= voxelSize;
|
||||
}
|
||||
|
||||
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
||||
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
|
||||
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
|
||||
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize};
|
||||
|
||||
} else if (intersection.face == "MIN_Z_FACE") {
|
||||
|
||||
highlightAt.z = intersection.voxel.z;
|
||||
resultVoxel.z = intersection.voxel.z;
|
||||
|
||||
if (operation == "add") {
|
||||
resultVoxel.z -= voxelSize;
|
||||
}
|
||||
|
||||
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
||||
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
|
||||
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
|
||||
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y + voxelSize, z: highlightAt.z};
|
||||
|
||||
} else if (intersection.face == "MAX_Z_FACE") {
|
||||
|
||||
highlightAt.z = intersection.voxel.z + intersection.voxel.s;
|
||||
resultVoxel.z = intersection.voxel.z + intersection.voxel.s;
|
||||
if (operation != "add") {
|
||||
resultVoxel.z -= voxelSize;
|
||||
}
|
||||
|
||||
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
||||
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
|
||||
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
|
||||
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y + voxelSize, z: highlightAt.z};
|
||||
|
||||
}
|
||||
|
||||
return resultVoxel;
|
||||
}
|
||||
|
||||
function showPreviewVoxel() {
|
||||
var voxelColor;
|
||||
|
||||
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
|
||||
// if the user hasn't updated the
|
||||
if (!pointerVoxelScaleSet) {
|
||||
calcThumbFromScale(intersection.voxel.s);
|
||||
}
|
||||
|
||||
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 = calculateVoxelFromIntersection(intersection,"delete");
|
||||
Overlays.editOverlay(voxelPreview, {
|
||||
position: guidePosition,
|
||||
size: guidePosition.s,
|
||||
visible: true,
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
solid: false,
|
||||
alpha: 1
|
||||
});
|
||||
} else if (trackAsRecolor || trackAsEyedropper) {
|
||||
guidePosition = calculateVoxelFromIntersection(intersection,"recolor");
|
||||
|
||||
Overlays.editOverlay(voxelPreview, {
|
||||
position: guidePosition,
|
||||
size: guidePosition.s + 0.002,
|
||||
visible: true,
|
||||
color: voxelColor,
|
||||
solid: true,
|
||||
alpha: 0.8
|
||||
});
|
||||
} else if (trackAsOrbit) {
|
||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||
} else if (!isExtruding) {
|
||||
guidePosition = calculateVoxelFromIntersection(intersection,"add");
|
||||
|
||||
Overlays.editOverlay(voxelPreview, {
|
||||
position: guidePosition,
|
||||
size: guidePosition.s,
|
||||
visible: true,
|
||||
color: voxelColor,
|
||||
solid: true,
|
||||
alpha: 0.7
|
||||
});
|
||||
} else if (isExtruding) {
|
||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||
}
|
||||
}
|
||||
|
||||
function showPreviewLines() {
|
||||
|
||||
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
|
||||
if (intersection.intersects) {
|
||||
|
||||
// if the user hasn't updated the
|
||||
if (!pointerVoxelScaleSet) {
|
||||
calcThumbFromScale(intersection.voxel.s);
|
||||
}
|
||||
|
||||
resultVoxel = calculateVoxelFromIntersection(intersection,"");
|
||||
Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true });
|
||||
Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true });
|
||||
Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true });
|
||||
Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true });
|
||||
|
||||
} else {
|
||||
Overlays.editOverlay(linePreviewTop, { visible: false });
|
||||
Overlays.editOverlay(linePreviewBottom, { visible: false });
|
||||
Overlays.editOverlay(linePreviewLeft, { visible: false });
|
||||
Overlays.editOverlay(linePreviewRight, { visible: false });
|
||||
}
|
||||
}
|
||||
|
||||
function showPreviewGuides() {
|
||||
if (editToolsOn) {
|
||||
if (previewAsVoxel) {
|
||||
showPreviewVoxel();
|
||||
|
||||
// make sure alternative is hidden
|
||||
Overlays.editOverlay(linePreviewTop, { visible: false });
|
||||
Overlays.editOverlay(linePreviewBottom, { visible: false });
|
||||
Overlays.editOverlay(linePreviewLeft, { visible: false });
|
||||
Overlays.editOverlay(linePreviewRight, { visible: false });
|
||||
} else {
|
||||
showPreviewLines();
|
||||
|
||||
// make sure alternative is hidden
|
||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||
}
|
||||
} else {
|
||||
// make sure all previews are off
|
||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||
Overlays.editOverlay(linePreviewTop, { visible: false });
|
||||
Overlays.editOverlay(linePreviewBottom, { visible: false });
|
||||
Overlays.editOverlay(linePreviewLeft, { visible: false });
|
||||
Overlays.editOverlay(linePreviewRight, { visible: false });
|
||||
}
|
||||
}
|
||||
|
||||
function trackMouseEvent(event) {
|
||||
trackLastMouseX = event.x;
|
||||
trackLastMouseY = event.y;
|
||||
trackAsDelete = event.isControl;
|
||||
trackAsRecolor = event.isShifted;
|
||||
trackAsEyedropper = event.isMeta;
|
||||
trackAsOrbit = event.isAlt;
|
||||
showPreviewGuides();
|
||||
}
|
||||
|
||||
function trackKeyPressEvent(event) {
|
||||
if (event.text == "CONTROL") {
|
||||
trackAsDelete = true;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
trackAsRecolor = true;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "META") {
|
||||
trackAsEyedropper = true;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "ALT") {
|
||||
trackAsOrbit = true;
|
||||
moveTools();
|
||||
}
|
||||
showPreviewGuides();
|
||||
}
|
||||
|
||||
function trackKeyReleaseEvent(event) {
|
||||
if (event.text == "ESC") {
|
||||
pointerVoxelScaleSet = false;
|
||||
}
|
||||
if (event.text == "-") {
|
||||
thumbX -= thumbDeltaPerStep;
|
||||
calcScaleFromThumb(thumbX);
|
||||
}
|
||||
if (event.text == "+") {
|
||||
thumbX += thumbDeltaPerStep;
|
||||
calcScaleFromThumb(thumbX);
|
||||
}
|
||||
if (event.text == "CONTROL") {
|
||||
trackAsDelete = false;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
trackAsRecolor = false;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "META") {
|
||||
trackAsEyedropper = false;
|
||||
moveTools();
|
||||
}
|
||||
if (event.text == "ALT") {
|
||||
trackAsOrbit = false;
|
||||
moveTools();
|
||||
}
|
||||
|
||||
// on TAB release, toggle our tool state
|
||||
if (event.text == "TAB") {
|
||||
editToolsOn = !editToolsOn;
|
||||
moveTools();
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
}
|
||||
|
||||
// on F1 toggle the preview mode between cubes and lines
|
||||
if (event.text == "F1") {
|
||||
previewAsVoxel = !previewAsVoxel;
|
||||
}
|
||||
|
||||
showPreviewGuides();
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
|
||||
// if our tools are off, then don't do anything
|
||||
if (!editToolsOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
var clickedOnSwatch = false;
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
|
||||
// If the user clicked on the thumb, handle the slider logic
|
||||
if (clickedOverlay == thumb) {
|
||||
isMovingSlider = true;
|
||||
thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb
|
||||
return; // no further processing
|
||||
} else {
|
||||
// if the user clicked on one of the color swatches, update the selectedSwatch
|
||||
for (s = 0; s < numColors; s++) {
|
||||
if (clickedOverlay == swatches[s]) {
|
||||
whichColor = s;
|
||||
moveTools();
|
||||
clickedOnSwatch = true;
|
||||
}
|
||||
}
|
||||
if (clickedOnSwatch) {
|
||||
return; // no further processing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
trackMouseEvent(event); // used by preview support
|
||||
mouseX = event.x;
|
||||
mouseY = event.y;
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction);
|
||||
if (intersection.intersects) {
|
||||
// if the user hasn't updated the
|
||||
if (!pointerVoxelScaleSet) {
|
||||
calcThumbFromScale(intersection.voxel.s);
|
||||
}
|
||||
|
||||
if (event.isAlt) {
|
||||
// start orbit camera!
|
||||
var cameraPosition = Camera.getPosition();
|
||||
|
@ -118,66 +673,58 @@ function mousePressEvent(event) {
|
|||
// get position for initial azimuth, elevation
|
||||
orbitCenter = intersection.intersection;
|
||||
var orbitVector = Vec3.subtract(cameraPosition, orbitCenter);
|
||||
orbitRadius = vLength(orbitVector);
|
||||
orbitRadius = Vec3.length(orbitVector);
|
||||
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
|
||||
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
|
||||
|
||||
} else if (event.isRightButton || event.isControl) {
|
||||
} else if (trackAsDelete || (event.isRightButton && !trackAsEyedropper)) {
|
||||
// Delete voxel
|
||||
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
|
||||
voxelDetails = calculateVoxelFromIntersection(intersection,"delete");
|
||||
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
|
||||
Audio.playSound(deleteSound, audioOptions);
|
||||
|
||||
} else if (event.isShifted) {
|
||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||
} else if (trackAsEyedropper) {
|
||||
if (whichColor != -1) {
|
||||
colors[whichColor].red = intersection.voxel.red;
|
||||
colors[whichColor].green = intersection.voxel.green;
|
||||
colors[whichColor].blue = intersection.voxel.blue;
|
||||
moveTools();
|
||||
}
|
||||
|
||||
} else if (trackAsRecolor) {
|
||||
// Recolor Voxel
|
||||
Voxels.setVoxel(intersection.voxel.x,
|
||||
intersection.voxel.y,
|
||||
intersection.voxel.z,
|
||||
intersection.voxel.s,
|
||||
voxelDetails = calculateVoxelFromIntersection(intersection,"recolor");
|
||||
|
||||
// doing this erase then set will make sure we only recolor just the target voxel
|
||||
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
|
||||
Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s,
|
||||
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
|
||||
Audio.playSound(changeColorSound, audioOptions);
|
||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||
} else {
|
||||
// Add voxel on face
|
||||
if (whichColor == -1) {
|
||||
// Copy mode - use clicked voxel color
|
||||
var newVoxel = {
|
||||
x: intersection.voxel.x,
|
||||
y: intersection.voxel.y,
|
||||
z: intersection.voxel.z,
|
||||
s: intersection.voxel.s,
|
||||
newColor = {
|
||||
red: intersection.voxel.red,
|
||||
green: intersection.voxel.green,
|
||||
blue: intersection.voxel.blue };
|
||||
} else {
|
||||
var newVoxel = {
|
||||
x: intersection.voxel.x,
|
||||
y: intersection.voxel.y,
|
||||
z: intersection.voxel.z,
|
||||
s: intersection.voxel.s,
|
||||
newColor = {
|
||||
red: colors[whichColor].red,
|
||||
green: colors[whichColor].green,
|
||||
blue: colors[whichColor].blue };
|
||||
}
|
||||
|
||||
if (intersection.face == "MIN_X_FACE") {
|
||||
newVoxel.x -= newVoxel.s;
|
||||
} else if (intersection.face == "MAX_X_FACE") {
|
||||
newVoxel.x += newVoxel.s;
|
||||
} else if (intersection.face == "MIN_Y_FACE") {
|
||||
newVoxel.y -= newVoxel.s;
|
||||
} else if (intersection.face == "MAX_Y_FACE") {
|
||||
newVoxel.y += newVoxel.s;
|
||||
} else if (intersection.face == "MIN_Z_FACE") {
|
||||
newVoxel.z -= newVoxel.s;
|
||||
} else if (intersection.face == "MAX_Z_FACE") {
|
||||
newVoxel.z += newVoxel.s;
|
||||
}
|
||||
|
||||
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||
lastVoxelPosition = { x: newVoxel.x, y: newVoxel.y, z: newVoxel.z };
|
||||
lastVoxelColor = { red: newVoxel.red, green: newVoxel.green, blue: newVoxel.blue };
|
||||
lastVoxelScale = newVoxel.s;
|
||||
voxelDetails = calculateVoxelFromIntersection(intersection,"add");
|
||||
Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s,
|
||||
newColor.red, newColor.green, newColor.blue);
|
||||
lastVoxelPosition = { x: voxelDetails.x, y: voxelDetails.y, z: voxelDetails.z };
|
||||
lastVoxelColor = { red: newColor.red, green: newColor.green, blue: newColor.blue };
|
||||
lastVoxelScale = voxelDetails.s;
|
||||
|
||||
Audio.playSound(addSound, audioOptions);
|
||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||
dragStart = { x: event.x, y: event.y };
|
||||
isAdding = true;
|
||||
}
|
||||
|
@ -185,47 +732,69 @@ function mousePressEvent(event) {
|
|||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
key_alt = event.isAlt;
|
||||
key_shift = event.isShifted;
|
||||
var nVal = parseInt(event.text);
|
||||
if (event.text == "0") {
|
||||
print("Color = Copy");
|
||||
whichColor = -1;
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
} else if ((nVal > 0) && (nVal <= numColors)) {
|
||||
whichColor = nVal - 1;
|
||||
print("Color = " + (whichColor + 1));
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
} else if (event.text == "9") {
|
||||
// Create a brand new 1 meter voxel in front of your avatar
|
||||
var color = whichColor;
|
||||
if (color == -1) color = 0;
|
||||
var newPosition = getNewVoxelPosition();
|
||||
var newVoxel = {
|
||||
x: newPosition.x,
|
||||
y: newPosition.y ,
|
||||
z: newPosition.z,
|
||||
s: NEW_VOXEL_SIZE,
|
||||
red: colors[color].red,
|
||||
green: colors[color].green,
|
||||
blue: colors[color].blue };
|
||||
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||
setAudioPosition();
|
||||
Audio.playSound(addSound, audioOptions);
|
||||
} else if (event.text == " ") {
|
||||
// if our tools are off, then don't do anything
|
||||
if (editToolsOn) {
|
||||
key_alt = event.isAlt;
|
||||
key_shift = event.isShifted;
|
||||
var nVal = parseInt(event.text);
|
||||
if (event.text == "0") {
|
||||
print("Color = Copy");
|
||||
whichColor = -1;
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
moveTools();
|
||||
} else if ((nVal > 0) && (nVal <= numColors)) {
|
||||
whichColor = nVal - 1;
|
||||
print("Color = " + (whichColor + 1));
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
moveTools();
|
||||
} else if (event.text == "9") {
|
||||
// Create a brand new 1 meter voxel in front of your avatar
|
||||
var color = whichColor;
|
||||
if (color == -1) color = 0;
|
||||
var newPosition = getNewVoxelPosition();
|
||||
var newVoxel = {
|
||||
x: newPosition.x,
|
||||
y: newPosition.y ,
|
||||
z: newPosition.z,
|
||||
s: NEW_VOXEL_SIZE,
|
||||
red: colors[color].red,
|
||||
green: colors[color].green,
|
||||
blue: colors[color].blue };
|
||||
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||
setAudioPosition();
|
||||
Audio.playSound(addSound, audioOptions);
|
||||
}
|
||||
}
|
||||
|
||||
// do this even if not in edit tools
|
||||
if (event.text == " ") {
|
||||
// Reset my orientation!
|
||||
var orientation = { x:0, y:0, z:0, w:1 };
|
||||
Camera.setOrientation(orientation);
|
||||
MyAvatar.orientation = orientation;
|
||||
}
|
||||
trackKeyPressEvent(event); // used by preview support
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
trackKeyReleaseEvent(event); // used by preview support
|
||||
key_alt = false;
|
||||
key_shift = false;
|
||||
}
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (isOrbiting) {
|
||||
if (isMovingSlider) {
|
||||
thumbX = (event.x - thumbClickOffsetX) - sliderX;
|
||||
if (thumbX < minThumbX) {
|
||||
thumbX = minThumbX;
|
||||
}
|
||||
if (thumbX > maxThumbX) {
|
||||
thumbX = maxThumbX;
|
||||
}
|
||||
calcScaleFromThumb(thumbX);
|
||||
|
||||
} else if (isOrbiting) {
|
||||
var cameraOrientation = Camera.getOrientation();
|
||||
var origEulers = Quat.safeEulerAngles(cameraOrientation);
|
||||
var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation));
|
||||
|
@ -240,15 +809,14 @@ function mouseMoveEvent(event) {
|
|||
Camera.setPosition(orbitPosition);
|
||||
mouseX = event.x;
|
||||
mouseY = event.y;
|
||||
}
|
||||
if (isAdding) {
|
||||
} else if (isAdding) {
|
||||
// Watch the drag direction to tell which way to 'extrude' this voxel
|
||||
if (!isExtruding) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
|
||||
y: pickRay.origin.y - lastVoxelPosition.y,
|
||||
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 };
|
||||
mouseSpot.x += pickRay.origin.x;
|
||||
mouseSpot.y += pickRay.origin.y;
|
||||
|
@ -279,9 +847,21 @@ function mouseMoveEvent(event) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the add voxel/delete voxel overlay preview
|
||||
trackMouseEvent(event);
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
// if our tools are off, then don't do anything
|
||||
if (!editToolsOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMovingSlider) {
|
||||
isMovingSlider = false;
|
||||
}
|
||||
|
||||
if (isOrbiting) {
|
||||
var cameraOrientation = Camera.getOrientation();
|
||||
var eulers = Quat.safeEulerAngles(cameraOrientation);
|
||||
|
@ -296,6 +876,101 @@ function mouseReleaseEvent(event) {
|
|||
isExtruding = false;
|
||||
}
|
||||
|
||||
function moveTools() {
|
||||
// move the swatches
|
||||
swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 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
|
||||
});
|
||||
}
|
||||
|
||||
// move the tools
|
||||
toolsY = (windowDimensions.y - toolsHeight) / 2;
|
||||
addToolColor = notSelectedColor;
|
||||
deleteToolColor = notSelectedColor;
|
||||
recolorToolColor = notSelectedColor;
|
||||
eyedropperToolColor = notSelectedColor;
|
||||
selectToolColor = notSelectedColor;
|
||||
|
||||
if (trackAsDelete) {
|
||||
deleteToolColor = toolSelectedColor;
|
||||
} else if (trackAsRecolor) {
|
||||
recolorToolColor = toolSelectedColor;
|
||||
} else if (trackAsEyedropper) {
|
||||
eyedropperToolColor = toolSelectedColor;
|
||||
} else if (trackAsOrbit) {
|
||||
// nothing gets selected in this case...
|
||||
} else {
|
||||
addToolColor = toolSelectedColor;
|
||||
}
|
||||
|
||||
Overlays.editOverlay(addTool, {
|
||||
x: 0, y: toolsY + (toolHeight * addToolAt), width: toolWidth, height: toolHeight,
|
||||
color: addToolColor,
|
||||
visible: editToolsOn
|
||||
});
|
||||
|
||||
Overlays.editOverlay(deleteTool, {
|
||||
x: 0, y: toolsY + (toolHeight * deleteToolAt), width: toolWidth, height: toolHeight,
|
||||
color: deleteToolColor,
|
||||
visible: editToolsOn
|
||||
});
|
||||
|
||||
Overlays.editOverlay(recolorTool, {
|
||||
x: 0, y: toolsY + (toolHeight * recolorToolAt), width: toolWidth, height: toolHeight,
|
||||
color: recolorToolColor,
|
||||
visible: editToolsOn
|
||||
});
|
||||
|
||||
Overlays.editOverlay(eyedropperTool, {
|
||||
x: 0, y: toolsY + (toolHeight * eyedropperToolAt), width: toolWidth, height: toolHeight,
|
||||
color: eyedropperToolColor,
|
||||
visible: editToolsOn
|
||||
});
|
||||
|
||||
Overlays.editOverlay(selectTool, {
|
||||
x: 0, y: toolsY + (toolHeight * selectToolAt), width: toolWidth, height: toolHeight,
|
||||
color: selectToolColor,
|
||||
visible: editToolsOn
|
||||
});
|
||||
|
||||
|
||||
sliderX = swatchesX + swatchesWidth;
|
||||
sliderY = windowDimensions.y - sliderHeight;
|
||||
Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn });
|
||||
|
||||
// This is the thumb of our slider
|
||||
thumbY = sliderY + 9;
|
||||
Overlays.editOverlay(thumb, { x: sliderX + thumbX, y: thumbY, visible: editToolsOn });
|
||||
|
||||
}
|
||||
|
||||
|
||||
function update() {
|
||||
var newWindowDimensions = Controller.getViewportDimensions();
|
||||
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
|
||||
windowDimensions = newWindowDimensions;
|
||||
moveTools();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
@ -303,5 +978,24 @@ Controller.keyPressEvent.connect(keyPressEvent);
|
|||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
function scriptEnding() {
|
||||
Overlays.deleteOverlay(voxelPreview);
|
||||
Overlays.deleteOverlay(linePreviewTop);
|
||||
Overlays.deleteOverlay(linePreviewBottom);
|
||||
Overlays.deleteOverlay(linePreviewLeft);
|
||||
Overlays.deleteOverlay(linePreviewRight);
|
||||
for (s = 0; s < numColors; s++) {
|
||||
Overlays.deleteOverlay(swatches[s]);
|
||||
}
|
||||
Overlays.deleteOverlay(addTool);
|
||||
Overlays.deleteOverlay(deleteTool);
|
||||
Overlays.deleteOverlay(recolorTool);
|
||||
Overlays.deleteOverlay(eyedropperTool);
|
||||
Overlays.deleteOverlay(selectTool);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
Script.willSendVisualDataCallback.connect(update);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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(LIBOVR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibOVR)
|
||||
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})
|
||||
set(BUILD_SEQ $ENV{JOB_ID})
|
||||
|
@ -138,9 +139,10 @@ find_package(Faceshift)
|
|||
find_package(GLM REQUIRED)
|
||||
find_package(LibOVR)
|
||||
find_package(Sixense)
|
||||
find_package(Visage)
|
||||
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)
|
||||
add_definitions(-DHAVE_SIXENSE)
|
||||
include_directories(SYSTEM ${SIXENSE_INCLUDE_DIRS})
|
||||
|
@ -150,6 +152,21 @@ if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
|
|||
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
|
||||
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
|
||||
if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
|
||||
add_definitions(-DHAVE_LIBOVR)
|
||||
|
|
14
interface/external/visage/readme.txt
vendored
Normal file
14
interface/external/visage/readme.txt
vendored
Normal 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.
|
||||
|
18
interface/resources/icons/backButton.svg
Normal file
18
interface/resources/icons/backButton.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 2000" enable-background="new 0 0 1000 2000" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M11,15.5c0-0.4,0.1-0.7,0.4-0.9l6.8-6.8c0.3-0.3,0.6-0.4,1-0.4c0.4,0,0.7,0.1,0.9,0.4L21,8.5
|
||||
c0.3,0.2,0.4,0.6,0.4,0.9s-0.1,0.7-0.4,0.9l-3.1,3.1h10c0.8,0,1.2,0.6,1.2,1.3v1.3c0,0.7-0.5,1.3-1.2,1.3h-10l3.1,3.1
|
||||
c0.3,0.3,0.4,0.6,0.4,1c0,0.4-0.1,0.7-0.4,1l-0.8,0.8c-0.3,0.2-0.6,0.4-0.9,0.4c-0.4,0-0.7-0.1-1-0.4l-6.8-6.8
|
||||
C11.2,16.2,11,15.9,11,15.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M41,27.5c0,2.5-2,4.5-4.5,4.5h-32C2,32,0,30,0,27.5v-23C0,2,2,0,4.5,0h32C39,0,41,2,41,4.5V27.5z M40,4.5
|
||||
C40,2.6,38.4,1,36.5,1h-32C2.6,1,1,2.6,1,4.5v23C1,29.4,2.6,31,4.5,31h32c1.9,0,3.5-1.6,3.5-3.5V4.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 1.1 KiB |
19
interface/resources/icons/forwardButton.svg
Normal file
19
interface/resources/icons/forwardButton.svg
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0.6 -2 48.7 34.1" enable-background="new 0.6 -2 48.7 34.1" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#666666" d="M30.7,14.8l-6.8,6.8c-0.3,0.2-0.6,0.4-1,0.4c-0.4,0-0.7-0.1-0.9-0.4l-0.8-0.8c-0.3-0.3-0.4-0.6-0.4-1
|
||||
c0-0.4,0.1-0.7,0.4-1l3.1-3.1h-10c-0.8,0-1.2-0.6-1.2-1.3v-1.3c0-0.7,0.5-1.3,1.2-1.3h10l-3.1-3.1c-0.3-0.2-0.4-0.6-0.4-0.9
|
||||
s0.1-0.7,0.4-0.9l0.8-0.8c0.3-0.3,0.6-0.4,0.9-0.4c0.4,0,0.7,0.1,1,0.4l6.8,6.8c0.3,0.2,0.4,0.6,0.4,0.9S30.9,14.6,30.7,14.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M42,25.5c0,2.5-2,4.5-4.5,4.5h-32C3,30,1,28,1,25.5v-23C1,0,3-2,5.5-2h32C40-2,42,0,42,2.5V25.5z M41,2.5
|
||||
C41,0.6,39.4-1,37.5-1h-32C3.6-1,2,0.6,2,2.5v23C2,27.4,3.6,29,5.5,29h32c1.9,0,3.5-1.6,3.5-3.5V2.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 1.1 KiB |
18
interface/resources/icons/toParentButton.svg
Normal file
18
interface/resources/icons/toParentButton.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 42.5 33.5" enable-background="new 0 0 42.5 33.5" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M36.5,32h-32C2,32,0,30,0,27.5v-23C0,2,2,0,4.5,0h32C39,0,41,2,41,4.5v23C41,30,39,32,36.5,32z M4.5,1
|
||||
C2.6,1,1,2.6,1,4.5v23C1,29.4,2.6,31,4.5,31h32c1.9,0,3.5-1.6,3.5-3.5v-23C40,2.6,38.4,1,36.5,1H4.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path fill="#8C8C8C" d="M29.8,19.6c-0.3-0.6-0.9-0.6-1.5-0.6H27v-1.9c0-1.1-1.3-2.1-2.4-2.1H23v1h1.6c0.5,0,0.4,0.7,0.4,1.1V19h-7.7
|
||||
c-0.8,0-1.6,0.2-2.1,0.9L13,22.5v-7.6c0-0.5,0.1-0.9,0.6-0.9h2.9c0.5,0,0.6,0.4,0.6,0.9v0.6c0,0.4,0.6,0.8,1,0.8V14h0.2
|
||||
c-0.3-0.7-1-1-1.8-1h-2.9c-1.1,0-1.6,0.8-1.6,1.9v8.6c0,1.1,0.5,1.6,1.6,1.6h11c0.8,0,1.8-0.3,2.3-0.9l2.6-3.1
|
||||
c0.2-0.3,0.4-0.5,0.4-0.9C29.9,19.9,29.9,19.8,29.8,19.6z M28.6,20.4L26,23.5C25.7,23.8,25,24,24.6,24h-11c-0.2,0-0.5,0.1-0.5-0.2
|
||||
c0-0.1,0.1-0.2,0.2-0.3l2.6-3.1c0.3-0.4,0.9-0.5,1.4-0.5h11c0.2,0,0.5-0.1,0.5,0.2C28.8,20.3,28.7,20.3,28.6,20.4z"/>
|
||||
<path fill="#666666" d="M22.6,11.1h3l-4.5-4.5l-4.5,4.5h3v7.5h3V11.1z"/>
|
||||
</svg>
|
After (image error) Size: 1.4 KiB |
|
@ -20,12 +20,23 @@ QLabel#infoLabel {
|
|||
color: #666666;
|
||||
}
|
||||
|
||||
QProgressBar {
|
||||
border: 0px;
|
||||
border-radius: 0px;
|
||||
background-color: #BFE4E4;
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
QProgressBar::chunk {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 18px;
|
||||
padding: 17px 0px 15px;
|
||||
width: 107px;
|
||||
width: 120px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
@ -51,6 +62,21 @@ QPushButton#cancelButton {
|
|||
margin-right: 11px;
|
||||
}
|
||||
|
||||
#backButton {
|
||||
background-image: url(resources/icons/backButton.svg);
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#forwardButton {
|
||||
background-image: url(resources/icons/forwardButton.svg);
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#toParentButton {
|
||||
background-image: url(resources/icons/toParentButton.svg);
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
QSidebar, QTreeView {
|
||||
border: 1px solid #C5C5C5;
|
||||
font-size: 14px;
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include <VoxelSceneStats.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ClipboardScriptingInterface.h"
|
||||
#include "DataServerClient.h"
|
||||
#include "InterfaceVersion.h"
|
||||
#include "Menu.h"
|
||||
|
@ -309,8 +310,13 @@ Application::~Application() {
|
|||
delete idleTimer;
|
||||
|
||||
Menu::getInstance()->saveSettings();
|
||||
|
||||
_rearMirrorTools->saveSettings(_settings);
|
||||
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
if (_voxelImporter) {
|
||||
_voxelImporter->saveSettings(_settings);
|
||||
delete _voxelImporter;
|
||||
}
|
||||
_settings->sync();
|
||||
|
||||
// let the avatar mixer know we're out
|
||||
|
@ -331,7 +337,6 @@ Application::~Application() {
|
|||
|
||||
storeSizeAndPosition();
|
||||
saveScripts();
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
|
||||
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
|
||||
Menu::getInstance()->deleteLater();
|
||||
|
@ -450,22 +455,22 @@ void Application::paintGL() {
|
|||
_myCamera.setUpShift(0.0f);
|
||||
_myCamera.setDistance(0.0f);
|
||||
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead().getOrientation());
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead()->getOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation());
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
_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.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0));
|
||||
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
|
||||
|
@ -529,14 +534,14 @@ void Application::paintGL() {
|
|||
_mirrorCamera.setTargetPosition(_myAvatar->getChestPosition());
|
||||
} else { // HEAD zoom level
|
||||
_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
|
||||
// face/body so that the average eye position lies at the origin
|
||||
eyeRelativeCamera = true;
|
||||
_mirrorCamera.setTargetPosition(glm::vec3());
|
||||
|
||||
} else {
|
||||
_mirrorCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
|
||||
_mirrorCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,26 +563,26 @@ void Application::paintGL() {
|
|||
if (eyeRelativeCamera) {
|
||||
// save absolute translations
|
||||
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
|
||||
glm::vec3 leftEyePosition, rightEyePosition;
|
||||
_myAvatar->getHead().getFaceModel().setTranslation(glm::vec3());
|
||||
_myAvatar->getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition);
|
||||
_myAvatar->getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f);
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
|
||||
_myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition);
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f);
|
||||
|
||||
// get the neck position relative to the body and use it to set the skeleton translation
|
||||
glm::vec3 neckPosition;
|
||||
_myAvatar->getSkeletonModel().setTranslation(glm::vec3());
|
||||
_myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
|
||||
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead().getFaceModel().getTranslation() -
|
||||
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
|
||||
neckPosition);
|
||||
|
||||
displaySide(_mirrorCamera, true);
|
||||
|
||||
// restore absolute translations
|
||||
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
|
||||
_myAvatar->getHead().getFaceModel().setTranslation(absoluteFaceTranslation);
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
|
||||
} else {
|
||||
displaySide(_mirrorCamera, true);
|
||||
}
|
||||
|
@ -649,6 +654,9 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum)
|
|||
}
|
||||
glFrustum(left, right, bottom, top, nearVal, farVal);
|
||||
|
||||
// save matrix
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&_projectionMatrix);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
|
@ -1296,8 +1304,14 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
pasteVoxels();
|
||||
}
|
||||
|
||||
} else if (event->button() == Qt::RightButton && Menu::getInstance()->isVoxelModeActionChecked()) {
|
||||
deleteVoxelUnderCursor();
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
if (Menu::getInstance()->isVoxelModeActionChecked()) {
|
||||
deleteVoxelUnderCursor();
|
||||
}
|
||||
if (_pasteMode) {
|
||||
_pasteMode = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1686,6 +1700,10 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) {
|
|||
}
|
||||
|
||||
void Application::exportVoxels() {
|
||||
exportVoxels(_mouseVoxel);
|
||||
}
|
||||
|
||||
void Application::exportVoxels(const VoxelDetail& sourceVoxel) {
|
||||
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
QString suggestedName = desktopLocation.append("/voxels.svo");
|
||||
|
||||
|
@ -1693,7 +1711,7 @@ void Application::exportVoxels() {
|
|||
tr("Sparse Voxel Octree Files (*.svo)"));
|
||||
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
|
||||
const char* fileName = fileNameAscii.data();
|
||||
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
if (selectedNode) {
|
||||
VoxelTree exportTree;
|
||||
_voxels.copySubTreeIntoNewTree(selectedNode, &exportTree, true);
|
||||
|
@ -1707,13 +1725,19 @@ void Application::exportVoxels() {
|
|||
void Application::importVoxels() {
|
||||
if (!_voxelImporter) {
|
||||
_voxelImporter = new VoxelImporter(_window);
|
||||
_voxelImporter->init(_settings);
|
||||
_voxelImporter->loadSettings(_settings);
|
||||
}
|
||||
|
||||
if (_voxelImporter->exec()) {
|
||||
qDebug("[DEBUG] Import succeeded.");
|
||||
if (!_voxelImporter->exec()) {
|
||||
qDebug() << "[DEBUG] Import succeeded." << endl;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::VoxelSelectMode, true);
|
||||
_pasteMode = true;
|
||||
} else {
|
||||
qDebug("[DEBUG] Import failed.");
|
||||
qDebug() << "[DEBUG] Import failed." << endl;
|
||||
if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) {
|
||||
_sharedVoxelSystem.killLocalVoxels();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
}
|
||||
}
|
||||
|
||||
// restore the main window's active state
|
||||
|
@ -1721,11 +1745,19 @@ void Application::importVoxels() {
|
|||
}
|
||||
|
||||
void Application::cutVoxels() {
|
||||
copyVoxels();
|
||||
deleteVoxelUnderCursor();
|
||||
cutVoxels(_mouseVoxel);
|
||||
}
|
||||
|
||||
void Application::cutVoxels(const VoxelDetail& sourceVoxel) {
|
||||
copyVoxels(sourceVoxel);
|
||||
deleteVoxelAt(sourceVoxel);
|
||||
}
|
||||
|
||||
void Application::copyVoxels() {
|
||||
copyVoxels(_mouseVoxel);
|
||||
}
|
||||
|
||||
void Application::copyVoxels(const VoxelDetail& sourceVoxel) {
|
||||
// switch to and clear the clipboard first...
|
||||
_sharedVoxelSystem.killLocalVoxels();
|
||||
if (_sharedVoxelSystem.getTree() != &_clipboard) {
|
||||
|
@ -1734,7 +1766,7 @@ void Application::copyVoxels() {
|
|||
}
|
||||
|
||||
// then copy onto it if there is something to copy
|
||||
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
if (selectedNode) {
|
||||
_voxels.copySubTreeIntoNewTree(selectedNode, &_sharedVoxelSystem, true);
|
||||
}
|
||||
|
@ -1756,8 +1788,12 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati
|
|||
}
|
||||
|
||||
void Application::pasteVoxels() {
|
||||
pasteVoxels(_mouseVoxel);
|
||||
}
|
||||
|
||||
void Application::pasteVoxels(const VoxelDetail& sourceVoxel) {
|
||||
unsigned char* calculatedOctCode = NULL;
|
||||
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
|
||||
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
|
||||
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
|
||||
|
@ -1766,14 +1802,15 @@ void Application::pasteVoxels() {
|
|||
if (selectedNode) {
|
||||
octalCodeDestination = selectedNode->getOctalCode();
|
||||
} else {
|
||||
octalCodeDestination = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
octalCodeDestination = calculatedOctCode = pointToVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
}
|
||||
|
||||
pasteVoxelsToOctalCode(octalCodeDestination);
|
||||
|
||||
|
||||
if (calculatedOctCode) {
|
||||
delete[] calculatedOctCode;
|
||||
}
|
||||
_pasteMode = false;
|
||||
}
|
||||
|
||||
void Application::findAxisAlignment() {
|
||||
|
@ -1810,12 +1847,14 @@ void Application::nudgeVoxels() {
|
|||
// calculate nudgeVec
|
||||
glm::vec3 nudgeVec(_nudgeGuidePosition.x - _nudgeVoxel.x, _nudgeGuidePosition.y - _nudgeVoxel.y, _nudgeGuidePosition.z - _nudgeVoxel.z);
|
||||
|
||||
VoxelTreeElement* nodeToNudge = _voxels.getVoxelAt(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z, _nudgeVoxel.s);
|
||||
nudgeVoxelsByVector(_nudgeVoxel, nudgeVec);
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeToNudge) {
|
||||
_voxels.getTree()->nudgeSubTree(nodeToNudge, nudgeVec, _voxelEditSender);
|
||||
_nudgeStarted = false;
|
||||
}
|
||||
void Application::nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) {
|
||||
VoxelTreeElement* nodeToNudge = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
if (nodeToNudge) {
|
||||
_voxels.getTree()->nudgeSubTree(nodeToNudge, nudgeVec, _voxelEditSender);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1845,6 +1884,7 @@ void Application::init() {
|
|||
|
||||
VoxelTreeElement::removeUpdateHook(&_sharedVoxelSystem);
|
||||
|
||||
// Cleanup of the original shared tree
|
||||
_sharedVoxelSystem.init();
|
||||
VoxelTree* tmpTree = _sharedVoxelSystem.getTree();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
|
@ -1864,7 +1904,7 @@ void Application::init() {
|
|||
// TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager
|
||||
_avatarManager.init();
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
_myCamera.setModeShiftPeriod(1.0f);
|
||||
|
||||
_mirrorCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
_mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT);
|
||||
|
@ -1936,12 +1976,11 @@ void Application::init() {
|
|||
_audio.init(_glWidget);
|
||||
|
||||
_rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings);
|
||||
|
||||
connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors()));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Application::closeMirrorView() {
|
||||
|
@ -1975,8 +2014,8 @@ const float MAX_VOXEL_EDIT_DISTANCE = 50.0f;
|
|||
const float HEAD_SPHERE_RADIUS = 0.07f;
|
||||
|
||||
bool Application::isLookingAtMyAvatar(Avatar* avatar) {
|
||||
glm::vec3 theirLookat = avatar->getHead().getLookAtPosition();
|
||||
glm::vec3 myHeadPosition = _myAvatar->getHead().getPosition();
|
||||
glm::vec3 theirLookat = avatar->getHead()->getLookAtPosition();
|
||||
glm::vec3 myHeadPosition = _myAvatar->getHead()->getPosition();
|
||||
|
||||
if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) {
|
||||
return true;
|
||||
|
@ -2037,10 +2076,19 @@ void Application::updateFaceshift() {
|
|||
|
||||
// Copy angular velocity if measured by faceshift, to the head
|
||||
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) {
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
|
@ -2054,23 +2102,35 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
|
|||
float distance = TREE_SCALE;
|
||||
if (_myAvatar->getLookAtTargetAvatar()) {
|
||||
distance = glm::distance(_mouseRayOrigin,
|
||||
static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead().calculateAverageEyePosition());
|
||||
static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition());
|
||||
|
||||
} else if (_isHoverVoxel) {
|
||||
distance = glm::distance(_mouseRayOrigin, getMouseVoxelWorldCoordinates(_hoverVoxel));
|
||||
}
|
||||
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * distance;
|
||||
}
|
||||
bool trackerActive = false;
|
||||
float eyePitch, eyeYaw;
|
||||
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
|
||||
glm::vec3 origin = _myAvatar->getHead().calculateAverageEyePosition();
|
||||
glm::vec3 origin = _myAvatar->getHead()->calculateAverageEyePosition();
|
||||
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
|
||||
float deflection = Menu::getInstance()->getFaceshiftEyeDeflection();
|
||||
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);
|
||||
}
|
||||
_myAvatar->getHead().setLookAtPosition(lookAtSpot);
|
||||
_myAvatar->getHead()->setLookAtPosition(lookAtSpot);
|
||||
}
|
||||
|
||||
void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) {
|
||||
|
@ -2226,17 +2286,17 @@ void Application::cameraMenuChanged() {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
_myCamera.setModeShiftRate(100.0f);
|
||||
_myCamera.setModeShiftPeriod(0.00f);
|
||||
}
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
_myCamera.setModeShiftPeriod(1.0f);
|
||||
}
|
||||
} else {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
_myCamera.setModeShiftPeriod(1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2318,6 +2378,7 @@ void Application::update(float deltaTime) {
|
|||
glm::vec3 lookAtSpot;
|
||||
|
||||
updateFaceshift();
|
||||
updateVisage();
|
||||
_myAvatar->updateLookAtTargetAvatar(lookAtSpot);
|
||||
updateMyAvatarLookAtPosition(lookAtSpot);
|
||||
|
||||
|
@ -2922,6 +2983,15 @@ void Application::loadTranslatedViewMatrix(const glm::vec3& translation) {
|
|||
translation.z + _viewMatrixTranslation.z);
|
||||
}
|
||||
|
||||
void Application::getModelViewMatrix(glm::dmat4* modelViewMatrix) {
|
||||
(*modelViewMatrix) =_untranslatedViewMatrix;
|
||||
(*modelViewMatrix)[3] = _untranslatedViewMatrix * glm::vec4(_viewMatrixTranslation, 1);
|
||||
}
|
||||
|
||||
void Application::getProjectionMatrix(glm::dmat4* projectionMatrix) {
|
||||
*projectionMatrix = _projectionMatrix;
|
||||
}
|
||||
|
||||
void Application::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
|
||||
|
||||
|
@ -3789,18 +3859,27 @@ bool Application::maybeEditVoxelUnderCursor() {
|
|||
}
|
||||
|
||||
void Application::deleteVoxelUnderCursor() {
|
||||
if (_mouseVoxel.s != 0) {
|
||||
deleteVoxelAt(_mouseVoxel);
|
||||
}
|
||||
|
||||
void Application::deleteVoxels(const VoxelDetail& voxel) {
|
||||
deleteVoxelAt(voxel);
|
||||
}
|
||||
|
||||
void Application::deleteVoxelAt(const VoxelDetail& voxel) {
|
||||
if (voxel.s != 0) {
|
||||
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
|
||||
_voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, _mouseVoxel);
|
||||
_voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, voxel);
|
||||
|
||||
// delete it locally to see the effect immediately (and in case no voxel server is present)
|
||||
_voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
|
||||
|
||||
}
|
||||
// remember the position for drag detection
|
||||
_justEditedVoxel = true;
|
||||
}
|
||||
|
||||
|
||||
void Application::eyedropperVoxelUnderCursor() {
|
||||
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
if (selectedNode && selectedNode->isColored()) {
|
||||
|
@ -3821,6 +3900,7 @@ void Application::resetSensors() {
|
|||
_mouseY = _glWidget->height() / 2;
|
||||
|
||||
_faceshift.reset();
|
||||
_visage.reset();
|
||||
|
||||
if (OculusManager::isConnected()) {
|
||||
OculusManager::reset();
|
||||
|
@ -4138,6 +4218,10 @@ void Application::loadScript(const QString& fileNameString) {
|
|||
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater()));
|
||||
|
||||
ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();
|
||||
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
|
||||
|
||||
scriptEngine->registerGlobalObject("Overlays", &_overlays);
|
||||
|
||||
QThread* workerThread = new QThread(this);
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "avatar/Profile.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
#include "devices/Visage.h"
|
||||
#include "renderer/AmbientOcclusionEffect.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
#include "renderer/GlowEffect.h"
|
||||
|
@ -160,6 +161,7 @@ public:
|
|||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
Faceshift* getFaceshift() { return &_faceshift; }
|
||||
Visage* getVisage() { return &_visage; }
|
||||
SixenseManager* getSixenseManager() { return &_sixenseManager; }
|
||||
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
||||
QSettings* getSettings() { return _settings; }
|
||||
|
@ -191,6 +193,9 @@ public:
|
|||
|
||||
const glm::mat4& getShadowMatrix() const { return _shadowMatrix; }
|
||||
|
||||
void getModelViewMatrix(glm::dmat4* modelViewMatrix);
|
||||
void getProjectionMatrix(glm::dmat4* projectionMatrix);
|
||||
|
||||
/// Computes the off-axis frustum parameters for the view frustum, taking mirroring into account.
|
||||
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
|
||||
|
@ -222,13 +227,20 @@ public slots:
|
|||
void nodeKilled(SharedNodePointer node);
|
||||
void packetSent(quint64 length);
|
||||
|
||||
void exportVoxels();
|
||||
void importVoxels();
|
||||
void cutVoxels();
|
||||
void copyVoxels();
|
||||
void pasteVoxels();
|
||||
void nudgeVoxels();
|
||||
void deleteVoxels();
|
||||
void exportVoxels();
|
||||
void importVoxels();
|
||||
void nudgeVoxels();
|
||||
|
||||
void cutVoxels(const VoxelDetail& sourceVoxel);
|
||||
void copyVoxels(const VoxelDetail& sourceVoxel);
|
||||
void pasteVoxels(const VoxelDetail& sourceVoxel);
|
||||
void deleteVoxels(const VoxelDetail& sourceVoxel);
|
||||
void exportVoxels(const VoxelDetail& sourceVoxel);
|
||||
void nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec);
|
||||
|
||||
void setRenderVoxels(bool renderVoxels);
|
||||
void doKillLocalVoxels();
|
||||
|
@ -283,6 +295,7 @@ private:
|
|||
// Various helper functions called during update()
|
||||
void updateMouseRay();
|
||||
void updateFaceshift();
|
||||
void updateVisage();
|
||||
void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot);
|
||||
void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face);
|
||||
void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face);
|
||||
|
@ -322,6 +335,7 @@ private:
|
|||
|
||||
bool maybeEditVoxelUnderCursor();
|
||||
void deleteVoxelUnderCursor();
|
||||
void deleteVoxelAt(const VoxelDetail& voxel);
|
||||
void eyedropperVoxelUnderCursor();
|
||||
|
||||
void setMenuShortcutsEnabled(bool enabled);
|
||||
|
@ -382,6 +396,7 @@ private:
|
|||
Profile _profile; // The data-server linked profile for this user
|
||||
|
||||
Faceshift _faceshift;
|
||||
Visage _visage;
|
||||
|
||||
SixenseManager _sixenseManager;
|
||||
QStringList _activeScripts;
|
||||
|
@ -394,6 +409,7 @@ private:
|
|||
|
||||
glm::mat4 _untranslatedViewMatrix;
|
||||
glm::vec3 _viewMatrixTranslation;
|
||||
glm::mat4 _projectionMatrix;
|
||||
|
||||
glm::mat4 _shadowMatrix;
|
||||
|
||||
|
|
|
@ -467,8 +467,8 @@ void Audio::handleAudioInput() {
|
|||
|
||||
if (audioMixer && audioMixer->getActiveSocket()) {
|
||||
MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar();
|
||||
glm::vec3 headPosition = interfaceAvatar->getHead().getPosition();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead().getOrientation();
|
||||
glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead()->getOrientation();
|
||||
|
||||
// we need the amount of bytes in the buffer + 1 for type
|
||||
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
#include "Menu.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_DISTANCE = 0.0f;
|
||||
const float CAMERA_FIRST_PERSON_MODE_TIGHTNESS = 100.0f;
|
||||
|
@ -57,7 +55,7 @@ Camera::Camera() :
|
|||
_newTightness(0.0f),
|
||||
_modeShift(1.0f),
|
||||
_linearModeShift(0.0f),
|
||||
_modeShiftRate(1.0f),
|
||||
_modeShiftPeriod(1.0f),
|
||||
_scale(1.0f),
|
||||
_lookingAt(0.0f, 0.0f, 0.0f),
|
||||
_isKeepLookingAt(false)
|
||||
|
@ -75,18 +73,18 @@ void Camera::update(float deltaTime) {
|
|||
// use iterative forces to keep the camera at the desired position and angle
|
||||
void Camera::updateFollowMode(float deltaTime) {
|
||||
if (_linearModeShift < 1.0f) {
|
||||
_linearModeShift += _modeShiftRate * deltaTime;
|
||||
_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;
|
||||
|
||||
_linearModeShift += deltaTime / _modeShiftPeriod;
|
||||
if (_needsToInitialize || _linearModeShift > 1.0f) {
|
||||
_linearModeShift = 1.0f;
|
||||
_modeShift = 1.0f;
|
||||
_upShift = _newUpShift;
|
||||
_distance = _newDistance;
|
||||
_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;
|
||||
}
|
||||
|
||||
void Camera::setModeShiftRate ( float rate ) {
|
||||
|
||||
_modeShiftRate = rate;
|
||||
|
||||
if (_modeShiftRate < CAMERA_MINIMUM_MODE_SHIFT_RATE ) {
|
||||
_modeShiftRate = CAMERA_MINIMUM_MODE_SHIFT_RATE;
|
||||
}
|
||||
void Camera::setModeShiftPeriod (float period) {
|
||||
const float MIN_PERIOD = 0.001f;
|
||||
const float MAX_PERIOD = 3.0f;
|
||||
_modeShiftPeriod = glm::clamp(period, MIN_PERIOD, MAX_PERIOD);
|
||||
}
|
||||
|
||||
void Camera::setMode(CameraMode m) {
|
||||
|
@ -307,7 +302,8 @@ void CameraScriptableObject::setMode(const QString& mode) {
|
|||
}
|
||||
if (currentMode != targetMode) {
|
||||
_camera->setMode(targetMode);
|
||||
_camera->setModeShiftRate(10.0f);
|
||||
const float DEFAULT_MODE_SHIFT_PERIOD = 0.5f; // half second
|
||||
_camera->setModeShiftPeriod(DEFAULT_MODE_SHIFT_PERIOD);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
void setTargetRotation(const glm::quat& rotation);
|
||||
|
||||
void setMode(CameraMode m);
|
||||
void setModeShiftRate(float r);
|
||||
void setModeShiftPeriod(float r);
|
||||
void setFieldOfView(float f);
|
||||
void setAspectRatio(float a);
|
||||
void setNearClip(float n);
|
||||
|
@ -109,7 +109,7 @@ private:
|
|||
float _newTightness;
|
||||
float _modeShift;
|
||||
float _linearModeShift;
|
||||
float _modeShiftRate;
|
||||
float _modeShiftPeriod;
|
||||
float _scale;
|
||||
|
||||
glm::vec3 _lookingAt;
|
||||
|
|
94
interface/src/ClipboardScriptingInterface.cpp
Normal file
94
interface/src/ClipboardScriptingInterface.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// ClipboardScriptingInterface.cpp
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Application.h"
|
||||
#include "ClipboardScriptingInterface.h"
|
||||
|
||||
ClipboardScriptingInterface::ClipboardScriptingInterface() {
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::cutVoxel(const VoxelDetail& sourceVoxel) {
|
||||
cutVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::cutVoxel(float x, float y, float z, float s) {
|
||||
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
|
||||
y / (float)TREE_SCALE,
|
||||
z / (float)TREE_SCALE,
|
||||
s / (float)TREE_SCALE };
|
||||
Application::getInstance()->cutVoxels(sourceVoxel);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::copyVoxel(const VoxelDetail& sourceVoxel) {
|
||||
copyVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::copyVoxel(float x, float y, float z, float s) {
|
||||
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
|
||||
y / (float)TREE_SCALE,
|
||||
z / (float)TREE_SCALE,
|
||||
s / (float)TREE_SCALE };
|
||||
Application::getInstance()->copyVoxels(sourceVoxel);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::pasteVoxel(const VoxelDetail& destinationVoxel) {
|
||||
pasteVoxel(destinationVoxel.x, destinationVoxel.y, destinationVoxel.z, destinationVoxel.s);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::pasteVoxel(float x, float y, float z, float s) {
|
||||
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
|
||||
y / (float)TREE_SCALE,
|
||||
z / (float)TREE_SCALE,
|
||||
s / (float)TREE_SCALE };
|
||||
|
||||
Application::getInstance()->pasteVoxels(sourceVoxel);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::deleteVoxel(const VoxelDetail& sourceVoxel) {
|
||||
deleteVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::deleteVoxel(float x, float y, float z, float s) {
|
||||
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
|
||||
y / (float)TREE_SCALE,
|
||||
z / (float)TREE_SCALE,
|
||||
s / (float)TREE_SCALE };
|
||||
Application::getInstance()->deleteVoxels(sourceVoxel);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::exportVoxel(const VoxelDetail& sourceVoxel) {
|
||||
exportVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::exportVoxel(float x, float y, float z, float s) {
|
||||
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
|
||||
y / (float)TREE_SCALE,
|
||||
z / (float)TREE_SCALE,
|
||||
s / (float)TREE_SCALE };
|
||||
|
||||
// TODO: should we be calling invokeMethod() in all these cases?
|
||||
Application::getInstance()->exportVoxels(sourceVoxel);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::importVoxels() {
|
||||
QMetaObject::invokeMethod(Application::getInstance(), "importVoxels");
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) {
|
||||
nudgeVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s, nudgeVec);
|
||||
}
|
||||
|
||||
void ClipboardScriptingInterface::nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec) {
|
||||
glm::vec3 nudgeVecInTreeSpace = nudgeVec / (float)TREE_SCALE;
|
||||
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
|
||||
y / (float)TREE_SCALE,
|
||||
z / (float)TREE_SCALE,
|
||||
s / (float)TREE_SCALE };
|
||||
|
||||
Application::getInstance()->nudgeVoxelsByVector(sourceVoxel, nudgeVecInTreeSpace);
|
||||
}
|
||||
|
43
interface/src/ClipboardScriptingInterface.h
Normal file
43
interface/src/ClipboardScriptingInterface.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// ClipboardScriptingInterface.h
|
||||
// interface
|
||||
//
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Scriptable interface for the Application clipboard
|
||||
//
|
||||
|
||||
#ifndef __interface__Clipboard__
|
||||
#define __interface__Clipboard__
|
||||
|
||||
#include <QObject>
|
||||
#include <VoxelDetail.h>
|
||||
|
||||
class ClipboardScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClipboardScriptingInterface();
|
||||
|
||||
public slots:
|
||||
void cutVoxel(const VoxelDetail& sourceVoxel);
|
||||
void cutVoxel(float x, float y, float z, float s);
|
||||
|
||||
void copyVoxel(const VoxelDetail& sourceVoxel);
|
||||
void copyVoxel(float x, float y, float z, float s);
|
||||
|
||||
void pasteVoxel(const VoxelDetail& destinationVoxel);
|
||||
void pasteVoxel(float x, float y, float z, float s);
|
||||
|
||||
void deleteVoxel(const VoxelDetail& sourceVoxel);
|
||||
void deleteVoxel(float x, float y, float z, float s);
|
||||
|
||||
void exportVoxel(const VoxelDetail& sourceVoxel);
|
||||
void exportVoxel(float x, float y, float z, float s);
|
||||
|
||||
void importVoxels();
|
||||
|
||||
void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec);
|
||||
void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec);
|
||||
};
|
||||
|
||||
#endif // __interface__Clipboard__
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ public slots:
|
|||
virtual void captureJoystick(int joystickIndex);
|
||||
virtual void releaseJoystick(int joystickIndex);
|
||||
|
||||
virtual glm::vec2 getViewportDimensions() const;
|
||||
|
||||
private:
|
||||
const PalmData* getPrimaryPalm() const;
|
||||
const PalmData* getPalm(int palmIndex) const;
|
||||
|
|
|
@ -16,14 +16,12 @@
|
|||
|
||||
const QString WINDOW_NAME = QObject::tr("Import Voxels");
|
||||
const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
|
||||
const QString LOADING_BUTTON_NAME = QObject::tr("Loading ...");
|
||||
const QString PLACE_BUTTON_NAME = QObject::tr("Place voxels");
|
||||
const QString IMPORT_INFO = QObject::tr("<b>Import</b> %1 as voxels");
|
||||
const QString CANCEL_BUTTON_NAME = QObject::tr("Cancel");
|
||||
const QString INFO_LABEL_TEXT = QObject::tr("<div style='line-height:20px;'>"
|
||||
"This will load the selected file into Hifi and allow you<br/>"
|
||||
"to place it with %1-V; you must be in select or<br/>"
|
||||
"add mode (S or V keys will toggle mode) to place.</div>");
|
||||
|
||||
const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
const QString DOWNLOAD_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||
const int SHORT_FILE_EXTENSION = 4;
|
||||
const int SECOND_INDEX_LETTER = 1;
|
||||
|
||||
|
@ -66,7 +64,7 @@ QIcon HiFiIconProvider::icon(const QFileInfo &info) const {
|
|||
if (info.isDir()) {
|
||||
if (info.absoluteFilePath() == QDir::homePath()) {
|
||||
return QIcon("resources/icons/home.svg");
|
||||
} else if (info.absoluteFilePath() == DESKTOP_LOCATION) {
|
||||
} else if (info.absoluteFilePath() == QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)) {
|
||||
return QIcon("resources/icons/desktop.svg");
|
||||
} else if (info.absoluteFilePath() == QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) {
|
||||
return QIcon("resources/icons/documents.svg");
|
||||
|
@ -95,108 +93,114 @@ QString HiFiIconProvider::type(const QFileInfo &info) const {
|
|||
}
|
||||
|
||||
ImportDialog::ImportDialog(QWidget* parent) :
|
||||
QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, NULL),
|
||||
QFileDialog(parent, WINDOW_NAME, DOWNLOAD_LOCATION, NULL),
|
||||
_progressBar(this),
|
||||
_importButton(IMPORT_BUTTON_NAME, this),
|
||||
_cancelButton(CANCEL_BUTTON_NAME, this),
|
||||
fileAccepted(false) {
|
||||
_mode(importMode) {
|
||||
|
||||
setOption(QFileDialog::DontUseNativeDialog, true);
|
||||
setFileMode(QFileDialog::ExistingFile);
|
||||
setViewMode(QFileDialog::Detail);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString cmdString = ("Command");
|
||||
#else
|
||||
QString cmdString = ("Control");
|
||||
#endif
|
||||
QLabel* infoLabel = new QLabel(QString(INFO_LABEL_TEXT).arg(cmdString));
|
||||
infoLabel->setObjectName("infoLabel");
|
||||
|
||||
QGridLayout* gridLayout = (QGridLayout*) layout();
|
||||
gridLayout->addWidget(infoLabel, 2, 0, 2, 1);
|
||||
gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
|
||||
gridLayout->addWidget(&_importButton, 2, 2, 2, 1);
|
||||
|
||||
setImportTypes();
|
||||
setLayout();
|
||||
|
||||
connect(&_importButton, SIGNAL(pressed()), SLOT(import()));
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
|
||||
connect(&_cancelButton, SIGNAL(pressed()), SLOT(close()));
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
}
|
||||
|
||||
ImportDialog::~ImportDialog() {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void ImportDialog::import() {
|
||||
fileAccepted = true;
|
||||
emit accepted();
|
||||
}
|
||||
|
||||
void ImportDialog::accept() {
|
||||
// do nothing if import is not enable
|
||||
if (!_importButton.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
_progressBar.setRange(0, 100);
|
||||
|
||||
if (!fileAccepted) {
|
||||
fileAccepted = true;
|
||||
emit accepted();
|
||||
} else {
|
||||
QFileDialog::accept();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::reject() {
|
||||
QFileDialog::reject();
|
||||
}
|
||||
|
||||
int ImportDialog::exec() {
|
||||
// deselect selected file
|
||||
selectFile(" ");
|
||||
return QFileDialog::exec();
|
||||
connect(&_importButton, SIGNAL(pressed()), SLOT(accept()));
|
||||
connect(&_cancelButton, SIGNAL(pressed()), SIGNAL(canceled()));
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
}
|
||||
|
||||
void ImportDialog::reset() {
|
||||
_importButton.setEnabled(false);
|
||||
setMode(importMode);
|
||||
_progressBar.setValue(0);
|
||||
}
|
||||
|
||||
void ImportDialog::saveCurrentFile(QString filename) {
|
||||
if (!filename.isEmpty() && QFileInfo(filename).isFile()) {
|
||||
_currentFile = filename;
|
||||
_importButton.setEnabled(true);
|
||||
} else {
|
||||
_currentFile.clear();
|
||||
_importButton.setEnabled(false);
|
||||
void ImportDialog::setMode(dialogMode mode) {
|
||||
_mode = mode;
|
||||
|
||||
switch (_mode) {
|
||||
case loadingMode:
|
||||
_importButton.setEnabled(false);
|
||||
_importButton.setText(LOADING_BUTTON_NAME);
|
||||
findChild<QWidget*>("sidebar")->setEnabled(false);
|
||||
findChild<QWidget*>("treeView")->setEnabled(false);
|
||||
findChild<QWidget*>("backButton")->setEnabled(false);
|
||||
findChild<QWidget*>("forwardButton")->setEnabled(false);
|
||||
findChild<QWidget*>("toParentButton")->setEnabled(false);
|
||||
break;
|
||||
case placeMode:
|
||||
_progressBar.setValue(100);
|
||||
_importButton.setEnabled(true);
|
||||
_importButton.setText(PLACE_BUTTON_NAME);
|
||||
findChild<QWidget*>("sidebar")->setEnabled(false);
|
||||
findChild<QWidget*>("treeView")->setEnabled(false);
|
||||
findChild<QWidget*>("backButton")->setEnabled(false);
|
||||
findChild<QWidget*>("forwardButton")->setEnabled(false);
|
||||
findChild<QWidget*>("toParentButton")->setEnabled(false);
|
||||
break;
|
||||
case importMode:
|
||||
default:
|
||||
_progressBar.setValue(0);
|
||||
_importButton.setEnabled(true);
|
||||
_importButton.setText(IMPORT_BUTTON_NAME);
|
||||
findChild<QWidget*>("sidebar")->setEnabled(true);
|
||||
findChild<QWidget*>("treeView")->setEnabled(true);
|
||||
findChild<QWidget*>("backButton")->setEnabled(true);
|
||||
findChild<QWidget*>("forwardButton")->setEnabled(true);
|
||||
findChild<QWidget*>("toParentButton")->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::setLayout() {
|
||||
void ImportDialog::setProgressBarValue(int value) {
|
||||
_progressBar.setValue(value);
|
||||
}
|
||||
|
||||
void ImportDialog::accept() {
|
||||
emit accepted();
|
||||
}
|
||||
|
||||
void ImportDialog::saveCurrentFile(QString filename) {
|
||||
_currentFile = QFileInfo(filename).isFile() ? filename : "";
|
||||
}
|
||||
|
||||
void ImportDialog::setLayout() {
|
||||
QGridLayout* gridLayout = (QGridLayout*) layout();
|
||||
gridLayout->addWidget(&_progressBar, 2, 0, 2, 1);
|
||||
gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
|
||||
gridLayout->addWidget(&_importButton, 2, 2, 2, 1);
|
||||
|
||||
// set ObjectName used in qss for styling
|
||||
_progressBar.setObjectName("progressBar");
|
||||
_importButton.setObjectName("importButton");
|
||||
_cancelButton.setObjectName("cancelButton");
|
||||
|
||||
// set fixed size
|
||||
_importButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
_cancelButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
_cancelButton.setFlat(true);
|
||||
int progressBarHeight = 7;
|
||||
_progressBar.setFixedHeight(progressBarHeight);
|
||||
_progressBar.setTextVisible(false);
|
||||
|
||||
QSize BUTTON_SIZE = QSize(43, 33);
|
||||
QPushButton* button = (QPushButton*) findChild<QWidget*>("backButton");
|
||||
button->setIcon(QIcon());
|
||||
button->setFixedSize(BUTTON_SIZE);
|
||||
button = (QPushButton*) findChild<QWidget*>("forwardButton");
|
||||
button->setIcon(QIcon());
|
||||
button->setFixedSize(BUTTON_SIZE);
|
||||
button = (QPushButton*) findChild<QWidget*>("toParentButton");
|
||||
button->setIcon(QIcon());
|
||||
button->setFixedSize(BUTTON_SIZE);
|
||||
|
||||
// hide unused embedded widgets in QFileDialog
|
||||
QWidget* widget = findChild<QWidget*>("lookInCombo");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("backButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("forwardButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("toParentButton");
|
||||
widget->hide();
|
||||
|
||||
|
||||
widget = findChild<QWidget*>("newFolderButton");
|
||||
widget->hide();
|
||||
|
||||
|
@ -230,7 +234,7 @@ void ImportDialog::setLayout() {
|
|||
|
||||
widget = findChild<QWidget*>("treeView");
|
||||
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QFile styleSheet("resources/styles/import_dialog.qss");
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
|
@ -281,11 +285,6 @@ void ImportDialog::setImportTypes() {
|
|||
setIconProvider(new HiFiIconProvider(iconsMap));
|
||||
setNameFilter(importFormatsFilterList);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString cmdString = ("Command");
|
||||
#else
|
||||
QString cmdString = ("Control");
|
||||
#endif
|
||||
setLabelText(QFileDialog::LookIn, QString(IMPORT_INFO).arg(importFormatsInfo));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <QFileDialog>
|
||||
#include <QPushButton>
|
||||
#include <QProgressBar>
|
||||
#include <QLabel>
|
||||
#include <QFileIconProvider>
|
||||
#include <QHash>
|
||||
|
@ -26,37 +27,42 @@ public:
|
|||
QHash<QString, QString> iconsMap;
|
||||
};
|
||||
|
||||
enum dialogMode {
|
||||
importMode,
|
||||
loadingMode,
|
||||
placeMode
|
||||
};
|
||||
|
||||
class ImportDialog : public QFileDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ImportDialog(QWidget* parent = NULL);
|
||||
~ImportDialog();
|
||||
|
||||
void reset();
|
||||
|
||||
|
||||
QString getCurrentFile() const { return _currentFile; }
|
||||
|
||||
dialogMode getMode() const { return _mode; }
|
||||
void setMode(dialogMode mode);
|
||||
|
||||
signals:
|
||||
void accepted();
|
||||
|
||||
void canceled();
|
||||
|
||||
public slots:
|
||||
int exec();
|
||||
void import();
|
||||
void accept();
|
||||
void reject();
|
||||
void setProgressBarValue(int value);
|
||||
|
||||
private slots:
|
||||
void saveCurrentFile(QString);
|
||||
void accept();
|
||||
void saveCurrentFile(QString filename);
|
||||
|
||||
private:
|
||||
QString _currentFile;
|
||||
QProgressBar _progressBar;
|
||||
QPushButton _importButton;
|
||||
QPushButton _cancelButton;
|
||||
|
||||
dialogMode _mode;
|
||||
|
||||
void setLayout();
|
||||
void setImportTypes();
|
||||
bool fileAccepted;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ImportDialog__) */
|
||||
|
|
|
@ -327,7 +327,8 @@ Menu::Menu() :
|
|||
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
|
||||
|
||||
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,
|
||||
|
@ -786,7 +787,7 @@ void Menu::editPreferences() {
|
|||
QFormLayout* form = new QFormLayout();
|
||||
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);
|
||||
faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
faceURLEdit->setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString());
|
||||
|
@ -798,8 +799,13 @@ void Menu::editPreferences() {
|
|||
skeletonURLEdit->setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString());
|
||||
form->addRow("Skeleton URL:", skeletonURLEdit);
|
||||
|
||||
QString displayNameString = applicationInstance->getAvatar()->getDisplayName();
|
||||
QLineEdit* displayNameEdit = new QLineEdit(displayNameString);
|
||||
displayNameEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Display name:", displayNameEdit);
|
||||
|
||||
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);
|
||||
|
||||
QSlider* faceshiftEyeDeflection = new QSlider(Qt::Horizontal);
|
||||
|
@ -870,12 +876,19 @@ void Menu::editPreferences() {
|
|||
applicationInstance->getAvatar()->setSkeletonModelURL(skeletonModelURL);
|
||||
shouldDispatchIdentityPacket = true;
|
||||
}
|
||||
|
||||
QString displayNameStr(displayNameEdit->text());
|
||||
|
||||
if (displayNameStr != displayNameString) {
|
||||
applicationInstance->getAvatar()->setDisplayName(displayNameStr);
|
||||
shouldDispatchIdentityPacket = true;
|
||||
}
|
||||
|
||||
if (shouldDispatchIdentityPacket) {
|
||||
applicationInstance->getAvatar()->sendIdentityPacket();
|
||||
}
|
||||
|
||||
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
|
||||
applicationInstance->getAvatar()->getHead()->setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
|
||||
|
||||
_maxVoxels = maxVoxels->value();
|
||||
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);
|
||||
|
|
|
@ -185,7 +185,6 @@ namespace MenuOption {
|
|||
const QString Bandwidth = "Bandwidth Display";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString ChatCircling = "Chat Circling";
|
||||
const QString CollisionProxies = "Collision Proxies";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString CollideWithAvatars = "Collide With Avatars";
|
||||
const QString CollideWithParticles = "Collide With Particles";
|
||||
|
@ -268,6 +267,8 @@ namespace MenuOption {
|
|||
const QString Preferences = "Preferences...";
|
||||
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";
|
||||
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 ResetSwatchColors = "Reset Swatch Colors";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include <QFileInfo>
|
||||
#include <QThreadPool>
|
||||
|
||||
const QString SETTINGS_GROUP_NAME = "VoxelImport";
|
||||
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
|
||||
|
||||
class ImportTask : public QObject, public QRunnable {
|
||||
public:
|
||||
ImportTask(const QString &filename);
|
||||
|
@ -21,18 +24,16 @@ private:
|
|||
QString _filename;
|
||||
};
|
||||
|
||||
const QString SETTINGS_GROUP_NAME = "VoxelImport";
|
||||
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
|
||||
|
||||
VoxelImporter::VoxelImporter(QWidget* parent) :
|
||||
QObject(parent),
|
||||
_voxelTree(true),
|
||||
_importDialog(parent),
|
||||
_currentTask(NULL),
|
||||
_nextTask(NULL)
|
||||
_task(NULL),
|
||||
_didImport(false)
|
||||
{
|
||||
connect(&_importDialog, &QFileDialog::currentChanged, this, &VoxelImporter::preImport);
|
||||
connect(&_importDialog, &QFileDialog::accepted, this, &VoxelImporter::import);
|
||||
connect(&_voxelTree, SIGNAL(importProgress(int)), &_importDialog, SLOT(setProgressBarValue(int)));
|
||||
connect(&_importDialog, SIGNAL(canceled()), this, SLOT(cancel()));
|
||||
connect(&_importDialog, SIGNAL(accepted()), this, SLOT(import()));
|
||||
}
|
||||
|
||||
void VoxelImporter::saveSettings(QSettings* settings) {
|
||||
|
@ -41,145 +42,106 @@ void VoxelImporter::saveSettings(QSettings* settings) {
|
|||
settings->endGroup();
|
||||
}
|
||||
|
||||
void VoxelImporter::init(QSettings* settings) {
|
||||
void VoxelImporter::loadSettings(QSettings* settings) {
|
||||
settings->beginGroup(SETTINGS_GROUP_NAME);
|
||||
_importDialog.restoreState(settings->value(IMPORT_DIALOG_SETTINGS_KEY).toByteArray());
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
VoxelImporter::~VoxelImporter() {
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
_nextTask = NULL;
|
||||
}
|
||||
|
||||
if (_currentTask) {
|
||||
disconnect(_currentTask, 0, 0, 0);
|
||||
_voxelTree.cancelImport();
|
||||
_currentTask = NULL;
|
||||
}
|
||||
cleanupTask();
|
||||
}
|
||||
|
||||
void VoxelImporter::reset() {
|
||||
_voxelTree.eraseAllOctreeElements();
|
||||
_importDialog.reset();
|
||||
_filename = "";
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
_nextTask = NULL;
|
||||
}
|
||||
|
||||
if (_currentTask) {
|
||||
_voxelTree.cancelImport();
|
||||
}
|
||||
cleanupTask();
|
||||
}
|
||||
|
||||
int VoxelImporter::exec() {
|
||||
reset();
|
||||
|
||||
int ret = _importDialog.exec();
|
||||
|
||||
if (!ret) {
|
||||
reset();
|
||||
} else {
|
||||
_importDialog.reset();
|
||||
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
|
||||
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
|
||||
Application::getInstance()->getClipboard(),
|
||||
true);
|
||||
voxelSystem->changeTree(Application::getInstance()->getClipboard());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int VoxelImporter::preImport() {
|
||||
QString filename = _importDialog.getCurrentFile();
|
||||
|
||||
if (!QFileInfo(filename).isFile()) {
|
||||
return 0;
|
||||
}
|
||||
_importDialog.exec();
|
||||
|
||||
_filename = filename;
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
}
|
||||
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VoxelImporter::import() {
|
||||
QString filename = _importDialog.getCurrentFile();
|
||||
|
||||
if (!QFileInfo(filename).isFile()) {
|
||||
_importDialog.reject();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_filename == filename) {
|
||||
if (_currentTask) {
|
||||
connect(_currentTask, SIGNAL(destroyed()), &_importDialog, SLOT(accept()));
|
||||
} else {
|
||||
_importDialog.accept();
|
||||
}
|
||||
if (!_didImport) {
|
||||
// if the import is rejected, we make sure to cleanup before leaving
|
||||
cleanupTask();
|
||||
return 1;
|
||||
}
|
||||
|
||||
_filename = filename;
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
}
|
||||
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
connect(_nextTask, SIGNAL(destroyed()), &_importDialog, SLOT(accept()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
_didImport = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VoxelImporter::launchTask() {
|
||||
if (_nextTask != NULL) {
|
||||
_currentTask = _nextTask;
|
||||
_nextTask = NULL;
|
||||
void VoxelImporter::import() {
|
||||
switch (_importDialog.getMode()) {
|
||||
case loadingMode:
|
||||
_importDialog.setMode(placeMode);
|
||||
return;
|
||||
case placeMode:
|
||||
// Means the user chose to import
|
||||
_didImport = true;
|
||||
_importDialog.close();
|
||||
return;
|
||||
case importMode:
|
||||
default:
|
||||
QString filename = _importDialog.getCurrentFile();
|
||||
// if it's not a file, we ignore the call
|
||||
if (!QFileInfo(filename).isFile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's prepare the dialog window for import
|
||||
_importDialog.setMode(loadingMode);
|
||||
|
||||
// If not already done, we switch to the local tree
|
||||
if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) {
|
||||
Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree);
|
||||
}
|
||||
|
||||
// Creation and launch of the import task on the thread pool
|
||||
_task = new ImportTask(filename);
|
||||
connect(_task, SIGNAL(destroyed()), SLOT(import()));
|
||||
QThreadPool::globalInstance()->start(_task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) {
|
||||
Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree);
|
||||
}
|
||||
void VoxelImporter::cancel() {
|
||||
switch (_importDialog.getMode()) {
|
||||
case loadingMode:
|
||||
disconnect(_task, 0, 0, 0);
|
||||
cleanupTask();
|
||||
case placeMode:
|
||||
_importDialog.setMode(importMode);
|
||||
break;
|
||||
case importMode:
|
||||
default:
|
||||
_importDialog.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QThreadPool::globalInstance()->start(_currentTask);
|
||||
} else {
|
||||
_currentTask = NULL;
|
||||
void VoxelImporter::cleanupTask() {
|
||||
// If a task is running, we cancel it and put the pointer to null
|
||||
if (_task) {
|
||||
_task = NULL;
|
||||
_voxelTree.cancelImport();
|
||||
}
|
||||
}
|
||||
|
||||
ImportTask::ImportTask(const QString &filename)
|
||||
: _filename(filename) {
|
||||
: _filename(filename)
|
||||
{
|
||||
setAutoDelete(true);
|
||||
}
|
||||
|
||||
void ImportTask::run() {
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
// We start by cleaning up the shared voxel system just in case
|
||||
voxelSystem->killLocalVoxels();
|
||||
|
||||
// Then we call the righ method for the job
|
||||
if (_filename.endsWith(".png", Qt::CaseInsensitive)) {
|
||||
voxelSystem->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data());
|
||||
} else if (_filename.endsWith(".svo", Qt::CaseInsensitive)) {
|
||||
|
@ -187,8 +149,10 @@ void ImportTask::run() {
|
|||
} else if (_filename.endsWith(".schematic", Qt::CaseInsensitive)) {
|
||||
voxelSystem->readFromSchematicFile(_filename.toLocal8Bit().data());
|
||||
} else {
|
||||
qDebug("[ERROR] Invalid file extension.");
|
||||
// We should never get here.
|
||||
qDebug() << "[ERROR] Invalid file extension." << endl;
|
||||
}
|
||||
|
||||
|
||||
// Here we reaverage the tree so that he is ready for preview
|
||||
voxelSystem->getTree()->reaverageOctreeElements();
|
||||
}
|
||||
|
|
|
@ -23,28 +23,25 @@ public:
|
|||
VoxelImporter(QWidget* parent = NULL);
|
||||
~VoxelImporter();
|
||||
|
||||
void init(QSettings* settings);
|
||||
void reset();
|
||||
void loadSettings(QSettings* settings);
|
||||
void saveSettings(QSettings* settings);
|
||||
|
||||
VoxelTree* getVoxelTree() { return &_voxelTree; }
|
||||
|
||||
public slots:
|
||||
int exec();
|
||||
int preImport();
|
||||
int import();
|
||||
|
||||
private slots:
|
||||
void launchTask();
|
||||
void import();
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
VoxelTree _voxelTree;
|
||||
ImportDialog _importDialog;
|
||||
|
||||
QString _filename;
|
||||
|
||||
ImportTask* _currentTask;
|
||||
ImportTask* _nextTask;
|
||||
ImportTask* _task;
|
||||
bool _didImport;
|
||||
|
||||
void cleanupTask();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelImporter__) */
|
||||
|
|
|
@ -2382,7 +2382,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) {
|
|||
bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraData) {
|
||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
||||
hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData;
|
||||
|
||||
|
||||
// If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine
|
||||
// how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not
|
||||
// consider that case.
|
||||
|
@ -2418,7 +2418,7 @@ bool VoxelSystem::hideAllSubTreeOperation(OctreeElement* element, void* extraDat
|
|||
bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraData) {
|
||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
||||
hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData;
|
||||
|
||||
|
||||
// If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine
|
||||
// how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not
|
||||
// consider that case.
|
||||
|
@ -2468,7 +2468,7 @@ bool VoxelSystem::showAllSubTreeOperation(OctreeElement* element, void* extraDat
|
|||
bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData) {
|
||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
||||
hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData;
|
||||
|
||||
|
||||
// If we're still recursing the tree using this operator, then we don't know if we're inside or outside...
|
||||
// so before we move forward we need to determine our frustum location
|
||||
ViewFrustum::location inFrustum = voxel->inFrustum(args->thisViewFrustum);
|
||||
|
@ -2485,7 +2485,6 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
|
|||
// ok, now do some processing for this node...
|
||||
switch (inFrustum) {
|
||||
case ViewFrustum::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
|
||||
// that all of it's children are outside both of our views, in which case we can just stop recursing...
|
||||
|
@ -2503,7 +2502,6 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
|
|||
|
||||
} break;
|
||||
case ViewFrustum::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
|
||||
// that all of it's children are INSIDE both of our views, in which case we can just stop recursing...
|
||||
|
@ -2517,12 +2515,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
|
||||
// the children and simply mark them as visible (as appropriate based on LOD)
|
||||
args->tree->recurseNodeWithOperation(voxel, showAllSubTreeOperation, args);
|
||||
|
||||
return false;
|
||||
} break;
|
||||
case ViewFrustum::INTERSECT: {
|
||||
args->nodesScanned++;
|
||||
|
||||
// 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
|
||||
// previously INSIDE and visible. So in this case stop recursing
|
||||
|
@ -2536,8 +2532,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 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
|
||||
|
||||
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()) {
|
||||
voxel->setDirtyBit(); // will this make it draw?
|
||||
voxel->markWithChangedTime(); // both are needed to force redraw
|
||||
args->nodesShown++;
|
||||
return false;
|
||||
}
|
||||
|
@ -2550,7 +2553,6 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
|
|||
} break;
|
||||
} // switch
|
||||
|
||||
|
||||
return true; // keep going!
|
||||
}
|
||||
|
||||
|
|
|
@ -56,11 +56,13 @@ const float HEAD_RATE_MAX = 50.f;
|
|||
const int NUM_BODY_CONE_SIDES = 9;
|
||||
const float CHAT_MESSAGE_SCALE = 0.0015f;
|
||||
const float CHAT_MESSAGE_HEIGHT = 0.1f;
|
||||
const float DISPLAYNAME_FADE_TIME = 0.5f;
|
||||
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
|
||||
const float DISPLAYNAME_ALPHA = 0.95f;
|
||||
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
|
||||
|
||||
Avatar::Avatar() :
|
||||
AvatarData(),
|
||||
_head(this),
|
||||
_hand(this),
|
||||
_skeletonModel(this),
|
||||
_bodyYawDelta(0.0f),
|
||||
_mode(AVATAR_MODE_STANDING),
|
||||
|
@ -81,18 +83,16 @@ Avatar::Avatar() :
|
|||
moveToThread(Application::getInstance()->thread());
|
||||
|
||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||
_headData = &_head;
|
||||
_handData = &_hand;
|
||||
_headData = static_cast<HeadData*>(new Head(this));
|
||||
_handData = static_cast<HandData*>(new Hand(this));
|
||||
}
|
||||
|
||||
Avatar::~Avatar() {
|
||||
_headData = NULL;
|
||||
_handData = NULL;
|
||||
}
|
||||
|
||||
void Avatar::init() {
|
||||
_head.init();
|
||||
_hand.init();
|
||||
getHead()->init();
|
||||
getHand()->init();
|
||||
_skeletonModel.init();
|
||||
_initialized = true;
|
||||
}
|
||||
|
@ -111,20 +111,21 @@ void Avatar::simulate(float deltaTime) {
|
|||
if (_scale != _targetScale) {
|
||||
setScale(_targetScale);
|
||||
}
|
||||
|
||||
|
||||
// copy velocity so we can use it later for acceleration
|
||||
glm::vec3 oldVelocity = getVelocity();
|
||||
|
||||
_hand.simulate(deltaTime, false);
|
||||
getHand()->simulate(deltaTime, false);
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
Head* head = getHead();
|
||||
head->setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _position;
|
||||
}
|
||||
_head.setPosition(headPosition);
|
||||
_head.setScale(_scale);
|
||||
_head.simulate(deltaTime, false);
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(_scale);
|
||||
getHead()->simulate(deltaTime, false);
|
||||
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
if (_speed + fabs(_bodyYawDelta) > 0.2) {
|
||||
|
@ -138,7 +139,23 @@ void Avatar::simulate(float deltaTime) {
|
|||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
||||
|
||||
// update animation for display name fade in/out
|
||||
if ( _displayNameTargetAlpha != _displayNameAlpha) {
|
||||
// the alpha function is
|
||||
// Fade out => alpha(t) = factor ^ t => alpha(t+dt) = alpha(t) * factor^(dt)
|
||||
// Fade in => alpha(t) = 1 - factor^t => alpha(t+dt) = 1-(1-alpha(t))*coef^(dt)
|
||||
// factor^(dt) = coef
|
||||
float coef = pow(DISPLAYNAME_FADE_FACTOR, deltaTime);
|
||||
if (_displayNameTargetAlpha < _displayNameAlpha) {
|
||||
// Fading out
|
||||
_displayNameAlpha *= coef;
|
||||
} else {
|
||||
// Fading in
|
||||
_displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef;
|
||||
}
|
||||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
||||
|
@ -146,33 +163,50 @@ void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
|||
_mouseRayDirection = direction;
|
||||
}
|
||||
|
||||
static TextRenderer* textRenderer() {
|
||||
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
return renderer;
|
||||
enum TextRendererType {
|
||||
CHAT,
|
||||
DISPLAYNAME
|
||||
};
|
||||
|
||||
static TextRenderer* textRenderer(TextRendererType type) {
|
||||
static TextRenderer* chatRenderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* displayNameRenderer = new TextRenderer(SANS_FONT_FAMILY, 12, -1, false, TextRenderer::NO_EFFECT);
|
||||
|
||||
switch(type) {
|
||||
case CHAT:
|
||||
return chatRenderer;
|
||||
case DISPLAYNAME:
|
||||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
void Avatar::render(bool forceRenderHead) {
|
||||
|
||||
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
||||
float lengthToTarget = glm::length(toTarget);
|
||||
|
||||
{
|
||||
// glow when moving in the distance
|
||||
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
||||
const float GLOW_DISTANCE = 5.0f;
|
||||
Glower glower(_moving && glm::length(toTarget) > GLOW_DISTANCE ? 1.0f : 0.0f);
|
||||
|
||||
// render body
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
|
||||
_skeletonModel.renderCollisionProxies(1.f);
|
||||
//_head.getFaceModel().renderCollisionProxies(0.5f);
|
||||
}
|
||||
const float GLOW_DISTANCE = 5.0f;
|
||||
Glower glower(_moving && lengthToTarget > GLOW_DISTANCE ? 1.0f : 0.0f);
|
||||
|
||||
// render body
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) {
|
||||
_skeletonModel.renderCollisionProxies(0.7f);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionProxies)) {
|
||||
getHead()->getFaceModel().renderCollisionProxies(0.7f);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
renderBody(forceRenderHead);
|
||||
}
|
||||
|
||||
|
||||
// render sphere when far away
|
||||
const float MAX_ANGLE = 10.f;
|
||||
float height = getHeight();
|
||||
glm::vec3 delta = height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f;
|
||||
float height = getSkeletonHeight();
|
||||
glm::vec3 delta = height * (getHead()->getCameraOrientation() * IDENTITY_UP) / 2.f;
|
||||
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
|
||||
|
||||
if (angle < MAX_ANGLE) {
|
||||
|
@ -180,21 +214,23 @@ void Avatar::render(bool forceRenderHead) {
|
|||
glPushMatrix();
|
||||
glTranslatef(_position.x, _position.y, _position.z);
|
||||
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();
|
||||
}
|
||||
}
|
||||
const float DISPLAYNAME_DISTANCE = 10.0f;
|
||||
setShowDisplayName(lengthToTarget < DISPLAYNAME_DISTANCE);
|
||||
renderDisplayName();
|
||||
|
||||
|
||||
if (!_chatMessage.empty()) {
|
||||
int width = 0;
|
||||
int lastWidth = 0;
|
||||
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
|
||||
width += (lastWidth = textRenderer()->computeWidth(*it));
|
||||
width += (lastWidth = textRenderer(CHAT)->computeWidth(*it));
|
||||
}
|
||||
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);
|
||||
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 chatAxis = glm::axis(chatRotation);
|
||||
|
@ -209,7 +245,7 @@ void Avatar::render(bool forceRenderHead) {
|
|||
glDisable(GL_LIGHTING);
|
||||
glDepthMask(false);
|
||||
if (_keyState == NO_KEY_DOWN) {
|
||||
textRenderer()->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
|
||||
} else {
|
||||
// rather than using substr and allocating a new string, just replace the last
|
||||
|
@ -217,10 +253,10 @@ void Avatar::render(bool forceRenderHead) {
|
|||
int lastIndex = _chatMessage.size() - 1;
|
||||
char lastChar = _chatMessage[lastIndex];
|
||||
_chatMessage[lastIndex] = '\0';
|
||||
textRenderer()->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
_chatMessage[lastIndex] = lastChar;
|
||||
glColor3f(0, 1, 0);
|
||||
textRenderer()->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex);
|
||||
textRenderer(CHAT)->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex);
|
||||
}
|
||||
glEnable(GL_LIGHTING);
|
||||
glDepthMask(true);
|
||||
|
@ -251,9 +287,105 @@ void Avatar::renderBody(bool forceRenderHead) {
|
|||
//printf("Render other at %.3f, %.2f, %.2f\n", pos.x, pos.y, pos.z);
|
||||
_skeletonModel.render(1.0f);
|
||||
if (forceRenderHead) {
|
||||
_head.render(1.0f);
|
||||
getHead()->render(1.0f);
|
||||
}
|
||||
_hand.render(false);
|
||||
getHand()->render(false);
|
||||
}
|
||||
|
||||
void Avatar::renderDisplayName() {
|
||||
|
||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glPushMatrix();
|
||||
glm::vec3 textPosition;
|
||||
getSkeletonModel().getNeckPosition(textPosition);
|
||||
textPosition += getBodyUpDirection() * getHeadHeight() * 1.1f;
|
||||
|
||||
glTranslatef(textPosition.x, textPosition.y, textPosition.z);
|
||||
|
||||
// we need "always facing camera": we must remove the camera rotation from the stack
|
||||
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
|
||||
|
||||
// We need to compute the scale factor such as the text remains with fixed size respect to window coordinates
|
||||
// We project a unit vector and check the difference in screen coordinates, to check which is the
|
||||
// correction scale needed
|
||||
// save the matrices for later scale correction factor
|
||||
glm::dmat4 modelViewMatrix;
|
||||
glm::dmat4 projectionMatrix;
|
||||
GLint viewportMatrix[4];
|
||||
Application::getInstance()->getModelViewMatrix(&modelViewMatrix);
|
||||
Application::getInstance()->getProjectionMatrix(&projectionMatrix);
|
||||
glGetIntegerv(GL_VIEWPORT, viewportMatrix);
|
||||
GLdouble result0[3], result1[3];
|
||||
|
||||
glm::dvec3 upVector(modelViewMatrix[1]);
|
||||
|
||||
glm::dvec3 testPoint0 = glm::dvec3(textPosition);
|
||||
glm::dvec3 testPoint1 = glm::dvec3(textPosition) + upVector;
|
||||
|
||||
bool success;
|
||||
success = gluProject(testPoint0.x, testPoint0.y, testPoint0.z,
|
||||
(GLdouble*)&modelViewMatrix, (GLdouble*)&projectionMatrix, viewportMatrix,
|
||||
&result0[0], &result0[1], &result0[2]);
|
||||
success = success &&
|
||||
gluProject(testPoint1.x, testPoint1.y, testPoint1.z,
|
||||
(GLdouble*)&modelViewMatrix, (GLdouble*)&projectionMatrix, viewportMatrix,
|
||||
&result1[0], &result1[1], &result1[2]);
|
||||
|
||||
if (success) {
|
||||
double textWindowHeight = abs(result1[1] - result0[1]);
|
||||
float scaleFactor = (textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f;
|
||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||
|
||||
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
||||
|
||||
int text_x = -_displayNameBoundingRect.width() / 2;
|
||||
int text_y = -_displayNameBoundingRect.height() / 2;
|
||||
|
||||
// draw a gray background
|
||||
int left = text_x + _displayNameBoundingRect.x();
|
||||
int right = left + _displayNameBoundingRect.width();
|
||||
int bottom = text_y + _displayNameBoundingRect.y();
|
||||
int top = bottom + _displayNameBoundingRect.height();
|
||||
const int border = 8;
|
||||
bottom -= border;
|
||||
left -= border;
|
||||
top += border;
|
||||
right += border;
|
||||
|
||||
// We are drawing coplanar textures with depth: need the polygon offset
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.0f, 1.0f);
|
||||
|
||||
glColor4f(0.2f, 0.2f, 0.2f, _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(left, bottom);
|
||||
glVertex2f(right, bottom);
|
||||
glVertex2f(right, top);
|
||||
glVertex2f(left, top);
|
||||
glEnd();
|
||||
|
||||
|
||||
glColor4f(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
|
||||
QByteArray ba = _displayName.toLocal8Bit();
|
||||
const char* text = ba.data();
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
textRenderer(DISPLAYNAME)->draw(text_x, text_y, text);
|
||||
|
||||
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
|
@ -262,7 +394,7 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
if (_skeletonModel.findRayIntersection(origin, direction, modelDistance)) {
|
||||
minDistance = qMin(minDistance, modelDistance);
|
||||
}
|
||||
if (_head.getFaceModel().findRayIntersection(origin, direction, modelDistance)) {
|
||||
if (getHead()->getFaceModel().findRayIntersection(origin, direction, modelDistance)) {
|
||||
minDistance = qMin(minDistance, modelDistance);
|
||||
}
|
||||
if (minDistance < FLT_MAX) {
|
||||
|
@ -277,7 +409,7 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet
|
|||
// Temporarily disabling collisions against the skeleton because the collision proxies up
|
||||
// near the neck are bad and prevent the hand from hitting the face.
|
||||
//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) {
|
||||
|
@ -356,7 +488,7 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
|
|||
void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
|
||||
AvatarData::setFaceModelURL(faceModelURL);
|
||||
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) {
|
||||
|
@ -365,6 +497,11 @@ void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) {
|
|||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL);
|
||||
}
|
||||
|
||||
void Avatar::setDisplayName(const QString& displayName) {
|
||||
AvatarData::setDisplayName(displayName);
|
||||
_displayNameBoundingRect = textRenderer(DISPLAYNAME)->metrics().tightBoundingRect(displayName);
|
||||
}
|
||||
|
||||
int Avatar::parseData(const QByteArray& packet) {
|
||||
// change in position implies movement
|
||||
glm::vec3 oldPosition = _position;
|
||||
|
@ -450,11 +587,16 @@ void Avatar::setScale(float scale) {
|
|||
}
|
||||
}
|
||||
|
||||
float Avatar::getHeight() const {
|
||||
float Avatar::getSkeletonHeight() const {
|
||||
Extents extents = _skeletonModel.getBindExtents();
|
||||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
float Avatar::getHeadHeight() const {
|
||||
Extents extents = getHead()->getFaceModel().getBindExtents();
|
||||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
return false;
|
||||
|
@ -467,7 +609,7 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
|||
return false;
|
||||
//return _skeletonModel.collisionHitsMoveableJoint(collision);
|
||||
}
|
||||
if (model == &(_head.getFaceModel())) {
|
||||
if (model == &(getHead()->getFaceModel())) {
|
||||
// ATM we always handle MODEL_COLLISIONS against the face.
|
||||
return true;
|
||||
}
|
||||
|
@ -480,8 +622,8 @@ void Avatar::applyCollision(CollisionInfo& collision) {
|
|||
}
|
||||
// TODO: make skeleton also respond to collisions
|
||||
Model* model = static_cast<Model*>(collision._data);
|
||||
if (model == &(_head.getFaceModel())) {
|
||||
_head.applyCollision(collision);
|
||||
if (model == &(getHead()->getFaceModel())) {
|
||||
getHead()->applyCollision(collision);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -490,6 +632,24 @@ float Avatar::getPelvisFloatingHeight() const {
|
|||
}
|
||||
|
||||
float Avatar::getPelvisToHeadLength() const {
|
||||
return glm::distance(_position, _head.getPosition());
|
||||
return glm::distance(_position, getHead()->getPosition());
|
||||
}
|
||||
|
||||
void Avatar::setShowDisplayName(bool showDisplayName) {
|
||||
// For myAvatar, the alpha update is not done (called in simulate for other avatars)
|
||||
if (Application::getInstance()->getAvatar() == this) {
|
||||
if (showDisplayName) {
|
||||
_displayNameAlpha = DISPLAYNAME_ALPHA;
|
||||
} else {
|
||||
_displayNameAlpha = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (showDisplayName) {
|
||||
_displayNameTargetAlpha = DISPLAYNAME_ALPHA;
|
||||
} else {
|
||||
_displayNameTargetAlpha = 0.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
void render(bool forceRenderHead);
|
||||
|
||||
//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);
|
||||
|
||||
//getters
|
||||
|
@ -83,8 +83,9 @@ public:
|
|||
glm::vec3 getChestPosition() const;
|
||||
float getScale() const { return _scale; }
|
||||
const glm::vec3& getVelocity() const { return _velocity; }
|
||||
Head& getHead() { return _head; }
|
||||
Hand& getHand() { return _hand; }
|
||||
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
||||
glm::quat getWorldAlignedOrientation() const;
|
||||
|
||||
Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); }
|
||||
|
@ -112,25 +113,28 @@ public:
|
|||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setDisplayName(const QString& displayName);
|
||||
|
||||
void setShowDisplayName(bool showDisplayName);
|
||||
|
||||
int parseData(const QByteArray& packet);
|
||||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
||||
|
||||
|
||||
/// \return true if we expect the avatar would move as a result of the collision
|
||||
bool collisionWouldMoveAvatar(CollisionInfo& collision) const;
|
||||
|
||||
/// \param collision a data structure for storing info about collisions against Models
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
|
||||
float getBoundingRadius() const { return 0.5f * getHeight(); }
|
||||
float getBoundingRadius() const { return 0.5f * getSkeletonHeight(); }
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
|
||||
protected:
|
||||
Head _head;
|
||||
Hand _hand;
|
||||
SkeletonModel _skeletonModel;
|
||||
float _bodyYawDelta;
|
||||
AvatarMode _mode;
|
||||
|
@ -155,7 +159,8 @@ protected:
|
|||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setScale(float scale);
|
||||
|
||||
float getHeight() const;
|
||||
float getSkeletonHeight() const;
|
||||
float getHeadHeight() const;
|
||||
float getPelvisFloatingHeight() const;
|
||||
float getPelvisToHeadLength() const;
|
||||
|
||||
|
@ -164,6 +169,8 @@ private:
|
|||
bool _initialized;
|
||||
|
||||
void renderBody(bool forceRenderHead);
|
||||
|
||||
void renderDisplayName();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
// Created by Stephen Birarda on 1/23/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
#include <string>
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <UUID.h>
|
||||
|
@ -71,6 +74,8 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
|
|||
"Application::renderAvatars()");
|
||||
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors);
|
||||
|
||||
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
|
@ -184,8 +189,9 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) {
|
|||
while (!identityStream.atEnd()) {
|
||||
|
||||
QUrl faceMeshURL, skeletonURL;
|
||||
identityStream >> nodeUUID >> faceMeshURL >> skeletonURL;
|
||||
|
||||
QString displayName;
|
||||
identityStream >> nodeUUID >> faceMeshURL >> skeletonURL >> displayName;
|
||||
|
||||
// mesh URL for a UUID, find avatar in our list
|
||||
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
|
||||
if (matchingAvatar) {
|
||||
|
@ -198,6 +204,10 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) {
|
|||
if (avatar->getSkeletonModelURL() != skeletonURL) {
|
||||
avatar->setSkeletonModelURL(skeletonURL);
|
||||
}
|
||||
|
||||
if (avatar->getDisplayName() != displayName) {
|
||||
avatar->setDisplayName(displayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,6 @@ bool FaceModel::render(float alpha) {
|
|||
if (!Model::render(alpha)) {
|
||||
return false;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) {
|
||||
renderCollisionProxies(alpha);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,41 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
}
|
||||
}
|
||||
|
||||
void Hand::playSlaps(PalmData& palm, Avatar* avatar)
|
||||
{
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
float palmCollisionDistance = 0.1f;
|
||||
bool wasColliding = palm.getIsCollidingWithPalm();
|
||||
palm.setIsCollidingWithPalm(false);
|
||||
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
|
||||
for (size_t j = 0; j < avatar->getHand()->getNumPalms(); j++) {
|
||||
PalmData& otherPalm = avatar->getHand()->getPalms()[j];
|
||||
if (!otherPalm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 otherPalmPosition = otherPalm.getPosition();
|
||||
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
|
||||
palm.setIsCollidingWithPalm(true);
|
||||
if (!wasColliding) {
|
||||
const float PALM_COLLIDE_VOLUME = 1.f;
|
||||
const float PALM_COLLIDE_FREQUENCY = 1000.f;
|
||||
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
|
||||
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
|
||||
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
|
||||
PALM_COLLIDE_FREQUENCY,
|
||||
PALM_COLLIDE_DURATION_MAX,
|
||||
PALM_COLLIDE_DECAY_PER_SAMPLE);
|
||||
// If the other person's palm is in motion, move mine downward to show I was hit
|
||||
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
|
||||
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
|
||||
// add slapback here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -139,41 +174,12 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
|||
PalmData& palm = getPalms()[i];
|
||||
if (!palm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 totalPenetration;
|
||||
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
float palmCollisionDistance = 0.1f;
|
||||
bool wasColliding = palm.getIsCollidingWithPalm();
|
||||
palm.setIsCollidingWithPalm(false);
|
||||
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
|
||||
for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) {
|
||||
PalmData& otherPalm = avatar->getHand().getPalms()[j];
|
||||
if (!otherPalm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 otherPalmPosition = otherPalm.getPosition();
|
||||
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
|
||||
palm.setIsCollidingWithPalm(true);
|
||||
if (!wasColliding) {
|
||||
const float PALM_COLLIDE_VOLUME = 1.f;
|
||||
const float PALM_COLLIDE_FREQUENCY = 1000.f;
|
||||
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
|
||||
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
|
||||
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
|
||||
PALM_COLLIDE_FREQUENCY,
|
||||
PALM_COLLIDE_DURATION_MAX,
|
||||
PALM_COLLIDE_DECAY_PER_SAMPLE);
|
||||
// If the other person's palm is in motion, move mine downward to show I was hit
|
||||
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
|
||||
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
|
||||
// add slapback here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
playSlaps(palm, avatar);
|
||||
}
|
||||
|
||||
glm::vec3 totalPenetration;
|
||||
handCollisions.clear();
|
||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions)) {
|
||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
|
@ -307,7 +313,8 @@ void Hand::render(bool isMine) {
|
|||
_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++) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
if (!palm.isActive()) {
|
||||
|
|
|
@ -93,6 +93,8 @@ private:
|
|||
void calculateGeometry();
|
||||
|
||||
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
|
||||
|
||||
void playSlaps(PalmData& palm, Avatar* avatar);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -62,24 +62,20 @@ void Head::simulate(float deltaTime, bool isMine) {
|
|||
|
||||
// Update audio trailing average for rendering facial animations
|
||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||
Visage* visage = Application::getInstance()->getVisage();
|
||||
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()) {
|
||||
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) {
|
||||
if (!_isFaceshiftConnected) {
|
||||
// Update eye saccades
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
|
||||
|
|
|
@ -53,7 +53,6 @@ MyAvatar::MyAvatar() :
|
|||
_elapsedTimeSinceCollision(0.0f),
|
||||
_lastCollisionPosition(0, 0, 0),
|
||||
_speedBrakes(false),
|
||||
_isCollisionsOn(true),
|
||||
_isThrustOn(false),
|
||||
_thrustMultiplier(1.0f),
|
||||
_moveTarget(0,0,0),
|
||||
|
@ -73,8 +72,8 @@ void MyAvatar::reset() {
|
|||
// TODO? resurrect headMouse stuff?
|
||||
//_headMouseX = _glWidget->width() / 2;
|
||||
//_headMouseY = _glWidget->height() / 2;
|
||||
_head.reset();
|
||||
_hand.reset();
|
||||
getHead()->reset();
|
||||
getHand()->reset();
|
||||
|
||||
setVelocity(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());
|
||||
}
|
||||
|
||||
Head* head = getHead();
|
||||
if (OculusManager::isConnected()) {
|
||||
float yaw, pitch, roll;
|
||||
OculusManager::getEulerAngles(yaw, pitch, roll);
|
||||
|
||||
_head.setYaw(yaw);
|
||||
_head.setPitch(pitch);
|
||||
_head.setRoll(roll);
|
||||
head->setYaw(yaw);
|
||||
head->setPitch(pitch);
|
||||
head->setRoll(roll);
|
||||
}
|
||||
|
||||
// Get audio loudness data from audio input device
|
||||
Audio* audio = Application::getInstance()->getAudio();
|
||||
_head.setAudioLoudness(audio->getLastInputLoudness());
|
||||
_head.setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
|
||||
head->setAudioLoudness(audio->getLastInputLoudness());
|
||||
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) {
|
||||
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
|
||||
|
@ -198,7 +198,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
if (_collisionFlags != 0) {
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getHeight() * COLLISION_RADIUS_SCALE;
|
||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
|
||||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
|
@ -267,7 +267,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
if (!Application::getInstance()->getFaceshift()->isActive() && OculusManager::isConnected() &&
|
||||
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
|
||||
// and this acceleration is above the pull threshold
|
||||
|
@ -277,7 +277,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_bodyYaw = getAbsoluteHeadYaw();
|
||||
|
||||
// set the head yaw to zero for this draw
|
||||
_head.setYaw(0);
|
||||
getHead()->setYaw(0);
|
||||
|
||||
// correct the oculus yaw offset
|
||||
OculusManager::updateYawOffset();
|
||||
|
@ -315,17 +315,20 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_position += _velocity * deltaTime;
|
||||
|
||||
// update avatar skeleton and simulate hand and head
|
||||
_hand.collideAgainstOurself();
|
||||
_hand.simulate(deltaTime, true);
|
||||
getHand()->collideAgainstOurself();
|
||||
getHand()->simulate(deltaTime, true);
|
||||
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
|
||||
Head* head = getHead();
|
||||
head->setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _position;
|
||||
}
|
||||
_head.setPosition(headPosition);
|
||||
_head.setScale(_scale);
|
||||
_head.simulate(deltaTime, true);
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(_scale);
|
||||
head->simulate(deltaTime, true);
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
@ -337,22 +340,34 @@ const float MAX_PITCH = 90.0f;
|
|||
// Update avatar head rotation with sensor data
|
||||
void MyAvatar::updateFromGyros(float deltaTime) {
|
||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||
Visage* visage = Application::getInstance()->getVisage();
|
||||
glm::vec3 estimatedPosition, estimatedRotation;
|
||||
|
||||
bool trackerActive = false;
|
||||
if (faceshift->isActive()) {
|
||||
estimatedPosition = faceshift->getHeadTranslation();
|
||||
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
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) {
|
||||
const float FACESHIFT_YAW_TURN_SENSITIVITY = 0.5f;
|
||||
const float FACESHIFT_MIN_YAW_TURN = 15.f;
|
||||
const float FACESHIFT_MAX_YAW_TURN = 50.f;
|
||||
if ( (fabs(estimatedRotation.y) > FACESHIFT_MIN_YAW_TURN) &&
|
||||
(fabs(estimatedRotation.y) < FACESHIFT_MAX_YAW_TURN) ) {
|
||||
const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f;
|
||||
const float TRACKER_MIN_YAW_TURN = 15.f;
|
||||
const float TRACKER_MAX_YAW_TURN = 50.f;
|
||||
if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) &&
|
||||
(fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) {
|
||||
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 {
|
||||
_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
|
||||
const float RESTORE_PERIOD = 1.f; // seconds
|
||||
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
|
||||
_head.setYaw(glm::mix(_head.getYaw(), 0.0f, restorePercentage));
|
||||
_head.setRoll(glm::mix(_head.getRoll(), 0.0f, restorePercentage));
|
||||
_head.setLeanSideways(glm::mix(_head.getLeanSideways(), 0.0f, restorePercentage));
|
||||
_head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, restorePercentage));
|
||||
head->setYaw(glm::mix(head->getYaw(), 0.0f, restorePercentage));
|
||||
head->setRoll(glm::mix(head->getRoll(), 0.0f, restorePercentage));
|
||||
head->setLeanSideways(glm::mix(head->getLeanSideways(), 0.0f, restorePercentage));
|
||||
head->setLeanForward(glm::mix(head->getLeanForward(), 0.0f, restorePercentage));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -372,17 +387,17 @@ void MyAvatar::updateFromGyros(float deltaTime) {
|
|||
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
|
||||
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
|
||||
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
|
||||
_head.tweakPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
||||
_head.tweakYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
||||
_head.tweakRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
||||
head->tweakPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
||||
head->tweakYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
||||
head->tweakRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
||||
|
||||
// Update torso lean distance based on accelerometer data
|
||||
const float TORSO_LENGTH = 0.5f;
|
||||
glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.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));
|
||||
_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));
|
||||
|
||||
// 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
|
||||
glm::quat orientation = _head.getCameraOrientation();
|
||||
glm::quat orientation = head->getCameraOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
float leanForward = _head.getLeanForward();
|
||||
float leanSideways = _head.getLeanSideways();
|
||||
float leanForward = head->getLeanForward();
|
||||
float leanSideways = head->getLeanSideways();
|
||||
|
||||
// Degrees of 'dead zone' when leaning, and amount of acceleration to apply to lean angle
|
||||
const float LEAN_FWD_DEAD_ZONE = 15.f;
|
||||
|
@ -426,7 +441,7 @@ static TextRenderer* textRenderer() {
|
|||
|
||||
void MyAvatar::renderDebugBodyPoints() {
|
||||
glm::vec3 torsoPosition(getPosition());
|
||||
glm::vec3 headPosition(getHead().getEyePosition());
|
||||
glm::vec3 headPosition(getHead()->getEyePosition());
|
||||
float torsoToHead = glm::length(headPosition - torsoPosition);
|
||||
glm::vec3 position;
|
||||
printf("head-above-torso %.2f, scale = %0.2f\n", torsoToHead, getScale());
|
||||
|
@ -452,7 +467,10 @@ void MyAvatar::renderDebugBodyPoints() {
|
|||
void MyAvatar::render(bool forceRenderHead) {
|
||||
|
||||
// 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);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
|
@ -469,7 +487,7 @@ void MyAvatar::render(bool forceRenderHead) {
|
|||
}
|
||||
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);
|
||||
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 chatAxis = glm::axis(chatRotation);
|
||||
|
@ -579,19 +597,20 @@ void MyAvatar::saveData(QSettings* settings) {
|
|||
settings->setValue("bodyPitch", _bodyPitch);
|
||||
settings->setValue("bodyRoll", _bodyRoll);
|
||||
|
||||
settings->setValue("headPitch", _head.getPitch());
|
||||
settings->setValue("headPitch", getHead()->getPitch());
|
||||
|
||||
settings->setValue("position_x", _position.x);
|
||||
settings->setValue("position_y", _position.y);
|
||||
settings->setValue("position_z", _position.z);
|
||||
|
||||
settings->setValue("pupilDilation", _head.getPupilDilation());
|
||||
settings->setValue("pupilDilation", getHead()->getPupilDilation());
|
||||
|
||||
settings->setValue("leanScale", _leanScale);
|
||||
settings->setValue("scale", _targetScale);
|
||||
|
||||
settings->setValue("faceModelURL", _faceModelURL);
|
||||
settings->setValue("skeletonModelURL", _skeletonModelURL);
|
||||
settings->setValue("displayName", _displayName);
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
@ -604,13 +623,13 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
_bodyPitch = loadSetting(settings, "bodyPitch", 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.y = loadSetting(settings, "position_y", 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);
|
||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
|
@ -619,6 +638,7 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
|
||||
setFaceModelURL(settings->value("faceModelURL").toUrl());
|
||||
setSkeletonModelURL(settings->value("skeletonModelURL").toUrl());
|
||||
setDisplayName(settings->value("displayName").toString());
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
@ -645,9 +665,9 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
|
|||
setOrientation(orientation);
|
||||
|
||||
// then vertically
|
||||
float oldPitch = _head.getPitch();
|
||||
_head.setPitch(oldPitch + deltaY * -ANGULAR_SCALE);
|
||||
rotation = glm::angleAxis(_head.getPitch() - oldPitch, orientation * IDENTITY_RIGHT);
|
||||
float oldPitch = getHead()->getPitch();
|
||||
getHead()->setPitch(oldPitch + deltaY * -ANGULAR_SCALE);
|
||||
rotation = glm::angleAxis(getHead()->getPitch() - oldPitch, orientation * IDENTITY_RIGHT);
|
||||
|
||||
setPosition(position + rotation * (getPosition() - position));
|
||||
}
|
||||
|
@ -667,11 +687,13 @@ void MyAvatar::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
|
|||
float distance;
|
||||
if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) {
|
||||
// rescale to compensate for head embiggening
|
||||
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
|
||||
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
|
||||
eyePosition = (avatar->getHead()->calculateAverageEyePosition() - avatar->getHead()->getScalePivot()) *
|
||||
(avatar->getScale() / avatar->getHead()->getScale()) + avatar->getHead()->getScalePivot();
|
||||
_lookAtTargetAvatar = avatarPointer;
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
|
||||
}
|
||||
_lookAtTargetAvatar.clear();
|
||||
}
|
||||
|
@ -682,7 +704,8 @@ void MyAvatar::clearLookAtTargetAvatar() {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -696,17 +719,17 @@ void MyAvatar::renderBody(bool forceRenderHead) {
|
|||
// Render head so long as the camera isn't inside it
|
||||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.10f;
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
if (forceRenderHead || (glm::length(myCamera->getPosition() - _head.calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE)) {
|
||||
_head.render(1.0f);
|
||||
if (forceRenderHead || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE)) {
|
||||
getHead()->render(1.0f);
|
||||
}
|
||||
_hand.render(true);
|
||||
getHand()->render(true);
|
||||
}
|
||||
|
||||
void MyAvatar::updateThrust(float deltaTime) {
|
||||
//
|
||||
// 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 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
|
@ -727,7 +750,7 @@ void MyAvatar::updateThrust(float deltaTime) {
|
|||
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
|
||||
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * 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 (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
|
||||
|
@ -835,7 +858,7 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
|
|||
float pelvisFloatingHeight = getPelvisFloatingHeight();
|
||||
if (Application::getInstance()->getEnvironment()->findCapsulePenetration(
|
||||
_position - up * (pelvisFloatingHeight - radius),
|
||||
_position + up * (getHeight() - pelvisFloatingHeight + radius), radius, penetration)) {
|
||||
_position + up * (getSkeletonHeight() - pelvisFloatingHeight + radius), radius, penetration)) {
|
||||
_lastCollisionPosition = _position;
|
||||
updateCollisionSound(penetration, deltaTime, ENVIRONMENT_COLLISION_FREQUENCY);
|
||||
applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING);
|
||||
|
@ -850,7 +873,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
|||
float pelvisFloatingHeight = getPelvisFloatingHeight();
|
||||
if (Application::getInstance()->getVoxels()->findCapsulePenetration(
|
||||
_position - glm::vec3(0.0f, pelvisFloatingHeight - radius, 0.0f),
|
||||
_position + glm::vec3(0.0f, getHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) {
|
||||
_position + glm::vec3(0.0f, getSkeletonHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) {
|
||||
_lastCollisionPosition = _position;
|
||||
updateCollisionSound(penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||
applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
|
@ -992,10 +1015,10 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
}
|
||||
|
||||
// collide our hands against them
|
||||
_hand.collideAgainstAvatar(avatar, true);
|
||||
getHand()->collideAgainstAvatar(avatar, true);
|
||||
|
||||
// collide their hands against us
|
||||
avatar->getHand().collideAgainstAvatar(this, false);
|
||||
avatar->getHand()->collideAgainstAvatar(this, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1110,7 +1133,7 @@ void MyAvatar::updateChatCircle(float deltaTime) {
|
|||
|
||||
void MyAvatar::setGravity(glm::vec3 gravity) {
|
||||
_gravity = gravity;
|
||||
_head.setGravity(_gravity);
|
||||
getHead()->setGravity(_gravity);
|
||||
|
||||
// use the gravity to determine the new world up direction, if possible
|
||||
float gravityLength = glm::length(gravity);
|
||||
|
|
|
@ -86,7 +86,6 @@ public:
|
|||
|
||||
public slots:
|
||||
void goHome();
|
||||
void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; }
|
||||
void increaseSize();
|
||||
void decreaseSize();
|
||||
void resetSize();
|
||||
|
@ -110,7 +109,6 @@ private:
|
|||
float _elapsedTimeSinceCollision;
|
||||
glm::vec3 _lastCollisionPosition;
|
||||
bool _speedBrakes;
|
||||
bool _isCollisionsOn;
|
||||
bool _isThrustOn;
|
||||
float _thrustMultiplier;
|
||||
glm::vec3 _moveTarget;
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
QString getUserString() const;
|
||||
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
||||
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
const QUuid& getUUID() { return _uuid; }
|
||||
|
||||
|
@ -45,6 +45,8 @@ public:
|
|||
private:
|
||||
|
||||
void setUsername(const QString& username);
|
||||
|
||||
void setDisplayName(const QString& displaName);
|
||||
|
||||
QString _username;
|
||||
QUuid _uuid;
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <HandData.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "Hand.h"
|
||||
#include "Menu.h"
|
||||
#include "SkeletonModel.h"
|
||||
|
||||
|
@ -33,8 +32,8 @@ void SkeletonModel::simulate(float deltaTime) {
|
|||
|
||||
// find the left and rightmost active Leap palms
|
||||
int leftPalmIndex, rightPalmIndex;
|
||||
HandData& hand = _owningAvatar->getHand();
|
||||
hand.getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
Hand* hand = _owningAvatar->getHand();
|
||||
hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
|
||||
const float HAND_RESTORATION_PERIOD = 1.f; // seconds
|
||||
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) {
|
||||
// right hand only
|
||||
applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices,
|
||||
hand.getPalms()[leftPalmIndex]);
|
||||
hand->getPalms()[leftPalmIndex]);
|
||||
restoreLeftHandPosition(handRestorePercent);
|
||||
|
||||
} else {
|
||||
applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices,
|
||||
hand.getPalms()[leftPalmIndex]);
|
||||
hand->getPalms()[leftPalmIndex]);
|
||||
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 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||
state.rotation = glm::angleAxis(-_owningAvatar->getHead().getLeanSideways(), glm::normalize(inverse * axes[2])) *
|
||||
glm::angleAxis(-_owningAvatar->getHead().getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||
state.rotation = glm::angleAxis(-_owningAvatar->getHead()->getLeanSideways(), glm::normalize(inverse * axes[2])) *
|
||||
glm::angleAxis(-_owningAvatar->getHead()->getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||
}
|
||||
|
||||
void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) {
|
||||
|
|
|
@ -45,7 +45,7 @@ void SixenseManager::update(float deltaTime) {
|
|||
return;
|
||||
}
|
||||
MyAvatar* avatar = Application::getInstance()->getAvatar();
|
||||
Hand& hand = avatar->getHand();
|
||||
Hand* hand = avatar->getHand();
|
||||
|
||||
int maxControllers = sixenseGetMaxControllers();
|
||||
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
|
||||
PalmData* palm;
|
||||
bool foundHand = false;
|
||||
for (int j = 0; j < hand.getNumPalms(); j++) {
|
||||
if (hand.getPalms()[j].getSixenseID() == data.controller_index) {
|
||||
palm = &hand.getPalms()[j];
|
||||
for (int j = 0; j < hand->getNumPalms(); j++) {
|
||||
if (hand->getPalms()[j].getSixenseID() == data.controller_index) {
|
||||
palm = &(hand->getPalms()[j]);
|
||||
foundHand = true;
|
||||
}
|
||||
}
|
||||
if (!foundHand) {
|
||||
PalmData newPalm(&hand);
|
||||
hand.getPalms().push_back(newPalm);
|
||||
palm = &hand.getPalms()[hand.getNumPalms() - 1];
|
||||
PalmData newPalm(hand);
|
||||
hand->getPalms().push_back(newPalm);
|
||||
palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
|
||||
palm->setSixenseID(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
|
||||
FingerData finger(palm, &hand);
|
||||
FingerData finger(palm, hand);
|
||||
finger.setActive(true);
|
||||
finger.setRawRootPosition(position);
|
||||
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
|
||||
const int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
166
interface/src/devices/Visage.cpp
Normal file
166
interface/src/devices/Visage.cpp
Normal 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;
|
||||
}
|
66
interface/src/devices/Visage.h
Normal file
66
interface/src/devices/Visage.h
Normal 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__) */
|
|
@ -1405,6 +1405,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
cluster.jointIndex = 0;
|
||||
}
|
||||
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
|
||||
|
|
|
@ -21,6 +21,9 @@ class FBXNode;
|
|||
|
||||
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.
|
||||
class FBXNode {
|
||||
public:
|
||||
|
|
|
@ -421,6 +421,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback) {
|
|||
_dilatedTextures.clear();
|
||||
_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);
|
||||
}
|
||||
|
||||
|
|
|
@ -227,7 +227,7 @@ private:
|
|||
void deleteGeometry();
|
||||
void renderMeshes(float alpha, bool translucent);
|
||||
|
||||
QSharedPointer<NetworkGeometry> _baseGeometry;
|
||||
QSharedPointer<NetworkGeometry> _baseGeometry; ///< reference required to prevent collection of base
|
||||
float _lodHysteresis;
|
||||
|
||||
float _pupilDilation;
|
||||
|
|
|
@ -64,7 +64,7 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar)
|
|||
QImage shot = widget->grabFrameBuffer();
|
||||
|
||||
glm::vec3 location = avatar->getPosition();
|
||||
glm::quat orientation = avatar->getHead().getOrientation();
|
||||
glm::quat orientation = avatar->getHead()->getOrientation();
|
||||
|
||||
// add metadata
|
||||
shot.setText(LOCATION_X, QString::number(location.x));
|
||||
|
|
|
@ -35,7 +35,10 @@ AvatarData::AvatarData() :
|
|||
_keyState(NO_KEY_DOWN),
|
||||
_isChatCirclingEnabled(false),
|
||||
_headData(NULL),
|
||||
_handData(NULL)
|
||||
_handData(NULL),
|
||||
_displayNameBoundingRect(),
|
||||
_displayNameTargetAlpha(0.0f),
|
||||
_displayNameAlpha(0.0f)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -63,10 +66,6 @@ QByteArray AvatarData::toByteArray() {
|
|||
if (!_headData) {
|
||||
_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;
|
||||
avatarDataByteArray.resize(MAX_PACKET_SIZE);
|
||||
|
@ -152,8 +151,8 @@ QByteArray AvatarData::toByteArray() {
|
|||
// pupil dilation
|
||||
destinationBuffer += packFloatToByte(destinationBuffer, _headData->_pupilDilation, 1.0f);
|
||||
|
||||
// leap hand data
|
||||
destinationBuffer += _handData->encodeRemoteData(destinationBuffer);
|
||||
// hand data
|
||||
destinationBuffer += HandData::encodeData(_handData, destinationBuffer);
|
||||
|
||||
return avatarDataByteArray.left(destinationBuffer - startPosition);
|
||||
}
|
||||
|
@ -259,7 +258,7 @@ int AvatarData::parseData(const QByteArray& packet) {
|
|||
// pupil dilation
|
||||
sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f);
|
||||
|
||||
// leap hand data
|
||||
// hand data
|
||||
if (sourceBuffer - startPosition < packet.size()) {
|
||||
// check passed, bytes match
|
||||
sourceBuffer += _handData->decodeRemoteData(packet.mid(sourceBuffer - startPosition));
|
||||
|
@ -274,7 +273,8 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
|
|||
|
||||
QUuid avatarUUID;
|
||||
QUrl faceModelURL, skeletonModelURL;
|
||||
packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL;
|
||||
QString displayName;
|
||||
packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> displayName;
|
||||
|
||||
bool hasIdentityChanged = false;
|
||||
|
||||
|
@ -287,15 +287,20 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
|
|||
setSkeletonModelURL(skeletonModelURL);
|
||||
hasIdentityChanged = true;
|
||||
}
|
||||
|
||||
|
||||
if (displayName != _displayName) {
|
||||
setDisplayName(displayName);
|
||||
hasIdentityChanged = true;
|
||||
}
|
||||
|
||||
return hasIdentityChanged;
|
||||
}
|
||||
|
||||
QByteArray AvatarData::identityByteArray() {
|
||||
QByteArray identityData;
|
||||
QDataStream identityStream(&identityData, QIODevice::Append);
|
||||
|
||||
identityStream << QUuid() << _faceModelURL << _skeletonModelURL;
|
||||
|
||||
identityStream << QUuid() << _faceModelURL << _skeletonModelURL << _displayName;
|
||||
|
||||
return identityData;
|
||||
}
|
||||
|
@ -312,6 +317,13 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
qDebug() << "Changing skeleton model for avatar to" << _skeletonModelURL.toString();
|
||||
}
|
||||
|
||||
void AvatarData::setDisplayName(const QString& displayName) {
|
||||
_displayName = displayName;
|
||||
|
||||
qDebug() << "Changing display name for avatar to" << displayName;
|
||||
}
|
||||
|
||||
|
||||
void AvatarData::setClampedTargetScale(float targetScale) {
|
||||
|
||||
targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
|
|
|
@ -33,6 +33,7 @@ typedef unsigned long long quint64;
|
|||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QVariantMap>
|
||||
#include <QRect>
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
@ -139,9 +140,6 @@ public:
|
|||
const HeadData* getHeadData() const { return _headData; }
|
||||
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 bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
|
@ -153,11 +151,13 @@ public:
|
|||
|
||||
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
||||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||
const QString& getDisplayName() const { return _displayName; }
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
virtual float getBoundingRadius() const { return 1.f; }
|
||||
virtual void setDisplayName(const QString& displayName);
|
||||
|
||||
virtual float getBoundingRadius() const { return 1.f; }
|
||||
|
||||
protected:
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _handPosition;
|
||||
|
@ -186,6 +186,12 @@ protected:
|
|||
|
||||
QUrl _faceModelURL;
|
||||
QUrl _skeletonModelURL;
|
||||
QString _displayName;
|
||||
|
||||
QRect _displayNameBoundingRect;
|
||||
float _displayNameTargetAlpha;
|
||||
float _displayNameAlpha;
|
||||
|
||||
private:
|
||||
// privatize the copy constructor and assignment operator so they cannot be called
|
||||
AvatarData(const AvatarData&);
|
||||
|
|
|
@ -113,17 +113,30 @@ _owningHandData(owningHandData)
|
|||
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) {
|
||||
const unsigned char* startPosition = destinationBuffer;
|
||||
|
||||
unsigned int numHands = 0;
|
||||
unsigned int numPalms = 0;
|
||||
for (unsigned int handIndex = 0; handIndex < getNumPalms(); ++handIndex) {
|
||||
PalmData& palm = getPalms()[handIndex];
|
||||
if (palm.isActive()) {
|
||||
numHands++;
|
||||
numPalms++;
|
||||
}
|
||||
}
|
||||
*destinationBuffer++ = numHands;
|
||||
*destinationBuffer++ = numPalms;
|
||||
|
||||
for (unsigned int handIndex = 0; handIndex < getNumPalms(); ++handIndex) {
|
||||
PalmData& palm = getPalms()[handIndex];
|
||||
|
@ -162,9 +175,9 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) {
|
|||
int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
|
||||
const unsigned char* startPosition;
|
||||
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())
|
||||
addNewPalm();
|
||||
PalmData& palm = getPalms()[handIndex];
|
||||
|
@ -203,7 +216,7 @@ int HandData::decodeRemoteData(const QByteArray& dataByteArray) {
|
|||
}
|
||||
}
|
||||
// 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];
|
||||
palm.setActive(false);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
void setFingerTrailLength(unsigned int length);
|
||||
void updateFingerTrails();
|
||||
|
||||
static int encodeData(HandData* hand, unsigned char* destinationBuffer);
|
||||
|
||||
// Use these for sending and receiving hand data
|
||||
int encodeRemoteData(unsigned char* destinationBuffer);
|
||||
int decodeRemoteData(const QByteArray& dataByteArray);
|
||||
|
|
|
@ -52,6 +52,10 @@ public slots:
|
|||
virtual void captureWheelEvents() = 0;
|
||||
virtual void releaseWheelEvents() = 0;
|
||||
|
||||
virtual void captureJoystick(int joystickIndex) = 0;
|
||||
virtual void releaseJoystick(int joystickIndex) = 0;
|
||||
|
||||
virtual glm::vec2 getViewportDimensions() const = 0;
|
||||
|
||||
signals:
|
||||
void keyPressEvent(const KeyEvent& event);
|
||||
|
|
|
@ -102,6 +102,18 @@ KeyEvent::KeyEvent(const QKeyEvent& event) {
|
|||
text = "END";
|
||||
} else if (key == Qt::Key_Help) {
|
||||
text = "HELP";
|
||||
} else if (key == Qt::Key_CapsLock) {
|
||||
text = "CAPS LOCK";
|
||||
} else if (key >= Qt::Key_A && key <= Qt::Key_Z && (isMeta || isControl || isAlt)) {
|
||||
// this little bit of hackery will fix the text character keys like a-z in cases of control/alt/meta where
|
||||
// qt doesn't always give you the key characters and will sometimes give you crazy non-printable characters
|
||||
const int lowerCaseAdjust = 0x20;
|
||||
QString unicode;
|
||||
if (isShifted) {
|
||||
text = QString(QChar(key));
|
||||
} else {
|
||||
text = QString(QChar(key + lowerCaseAdjust));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,6 +220,8 @@ void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event) {
|
|||
event.key = Qt::Key_End;
|
||||
} else if (event.text.toUpper() == "HELP") {
|
||||
event.key = Qt::Key_Help;
|
||||
} else if (event.text.toUpper() == "CAPS LOCK") {
|
||||
event.key = Qt::Key_CapsLock;
|
||||
} else {
|
||||
event.key = event.text.at(0).unicode();
|
||||
}
|
||||
|
@ -258,10 +272,17 @@ MouseEvent::MouseEvent() :
|
|||
};
|
||||
|
||||
|
||||
MouseEvent::MouseEvent(const QMouseEvent& event) {
|
||||
x = event.x();
|
||||
y = event.y();
|
||||
|
||||
MouseEvent::MouseEvent(const QMouseEvent& event) :
|
||||
x(event.x()),
|
||||
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
|
||||
switch (event.button()) {
|
||||
case Qt::LeftButton:
|
||||
|
@ -280,16 +301,6 @@ MouseEvent::MouseEvent(const QMouseEvent& event) {
|
|||
button = "NONE";
|
||||
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) {
|
||||
|
|
|
@ -229,6 +229,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
|||
// send back a reply
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
if (matchingNode) {
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
QByteArray replyPacket = constructPingReplyPacket(packet);
|
||||
writeDatagram(replyPacket, matchingNode, senderSockAddr);
|
||||
}
|
||||
|
@ -239,6 +240,8 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
|||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
|
||||
if (sendingNode) {
|
||||
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
// activate the appropriate socket for this node, if not yet updated
|
||||
activateSocketFromNodeCommunication(packet, sendingNode);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
/// PacketTypeVoxelSet, PacketTypeVoxelSetDestructive, or PacketTypeVoxelErase. The buffer is returned to caller becomes
|
||||
/// responsibility of caller and MUST be deleted by caller.
|
||||
bool createVoxelEditMessage(PacketType command, short int sequence,
|
||||
int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) {
|
||||
int voxelCount, const VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) {
|
||||
|
||||
bool success = true; // assume the best
|
||||
int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now
|
||||
|
@ -102,7 +102,7 @@ bool encodeVoxelEditMessageDetails(PacketType, int voxelCount, VoxelDetail* voxe
|
|||
return success;
|
||||
}
|
||||
|
||||
void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, VoxelDetail& detail) {
|
||||
void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, const VoxelDetail& detail) {
|
||||
// allows app to disable sending if for example voxels have been disabled
|
||||
if (!_shouldSend) {
|
||||
return; // bail early
|
||||
|
|
|
@ -19,7 +19,7 @@ class VoxelEditPacketSender : public OctreeEditPacketSender {
|
|||
Q_OBJECT
|
||||
public:
|
||||
/// Send voxel edit message immediately
|
||||
void sendVoxelEditMessage(PacketType type, VoxelDetail& detail);
|
||||
void sendVoxelEditMessage(PacketType type, const VoxelDetail& detail);
|
||||
|
||||
/// Queues a single voxel edit message. Will potentially send a pending multi-command packet. Determines which voxel-server
|
||||
/// node or nodes the packet should be sent to. Can be called even before voxel servers are known, in which case up to
|
||||
|
|
Loading…
Reference in a new issue