Merge branch 'master' of https://github.com/highfidelity/hifi
6
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -21,7 +21,11 @@ if (WIN32)
|
|||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/LibOVR/Include CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/Win32/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/x64/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL)
|
||||
else()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/LibOVR/Lib/Windows/Win32/Release/VS2013/LibOVR.lib CACHE TYPE INTERNAL)
|
||||
endif()
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
|
|
15
cmake/externals/glew/CMakeLists.txt
vendored
|
@ -16,11 +16,16 @@ if (WIN32)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of glew include directories")
|
||||
|
||||
set(_LIB_DIR ${SOURCE_DIR}/lib/Release/Win32)
|
||||
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(_LIB_DIR ${SOURCE_DIR}/lib/Release/x64)
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/x64 CACHE FILEPATH "Location of GLEW DLL")
|
||||
else()
|
||||
set(_LIB_DIR ${SOURCE_DIR}/lib/Release/Win32)
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/Win32 CACHE FILEPATH "Location of GLEW DLL")
|
||||
endif()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_LIB_DIR}/glew32.lib CACHE FILEPATH "Location of GLEW release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of GLEW debug library")
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/bin/Release/Win32 CACHE FILEPATH "Location of GLEW DLL")
|
||||
|
||||
endif ()
|
9
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -73,9 +73,14 @@ if (APPLE)
|
|||
)
|
||||
|
||||
elseif (WIN32)
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/vc12")
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/intel64/vc12")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/intel64/vc12" CACHE PATH "Path to TBB DLLs")
|
||||
else()
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/vc12")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/vc12" CACHE PATH "Path to TBB DLLs")
|
||||
endif()
|
||||
set(_LIB_EXT "lib")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/vc12" CACHE PATH "Path to TBB DLLs")
|
||||
elseif (ANDROID)
|
||||
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib")
|
||||
set(_LIB_PREFIX "lib")
|
||||
|
|
|
@ -28,7 +28,11 @@ if (APPLE)
|
|||
elseif (UNIX)
|
||||
set(ARCH_DIR "UNIX")
|
||||
elseif (WIN32)
|
||||
set(ARCH_DIR "Win32")
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
else()
|
||||
set(ARCH_DIR "Win32")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS})
|
||||
|
|
|
@ -16,7 +16,16 @@
|
|||
#
|
||||
|
||||
if (WIN32)
|
||||
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
set(ARCH_NAME "64")
|
||||
else()
|
||||
set(ARCH_DIR "Win32")
|
||||
set(ARCH_NAME "32")
|
||||
endif()
|
||||
|
||||
|
||||
find_path(NSIGHT_INCLUDE_DIRS
|
||||
NAMES
|
||||
nvToolsExt.h
|
||||
|
@ -25,17 +34,18 @@ if (WIN32)
|
|||
PATHS
|
||||
"C:/Program Files/NVIDIA Corporation/NvToolsExt")
|
||||
|
||||
find_library(NSIGHT_LIBRARY_RELEASE nvToolsExt32_1
|
||||
find_library(NSIGHT_LIBRARY_RELEASE "nvToolsExt${ARCH_NAME}_1"
|
||||
PATH_SUFFIXES
|
||||
"lib/Win32" "lib"
|
||||
"lib/${ARCH_DIR}" "lib"
|
||||
PATHS
|
||||
"C:/Program Files/NVIDIA Corporation/NvToolsExt")
|
||||
find_library(NSIGHT_LIBRARY_DEBUG nvToolsExt32_1
|
||||
find_library(NSIGHT_LIBRARY_DEBUG "nvToolsExt${ARCH_NAME}_1"
|
||||
PATH_SUFFIXES
|
||||
"lib/Win32" "lib"
|
||||
"lib/${ARCH_DIR}" "lib"
|
||||
PATHS
|
||||
"C:/Program Files/NVIDIA Corporation/NvToolsExt")
|
||||
|
||||
|
||||
add_paths_to_fixup_libs("C:/Program Files/NVIDIA Corporation/NvToolsExt/bin/${ARCH_DIR}")
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(NSIGHT)
|
||||
endif ()
|
||||
|
|
|
@ -30,12 +30,20 @@ elseif (UNIX)
|
|||
find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
# find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
elseif (WIN32)
|
||||
find_library(SIXENSE_LIBRARY_RELEASE lib/win32/release_dll/sixense.lib HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_library(SIXENSE_LIBRARY_DEBUG lib/win32/debug_dll/sixensed.lib HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
set(ARCH_SUFFIX "_x64")
|
||||
else()
|
||||
set(ARCH_DIR "Win32")
|
||||
set(ARCH_SUFFIX "")
|
||||
endif()
|
||||
|
||||
find_library(SIXENSE_LIBRARY_RELEASE "lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_library(SIXENSE_LIBRARY_DEBUG "lib/${ARCH_DIR}/debug_dll/sixensed.lib" HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
|
||||
find_path(SIXENSE_DEBUG_DLL_PATH sixensed.dll PATH_SUFFIXES bin/win32/debug_dll HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_path(SIXENSE_RELEASE_DLL_PATH sixense.dll PATH_SUFFIXES bin/win32/release_dll HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/win32/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_path(SIXENSE_DEBUG_DLL_PATH "sixensed${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/debug_dll HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_path(SIXENSE_RELEASE_DLL_PATH "sixense${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/release_dll HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/${ARCH_DIR}/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
|
|
|
@ -49,7 +49,7 @@ elseif (UNIX AND NOT ANDROID)
|
|||
endif ()
|
||||
|
||||
elseif (WIN32)
|
||||
if (CMAKE_CL_64)
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_TBB_ARCH_DIR "intel64")
|
||||
else()
|
||||
set(_TBB_ARCH_DIR "ia32")
|
||||
|
|
|
@ -100,12 +100,12 @@ var NUM_BUTTONS = 3;
|
|||
var screenSize = Controller.getViewportDimensions();
|
||||
var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
|
||||
Script.include(["../../libraries/toolBars.js"]);
|
||||
const persistKey = "highfidelity.gun.toolbar.position";
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
||||
toolBar.save = function () {
|
||||
Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
|
||||
};
|
||||
var old = JSON.parse(Settings.getValue(persistKey) || '0');
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) {
|
||||
return {
|
||||
x: startX,
|
||||
y: (screenSize.y - (BUTTON_SIZE + PADDING)),
|
||||
};
|
||||
});
|
||||
var reticle = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - (BUTTON_SIZE / 2),
|
||||
y: screenSize.y / 2 - (BUTTON_SIZE / 2),
|
||||
|
@ -116,8 +116,6 @@ var reticle = Overlays.addOverlay("image", {
|
|||
});
|
||||
|
||||
var offButton = toolBar.addOverlay("image", {
|
||||
x: old ? old[0] : startX,
|
||||
y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
|
||||
|
@ -181,7 +179,7 @@ function entityCollisionWithEntity(entity1, entity2, collision) {
|
|||
}
|
||||
|
||||
function shootBullet(position, velocity, grenade) {
|
||||
var BULLET_SIZE = 0.10;
|
||||
var BULLET_SIZE = .09;
|
||||
var BULLET_LIFETIME = 10.0;
|
||||
var BULLET_GRAVITY = -0.25;
|
||||
var GRENADE_VELOCITY = 15.0;
|
||||
|
|
|
@ -18,4 +18,5 @@ Script.load("users.js");
|
|||
Script.load("grab.js");
|
||||
Script.load("directory.js");
|
||||
Script.load("mouseLook.js");
|
||||
Script.load("hmdControls.js");
|
||||
Script.load("dialTone.js");
|
||||
|
|
|
@ -33,15 +33,13 @@ var BUTTON_SIZE = 32;
|
|||
var PADDING = 3;
|
||||
|
||||
Script.include(["libraries/toolBars.js"]);
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
||||
const persistKey = "highfidelity.dice.toolbar.position";
|
||||
toolBar.save = function () {
|
||||
Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
|
||||
};
|
||||
var old = JSON.parse(Settings.getValue(persistKey) || '0');
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.dice.toolbar", function (screenSize) {
|
||||
return {
|
||||
x: (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING),
|
||||
y: (screenSize.y - (BUTTON_SIZE + PADDING))
|
||||
};
|
||||
});
|
||||
var offButton = toolBar.addOverlay("image", {
|
||||
x: old ? old[0] : (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING),
|
||||
y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/close.png",
|
||||
|
|
|
@ -49,7 +49,6 @@ selectionManager.addEventListener(function() {
|
|||
lightOverlayManager.updatePositions();
|
||||
});
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||
var toolHeight = 50;
|
||||
var toolWidth = 50;
|
||||
|
@ -143,7 +142,12 @@ var toolBar = (function () {
|
|||
browseMarketplaceButton;
|
||||
|
||||
function initialize() {
|
||||
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
|
||||
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function (windowDimensions, toolbar) {
|
||||
return {
|
||||
x: windowDimensions.x - 8 - toolbar.width,
|
||||
y: (windowDimensions.y - toolbar.height) / 2
|
||||
};
|
||||
});
|
||||
|
||||
browseMarketplaceButton = toolBar.addTool({
|
||||
imageURL: toolIconUrl + "marketplace.svg",
|
||||
|
@ -321,38 +325,6 @@ var toolBar = (function () {
|
|||
}
|
||||
}
|
||||
|
||||
const persistKey = "highfidelity.edit.toolbar.position";
|
||||
that.move = function () {
|
||||
var newViewPort,
|
||||
toolsX,
|
||||
toolsY;
|
||||
|
||||
newViewPort = Controller.getViewportDimensions();
|
||||
|
||||
if (toolBar === undefined) {
|
||||
initialize();
|
||||
|
||||
toolBar.save = function () {
|
||||
Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
|
||||
};
|
||||
var old = JSON.parse(Settings.getValue(persistKey) || '0');
|
||||
if (old) {
|
||||
windowDimensions = newViewPort;
|
||||
toolBar.move(old[0], old[1]);
|
||||
return;
|
||||
}
|
||||
} else if (windowDimensions.x === newViewPort.x &&
|
||||
windowDimensions.y === newViewPort.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
windowDimensions = newViewPort;
|
||||
toolsX = windowDimensions.x - 8 - toolBar.width;
|
||||
toolsY = (windowDimensions.y - toolBar.height) / 2;
|
||||
|
||||
toolBar.move(toolsX, toolsY);
|
||||
};
|
||||
|
||||
var newModelButtonDown = false;
|
||||
var browseMarketplaceButtonDown = false;
|
||||
that.mousePressEvent = function (event) {
|
||||
|
@ -552,6 +524,7 @@ var toolBar = (function () {
|
|||
toolBar.cleanup();
|
||||
};
|
||||
|
||||
initialize();
|
||||
return that;
|
||||
}());
|
||||
|
||||
|
@ -931,7 +904,6 @@ var lastPosition = null;
|
|||
|
||||
// Do some stuff regularly, like check for placement of various overlays
|
||||
Script.update.connect(function (deltaTime) {
|
||||
toolBar.move();
|
||||
progressDialog.move();
|
||||
selectionDisplay.checkMove();
|
||||
var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1);
|
||||
|
|
285
examples/hmdControls.js
Normal file
|
@ -0,0 +1,285 @@
|
|||
//
|
||||
// hmdControls.js
|
||||
// examples
|
||||
//
|
||||
// Created by Sam Gondelman on 6/17/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var MOVE_DISTANCE = 10.0;
|
||||
var PITCH_INCREMENT = 0.5; // degrees
|
||||
var pitchChange = 0; // degrees
|
||||
var YAW_INCREMENT = 0.5; // degrees
|
||||
var VR_YAW_INCREMENT = 15.0; // degrees
|
||||
var yawChange = 0;
|
||||
var BOOM_SPEED = 0.5;
|
||||
var THRESHOLD = 0.2;
|
||||
|
||||
var CAMERA_UPDATE_TIME = 0.5;
|
||||
var yawTimer = CAMERA_UPDATE_TIME;
|
||||
|
||||
var shifted = false;
|
||||
var SHIFT_UPDATE_TIME = 0.5;
|
||||
var shiftTimer = SHIFT_UPDATE_TIME;
|
||||
var SHIFT_MAG = 4.0;
|
||||
|
||||
var warpActive = false;
|
||||
var WARP_UPDATE_TIME = .5;
|
||||
var warpTimer = WARP_UPDATE_TIME;
|
||||
|
||||
var warpPosition = { x: 0, y: 0, z: 0 };
|
||||
|
||||
var WARP_SPHERE_SIZE = 1;
|
||||
var warpSphere = Overlays.addOverlay("sphere", {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
size: WARP_SPHERE_SIZE,
|
||||
color: { red: 0, green: 255, blue: 0 },
|
||||
alpha: 1.0,
|
||||
solid: true,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
var WARP_LINE_HEIGHT = 10;
|
||||
var warpLine = Overlays.addOverlay("line3d", {
|
||||
start: { x: 0, y: 0, z:0 },
|
||||
end: { x: 0, y: 0, z: 0 },
|
||||
color: { red: 0, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
lineWidth: 5,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
var velocity = { x: 0, y: 0, z: 0 };
|
||||
var VERY_LONG_TIME = 1000000.0;
|
||||
|
||||
var active = Menu.isOptionChecked("Enable VR Mode");
|
||||
var prevVRMode = Menu.isOptionChecked("Enable VR Mode");
|
||||
|
||||
var hmdControls = (function () {
|
||||
|
||||
function onKeyPressEvent(event) {
|
||||
if (event.text == 'g' && event.isMeta) {
|
||||
active = !active;
|
||||
}
|
||||
}
|
||||
|
||||
function findAction(name) {
|
||||
var actions = Controller.getAllActions();
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
if (actions[i].actionName == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// If the action isn't found, it will default to the first available action
|
||||
return 0;
|
||||
}
|
||||
|
||||
function onActionEvent(action, state) {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
if (state < THRESHOLD) {
|
||||
if (action == findAction("YAW_LEFT") || action == findAction("YAW_RIGHT")) {
|
||||
yawTimer = CAMERA_UPDATE_TIME;
|
||||
} else if (action == findAction("PITCH_UP") || action == findAction("PITCH_DOWN")) {
|
||||
pitchTimer = CAMERA_UPDATE_TIME;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (action) {
|
||||
case findAction("LONGITUDINAL_BACKWARD"):
|
||||
var direction = {x: 0.0, y: 0.0, z:1.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("LONGITUDINAL_FORWARD"):
|
||||
var direction = {x: 0.0, y: 0.0, z:-1.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("LATERAL_LEFT"):
|
||||
var direction = {x:-1.0, y: 0.0, z: 0.0}
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("LATERAL_RIGHT"):
|
||||
var direction = {x:1.0, y: 0.0, z: 0.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("VERTICAL_DOWN"):
|
||||
var direction = {x: 0.0, y: -1.0, z: 0.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("VERTICAL_UP"):
|
||||
var direction = {x: 0.0, y: 1.0, z: 0.0};
|
||||
direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
|
||||
velocity = Vec3.sum(velocity, direction);
|
||||
break;
|
||||
case findAction("YAW_LEFT"):
|
||||
if (yawTimer < 0.0 && Menu.isOptionChecked("Enable VR Mode")) {
|
||||
yawChange = yawChange + (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
|
||||
yawTimer = CAMERA_UPDATE_TIME;
|
||||
} else if (!Menu.isOptionChecked("Enable VR Mode")) {
|
||||
yawChange = yawChange + (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("YAW_RIGHT"):
|
||||
if (yawTimer < 0.0 && Menu.isOptionChecked("Enable VR Mode")) {
|
||||
yawChange = yawChange - (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
|
||||
yawTimer = CAMERA_UPDATE_TIME;
|
||||
} else if (!Menu.isOptionChecked("Enable VR Mode")) {
|
||||
yawChange = yawChange - (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("PITCH_DOWN"):
|
||||
if (!Menu.isOptionChecked("Enable VR Mode")) {
|
||||
pitchChange = pitchChange - (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("PITCH_UP"):
|
||||
if (!Menu.isOptionChecked("Enable VR Mode")) {
|
||||
pitchChange = pitchChange + (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
|
||||
}
|
||||
break;
|
||||
case findAction("SHIFT"): // speed up
|
||||
if (shiftTimer < 0.0) {
|
||||
shifted = !shifted;
|
||||
shiftTimer = SHIFT_UPDATE_TIME;
|
||||
}
|
||||
break;
|
||||
case findAction("ACTION1"): // start/end warp
|
||||
if (warpTimer < 0.0) {
|
||||
warpActive = !warpActive;
|
||||
if (!warpActive) {
|
||||
finishWarp();
|
||||
}
|
||||
warpTimer = WARP_UPDATE_TIME;
|
||||
}
|
||||
break;
|
||||
case findAction("ACTION2"): // cancel warp
|
||||
warpActive = false;
|
||||
Overlays.editOverlay(warpSphere, {
|
||||
visible: false,
|
||||
});
|
||||
Overlays.editOverlay(warpLine, {
|
||||
visible: false,
|
||||
});
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
if (prevVRMode != Menu.isOptionChecked("Enable VR Mode")) {
|
||||
active = Menu.isOptionChecked("Enable VR Mode");
|
||||
prevVRMode = Menu.isOptionChecked("Enable VR Mode");
|
||||
}
|
||||
|
||||
if (yawTimer >= 0.0) {
|
||||
yawTimer = yawTimer - dt;
|
||||
}
|
||||
if (shiftTimer >= 0.0) {
|
||||
shiftTimer = shiftTimer - dt;
|
||||
}
|
||||
if (warpTimer >= 0.0) {
|
||||
warpTimer = warpTimer - dt;
|
||||
}
|
||||
|
||||
if (warpActive) {
|
||||
updateWarp();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
Controller.captureActionEvents();
|
||||
|
||||
MyAvatar.bodyYaw = MyAvatar.bodyYaw + yawChange;
|
||||
MyAvatar.headPitch = Math.max(-180, Math.min(180, MyAvatar.headPitch + pitchChange));
|
||||
yawChange = 0;
|
||||
pitchChange = 0;
|
||||
|
||||
MyAvatar.motorVelocity = velocity;
|
||||
MyAvatar.motorTimescale = 0.0;
|
||||
velocity = { x: 0, y: 0, z: 0 };
|
||||
} else {
|
||||
Controller.releaseActionEvents();
|
||||
yawChange = 0;
|
||||
pitchChange = 0;
|
||||
MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}
|
||||
MyAvatar.motorTimescale = VERY_LONG_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
function updateWarp() {
|
||||
var look = Quat.getFront(Camera.getOrientation());
|
||||
var pitch = Math.asin(look.y);
|
||||
|
||||
// Get relative to looking straight down
|
||||
pitch += Math.PI / 2;
|
||||
|
||||
// Scale up
|
||||
pitch *= 2;
|
||||
var distance = pitch * pitch * pitch;
|
||||
|
||||
var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z });
|
||||
warpPosition = Vec3.multiply(warpDirection, distance);
|
||||
warpPosition = Vec3.sum(MyAvatar.position, warpPosition);
|
||||
|
||||
// Commented out until ray picking can be fixed
|
||||
// var pickRay = {
|
||||
// origin: Vec3.sum(warpPosition, WARP_PICK_OFFSET),
|
||||
// direction: { x: 0, y: -1, z: 0 }
|
||||
// };
|
||||
|
||||
// var intersection = Entities.findRayIntersection(pickRay);
|
||||
|
||||
// if (intersection.intersects && intersection.distance < WARP_PICK_MAX_DISTANCE) {
|
||||
// // Warp 1 meter above the object - this is an approximation
|
||||
// // TODO Get the actual offset to the Avatar's feet and plant them to
|
||||
// // the object.
|
||||
// warpPosition = Vec3.sum(intersection.intersection, { x: 0, y: 1, z:0 });
|
||||
// }
|
||||
|
||||
// Adjust overlays to match warp position
|
||||
Overlays.editOverlay(warpSphere, {
|
||||
position: warpPosition,
|
||||
visible: true,
|
||||
});
|
||||
Overlays.editOverlay(warpLine, {
|
||||
start: warpPosition,
|
||||
end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }),
|
||||
visible: true,
|
||||
});
|
||||
}
|
||||
|
||||
function finishWarp() {
|
||||
Overlays.editOverlay(warpSphere, {
|
||||
visible: false,
|
||||
});
|
||||
Overlays.editOverlay(warpLine, {
|
||||
visible: false,
|
||||
});
|
||||
MyAvatar.position = warpPosition;
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
Controller.keyPressEvent.connect(onKeyPressEvent);
|
||||
|
||||
Controller.actionEvent.connect(onActionEvent);
|
||||
|
||||
Script.update.connect(update);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
Controller.releaseActionEvents();
|
||||
MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}
|
||||
MyAvatar.motorTimescale = VERY_LONG_TIME;
|
||||
}
|
||||
|
||||
setUp();
|
||||
Script.scriptEnding.connect(tearDown);
|
||||
}());
|
|
@ -13,4 +13,5 @@ Script.load("progress.js");
|
|||
Script.load("lobby.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("controllers/oculus/goTo.js");
|
||||
Script.load("hmdControls.js");
|
||||
//Script.load("scripts.js"); // Not created yet
|
||||
|
|
|
@ -932,6 +932,7 @@
|
|||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="properties" onload='loaded();'>
|
||||
<div id="properties-list">
|
||||
<div id="type" class="property">
|
||||
|
@ -939,9 +940,12 @@
|
|||
<label>Type: </label><span id="property-type"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property">
|
||||
<div id="id" class="property">
|
||||
<span class="label" style="float: left; margin-right: 6px">
|
||||
<label>ID: <label>
|
||||
</span>
|
||||
<div class="value">
|
||||
<label id="property-id" class="selectable"></label>
|
||||
<span id="property-id" class="selectable"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property">
|
||||
|
@ -951,17 +955,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Hyperlink</div>
|
||||
<div class="input-area">Href<br></div>
|
||||
<div class="value">
|
||||
<input id="property-hyperlink-href" class="url"></input>
|
||||
</div>
|
||||
<div class="input-area">Description<br></div> <div class="value">
|
||||
<input id="property-hyperlink-description" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label">Locked</span>
|
||||
<span class="value">
|
||||
|
@ -976,6 +969,36 @@
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">User Data</div>
|
||||
<div class="value">
|
||||
<textarea id="property-user-data"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sub-section-header">
|
||||
<label>Hyperlink</label>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Href</div>
|
||||
<div class="value">
|
||||
<input id="property-hyperlink-href" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="property">
|
||||
<div class="label">Description</div>
|
||||
<div class="value">
|
||||
<input id="property-hyperlink-description" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header">
|
||||
<label>Spacial Properites</label>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Position</div>
|
||||
<div class="value">
|
||||
|
@ -1043,6 +1066,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header">
|
||||
<label>Physical Properites</label>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Linear Velocity</div>
|
||||
<div class="value">
|
||||
|
@ -1109,6 +1137,21 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="color-section" class="property">
|
||||
<div class="label">Color</div>
|
||||
<div class="value">
|
||||
<div id="property-color" class='color-picker'></div>
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-color-red"></input></div>
|
||||
<div class="input-area">G <input class="coord" type='number' id="property-color-green"></input></div>
|
||||
<div class="input-area">B <input class="coord" type='number' id="property-color-blue"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header">
|
||||
<label>Behavior</label>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label">Ignore For Collisions</span>
|
||||
<span class="value">
|
||||
|
@ -1144,74 +1187,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">User Data</div>
|
||||
<div class="value">
|
||||
<textarea id="property-user-data"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="color-section" class="property">
|
||||
<div class="label">Color</div>
|
||||
<div class="value">
|
||||
<div id="property-color" class='color-picker'></div>
|
||||
<div class="input-area">R <input class="coord" type='number' id="property-color-red"></input></div>
|
||||
<div class="input-area">G <input class="coord" type='number' id="property-color-green"></input></div>
|
||||
<div class="input-area">B <input class="coord" type='number' id="property-color-blue"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="web-section property">
|
||||
<div class="label">Source URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-web-source-url" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="particle-section property">
|
||||
<div class="label">Max Particles</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-maxparticles" min="0" max="2048" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Life Span</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-lifespan" min="0" step="0.1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Emission Rate</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-emit-rate" min="0" step="0.5"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Emission Direction</div>
|
||||
<div class="value">
|
||||
<div class="input-area">X <input class="coord" type='number' id="property-particle-emit-direction-x"></input></div>
|
||||
<div class="input-area">Y <input class="coord" type='number' id="property-particle-emit-direction-y"></input></div>
|
||||
<div class="input-area">Z <input class="coord" type='number' id="property-particle-emit-direction-z"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Emission Strength</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-emit-strength" min="0" step="0.1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Local Gravity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-particle-localgravity" step="0.05"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Radius</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-particle-radius" min="0" step="0.005"></input>
|
||||
</div>
|
||||
<div class="section-header model-section zone-section">
|
||||
<label>Model</label>
|
||||
</div>
|
||||
|
||||
<div class="model-section property">
|
||||
|
@ -1220,6 +1198,7 @@
|
|||
<input type="text" id="property-model-url" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="model-section zone-section property">
|
||||
<div class="label">Shape Type</div>
|
||||
<div class="value">
|
||||
|
@ -1280,8 +1259,75 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header web-section">
|
||||
<label>Web</label>
|
||||
</div>
|
||||
|
||||
<div class="web-section property">
|
||||
<div class="label">Source URL</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-web-source-url" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header particle-section">
|
||||
<label>Particle</label>
|
||||
</div>
|
||||
|
||||
<div class="particle-section property">
|
||||
<div class="label">Max Particles</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-maxparticles" min="0" max="2048" step="1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Life Span</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-lifespan" min="0" step="0.1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Emission Rate</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-emit-rate" min="0" step="0.5"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Emission Direction</div>
|
||||
<div class="value">
|
||||
<div class="input-area">X <input class="coord" type='number' id="property-particle-emit-direction-x"></input></div>
|
||||
<div class="input-area">Y <input class="coord" type='number' id="property-particle-emit-direction-y"></input></div>
|
||||
<div class="input-area">Z <input class="coord" type='number' id="property-particle-emit-direction-z"></input></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Emission Strength</div>
|
||||
<div class="value">
|
||||
<input type='number' id="property-particle-emit-strength" min="0" step="0.1"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Local Gravity</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-particle-localgravity" step="0.05"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="particle-section property">
|
||||
<div class="label">Particle Radius</div>
|
||||
<div class="value">
|
||||
<input class="coord" type='number' id="property-particle-radius" min="0" step="0.005"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header text-section">
|
||||
<label>Text</label>
|
||||
</div>
|
||||
|
||||
<div class="text-section property">
|
||||
<div class="label">Text</div>
|
||||
<div class="label">Text Content</div>
|
||||
<div class="value">
|
||||
<input type="text" id="property-text-text"></input>
|
||||
</div>
|
||||
|
@ -1311,6 +1357,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header light-section">
|
||||
<label>Light</label>
|
||||
</div>
|
||||
|
||||
<div class="light-section property">
|
||||
<span class="label">Spot Light</span>
|
||||
<span class="value">
|
||||
|
@ -1345,6 +1396,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header zone-section">
|
||||
<label>Zone</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section property">
|
||||
<span class="label">Stage Sun Model Enabled</span>
|
||||
<span class="value">
|
||||
|
@ -1431,6 +1487,12 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sub-section-header zone-section skybox-section">
|
||||
<label>Skybox</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section skybox-section property">
|
||||
<div class="label">Skybox Color</div>
|
||||
<div class="value">
|
||||
|
@ -1446,6 +1508,12 @@
|
|||
<input type="text" id="property-zone-skybox-url" class="url"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sub-section-header zone-section atmosphere-section">
|
||||
<label>Atmosphere</label>
|
||||
</div>
|
||||
|
||||
<div class="zone-section atmosphere-section property">
|
||||
<div class="label">Atmosphere Center</div>
|
||||
<div class="value">
|
||||
|
@ -1495,8 +1563,6 @@
|
|||
<input type='checkbox' id="property-zone-atmosphere-has-stars">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -8,7 +8,7 @@ body {
|
|||
background-color: rgb(76, 76, 76);
|
||||
color: rgb(204, 204, 204);
|
||||
font-family: Arial;
|
||||
font-size: 8.25pt;
|
||||
font-size: 9.0pt;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
|
@ -44,20 +44,31 @@ body {
|
|||
border: 1.5pt solid black;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
.section-header, .sub-section-header {
|
||||
background: #AAA;
|
||||
border-bottom: 0.75pt solid #CCC;
|
||||
background-color: #333333;
|
||||
color: #999;
|
||||
padding: 3pt;
|
||||
padding-top: 6pt;
|
||||
}
|
||||
|
||||
.section-header label {
|
||||
.section-header label, .sub-section-header label {
|
||||
font-weight: bold;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.sub-section-header {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.sub-section-header label {
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
.multi-property-section {
|
||||
}
|
||||
|
||||
.property-section {
|
||||
display: block;
|
||||
margin: 10 10;
|
||||
|
@ -132,7 +143,7 @@ input.no-spin::-webkit-inner-spin-button {
|
|||
table#entity-table {
|
||||
border-collapse: collapse;
|
||||
font-family: Sans-Serif;
|
||||
font-size: 7.5pt;
|
||||
font-size: 9pt;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
@ -156,7 +167,7 @@ table#entity-table {
|
|||
}
|
||||
|
||||
#entity-table td {
|
||||
font-size: 8.25pt;
|
||||
font-size: 9.0pt;
|
||||
border: 0pt black solid;
|
||||
word-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
|
@ -176,29 +187,29 @@ th#entity-type {
|
|||
|
||||
div.input-area {
|
||||
display: inline-block;
|
||||
font-size: 7.5pt;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
input {
|
||||
}
|
||||
|
||||
#type {
|
||||
font-size: 10.5pt;
|
||||
#type, #id {
|
||||
font-size: 9.0pt;
|
||||
}
|
||||
|
||||
#type label {
|
||||
#type label, #id label {
|
||||
color: rgb(150, 150, 150);
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
background-color: rgb(102, 102, 102);
|
||||
color: rgb(204, 204, 204);
|
||||
background-color: rgb(63, 63, 63);
|
||||
color: rgb(255, 255, 255);
|
||||
border: none;
|
||||
font-size: 7.5pt;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
input:disabled, textarea:disabled {
|
||||
background-color: rgb(102, 102, 102);
|
||||
background-color: rgb(63, 63, 63);
|
||||
color: rgb(160, 160, 160);
|
||||
}
|
||||
|
||||
|
@ -224,7 +235,7 @@ input:disabled, textarea:disabled {
|
|||
#properties-list .property {
|
||||
padding: 4pt;
|
||||
border-bottom: 0.75pt solid rgb(63, 63, 63);
|
||||
min-height: 1em;
|
||||
min-height: 12pt;
|
||||
}
|
||||
|
||||
|
||||
|
@ -245,7 +256,6 @@ table#properties-list {
|
|||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
vertical-align: middle;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
@ -270,9 +280,7 @@ div.inner {
|
|||
}
|
||||
|
||||
td {
|
||||
|
||||
|
||||
vertical-align: top;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#no-entities {
|
||||
|
|
|
@ -126,12 +126,12 @@ Tool.prototype = new Overlay2D;
|
|||
Tool.IMAGE_HEIGHT = 50;
|
||||
Tool.IMAGE_WIDTH = 50;
|
||||
|
||||
ToolBar = function(x, y, direction) {
|
||||
ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPositionFunction) {
|
||||
this.tools = new Array();
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.height = ToolBar.TITLE_BAR_HEIGHT;
|
||||
this.back = this.back = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 255, green: 255, blue: 255 },
|
||||
x: this.x,
|
||||
|
@ -336,7 +336,7 @@ ToolBar = function(x, y, direction) {
|
|||
return (that.x <= x) && (x <= (that.x + that.width)) &&
|
||||
(that.y <= y) && (y <= (that.y + that.height));
|
||||
}
|
||||
that.hover = function (enable) {
|
||||
that.hover = function (enable) { // Can be overriden or extended by clients.
|
||||
that.isHovering = enable;
|
||||
if (that.back) {
|
||||
if (enable) {
|
||||
|
@ -348,16 +348,36 @@ ToolBar = function(x, y, direction) {
|
|||
});
|
||||
}
|
||||
};
|
||||
that.windowDimensions = Controller.getViewportDimensions();
|
||||
// Maybe fixme: Keeping the same percent of the window size isn't always the right thing.
|
||||
// For example, maybe we want "keep the same percentage to whatever two edges are closest to the edge of screen".
|
||||
// If we change that, the places to do so are onResizeViewport, save (maybe), and the initial move based on Settings, below.
|
||||
that.onResizeViewport = function (newSize) { // Can be overridden or extended by clients.
|
||||
var fractionX = that.x / that.windowDimensions.x;
|
||||
var fractionY = that.y / that.windowDimensions.y;
|
||||
that.windowDimensions = newSize || Controller.getViewportDimensions();
|
||||
that.move(fractionX * that.windowDimensions.x, fractionY * that.windowDimensions.y);
|
||||
};
|
||||
if (optionalPersistenceKey) {
|
||||
this.fractionKey = optionalPersistenceKey + '.fraction';
|
||||
this.save = function () {
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
var fraction = {x: that.x / screenSize.x, y: that.y / screenSize.y};
|
||||
Settings.setValue(this.fractionKey, JSON.stringify(fraction));
|
||||
}
|
||||
} else {
|
||||
this.save = function () { }; // Called on move. Can be overriden or extended by clients.
|
||||
}
|
||||
// These are currently only doing that which is necessary for toolbar hover and toolbar drag.
|
||||
// They have not yet been extended to tool hover/click/release, etc.
|
||||
this.mousePressEvent = function (event) {
|
||||
if (!that.contains(event)) {
|
||||
if (Overlays.getOverlayAtPoint({ x: event.x, y: event.y }) == that.back) {
|
||||
that.mightBeDragging = true;
|
||||
that.dragOffsetX = that.x - event.x;
|
||||
that.dragOffsetY = that.y - event.y;
|
||||
} else {
|
||||
that.mightBeDragging = false;
|
||||
return;
|
||||
}
|
||||
that.mightBeDragging = true;
|
||||
that.dragOffsetX = that.x - event.x;
|
||||
that.dragOffsetY = that.y - event.y;
|
||||
};
|
||||
this.mouseMove = function (event) {
|
||||
if (!that.mightBeDragging || !event.isLeftButton) {
|
||||
|
@ -375,15 +395,19 @@ ToolBar = function(x, y, direction) {
|
|||
}
|
||||
that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y);
|
||||
};
|
||||
that.checkResize = function () { // Can be overriden or extended, but usually not. See onResizeViewport.
|
||||
var currentWindowSize = Controller.getViewportDimensions();
|
||||
if ((currentWindowSize.x !== that.windowDimensions.x) || (currentWindowSize.y !== that.windowDimensions.y)) {
|
||||
that.onResizeViewport(currentWindowSize);
|
||||
}
|
||||
};
|
||||
Controller.mousePressEvent.connect(this.mousePressEvent);
|
||||
Controller.mouseMoveEvent.connect(this.mouseMove);
|
||||
// Called on move. A different approach would be to have all this on the prototype,
|
||||
// and let apps extend where needed. Ex. app defines its toolbar.move() to call this.__proto__.move and then save.
|
||||
this.save = function () { };
|
||||
Script.update.connect(that.checkResize);
|
||||
// This compatability hack breaks the model, but makes converting existing scripts easier:
|
||||
this.addOverlay = function (ignored, oldSchoolProperties) {
|
||||
var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy
|
||||
if (that.numberOfTools() === 0) {
|
||||
if ((that.numberOfTools() === 0) && (properties.x != undefined) && (properties.y != undefined)) {
|
||||
that.move(properties.x, properties.y);
|
||||
}
|
||||
delete properties.x;
|
||||
|
@ -392,7 +416,25 @@ ToolBar = function(x, y, direction) {
|
|||
var id = that.tools[index].overlay();
|
||||
return id;
|
||||
}
|
||||
if (this.fractionKey || optionalInitialPositionFunction) {
|
||||
var savedFraction = JSON.parse(Settings.getValue(this.fractionKey) || '0'); // getValue can answer empty string
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
if (savedFraction) {
|
||||
// If we have saved data, keep the toolbar at the same proportion of the screen width/height.
|
||||
that.move(savedFraction.x * screenSize.x, savedFraction.y * screenSize.y);
|
||||
} else if (!optionalInitialPositionFunction) {
|
||||
print("No initPosition(screenSize, intializedToolbar) specified for ToolBar");
|
||||
} else {
|
||||
// Call the optionalInitialPositionFunctinon() AFTER the client has had a chance to set up.
|
||||
var that = this;
|
||||
Script.setTimeout(function () {
|
||||
var position = optionalInitialPositionFunction(screenSize, that);
|
||||
that.move(position.x, position.y);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
ToolBar.SPACING = 4;
|
||||
ToolBar.VERTICAL = 0;
|
||||
ToolBar.HORIZONTAL = 1;
|
||||
ToolBar.TITLE_BAR_HEIGHT = 10;
|
||||
|
|
|
@ -10,17 +10,16 @@
|
|||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
Script.include('lineRider.js')
|
||||
var MAX_POINTS_PER_LINE = 30;
|
||||
var LINE_LIFETIME = 60 * 5 //5 minute lifetime
|
||||
|
||||
var LINE_DIMENSIONS = 5;
|
||||
var LIFETIME = 6000;
|
||||
|
||||
var colorPalette = [{
|
||||
red: 236,
|
||||
green: 208,
|
||||
blue: 120
|
||||
}, {
|
||||
red: 217,
|
||||
red: 214,
|
||||
green: 91,
|
||||
blue: 67
|
||||
}, {
|
||||
|
@ -40,15 +39,6 @@ var colorPalette = [{
|
|||
var currentColorIndex = 0;
|
||||
var currentColor = colorPalette[currentColorIndex];
|
||||
|
||||
|
||||
|
||||
if (hydraCheck() === true) {
|
||||
HydraPaint();
|
||||
} else {
|
||||
MousePaint();
|
||||
}
|
||||
|
||||
|
||||
function cycleColor() {
|
||||
currentColor = colorPalette[++currentColorIndex];
|
||||
if (currentColorIndex === colorPalette.length - 1) {
|
||||
|
@ -57,42 +47,17 @@ function cycleColor() {
|
|||
|
||||
}
|
||||
|
||||
function hydraCheck() {
|
||||
var numberOfButtons = Controller.getNumberOfButtons();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
|
||||
return hydrasConnected; //hydrasConnected;
|
||||
}
|
||||
|
||||
//************ Mouse Paint **************************
|
||||
MousePaint();
|
||||
|
||||
function MousePaint() {
|
||||
var DRAWING_DISTANCE = 2;
|
||||
var DRAWING_DISTANCE = 5;
|
||||
var lines = [];
|
||||
var deletedLines = [];
|
||||
var isDrawing = false;
|
||||
var path = [];
|
||||
|
||||
var lineRider = new LineRider();
|
||||
lineRider.addStartHandler(function() {
|
||||
var points = [];
|
||||
//create points array from list of all points in path
|
||||
path.forEach(function(point) {
|
||||
points.push(point);
|
||||
});
|
||||
lineRider.setPath(points);
|
||||
});
|
||||
|
||||
|
||||
|
||||
var LINE_WIDTH = 7;
|
||||
var line;
|
||||
var points = [];
|
||||
var line, linePosition;
|
||||
|
||||
|
||||
var BRUSH_SIZE = 0.02;
|
||||
var BRUSH_SIZE = .05;
|
||||
|
||||
var brush = Entities.addEntity({
|
||||
type: 'Sphere',
|
||||
|
@ -110,51 +75,42 @@ function MousePaint() {
|
|||
});
|
||||
|
||||
|
||||
function newLine(point) {
|
||||
function newLine(position) {
|
||||
linePosition = position;
|
||||
line = Entities.addEntity({
|
||||
position: MyAvatar.position,
|
||||
position: position,
|
||||
type: "Line",
|
||||
color: currentColor,
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
x: LINE_DIMENSIONS,
|
||||
y: LINE_DIMENSIONS,
|
||||
z: LINE_DIMENSIONS
|
||||
},
|
||||
linePoints: [],
|
||||
lineWidth: LINE_WIDTH,
|
||||
lifetime: LINE_LIFETIME
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
points = [];
|
||||
if (point) {
|
||||
|
||||
points.push(point);
|
||||
path.push(point);
|
||||
}
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
|
||||
var worldPoint = computeWorldPoint(event);
|
||||
Entities.editEntity(brush, {
|
||||
position: worldPoint
|
||||
});
|
||||
|
||||
if (!isDrawing) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE);
|
||||
var point = Vec3.sum(Camera.getPosition(), addVector);
|
||||
points.push(point);
|
||||
path.push(point);
|
||||
Entities.editEntity(line, {
|
||||
linePoints: points
|
||||
});
|
||||
Entities.editEntity(brush, {
|
||||
position: point
|
||||
});
|
||||
var localPoint = computeLocalPoint(event)
|
||||
var success = Entities.appendPoint(line, localPoint);
|
||||
|
||||
|
||||
if (points.length === MAX_POINTS_PER_LINE) {
|
||||
//We need to start a new line!
|
||||
newLine(point);
|
||||
if (!success) {
|
||||
newLine(worldPoint);
|
||||
Entities.appendPoint(line, computeLocalPoint(event));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,17 +127,26 @@ function MousePaint() {
|
|||
lines.push(restoredLine);
|
||||
}
|
||||
|
||||
function computeWorldPoint(event) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE);
|
||||
return Vec3.sum(Camera.getPosition(), addVector);
|
||||
}
|
||||
|
||||
function computeLocalPoint(event) {
|
||||
|
||||
var localPoint = Vec3.subtract(computeWorldPoint(event), linePosition);
|
||||
return localPoint;
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if(!event.isLeftButton) {
|
||||
if (!event.isLeftButton) {
|
||||
isDrawing = false;
|
||||
return;
|
||||
}
|
||||
lineRider.mousePressEvent(event);
|
||||
path = [];
|
||||
newLine();
|
||||
newLine(computeWorldPoint(event));
|
||||
isDrawing = true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
function mouseReleaseEvent() {
|
||||
|
@ -198,21 +163,21 @@ function MousePaint() {
|
|||
if (event.text === "z") {
|
||||
undoStroke();
|
||||
}
|
||||
if(event.text === "x") {
|
||||
if (event.text === "x") {
|
||||
redoStroke();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function cleanup() {
|
||||
lines.forEach(function(line) {
|
||||
Entities.deleteEntity(line);
|
||||
// Entities.deleteEntity(line);
|
||||
});
|
||||
Entities.deleteEntity(brush);
|
||||
lineRider.cleanup();
|
||||
|
||||
}
|
||||
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
@ -222,266 +187,6 @@ function MousePaint() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
//*****************HYDRA PAINT *******************************************
|
||||
|
||||
|
||||
|
||||
function HydraPaint() {
|
||||
|
||||
|
||||
|
||||
var lineRider = new LineRider();
|
||||
lineRider.addStartHandler(function() {
|
||||
var points = [];
|
||||
//create points array from list of all points in path
|
||||
rightController.path.forEach(function(point) {
|
||||
points.push(point);
|
||||
});
|
||||
lineRider.setPath(points);
|
||||
});
|
||||
|
||||
var LEFT = 0;
|
||||
var RIGHT = 1;
|
||||
|
||||
var currentTime = 0;
|
||||
|
||||
|
||||
var minBrushSize = .02;
|
||||
var maxBrushSize = .04
|
||||
|
||||
|
||||
var minLineWidth = 5;
|
||||
var maxLineWidth = 10;
|
||||
var currentLineWidth = minLineWidth;
|
||||
var MIN_PAINT_TRIGGER_THRESHOLD = .01;
|
||||
var COLOR_CHANGE_TIME_FACTOR = 0.1;
|
||||
|
||||
var RIGHT_BUTTON_1 = 7
|
||||
var RIGHT_BUTTON_2 = 8
|
||||
var RIGHT_BUTTON_3 = 9;
|
||||
var RIGHT_BUTTON_4 = 10
|
||||
|
||||
var LEFT_BUTTON_1 = 1;
|
||||
var LEFT_BUTTON_2 = 2;
|
||||
var LEFT_BUTTON_3 = 3;
|
||||
var LEFT_BUTTON_4 = 4;
|
||||
|
||||
var STROKE_SMOOTH_FACTOR = 1;
|
||||
|
||||
var MIN_DRAW_DISTANCE = 0.2;
|
||||
var MAX_DRAW_DISTANCE = 0.4;
|
||||
|
||||
function controller(side, undoButton, redoButton, cycleColorButton, startRideButton) {
|
||||
this.triggerHeld = false;
|
||||
this.triggerThreshold = 0.9;
|
||||
this.side = side;
|
||||
this.palm = 2 * side;
|
||||
this.tip = 2 * side + 1;
|
||||
this.trigger = side;
|
||||
this.lines = [];
|
||||
this.deletedLines = [] //just an array of properties objects
|
||||
this.isPainting = false;
|
||||
|
||||
this.undoButton = undoButton;
|
||||
this.undoButtonPressed = false;
|
||||
this.prevUndoButtonPressed = false;
|
||||
|
||||
this.redoButton = redoButton;
|
||||
this.redoButtonPressed = false;
|
||||
this.prevRedoButtonPressed = false;
|
||||
|
||||
this.cycleColorButton = cycleColorButton;
|
||||
this.cycleColorButtonPressed = false;
|
||||
this.prevColorCycleButtonPressed = false;
|
||||
|
||||
this.startRideButton = startRideButton;
|
||||
this.startRideButtonPressed = false;
|
||||
this.prevStartRideButtonPressed = false;
|
||||
|
||||
this.strokeCount = 0;
|
||||
this.currentBrushSize = minBrushSize;
|
||||
this.points = [];
|
||||
this.path = [];
|
||||
|
||||
this.brush = Entities.addEntity({
|
||||
type: 'Sphere',
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
color: currentColor,
|
||||
dimensions: {
|
||||
x: minBrushSize,
|
||||
y: minBrushSize,
|
||||
z: minBrushSize
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.newLine = function(point) {
|
||||
this.line = Entities.addEntity({
|
||||
position: MyAvatar.position,
|
||||
type: "Line",
|
||||
color: currentColor,
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
},
|
||||
lineWidth: 5,
|
||||
lifetime: LINE_LIFETIME
|
||||
});
|
||||
this.points = [];
|
||||
if (point) {
|
||||
this.points.push(point);
|
||||
this.path.push(point);
|
||||
}
|
||||
this.lines.push(this.line);
|
||||
}
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
this.updateControllerState();
|
||||
this.avatarPalmOffset = Vec3.subtract(this.palmPosition, MyAvatar.position);
|
||||
this.projectedForwardDistance = Vec3.dot(Quat.getFront(Camera.getOrientation()), this.avatarPalmOffset);
|
||||
this.mappedPalmOffset = map(this.projectedForwardDistance, -.5, .5, MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
|
||||
this.tipDirection = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition));
|
||||
this.offsetVector = Vec3.multiply(this.mappedPalmOffset, this.tipDirection);
|
||||
this.drawPoint = Vec3.sum(this.palmPosition, this.offsetVector);
|
||||
this.currentBrushSize = map(this.triggerValue, 0, 1, minBrushSize, maxBrushSize);
|
||||
Entities.editEntity(this.brush, {
|
||||
position: this.drawPoint,
|
||||
dimensions: {
|
||||
x: this.currentBrushSize,
|
||||
y: this.currentBrushSize,
|
||||
z: this.currentBrushSize
|
||||
},
|
||||
color: currentColor
|
||||
});
|
||||
if (this.triggerValue > MIN_PAINT_TRIGGER_THRESHOLD) {
|
||||
if (!this.isPainting) {
|
||||
this.isPainting = true;
|
||||
this.newLine();
|
||||
this.path = [];
|
||||
}
|
||||
if (this.strokeCount % STROKE_SMOOTH_FACTOR === 0) {
|
||||
this.paint(this.drawPoint);
|
||||
}
|
||||
this.strokeCount++;
|
||||
} else if (this.triggerValue < MIN_PAINT_TRIGGER_THRESHOLD && this.isPainting) {
|
||||
this.releaseTrigger();
|
||||
}
|
||||
|
||||
this.oldPalmPosition = this.palmPosition;
|
||||
this.oldTipPosition = this.tipPosition;
|
||||
}
|
||||
|
||||
this.releaseTrigger = function() {
|
||||
this.isPainting = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.updateControllerState = function() {
|
||||
this.undoButtonPressed = Controller.isButtonPressed(this.undoButton);
|
||||
this.redoButtonPressed = Controller.isButtonPressed(this.redoButton);
|
||||
this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton);
|
||||
this.startRideButtonPressed = Controller.isButtonPressed(this.startRideButton);
|
||||
|
||||
//This logic gives us button release
|
||||
if (this.prevUndoButtonPressed === true && this.undoButtonPressed === false) {
|
||||
//User released undo button, so undo
|
||||
this.undoStroke();
|
||||
}
|
||||
if (this.prevRedoButtonPressed === true && this.redoButtonPressed === false) {
|
||||
this.redoStroke();
|
||||
}
|
||||
|
||||
if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {
|
||||
cycleColor();
|
||||
Entities.editEntity(this.brush, {
|
||||
color: currentColor
|
||||
});
|
||||
}
|
||||
if (this.prevStartRideButtonPressed === true && this.startRideButtonPressed === false) {
|
||||
lineRider.toggleRide();
|
||||
}
|
||||
this.prevRedoButtonPressed = this.redoButtonPressed;
|
||||
this.prevUndoButtonPressed = this.undoButtonPressed;
|
||||
this.prevCycleColorButtonPressed = this.cycleColorButtonPressed;
|
||||
this.prevStartRideButtonPressed = this.startRideButtonPressed;
|
||||
|
||||
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
|
||||
this.triggerValue = Controller.getTriggerValue(this.trigger);
|
||||
}
|
||||
|
||||
this.undoStroke = function() {
|
||||
var deletedLine = this.lines.pop();
|
||||
var deletedLineProps = Entities.getEntityProperties(deletedLine);
|
||||
this.deletedLines.push(deletedLineProps);
|
||||
Entities.deleteEntity(deletedLine);
|
||||
}
|
||||
|
||||
this.redoStroke = function() {
|
||||
var restoredLine = Entities.addEntity(this.deletedLines.pop());
|
||||
Entities.addEntity(restoredLine);
|
||||
this.lines.push(restoredLine);
|
||||
}
|
||||
|
||||
this.paint = function(point) {
|
||||
|
||||
currentLineWidth = map(this.triggerValue, 0, 1, minLineWidth, maxLineWidth);
|
||||
this.points.push(point);
|
||||
this.path.push(point);
|
||||
Entities.editEntity(this.line, {
|
||||
linePoints: this.points,
|
||||
lineWidth: currentLineWidth,
|
||||
});
|
||||
if (this.points.length > MAX_POINTS_PER_LINE) {
|
||||
this.newLine(point);
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanup = function() {
|
||||
Entities.deleteEntity(this.brush);
|
||||
this.lines.forEach(function(line) {
|
||||
Entities.deleteEntity(line);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
rightController.update(deltaTime);
|
||||
leftController.update(deltaTime);
|
||||
currentTime += deltaTime;
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
rightController.cleanup();
|
||||
leftController.cleanup();
|
||||
lineRider.cleanup();
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
lineRider.mousePressEvent(event);
|
||||
}
|
||||
|
||||
function vectorIsZero(v) {
|
||||
return v.x === 0 && v.y === 0 && v.z === 0;
|
||||
}
|
||||
|
||||
|
||||
var rightController = new controller(RIGHT, RIGHT_BUTTON_3, RIGHT_BUTTON_4, RIGHT_BUTTON_1, RIGHT_BUTTON_2);
|
||||
var leftController = new controller(LEFT, LEFT_BUTTON_3, LEFT_BUTTON_4, LEFT_BUTTON_1, LEFT_BUTTON_2);
|
||||
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
||||
}
|
||||
|
||||
function randFloat(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
|
|
@ -29,19 +29,13 @@ var buttonOnColor = {
|
|||
};
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
|
||||
var userCanPoint = false;
|
||||
Script.include(["libraries/toolBars.js"]);
|
||||
const persistKey = "highfidelity.pointer.toolbar.position";
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
||||
toolBar.save = function () {
|
||||
Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y]));
|
||||
};
|
||||
var old = JSON.parse(Settings.getValue(persistKey) || '0');
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.pointer.toolbar", function (screenSize) {
|
||||
return {x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, y: screenSize.y - (BUTTON_SIZE + PADDING)},
|
||||
});
|
||||
var pointerButton = toolBar.addOverlay("image", {
|
||||
x: old ? old[0] : screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
|
||||
y: old ? old[1] : screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png",
|
||||
|
|
|
@ -9,48 +9,52 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
function hslToRgb(hslColor) {
|
||||
var h = hslColor.hue;
|
||||
var s = hslColor.sat;
|
||||
var l = hslColor.light;
|
||||
var r, g, b;
|
||||
|
||||
if (s == 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
var hue2rgb = function hue2rgb(p, q, t) {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
|
||||
map = function(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
hslToRgb = function(hslColor) {
|
||||
var h = hslColor.hue;
|
||||
var s = hslColor.sat;
|
||||
var l = hslColor.light;
|
||||
var r, g, b;
|
||||
|
||||
if (s == 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
var hue2rgb = function hue2rgb(p, q, t) {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
|
||||
return {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
return {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function map(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
function randFloat(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
|
||||
randFloat = function(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
|
||||
function randInt(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
randInt = function(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
}
|
Before Width: | Height: | Size: 369 B |
50
interface/resources/images/left-arrow-disabled.svg
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="left-arrow.svg"><metadata
|
||||
id="metadata13"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs11" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="775"
|
||||
id="namedview9"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.131736"
|
||||
inkscape:cx="12.772881"
|
||||
inkscape:cy="16.700001"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g5" /><g
|
||||
id="g3"
|
||||
transform="matrix(-1,0,0,1,287.5,0)"><g
|
||||
id="g5"><path
|
||||
d="m 133.1,714.2 21.3,16.7 -21.3,16.7 0,-33.4 z"
|
||||
id="path7"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#cccccc;fill-opacity:1" /></g></g></svg>
|
After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 369 B |
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#7E7E7E" d="M133.1,714.2l21.3,16.7l-21.3,16.7V714.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 501 B |
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FF5353" d="M133.1,714.2l21.3,16.7l-21.3,16.7V714.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 501 B |
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="225.8 714.2 326.5 512"
|
||||
enable-background="new 225.8 714.2 326.5 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#FF5353" d="M552.4,1226.2l-326.5-256l326.5-256V1226.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 503 B |
11
interface/resources/images/right-arrow-disabled.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns:xl="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="133.1 714.2 21.3 33.4"
|
||||
enable-background="new 133.1 714.2 21.3 33.4" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M133.1,714.2l21.3,16.7l-21.3,16.7V714.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 507 B |
Before Width: | Height: | Size: 501 B After Width: | Height: | Size: 501 B |
Before Width: | Height: | Size: 127 B |
|
@ -1,3 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="344 454 26 74" width="26pt" height="74pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2015-06-12 18:23Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.4 --></metadata><defs></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="1728" height="1466"/><g><title> Navi Bar</title><line x1="356.58927" y1="466.42861" x2="356.58927" y2="515.4286" stroke="#b3b3b3" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="3"/></g></g></svg>
|
Before Width: | Height: | Size: 778 B |
|
@ -46,10 +46,40 @@ DialogContainer {
|
|||
property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area
|
||||
property int inputAreaStep: (height - inputAreaHeight) / 2
|
||||
|
||||
MouseArea {
|
||||
// Drag the icon
|
||||
width: parent.height
|
||||
height: parent.height
|
||||
x: 0
|
||||
y: 0
|
||||
drag {
|
||||
target: root
|
||||
minimumX: -parent.inputAreaStep
|
||||
minimumY: -parent.inputAreaStep
|
||||
maximumX: root.parent ? root.maximumX : 0
|
||||
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
// Drag the input rectangle
|
||||
width: parent.width - parent.height
|
||||
height: parent.inputAreaHeight
|
||||
x: parent.height
|
||||
y: parent.inputAreaStep
|
||||
drag {
|
||||
target: root
|
||||
minimumX: -parent.inputAreaStep
|
||||
minimumY: -parent.inputAreaStep
|
||||
maximumX: root.parent ? root.maximumX : 0
|
||||
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: backArrow
|
||||
|
||||
source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/redarrow_reversed.svg"
|
||||
source: addressBarDialog.backEnabled ? "../images/left-arrow.svg" : "../images/left-arrow-disabled.svg"
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
|
@ -71,7 +101,7 @@ DialogContainer {
|
|||
Image {
|
||||
id: forwardArrow
|
||||
|
||||
source: addressBarDialog.forwardEnabled ? "../images/darkgreyarrow.svg" : "../images/redarrow.svg"
|
||||
source: addressBarDialog.forwardEnabled ? "../images/right-arrow.svg" : "../images/right-arrow-disabled.svg"
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
|
@ -111,38 +141,7 @@ DialogContainer {
|
|||
addressBarDialog.loadAddress(addressLine.text)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
// Drag the icon
|
||||
width: parent.height
|
||||
height: parent.height
|
||||
x: 0
|
||||
y: 0
|
||||
drag {
|
||||
target: root
|
||||
minimumX: -parent.inputAreaStep
|
||||
minimumY: -parent.inputAreaStep
|
||||
maximumX: root.parent ? root.maximumX : 0
|
||||
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
|
||||
}
|
||||
}
|
||||
|
||||
// Add this code to make text bar draggable
|
||||
/*
|
||||
MouseArea {
|
||||
// Drag the input rectangle
|
||||
width: parent.width - parent.height
|
||||
height: parent.inputAreaHeight
|
||||
x: parent.height
|
||||
y: parent.inputAreaStep
|
||||
drag {
|
||||
target: root
|
||||
minimumX: -parent.inputAreaStep
|
||||
minimumY: -parent.inputAreaStep
|
||||
maximumX: root.parent ? root.maximumX : 0
|
||||
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,11 +12,6 @@ Item {
|
|||
implicitWidth: row.width
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
readonly property int sTATS_GENERAL_MIN_WIDTH: 165
|
||||
readonly property int sTATS_PING_MIN_WIDTH: 190
|
||||
readonly property int sTATS_GEO_MIN_WIDTH: 240
|
||||
readonly property int sTATS_OCTREE_MIN_WIDTH: 410
|
||||
readonly property int fontSize: 12
|
||||
readonly property string fontColor: "white"
|
||||
readonly property string bgColor: "#99333333"
|
||||
|
@ -37,7 +32,6 @@ Item {
|
|||
Column {
|
||||
id: generalCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_GENERAL_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
|
@ -78,7 +72,6 @@ Item {
|
|||
Column {
|
||||
id: pingCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_PING_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor
|
||||
font.pixelSize: root.fontSize
|
||||
|
@ -114,7 +107,6 @@ Item {
|
|||
Column {
|
||||
id: geoCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_GEO_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
|
@ -157,7 +149,6 @@ Item {
|
|||
Column {
|
||||
id: octreeCol
|
||||
spacing: 4; x: 4; y: 4;
|
||||
width: sTATS_OCTREE_MIN_WIDTH
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
|
|
|
@ -984,7 +984,7 @@ void Application::paintGL() {
|
|||
glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
|
||||
glBlitFramebuffer(0, 0, _renderResolution.x, _renderResolution.y,
|
||||
0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
_compositor.displayOverlayTexture(&renderArgs);
|
||||
|
@ -1063,9 +1063,7 @@ void Application::resizeGL() {
|
|||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
auto canvasSize = _glWidget->size();
|
||||
if (canvasSize != offscreenUi->getWindow()->size()) {
|
||||
offscreenUi->resize(canvasSize);
|
||||
}
|
||||
offscreenUi->resize(canvasSize);
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
}
|
||||
|
@ -2453,16 +2451,18 @@ void Application::update(float deltaTime) {
|
|||
|
||||
// Transfer the user inputs to the driveKeys
|
||||
_myAvatar->clearDriveKeys();
|
||||
_myAvatar->setDriveKeys(FWD, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_FORWARD));
|
||||
_myAvatar->setDriveKeys(BACK, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_BACKWARD));
|
||||
_myAvatar->setDriveKeys(UP, _userInputMapper.getActionState(UserInputMapper::VERTICAL_UP));
|
||||
_myAvatar->setDriveKeys(DOWN, _userInputMapper.getActionState(UserInputMapper::VERTICAL_DOWN));
|
||||
_myAvatar->setDriveKeys(LEFT, _userInputMapper.getActionState(UserInputMapper::LATERAL_LEFT));
|
||||
_myAvatar->setDriveKeys(RIGHT, _userInputMapper.getActionState(UserInputMapper::LATERAL_RIGHT));
|
||||
_myAvatar->setDriveKeys(ROT_UP, _userInputMapper.getActionState(UserInputMapper::PITCH_UP));
|
||||
_myAvatar->setDriveKeys(ROT_DOWN, _userInputMapper.getActionState(UserInputMapper::PITCH_DOWN));
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, _userInputMapper.getActionState(UserInputMapper::YAW_LEFT));
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, _userInputMapper.getActionState(UserInputMapper::YAW_RIGHT));
|
||||
if (!_controllerScriptingInterface.areActionsCaptured()) {
|
||||
_myAvatar->setDriveKeys(FWD, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_FORWARD));
|
||||
_myAvatar->setDriveKeys(BACK, _userInputMapper.getActionState(UserInputMapper::LONGITUDINAL_BACKWARD));
|
||||
_myAvatar->setDriveKeys(UP, _userInputMapper.getActionState(UserInputMapper::VERTICAL_UP));
|
||||
_myAvatar->setDriveKeys(DOWN, _userInputMapper.getActionState(UserInputMapper::VERTICAL_DOWN));
|
||||
_myAvatar->setDriveKeys(LEFT, _userInputMapper.getActionState(UserInputMapper::LATERAL_LEFT));
|
||||
_myAvatar->setDriveKeys(RIGHT, _userInputMapper.getActionState(UserInputMapper::LATERAL_RIGHT));
|
||||
_myAvatar->setDriveKeys(ROT_UP, _userInputMapper.getActionState(UserInputMapper::PITCH_UP));
|
||||
_myAvatar->setDriveKeys(ROT_DOWN, _userInputMapper.getActionState(UserInputMapper::PITCH_DOWN));
|
||||
_myAvatar->setDriveKeys(ROT_LEFT, _userInputMapper.getActionState(UserInputMapper::YAW_LEFT));
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, _userInputMapper.getActionState(UserInputMapper::YAW_RIGHT));
|
||||
}
|
||||
_myAvatar->setDriveKeys(BOOM_IN, _userInputMapper.getActionState(UserInputMapper::BOOM_IN));
|
||||
_myAvatar->setDriveKeys(BOOM_OUT, _userInputMapper.getActionState(UserInputMapper::BOOM_OUT));
|
||||
|
||||
|
@ -4748,3 +4748,10 @@ mat4 Application::getEyePose(int eye) const {
|
|||
|
||||
return mat4();
|
||||
}
|
||||
|
||||
mat4 Application::getHeadPose() const {
|
||||
if (isHMDMode()) {
|
||||
return OculusManager::getHeadPose();
|
||||
}
|
||||
return mat4();
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ Avatar::Avatar() :
|
|||
}
|
||||
|
||||
Avatar::~Avatar() {
|
||||
assert(_motionState == nullptr);
|
||||
for(auto attachment : _unusedAttachments) {
|
||||
delete attachment;
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
// DO NOT update or fade out uninitialized Avatars
|
||||
++avatarIterator;
|
||||
} else if (avatar->shouldDie()) {
|
||||
removeAvatarMotionState(avatar);
|
||||
_avatarFades.push_back(avatarIterator.value());
|
||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||
} else {
|
||||
|
@ -163,12 +164,13 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
}
|
||||
|
||||
// protected
|
||||
void AvatarManager::removeAvatarMotionState(Avatar* avatar) {
|
||||
AvatarMotionState* motionState= avatar->_motionState;
|
||||
void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
|
||||
auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
|
||||
AvatarMotionState* motionState= rawPointer->_motionState;
|
||||
if (motionState) {
|
||||
// clean up physics stuff
|
||||
motionState->clearObjectBackPointer();
|
||||
avatar->_motionState = nullptr;
|
||||
rawPointer->_motionState = nullptr;
|
||||
_avatarMotionStates.remove(motionState);
|
||||
_motionStatesToAdd.remove(motionState);
|
||||
_motionStatesToDelete.push_back(motionState);
|
||||
|
@ -181,7 +183,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
|||
if (avatarIterator != _avatarHash.end()) {
|
||||
std::shared_ptr<Avatar> avatar = std::dynamic_pointer_cast<Avatar>(avatarIterator.value());
|
||||
if (avatar != _myAvatar && avatar->isInitialized()) {
|
||||
removeAvatarMotionState(avatar.get());
|
||||
removeAvatarMotionState(avatar);
|
||||
_avatarFades.push_back(avatarIterator.value());
|
||||
_avatarHash.erase(avatarIterator);
|
||||
}
|
||||
|
@ -197,7 +199,7 @@ void AvatarManager::clearOtherAvatars() {
|
|||
// don't remove myAvatar or uninitialized avatars from the list
|
||||
++avatarIterator;
|
||||
} else {
|
||||
removeAvatarMotionState(avatar.get());
|
||||
removeAvatarMotionState(avatar);
|
||||
_avatarFades.push_back(avatarIterator.value());
|
||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ private:
|
|||
// virtual overrides
|
||||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
void removeAvatarMotionState(Avatar* avatar);
|
||||
void removeAvatarMotionState(AvatarSharedPointer avatar);
|
||||
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarFades;
|
||||
|
|
|
@ -124,18 +124,25 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
|
||||
if (!(_isFaceTrackerConnected || billboard)) {
|
||||
// Update eye saccades
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f;
|
||||
const float AVERAGE_SACCADE_INTERVAL = 4.0f;
|
||||
const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f;
|
||||
const float AVERAGE_SACCADE_INTERVAL = 6.0f;
|
||||
const float MICROSACCADE_MAGNITUDE = 0.002f;
|
||||
const float SACCADE_MAGNITUDE = 0.04f;
|
||||
|
||||
const float MAXIMUM_SACCADE_SPEED = 0.8f;
|
||||
|
||||
if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) {
|
||||
_saccadeTarget = MICROSACCADE_MAGNITUDE * randVector();
|
||||
} else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) {
|
||||
_saccadeTarget = SACCADE_MAGNITUDE * randVector();
|
||||
}
|
||||
_saccade += (_saccadeTarget - _saccade) * 0.50f;
|
||||
|
||||
|
||||
glm::vec3 saccadeDelta = (_saccadeTarget - _saccade) * 0.5f;
|
||||
float speed = glm::length(saccadeDelta) / deltaTime;
|
||||
if (speed > MAXIMUM_SACCADE_SPEED) {
|
||||
saccadeDelta = saccadeDelta * MAXIMUM_SACCADE_SPEED / speed;
|
||||
}
|
||||
_saccade += saccadeDelta;
|
||||
|
||||
// Detect transition from talking to not; force blink after that and a delay
|
||||
bool forceBlink = false;
|
||||
const float TALKING_LOUDNESS = 100.0f;
|
||||
|
|
|
@ -76,7 +76,6 @@ const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
|||
|
||||
MyAvatar::MyAvatar() :
|
||||
Avatar(),
|
||||
_turningKeyPressTime(0.0f),
|
||||
_gravity(0.0f, 0.0f, 0.0f),
|
||||
_wasPushing(false),
|
||||
_isPushing(false),
|
||||
|
@ -1205,28 +1204,9 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs, const glm::vec3& c
|
|||
}
|
||||
|
||||
void MyAvatar::updateOrientation(float deltaTime) {
|
||||
// Gather rotation information from keyboard
|
||||
const float TIME_BETWEEN_HMD_TURNS = 0.5f;
|
||||
const float HMD_TURN_DEGREES = 22.5f;
|
||||
if (!qApp->isHMDMode()) {
|
||||
// Smoothly rotate body with arrow keys if not in HMD
|
||||
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime;
|
||||
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime;
|
||||
} else {
|
||||
// Jump turns if in HMD
|
||||
if (_driveKeys[ROT_RIGHT] || _driveKeys[ROT_LEFT]) {
|
||||
if (_turningKeyPressTime == 0.0f) {
|
||||
setOrientation(getOrientation() *
|
||||
glm::quat(glm::radians(glm::vec3(0.f, _driveKeys[ROT_LEFT] ? HMD_TURN_DEGREES : -HMD_TURN_DEGREES, 0.0f))));
|
||||
}
|
||||
_turningKeyPressTime += deltaTime;
|
||||
if (_turningKeyPressTime > TIME_BETWEEN_HMD_TURNS) {
|
||||
_turningKeyPressTime = 0.0f;
|
||||
}
|
||||
} else {
|
||||
_turningKeyPressTime = 0.0f;
|
||||
}
|
||||
}
|
||||
// Smoothly rotate body with arrow keys
|
||||
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime;
|
||||
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime;
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime);
|
||||
|
||||
// update body orientation by movement inputs
|
||||
|
|
|
@ -210,7 +210,6 @@ private:
|
|||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
float _turningKeyPressTime;
|
||||
glm::vec3 _gravity;
|
||||
|
||||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "Joystick.h"
|
||||
|
||||
const float CONTROLLER_THRESHOLD = 0.25f;
|
||||
const float CONTROLLER_THRESHOLD = 0.3f;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
const float MAX_AXIS = 32768.0f;
|
||||
|
@ -173,9 +173,7 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), DPAD_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED);
|
||||
|
@ -203,13 +201,16 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
|
||||
// Button controls
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
|
||||
|
||||
// Zoom
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -276,7 +276,10 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(Qt::Key_Space));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(Qt::Key_R));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T));
|
||||
}
|
||||
|
||||
float KeyboardMouseDevice::getButton(int channel) const {
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include "OculusManager.h"
|
||||
#include "ui/overlays/Text3DOverlay.h"
|
||||
|
||||
#include <CursorManager.h>
|
||||
#include <QDesktopWidget>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
|
@ -37,7 +37,6 @@
|
|||
#include "InterfaceLogging.h"
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
template <typename Function>
|
||||
void for_each_eye(Function function) {
|
||||
for (ovrEyeType eye = ovrEyeType::ovrEye_Left;
|
||||
|
@ -54,6 +53,71 @@ void for_each_eye(const ovrHmd & hmd, Function function) {
|
|||
function(eye);
|
||||
}
|
||||
}
|
||||
enum CalibrationState {
|
||||
UNCALIBRATED,
|
||||
WAITING_FOR_DELTA,
|
||||
WAITING_FOR_ZERO,
|
||||
WAITING_FOR_ZERO_HELD,
|
||||
CALIBRATED
|
||||
};
|
||||
|
||||
inline glm::mat4 toGlm(const ovrMatrix4f & om) {
|
||||
return glm::transpose(glm::make_mat4(&om.M[0][0]));
|
||||
}
|
||||
|
||||
inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {
|
||||
return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));
|
||||
}
|
||||
|
||||
inline glm::vec3 toGlm(const ovrVector3f & ov) {
|
||||
return glm::make_vec3(&ov.x);
|
||||
}
|
||||
|
||||
inline glm::vec2 toGlm(const ovrVector2f & ov) {
|
||||
return glm::make_vec2(&ov.x);
|
||||
}
|
||||
|
||||
inline glm::ivec2 toGlm(const ovrVector2i & ov) {
|
||||
return glm::ivec2(ov.x, ov.y);
|
||||
}
|
||||
|
||||
inline glm::uvec2 toGlm(const ovrSizei & ov) {
|
||||
return glm::uvec2(ov.w, ov.h);
|
||||
}
|
||||
|
||||
inline glm::quat toGlm(const ovrQuatf & oq) {
|
||||
return glm::make_quat(&oq.x);
|
||||
}
|
||||
|
||||
inline glm::mat4 toGlm(const ovrPosef & op) {
|
||||
glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));
|
||||
glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));
|
||||
return translation * orientation;
|
||||
}
|
||||
|
||||
inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) {
|
||||
ovrMatrix4f result;
|
||||
glm::mat4 transposed(glm::transpose(m));
|
||||
memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {
|
||||
return{ v.x, v.y, v.z };
|
||||
}
|
||||
|
||||
inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {
|
||||
return{ v.x, v.y };
|
||||
}
|
||||
|
||||
inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {
|
||||
return{ (int)v.x, (int)v.y };
|
||||
}
|
||||
|
||||
inline ovrQuatf ovrFromGlm(const glm::quat & q) {
|
||||
return{ q.x, q.y, q.z, q.w };
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
@ -171,50 +235,54 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
SwapFramebufferWrapper* OculusManager::_swapFbo{ nullptr };
|
||||
MirrorFramebufferWrapper* OculusManager::_mirrorFbo{ nullptr };
|
||||
ovrLayerEyeFov OculusManager::_sceneLayer;
|
||||
static SwapFramebufferWrapper* _swapFbo{ nullptr };
|
||||
static MirrorFramebufferWrapper* _mirrorFbo{ nullptr };
|
||||
static ovrLayerEyeFov _sceneLayer;
|
||||
|
||||
#else
|
||||
|
||||
ovrTexture OculusManager::_eyeTextures[ovrEye_Count];
|
||||
GlWindow* OculusManager::_outputWindow{ nullptr };
|
||||
static ovrTexture _eyeTextures[ovrEye_Count];
|
||||
static GlWindow* _outputWindow{ nullptr };
|
||||
|
||||
#endif
|
||||
|
||||
bool OculusManager::_isConnected = false;
|
||||
ovrHmd OculusManager::_ovrHmd;
|
||||
ovrFovPort OculusManager::_eyeFov[ovrEye_Count];
|
||||
ovrVector3f OculusManager::_eyeOffset[ovrEye_Count];
|
||||
ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count];
|
||||
ovrSizei OculusManager::_renderTargetSize;
|
||||
glm::mat4 OculusManager::_eyeProjection[ovrEye_Count];
|
||||
unsigned int OculusManager::_frameIndex = 0;
|
||||
bool OculusManager::_frameTimingActive = false;
|
||||
Camera* OculusManager::_camera = NULL;
|
||||
ovrEyeType OculusManager::_activeEye = ovrEye_Count;
|
||||
bool OculusManager::_hswDismissed = false;
|
||||
static bool _isConnected = false;
|
||||
static ovrHmd _ovrHmd;
|
||||
static ovrFovPort _eyeFov[ovrEye_Count];
|
||||
static ovrVector3f _eyeOffset[ovrEye_Count];
|
||||
static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count];
|
||||
static ovrSizei _renderTargetSize;
|
||||
static glm::mat4 _eyeProjection[ovrEye_Count];
|
||||
static unsigned int _frameIndex = 0;
|
||||
static bool _frameTimingActive = false;
|
||||
static Camera* _camera = NULL;
|
||||
static ovrEyeType _activeEye = ovrEye_Count;
|
||||
static bool _hswDismissed = false;
|
||||
|
||||
float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;
|
||||
float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;
|
||||
float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;
|
||||
float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;
|
||||
quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec
|
||||
float OculusManager::CALIBRATION_MESSAGE_DISTANCE = 2.5f;
|
||||
OculusManager::CalibrationState OculusManager::_calibrationState;
|
||||
glm::vec3 OculusManager::_calibrationPosition;
|
||||
glm::quat OculusManager::_calibrationOrientation;
|
||||
quint64 OculusManager::_calibrationStartTime;
|
||||
int OculusManager::_calibrationMessage = 0;
|
||||
glm::vec3 OculusManager::_eyePositions[ovrEye_Count];
|
||||
static const float CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;
|
||||
static const float CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;
|
||||
static const float CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;
|
||||
static const float CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;
|
||||
static const quint64 CALIBRATION_ZERO_HOLD_TIME = 3000000; // usec
|
||||
static const float CALIBRATION_MESSAGE_DISTANCE = 2.5f;
|
||||
static CalibrationState _calibrationState;
|
||||
static glm::vec3 _calibrationPosition;
|
||||
static glm::quat _calibrationOrientation;
|
||||
static quint64 _calibrationStartTime;
|
||||
static int _calibrationMessage = 0;
|
||||
static glm::vec3 _eyePositions[ovrEye_Count];
|
||||
// TODO expose this as a developer toggle
|
||||
bool OculusManager::_eyePerFrameMode = false;
|
||||
ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count;
|
||||
ovrSizei OculusManager::_recommendedTexSize = { 0, 0 };
|
||||
float OculusManager::_offscreenRenderScale = 1.0;
|
||||
static bool _eyePerFrameMode = false;
|
||||
static ovrEyeType _lastEyeRendered = ovrEye_Count;
|
||||
static ovrSizei _recommendedTexSize = { 0, 0 };
|
||||
static float _offscreenRenderScale = 1.0;
|
||||
static glm::mat4 _combinedProjection;
|
||||
static ovrPosef _eyeRenderPoses[ovrEye_Count];
|
||||
ovrRecti OculusManager::_eyeViewports[ovrEye_Count];
|
||||
static ovrRecti _eyeViewports[ovrEye_Count];
|
||||
static ovrVector3f _eyeOffsets[ovrEye_Count];
|
||||
|
||||
glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }
|
||||
glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; }
|
||||
|
||||
void OculusManager::connect(QOpenGLContext* shareContext) {
|
||||
qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING;
|
||||
|
@ -267,6 +335,8 @@ void OculusManager::connect(QOpenGLContext* shareContext) {
|
|||
_eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye];
|
||||
_eyeProjection[eye] = toGlm(ovrMatrix4f_Projection(_eyeFov[eye],
|
||||
DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded));
|
||||
ovrEyeRenderDesc erd = ovrHmd_GetRenderDesc(_ovrHmd, eye, _eyeFov[eye]);
|
||||
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
||||
});
|
||||
ovrFovPort combinedFov = _ovrHmd->MaxEyeFov[0];
|
||||
combinedFov.RightTan = _ovrHmd->MaxEyeFov[1].RightTan;
|
||||
|
@ -389,7 +459,7 @@ void OculusManager::disconnect() {
|
|||
}
|
||||
}
|
||||
|
||||
void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) {
|
||||
void positionCalibrationBillboard(Text3DOverlay* billboard) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::quat headOrientation = myAvatar->getHeadOrientation();
|
||||
headOrientation.x = 0;
|
||||
|
@ -400,7 +470,7 @@ void OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) {
|
|||
billboard->setRotation(headOrientation);
|
||||
}
|
||||
|
||||
void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) {
|
||||
void calibrate(const glm::vec3& position, const glm::quat& orientation) {
|
||||
static QString instructionMessage = "Hold still to calibrate";
|
||||
static QString progressMessage;
|
||||
static Text3DOverlay* billboard;
|
||||
|
@ -605,9 +675,8 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
|
|||
}
|
||||
|
||||
trackerPosition = bodyOrientation * trackerPosition;
|
||||
static ovrVector3f eyeOffsets[2] = { { 0, 0, 0 }, { 0, 0, 0 } };
|
||||
ovrPosef eyePoses[ovrEye_Count];
|
||||
ovrHmd_GetEyePoses(_ovrHmd, _frameIndex, eyeOffsets, eyePoses, nullptr);
|
||||
ovrHmd_GetEyePoses(_ovrHmd, _frameIndex, _eyeOffsets, eyePoses, nullptr);
|
||||
#ifndef Q_OS_WIN
|
||||
ovrHmd_BeginFrame(_ovrHmd, _frameIndex);
|
||||
#endif
|
||||
|
@ -721,7 +790,18 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const
|
|||
ovrHmd_EndFrame(_ovrHmd, _eyeRenderPoses, _eyeTextures);
|
||||
glCanvas->makeCurrent();
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// in order to account account for changes in the pick ray caused by head movement
|
||||
// we need to force a mouse move event on every frame (perhaps we could change this
|
||||
// to based on the head moving a minimum distance from the last position in which we
|
||||
// sent?)
|
||||
{
|
||||
QMouseEvent mouseEvent(QEvent::MouseMove, glCanvas->mapFromGlobal(QCursor::pos()),
|
||||
Qt::NoButton, Qt::NoButton, 0);
|
||||
qApp->mouseMoveEvent(&mouseEvent, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -826,3 +906,8 @@ mat4 OculusManager::getEyeProjection(int eye) {
|
|||
mat4 OculusManager::getEyePose(int eye) {
|
||||
return toGlm(_eyeRenderPoses[eye]);
|
||||
}
|
||||
|
||||
mat4 OculusManager::getHeadPose() {
|
||||
ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
|
||||
return toGlm(ts.HeadPose.ThePose);
|
||||
}
|
|
@ -13,8 +13,6 @@
|
|||
#ifndef hifi_OculusManager_h
|
||||
#define hifi_OculusManager_h
|
||||
|
||||
#include <OVR_CAPI.h>
|
||||
|
||||
#include <ProgramObject.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
@ -23,17 +21,7 @@
|
|||
#include "RenderArgs.h"
|
||||
|
||||
class QOpenGLContext;
|
||||
|
||||
class Camera;
|
||||
class GlWindow;
|
||||
class PalmData;
|
||||
class Text3DOverlay;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
struct SwapFramebufferWrapper;
|
||||
struct MirrorFramebufferWrapper;
|
||||
#endif
|
||||
|
||||
|
||||
/// Handles interaction with the Oculus Rift.
|
||||
class OculusManager {
|
||||
|
@ -57,124 +45,14 @@ public:
|
|||
static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane);
|
||||
|
||||
static glm::vec3 getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }
|
||||
static glm::vec3 getRightEyePosition() { return _eyePositions[ovrEye_Right]; }
|
||||
static glm::vec3 getLeftEyePosition();
|
||||
static glm::vec3 getRightEyePosition();
|
||||
|
||||
static int getHMDScreen();
|
||||
|
||||
static glm::mat4 getEyeProjection(int eye);
|
||||
static glm::mat4 getEyePose(int eye);
|
||||
|
||||
private:
|
||||
static void initSdk();
|
||||
static void shutdownSdk();
|
||||
|
||||
static bool _isConnected;
|
||||
static glm::vec3 _eyePositions[ovrEye_Count];
|
||||
static ovrHmd _ovrHmd;
|
||||
static ovrFovPort _eyeFov[ovrEye_Count];
|
||||
static ovrVector3f _eyeOffset[ovrEye_Count];
|
||||
static glm::mat4 _eyeProjection[ovrEye_Count];
|
||||
static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count];
|
||||
static ovrRecti _eyeViewports[ovrEye_Count];
|
||||
static ovrSizei _renderTargetSize;
|
||||
static unsigned int _frameIndex;
|
||||
static bool _frameTimingActive;
|
||||
static Camera* _camera;
|
||||
static ovrEyeType _activeEye;
|
||||
static bool _hswDismissed;
|
||||
|
||||
static void calibrate(const glm::vec3 position, const glm::quat orientation);
|
||||
enum CalibrationState {
|
||||
UNCALIBRATED,
|
||||
WAITING_FOR_DELTA,
|
||||
WAITING_FOR_ZERO,
|
||||
WAITING_FOR_ZERO_HELD,
|
||||
CALIBRATED
|
||||
};
|
||||
static void positionCalibrationBillboard(Text3DOverlay* message);
|
||||
static float CALIBRATION_DELTA_MINIMUM_LENGTH;
|
||||
static float CALIBRATION_DELTA_MINIMUM_ANGLE;
|
||||
static float CALIBRATION_ZERO_MAXIMUM_LENGTH;
|
||||
static float CALIBRATION_ZERO_MAXIMUM_ANGLE;
|
||||
static quint64 CALIBRATION_ZERO_HOLD_TIME;
|
||||
static float CALIBRATION_MESSAGE_DISTANCE;
|
||||
static CalibrationState _calibrationState;
|
||||
static glm::vec3 _calibrationPosition;
|
||||
static glm::quat _calibrationOrientation;
|
||||
static quint64 _calibrationStartTime;
|
||||
static int _calibrationMessage;
|
||||
// TODO drop this variable and use the existing 'Developer | Render | Scale Resolution' value
|
||||
static ovrSizei _recommendedTexSize;
|
||||
static float _offscreenRenderScale;
|
||||
static bool _eyePerFrameMode;
|
||||
static ovrEyeType _lastEyeRendered;
|
||||
#ifdef Q_OS_WIN
|
||||
static SwapFramebufferWrapper* _swapFbo;
|
||||
static MirrorFramebufferWrapper* _mirrorFbo;
|
||||
static ovrLayerEyeFov _sceneLayer;
|
||||
#else
|
||||
static ovrTexture _eyeTextures[ovrEye_Count];
|
||||
static GlWindow* _outputWindow;
|
||||
#endif
|
||||
static glm::mat4 getHeadPose();
|
||||
};
|
||||
|
||||
|
||||
inline glm::mat4 toGlm(const ovrMatrix4f & om) {
|
||||
return glm::transpose(glm::make_mat4(&om.M[0][0]));
|
||||
}
|
||||
|
||||
inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {
|
||||
return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));
|
||||
}
|
||||
|
||||
inline glm::vec3 toGlm(const ovrVector3f & ov) {
|
||||
return glm::make_vec3(&ov.x);
|
||||
}
|
||||
|
||||
inline glm::vec2 toGlm(const ovrVector2f & ov) {
|
||||
return glm::make_vec2(&ov.x);
|
||||
}
|
||||
|
||||
inline glm::ivec2 toGlm(const ovrVector2i & ov) {
|
||||
return glm::ivec2(ov.x, ov.y);
|
||||
}
|
||||
|
||||
inline glm::uvec2 toGlm(const ovrSizei & ov) {
|
||||
return glm::uvec2(ov.w, ov.h);
|
||||
}
|
||||
|
||||
inline glm::quat toGlm(const ovrQuatf & oq) {
|
||||
return glm::make_quat(&oq.x);
|
||||
}
|
||||
|
||||
inline glm::mat4 toGlm(const ovrPosef & op) {
|
||||
glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));
|
||||
glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));
|
||||
return translation * orientation;
|
||||
}
|
||||
|
||||
inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) {
|
||||
ovrMatrix4f result;
|
||||
glm::mat4 transposed(glm::transpose(m));
|
||||
memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {
|
||||
return{ v.x, v.y, v.z };
|
||||
}
|
||||
|
||||
inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {
|
||||
return{ v.x, v.y };
|
||||
}
|
||||
|
||||
inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {
|
||||
return{ (int)v.x, (int)v.y };
|
||||
}
|
||||
|
||||
inline ovrQuatf ovrFromGlm(const glm::quat & q) {
|
||||
return{ q.x, q.y, q.z, q.w };
|
||||
}
|
||||
|
||||
#endif // hifi_OculusManager_h
|
||||
|
|
|
@ -36,6 +36,8 @@ const float NECK_X = 0.25f; // meters
|
|||
const float NECK_Y = 0.3f; // meters
|
||||
const float NECK_Z = 0.3f; // meters
|
||||
|
||||
const float CONTROLLER_THRESHOLD = 0.35f;
|
||||
|
||||
#ifdef __APPLE__
|
||||
typedef int (*SixenseBaseFunction)();
|
||||
typedef int (*SixenseTakeIntFunction)(int);
|
||||
|
@ -326,6 +328,12 @@ void SixenseManager::update(float deltaTime) {
|
|||
}
|
||||
_controllersAtBase = (numControllersAtBase == 2);
|
||||
}
|
||||
|
||||
for (auto axisState : _axisStateMap) {
|
||||
if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
|
||||
_axisStateMap[axisState.first] = 0.0f;
|
||||
}
|
||||
}
|
||||
#endif // HAVE_SIXENSE
|
||||
}
|
||||
|
||||
|
@ -724,6 +732,13 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED);
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0));
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1));
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1));
|
||||
|
||||
}
|
||||
|
||||
float SixenseManager::getButton(int channel) const {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#ifdef Q_OS_WIN
|
||||
static BOOL CALLBACK enumWindowsCallback(HWND hWnd, LPARAM lParam) {
|
||||
const UINT TIMEOUT = 200; // ms
|
||||
DWORD response;
|
||||
DWORD_PTR response;
|
||||
LRESULT result = SendMessageTimeout(hWnd, UWM_IDENTIFY_INSTANCES, 0, 0, SMTO_BLOCK | SMTO_ABORTIFHUNG, TIMEOUT, &response);
|
||||
if (result == 0) { // Timeout; continue search.
|
||||
return TRUE;
|
||||
|
|
|
@ -28,6 +28,7 @@ ControllerScriptingInterface::ControllerScriptingInterface() :
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
|
||||
static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
|
||||
static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
|
||||
|
|
|
@ -79,6 +79,7 @@ public:
|
|||
bool isMouseCaptured() const { return _mouseCaptured; }
|
||||
bool isTouchCaptured() const { return _touchCaptured; }
|
||||
bool isWheelCaptured() const { return _wheelCaptured; }
|
||||
bool areActionsCaptured() const { return _actionsCaptured; }
|
||||
bool isJoystickCaptured(int joystickIndex) const;
|
||||
|
||||
void updateInputControllers();
|
||||
|
@ -123,6 +124,9 @@ public slots:
|
|||
|
||||
virtual void captureWheelEvents() { _wheelCaptured = true; }
|
||||
virtual void releaseWheelEvents() { _wheelCaptured = false; }
|
||||
|
||||
virtual void captureActionEvents() { _actionsCaptured = true; }
|
||||
virtual void releaseActionEvents() { _actionsCaptured = false; }
|
||||
|
||||
virtual void captureJoystick(int joystickIndex);
|
||||
virtual void releaseJoystick(int joystickIndex);
|
||||
|
@ -143,6 +147,7 @@ private:
|
|||
bool _mouseCaptured;
|
||||
bool _touchCaptured;
|
||||
bool _wheelCaptured;
|
||||
bool _actionsCaptured;
|
||||
QMultiMap<int,KeyEvent> _capturedKeys;
|
||||
QSet<int> _capturedJoysticks;
|
||||
|
||||
|
|
|
@ -182,7 +182,8 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
|
||||
updateTooltips();
|
||||
|
||||
vec2 canvasSize = qApp->getCanvasSize();
|
||||
auto deviceSize = qApp->getDeviceSize();
|
||||
glViewport(0, 0, deviceSize.width(), deviceSize.height());
|
||||
|
||||
//Handle fading and deactivation/activation of UI
|
||||
gpu::Batch batch;
|
||||
|
@ -204,6 +205,7 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
|
||||
//draw the mouse pointer
|
||||
// Get the mouse coordinates and convert to NDC [-1, 1]
|
||||
vec2 canvasSize = qApp->getCanvasSize();
|
||||
vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
|
||||
// Invert the Y axis
|
||||
mousePosition.y *= -1.0f;
|
||||
|
@ -334,12 +336,12 @@ void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& or
|
|||
|
||||
// Intersection UI overlay space
|
||||
glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection;
|
||||
glm::vec3 intersectionWithUi = glm::normalize(worldSpaceDirection) * _oculusUIRadius;
|
||||
intersectionWithUi += overlayPosition;
|
||||
glm::vec3 worldSpaceIntersection = (glm::normalize(worldSpaceDirection) * _oculusUIRadius) + overlayPosition;
|
||||
glm::vec3 worldSpaceHeadPosition = (overlayOrientation * glm::vec3(qApp->getHeadPose()[3])) + overlayPosition;
|
||||
|
||||
// Intersection in world space
|
||||
origin = overlayPosition;
|
||||
direction = glm::normalize(intersectionWithUi - origin);
|
||||
origin = worldSpaceHeadPosition;
|
||||
direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition);
|
||||
}
|
||||
|
||||
//Caculate the click location using one of the sixense controllers. Scale is not applied
|
||||
|
@ -394,42 +396,13 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi
|
|||
void ApplicationCompositor::renderPointers(gpu::Batch& batch) {
|
||||
if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) {
|
||||
//If we are in oculus, render reticle later
|
||||
if (_lastMouseMove == 0) {
|
||||
_lastMouseMove = usecTimestampNow();
|
||||
}
|
||||
QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY());
|
||||
|
||||
static const int MAX_IDLE_TIME = 3;
|
||||
if (_reticlePosition[MOUSE] != position) {
|
||||
_lastMouseMove = usecTimestampNow();
|
||||
} else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) {
|
||||
//float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians
|
||||
//OculusManager::getEulerAngles(yaw, pitch, roll);
|
||||
glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll));
|
||||
glm::vec3 result;
|
||||
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
if (calculateRayUICollisionPoint(myAvatar->getEyePosition(),
|
||||
myAvatar->getOrientation() * orientation * IDENTITY_FRONT,
|
||||
result)) {
|
||||
glm::vec3 lookAtDirection = glm::inverse(myAvatar->getOrientation()) * (result - myAvatar->getDefaultEyePosition());
|
||||
glm::vec2 spericalPos = directionToSpherical(glm::normalize(lookAtDirection));
|
||||
glm::vec2 screenPos = sphericalToScreen(spericalPos);
|
||||
position = QPoint(screenPos.x, screenPos.y);
|
||||
// FIXME
|
||||
//glCanvas->cursor().setPos(glCanvas->mapToGlobal(position));
|
||||
} else {
|
||||
qDebug() << "No collision point";
|
||||
}
|
||||
}
|
||||
|
||||
_reticlePosition[MOUSE] = position;
|
||||
_reticleActive[MOUSE] = true;
|
||||
_magActive[MOUSE] = _magnifier;
|
||||
_reticleActive[LEFT_CONTROLLER] = false;
|
||||
_reticleActive[RIGHT_CONTROLLER] = false;
|
||||
} else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
|
||||
_lastMouseMove = 0;
|
||||
//only render controller pointer if we aren't already rendering a mouse pointer
|
||||
_reticleActive[MOUSE] = false;
|
||||
_magActive[MOUSE] = false;
|
||||
|
|
|
@ -97,7 +97,6 @@ private:
|
|||
QPoint _reticlePosition[NUMBER_OF_RETICLES];
|
||||
bool _magActive[NUMBER_OF_RETICLES];
|
||||
float _magSizeMult[NUMBER_OF_RETICLES];
|
||||
quint64 _lastMouseMove{ 0 };
|
||||
bool _magnifier{ true };
|
||||
|
||||
float _alpha{ 1.0f };
|
||||
|
|
|
@ -35,7 +35,8 @@ AvatarInputs* AvatarInputs::getInstance() {
|
|||
|
||||
AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
|
||||
INSTANCE = this;
|
||||
_mirrorZoomed = rearViewZoomLevel.get() != 0;
|
||||
int zoomSetting = rearViewZoomLevel.get();
|
||||
_mirrorZoomed = zoomSetting == 0;
|
||||
}
|
||||
|
||||
#define AI_UPDATE(name, src) \
|
||||
|
@ -50,7 +51,7 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
|
|||
#define AI_UPDATE_FLOAT(name, src, epsilon) \
|
||||
{ \
|
||||
float val = src; \
|
||||
if (abs(_##name - val) >= epsilon) { \
|
||||
if (fabs(_##name - val) >= epsilon) { \
|
||||
_##name = val; \
|
||||
emit name##Changed(); \
|
||||
} \
|
||||
|
|
|
@ -86,7 +86,7 @@ bool Stats::includeTimingRecord(const QString& name) {
|
|||
#define STAT_UPDATE_FLOAT(name, src, epsilon) \
|
||||
{ \
|
||||
float val = src; \
|
||||
if (abs(_##name - val) >= epsilon) { \
|
||||
if (fabs(_##name - val) >= epsilon) { \
|
||||
_##name = val; \
|
||||
emit name##Changed(); \
|
||||
} \
|
||||
|
|
|
@ -29,7 +29,7 @@ class Stats : public QQuickItem {
|
|||
HIFI_QML_DECL
|
||||
Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged)
|
||||
Q_PROPERTY(bool timingExpanded READ isTimingExpanded NOTIFY timingExpandedChanged)
|
||||
Q_PROPERTY(QString monospaceFont READ monospaceFont)
|
||||
Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT)
|
||||
|
||||
STATS_PROPERTY(int, serverCount, 0)
|
||||
STATS_PROPERTY(int, framerate, 0)
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "UserInputMapper.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "UserInputMapper.h"
|
||||
|
||||
|
||||
// UserInputMapper Class
|
||||
|
||||
|
@ -207,6 +210,9 @@ void UserInputMapper::update(float deltaTime) {
|
|||
// Scale all the channel step with the scale
|
||||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
if (_actionStates[i] > 0) {
|
||||
emit Application::getInstance()->getControllerScriptingInterface()->actionEvent(i, _actionStates[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,6 +247,9 @@ void UserInputMapper::assignDefaulActionScales() {
|
|||
_actionScales[PITCH_UP] = 1.0f; // 1 degree per unit
|
||||
_actionScales[BOOM_IN] = 1.0f; // 1m per unit
|
||||
_actionScales[BOOM_OUT] = 1.0f; // 1m per unit
|
||||
_actionStates[SHIFT] = 1.0f; // on
|
||||
_actionStates[ACTION1] = 1.0f; // default
|
||||
_actionStates[ACTION2] = 1.0f; // default
|
||||
}
|
||||
|
||||
// This is only necessary as long as the actions are hardcoded
|
||||
|
@ -258,4 +267,7 @@ void UserInputMapper::createActionNames() {
|
|||
_actionNames[PITCH_UP] = "PITCH_UP";
|
||||
_actionNames[BOOM_IN] = "BOOM_IN";
|
||||
_actionNames[BOOM_OUT] = "BOOM_OUT";
|
||||
_actionNames[SHIFT] = "SHIFT";
|
||||
_actionNames[ACTION1] = "ACTION1";
|
||||
_actionNames[ACTION2] = "ACTION2";
|
||||
}
|
|
@ -139,6 +139,11 @@ public:
|
|||
|
||||
BOOM_IN,
|
||||
BOOM_OUT,
|
||||
|
||||
SHIFT,
|
||||
|
||||
ACTION1,
|
||||
ACTION2,
|
||||
|
||||
NUM_ACTIONS,
|
||||
};
|
||||
|
|
|
@ -107,7 +107,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
Transform transform;
|
||||
transform.setTranslation(getCenter());
|
||||
transform.setRotation(getRotation());
|
||||
transform.setScale(glm::vec3(getDimensions(), 0.01f));
|
||||
transform.setScale(glm::vec3(getDimensions(), 0.01f) / 2.0f);
|
||||
|
||||
|
||||
auto& batch = *args->_batch;
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
#include "Application.h"
|
||||
#include "Text3DOverlay.h"
|
||||
|
||||
#include <RenderDeferredTask.h>
|
||||
|
||||
const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
|
||||
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
|
||||
const float DEFAULT_MARGIN = 0.1f;
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation
|
||||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation
|
||||
const float LINE_SCALE_RATIO = 1.2f;
|
||||
|
||||
Text3DOverlay::Text3DOverlay() :
|
||||
|
@ -52,7 +54,7 @@ Text3DOverlay::~Text3DOverlay() {
|
|||
|
||||
xColor Text3DOverlay::getBackgroundColor() {
|
||||
if (_colorPulse == 0.0f) {
|
||||
return _backgroundColor;
|
||||
return _backgroundColor;
|
||||
}
|
||||
|
||||
float pulseLevel = updatePulse();
|
||||
|
@ -75,19 +77,23 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
return; // do nothing if we're not visible
|
||||
}
|
||||
|
||||
glPushMatrix(); {
|
||||
glTranslatef(_position.x, _position.y, _position.z);
|
||||
auto batch = args->_batch;
|
||||
|
||||
if (batch) {
|
||||
glm::quat rotation;
|
||||
|
||||
|
||||
if (_isFacingAvatar) {
|
||||
// rotate about vertical to face the camera
|
||||
rotation = Application::getInstance()->getCamera()->getRotation();
|
||||
} else {
|
||||
rotation = getRotation();
|
||||
}
|
||||
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_position);
|
||||
transform.setRotation(rotation);
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
xColor backgroundColor = getBackgroundColor();
|
||||
|
@ -96,51 +102,38 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
glm::vec2 dimensions = getDimensions();
|
||||
glm::vec2 halfDimensions = dimensions * 0.5f;
|
||||
|
||||
|
||||
const float SLIGHTLY_BEHIND = -0.005f;
|
||||
|
||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(*batch, topLeft, bottomRight, quadColor);
|
||||
|
||||
// Same font properties as textSize()
|
||||
float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
|
||||
|
||||
float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
|
||||
|
||||
glTranslatef(-(halfDimensions.x - _leftMargin), halfDimensions.y - _topMargin, 0.0f);
|
||||
float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
|
||||
|
||||
glm::vec2 clipMinimum(0.0f, 0.0f);
|
||||
glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor,
|
||||
glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor,
|
||||
(dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor);
|
||||
|
||||
glScalef(scaleFactor, -scaleFactor, scaleFactor);
|
||||
enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipDimensions.x);
|
||||
enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x);
|
||||
enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipDimensions.y);
|
||||
enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y);
|
||||
|
||||
transform.setTranslation(_position);
|
||||
transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin) , halfDimensions.y - _topMargin, 0.01f));
|
||||
transform.setScale(scaleFactor);
|
||||
batch->setModelTransform(transform);
|
||||
|
||||
glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, getAlpha() };
|
||||
_textRenderer->draw(0, 0, _text, textColor);
|
||||
_textRenderer->draw(*batch, 0, 0, _text, textColor);
|
||||
|
||||
glDisable(GL_CLIP_PLANE0);
|
||||
glDisable(GL_CLIP_PLANE1);
|
||||
glDisable(GL_CLIP_PLANE2);
|
||||
glDisable(GL_CLIP_PLANE3);
|
||||
|
||||
} glPopMatrix();
|
||||
|
||||
}
|
||||
batch->setPipeline(DrawOverlay3D::getOpaquePipeline());
|
||||
}
|
||||
|
||||
void Text3DOverlay::enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
||||
GLdouble coefficients[] = { x, y, z, w };
|
||||
glClipPlane(plane, coefficients);
|
||||
glEnable(plane);
|
||||
}
|
||||
|
||||
void Text3DOverlay::setProperties(const QScriptValue& properties) {
|
||||
Planar3DOverlay::setProperties(properties);
|
||||
|
||||
|
||||
QScriptValue text = properties.property("text");
|
||||
if (text.isValid()) {
|
||||
setText(text.toVariant().toString());
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <QString>
|
||||
|
||||
#include <RenderArgs.h>
|
||||
#include <TextRenderer.h>
|
||||
#include <TextRenderer3D.h>
|
||||
|
||||
#include "Planar3DOverlay.h"
|
||||
|
||||
|
@ -60,9 +60,7 @@ public:
|
|||
virtual Text3DOverlay* createClone() const;
|
||||
|
||||
private:
|
||||
void enableClipPlane(GLenum plane, float x, float y, float z, float w);
|
||||
|
||||
TextRenderer* _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
|
||||
TextRenderer3D* _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
|
||||
|
||||
QString _text;
|
||||
xColor _backgroundColor;
|
||||
|
|
|
@ -80,6 +80,7 @@ void TextOverlay::render(RenderArgs* args) {
|
|||
|
||||
glm::vec2 topLeft(left, top);
|
||||
glm::vec2 bottomRight(right, bottom);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
|
||||
|
||||
const int leftAdjust = -1; // required to make text render relative to left edge of bounds
|
||||
|
|
|
@ -795,6 +795,11 @@ void AudioClient::handleAudioInput() {
|
|||
|
||||
delete[] inputAudioSamples;
|
||||
|
||||
// Remove DC offset
|
||||
if (!_isStereoInput && !_audioSourceInjectEnabled) {
|
||||
_inputGate.removeDCOffset(networkAudioSamples, numNetworkSamples);
|
||||
}
|
||||
|
||||
// only impose the noise gate and perform tone injection if we are sending mono audio
|
||||
if (!_isStereoInput && !_audioSourceInjectEnabled && _isNoiseGateEnabled) {
|
||||
_inputGate.gateSamples(networkAudioSamples, numNetworkSamples);
|
||||
|
|
|
@ -33,6 +33,32 @@ AudioNoiseGate::AudioNoiseGate() :
|
|||
|
||||
}
|
||||
|
||||
void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
|
||||
//
|
||||
// DC Offset correction
|
||||
//
|
||||
// Measure the DC offset over a trailing number of frames, and remove it from the input signal.
|
||||
// This causes the noise background measurements and server muting to be more accurate. Many off-board
|
||||
// ADC's have a noticeable DC offset.
|
||||
//
|
||||
const float DC_OFFSET_AVERAGING = 0.99f;
|
||||
float measuredDcOffset = 0.0f;
|
||||
// Remove trailing DC offset from samples
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
measuredDcOffset += samples[i];
|
||||
samples[i] -= (int16_t) _dcOffset;
|
||||
}
|
||||
// Update measured DC offset
|
||||
measuredDcOffset /= numSamples;
|
||||
if (_dcOffset == 0.0f) {
|
||||
// On first frame, copy over measured offset
|
||||
_dcOffset = measuredDcOffset;
|
||||
} else {
|
||||
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
|
||||
//
|
||||
// Impose Noise Gate
|
||||
|
@ -61,17 +87,12 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
|
|||
const int NOISE_GATE_WIDTH = 5;
|
||||
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
|
||||
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
|
||||
const float DC_OFFSET_AVERAGING = 0.99f;
|
||||
|
||||
// Check clipping, adjust DC offset, and check if should open noise gate
|
||||
float measuredDcOffset = 0.0f;
|
||||
|
||||
// Check clipping, and check if should open noise gate
|
||||
_didClipInLastFrame = false;
|
||||
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
measuredDcOffset += samples[i];
|
||||
samples[i] -= (int16_t) _dcOffset;
|
||||
thisSample = std::abs(samples[i]);
|
||||
|
||||
if (thisSample >= ((float) AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) {
|
||||
_didClipInLastFrame = true;
|
||||
}
|
||||
|
@ -83,14 +104,6 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
|
|||
}
|
||||
}
|
||||
|
||||
measuredDcOffset /= numSamples;
|
||||
if (_dcOffset == 0.0f) {
|
||||
// On first frame, copy over measured offset
|
||||
_dcOffset = measuredDcOffset;
|
||||
} else {
|
||||
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
||||
}
|
||||
|
||||
_lastLoudness = fabs(loudness / numSamples);
|
||||
|
||||
if (_quietestFrame > _lastLoudness) {
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
AudioNoiseGate();
|
||||
|
||||
void gateSamples(int16_t* samples, int numSamples);
|
||||
void removeDCOffset(int16_t* samples, int numSamples);
|
||||
|
||||
bool clippedInLastFrame() const { return _didClipInLastFrame; }
|
||||
float getMeasuredFloor() const { return _measuredFloor; }
|
||||
|
|
|
@ -7,4 +7,10 @@ add_dependency_external_projects(glm)
|
|||
find_package(GLM REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||
|
||||
link_hifi_libraries(networking shared)
|
||||
# we use libsoxr for resampling
|
||||
add_dependency_external_projects(soxr)
|
||||
find_package(Soxr REQUIRED)
|
||||
target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES})
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS})
|
||||
|
||||
link_hifi_libraries(networking shared)
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
#include <soxr.h>
|
||||
|
||||
#include "AbstractAudioInterface.h"
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioLogging.h"
|
||||
#include "SoundCache.h"
|
||||
|
||||
#include "AudioInjector.h"
|
||||
|
||||
|
@ -91,7 +93,6 @@ void AudioInjector::injectAudio() {
|
|||
}
|
||||
|
||||
void AudioInjector::restart() {
|
||||
qCDebug(audio) << "Restarting an AudioInjector by stopping and starting over.";
|
||||
connect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished);
|
||||
if (!_isStarted || _isFinished) {
|
||||
emit finished();
|
||||
|
@ -284,3 +285,66 @@ void AudioInjector::stopAndDeleteLater() {
|
|||
stop();
|
||||
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position) {
|
||||
if (soundUrl.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
auto soundCache = DependencyManager::get<SoundCache>();
|
||||
if (soundCache.isNull()) {
|
||||
return NULL;
|
||||
}
|
||||
SharedSoundPointer sound = soundCache.data()->getSound(QUrl(soundUrl));
|
||||
if (sound.isNull() || !sound->isReady()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AudioInjectorOptions options;
|
||||
options.stereo = sound->isStereo();
|
||||
options.position = position;
|
||||
options.volume = volume;
|
||||
|
||||
QByteArray samples = sound->getByteArray();
|
||||
if (stretchFactor == 1.0f) {
|
||||
return playSound(samples, options, NULL);
|
||||
}
|
||||
|
||||
soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
|
||||
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
|
||||
const int channelCount = sound->isStereo() ? 2 : 1;
|
||||
const int standardRate = AudioConstants::SAMPLE_RATE;
|
||||
const int resampledRate = standardRate * stretchFactor;
|
||||
const int nInputSamples = samples.size() / sizeof(int16_t);
|
||||
const int nOutputSamples = nInputSamples * stretchFactor;
|
||||
QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0');
|
||||
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(samples.data());
|
||||
soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount,
|
||||
receivedSamples, nInputSamples, NULL,
|
||||
reinterpret_cast<int16_t*>(resampled.data()), nOutputSamples, NULL,
|
||||
&spec, &qualitySpec, 0);
|
||||
if (soxError) {
|
||||
qCDebug(audio) << "Unable to resample" << soundUrl << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate;
|
||||
resampled = samples;
|
||||
}
|
||||
return playSound(resampled, options, NULL);
|
||||
}
|
||||
|
||||
AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) {
|
||||
QThread* injectorThread = new QThread();
|
||||
injectorThread->setObjectName("Audio Injector Thread");
|
||||
|
||||
AudioInjector* injector = new AudioInjector(buffer, options);
|
||||
injector->setLocalAudioInterface(localInterface);
|
||||
|
||||
injector->moveToThread(injectorThread);
|
||||
|
||||
// start injecting when the injector thread starts
|
||||
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
|
||||
|
||||
// connect the right slots and signals for AudioInjector and thread cleanup
|
||||
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
|
||||
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
|
||||
|
||||
injectorThread->start();
|
||||
return injector;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@ public:
|
|||
bool isLocalOnly() const { return _options.localOnly; }
|
||||
|
||||
void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; }
|
||||
|
||||
static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface);
|
||||
static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position);
|
||||
|
||||
public slots:
|
||||
void injectAudio();
|
||||
void restart();
|
||||
|
|
|
@ -17,10 +17,4 @@ find_package(PolyVox REQUIRED)
|
|||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
|
||||
|
||||
# for changing the pitch of collision sounds
|
||||
add_dependency_external_projects(soxr)
|
||||
find_package(Soxr REQUIRED)
|
||||
target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES})
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS})
|
||||
|
||||
link_hifi_libraries(shared gpu script-engine render render-utils)
|
||||
|
|
|
@ -27,10 +27,6 @@
|
|||
#include <SceneScriptingInterface.h>
|
||||
#include <ScriptEngine.h>
|
||||
#include <TextureCache.h>
|
||||
#include <SoundCache.h>
|
||||
#include <soxr.h>
|
||||
#include <AudioConstants.h>
|
||||
|
||||
|
||||
#include "EntityTreeRenderer.h"
|
||||
|
||||
|
@ -48,16 +44,12 @@
|
|||
#include "RenderablePolyVoxEntityItem.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include "AddressManager.h"
|
||||
|
||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
AbstractScriptingServicesInterface* scriptingServices) :
|
||||
OctreeRenderer(),
|
||||
_wantScripts(wantScripts),
|
||||
_entitiesScriptEngine(NULL),
|
||||
_sandboxScriptEngine(NULL),
|
||||
_localAudioInterface(NULL),
|
||||
_lastMouseEventValid(false),
|
||||
_viewState(viewState),
|
||||
_scriptingServices(scriptingServices),
|
||||
|
@ -1057,7 +1049,6 @@ void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision) {
|
||||
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
|
||||
if (!entity) {
|
||||
|
@ -1100,61 +1091,15 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
|
|||
if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto soundCache = DependencyManager::get<SoundCache>();
|
||||
if (soundCache.isNull()) {
|
||||
return;
|
||||
}
|
||||
SharedSoundPointer sound = soundCache.data()->getSound(QUrl(collisionSoundURL));
|
||||
if (sound.isNull() || !sound->isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range [1-c, 1], if we play it all.
|
||||
// Quiet sound aren't really heard at all, so we can compress everything to the range [1-c, 1], if we play it all.
|
||||
const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes.
|
||||
float volume = energyFactorOfFull;
|
||||
volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
|
||||
|
||||
// This is quite similar to AudioScriptingInterface::playSound() and should probably be refactored.
|
||||
AudioInjectorOptions options;
|
||||
options.stereo = sound->isStereo();
|
||||
options.position = position;
|
||||
options.volume = volume;
|
||||
const float volume = (energyFactorOfFull * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
|
||||
|
||||
|
||||
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
|
||||
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
|
||||
QByteArray samples = sound->getByteArray();
|
||||
soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
|
||||
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
|
||||
const int channelCount = sound->isStereo() ? 2 : 1;
|
||||
const float factor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
|
||||
const int standardRate = AudioConstants::SAMPLE_RATE;
|
||||
const int resampledRate = standardRate * factor;
|
||||
const int nInputSamples = samples.size() / sizeof(int16_t);
|
||||
const int nOutputSamples = nInputSamples * factor;
|
||||
QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0');
|
||||
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(samples.data());
|
||||
soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount,
|
||||
receivedSamples, nInputSamples, NULL,
|
||||
reinterpret_cast<int16_t*>(resampled.data()), nOutputSamples, NULL,
|
||||
&spec, &qualitySpec, 0);
|
||||
if (soxError) {
|
||||
qCDebug(entitiesrenderer) << "Unable to resample" << collisionSoundURL << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate;
|
||||
resampled = samples;
|
||||
}
|
||||
|
||||
AudioInjector* injector = new AudioInjector(resampled, options);
|
||||
injector->setLocalAudioInterface(_localAudioInterface);
|
||||
injector->triggerDeleteAfterFinish();
|
||||
QThread* injectorThread = new QThread();
|
||||
injectorThread->setObjectName("Audio Injector Thread");
|
||||
injector->moveToThread(injectorThread);
|
||||
// start injecting when the injector thread starts
|
||||
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
|
||||
// connect the right slots and signals for AudioInjector and thread cleanup
|
||||
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
|
||||
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
|
||||
injectorThread->start();
|
||||
const float stretchFactor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
|
||||
AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,
|
||||
|
|
|
@ -158,7 +158,6 @@ private:
|
|||
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
||||
|
||||
void playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision);
|
||||
AbstractAudioInterface* _localAudioInterface; // So we can render collision sounds
|
||||
|
||||
bool _lastMouseEventValid;
|
||||
MouseEvent _lastMouseEvent;
|
||||
|
|
|
@ -43,9 +43,9 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
|
|||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
// TODO: Figure out clean , efficient way to do relative line positioning. For now we'll just use absolute positioning.
|
||||
//batch.setModelTransform(getTransformToCenter());
|
||||
batch.setModelTransform(Transform());
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(getPosition());
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
batch._glLineWidth(getLineWidth());
|
||||
if (getLinePoints().size() > 1) {
|
||||
|
|
|
@ -197,6 +197,8 @@ public:
|
|||
QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); }
|
||||
|
||||
void setVoxelDataDirty() { _voxelDataChanged = true; }
|
||||
|
||||
void setLinePointsDirty() {_linePointsChanged = true; }
|
||||
|
||||
void setCreated(QDateTime& v);
|
||||
|
||||
|
|
|
@ -442,6 +442,43 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor) {
|
||||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
|
||||
}
|
||||
|
||||
EntityTypes::EntityType entityType = entity->getType();
|
||||
|
||||
if (entityType != EntityTypes::Line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
|
||||
LineEntityItem* lineEntity = static_cast<LineEntityItem*>(entity.get());
|
||||
_entityTree->lockForWrite();
|
||||
bool success = actor(*lineEntity);
|
||||
entity->setLastEdited(now);
|
||||
entity->setLastBroadcast(now);
|
||||
_entityTree->unlock();
|
||||
|
||||
_entityTree->lockForRead();
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
_entityTree->unlock();
|
||||
|
||||
properties.setLinePointsDirty();
|
||||
properties.setLastEdited(now);
|
||||
|
||||
|
||||
queueEntityMessage(PacketTypeEntityEdit, entityID, properties);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
|
||||
return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
polyVoxEntity.setSphere(center, radius, value);
|
||||
|
@ -460,6 +497,21 @@ bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
|
|||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::vec3>& points) {
|
||||
return setPoints(entityID, [points](LineEntityItem& lineEntity) -> bool
|
||||
{
|
||||
return lineEntity.setLinePoints(points);
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& point) {
|
||||
return setPoints(entityID, [point](LineEntityItem& lineEntity) -> bool
|
||||
{
|
||||
return lineEntity.appendPoint(point);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
|
||||
std::function<bool(EntitySimulation*, EntityItemPointer)> actor) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <OctreeScriptingInterface.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "LineEntityItem.h"
|
||||
|
||||
#include "EntityEditPacketSender.h"
|
||||
|
||||
|
@ -121,6 +122,9 @@ public slots:
|
|||
Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value);
|
||||
Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value);
|
||||
Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value);
|
||||
|
||||
Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector<glm::vec3>& points);
|
||||
Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point);
|
||||
|
||||
Q_INVOKABLE void dumpTree() const;
|
||||
|
||||
|
@ -157,6 +161,7 @@ signals:
|
|||
private:
|
||||
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulation*, EntityItemPointer)> actor);
|
||||
bool setVoxels(QUuid entityID, std::function<void(PolyVoxEntityItem&)> actor);
|
||||
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
|
||||
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
|
||||
const float LineEntityItem::DEFAULT_LINE_WIDTH = 2.0f;
|
||||
const int LineEntityItem::MAX_POINTS_PER_LINE = 70;
|
||||
|
||||
|
||||
EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
|
@ -85,24 +86,38 @@ bool LineEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
return somethingChanged;
|
||||
}
|
||||
|
||||
void LineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
|
||||
QVector<glm::vec3> sanitizedPoints;
|
||||
int invalidPoints = 0;
|
||||
bool LineEntityItem::appendPoint(const glm::vec3& point) {
|
||||
if (_points.size() > MAX_POINTS_PER_LINE - 1) {
|
||||
qDebug() << "MAX POINTS REACHED!";
|
||||
return false;
|
||||
}
|
||||
glm::vec3 halfBox = getDimensions() * 0.5f;
|
||||
if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) {
|
||||
qDebug() << "Point is outside entity's bounding box";
|
||||
return false;
|
||||
}
|
||||
_points << point;
|
||||
_pointsChanged = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
|
||||
if (points.size() > MAX_POINTS_PER_LINE) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
glm::vec3 point = points.at(i);
|
||||
// Make sure all of our points are valid numbers.
|
||||
// Must be greater than 0 because vector component is set to 0 if it is invalid data. Also should never be greater than TREE_SCALE
|
||||
if ( (point.x > 0 && point.x < TREE_SCALE) && (point.y > 0 && point.y < TREE_SCALE) && (point.z > 0 && point.z < TREE_SCALE) ) {
|
||||
sanitizedPoints << point;
|
||||
} else {
|
||||
++invalidPoints;
|
||||
glm::vec3 pos = getPosition();
|
||||
glm::vec3 halfBox = getDimensions() * 0.5f;
|
||||
if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) {
|
||||
qDebug() << "Point is outside entity's bounding box";
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
if (invalidPoints > 0) {
|
||||
qDebug() << "Line with" << invalidPoints << "INVALID POINTS";
|
||||
}
|
||||
_points = sanitizedPoints;
|
||||
_points = points;
|
||||
_pointsChanged = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
|
|
|
@ -54,7 +54,8 @@ class LineEntityItem : public EntityItem {
|
|||
void setLineWidth(float lineWidth){ _lineWidth = lineWidth; }
|
||||
float getLineWidth() const{ return _lineWidth; }
|
||||
|
||||
void setLinePoints(const QVector<glm::vec3>& points);
|
||||
bool setLinePoints(const QVector<glm::vec3>& points);
|
||||
bool appendPoint(const glm::vec3& point);
|
||||
|
||||
const QVector<glm::vec3>& getLinePoints() const{ return _points; }
|
||||
|
||||
|
@ -68,6 +69,7 @@ class LineEntityItem : public EntityItem {
|
|||
|
||||
virtual void debugDump() const;
|
||||
static const float DEFAULT_LINE_WIDTH;
|
||||
static const int MAX_POINTS_PER_LINE;
|
||||
|
||||
protected:
|
||||
rgbColor _color;
|
||||
|
|
|
@ -308,6 +308,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
|||
handlePath(returnedPath, trigger);
|
||||
}
|
||||
} else {
|
||||
// we're going to hit the index path, set that as the _newHostLookupPath
|
||||
_newHostLookupPath = INDEX_PATH;
|
||||
|
||||
// we didn't override the path or get one back - ask the DS for the viewpoint of its index path
|
||||
// which we will jump to if it exists
|
||||
emit pathChangeRequired(INDEX_PATH);
|
||||
|
@ -479,6 +482,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
|
|||
// We use _newHostLookupPath to determine if the client has already stored its last address
|
||||
// before moving to a new host thanks to the information in the same lookup URL.
|
||||
|
||||
|
||||
if (definitelyPathOnly || (!pathString.isEmpty() && pathString != _newHostLookupPath)) {
|
||||
addCurrentAddressToHistory(LookupTrigger::UserInput);
|
||||
}
|
||||
|
|
|
@ -350,6 +350,19 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
|
|||
if (rule.extensionFormat.contains("%N")) {
|
||||
if (rule.maxBackupVersions > 0) {
|
||||
qCDebug(octree) << "Rolling old backup versions for rule" << rule.name << "...";
|
||||
|
||||
// Delete maximum rolling file because rename() fails on Windows if target exists
|
||||
QString backupMaxExtensionN = rule.extensionFormat;
|
||||
backupMaxExtensionN.replace(QString("%N"), QString::number(rule.maxBackupVersions));
|
||||
QString backupMaxFilenameN = _filename + backupMaxExtensionN;
|
||||
QFile backupMaxFileN(backupMaxFilenameN);
|
||||
if (backupMaxFileN.exists()) {
|
||||
int result = remove(qPrintable(backupMaxFilenameN));
|
||||
if (result != 0) {
|
||||
qCDebug(octree) << "ERROR deleting old backup file " << backupMaxFilenameN;
|
||||
}
|
||||
}
|
||||
|
||||
for(int n = rule.maxBackupVersions - 1; n > 0; n--) {
|
||||
QString backupExtensionN = rule.extensionFormat;
|
||||
QString backupExtensionNplusOne = rule.extensionFormat;
|
||||
|
|
|
@ -545,11 +545,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
|||
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame
|
||||
// and testing intersection there.
|
||||
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) {
|
||||
|
||||
if (!_calculatedMeshBoxesValid) {
|
||||
recalculateMeshBoxes(pickAgainstTriangles);
|
||||
}
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
float distanceToSubMesh;
|
||||
|
@ -560,6 +555,11 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
|||
|
||||
// If we hit the models box, then consider the submeshes...
|
||||
_mutex.lock();
|
||||
|
||||
if (!_calculatedMeshBoxesValid) {
|
||||
recalculateMeshBoxes(pickAgainstTriangles);
|
||||
}
|
||||
|
||||
foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
|
||||
|
||||
if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace)) {
|
||||
|
@ -1811,7 +1811,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
|
|||
|
||||
// We need to make sure we have valid offsets calculated before we can render
|
||||
if (!_calculatedMeshPartOffsetValid) {
|
||||
_mutex.lock();
|
||||
recalculateMeshPartOffsets();
|
||||
_mutex.unlock();
|
||||
}
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
|
||||
|
@ -2019,7 +2021,9 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
|
|||
}
|
||||
}
|
||||
|
||||
_mutex.lock();
|
||||
qint64 offset = _calculatedMeshPartOffset[QPair<int,int>(meshIndex, partIndex)];
|
||||
_mutex.unlock();
|
||||
|
||||
if (part.quadIndices.size() > 0) {
|
||||
batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
|
||||
|
|
|
@ -174,11 +174,11 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const
|
|||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() const {
|
||||
gpu::PipelinePointer DrawOverlay3D::_opaquePipeline;
|
||||
const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() {
|
||||
if (!_opaquePipeline) {
|
||||
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(overlay3D_vert)));
|
||||
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(overlay3D_frag)));
|
||||
|
||||
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||
|
||||
auto state = gpu::StatePointer(new gpu::State());
|
||||
|
@ -238,4 +238,3 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
|
|||
args->_batch = nullptr;
|
||||
args->_whiteTexture.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,10 +53,10 @@ public:
|
|||
};
|
||||
|
||||
class DrawOverlay3D {
|
||||
mutable gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable
|
||||
static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable
|
||||
public:
|
||||
const gpu::PipelinePointer& getOpaquePipeline() const;
|
||||
|
||||
static const gpu::PipelinePointer& getOpaquePipeline();
|
||||
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||
|
||||
typedef render::Job::Model<DrawOverlay3D> JobModel;
|
||||
|
|
|
@ -83,6 +83,9 @@ public slots:
|
|||
|
||||
virtual void captureWheelEvents() = 0;
|
||||
virtual void releaseWheelEvents() = 0;
|
||||
|
||||
virtual void captureActionEvents() = 0;
|
||||
virtual void releaseActionEvents() = 0;
|
||||
|
||||
virtual void captureJoystick(int joystickIndex) = 0;
|
||||
virtual void releaseJoystick(int joystickIndex) = 0;
|
||||
|
@ -112,6 +115,8 @@ signals:
|
|||
void touchUpdateEvent(const TouchEvent& event);
|
||||
|
||||
void wheelEvent(const WheelEvent& event);
|
||||
|
||||
void actionEvent(int action, float state);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -46,24 +46,7 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const Audi
|
|||
AudioInjectorOptions optionsCopy = injectorOptions;
|
||||
optionsCopy.stereo = sound->isStereo();
|
||||
|
||||
QThread* injectorThread = new QThread();
|
||||
injectorThread->setObjectName("Audio Injector Thread");
|
||||
|
||||
AudioInjector* injector = new AudioInjector(sound, optionsCopy);
|
||||
injector->setLocalAudioInterface(_localAudioInterface);
|
||||
|
||||
injector->moveToThread(injectorThread);
|
||||
|
||||
// start injecting when the injector thread starts
|
||||
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
|
||||
|
||||
// connect the right slots and signals for AudioInjector and thread cleanup
|
||||
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
|
||||
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
|
||||
|
||||
injectorThread->start();
|
||||
|
||||
return new ScriptAudioInjector(injector);
|
||||
return new ScriptAudioInjector(AudioInjector::playSound(sound->getByteArray(), optionsCopy, _localAudioInterface));
|
||||
|
||||
} else {
|
||||
qCDebug(scriptengine) << "AudioScriptingInterface::playSound called with null Sound object.";
|
||||
|
|
|
@ -28,7 +28,7 @@ public slots:
|
|||
void restart() { _injector->restart(); }
|
||||
void stop() { _injector->stop(); }
|
||||
|
||||
void setOptions(AudioInjectorOptions& options) { _injector->setOptions(options); }
|
||||
void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); }
|
||||
|
||||
float getLoudness() const { return _injector->getLoudness(); }
|
||||
bool isPlaying() const { return _injector->isPlaying(); }
|
||||
|
|
|
@ -317,7 +317,10 @@ void ScriptEngine::init() {
|
|||
registerAnimationTypes(this);
|
||||
registerAvatarTypes(this);
|
||||
registerAudioMetaTypes(this);
|
||||
_controllerScriptingInterface->registerControllerTypes(this);
|
||||
|
||||
if (_controllerScriptingInterface) {
|
||||
_controllerScriptingInterface->registerControllerTypes(this);
|
||||
}
|
||||
|
||||
qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
|
||||
qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
static int vec4MetaTypeId = qRegisterMetaType<glm::vec4>();
|
||||
static int vec3MetaTypeId = qRegisterMetaType<glm::vec3>();
|
||||
static int qVectorVec3MetaTypeId = qRegisterMetaType<QVector<glm::vec3>>();
|
||||
static int vec2MetaTypeId = qRegisterMetaType<glm::vec2>();
|
||||
static int quatMetaTypeId = qRegisterMetaType<glm::quat>();
|
||||
static int xColorMetaTypeId = qRegisterMetaType<xColor>();
|
||||
|
@ -31,6 +32,7 @@ static int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
|||
void registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue);
|
||||
|
@ -93,6 +95,16 @@ QVector<glm::vec3> qVectorVec3FromScriptValue(const QScriptValue& array){
|
|||
return newVector;
|
||||
}
|
||||
|
||||
void qVectorVec3FromScriptValue(const QScriptValue& array, QVector<glm::vec3>& vector ) {
|
||||
int length = array.property("length").toInteger();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
glm::vec3 newVec3 = glm::vec3();
|
||||
vec3FromScriptValue(array.property(i), newVec3);
|
||||
vector << newVec3;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", vec2.x);
|
||||
|
|
|
@ -57,6 +57,7 @@ QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url);
|
|||
void qURLFromScriptValue(const QScriptValue& object, QUrl& url);
|
||||
|
||||
QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::vec3>& vector);
|
||||
void qVectorVec3FromScriptValue(const QScriptValue& array, QVector<glm::vec3>& vector);
|
||||
QVector<glm::vec3> qVectorVec3FromScriptValue( const QScriptValue& array);
|
||||
|
||||
class PickRay {
|
||||
|
|