mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-11 23:27:32 +02:00
merging
This commit is contained in:
commit
a9f6c93a2c
144 changed files with 3785 additions and 5825 deletions
1
BUILD.md
1
BUILD.md
|
@ -13,7 +13,6 @@
|
|||
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
|
||||
* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4
|
||||
* [gverb](https://github.com/highfidelity/gverb)
|
||||
* [Soxr](http://sourceforge.net/projects/soxr/) ~> 0.1.1
|
||||
|
||||
The following external projects are optional dependencies. You can indicate to CMake that you would like to include them by passing -DGET_$NAME=1 when running a clean CMake build. For example, to get CMake to download and compile SDL2 you would pass -DGET_SDL2=1.
|
||||
|
||||
|
|
|
@ -112,7 +112,11 @@ set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH})
|
|||
|
||||
if (APPLE)
|
||||
|
||||
SET(OSX_SDK "10.9" CACHE String "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH")
|
||||
exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE OSX_VERSION)
|
||||
string(REGEX MATCH "^[0-9]+\\.[0-9]+" OSX_VERSION ${OSX_VERSION})
|
||||
message(STATUS "Detected OS X version = ${OSX_VERSION}")
|
||||
|
||||
set(OSX_SDK "${OSX_VERSION}" CACHE String "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH")
|
||||
|
||||
# set our OS X deployment target
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8)
|
||||
|
@ -127,13 +131,14 @@ if (APPLE)
|
|||
)
|
||||
|
||||
if (NOT _OSX_DESIRED_SDK_PATH)
|
||||
message(FATAL_ERROR "Could not find OS X ${OSX_SDK} SDK. Please pass OSX_SDK_PATH to CMake to point us to your SDKs directory.")
|
||||
message(STATUS "Could not find OS X ${OSX_SDK} SDK. Will fall back to default. If you want a specific SDK, please pass OSX_SDK and optionally OSX_SDK_PATH to CMake.")
|
||||
else ()
|
||||
message(STATUS "Found OS X ${OSX_SDK} SDK at ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk")
|
||||
|
||||
# set that as the SDK to use
|
||||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
|
||||
# set that as the SDK to use
|
||||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
|
||||
# Hide automoc folders (for IDEs)
|
||||
|
@ -181,7 +186,6 @@ setup_externals_binary_dir()
|
|||
option(GET_BULLET "Get Bullet library automatically as external project" 1)
|
||||
option(GET_GLM "Get GLM library automatically as external project" 1)
|
||||
option(GET_GVERB "Get Gverb library automatically as external project" 1)
|
||||
option(GET_SOXR "Get Soxr library automatically as external project" 1)
|
||||
option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
|
||||
option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
|
||||
option(GET_VHACD "Get V-HACD library automatically as external project" 1)
|
||||
|
|
2
cmake/externals/LibOVR/CMakeLists.txt
vendored
2
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -43,7 +43,7 @@ if (WIN32)
|
|||
endif()
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://static.oculus.com/sdk-downloads/ovr_sdk_macos_0.5.0.1.tar.gz
|
||||
|
|
7
cmake/externals/polyvox/CMakeLists.txt
vendored
7
cmake/externals/polyvox/CMakeLists.txt
vendored
|
@ -19,19 +19,20 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
|||
|
||||
if (APPLE)
|
||||
set(INSTALL_NAME_LIBRARY_DIR ${INSTALL_DIR}/lib)
|
||||
message(STATUS "in polyvox INSTALL_NAME_LIBRARY_DIR ${INSTALL_NAME_LIBRARY_DIR}")
|
||||
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-install-name
|
||||
change-install-name-debug
|
||||
COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR}/Debug -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <SOURCE_DIR>
|
||||
LOG 1
|
||||
)
|
||||
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-install-name
|
||||
change-install-name-release
|
||||
COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR}/Release -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
|
|
34
cmake/externals/soxr/CMakeLists.txt
vendored
34
cmake/externals/soxr/CMakeLists.txt
vendored
|
@ -1,34 +0,0 @@
|
|||
set(EXTERNAL_NAME soxr)
|
||||
|
||||
if (ANDROID)
|
||||
set(PLATFORM_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19" "-DHAVE_WORDS_BIGENDIAN_EXITCODE=1")
|
||||
endif ()
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/soxr-0.1.1.zip
|
||||
URL_MD5 349b5b2f323a7380bc12186d98c77d1d
|
||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DBUILD_SHARED_LIBS=1 -DBUILD_TESTS=0 -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of soxr include directories")
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/soxr.lib CACHE FILEPATH "List of soxr libraries")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/bin CACHE PATH "Path to soxr dll")
|
||||
elseif (APPLE)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/libsoxr.dylib CACHE FILEPATH "List of soxr libraries")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/libsoxr.so CACHE FILEPATH "List of soxr libraries")
|
||||
endif ()
|
|
@ -1,43 +0,0 @@
|
|||
#
|
||||
# FindSoxr.cmake
|
||||
#
|
||||
# Try to find the libsoxr resampling library
|
||||
#
|
||||
# You can provide a LIBSOXR_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# SOXR_FOUND - system found libsoxr
|
||||
# SOXR_INCLUDE_DIRS - the libsoxr include directory
|
||||
# SOXR_LIBRARIES - link to this to use libsoxr
|
||||
#
|
||||
# Created on 1/22/2015 by Stephen Birarda
|
||||
# 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
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("soxr")
|
||||
|
||||
find_path(SOXR_INCLUDE_DIRS soxr.h PATH_SUFFIXES include HINTS ${SOXR_SEARCH_DIRS})
|
||||
find_library(SOXR_LIBRARIES NAMES soxr PATH_SUFFIXES lib HINTS ${SOXR_SEARCH_DIRS})
|
||||
|
||||
if (WIN32)
|
||||
find_path(SOXR_DLL_PATH soxr.dll PATH_SUFFIXES bin HINTS ${SOXR_SEARCH_DIRS})
|
||||
endif()
|
||||
|
||||
set(SOXR_REQUIREMENTS SOXR_INCLUDE_DIRS SOXR_LIBRARIES)
|
||||
if (WIN32)
|
||||
list(APPEND SOXR_REQUIREMENTS SOXR_DLL_PATH)
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Soxr DEFAULT_MSG ${SOXR_REQUIREMENTS})
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${SOXR_DLL_PATH})
|
||||
endif ()
|
||||
|
||||
mark_as_advanced(SOXR_INCLUDE_DIRS SOXR_LIBRARIES SOXR_SEARCH_DIRS)
|
|
@ -2,17 +2,17 @@
|
|||
// examples
|
||||
//
|
||||
// Created by Eric Levin on 9/2/15
|
||||
// Additions by James B. Pollack @imgntn on 9/24/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
|
||||
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// these tune time-averaging and "on" value for analog trigger
|
||||
|
@ -29,9 +29,9 @@ var TRIGGER_ON_VALUE = 0.2;
|
|||
var DISTANCE_HOLDING_RADIUS_FACTOR = 5; // multiplied by distance between hand and object
|
||||
var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position
|
||||
var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did
|
||||
var NO_INTERSECT_COLOR = {red: 10, green: 10, blue: 255}; // line color when pick misses
|
||||
var INTERSECT_COLOR = {red: 250, green: 10, blue: 10}; // line color when pick hits
|
||||
var LINE_ENTITY_DIMENSIONS = {x: 1000, y: 1000, z: 1000};
|
||||
var NO_INTERSECT_COLOR = { red: 10, green: 10, blue: 255}; // line color when pick misses
|
||||
var INTERSECT_COLOR = { red: 250, green: 10, blue: 10}; // line color when pick hits
|
||||
var LINE_ENTITY_DIMENSIONS = { x: 1000, y: 1000, z: 1000};
|
||||
var LINE_LENGTH = 500;
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things
|
|||
var RIGHT_HAND = 1;
|
||||
var LEFT_HAND = 0;
|
||||
|
||||
var ZERO_VEC = {x: 0, y: 0, z: 0};
|
||||
var ZERO_VEC = { x: 0, y: 0, z: 0};
|
||||
var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}";
|
||||
var MSEC_PER_SEC = 1000.0;
|
||||
|
||||
|
@ -68,11 +68,14 @@ var STATE_DISTANCE_HOLDING = 1;
|
|||
var STATE_CONTINUE_DISTANCE_HOLDING = 2;
|
||||
var STATE_NEAR_GRABBING = 3;
|
||||
var STATE_CONTINUE_NEAR_GRABBING = 4;
|
||||
var STATE_RELEASE = 5;
|
||||
var STATE_NEAR_GRABBING_NON_COLLIDING = 5;
|
||||
var STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING = 6;
|
||||
var STATE_RELEASE = 7;
|
||||
|
||||
var GRAB_USER_DATA_KEY = "grabKey";
|
||||
var GRABBABLE_DATA_KEY = "grabbableKey";
|
||||
|
||||
function controller(hand, triggerAction) {
|
||||
function MyController(hand, triggerAction) {
|
||||
this.hand = hand;
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
this.getHandPosition = MyAvatar.getRightPalmPosition;
|
||||
|
@ -81,6 +84,7 @@ function controller(hand, triggerAction) {
|
|||
this.getHandPosition = MyAvatar.getLeftPalmPosition;
|
||||
this.getHandRotation = MyAvatar.getLeftPalmRotation;
|
||||
}
|
||||
|
||||
this.triggerAction = triggerAction;
|
||||
this.palm = 2 * hand;
|
||||
// this.tip = 2 * hand + 1; // unused, but I'm leaving this here for fear it will be needed
|
||||
|
@ -91,11 +95,13 @@ function controller(hand, triggerAction) {
|
|||
this.state = 0;
|
||||
this.pointer = null; // entity-id of line object
|
||||
this.triggerValue = 0; // rolling average of trigger value
|
||||
var _this = this;
|
||||
|
||||
this.update = function() {
|
||||
switch(this.state) {
|
||||
switch (this.state) {
|
||||
case STATE_SEARCHING:
|
||||
this.search();
|
||||
this.touchTest();
|
||||
break;
|
||||
case STATE_DISTANCE_HOLDING:
|
||||
this.distanceHolding();
|
||||
|
@ -109,44 +115,48 @@ function controller(hand, triggerAction) {
|
|||
case STATE_CONTINUE_NEAR_GRABBING:
|
||||
this.continueNearGrabbing();
|
||||
break;
|
||||
case STATE_NEAR_GRABBING_NON_COLLIDING:
|
||||
this.nearGrabbingNonColliding();
|
||||
break;
|
||||
case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING:
|
||||
this.continueNearGrabbingNonColliding();
|
||||
break;
|
||||
case STATE_RELEASE:
|
||||
this.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.lineOn = function(closePoint, farPoint, color) {
|
||||
// draw a line
|
||||
if (this.pointer == null) {
|
||||
if (this.pointer === null) {
|
||||
this.pointer = Entities.addEntity({
|
||||
type: "Line",
|
||||
name: "pointer",
|
||||
dimensions: LINE_ENTITY_DIMENSIONS,
|
||||
visible: true,
|
||||
position: closePoint,
|
||||
linePoints: [ ZERO_VEC, farPoint ],
|
||||
linePoints: [ZERO_VEC, farPoint],
|
||||
color: color,
|
||||
lifetime: LIFETIME
|
||||
});
|
||||
} else {
|
||||
Entities.editEntity(this.pointer, {
|
||||
position: closePoint,
|
||||
linePoints: [ ZERO_VEC, farPoint ],
|
||||
linePoints: [ZERO_VEC, farPoint],
|
||||
color: color,
|
||||
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.lineOff = function() {
|
||||
if (this.pointer != null) {
|
||||
if (this.pointer !== null) {
|
||||
Entities.deleteEntity(this.pointer);
|
||||
}
|
||||
this.pointer = null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.triggerSmoothedSqueezed = function() {
|
||||
var triggerValue = Controller.getActionValue(this.triggerAction);
|
||||
|
@ -154,14 +164,12 @@ function controller(hand, triggerAction) {
|
|||
this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
|
||||
(triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
|
||||
return this.triggerValue > TRIGGER_ON_VALUE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.triggerSqueezed = function() {
|
||||
var triggerValue = Controller.getActionValue(this.triggerAction);
|
||||
return triggerValue > TRIGGER_ON_VALUE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.search = function() {
|
||||
if (!this.triggerSmoothedSqueezed()) {
|
||||
|
@ -171,7 +179,15 @@ function controller(hand, triggerAction) {
|
|||
|
||||
// the trigger is being pressed, do a ray test
|
||||
var handPosition = this.getHandPosition();
|
||||
var pickRay = {origin: handPosition, direction: Quat.getUp(this.getHandRotation())};
|
||||
var pickRay = {
|
||||
origin: handPosition,
|
||||
direction: Quat.getUp(this.getHandRotation())
|
||||
};
|
||||
|
||||
var defaultGrabbableData = {
|
||||
grabbable: true
|
||||
};
|
||||
|
||||
var intersection = Entities.findRayIntersection(pickRay, true);
|
||||
if (intersection.intersects &&
|
||||
intersection.properties.collisionsWillMove === 1 &&
|
||||
|
@ -180,9 +196,15 @@ function controller(hand, triggerAction) {
|
|||
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
var intersectionDistance = Vec3.distance(handControllerPosition, intersection.intersection);
|
||||
this.grabbedEntity = intersection.entityID;
|
||||
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, defaultGrabbableData);
|
||||
if (grabbableData.grabbable === false) {
|
||||
return;
|
||||
}
|
||||
if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) {
|
||||
// the hand is very close to the intersected object. go into close-grabbing mode.
|
||||
this.state = STATE_NEAR_GRABBING;
|
||||
|
||||
} else {
|
||||
// the hand is far from the intersected object. go into distance-holding mode
|
||||
this.state = STATE_DISTANCE_HOLDING;
|
||||
|
@ -192,30 +214,39 @@ function controller(hand, triggerAction) {
|
|||
// forward ray test failed, try sphere test.
|
||||
var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS);
|
||||
var minDistance = GRAB_RADIUS;
|
||||
var grabbedEntity = null;
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
var props = Entities.getEntityProperties(nearbyEntities[i]);
|
||||
var distance = Vec3.distance(props.position, handPosition);
|
||||
if (distance < minDistance && props.name !== "pointer" &&
|
||||
props.collisionsWillMove === 1 &&
|
||||
props.locked === 0) {
|
||||
var i, props, distance;
|
||||
|
||||
for (i = 0; i < nearbyEntities.length; i++) {
|
||||
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], defaultGrabbableData);
|
||||
if (grabbableData.grabbable === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
props = Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]);
|
||||
|
||||
distance = Vec3.distance(props.position, handPosition);
|
||||
if (distance < minDistance && props.name !== "pointer") {
|
||||
this.grabbedEntity = nearbyEntities[i];
|
||||
minDistance = distance;
|
||||
}
|
||||
}
|
||||
if (this.grabbedEntity === null) {
|
||||
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
} else {
|
||||
} else if (props.locked === 0 && props.collisionsWillMove === 1) {
|
||||
this.state = STATE_NEAR_GRABBING;
|
||||
} else if (props.collisionsWillMove === 0) {
|
||||
// We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event
|
||||
this.state = STATE_NEAR_GRABBING_NON_COLLIDING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.distanceHolding = function() {
|
||||
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position","rotation"]);
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
|
||||
|
||||
// add the action and initialize some variables
|
||||
this.currentObjectPosition = grabbedProperties.position;
|
||||
|
@ -230,23 +261,22 @@ function controller(hand, triggerAction) {
|
|||
targetRotation: this.currentObjectRotation,
|
||||
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME
|
||||
});
|
||||
if (this.actionID == NULL_ACTION_ID) {
|
||||
if (this.actionID === NULL_ACTION_ID) {
|
||||
this.actionID = null;
|
||||
}
|
||||
|
||||
if (this.actionID != null) {
|
||||
if (this.actionID !== null) {
|
||||
this.state = STATE_CONTINUE_DISTANCE_HOLDING;
|
||||
this.activateEntity(this.grabbedEntity);
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab");
|
||||
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
||||
} else {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
|
||||
}
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.continueDistanceHolding = function() {
|
||||
if (!this.triggerSmoothedSqueezed()) {
|
||||
|
@ -262,9 +292,7 @@ function controller(hand, triggerAction) {
|
|||
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
|
||||
|
||||
// the action was set up on a previous call. update the targets.
|
||||
var radius = Math.max(Vec3.distance(this.currentObjectPosition,
|
||||
handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR,
|
||||
DISTANCE_HOLDING_RADIUS_FACTOR);
|
||||
var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR);
|
||||
|
||||
var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition);
|
||||
this.handPreviousPosition = handControllerPosition;
|
||||
|
@ -280,20 +308,19 @@ function controller(hand, triggerAction) {
|
|||
this.currentObjectTime = now;
|
||||
|
||||
// this doubles hand rotation
|
||||
var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation,
|
||||
DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR),
|
||||
Quat.inverse(this.handPreviousRotation));
|
||||
var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), Quat.inverse(this.handPreviousRotation));
|
||||
this.handPreviousRotation = handRotation;
|
||||
this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation);
|
||||
|
||||
Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab");
|
||||
|
||||
Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME
|
||||
targetPosition: this.currentObjectPosition,
|
||||
linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
targetRotation: this.currentObjectRotation,
|
||||
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.nearGrabbing = function() {
|
||||
if (!this.triggerSmoothedSqueezed()) {
|
||||
|
@ -313,32 +340,32 @@ function controller(hand, triggerAction) {
|
|||
var objectRotation = grabbedProperties.rotation;
|
||||
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
|
||||
|
||||
currentObjectPosition = grabbedProperties.position;
|
||||
var currentObjectPosition = grabbedProperties.position;
|
||||
var offset = Vec3.subtract(currentObjectPosition, handPosition);
|
||||
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
|
||||
|
||||
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
||||
hand: this.hand == RIGHT_HAND ? "right" : "left",
|
||||
hand: this.hand === RIGHT_HAND ? "right" : "left",
|
||||
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
|
||||
relativePosition: offsetPosition,
|
||||
relativeRotation: offsetRotation
|
||||
});
|
||||
if (this.actionID == NULL_ACTION_ID) {
|
||||
if (this.actionID === NULL_ACTION_ID) {
|
||||
this.actionID = null;
|
||||
} else {
|
||||
this.state = STATE_CONTINUE_NEAR_GRABBING;
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startNearGrab");
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
||||
} else {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
|
||||
}
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startNearGrab");
|
||||
|
||||
}
|
||||
|
||||
this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm);
|
||||
this.currentObjectTime = Date.now();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.continueNearGrabbing = function() {
|
||||
if (!this.triggerSmoothedSqueezed()) {
|
||||
|
@ -357,8 +384,94 @@ function controller(hand, triggerAction) {
|
|||
this.currentHandControllerPosition = handControllerPosition;
|
||||
this.currentObjectTime = now;
|
||||
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab");
|
||||
}
|
||||
};
|
||||
|
||||
this.nearGrabbingNonColliding = function() {
|
||||
if (!this.triggerSmoothedSqueezed()) {
|
||||
this.state = STATE_RELEASE;
|
||||
return;
|
||||
}
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding");
|
||||
this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING;
|
||||
};
|
||||
|
||||
this.continueNearGrabbingNonColliding = function() {
|
||||
if (!this.triggerSmoothedSqueezed()) {
|
||||
this.state = STATE_RELEASE;
|
||||
return;
|
||||
}
|
||||
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding");
|
||||
};
|
||||
|
||||
_this.allTouchedIDs = {};
|
||||
this.touchTest = function() {
|
||||
var maxDistance = 0.05;
|
||||
var leftHandPosition = MyAvatar.getLeftPalmPosition();
|
||||
var rightHandPosition = MyAvatar.getRightPalmPosition();
|
||||
var leftEntities = Entities.findEntities(leftHandPosition, maxDistance);
|
||||
var rightEntities = Entities.findEntities(rightHandPosition, maxDistance);
|
||||
var ids = [];
|
||||
|
||||
if (leftEntities.length !== 0) {
|
||||
leftEntities.forEach(function(entity) {
|
||||
ids.push(entity);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (rightEntities.length !== 0) {
|
||||
rightEntities.forEach(function(entity) {
|
||||
ids.push(entity);
|
||||
});
|
||||
}
|
||||
|
||||
ids.forEach(function(id) {
|
||||
|
||||
var props = Entities.getEntityProperties(id, ["boundingBox", "name"]);
|
||||
if (props.name === 'pointer') {
|
||||
return;
|
||||
} else {
|
||||
var entityMinPoint = props.boundingBox.brn;
|
||||
var entityMaxPoint = props.boundingBox.tfl;
|
||||
var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint);
|
||||
var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint);
|
||||
|
||||
if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) {
|
||||
// we haven't been touched before, but either right or left is touching us now
|
||||
_this.allTouchedIDs[id] = true;
|
||||
_this.startTouch(id);
|
||||
} else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === true) {
|
||||
// we have been touched before and are still being touched
|
||||
// continue touch
|
||||
_this.continueTouch(id);
|
||||
} else if (_this.allTouchedIDs[id] === true) {
|
||||
delete _this.allTouchedIDs[id];
|
||||
_this.stopTouch(id);
|
||||
|
||||
} else {
|
||||
//we are in another state
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.startTouch = function(entityID) {
|
||||
// print('START TOUCH' + entityID);
|
||||
Entities.callEntityMethod(entityID, "startTouch");
|
||||
};
|
||||
|
||||
this.continueTouch = function(entityID) {
|
||||
// print('CONTINUE TOUCH' + entityID);
|
||||
Entities.callEntityMethod(entityID, "continueTouch");
|
||||
};
|
||||
|
||||
this.stopTouch = function(entityID) {
|
||||
// print('STOP TOUCH' + entityID);
|
||||
Entities.callEntityMethod(entityID, "stopTouch");
|
||||
};
|
||||
|
||||
this.computeReleaseVelocity = function(deltaPosition, deltaTime, useMultiplier) {
|
||||
if (deltaTime > 0.0 && !vec3equal(deltaPosition, ZERO_VEC)) {
|
||||
|
@ -367,75 +480,70 @@ function controller(hand, triggerAction) {
|
|||
// value would otherwise give the held object time to slow down.
|
||||
if (this.triggerSqueezed()) {
|
||||
this.grabbedVelocity =
|
||||
Vec3.sum(Vec3.multiply(this.grabbedVelocity,
|
||||
(1.0 - NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)),
|
||||
Vec3.multiply(grabbedVelocity, NEAR_GRABBING_VELOCITY_SMOOTH_RATIO));
|
||||
Vec3.sum(Vec3.multiply(this.grabbedVelocity, (1.0 - NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)),
|
||||
Vec3.multiply(grabbedVelocity, NEAR_GRABBING_VELOCITY_SMOOTH_RATIO));
|
||||
}
|
||||
|
||||
if (useMultiplier) {
|
||||
this.grabbedVelocity = Vec3.multiply(this.grabbedVelocity, RELEASE_VELOCITY_MULTIPLIER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.release = function() {
|
||||
this.lineOff();
|
||||
|
||||
if (this.grabbedEntity != null && this.actionID != null) {
|
||||
if (this.grabbedEntity !== null && this.actionID !== null) {
|
||||
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
||||
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
||||
}
|
||||
|
||||
// the action will tend to quickly bring an object's velocity to zero. now that
|
||||
// the action is gone, set the objects velocity to something the holder might expect.
|
||||
Entities.editEntity(this.grabbedEntity, {velocity: this.grabbedVelocity});
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
velocity: this.grabbedVelocity
|
||||
});
|
||||
this.deactivateEntity(this.grabbedEntity);
|
||||
|
||||
this.grabbedVelocity = ZERO_VEC;
|
||||
this.grabbedEntity = null;
|
||||
this.actionID = null;
|
||||
this.state = STATE_SEARCHING;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.cleanup = function() {
|
||||
release();
|
||||
}
|
||||
this.release();
|
||||
};
|
||||
|
||||
this.activateEntity = function(entity) {
|
||||
this.activateEntity = function() {
|
||||
var data = {
|
||||
activated: true,
|
||||
avatarId: MyAvatar.sessionUUID
|
||||
};
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
|
||||
}
|
||||
};
|
||||
|
||||
this.deactivateEntity = function(entity) {
|
||||
this.deactivateEntity = function() {
|
||||
var data = {
|
||||
activated: false,
|
||||
avatarId: null
|
||||
};
|
||||
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var rightController = new controller(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
|
||||
var leftController = new controller(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
|
||||
|
||||
var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
|
||||
var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
|
||||
|
||||
function update() {
|
||||
rightController.update();
|
||||
leftController.update();
|
||||
}
|
||||
|
||||
|
||||
function cleanup() {
|
||||
rightController.cleanup();
|
||||
leftController.cleanup();
|
||||
}
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Script.update.connect(update)
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -1035,7 +1035,7 @@ function handeMenuEvent(menuItem) {
|
|||
|
||||
var importURL;
|
||||
if (menuItem == "Import Entities") {
|
||||
importURL = Window.browse("Select models to import", "", "*.json");
|
||||
importURL = "file:///" + Window.browse("Select models to import", "", "*.json");
|
||||
} else {
|
||||
importURL = Window.prompt("URL of SVO to import", "");
|
||||
}
|
||||
|
|
71
examples/entityScripts/changeColorOnTouch.js
Normal file
71
examples/entityScripts/changeColorOnTouch.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// changeColorOnTouch.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 11/1/14.
|
||||
// Additions by James B. Pollack @imgntn on 9/23/2015
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// ATTENTION: Requires you to run handControllerGrab.js
|
||||
// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will
|
||||
// change the color of the entity when you touch it.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
function ChangeColorOnTouch () {
|
||||
this.oldColor = {};
|
||||
this.oldColorKnown = false;
|
||||
}
|
||||
|
||||
ChangeColorOnTouch.prototype = {
|
||||
|
||||
storeOldColor: function(entityID) {
|
||||
var oldProperties = Entities.getEntityProperties(entityID);
|
||||
this.oldColor = oldProperties.color;
|
||||
this.oldColorKnown = true;
|
||||
|
||||
print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
print("preload");
|
||||
|
||||
this.entityID = entityID;
|
||||
this.storeOldColor(entityID);
|
||||
},
|
||||
|
||||
startTouch: function() {
|
||||
print("startTouch");
|
||||
|
||||
if (!this.oldColorKnown) {
|
||||
this.storeOldColor(this.entityID);
|
||||
}
|
||||
|
||||
Entities.editEntity(this.entityID, {color: { red: 0, green: 255, blue: 255 }});
|
||||
},
|
||||
|
||||
continueTouch: function() {
|
||||
//unused here
|
||||
return;
|
||||
},
|
||||
|
||||
stopTouch: function() {
|
||||
print("stopTouch");
|
||||
|
||||
if (this.oldColorKnown) {
|
||||
print("leave restoring old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||
Entities.editEntity(this.entityID, {color: this.oldColor});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
return new ChangeColorOnTouch();
|
||||
});
|
|
@ -79,19 +79,18 @@
|
|||
lastFrame: 10000,
|
||||
running: true
|
||||
});
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
||||
this.paintStream = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
animationSettings: animationSettings,
|
||||
position: this.properties.position,
|
||||
textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png",
|
||||
emitVelocity: ZERO_VEC,
|
||||
emitSpeed: 0,
|
||||
speedSpread: 0.02,
|
||||
polarFinish: 2 * DEG_TO_RAD,
|
||||
emitAcceleration: ZERO_VEC,
|
||||
velocitySpread: {
|
||||
x: .1,
|
||||
y: .1,
|
||||
z: 0.1
|
||||
},
|
||||
emitRate: 100,
|
||||
particleRadius: 0.01,
|
||||
color: {
|
||||
|
@ -127,7 +126,8 @@
|
|||
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
|
||||
Entities.editEntity(self.paintStream, {
|
||||
position: position,
|
||||
emitVelocity: Vec3.multiply(5, forwardVec)
|
||||
emitOrientation: forwardVec,
|
||||
emitSpeed: 5
|
||||
});
|
||||
|
||||
//Now check for an intersection with an entity
|
||||
|
|
|
@ -13,35 +13,49 @@
|
|||
|
||||
(function () {
|
||||
var box,
|
||||
sphere,
|
||||
sphereDimensions = { x: 0.4, y: 0.8, z: 0.2 },
|
||||
pointDimensions = { x: 0.0, y: 0.0, z: 0.0 },
|
||||
sphereOrientation = Quat.fromPitchYawRollDegrees(-60.0, 30.0, 0.0),
|
||||
verticalOrientation = Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0),
|
||||
particles,
|
||||
particleExample = -1,
|
||||
NUM_PARTICLE_EXAMPLES = 11,
|
||||
PARTICLE_RADIUS = 0.04;
|
||||
PARTICLE_RADIUS = 0.04,
|
||||
SLOW_EMIT_RATE = 2.0,
|
||||
HALF_EMIT_RATE = 50.0,
|
||||
FAST_EMIT_RATE = 100.0,
|
||||
SLOW_EMIT_SPEED = 0.025,
|
||||
FAST_EMIT_SPEED = 1.0,
|
||||
GRAVITY_EMIT_ACCELERATON = { x: 0.0, y: -0.3, z: 0.0 },
|
||||
ZERO_EMIT_ACCELERATON = { x: 0.0, y: 0.0, z: 0.0 },
|
||||
PI = 3.141593,
|
||||
DEG_TO_RAD = PI / 180.0,
|
||||
NUM_PARTICLE_EXAMPLES = 18;
|
||||
|
||||
function onClickDownOnEntity(entityID) {
|
||||
if (entityID === box || entityID === particles) {
|
||||
if (entityID === box || entityID === sphere || entityID === particles) {
|
||||
particleExample = (particleExample + 1) % NUM_PARTICLE_EXAMPLES;
|
||||
|
||||
switch (particleExample) {
|
||||
case 0:
|
||||
print("Simple emitter");
|
||||
Entities.editEntity(particles, {
|
||||
velocitySpread: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
speedSpread: 0.0,
|
||||
accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
radiusSpread: 0.0,
|
||||
animationIsPlaying: true
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
print("Velocity spread");
|
||||
print("Speed spread");
|
||||
Entities.editEntity(particles, {
|
||||
velocitySpread: { x: 0.1, y: 0.0, z: 0.1 }
|
||||
speedSpread: 0.1
|
||||
});
|
||||
break;
|
||||
case 2:
|
||||
print("Acceleration spread");
|
||||
Entities.editEntity(particles, {
|
||||
velocitySpread: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
speedSpread: 0.0,
|
||||
accelerationSpread: { x: 0.0, y: 0.1, z: 0.0 }
|
||||
});
|
||||
break;
|
||||
|
@ -104,19 +118,99 @@
|
|||
});
|
||||
break;
|
||||
case 10:
|
||||
print("Stop emitting");
|
||||
print("Emit in a spread beam");
|
||||
Entities.editEntity(particles, {
|
||||
colorStart: { red: 255, green: 255, blue: 255 },
|
||||
colorFinish: { red: 255, green: 255, blue: 255 },
|
||||
alphaFinish: 0.0,
|
||||
emitRate: FAST_EMIT_RATE,
|
||||
polarFinish: 2.0 * DEG_TO_RAD
|
||||
});
|
||||
break;
|
||||
case 11:
|
||||
print("Emit in all directions from point");
|
||||
Entities.editEntity(particles, {
|
||||
emitSpeed: SLOW_EMIT_SPEED,
|
||||
emitAcceleration: ZERO_EMIT_ACCELERATON,
|
||||
polarFinish: PI
|
||||
});
|
||||
break;
|
||||
case 12:
|
||||
print("Emit from sphere surface");
|
||||
Entities.editEntity(particles, {
|
||||
colorStart: { red: 255, green: 255, blue: 255 },
|
||||
colorFinish: { red: 255, green: 255, blue: 255 },
|
||||
emitDimensions: sphereDimensions,
|
||||
emitOrientation: sphereOrientation
|
||||
});
|
||||
Entities.editEntity(box, {
|
||||
visible: false
|
||||
});
|
||||
Entities.editEntity(sphere, {
|
||||
visible: true
|
||||
});
|
||||
break;
|
||||
case 13:
|
||||
print("Emit from hemisphere of sphere surface");
|
||||
Entities.editEntity(particles, {
|
||||
polarFinish: PI / 2.0
|
||||
});
|
||||
break;
|
||||
case 14:
|
||||
print("Emit from equator of sphere surface");
|
||||
Entities.editEntity(particles, {
|
||||
polarStart: PI / 2.0,
|
||||
emitRate: HALF_EMIT_RATE
|
||||
});
|
||||
break;
|
||||
case 15:
|
||||
print("Emit from half equator of sphere surface");
|
||||
Entities.editEntity(particles, {
|
||||
azimuthStart: -PI / 2.0,
|
||||
azimuthFinish: PI / 2.0
|
||||
});
|
||||
break;
|
||||
case 16:
|
||||
print("Emit within eighth of sphere volume");
|
||||
Entities.editEntity(particles, {
|
||||
polarStart: 0.0,
|
||||
polarFinish: PI / 2.0,
|
||||
azimuthStart: 0,
|
||||
azimuthFinish: PI / 2.0,
|
||||
emitRadiusStart: 0.0,
|
||||
alphaFinish: 1.0,
|
||||
emitSpeed: 0.0
|
||||
});
|
||||
break;
|
||||
case 17:
|
||||
print("Stop emitting");
|
||||
Entities.editEntity(particles, {
|
||||
emitDimensions: pointDimensions,
|
||||
emitOrientation: verticalOrientation,
|
||||
alphaFinish: 1.0,
|
||||
emitRate: SLOW_EMIT_RATE,
|
||||
emitSpeed: FAST_EMIT_SPEED,
|
||||
emitAcceleration: GRAVITY_EMIT_ACCELERATON,
|
||||
polarStart: 0.0,
|
||||
polarFinish: 0.0,
|
||||
azimuthStart: -PI,
|
||||
azimuthFinish: PI,
|
||||
animationIsPlaying: false
|
||||
});
|
||||
Entities.editEntity(box, {
|
||||
visible: true
|
||||
});
|
||||
Entities.editEntity(sphere, {
|
||||
visible: false
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
var spawnPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))),
|
||||
var boxPoint,
|
||||
spawnPoint,
|
||||
animation = {
|
||||
fps: 30,
|
||||
frameIndex: 0,
|
||||
|
@ -126,28 +220,50 @@
|
|||
loop: true
|
||||
};
|
||||
|
||||
boxPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
|
||||
boxPoint = Vec3.sum(boxPoint, { x: 0.0, y: -0.5, z: 0.0 });
|
||||
spawnPoint = Vec3.sum(boxPoint, { x: 0.0, y: 1.0, z: 0.0 });
|
||||
|
||||
box = Entities.addEntity({
|
||||
type: "Box",
|
||||
position: spawnPoint,
|
||||
name: "ParticlesTest Box",
|
||||
position: boxPoint,
|
||||
rotation: verticalOrientation,
|
||||
dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
color: { red: 128, green: 128, blue: 128 },
|
||||
lifetime: 3600 // 1 hour; just in case
|
||||
lifetime: 3600, // 1 hour; just in case
|
||||
visible: true
|
||||
});
|
||||
|
||||
// Same size and orientation as emitter when ellipsoid.
|
||||
sphere = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
name: "ParticlesTest Sphere",
|
||||
position: boxPoint,
|
||||
rotation: sphereOrientation,
|
||||
dimensions: sphereDimensions,
|
||||
color: { red: 128, green: 128, blue: 128 },
|
||||
lifetime: 3600, // 1 hour; just in case
|
||||
visible: false
|
||||
});
|
||||
|
||||
// 1.0m above the box or ellipsoid.
|
||||
particles = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
name: "ParticlesTest Emitter",
|
||||
position: spawnPoint,
|
||||
emitOrientation: verticalOrientation,
|
||||
particleRadius: PARTICLE_RADIUS,
|
||||
radiusSpread: 0.0,
|
||||
emitRate: 2.0,
|
||||
emitVelocity: { x: 0.0, y: 1.0, z: 0.0 },
|
||||
velocitySpread: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
emitAcceleration: { x: 0.0, y: -0.3, z: 0.0 },
|
||||
emitRate: SLOW_EMIT_RATE,
|
||||
emitSpeed: FAST_EMIT_SPEED,
|
||||
speedSpread: 0.0,
|
||||
emitAcceleration: GRAVITY_EMIT_ACCELERATON,
|
||||
accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
lifespan: 5.0,
|
||||
visible: true,
|
||||
visible: false,
|
||||
locked: false,
|
||||
animationSettings: animation,
|
||||
animationIsPlaying: false,
|
||||
|
@ -163,6 +279,7 @@
|
|||
Entities.clickDownOnEntity.disconnect(onClickDownOnEntity);
|
||||
Entities.deleteEntity(particles);
|
||||
Entities.deleteEntity(box);
|
||||
Entities.deleteEntity(sphere);
|
||||
}
|
||||
|
||||
setUp();
|
||||
|
|
94
examples/example/misc/collectHifiStats.js
Normal file
94
examples/example/misc/collectHifiStats.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// collectHifiStats.js
|
||||
//
|
||||
// Created by Thijs Wenker on 24 Sept 2015
|
||||
// Additions by James B. Pollack @imgntn on 25 Sept 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Collects stats for analysis to a REST endpoint. Defaults to batching stats, but can be customized.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// The url where the data will be posted.
|
||||
var ENDPOINT_URL = "";
|
||||
|
||||
var BATCH_STATS = true;
|
||||
var BATCH_SIZE = 5;
|
||||
|
||||
var batch = [];
|
||||
|
||||
if (BATCH_STATS) {
|
||||
|
||||
var RECORD_EVERY = 1000; // 1 seconds
|
||||
var batchCount = 0;
|
||||
|
||||
Script.setInterval(function() {
|
||||
|
||||
if (batchCount === BATCH_SIZE) {
|
||||
sendBatchToEndpoint(batch);
|
||||
batchCount = 0;
|
||||
}
|
||||
Stats.forceUpdateStats();
|
||||
batch.push(getBatchStats());
|
||||
batchCount++;
|
||||
}, RECORD_EVERY);
|
||||
|
||||
|
||||
} else {
|
||||
// Send the data every:
|
||||
var SEND_EVERY = 30000; // 30 seconds
|
||||
|
||||
Script.setInterval(function() {
|
||||
Stats.forceUpdateStats();
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("POST", ENDPOINT_URL, false);
|
||||
req.send(getStats());
|
||||
}, SEND_EVERY);
|
||||
}
|
||||
|
||||
function getStats() {
|
||||
return JSON.stringify({
|
||||
username: GlobalServices.username,
|
||||
location: Window.location.hostname,
|
||||
framerate: Stats.framerate,
|
||||
simrate: Stats.simrate,
|
||||
ping: {
|
||||
audio: Stats.audioPing,
|
||||
avatar: Stats.avatarPing,
|
||||
entities: Stats.entitiesPing,
|
||||
asset: Stats.assetPing
|
||||
},
|
||||
position: Camera.position,
|
||||
yaw: Stats.yaw,
|
||||
rotation: Camera.orientation.x + ',' + Camera.orientation.y + ',' + Camera.orientation.z + ',' + Camera.orientation.w
|
||||
})
|
||||
}
|
||||
|
||||
function getBatchStats() {
|
||||
// print('GET BATCH STATS');
|
||||
return {
|
||||
username: GlobalServices.username,
|
||||
location: Window.location.hostname,
|
||||
framerate: Stats.framerate,
|
||||
simrate: Stats.simrate,
|
||||
ping: {
|
||||
audio: Stats.audioPing,
|
||||
avatar: Stats.avatarPing,
|
||||
entities: Stats.entitiesPing,
|
||||
asset: Stats.assetPing
|
||||
},
|
||||
position: Camera.position,
|
||||
yaw: Stats.yaw,
|
||||
rotation: Camera.orientation.x + ',' + Camera.orientation.y + ',' + Camera.orientation.z + ',' + Camera.orientation.w
|
||||
}
|
||||
}
|
||||
|
||||
function sendBatchToEndpoint(batch) {
|
||||
// print('SEND BATCH TO ENDPOINT');
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("POST", ENDPOINT_URL, false);
|
||||
req.send(JSON.stringify(batch));
|
||||
batch = [];
|
||||
}
|
67
examples/example/misc/statsExample.js
Normal file
67
examples/example/misc/statsExample.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// statsExample.js
|
||||
// examples/example/misc
|
||||
//
|
||||
// Created by Thijs Wenker on 24 Sept 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Prints the stats to the console.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// The stats to be displayed
|
||||
var stats = [
|
||||
'serverCount',
|
||||
'framerate', // a.k.a. FPS
|
||||
'simrate',
|
||||
'avatarSimrate',
|
||||
'avatarCount',
|
||||
'packetInCount',
|
||||
'packetOutCount',
|
||||
'mbpsIn',
|
||||
'mbpsOut',
|
||||
'audioPing',
|
||||
'avatarPing',
|
||||
'entitiesPing',
|
||||
'assetPing',
|
||||
'velocity',
|
||||
'yaw',
|
||||
'avatarMixerKbps',
|
||||
'avatarMixerPps',
|
||||
'audioMixerKbps',
|
||||
'audioMixerPps',
|
||||
'downloads',
|
||||
'downloadsPending',
|
||||
'triangles',
|
||||
'quads',
|
||||
'materialSwitches',
|
||||
'meshOpaque',
|
||||
'meshTranslucent',
|
||||
'opaqueConsidered',
|
||||
'opaqueOutOfView',
|
||||
'opaqueTooSmall',
|
||||
'translucentConsidered',
|
||||
'translucentOutOfView',
|
||||
'translucentTooSmall',
|
||||
'sendingMode',
|
||||
'packetStats',
|
||||
'lodStatus',
|
||||
'timingStats',
|
||||
'serverElements',
|
||||
'serverInternal',
|
||||
'serverLeaves',
|
||||
'localElements',
|
||||
'localInternal',
|
||||
'localLeaves'
|
||||
];
|
||||
|
||||
// Force update the stats, in case the stats panel is invisible
|
||||
Stats.forceUpdateStats();
|
||||
|
||||
// Loop through the stats and display them
|
||||
for (var i in stats) {
|
||||
var stat = stats[i];
|
||||
print(stat + " = " + Stats[stat]);
|
||||
}
|
|
@ -11,6 +11,17 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
var normalDisplay = Entities.addEntity({
|
||||
type: "Line",
|
||||
name: "normalDisplay",
|
||||
visible: false,
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
dimensions: { x: 100, y: 100, z: 100 }
|
||||
});
|
||||
|
||||
var wasVisible = false;
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
@ -18,26 +29,45 @@ function mouseMoveEvent(event) {
|
|||
print("computePickRay origin=" + pickRay.origin.x + ", " + pickRay.origin.y + ", " + pickRay.origin.z);
|
||||
print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z);
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
intersection = Entities.findRayIntersection(pickRay);
|
||||
intersection = Entities.findRayIntersection(pickRay, true); // to get precise picking
|
||||
if (!intersection.accurate) {
|
||||
print(">>> NOTE: intersection not accurate. will try calling Entities.findRayIntersectionBlocking()");
|
||||
intersection = Entities.findRayIntersectionBlocking(pickRay);
|
||||
intersection = Entities.findRayIntersectionBlocking(pickRay, true); // to get precise picking
|
||||
print(">>> AFTER BLOCKING CALL intersection.accurate=" + intersection.accurate);
|
||||
}
|
||||
|
||||
if (intersection.intersects) {
|
||||
print("intersection entityID.id=" + intersection.entityID.id);
|
||||
print("intersection entityID=" + intersection.entityID);
|
||||
print("intersection properties.modelURL=" + intersection.properties.modelURL);
|
||||
print("intersection face=" + intersection.face);
|
||||
print("intersection distance=" + intersection.distance);
|
||||
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
|
||||
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
|
||||
+ intersection.intersection.y + ", " + intersection.intersection.z);
|
||||
print("intersection surfaceNormal.x/y/z=" + intersection.surfaceNormal.x + ", "
|
||||
+ intersection.surfaceNormal.y + ", " + intersection.surfaceNormal.z);
|
||||
|
||||
|
||||
// Note: line entity takes points in "entity relative frame"
|
||||
var lineStart = { x: 0, y: 0, z: 0 };
|
||||
var lineEnd = intersection.surfaceNormal;
|
||||
|
||||
Entities.editEntity(normalDisplay, {
|
||||
visible: true,
|
||||
position: intersection.intersection,
|
||||
linePoints: [lineStart, lineEnd],
|
||||
});
|
||||
wasVisible = true;
|
||||
} else {
|
||||
if (wasVisible) {
|
||||
Entities.editEntity(normalDisplay, { visible: false });
|
||||
wasVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
||||
function scriptEnding() {
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.scriptEnding.connect(function() {
|
||||
Entities.deleteEntity(normalDisplay);
|
||||
});
|
||||
|
||||
|
|
|
@ -101,7 +101,6 @@ Rocket = function(point, colorPalette) {
|
|||
this.burst = false;
|
||||
|
||||
this.emitRate = randInt(80, 120);
|
||||
this.emitStrength = randInt(5.0, 7.0);
|
||||
|
||||
this.rocket = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
|
@ -163,6 +162,9 @@ Rocket.prototype.explode = function(position) {
|
|||
});
|
||||
|
||||
var colorIndex = 0;
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
||||
for (var i = 0; i < NUM_BURSTS; ++i) {
|
||||
var color = this.colors[colorIndex];
|
||||
print(JSON.stringify(color));
|
||||
|
@ -172,12 +174,7 @@ Rocket.prototype.explode = function(position) {
|
|||
position: position,
|
||||
textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png',
|
||||
emitRate: this.emitRate,
|
||||
emitStrength: this.emitStrength,
|
||||
emitDirection: {
|
||||
x: Math.pow(-1, i) * randFloat(0.0, 1.4),
|
||||
y: 1.0,
|
||||
z: 0.0
|
||||
},
|
||||
polarFinish: 25 * DEG_TO_RAD,
|
||||
color: color,
|
||||
lifespan: 1.0,
|
||||
visible: true,
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
var AUDIO_RANGE = 0.5 * RANGE;
|
||||
var DIST_BETWEEN_BURSTS = 1.0;
|
||||
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
||||
var LOUDNESS_RADIUS_RATIO = 10;
|
||||
|
||||
var TEXTURE_PATH = 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png';
|
||||
|
@ -86,12 +89,7 @@
|
|||
position: this.point,
|
||||
textures: TEXTURE_PATH,
|
||||
emitRate: this.emitRate,
|
||||
emitStrength: this.emitStrength,
|
||||
emitDirection: {
|
||||
x: Math.pow(-1, i) * randFloat(0.0, 0.4),
|
||||
y: 1.0,
|
||||
z: 0.0
|
||||
},
|
||||
polarFinish: 25 * DEG_TO_RAD,
|
||||
color: color,
|
||||
lifespan: 1.0,
|
||||
visible: true,
|
||||
|
|
|
@ -35,13 +35,16 @@
|
|||
firstFrame: 0,
|
||||
lastFrame: 30,
|
||||
loop: true });
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
||||
this.entity = Entities.addEntity({ type: "ParticleEffect",
|
||||
animationSettings: animationSettings,
|
||||
position: spawnPoint,
|
||||
dimensions: {x: 2, y: 2, z: 2},
|
||||
emitVelocity: {x: 0, y: 5, z: 0},
|
||||
velocitySpread: {x: 2, y: 0, z: 2},
|
||||
emitSpeed: 5,
|
||||
speedSpread: 2,
|
||||
polarFinish: 30 * DEG_TO_RAD,
|
||||
emitAcceleration: {x: 0, y: -9.8, z: 0},
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
color: color,
|
||||
|
|
47
examples/toys/doll/createDoll.js
Normal file
47
examples/toys/doll/createDoll.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
// createDoll.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
// Created by James B. Pollack @imgntn 9/23/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Creates a doll entity in front of you.
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
|
||||
function createDoll() {
|
||||
var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx";
|
||||
|
||||
var scriptURL = Script.resolvePath("doll.js");
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var naturalDimensions = { x: 1.63, y: 1.67, z: 0.26};
|
||||
|
||||
var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15);
|
||||
|
||||
var doll = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "doll",
|
||||
modelURL: modelURL,
|
||||
script: scriptURL,
|
||||
position: center,
|
||||
shapeType: 'box',
|
||||
dimensions: desiredDimensions,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
collisionsWillMove: true
|
||||
});
|
||||
return doll;
|
||||
}
|
||||
|
||||
createDoll();
|
92
examples/toys/doll/doll.js
Normal file
92
examples/toys/doll/doll.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
// doll.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
// Created by Eric Levin on 9/21/15.
|
||||
// Additions by James B. Pollack @imgntn on 9/24/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This entity script plays an animation and a sound while you hold it.
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Known issues: If you pass the doll between hands, animation can get into a weird state. We want to prevent the animation from starting again when you switch hands, but when you switch mid-animation your hand at release is still the first hand and not the current hand.
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
|
||||
(function() {
|
||||
Script.include("../../utilities.js");
|
||||
Script.include("../../libraries/utils.js");
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here
|
||||
var Doll = function() {
|
||||
this.screamSounds = [SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav")];
|
||||
};
|
||||
|
||||
Doll.prototype = {
|
||||
startAnimationSetting: JSON.stringify({
|
||||
running: true,
|
||||
fps: 30,
|
||||
startFrame: 0,
|
||||
lastFrame: 128,
|
||||
startAutomatically: true
|
||||
}),
|
||||
stopAnimationSetting: JSON.stringify({running: false}),
|
||||
audioInjector: null,
|
||||
isGrabbed: false,
|
||||
setLeftHand: function() {
|
||||
this.hand = 'left';
|
||||
},
|
||||
|
||||
setRightHand: function() {
|
||||
this.hand = 'right';
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
if (this.isGrabbed === false) {
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
animationURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx",
|
||||
animationSettings: this.startAnimationSetting
|
||||
});
|
||||
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], {
|
||||
position: position,
|
||||
volume: 0.1
|
||||
});
|
||||
|
||||
this.isGrabbed = true;
|
||||
this.initialHand = this.hand;
|
||||
}
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
var props = Entities.getEntityProperties(this.entityID, "position");
|
||||
var audioOptions = {
|
||||
position: props.position
|
||||
};
|
||||
this.audioInjector.options = audioOptions;
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
if (this.isGrabbed === true && this.hand === this.initialHand) {
|
||||
|
||||
this.audioInjector.stop();
|
||||
|
||||
Entities.editEntity(this.entityID, {
|
||||
animationSettings: this.stopAnimationSetting,
|
||||
animationURL: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx",
|
||||
});
|
||||
this.isGrabbed = false;
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
this.entityID = entityID;
|
||||
},
|
||||
};
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Doll();
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// createFlashligh.js
|
||||
// createFlashlight.js
|
||||
// examples/entityScripts
|
||||
//
|
||||
// Created by Sam Gateau on 9/9/15.
|
||||
|
@ -11,7 +11,7 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js");
|
||||
|
||||
|
||||
|
@ -19,31 +19,14 @@ var scriptURL = Script.resolvePath('flashlight.js?123123');
|
|||
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var flashlight = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.30,
|
||||
z: 0.08
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
dimensions: { x: 0.08, y: 0.30, z: 0.08},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
});
|
||||
|
||||
|
||||
function cleanup() {
|
||||
//commenting out the line below makes this persistent. to delete at cleanup, uncomment
|
||||
//Entities.deleteEntity(flashlight);
|
||||
}
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -14,39 +14,28 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||
(function() {
|
||||
|
||||
Script.include("../../libraries/utils.js");
|
||||
|
||||
var _this;
|
||||
var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav';
|
||||
var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav';
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||
Flashlight = function() {
|
||||
_this = this;
|
||||
};
|
||||
function Flashlight() {
|
||||
return;
|
||||
}
|
||||
|
||||
//if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will
|
||||
var DISABLE_LIGHT_THRESHOLD = 0.7;
|
||||
|
||||
// These constants define the Spotlight position and orientation relative to the model
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.3,
|
||||
z: 0
|
||||
};
|
||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
});
|
||||
var MODEL_LIGHT_POSITION = { x: 0, y: -0.3, z: 0 };
|
||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { x: 1, y: 0, z: 0 });
|
||||
|
||||
var GLOW_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: 0
|
||||
}
|
||||
var GLOW_LIGHT_POSITION = { x: 0, y: -0.1, z: 0};
|
||||
|
||||
// Evaluate the world light entity positions and orientations from the model ones
|
||||
function evalLightWorldTransform(modelPos, modelRot) {
|
||||
|
@ -55,15 +44,14 @@
|
|||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function glowLightWorldTransform(modelPos, modelRot) {
|
||||
return {
|
||||
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, GLOW_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Flashlight.prototype = {
|
||||
lightOn: false,
|
||||
|
@ -74,11 +62,13 @@
|
|||
setRightHand: function() {
|
||||
this.hand = 'RIGHT';
|
||||
},
|
||||
|
||||
setLeftHand: function() {
|
||||
this.hand = 'LEFT';
|
||||
},
|
||||
|
||||
startNearGrab: function() {
|
||||
if (!_this.hasSpotlight) {
|
||||
if (!this.hasSpotlight) {
|
||||
|
||||
//this light casts the beam
|
||||
this.spotlight = Entities.addEntity({
|
||||
|
@ -117,29 +107,16 @@
|
|||
cutoff: 90, // in degrees
|
||||
});
|
||||
|
||||
this.debugBox = Entities.addEntity({
|
||||
type: 'Box',
|
||||
color: {
|
||||
red: 255,
|
||||
blue: 0,
|
||||
green: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.25,
|
||||
y: 0.25,
|
||||
z: 0.25
|
||||
},
|
||||
ignoreForCollisions:true
|
||||
})
|
||||
|
||||
this.hasSpotlight = true;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
setWhichHand: function() {
|
||||
this.whichHand = this.hand;
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
if (this.whichHand === null) {
|
||||
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||
|
@ -149,6 +126,7 @@
|
|||
this.changeLightWithTriggerPressure(this.whichHand);
|
||||
}
|
||||
},
|
||||
|
||||
releaseGrab: function() {
|
||||
//delete the lights and reset state
|
||||
if (this.hasSpotlight) {
|
||||
|
@ -158,9 +136,10 @@
|
|||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
|
||||
this.lightOn = false;
|
||||
}
|
||||
},
|
||||
|
||||
updateLightPositions: function() {
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
|
||||
|
@ -168,27 +147,20 @@
|
|||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
|
||||
//move them with the entity model
|
||||
Entities.editEntity(this.spotlight, {
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
})
|
||||
|
||||
// Entities.editEntity(this.debugBox, {
|
||||
// position: lightTransform.p,
|
||||
// rotation: lightTransform.q,
|
||||
// })
|
||||
});
|
||||
|
||||
},
|
||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||
|
||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||
var handClickString = flashLightHand + "_HAND_CLICK";
|
||||
|
||||
var handClick = Controller.findAction(handClickString);
|
||||
|
@ -200,37 +172,62 @@
|
|||
} else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) {
|
||||
this.turnLightOn();
|
||||
}
|
||||
return
|
||||
return;
|
||||
},
|
||||
|
||||
turnLightOff: function() {
|
||||
this.playSoundAtCurrentPosition(false);
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 0
|
||||
});
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 0
|
||||
});
|
||||
this.lightOn = false
|
||||
this.lightOn = false;
|
||||
},
|
||||
|
||||
turnLightOn: function() {
|
||||
this.playSoundAtCurrentPosition(true);
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 2
|
||||
});
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 2
|
||||
});
|
||||
this.lightOn = true
|
||||
this.lightOn = true;
|
||||
},
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
|
||||
playSoundAtCurrentPosition: function(playOnSound) {
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
|
||||
var audioProperties = {
|
||||
volume: 0.25,
|
||||
position: position
|
||||
};
|
||||
|
||||
if (playOnSound) {
|
||||
Audio.playSound(this.ON_SOUND, audioProperties);
|
||||
} else {
|
||||
Audio.playSound(this.OFF_SOUND, audioProperties);
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
// * preloading sounds
|
||||
this.entityID = entityID;
|
||||
this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL);
|
||||
this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL);
|
||||
|
||||
},
|
||||
|
||||
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||
// or because we've left the domain or quit the application.
|
||||
unload: function(entityID) {
|
||||
|
||||
unload: function() {
|
||||
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||
// or because we've left the domain or quit the application.
|
||||
if (this.hasSpotlight) {
|
||||
Entities.deleteEntity(this.spotlight);
|
||||
Entities.deleteEntity(this.glowLight);
|
||||
|
@ -238,12 +235,13 @@
|
|||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
this.lightOn = false;
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Flashlight();
|
||||
})
|
||||
});
|
|
@ -45,6 +45,8 @@ var explodeAnimationSettings = JSON.stringify({
|
|||
var GRAVITY = -9.8;
|
||||
var TIME_TO_EXPLODE = 2500;
|
||||
var DISTANCE_IN_FRONT_OF_ME = 1.0;
|
||||
var PI = 3.141593;
|
||||
var DEG_TO_RAD = PI / 180.0;
|
||||
|
||||
function makeGrenade() {
|
||||
var position = Vec3.sum(MyAvatar.position,
|
||||
|
@ -87,8 +89,7 @@ function update() {
|
|||
position: newProperties.position,
|
||||
textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png',
|
||||
emitRate: 100,
|
||||
emitStrength: 2.0,
|
||||
emitDirection: { x: 0.0, y: 1.0, z: 0.0 },
|
||||
polarFinish: 25 * DEG_TO_RAD,
|
||||
color: { red: 200, green: 0, blue: 0 },
|
||||
lifespan: 10.0,
|
||||
visible: true,
|
||||
|
@ -145,8 +146,7 @@ function boom() {
|
|||
position: properties.position,
|
||||
textures: 'https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png',
|
||||
emitRate: 200,
|
||||
emitStrength: 3.0,
|
||||
emitDirection: { x: 0.0, y: 1.0, z: 0.0 },
|
||||
polarFinish: 25 * DEG_TO_RAD,
|
||||
color: { red: 255, green: 255, blue: 0 },
|
||||
lifespan: 2.0,
|
||||
visible: true,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
var createdRenderMenu = false;
|
||||
var createdGeneratedAudioMenu = false;
|
||||
var createdAudioListenerModeMenu = false;
|
||||
var createdStereoInputMenuItem = false;
|
||||
|
||||
var DEVELOPER_MENU = "Developer";
|
||||
|
@ -29,6 +30,20 @@ var AUDIO_SOURCE_INJECT = "Generated Audio";
|
|||
var AUDIO_SOURCE_MENU = AUDIO_MENU + " > Generated Audio Source";
|
||||
var AUDIO_SOURCE_PINK_NOISE = "Pink Noise";
|
||||
var AUDIO_SOURCE_SINE_440 = "Sine 440hz";
|
||||
var AUDIO_LISTENER_MODE_MENU = AUDIO_MENU + " > Audio Listener Mode"
|
||||
var AUDIO_LISTENER_MODE_FROM_HEAD = "Audio from head";
|
||||
var AUDIO_LISTENER_MODE_FROM_CAMERA = "Audio from camera";
|
||||
var AUDIO_LISTENER_MODE_CUSTOM = "Audio from custom position";
|
||||
|
||||
// be sure that the audio listener options are in the right order (same as the enumerator)
|
||||
var AUDIO_LISTENER_OPTIONS = [
|
||||
// MyAvatar.FROM_HEAD (0)
|
||||
AUDIO_LISTENER_MODE_FROM_HEAD,
|
||||
// MyAvatar.FROM_CAMERA (1)
|
||||
AUDIO_LISTENER_MODE_FROM_CAMERA,
|
||||
// MyAvatar.CUSTOM (2)
|
||||
AUDIO_LISTENER_MODE_CUSTOM
|
||||
];
|
||||
var AUDIO_STEREO_INPUT = "Stereo Input";
|
||||
|
||||
|
||||
|
@ -67,7 +82,6 @@ function setupMenus() {
|
|||
Menu.addMenuItem({ menuName: RENDER_MENU, menuItemName: AVATARS_ITEM, isCheckable: true, isChecked: Scene.shouldRenderAvatars })
|
||||
}
|
||||
|
||||
|
||||
if (!Menu.menuExists(AUDIO_MENU)) {
|
||||
Menu.addMenu(AUDIO_MENU);
|
||||
}
|
||||
|
@ -80,6 +94,15 @@ function setupMenus() {
|
|||
Audio.selectPinkNoise();
|
||||
createdGeneratedAudioMenu = true;
|
||||
}
|
||||
|
||||
if (!Menu.menuExists(AUDIO_LISTENER_MODE_MENU)) {
|
||||
Menu.addMenu(AUDIO_LISTENER_MODE_MENU);
|
||||
for (var i = 0; i < AUDIO_LISTENER_OPTIONS.length; i++) {
|
||||
Menu.addMenuItem({ menuName: AUDIO_LISTENER_MODE_MENU, menuItemName: AUDIO_LISTENER_OPTIONS[i], isCheckable: true, isChecked: (MyAvatar.audioListenerMode === i) });
|
||||
}
|
||||
createdAudioListenerModeMenu = true;
|
||||
}
|
||||
|
||||
if (!Menu.menuItemExists(AUDIO_MENU, AUDIO_STEREO_INPUT)) {
|
||||
Menu.addMenuItem({ menuName: AUDIO_MENU, menuItemName: AUDIO_STEREO_INPUT, isCheckable: true, isChecked: false });
|
||||
createdStereoInputMenuItem = true;
|
||||
|
@ -99,15 +122,23 @@ Menu.menuItemEvent.connect(function (menuItem) {
|
|||
Scene.shouldRenderAvatars = Menu.isOptionChecked(AVATARS_ITEM);
|
||||
} else if (menuItem == AUDIO_SOURCE_INJECT && !createdGeneratedAudioMenu) {
|
||||
Audio.injectGeneratedNoise(Menu.isOptionChecked(AUDIO_SOURCE_INJECT));
|
||||
} else if (menuItem == AUDIO_SOURCE_PINK_NOISE && !createdGeneratedAudioMenu) {
|
||||
Audio.selectPinkNoise();
|
||||
Menu.setIsOptionChecked(AUDIO_SOURCE_SINE_440, false);
|
||||
} else if (menuItem == AUDIO_SOURCE_SINE_440 && !createdGeneratedAudioMenu) {
|
||||
Audio.selectSine440();
|
||||
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, false);
|
||||
} else if (menuItem == AUDIO_STEREO_INPUT) {
|
||||
Audio.setStereoInput(Menu.isOptionChecked(AUDIO_STEREO_INPUT))
|
||||
}
|
||||
} else if (menuItem == AUDIO_SOURCE_PINK_NOISE && !createdGeneratedAudioMenu) {
|
||||
Audio.selectPinkNoise();
|
||||
Menu.setIsOptionChecked(AUDIO_SOURCE_SINE_440, false);
|
||||
} else if (menuItem == AUDIO_SOURCE_SINE_440 && !createdGeneratedAudioMenu) {
|
||||
Audio.selectSine440();
|
||||
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, false);
|
||||
} else if (menuItem == AUDIO_STEREO_INPUT) {
|
||||
Audio.setStereoInput(Menu.isOptionChecked(AUDIO_STEREO_INPUT))
|
||||
} else if (AUDIO_LISTENER_OPTIONS.indexOf(menuItem) !== -1) {
|
||||
MyAvatar.audioListenerMode = AUDIO_LISTENER_OPTIONS.indexOf(menuItem);
|
||||
}
|
||||
});
|
||||
|
||||
MyAvatar.audioListenerModeChanged.connect(function() {
|
||||
for (var i = 0; i < AUDIO_LISTENER_OPTIONS.length; i++) {
|
||||
Menu.setIsOptionChecked(AUDIO_LISTENER_OPTIONS[i], (MyAvatar.audioListenerMode === i));
|
||||
}
|
||||
});
|
||||
|
||||
Scene.shouldRenderAvatarsChanged.connect(function(shouldRenderAvatars) {
|
||||
|
@ -134,6 +165,10 @@ function scriptEnding() {
|
|||
Menu.removeMenu(AUDIO_SOURCE_MENU);
|
||||
}
|
||||
|
||||
if (createdAudioListenerModeMenu) {
|
||||
Menu.removeMenu(AUDIO_LISTENER_MODE_MENU);
|
||||
}
|
||||
|
||||
if (createdStereoInputMenuItem) {
|
||||
Menu.removeMenuItem(AUDIO_MENU, AUDIO_STEREO_INPUT);
|
||||
}
|
||||
|
|
|
@ -23,10 +23,16 @@
|
|||
"positionVar": "leftHandPosition",
|
||||
"rotationVar": "leftHandRotation"
|
||||
},
|
||||
{
|
||||
"jointName": "Neck",
|
||||
"positionVar": "neckPosition",
|
||||
"rotationVar": "neckRotation"
|
||||
},
|
||||
{
|
||||
"jointName": "Head",
|
||||
"positionVar": "headPosition",
|
||||
"rotationVar": "headRotation"
|
||||
"rotationVar": "headRotation",
|
||||
"typeVar": "headType"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -554,7 +560,7 @@
|
|||
"id": "strafeLeft",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_left.fbx",
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 31.0,
|
||||
"timeScale": 1.0,
|
||||
|
@ -566,7 +572,7 @@
|
|||
"id": "strafeRight",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_right.fbx",
|
||||
"url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 31.0,
|
||||
"timeScale": 1.0,
|
||||
|
|
|
@ -708,7 +708,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
// Now that menu is initalized we can sync myAvatar with it's state.
|
||||
_myAvatar->updateMotionBehaviorFromMenu();
|
||||
_myAvatar->updateStandingHMDModeFromMenu();
|
||||
|
||||
// the 3Dconnexion device wants to be initiliazed after a window is displayed.
|
||||
ConnexionClient::getInstance().init();
|
||||
|
@ -794,6 +793,8 @@ void Application::cleanupBeforeQuit() {
|
|||
DependencyManager::get<EyeTracker>()->setEnabled(false, true);
|
||||
#endif
|
||||
|
||||
AnimDebugDraw::getInstance().shutdown();
|
||||
|
||||
if (_keyboardFocusHighlightID > 0) {
|
||||
getOverlays().deleteOverlay(_keyboardFocusHighlightID);
|
||||
_keyboardFocusHighlightID = -1;
|
||||
|
@ -1033,15 +1034,10 @@ void Application::initializeUi() {
|
|||
updateInputModes();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void doInBatch(RenderArgs* args, F f) {
|
||||
gpu::Batch batch;
|
||||
f(batch);
|
||||
args->_context->render(batch);
|
||||
}
|
||||
|
||||
void Application::paintGL() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
if (nullptr == _displayPlugin) {
|
||||
return;
|
||||
}
|
||||
|
@ -1058,6 +1054,7 @@ void Application::paintGL() {
|
|||
auto displayPlugin = getActiveDisplayPlugin();
|
||||
displayPlugin->preRender();
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
// update the avatar with a fresh HMD pose
|
||||
_myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose());
|
||||
|
||||
|
@ -1068,18 +1065,19 @@ void Application::paintGL() {
|
|||
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
|
||||
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
|
||||
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
|
||||
PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
||||
resizeGL();
|
||||
|
||||
// Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened
|
||||
renderArgs._context->syncCache();
|
||||
{
|
||||
PerformanceTimer perfTimer("syncCache");
|
||||
renderArgs._context->syncCache();
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
PerformanceTimer perfTimer("Mirror");
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
||||
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
|
@ -1093,12 +1091,12 @@ void Application::paintGL() {
|
|||
auto mirrorRectDest = glm::ivec4(mirrorRect.z, mirrorRect.y, mirrorRect.x, mirrorRect.w);
|
||||
|
||||
auto selfieFbo = DependencyManager::get<FramebufferCache>()->getSelfieFramebuffer();
|
||||
gpu::Batch batch;
|
||||
batch.setFramebuffer(selfieFbo);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest);
|
||||
batch.setFramebuffer(nullptr);
|
||||
renderArgs._context->render(batch);
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
batch.setFramebuffer(selfieFbo);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest);
|
||||
batch.setFramebuffer(nullptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1111,81 +1109,86 @@ void Application::paintGL() {
|
|||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
}
|
||||
|
||||
_myAvatar->startCapture();
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(_myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
|
||||
Application::getInstance()->cameraMenuChanged();
|
||||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("CameraUpdates");
|
||||
|
||||
// The render mode is default or mirror if the camera is in mirror mode, assigned further below
|
||||
renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
||||
|
||||
// Always use the default eye position, not the actual head eye position.
|
||||
// Using the latter will cause the camera to wobble with idle animations,
|
||||
// or with changes from the face tracker
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
if (isHMDMode()) {
|
||||
mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix();
|
||||
_myCamera.setPosition(extractTranslation(camMat));
|
||||
_myCamera.setRotation(glm::quat_cast(camMat));
|
||||
} else {
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition());
|
||||
_myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation());
|
||||
_myAvatar->startCapture();
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(_myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN));
|
||||
Application::getInstance()->cameraMenuChanged();
|
||||
}
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
if (isHMDMode()) {
|
||||
glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * hmdRotation);
|
||||
// Ignore MenuOption::CenterPlayerInView in HMD view
|
||||
glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ _myAvatar->getOrientation()
|
||||
* (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset));
|
||||
} else {
|
||||
_myCamera.setRotation(_myAvatar->getHead()->getOrientation());
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ _myCamera.getRotation()
|
||||
* (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
|
||||
|
||||
// The render mode is default or mirror if the camera is in mirror mode, assigned further below
|
||||
renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
||||
|
||||
// Always use the default eye position, not the actual head eye position.
|
||||
// Using the latter will cause the camera to wobble with idle animations,
|
||||
// or with changes from the face tracker
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
if (isHMDMode()) {
|
||||
mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix();
|
||||
_myCamera.setPosition(extractTranslation(camMat));
|
||||
_myCamera.setRotation(glm::quat_cast(camMat));
|
||||
} else {
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition());
|
||||
_myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation());
|
||||
}
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
if (isHMDMode()) {
|
||||
glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * hmdRotation);
|
||||
// Ignore MenuOption::CenterPlayerInView in HMD view
|
||||
glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ _myAvatar->getOrientation()
|
||||
* (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
|
||||
* (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset));
|
||||
} else {
|
||||
_myCamera.setRotation(_myAvatar->getHead()->getOrientation());
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ _myCamera.getRotation()
|
||||
* (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
|
||||
} else {
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ _myAvatar->getOrientation()
|
||||
* (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)));
|
||||
}
|
||||
}
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (isHMDMode()) {
|
||||
glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation);
|
||||
glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)
|
||||
+ (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
|
||||
+ (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset);
|
||||
} else {
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)
|
||||
+ (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||
}
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
}
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
if (isHMDMode()) {
|
||||
glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation);
|
||||
glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)
|
||||
+ (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
|
||||
+ (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset);
|
||||
} else {
|
||||
_myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
_myCamera.setPosition(_myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)
|
||||
+ (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||
// Update camera position
|
||||
if (!isHMDMode()) {
|
||||
_myCamera.update(1.0f / _fps);
|
||||
}
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
_myAvatar->endCapture();
|
||||
}
|
||||
// Update camera position
|
||||
if (!isHMDMode()) {
|
||||
_myCamera.update(1.0f / _fps);
|
||||
}
|
||||
_myAvatar->endCapture();
|
||||
|
||||
// Primary rendering pass
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
const QSize size = framebufferCache->getFrameBufferSize();
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
||||
PerformanceTimer perfTimer("mainRender");
|
||||
// Viewport is assigned to the size of the framebuffer
|
||||
renderArgs._viewport = ivec4(0, 0, size.width(), size.height());
|
||||
if (displayPlugin->isStereo()) {
|
||||
|
@ -1222,7 +1225,7 @@ void Application::paintGL() {
|
|||
}
|
||||
displaySide(&renderArgs, _myCamera);
|
||||
renderArgs._context->enableStereo(false);
|
||||
doInBatch(&renderArgs, [](gpu::Batch& batch) {
|
||||
gpu::doInBatch(renderArgs._context, [](gpu::Batch& batch) {
|
||||
batch.setFramebuffer(nullptr);
|
||||
});
|
||||
}
|
||||
|
@ -1230,6 +1233,7 @@ void Application::paintGL() {
|
|||
// Overlay Composition, needs to occur after screen space effects have completed
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/compositor");
|
||||
PerformanceTimer perfTimer("compositor");
|
||||
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo));
|
||||
if (displayPlugin->isStereo()) {
|
||||
|
@ -1254,6 +1258,7 @@ void Application::paintGL() {
|
|||
// deliver final composited scene to the display plugin
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
|
||||
GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0));
|
||||
// Ensure the rendering context commands are completed when rendering
|
||||
|
@ -1271,24 +1276,29 @@ void Application::paintGL() {
|
|||
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
|
||||
PerformanceTimer perfTimer("pluginDisplay");
|
||||
displayPlugin->display(finalTexture, toGlm(size));
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/bufferSwap");
|
||||
PerformanceTimer perfTimer("bufferSwap");
|
||||
displayPlugin->finishFrame();
|
||||
}
|
||||
}
|
||||
|
||||
_offscreenContext->makeCurrent();
|
||||
_frameCount++;
|
||||
Stats::getInstance()->setRenderDetails(renderArgs._details);
|
||||
{
|
||||
PerformanceTimer perfTimer("makeCurrent");
|
||||
_offscreenContext->makeCurrent();
|
||||
_frameCount++;
|
||||
Stats::getInstance()->setRenderDetails(renderArgs._details);
|
||||
|
||||
// Reset the gpu::Context Stages
|
||||
// Back to the default framebuffer;
|
||||
gpu::Batch batch;
|
||||
batch.resetStages();
|
||||
renderArgs._context->render(batch);
|
||||
// Reset the gpu::Context Stages
|
||||
// Back to the default framebuffer;
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
batch.resetStages();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Application::runTests() {
|
||||
|
@ -2330,7 +2340,8 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
|
|||
QVector<EntityItemPointer> entities;
|
||||
|
||||
auto entityTree = _entities.getTree();
|
||||
EntityTree exportTree;
|
||||
auto exportTree = std::make_shared<EntityTree>();
|
||||
exportTree->createRootElement();
|
||||
|
||||
glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||
for (auto entityID : entityIDs) {
|
||||
|
@ -2357,10 +2368,10 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
|
|||
auto properties = entityItem->getProperties();
|
||||
|
||||
properties.setPosition(properties.getPosition() - root);
|
||||
exportTree.addEntity(entityItem->getEntityItemID(), properties);
|
||||
exportTree->addEntity(entityItem->getEntityItemID(), properties);
|
||||
}
|
||||
|
||||
exportTree.writeToJSONFile(filename.toLocal8Bit().constData());
|
||||
exportTree->writeToJSONFile(filename.toLocal8Bit().constData());
|
||||
|
||||
// restore the main window's active state
|
||||
_window->activateWindow();
|
||||
|
@ -2373,15 +2384,16 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa
|
|||
|
||||
if (entities.size() > 0) {
|
||||
glm::vec3 root(x, y, z);
|
||||
EntityTree exportTree;
|
||||
auto exportTree = std::make_shared<EntityTree>();
|
||||
exportTree->createRootElement();
|
||||
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
EntityItemProperties properties = entities.at(i)->getProperties();
|
||||
EntityItemID id = entities.at(i)->getEntityItemID();
|
||||
properties.setPosition(properties.getPosition() - root);
|
||||
exportTree.addEntity(id, properties);
|
||||
exportTree->addEntity(id, properties);
|
||||
}
|
||||
exportTree.writeToSVOFile(filename.toLocal8Bit().constData());
|
||||
exportTree->writeToSVOFile(filename.toLocal8Bit().constData());
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "No models were selected";
|
||||
return false;
|
||||
|
@ -4029,6 +4041,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
// hook our avatar and avatar hash map object into this script engine
|
||||
scriptEngine->registerGlobalObject("MyAvatar", _myAvatar);
|
||||
qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Camera", &_myCamera);
|
||||
|
@ -4063,6 +4077,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
|
|
|
@ -142,8 +142,8 @@ public:
|
|||
static Application* getInstance() { return qApp; } // TODO: replace fully by qApp
|
||||
static const glm::vec3& getPositionForPath() { return getInstance()->_myAvatar->getPosition(); }
|
||||
static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); }
|
||||
static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); }
|
||||
static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); }
|
||||
static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getPositionForAudio(); }
|
||||
static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getOrientationForAudio(); }
|
||||
static void initPlugins();
|
||||
static void shutdownPlugins();
|
||||
|
||||
|
|
|
@ -294,9 +294,6 @@ Menu::Menu() {
|
|||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::StandingHMDSensorMode, 0, false,
|
||||
avatar, SLOT(updateStandingHMDModeFromMenu()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::WorldAxes);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
|
||||
|
|
|
@ -124,14 +124,12 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
|||
_isEyeTrackerConnected = eyeTracker->isTracking();
|
||||
}
|
||||
|
||||
if (!myAvatar->getStandingHMDSensorMode()) {
|
||||
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
|
||||
// since everyone else will see the full joint rotations for other people.
|
||||
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
|
||||
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
|
||||
float currentTwist = getTorsoTwist();
|
||||
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
|
||||
}
|
||||
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
|
||||
// since everyone else will see the full joint rotations for other people.
|
||||
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
|
||||
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
|
||||
float currentTwist = getTorsoTwist();
|
||||
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
|
||||
}
|
||||
|
||||
if (!(_isFaceTrackerConnected || billboard)) {
|
||||
|
@ -392,7 +390,7 @@ glm::quat Head::getCameraOrientation() const {
|
|||
// always the same.
|
||||
if (qApp->getAvatarUpdater()->isHMDMode()) {
|
||||
MyAvatar* myAvatar = dynamic_cast<MyAvatar*>(_owningAvatar);
|
||||
if (myAvatar && myAvatar->getStandingHMDSensorMode()) {
|
||||
if (myAvatar) {
|
||||
return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation();
|
||||
} else {
|
||||
return getOrientation();
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "Recorder.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "DebugDraw.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -103,12 +104,12 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
|||
_hmdSensorPosition(),
|
||||
_bodySensorMatrix(),
|
||||
_sensorToWorldMatrix(),
|
||||
_standingHMDSensorMode(false),
|
||||
_goToPending(false),
|
||||
_goToPosition(),
|
||||
_goToOrientation(),
|
||||
_rig(rig),
|
||||
_prevShouldDrawHead(true)
|
||||
_prevShouldDrawHead(true),
|
||||
_audioListenerMode(FROM_HEAD)
|
||||
{
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
|
@ -149,6 +150,25 @@ void MyAvatar::reset() {
|
|||
eulers.x = 0.0f;
|
||||
eulers.z = 0.0f;
|
||||
setOrientation(glm::quat(eulers));
|
||||
|
||||
// This should be simpler when we have only graph animations always on.
|
||||
bool isRig = _rig->getEnableRig();
|
||||
bool isGraph = _rig->getEnableAnimGraph();
|
||||
qApp->setRawAvatarUpdateThreading(false);
|
||||
_rig->disableHands = true;
|
||||
setEnableRigAnimations(true);
|
||||
_skeletonModel.simulate(0.1f); // non-zero
|
||||
setEnableRigAnimations(false);
|
||||
_skeletonModel.simulate(0.1f);
|
||||
if (isRig) {
|
||||
setEnableRigAnimations(true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, true);
|
||||
} else if (isGraph) {
|
||||
setEnableAnimGraph(true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, true);
|
||||
}
|
||||
_rig->disableHands = false;
|
||||
qApp->setRawAvatarUpdateThreading();
|
||||
}
|
||||
|
||||
void MyAvatar::update(float deltaTime) {
|
||||
|
@ -247,29 +267,68 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
}
|
||||
|
||||
glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
|
||||
if (getStandingHMDSensorMode()) {
|
||||
return _sensorToWorldMatrix;
|
||||
} else {
|
||||
return createMatFromQuatAndPos(getWorldAlignedOrientation(), getDefaultEyePosition());
|
||||
}
|
||||
return _sensorToWorldMatrix;
|
||||
}
|
||||
|
||||
// best called at start of main loop just after we have a fresh hmd pose.
|
||||
// update internal body position from new hmd pose.
|
||||
// Pass a recent sample of the HMD to the avatar.
|
||||
// This can also update the avatar's position to follow the HMD
|
||||
// as it moves through the world.
|
||||
void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
auto deltaUsecs = now - _lastUpdateFromHMDTime;
|
||||
_lastUpdateFromHMDTime = now;
|
||||
double actualDeltaTime = (double)deltaUsecs / (double)USECS_PER_SECOND;
|
||||
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||
float deltaTime = glm::clamp((float)actualDeltaTime, 0.0f, BIGGEST_DELTA_TIME_SECS);
|
||||
|
||||
// update the sensorMatrices based on the new hmd pose
|
||||
_hmdSensorMatrix = hmdSensorMatrix;
|
||||
_hmdSensorPosition = extractTranslation(hmdSensorMatrix);
|
||||
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
|
||||
_bodySensorMatrix = deriveBodyFromHMDSensor();
|
||||
|
||||
if (getStandingHMDSensorMode()) {
|
||||
// set the body position/orientation to reflect motion due to the head.
|
||||
auto worldMat = _sensorToWorldMatrix * _bodySensorMatrix;
|
||||
nextAttitude(extractTranslation(worldMat), glm::quat_cast(worldMat));
|
||||
const float STRAIGHTING_LEAN_DURATION = 0.5f; // seconds
|
||||
const float STRAIGHTING_LEAN_THRESHOLD = 0.2f; // meters
|
||||
|
||||
auto newBodySensorMatrix = deriveBodyFromHMDSensor();
|
||||
glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix);
|
||||
if (!_straightingLean && glm::length(diff) > STRAIGHTING_LEAN_THRESHOLD) {
|
||||
|
||||
// begin homing toward derived body position.
|
||||
_straightingLean = true;
|
||||
_straightingLeanAlpha = 0.0f;
|
||||
|
||||
} else if (_straightingLean) {
|
||||
|
||||
auto newBodySensorMatrix = deriveBodyFromHMDSensor();
|
||||
auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix;
|
||||
glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix);
|
||||
glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix));
|
||||
|
||||
_straightingLeanAlpha += (1.0f / STRAIGHTING_LEAN_DURATION) * deltaTime;
|
||||
|
||||
if (_straightingLeanAlpha >= 1.0f) {
|
||||
_straightingLean = false;
|
||||
nextAttitude(worldBodyPos, worldBodyRot);
|
||||
_bodySensorMatrix = newBodySensorMatrix;
|
||||
} else {
|
||||
// interp position toward the desired pos
|
||||
glm::vec3 pos = lerp(getPosition(), worldBodyPos, _straightingLeanAlpha);
|
||||
glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _straightingLeanAlpha));
|
||||
nextAttitude(pos, rot);
|
||||
|
||||
// interp sensor matrix toward desired
|
||||
glm::vec3 nextBodyPos = extractTranslation(newBodySensorMatrix);
|
||||
glm::quat nextBodyRot = glm::normalize(glm::quat_cast(newBodySensorMatrix));
|
||||
glm::vec3 prevBodyPos = extractTranslation(_bodySensorMatrix);
|
||||
glm::quat prevBodyRot = glm::normalize(glm::quat_cast(_bodySensorMatrix));
|
||||
pos = lerp(prevBodyPos, nextBodyPos, _straightingLeanAlpha);
|
||||
rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _straightingLeanAlpha));
|
||||
_bodySensorMatrix = createMatFromQuatAndPos(rot, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// best called at end of main loop, just before rendering.
|
||||
// update sensor to world matrix from current body position and hmd sensor.
|
||||
// This is so the correct camera can be used for rendering.
|
||||
|
@ -338,11 +397,9 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
|||
|
||||
Head* head = getHead();
|
||||
if (inHmd || isPlaying()) {
|
||||
if (!getStandingHMDSensorMode()) {
|
||||
head->setDeltaPitch(estimatedRotation.x);
|
||||
head->setDeltaYaw(estimatedRotation.y);
|
||||
head->setDeltaRoll(estimatedRotation.z);
|
||||
}
|
||||
head->setDeltaPitch(estimatedRotation.x);
|
||||
head->setDeltaYaw(estimatedRotation.y);
|
||||
head->setDeltaRoll(estimatedRotation.z);
|
||||
} else {
|
||||
float magnifyFieldOfView = qApp->getFieldOfView() /
|
||||
_realWorldFieldOfView.get();
|
||||
|
@ -364,12 +421,10 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
|
|||
relativePosition.x = -relativePosition.x;
|
||||
}
|
||||
|
||||
if (!(inHmd && getStandingHMDSensorMode())) {
|
||||
head->setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)),
|
||||
-MAX_LEAN, MAX_LEAN));
|
||||
head->setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)),
|
||||
-MAX_LEAN, MAX_LEAN));
|
||||
}
|
||||
head->setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)),
|
||||
-MAX_LEAN, MAX_LEAN));
|
||||
head->setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)),
|
||||
-MAX_LEAN, MAX_LEAN));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1333,7 +1388,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
|||
|
||||
// bones are aligned such that z is forward, not -z.
|
||||
glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
AnimPose xform(glm::vec3(1), rotY180 * getOrientation(), getPosition());
|
||||
AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition());
|
||||
|
||||
if (_enableDebugDrawBindPose && _debugDrawSkeleton) {
|
||||
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
|
||||
|
@ -1358,6 +1413,9 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
|
|||
}
|
||||
}
|
||||
|
||||
DebugDraw::getInstance().updateMyAvatarPos(getPosition());
|
||||
DebugDraw::getInstance().updateMyAvatarRot(getOrientation());
|
||||
|
||||
if (shouldDrawHead != _prevShouldDrawHead) {
|
||||
_skeletonModel.setCauterizeBones(!shouldDrawHead);
|
||||
}
|
||||
|
@ -1720,11 +1778,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() {
|
|||
_characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
|
||||
}
|
||||
|
||||
void MyAvatar::updateStandingHMDModeFromMenu() {
|
||||
Menu* menu = Menu::getInstance();
|
||||
_standingHMDSensorMode = menu->isOptionChecked(MenuOption::StandingHMDSensorMode);
|
||||
}
|
||||
|
||||
//Renders sixense laser pointers for UI selection with controllers
|
||||
void MyAvatar::renderLaserPointers(gpu::Batch& batch) {
|
||||
const float PALM_TIP_ROD_RADIUS = 0.002f;
|
||||
|
@ -1810,3 +1863,42 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
|||
// avatar facing is determined solely by hmd orientation.
|
||||
return createMatFromQuatAndPos(hmdOrientationYawOnly, bodyPos);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getPositionForAudio() {
|
||||
switch (_audioListenerMode) {
|
||||
case AudioListenerMode::FROM_HEAD:
|
||||
return getHead()->getPosition();
|
||||
case AudioListenerMode::FROM_CAMERA:
|
||||
return Application::getInstance()->getCamera()->getPosition();
|
||||
case AudioListenerMode::CUSTOM:
|
||||
return _customListenPosition;
|
||||
}
|
||||
return vec3();
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::getOrientationForAudio() {
|
||||
switch (_audioListenerMode) {
|
||||
case AudioListenerMode::FROM_HEAD:
|
||||
return getHead()->getFinalOrientationInWorldFrame();
|
||||
case AudioListenerMode::FROM_CAMERA:
|
||||
return Application::getInstance()->getCamera()->getOrientation();
|
||||
case AudioListenerMode::CUSTOM:
|
||||
return _customListenOrientation;
|
||||
}
|
||||
return quat();
|
||||
}
|
||||
|
||||
void MyAvatar::setAudioListenerMode(AudioListenerMode audioListenerMode) {
|
||||
if (_audioListenerMode != audioListenerMode) {
|
||||
_audioListenerMode = audioListenerMode;
|
||||
emit audioListenerModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode) {
|
||||
return audioListenerMode;
|
||||
}
|
||||
|
||||
void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode) {
|
||||
audioListenerMode = (AudioListenerMode)object.toUInt16();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,14 @@ enum eyeContactTarget {
|
|||
MOUTH
|
||||
};
|
||||
|
||||
enum AudioListenerMode {
|
||||
FROM_HEAD = 0,
|
||||
FROM_CAMERA,
|
||||
CUSTOM
|
||||
};
|
||||
Q_DECLARE_METATYPE(AudioListenerMode);
|
||||
|
||||
|
||||
class MyAvatar : public Avatar {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
|
||||
|
@ -33,12 +41,21 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale)
|
||||
Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame)
|
||||
Q_PROPERTY(QString collisionSoundURL READ getCollisionSoundURL WRITE setCollisionSoundURL)
|
||||
Q_PROPERTY(AudioListenerMode audioListenerMode READ getAudioListenerMode WRITE setAudioListenerMode)
|
||||
Q_PROPERTY(glm::vec3 customListenPosition READ getCustomListenPosition WRITE setCustomListenPosition)
|
||||
Q_PROPERTY(glm::quat customListenOrientation READ getCustomListenOrientation WRITE setCustomListenOrientation)
|
||||
Q_PROPERTY(AudioListenerMode FROM_HEAD READ getAudioListenerModeHead)
|
||||
Q_PROPERTY(AudioListenerMode FROM_CAMERA READ getAudioListenerModeCamera)
|
||||
Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom)
|
||||
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
|
||||
|
||||
public:
|
||||
MyAvatar(RigPointer rig);
|
||||
~MyAvatar();
|
||||
|
||||
AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; }
|
||||
AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; }
|
||||
AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; }
|
||||
|
||||
void reset();
|
||||
void update(float deltaTime);
|
||||
|
@ -49,8 +66,9 @@ public:
|
|||
const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; }
|
||||
glm::mat4 getSensorToWorldMatrix() const;
|
||||
|
||||
// best called at start of main loop just after we have a fresh hmd pose.
|
||||
// update internal body position from new hmd pose.
|
||||
// Pass a recent sample of the HMD to the avatar.
|
||||
// This can also update the avatar's position to follow the HMD
|
||||
// as it moves through the world.
|
||||
void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix);
|
||||
|
||||
// best called at end of main loop, just before rendering.
|
||||
|
@ -151,10 +169,16 @@ public:
|
|||
static const float ZOOM_MAX;
|
||||
static const float ZOOM_DEFAULT;
|
||||
|
||||
bool getStandingHMDSensorMode() const { return _standingHMDSensorMode; }
|
||||
void doUpdateBillboard();
|
||||
void destroyAnimGraph();
|
||||
|
||||
AudioListenerMode getAudioListenerMode() { return _audioListenerMode; }
|
||||
void setAudioListenerMode(AudioListenerMode audioListenerMode);
|
||||
glm::vec3 getCustomListenPosition() { return _customListenPosition; }
|
||||
void setCustomListenPosition(glm::vec3 customListenPosition) { _customListenPosition = customListenPosition; }
|
||||
glm::quat getCustomListenOrientation() { return _customListenOrientation; }
|
||||
void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; }
|
||||
|
||||
public slots:
|
||||
void increaseSize();
|
||||
void decreaseSize();
|
||||
|
@ -170,7 +194,6 @@ public slots:
|
|||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
void updateMotionBehaviorFromMenu();
|
||||
void updateStandingHMDModeFromMenu();
|
||||
|
||||
glm::vec3 getLeftPalmPosition();
|
||||
glm::vec3 getLeftPalmVelocity();
|
||||
|
@ -204,7 +227,11 @@ public slots:
|
|||
void setEnableMeshVisible(bool isEnabled);
|
||||
void setAnimGraphUrl(const QString& url) { _animGraphUrl = url; }
|
||||
|
||||
glm::vec3 getPositionForAudio();
|
||||
glm::quat getOrientationForAudio();
|
||||
|
||||
signals:
|
||||
void audioListenerModeChanged();
|
||||
void transformChanged();
|
||||
void newCollisionSoundURL(const QUrl& url);
|
||||
void collisionWithEntity(const Collision& collision);
|
||||
|
@ -317,8 +344,6 @@ private:
|
|||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||
glm::mat4 _sensorToWorldMatrix;
|
||||
|
||||
bool _standingHMDSensorMode;
|
||||
|
||||
bool _goToPending;
|
||||
glm::vec3 _goToPosition;
|
||||
glm::quat _goToOrientation;
|
||||
|
@ -330,6 +355,18 @@ private:
|
|||
bool _enableDebugDrawBindPose = false;
|
||||
bool _enableDebugDrawAnimPose = false;
|
||||
AnimSkeleton::ConstPointer _debugDrawSkeleton = nullptr;
|
||||
|
||||
AudioListenerMode _audioListenerMode;
|
||||
glm::vec3 _customListenPosition;
|
||||
glm::quat _customListenOrientation;
|
||||
|
||||
bool _straightingLean = false;
|
||||
float _straightingLeanAlpha = 0.0f;
|
||||
|
||||
quint64 _lastUpdateFromHMDTime = usecTimestampNow();
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode);
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "SkeletonModel.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "AnimDebugDraw.h"
|
||||
|
||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
|
||||
Model(rig, parent),
|
||||
|
@ -122,23 +123,39 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
Rig::HeadParameters headParams;
|
||||
headParams.modelRotation = getRotation();
|
||||
headParams.modelTranslation = getTranslation();
|
||||
headParams.enableLean = qApp->getAvatarUpdater()->isHMDMode() && !myAvatar->getStandingHMDSensorMode();
|
||||
headParams.enableLean = qApp->getAvatarUpdater()->isHMDMode();
|
||||
headParams.leanSideways = head->getFinalLeanSideways();
|
||||
headParams.leanForward = head->getFinalLeanForward();
|
||||
headParams.torsoTwist = head->getTorsoTwist();
|
||||
headParams.localHeadOrientation = head->getFinalOrientationInLocalFrame();
|
||||
headParams.localHeadPitch = head->getFinalPitch();
|
||||
headParams.localHeadYaw = head->getFinalYaw();
|
||||
headParams.localHeadRoll = head->getFinalRoll();
|
||||
headParams.isInHMD = qApp->getAvatarUpdater()->isHMDMode();
|
||||
|
||||
// get HMD position from sensor space into world space, and back into model space
|
||||
glm::mat4 worldToModel = glm::inverse(createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()));
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 hmdPosition = glm::angleAxis((float)M_PI, yAxis) * transformPoint(worldToModel * myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition());
|
||||
headParams.localHeadPosition = hmdPosition;
|
||||
if (qApp->getAvatarUpdater()->isHMDMode()) {
|
||||
headParams.isInHMD = true;
|
||||
|
||||
// get HMD position from sensor space into world space, and back into model space
|
||||
AnimPose avatarToWorld(glm::vec3(1), myAvatar->getOrientation(), myAvatar->getPosition());
|
||||
glm::mat4 worldToAvatar = glm::inverse((glm::mat4)avatarToWorld);
|
||||
glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
||||
glm::mat4 hmdMat = worldToAvatar * worldHMDMat;
|
||||
|
||||
// in local avatar space (i.e. relative to avatar pos/orientation.)
|
||||
glm::vec3 hmdPosition = extractTranslation(hmdMat);
|
||||
glm::quat hmdOrientation = extractRotation(hmdMat);
|
||||
|
||||
headParams.localHeadPosition = hmdPosition;
|
||||
headParams.localHeadOrientation = hmdOrientation;
|
||||
|
||||
headParams.worldHeadOrientation = extractRotation(worldHMDMat);
|
||||
} else {
|
||||
headParams.isInHMD = false;
|
||||
|
||||
// We don't have a valid localHeadPosition.
|
||||
headParams.localHeadOrientation = head->getFinalOrientationInLocalFrame();
|
||||
headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
|
||||
}
|
||||
|
||||
headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame();
|
||||
headParams.eyeLookAt = head->getLookAtPosition();
|
||||
headParams.eyeSaccade = head->getSaccade();
|
||||
headParams.leanJointIndex = geometry.leanJointIndex;
|
||||
|
@ -229,33 +246,36 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
Hand* hand = _owningAvatar->getHand();
|
||||
hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
|
||||
const float HAND_RESTORATION_RATE = 0.25f;
|
||||
if (leftPalmIndex == -1 && rightPalmIndex == -1) {
|
||||
// palms are not yet set, use mouse
|
||||
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
} else {
|
||||
// transform into model-frame
|
||||
glm::vec3 handPosition = glm::inverse(_rotation) * (_owningAvatar->getHandPosition() - _translation);
|
||||
applyHandPosition(geometry.rightHandJointIndex, handPosition);
|
||||
}
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else if (leftPalmIndex == rightPalmIndex) {
|
||||
// right hand only
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else {
|
||||
if (leftPalmIndex != -1) {
|
||||
applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
} else {
|
||||
// Don't Relax toward hand positions when in animGraph mode.
|
||||
if (!_rig->getEnableAnimGraph()) {
|
||||
const float HAND_RESTORATION_RATE = 0.25f;
|
||||
if (leftPalmIndex == -1 && rightPalmIndex == -1) {
|
||||
// palms are not yet set, use mouse
|
||||
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
} else {
|
||||
// transform into model-frame
|
||||
glm::vec3 handPosition = glm::inverse(_rotation) * (_owningAvatar->getHandPosition() - _translation);
|
||||
applyHandPosition(geometry.rightHandJointIndex, handPosition);
|
||||
}
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
}
|
||||
if (rightPalmIndex != -1) {
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]);
|
||||
|
||||
} else if (leftPalmIndex == rightPalmIndex) {
|
||||
// right hand only
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
if (leftPalmIndex != -1) {
|
||||
applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
} else {
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
}
|
||||
if (rightPalmIndex != -1) {
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]);
|
||||
} else {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,6 +160,8 @@ ConnexionClient& ConnexionClient::getInstance() {
|
|||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
void ConnexionClient::toggleConnexion(bool shouldEnable) {
|
||||
ConnexionData& connexiondata = ConnexionData::getInstance();
|
||||
if (shouldEnable && connexiondata.getDeviceID() == 0) {
|
||||
|
@ -425,18 +427,13 @@ bool ConnexionClient::InitializeRawInput(HWND hwndTarget) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// FIXME - http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
|
||||
// Get OS version.
|
||||
OSVERSIONINFO osvi = { sizeof(OSVERSIONINFO), 0 };
|
||||
::GetVersionEx(&osvi);
|
||||
|
||||
unsigned int cbSize = sizeof(devicesToRegister[0]);
|
||||
for (size_t i = 0; i < numDevices; i++) {
|
||||
// Set the target window to use
|
||||
//devicesToRegister[i].hwndTarget = hwndTarget;
|
||||
|
||||
// If Vista or newer, enable receiving the WM_INPUT_DEVICE_CHANGE message.
|
||||
if (osvi.dwMajorVersion >= 6) {
|
||||
if (IsWindowsVistaOrGreater()) {
|
||||
devicesToRegister[i].dwFlags |= RIDEV_DEVNOTIFY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,17 +19,40 @@ float ClipboardScriptingInterface::getClipboardContentsLargestDimension() {
|
|||
}
|
||||
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
|
||||
return Application::getInstance()->exportEntities(filename, entityIDs);
|
||||
bool retVal;
|
||||
QMetaObject::invokeMethod(Application::getInstance(), "exportEntities", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, retVal),
|
||||
Q_ARG(const QString&, filename),
|
||||
Q_ARG(const QVector<EntityItemID>&, entityIDs));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
|
||||
return Application::getInstance()->exportEntities(filename, x, y, z, s);
|
||||
bool retVal;
|
||||
QMetaObject::invokeMethod(Application::getInstance(), "exportEntities", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, retVal),
|
||||
Q_ARG(const QString&, filename),
|
||||
Q_ARG(float, x),
|
||||
Q_ARG(float, y),
|
||||
Q_ARG(float, z),
|
||||
Q_ARG(float, s));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool ClipboardScriptingInterface::importEntities(const QString& filename) {
|
||||
return Application::getInstance()->importEntities(filename);
|
||||
bool retVal;
|
||||
QMetaObject::invokeMethod(Application::getInstance(), "importEntities", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, retVal),
|
||||
Q_ARG(const QString&, filename));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position) {
|
||||
return Application::getInstance()->pasteEntities(position.x, position.y, position.z);
|
||||
QVector<EntityItemID> retVal;
|
||||
QMetaObject::invokeMethod(Application::getInstance(), "pasteEntities", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QVector<EntityItemID>, retVal),
|
||||
Q_ARG(float, position.x),
|
||||
Q_ARG(float, position.y),
|
||||
Q_ARG(float, position.z));
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -207,36 +207,36 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
|||
updateTooltips();
|
||||
|
||||
//Handle fading and deactivation/activation of UI
|
||||
gpu::Batch batch;
|
||||
gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) {
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
batch.setViewportTransform(renderArgs->_viewport);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.setViewTransform(Transform());
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));
|
||||
geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
batch.setViewportTransform(renderArgs->_viewport);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.setViewTransform(Transform());
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));
|
||||
geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
|
||||
|
||||
// Doesn't actually render
|
||||
renderPointers(batch);
|
||||
// Doesn't actually render
|
||||
renderPointers(batch);
|
||||
|
||||
//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;
|
||||
//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;
|
||||
|
||||
Transform model;
|
||||
model.setTranslation(vec3(mousePosition, 0));
|
||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||
model.setScale(vec3(mouseSize, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
bindCursorTexture(batch);
|
||||
geometryCache->renderUnitQuad(batch, vec4(1));
|
||||
renderArgs->_context->render(batch);
|
||||
Transform model;
|
||||
model.setTranslation(vec3(mousePosition, 0));
|
||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||
model.setScale(vec3(mouseSize, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
bindCursorTexture(batch);
|
||||
geometryCache->renderUnitQuad(batch, vec4(1));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -278,75 +278,67 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
gpu::Batch batch;
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
//batch._glDisable(GL_DEPTH_TEST);
|
||||
//batch._glDisable(GL_CULL_FACE);
|
||||
//batch._glBindTexture(GL_TEXTURE_2D, texture);
|
||||
//batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
//batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) {
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
|
||||
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));
|
||||
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));
|
||||
|
||||
mat4 camMat;
|
||||
_cameraBaseTransform.getMatrix(camMat);
|
||||
camMat = camMat * qApp->getEyePose(eye);
|
||||
batch.setViewportTransform(renderArgs->_viewport);
|
||||
batch.setViewTransform(camMat);
|
||||
mat4 camMat;
|
||||
_cameraBaseTransform.getMatrix(camMat);
|
||||
camMat = camMat * qApp->getEyePose(eye);
|
||||
batch.setViewportTransform(renderArgs->_viewport);
|
||||
batch.setViewTransform(camMat);
|
||||
|
||||
batch.setProjectionTransform(qApp->getEyeProjection(eye));
|
||||
batch.setProjectionTransform(qApp->getEyeProjection(eye));
|
||||
|
||||
#ifdef DEBUG_OVERLAY
|
||||
{
|
||||
batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2)));
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1));
|
||||
}
|
||||
#else
|
||||
{
|
||||
//batch.setModelTransform(overlayXfm);
|
||||
#ifdef DEBUG_OVERLAY
|
||||
{
|
||||
batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2)));
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1));
|
||||
}
|
||||
#else
|
||||
{
|
||||
batch.setModelTransform(_modelTransform);
|
||||
drawSphereSection(batch);
|
||||
}
|
||||
#endif
|
||||
|
||||
batch.setModelTransform(_modelTransform);
|
||||
drawSphereSection(batch);
|
||||
}
|
||||
#endif
|
||||
// Doesn't actually render
|
||||
renderPointers(batch);
|
||||
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
|
||||
|
||||
// Doesn't actually render
|
||||
renderPointers(batch);
|
||||
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
|
||||
bindCursorTexture(batch);
|
||||
|
||||
bindCursorTexture(batch);
|
||||
//Controller Pointers
|
||||
glm::mat4 overlayXfm;
|
||||
_modelTransform.getMatrix(overlayXfm);
|
||||
|
||||
//Controller Pointers
|
||||
glm::mat4 overlayXfm;
|
||||
_modelTransform.getMatrix(overlayXfm);
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
|
||||
PalmData& palm = myAvatar->getHand()->getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
glm::vec2 polar = getPolarCoordinates(palm);
|
||||
// Convert to quaternion
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||
batch.setModelTransform(reticleXfm);
|
||||
// Render reticle at location
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
}
|
||||
}
|
||||
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
|
||||
PalmData& palm = myAvatar->getHand()->getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
glm::vec2 polar = getPolarCoordinates(palm);
|
||||
// Convert to quaternion
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
//Mouse Pointer
|
||||
if (_reticleActive[MOUSE]) {
|
||||
glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
|
||||
_reticlePosition[MOUSE].y()));
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||
batch.setModelTransform(reticleXfm);
|
||||
// Render reticle at location
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
}
|
||||
}
|
||||
|
||||
//Mouse Pointer
|
||||
if (_reticleActive[MOUSE]) {
|
||||
glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(),
|
||||
_reticlePosition[MOUSE].y()));
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||
batch.setModelTransform(reticleXfm);
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
}
|
||||
|
||||
renderArgs->_context->render(batch);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -70,29 +70,28 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
// Execute the batch into our framebuffer
|
||||
gpu::Batch batch;
|
||||
renderArgs->_batch = &batch;
|
||||
doInBatch(renderArgs->_context, [=](gpu::Batch& batch) {
|
||||
renderArgs->_batch = &batch;
|
||||
|
||||
int width = _overlayFramebuffer->getWidth();
|
||||
int height = _overlayFramebuffer->getHeight();
|
||||
int width = _overlayFramebuffer->getWidth();
|
||||
int height = _overlayFramebuffer->getHeight();
|
||||
|
||||
batch.setViewportTransform(glm::ivec4(0, 0, width, height));
|
||||
batch.setFramebuffer(_overlayFramebuffer);
|
||||
batch.setViewportTransform(glm::ivec4(0, 0, width, height));
|
||||
batch.setFramebuffer(_overlayFramebuffer);
|
||||
|
||||
glm::vec4 color { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
float depth = 1.0f;
|
||||
int stencil = 0;
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, color, depth, stencil);
|
||||
glm::vec4 color { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
float depth = 1.0f;
|
||||
int stencil = 0;
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, color, depth, stencil);
|
||||
|
||||
// Now render the overlay components together into a single texture
|
||||
renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line
|
||||
renderAudioScope(renderArgs); // audio scope in the very back
|
||||
renderRearView(renderArgs); // renders the mirror view selfie
|
||||
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
|
||||
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
||||
renderStatsAndLogs(renderArgs); // currently renders nothing
|
||||
|
||||
renderArgs->_context->render(batch);
|
||||
// Now render the overlay components together into a single texture
|
||||
renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line
|
||||
renderAudioScope(renderArgs); // audio scope in the very back
|
||||
renderRearView(renderArgs); // renders the mirror view selfie
|
||||
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
|
||||
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
||||
renderStatsAndLogs(renderArgs); // currently renders nothing
|
||||
});
|
||||
|
||||
renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch
|
||||
|
||||
|
|
|
@ -68,12 +68,7 @@ void OverlayConductor::updateMode() {
|
|||
|
||||
Mode newMode;
|
||||
if (qApp->isHMDMode()) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
if (myAvatar->getStandingHMDSensorMode()) {
|
||||
newMode = STANDING;
|
||||
} else {
|
||||
newMode = SITTING;
|
||||
}
|
||||
newMode = SITTING;
|
||||
} else {
|
||||
newMode = FLAT;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ Stats::Stats(QQuickItem* parent) : QQuickItem(parent) {
|
|||
INSTANCE = this;
|
||||
const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
_monospaceFont = font.family();
|
||||
_audioStats = &DependencyManager::get<AudioClient>()->getStats();
|
||||
}
|
||||
|
||||
bool Stats::includeTimingRecord(const QString& name) {
|
||||
|
@ -89,14 +90,14 @@ bool Stats::includeTimingRecord(const QString& name) {
|
|||
}
|
||||
|
||||
|
||||
void Stats::updateStats() {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
if (isVisible()) {
|
||||
setVisible(false);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (!isVisible()) {
|
||||
void Stats::updateStats(bool force) {
|
||||
if (!force) {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
if (isVisible()) {
|
||||
setVisible(false);
|
||||
}
|
||||
return;
|
||||
} else if (!isVisible()) {
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +162,7 @@ void Stats::updateStats() {
|
|||
STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z));
|
||||
STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f);
|
||||
STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f);
|
||||
if (_expanded) {
|
||||
if (_expanded || force) {
|
||||
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||
if (avatarMixer) {
|
||||
STAT_UPDATE(avatarMixerKbps, roundf(
|
||||
|
@ -175,7 +176,7 @@ void Stats::updateStats() {
|
|||
STAT_UPDATE(avatarMixerPps, -1);
|
||||
}
|
||||
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
if (audioMixerNode) {
|
||||
if (audioMixerNode || force) {
|
||||
STAT_UPDATE(audioMixerKbps, roundf(
|
||||
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
|
||||
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
|
||||
|
@ -230,7 +231,7 @@ void Stats::updateStats() {
|
|||
totalLeaves += stats.getTotalLeaves();
|
||||
}
|
||||
}
|
||||
if (_expanded) {
|
||||
if (_expanded || force) {
|
||||
if (serverCount == 0) {
|
||||
sendingModeStream << "---";
|
||||
}
|
||||
|
@ -272,7 +273,7 @@ void Stats::updateStats() {
|
|||
STAT_UPDATE(serverElements, (int)totalNodes);
|
||||
STAT_UPDATE(localElements, (int)OctreeElement::getNodeCount());
|
||||
|
||||
if (_expanded) {
|
||||
if (_expanded || force) {
|
||||
STAT_UPDATE(serverInternal, (int)totalInternal);
|
||||
STAT_UPDATE(serverLeaves, (int)totalLeaves);
|
||||
// Local Voxels
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <OffscreenQmlElement.h>
|
||||
#include <RenderArgs.h>
|
||||
#include <QVector3D>
|
||||
#include <AudioIOStats.h>
|
||||
|
||||
#define STATS_PROPERTY(type, name, initialValue) \
|
||||
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
|
||||
|
@ -27,6 +28,8 @@ class Stats : public QQuickItem {
|
|||
Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded NOTIFY expandedChanged)
|
||||
Q_PROPERTY(bool timingExpanded READ isTimingExpanded NOTIFY timingExpandedChanged)
|
||||
Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT)
|
||||
Q_PROPERTY(float audioPacketlossUpstream READ getAudioPacketLossUpstream)
|
||||
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
|
||||
|
||||
STATS_PROPERTY(int, serverCount, 0)
|
||||
STATS_PROPERTY(int, framerate, 0)
|
||||
|
@ -81,7 +84,11 @@ public:
|
|||
const QString& monospaceFont() {
|
||||
return _monospaceFont;
|
||||
}
|
||||
void updateStats();
|
||||
|
||||
float getAudioPacketLossUpstream() { return _audioStats->getMixerAvatarStreamStats()._packetStreamStats.getLostRate(); }
|
||||
float getAudioPacketLossDownstream() { return _audioStats->getMixerDownstreamStats()._packetStreamStats.getLostRate(); }
|
||||
|
||||
void updateStats(bool force = false);
|
||||
|
||||
bool isExpanded() { return _expanded; }
|
||||
bool isTimingExpanded() { return _timingExpanded; }
|
||||
|
@ -93,6 +100,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
public slots:
|
||||
void forceUpdateStats() { updateStats(true); }
|
||||
|
||||
signals:
|
||||
void expandedChanged();
|
||||
void timingExpandedChanged();
|
||||
|
@ -146,6 +156,8 @@ private:
|
|||
bool _expanded{ false };
|
||||
bool _timingExpanded{ false };
|
||||
QString _monospaceFont;
|
||||
const AudioIOStats* _audioStats;
|
||||
};
|
||||
|
||||
#endif // hifi_Stats_h
|
||||
|
||||
|
|
|
@ -171,6 +171,6 @@ QScriptValue Base3DOverlay::getProperty(const QString& property) {
|
|||
}
|
||||
|
||||
bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face) {
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -57,11 +57,12 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
||||
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, QString& extraInfo) {
|
||||
return findRayIntersection(origin, direction, distance, face);
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) {
|
||||
return findRayIntersection(origin, direction, distance, face, surfaceNormal);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
@ -391,10 +391,10 @@ QScriptValue Circle3DOverlay::getProperty(const QString& property) {
|
|||
}
|
||||
|
||||
|
||||
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance, BoxFace& face) {
|
||||
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
|
||||
bool intersects = Planar3DOverlay::findRayIntersection(origin, direction, distance, face);
|
||||
bool intersects = Planar3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal);
|
||||
|
||||
if (intersects) {
|
||||
glm::vec3 hitPosition = origin + (distance * direction);
|
||||
|
|
|
@ -52,7 +52,8 @@ public:
|
|||
void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; }
|
||||
void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; }
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
||||
virtual Circle3DOverlay* createClone() const;
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ void Image3DOverlay::setURL(const QString& url) {
|
|||
}
|
||||
|
||||
bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face) {
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
if (_texture && _texture->isLoaded()) {
|
||||
// Make sure position and rotation is updated.
|
||||
applyTransformTo(_transform, true);
|
||||
|
@ -178,6 +178,7 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
|
|||
float maxSize = glm::max(width, height);
|
||||
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
|
||||
|
||||
// FIXME - face and surfaceNormal not being set
|
||||
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
||||
virtual Image3DOverlay* createClone() const;
|
||||
|
||||
|
|
|
@ -159,16 +159,16 @@ QScriptValue ModelOverlay::getProperty(const QString& property) {
|
|||
}
|
||||
|
||||
bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face) {
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
|
||||
QString subMeshNameTemp;
|
||||
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, subMeshNameTemp);
|
||||
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp);
|
||||
}
|
||||
|
||||
bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, QString& extraInfo) {
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) {
|
||||
|
||||
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo);
|
||||
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo);
|
||||
}
|
||||
|
||||
ModelOverlay* ModelOverlay::createClone() const {
|
||||
|
|
|
@ -29,9 +29,10 @@ public:
|
|||
virtual void render(RenderArgs* args);
|
||||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face, QString& extraInfo);
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo);
|
||||
|
||||
virtual ModelOverlay* createClone() const;
|
||||
|
||||
|
|
|
@ -346,6 +346,7 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
|||
glm::vec3 origin(pointCopy.x, pointCopy.y, LARGE_NEGATIVE_FLOAT);
|
||||
glm::vec3 direction(0, 0, 1);
|
||||
BoxFace thisFace;
|
||||
glm::vec3 thisSurfaceNormal;
|
||||
float distance;
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
|
@ -354,7 +355,7 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
|||
if (i.value()->is3D()) {
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Base3DOverlay>(i.value());
|
||||
if (thisOverlay && !thisOverlay->getIgnoreRayIntersection()) {
|
||||
if (thisOverlay->findRayIntersection(origin, direction, distance, thisFace)) {
|
||||
if (thisOverlay->findRayIntersection(origin, direction, distance, thisFace, thisSurfaceNormal)) {
|
||||
return thisID;
|
||||
}
|
||||
}
|
||||
|
@ -423,8 +424,10 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
|
|||
if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) {
|
||||
float thisDistance;
|
||||
BoxFace thisFace;
|
||||
glm::vec3 thisSurfaceNormal;
|
||||
QString thisExtraInfo;
|
||||
if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, thisFace, thisExtraInfo)) {
|
||||
if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance,
|
||||
thisFace, thisSurfaceNormal, thisExtraInfo)) {
|
||||
bool isDrawInFront = thisOverlay->getDrawInFront();
|
||||
if (thisDistance < bestDistance && (!bestIsFront || isDrawInFront)) {
|
||||
bestIsFront = isDrawInFront;
|
||||
|
@ -432,6 +435,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
|
|||
result.intersects = true;
|
||||
result.distance = thisDistance;
|
||||
result.face = thisFace;
|
||||
result.surfaceNormal = thisSurfaceNormal;
|
||||
result.overlayID = thisID;
|
||||
result.intersection = ray.origin + (ray.direction * thisDistance);
|
||||
result.extraInfo = thisExtraInfo;
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
int overlayID;
|
||||
float distance;
|
||||
BoxFace face;
|
||||
glm::vec3 surfaceNormal;
|
||||
glm::vec3 intersection;
|
||||
QString extraInfo;
|
||||
};
|
||||
|
|
|
@ -92,6 +92,7 @@ QScriptValue Planar3DOverlay::getProperty(const QString& property) {
|
|||
}
|
||||
|
||||
bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face) {
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
// FIXME - face and surfaceNormal not being returned
|
||||
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), getDimensions(), distance);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
||||
protected:
|
||||
glm::vec2 _dimensions;
|
||||
|
|
|
@ -211,7 +211,8 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
|
|||
return QSizeF(extents.x, extents.y) * pointToWorldScale;
|
||||
}
|
||||
|
||||
bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, BoxFace &face) {
|
||||
bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance,
|
||||
BoxFace &face, glm::vec3& surfaceNormal) {
|
||||
applyTransformTo(_transform, true);
|
||||
return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face);
|
||||
return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ public:
|
|||
|
||||
QSizeF textSize(const QString& test) const; // Meters
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
||||
virtual Text3DOverlay* createClone() const;
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ QScriptValue Volume3DOverlay::getProperty(const QString& property) {
|
|||
}
|
||||
|
||||
bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face) {
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
// extents is the entity relative, scaled, centered extents of the entity
|
||||
glm::mat4 worldToEntityMatrix;
|
||||
_transform.getInverseMatrix(worldToEntityMatrix);
|
||||
|
@ -103,5 +103,5 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
|
|||
|
||||
// we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame
|
||||
// and testing intersection there.
|
||||
return _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face);
|
||||
return _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
||||
protected:
|
||||
// Centered local bounding box
|
||||
|
|
|
@ -151,8 +151,10 @@ void Web3DOverlay::setURL(const QString& url) {
|
|||
|
||||
}
|
||||
|
||||
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) {
|
||||
//// Make sure position and rotation is updated.
|
||||
bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
// FIXME - face and surfaceNormal not being returned
|
||||
|
||||
// Make sure position and rotation is updated.
|
||||
applyTransformTo(_transform, true);
|
||||
vec2 size = _resolution / _dpi * INCHES_TO_METERS * vec2(getDimensions());
|
||||
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
|
||||
|
|
|
@ -34,7 +34,8 @@ public:
|
|||
virtual void setProperties(const QScriptValue& properties);
|
||||
virtual QScriptValue getProperty(const QString& property);
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
||||
virtual Web3DOverlay* createClone() const;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "AnimInverseKinematics.h"
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -42,9 +43,8 @@ void AnimInverseKinematics::loadPoses(const AnimPoseVec& poses) {
|
|||
|
||||
void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) const {
|
||||
int numJoints = (int)_relativePoses.size();
|
||||
absolutePoses.clear();
|
||||
absolutePoses.resize(numJoints);
|
||||
assert(numJoints <= _skeleton->getNumJoints());
|
||||
assert(numJoints == (int)absolutePoses.size());
|
||||
for (int i = 0; i < numJoints; ++i) {
|
||||
int parentIndex = _skeleton->getParentIndex(i);
|
||||
if (parentIndex < 0) {
|
||||
|
@ -55,7 +55,11 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con
|
|||
}
|
||||
}
|
||||
|
||||
void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar) {
|
||||
void AnimInverseKinematics::setTargetVars(
|
||||
const QString& jointName,
|
||||
const QString& positionVar,
|
||||
const QString& rotationVar,
|
||||
const QString& typeVar) {
|
||||
// if there are dups, last one wins.
|
||||
bool found = false;
|
||||
for (auto& targetVar: _targetVarVec) {
|
||||
|
@ -63,13 +67,14 @@ void AnimInverseKinematics::setTargetVars(const QString& jointName, const QStrin
|
|||
// update existing targetVar
|
||||
targetVar.positionVar = positionVar;
|
||||
targetVar.rotationVar = rotationVar;
|
||||
targetVar.typeVar = typeVar;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// create a new entry
|
||||
_targetVarVec.push_back(IKTargetVar(jointName, positionVar, rotationVar));
|
||||
_targetVarVec.push_back(IKTargetVar(jointName, positionVar, rotationVar, typeVar));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,6 +91,7 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde
|
|||
|
||||
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets) {
|
||||
// build a list of valid targets from _targetVarVec and animVars
|
||||
_maxTargetIndex = -1;
|
||||
bool removeUnfoundJoints = false;
|
||||
for (auto& targetVar : _targetVarVec) {
|
||||
if (targetVar.jointIndex == -1) {
|
||||
|
@ -100,14 +106,17 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
|||
removeUnfoundJoints = true;
|
||||
}
|
||||
} else {
|
||||
// TODO: get this done without a double-lookup of each var in animVars
|
||||
IKTarget target;
|
||||
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses);
|
||||
target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
|
||||
target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
|
||||
target.setType(animVars.lookup(targetVar.typeVar, QString("")));
|
||||
target.rootIndex = targetVar.rootIndex;
|
||||
target.index = targetVar.jointIndex;
|
||||
targets.push_back(target);
|
||||
if (target.index > _maxTargetIndex) {
|
||||
_maxTargetIndex = target.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,9 +138,10 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
|
|||
}
|
||||
}
|
||||
|
||||
void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector<IKTarget>& targets) {
|
||||
void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets) {
|
||||
// compute absolute poses that correspond to relative target poses
|
||||
AnimPoseVec absolutePoses;
|
||||
absolutePoses.resize(_relativePoses.size());
|
||||
computeAbsolutePoses(absolutePoses);
|
||||
|
||||
// clear the accumulators before we start the IK solver
|
||||
|
@ -139,86 +149,124 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector<IKTarge
|
|||
accumulator.clearAndClean();
|
||||
}
|
||||
|
||||
float largestError = 0.0f;
|
||||
const float ACCEPTABLE_RELATIVE_ERROR = 1.0e-3f;
|
||||
int numLoops = 0;
|
||||
const int MAX_IK_LOOPS = 16;
|
||||
const quint64 MAX_IK_TIME = 10 * USECS_PER_MSEC;
|
||||
quint64 expiry = usecTimestampNow() + MAX_IK_TIME;
|
||||
const int MAX_IK_LOOPS = 4;
|
||||
do {
|
||||
largestError = 0.0f;
|
||||
int lowestMovedIndex = _relativePoses.size();
|
||||
for (auto& target: targets) {
|
||||
int tipIndex = target.index;
|
||||
if (target.type == IKTarget::Type::RotationOnly) {
|
||||
// the final rotation will be enforced after the iterations
|
||||
continue;
|
||||
}
|
||||
AnimPose targetPose = target.pose;
|
||||
|
||||
glm::vec3 tip = absolutePoses[tipIndex].trans;
|
||||
float error = glm::length(targetPose.trans - tip);
|
||||
// cache tip absolute transform
|
||||
glm::vec3 tipPosition = absolutePoses[tipIndex].trans;
|
||||
glm::quat tipRotation = absolutePoses[tipIndex].rot;
|
||||
|
||||
// cache tip's parent's absolute rotation so we can recompute the tip's parent-relative
|
||||
// as we proceed walking down the joint chain
|
||||
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
||||
glm::quat tipParentRotation;
|
||||
if (pivotIndex != -1) {
|
||||
tipParentRotation = absolutePoses[pivotIndex].rot;
|
||||
}
|
||||
|
||||
// descend toward root, pivoting each joint to get tip closer to target
|
||||
int pivotIndex = _skeleton->getParentIndex(tipIndex);
|
||||
while (pivotIndex != -1 && error > ACCEPTABLE_RELATIVE_ERROR) {
|
||||
int ancestorCount = 1;
|
||||
while (pivotIndex != -1) {
|
||||
// compute the two lines that should be aligned
|
||||
glm::vec3 jointPosition = absolutePoses[pivotIndex].trans;
|
||||
glm::vec3 leverArm = tip - jointPosition;
|
||||
glm::vec3 leverArm = tipPosition - jointPosition;
|
||||
glm::vec3 targetLine = targetPose.trans - jointPosition;
|
||||
|
||||
// compute the axis of the rotation that would align them
|
||||
// compute the swing that would get get tip closer
|
||||
glm::vec3 axis = glm::cross(leverArm, targetLine);
|
||||
float axisLength = glm::length(axis);
|
||||
if (axisLength > EPSILON) {
|
||||
// compute deltaRotation for alignment (brings tip closer to target)
|
||||
glm::quat deltaRotation;
|
||||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||
if (axisLength > MIN_AXIS_LENGTH) {
|
||||
// compute deltaRotation for alignment (swings tip closer to target)
|
||||
axis /= axisLength;
|
||||
float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine)));
|
||||
|
||||
// NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is
|
||||
// still possible for the angle to be zero so we also check that to avoid unnecessary calculations.
|
||||
if (angle > EPSILON) {
|
||||
// reduce angle by half: slows convergence but adds stability to IK solution
|
||||
angle = 0.5f * angle;
|
||||
glm::quat deltaRotation = glm::angleAxis(angle, axis);
|
||||
const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f;
|
||||
if (angle > MIN_ADJUSTMENT_ANGLE) {
|
||||
// reduce angle by a fraction (reduces IK swing contribution of this joint)
|
||||
angle /= (float)ancestorCount;
|
||||
deltaRotation = glm::angleAxis(angle, axis);
|
||||
}
|
||||
|
||||
int parentIndex = _skeleton->getParentIndex(pivotIndex);
|
||||
if (parentIndex == -1) {
|
||||
// TODO? apply constraints to root?
|
||||
// TODO? harvest the root's transform as movement of entire skeleton?
|
||||
} else {
|
||||
// compute joint's new parent-relative rotation
|
||||
// Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q
|
||||
glm::quat newRot = glm::normalize(glm::inverse(
|
||||
absolutePoses[parentIndex].rot) *
|
||||
deltaRotation *
|
||||
absolutePoses[pivotIndex].rot);
|
||||
RotationConstraint* constraint = getConstraint(pivotIndex);
|
||||
if (constraint) {
|
||||
bool constrained = constraint->apply(newRot);
|
||||
if (constrained) {
|
||||
// the constraint will modify the movement of the tip so we have to compute the modified
|
||||
// model-frame deltaRotation
|
||||
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
|
||||
deltaRotation = absolutePoses[parentIndex].rot *
|
||||
newRot *
|
||||
glm::inverse(absolutePoses[pivotIndex].rot);
|
||||
}
|
||||
}
|
||||
// store the rotation change in the accumulator
|
||||
_accumulators[pivotIndex].add(newRot);
|
||||
}
|
||||
// this joint has been changed so we check to see if it has the lowest index
|
||||
if (pivotIndex < lowestMovedIndex) {
|
||||
lowestMovedIndex = pivotIndex;
|
||||
}
|
||||
// The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's
|
||||
// new rotation and its target. We compute that delta here and rotate the tipJoint accordingly.
|
||||
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentRotation) * targetPose.rot;
|
||||
|
||||
// keep track of tip's new position as we descend towards root
|
||||
tip = jointPosition + deltaRotation * leverArm;
|
||||
error = glm::length(targetPose.trans - tip);
|
||||
// enforce tip's constraint
|
||||
RotationConstraint* constraint = getConstraint(tipIndex);
|
||||
if (constraint) {
|
||||
bool constrained = constraint->apply(tipRelativeRotation);
|
||||
if (constrained) {
|
||||
// The tip's final parent-relative rotation violates its constraint
|
||||
// so we try to twist this pivot to compensate.
|
||||
glm::quat constrainedTipRotation = deltaRotation * tipParentRotation * tipRelativeRotation;
|
||||
glm::quat missingRotation = targetPose.rot * glm::inverse(constrainedTipRotation);
|
||||
glm::quat swingPart;
|
||||
glm::quat twistPart;
|
||||
glm::vec3 axis = glm::normalize(deltaRotation * leverArm);
|
||||
swingTwistDecomposition(missingRotation, axis, swingPart, twistPart);
|
||||
deltaRotation = twistPart * deltaRotation;
|
||||
}
|
||||
// we update the tip rotation here to rotate it as close to its target orientation as possible
|
||||
// before moving on to next pivot
|
||||
tipRotation = tipParentRotation * tipRelativeRotation;
|
||||
}
|
||||
}
|
||||
++ancestorCount;
|
||||
|
||||
int parentIndex = _skeleton->getParentIndex(pivotIndex);
|
||||
if (parentIndex == -1) {
|
||||
// TODO? apply constraints to root?
|
||||
// TODO? harvest the root's transform as movement of entire skeleton?
|
||||
} else {
|
||||
// compute joint's new parent-relative rotation after swing
|
||||
// Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q
|
||||
glm::quat newRot = glm::normalize(glm::inverse(
|
||||
absolutePoses[parentIndex].rot) *
|
||||
deltaRotation *
|
||||
absolutePoses[pivotIndex].rot);
|
||||
|
||||
// enforce pivot's constraint
|
||||
RotationConstraint* constraint = getConstraint(pivotIndex);
|
||||
if (constraint) {
|
||||
bool constrained = constraint->apply(newRot);
|
||||
if (constrained) {
|
||||
// the constraint will modify the movement of the tip so we have to compute the modified
|
||||
// model-frame deltaRotation
|
||||
// Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^
|
||||
deltaRotation = absolutePoses[parentIndex].rot *
|
||||
newRot *
|
||||
glm::inverse(absolutePoses[pivotIndex].rot);
|
||||
}
|
||||
}
|
||||
|
||||
// store the rotation change in the accumulator
|
||||
_accumulators[pivotIndex].add(newRot);
|
||||
}
|
||||
// this joint has been changed so we check to see if it has the lowest index
|
||||
if (pivotIndex < lowestMovedIndex) {
|
||||
lowestMovedIndex = pivotIndex;
|
||||
}
|
||||
|
||||
// keep track of tip's new transform as we descend towards root
|
||||
tipPosition = jointPosition + deltaRotation * leverArm;
|
||||
tipRotation = glm::normalize(deltaRotation * tipRotation);
|
||||
tipParentRotation = glm::normalize(deltaRotation * tipParentRotation);
|
||||
|
||||
pivotIndex = _skeleton->getParentIndex(pivotIndex);
|
||||
}
|
||||
if (largestError < error) {
|
||||
largestError = error;
|
||||
}
|
||||
}
|
||||
++numLoops;
|
||||
|
||||
|
@ -238,7 +286,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector<IKTarge
|
|||
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
|
||||
}
|
||||
}
|
||||
} while (largestError > ACCEPTABLE_RELATIVE_ERROR && numLoops < MAX_IK_LOOPS && usecTimestampNow() < expiry);
|
||||
} while (numLoops < MAX_IK_LOOPS);
|
||||
|
||||
// finally set the relative rotation of each tip to agree with absolute target rotation
|
||||
for (auto& target: targets) {
|
||||
|
@ -380,7 +428,7 @@ void AnimInverseKinematics::initConstraints() {
|
|||
}
|
||||
}
|
||||
|
||||
_constraints.clear();
|
||||
clearConstraints();
|
||||
for (int i = 0; i < numJoints; ++i) {
|
||||
// compute the joint's baseName and remember whether its prefix was "Left" or not
|
||||
QString baseName = _skeleton->getJointName(i);
|
||||
|
@ -456,7 +504,7 @@ void AnimInverseKinematics::initConstraints() {
|
|||
} else if (0 == baseName.compare("Hand", Qt::CaseInsensitive)) {
|
||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
|
||||
const float MAX_HAND_TWIST = PI;
|
||||
const float MAX_HAND_TWIST = 3.0f * PI / 5.0f;
|
||||
const float MIN_HAND_TWIST = -PI / 2.0f;
|
||||
if (isLeft) {
|
||||
stConstraint->setTwistLimits(-MAX_HAND_TWIST, -MIN_HAND_TWIST);
|
||||
|
@ -639,9 +687,12 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
|||
targetVar.jointIndex = -1;
|
||||
}
|
||||
|
||||
_maxTargetIndex = 0;
|
||||
_maxTargetIndex = -1;
|
||||
|
||||
for (auto& accumulator: _accumulators) {
|
||||
accumulator.clearAndClean();
|
||||
}
|
||||
|
||||
_accumulators.clear();
|
||||
if (skeleton) {
|
||||
initConstraints();
|
||||
} else {
|
||||
|
|
|
@ -31,20 +31,27 @@ public:
|
|||
void loadPoses(const AnimPoseVec& poses);
|
||||
void computeAbsolutePoses(AnimPoseVec& absolutePoses) const;
|
||||
|
||||
void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar);
|
||||
void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar, const QString& typeVar);
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
|
||||
protected:
|
||||
struct IKTarget {
|
||||
enum class Type {
|
||||
RotationAndPosition,
|
||||
RotationOnly
|
||||
};
|
||||
AnimPose pose;
|
||||
int index;
|
||||
int rootIndex;
|
||||
Type type = Type::RotationAndPosition;
|
||||
|
||||
void setType(const QString& typeVar) { type = ((typeVar == "RotationOnly") ? Type::RotationOnly : Type::RotationAndPosition); }
|
||||
};
|
||||
|
||||
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets);
|
||||
void solveWithCyclicCoordinateDescent(std::vector<IKTarget>& targets);
|
||||
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& targets);
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton);
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
|
@ -55,15 +62,20 @@ protected:
|
|||
void initConstraints();
|
||||
|
||||
struct IKTargetVar {
|
||||
IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn) :
|
||||
IKTargetVar(const QString& jointNameIn,
|
||||
const QString& positionVarIn,
|
||||
const QString& rotationVarIn,
|
||||
const QString& typeVarIn) :
|
||||
positionVar(positionVarIn),
|
||||
rotationVar(rotationVarIn),
|
||||
typeVar(typeVarIn),
|
||||
jointName(jointNameIn),
|
||||
jointIndex(-1),
|
||||
rootIndex(-1) {}
|
||||
|
||||
QString positionVar;
|
||||
QString rotationVar;
|
||||
QString typeVar;
|
||||
QString jointName;
|
||||
int jointIndex; // cached joint index
|
||||
int rootIndex; // cached root index
|
||||
|
|
|
@ -29,6 +29,12 @@ const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, flo
|
|||
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
_poses = underPoses;
|
||||
|
||||
if (underPoses.size() == 0) {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
for (auto& jointVar : _jointVars) {
|
||||
if (!jointVar.hasPerformedJointLookup) {
|
||||
jointVar.jointIndex = _skeleton->nameToJointIndex(jointVar.jointName);
|
||||
|
|
|
@ -61,7 +61,7 @@ public:
|
|||
// hierarchy accessors
|
||||
void addChild(Pointer child) { _children.push_back(child); }
|
||||
void removeChild(Pointer child);
|
||||
|
||||
|
||||
Pointer getChild(int i) const;
|
||||
int getChildCount() const { return (int)_children.size(); }
|
||||
|
||||
|
|
|
@ -342,8 +342,9 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
|
|||
READ_STRING(jointName, targetObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(positionVar, targetObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(rotationVar, targetObj, id, jsonUrl, nullptr);
|
||||
READ_OPTIONAL_STRING(typeVar, targetObj);
|
||||
|
||||
node->setTargetVars(jointName, positionVar, rotationVar);
|
||||
node->setTargetVars(jointName, positionVar, rotationVar, typeVar);
|
||||
};
|
||||
|
||||
return node;
|
||||
|
|
|
@ -51,8 +51,8 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float d
|
|||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
if (_children.size() >= 2) {
|
||||
auto underPoses = _children[1]->evaluate(animVars, dt, triggersOut);
|
||||
auto overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses);
|
||||
auto& underPoses = _children[1]->evaluate(animVars, dt, triggersOut);
|
||||
auto& overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses);
|
||||
|
||||
if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) {
|
||||
_poses.resize(underPoses.size());
|
||||
|
|
|
@ -22,7 +22,7 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
|||
|
||||
AnimPose::AnimPose(const glm::mat4& mat) {
|
||||
scale = extractScale(mat);
|
||||
rot = extractRotation(mat);
|
||||
rot = glm::normalize(glm::quat_cast(mat));
|
||||
trans = extractTranslation(mat);
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,7 @@ void AnimSkeleton::dump() const {
|
|||
qCDebug(animation) << "[";
|
||||
for (int i = 0; i < getNumJoints(); i++) {
|
||||
qCDebug(animation) << " {";
|
||||
qCDebug(animation) << " index =" << i;
|
||||
qCDebug(animation) << " name =" << getJointName(i);
|
||||
qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i);
|
||||
qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i);
|
||||
|
@ -188,6 +189,7 @@ void AnimSkeleton::dump(const AnimPoseVec& poses) const {
|
|||
qCDebug(animation) << "[";
|
||||
for (int i = 0; i < getNumJoints(); i++) {
|
||||
qCDebug(animation) << " {";
|
||||
qCDebug(animation) << " index =" << i;
|
||||
qCDebug(animation) << " name =" << getJointName(i);
|
||||
qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i);
|
||||
qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i);
|
||||
|
|
|
@ -52,7 +52,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl
|
|||
if (_duringInterp) {
|
||||
_alpha += _alphaVel * dt;
|
||||
if (_alpha < 1.0f) {
|
||||
if (_poses.size() > 0) {
|
||||
if (_poses.size() > 0 && _nextPoses.size() > 0 && _prevPoses.size() > 0) {
|
||||
::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]);
|
||||
}
|
||||
} else {
|
||||
|
@ -77,8 +77,6 @@ void AnimStateMachine::addState(State::Pointer state) {
|
|||
|
||||
void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointer desiredState) {
|
||||
|
||||
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID();
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
|
||||
auto prevStateNode = _currentState->getNode();
|
||||
|
@ -96,6 +94,8 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe
|
|||
Triggers triggers;
|
||||
_nextPoses = nextStateNode->evaluate(animVars, dt, triggers);
|
||||
|
||||
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget;
|
||||
|
||||
_currentState = desiredState;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "AnimationLogging.h"
|
||||
|
||||
class AnimVariant {
|
||||
public:
|
||||
|
@ -46,6 +47,7 @@ public:
|
|||
bool isQuat() const { return _type == Type::Quat; }
|
||||
bool isMat4() const { return _type == Type::Mat4; }
|
||||
bool isString() const { return _type == Type::String; }
|
||||
Type getType() const { return _type; }
|
||||
|
||||
void setBool(bool value) { assert(_type == Type::Bool); _val.boolVal = value; }
|
||||
void setInt(int value) { assert(_type == Type::Int); _val.intVal = value; }
|
||||
|
@ -156,6 +158,37 @@ public:
|
|||
|
||||
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
|
||||
|
||||
#ifdef NDEBUG
|
||||
void dump() const {
|
||||
qCDebug(animation) << "AnimVariantMap =";
|
||||
for (auto& pair : _map) {
|
||||
switch (pair.second.getType()) {
|
||||
case AnimVariant::Type::Bool:
|
||||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getBool();
|
||||
break;
|
||||
case AnimVariant::Type::Int:
|
||||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getInt();
|
||||
break;
|
||||
case AnimVariant::Type::Float:
|
||||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getFloat();
|
||||
break;
|
||||
case AnimVariant::Type::Vec3:
|
||||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getVec3();
|
||||
break;
|
||||
case AnimVariant::Type::Quat:
|
||||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getQuat();
|
||||
break;
|
||||
case AnimVariant::Type::Mat4:
|
||||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getMat4();
|
||||
break;
|
||||
case AnimVariant::Type::String:
|
||||
qCDebug(animation) << " " << pair.first << "=" << pair.second.getString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
std::map<QString, AnimVariant> _map;
|
||||
std::set<QString> _triggers;
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <queue>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
#include "AnimationHandle.h"
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimSkeleton.h"
|
||||
#include "DebugDraw.h"
|
||||
|
||||
#include "Rig.h"
|
||||
|
||||
|
@ -143,16 +145,20 @@ AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QStrin
|
|||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
const float FADE_FRAMES = 30.0f;
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
|
||||
void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
||||
AnimationHandlePointer handle = addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true);
|
||||
handle->setFadePerSecond(1.0f); // For now. Could be individualized later.
|
||||
handle->setFadePerSecond(FRAMES_PER_SECOND / FADE_FRAMES); // For now. Could be individualized later.
|
||||
}
|
||||
|
||||
void Rig::stopAnimationByRole(const QString& role) {
|
||||
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
|
||||
if (handle->getRole() == role) {
|
||||
handle->setFadePerSecond(-1.0f); // For now. Could be individualized later.
|
||||
handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // For now. Could be individualized later.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +167,7 @@ void Rig::stopAnimation(const QString& url) {
|
|||
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
|
||||
if (handle->getURL() == url) {
|
||||
handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking
|
||||
handle->setFadePerSecond(-1.0f); // so that the updateAnimation code notices
|
||||
handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // so that the updateAnimation code notices
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -585,10 +591,12 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
|||
_animVars.setTrigger(trigger);
|
||||
}
|
||||
|
||||
clearJointStatePriorities();
|
||||
|
||||
// copy poses into jointStates
|
||||
const float PRIORITY = 1.0f;
|
||||
for (size_t i = 0; i < poses.size(); i++) {
|
||||
setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false);
|
||||
setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false, 1.0f);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -736,7 +744,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
return;
|
||||
}
|
||||
|
||||
if (_enableAnimGraph && _animSkeleton) {
|
||||
if (disableHands || (_enableAnimGraph && _animSkeleton)) {
|
||||
// the hand data goes through a different path: Rig::updateFromHandParameters() --> early-exit
|
||||
return;
|
||||
}
|
||||
|
@ -924,6 +932,12 @@ void Rig::updateVisibleJointStates() {
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::clearJointStatePriorities() {
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].setAnimationPriority(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return;
|
||||
|
@ -948,6 +962,8 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
|
|||
void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) {
|
||||
if (params.enableLean) {
|
||||
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||
} else {
|
||||
_animVars.unset("lean");
|
||||
}
|
||||
updateNeckJoint(params.neckJointIndex, params);
|
||||
updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation,
|
||||
|
@ -984,21 +1000,101 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa
|
|||
}
|
||||
}
|
||||
|
||||
static AnimPose avatarToBonePose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) {
|
||||
AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips"));
|
||||
AnimPose rotY180(glm::vec3(1), glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0));
|
||||
return rootPose * rotY180 * pose;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_RENDERING
|
||||
static AnimPose boneToAvatarPose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) {
|
||||
AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips"));
|
||||
AnimPose rotY180(glm::vec3(1), glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0));
|
||||
return (rootPose * rotY180).inverse() * pose;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const AnimPose& avatarHMDPose,
|
||||
glm::vec3& headPositionOut, glm::quat& headOrientationOut,
|
||||
glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) {
|
||||
|
||||
// the input hmd values are in avatar space
|
||||
// we need to transform them into bone space.
|
||||
AnimPose hmdPose = avatarToBonePose(avatarHMDPose, skeleton);
|
||||
const glm::vec3 hmdPosition = hmdPose.trans;
|
||||
const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
const glm::quat hmdOrientation = hmdPose.rot * rotY180; // rotY180 will make z forward not -z
|
||||
|
||||
// TODO: cache jointIndices
|
||||
|
||||
// Use absolute bindPose positions just in case the relBindPose have rotations we don't expect.
|
||||
glm::vec3 absRightEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("RightEye")).trans;
|
||||
glm::vec3 absLeftEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("LeftEye")).trans;
|
||||
glm::vec3 absHeadPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Head")).trans;
|
||||
glm::vec3 absNeckPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Neck")).trans;
|
||||
|
||||
glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f;
|
||||
glm::vec3 eyeOffset = absCenterEyePos - absHeadPos;
|
||||
glm::vec3 headOffset = absHeadPos - absNeckPos;
|
||||
|
||||
// apply simplistic head/neck model
|
||||
|
||||
// head
|
||||
headPositionOut = hmdPosition - hmdOrientation * eyeOffset;
|
||||
headOrientationOut = hmdOrientation;
|
||||
|
||||
// neck
|
||||
neckPositionOut = hmdPosition - hmdOrientation * (headOffset + eyeOffset);
|
||||
|
||||
// slerp between bind orientation and hmdOrientation
|
||||
neckOrientationOut = safeMix(hmdOrientation, skeleton->getRelativeBindPose(skeleton->nameToJointIndex("Neck")).rot, 0.5f);
|
||||
}
|
||||
|
||||
void Rig::updateNeckJoint(int index, const HeadParameters& params) {
|
||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||
if (_enableAnimGraph && _animSkeleton && _animNode) {
|
||||
// the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll.
|
||||
glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) *
|
||||
glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) *
|
||||
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
|
||||
_animVars.set("headRotation", realLocalHeadOrientation);
|
||||
if (_enableAnimGraph && _animSkeleton) {
|
||||
|
||||
if (params.isInHMD) {
|
||||
int headIndex = _animSkeleton->nameToJointIndex("Head");
|
||||
AnimPose rootPose = _animNode->getRootPose(headIndex);
|
||||
_animVars.set("headPosition", rootPose.trans + params.localHeadPosition); // rootPose.rot is handled in params?d
|
||||
glm::vec3 headPos, neckPos;
|
||||
glm::quat headRot, neckRot;
|
||||
|
||||
AnimPose avatarHMDPose(glm::vec3(1), params.localHeadOrientation, params.localHeadPosition);
|
||||
computeHeadNeckAnimVars(_animSkeleton, avatarHMDPose, headPos, headRot, neckPos, neckRot);
|
||||
|
||||
// debug rendering
|
||||
#ifdef DEBUG_RENDERING
|
||||
const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const glm::vec4 green(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
// transform from bone into avatar space
|
||||
AnimPose headPose(glm::vec3(1), headRot, headPos);
|
||||
headPose = boneToAvatarPose(headPose, _animSkeleton);
|
||||
DebugDraw::getInstance().addMyAvatarMarker("headTarget", headPose.rot, headPose.trans, red);
|
||||
|
||||
// transform from bone into avatar space
|
||||
AnimPose neckPose(glm::vec3(1), neckRot, neckPos);
|
||||
neckPose = boneToAvatarPose(neckPose, _animSkeleton);
|
||||
DebugDraw::getInstance().addMyAvatarMarker("neckTarget", neckPose.rot, neckPose.trans, green);
|
||||
#endif
|
||||
|
||||
_animVars.set("headPosition", headPos);
|
||||
_animVars.set("headRotation", headRot);
|
||||
_animVars.set("headType", QString("RotationAndPosition"));
|
||||
_animVars.set("neckPosition", neckPos);
|
||||
_animVars.set("neckRotation", neckRot);
|
||||
|
||||
} else {
|
||||
|
||||
// the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll.
|
||||
glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) *
|
||||
glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) *
|
||||
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
|
||||
|
||||
_animVars.unset("headPosition");
|
||||
_animVars.set("headRotation", realLocalHeadOrientation);
|
||||
_animVars.set("headType", QString("RotationOnly"));
|
||||
_animVars.unset("neckPosition");
|
||||
_animVars.unset("neckRotation");
|
||||
}
|
||||
} else if (!_enableAnimGraph) {
|
||||
|
||||
|
|
|
@ -172,6 +172,7 @@ public:
|
|||
bool getJointRotationInConstrainedFrame(int jointIndex, glm::quat& rotOut) const;
|
||||
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
|
||||
void updateVisibleJointStates();
|
||||
void clearJointStatePriorities();
|
||||
|
||||
virtual void updateJointState(int index, glm::mat4 rootTransform) = 0;
|
||||
|
||||
|
@ -193,6 +194,7 @@ public:
|
|||
|
||||
AnimNode::ConstPointer getAnimNode() const { return _animNode; }
|
||||
AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
|
||||
bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics)
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -9,18 +9,13 @@ link_hifi_libraries(audio)
|
|||
target_include_directories(${TARGET_NAME} PUBLIC "${HIFI_LIBRARY_DIR}/audio/src")
|
||||
|
||||
# have CMake grab externals for us
|
||||
add_dependency_external_projects(gverb soxr)
|
||||
add_dependency_external_projects(gverb)
|
||||
|
||||
find_package(Gverb REQUIRED)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${GVERB_LIBRARIES})
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${GVERB_INCLUDE_DIRS})
|
||||
|
||||
# we use libsoxr for resampling
|
||||
find_package(Soxr REQUIRED)
|
||||
target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES})
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS})
|
||||
|
||||
if (APPLE)
|
||||
find_library(CoreAudio CoreAudio)
|
||||
find_library(CoreFoundation CoreFoundation)
|
||||
|
|
|
@ -38,11 +38,20 @@
|
|||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4273 4305)
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <gverb/gverb.h>
|
||||
#include <gverb/gverbdsp.h>
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
|
|
@ -7,10 +7,4 @@ add_dependency_external_projects(glm)
|
|||
find_package(GLM REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
|
||||
|
||||
# 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)
|
||||
|
|
|
@ -330,6 +330,7 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol
|
|||
reinterpret_cast<int16_t*>(resampled.data()),
|
||||
nInputFrames);
|
||||
|
||||
Q_UNUSED(nOutputFrames);
|
||||
return playSoundAndDelete(resampled, options, NULL);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,6 +31,8 @@ public:
|
|||
|
||||
private:
|
||||
float* _polyphaseFilter;
|
||||
int* _stepTable;
|
||||
|
||||
float* _history[MAX_CHANNELS];
|
||||
float* _inputs[MAX_CHANNELS];
|
||||
float* _outputs[MAX_CHANNELS];
|
||||
|
@ -45,10 +47,13 @@ private:
|
|||
int _numTaps;
|
||||
int _numHistory;
|
||||
|
||||
int _phase;
|
||||
int64_t _offset;
|
||||
int64_t _step;
|
||||
|
||||
int createPolyphaseFilter(int upFactor, int downFactor, float gain);
|
||||
int createRationalFilter(int upFactor, int downFactor, float gain);
|
||||
int createIrrationalFilter(int upFactor, int downFactor, float gain);
|
||||
|
||||
int multirateFilter1(const float* input0, float* output0, int inputFrames);
|
||||
int multirateFilter2(const float* input0, const float* input1, float* output0, float* output1, int inputFrames);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
set(TARGET_NAME entities-renderer)
|
||||
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model render)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
|
||||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(Widgets Network Script)
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
<!
|
||||
// DeferredBufferWrite.slh
|
||||
// libraries/render-utils/src
|
||||
//
|
||||
// Created by Sam Gateau on 1/12/15.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
!>
|
||||
<@if not DEFERRED_BUFFER_WRITE_SLH@>
|
||||
<@def DEFERRED_BUFFER_WRITE_SLH@>
|
||||
|
||||
layout(location = 0) out vec4 _fragColor0;
|
||||
layout(location = 1) out vec4 _fragColor1;
|
||||
layout(location = 2) out vec4 _fragColor2;
|
||||
|
||||
// the glow intensity
|
||||
uniform float glowIntensity;
|
||||
|
||||
// the alpha threshold
|
||||
uniform float alphaThreshold;
|
||||
|
||||
uniform sampler2D normalFittingMap;
|
||||
|
||||
vec3 bestFitNormal(vec3 normal) {
|
||||
vec3 absNorm = abs(normal);
|
||||
float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
|
||||
|
||||
vec2 texcoord = (absNorm.z < maxNAbs ?
|
||||
(absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
|
||||
absNorm.xy);
|
||||
texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
|
||||
texcoord.y /= texcoord.x;
|
||||
vec3 cN = normal / maxNAbs;
|
||||
float fittingScale = texture(normalFittingMap, texcoord).a;
|
||||
cN *= fittingScale;
|
||||
return (cN * 0.5 + 0.5);
|
||||
}
|
||||
|
||||
float evalOpaqueFinalAlpha(float alpha, float mapAlpha) {
|
||||
return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold));
|
||||
}
|
||||
|
||||
const vec3 DEFAULT_SPECULAR = vec3(0.1);
|
||||
const float DEFAULT_SHININESS = 10;
|
||||
|
||||
void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) {
|
||||
if (alpha != glowIntensity) {
|
||||
discard;
|
||||
}
|
||||
_fragColor0 = vec4(diffuse.rgb, alpha);
|
||||
_fragColor1 = vec4(bestFitNormal(normal), 1.0);
|
||||
_fragColor2 = vec4(specular, shininess / 128.0);
|
||||
}
|
||||
|
||||
void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) {
|
||||
if (alpha != glowIntensity) {
|
||||
discard;
|
||||
}
|
||||
|
||||
_fragColor0 = vec4(diffuse.rgb, alpha);
|
||||
//_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
|
||||
_fragColor1 = vec4(bestFitNormal(normal), 0.5);
|
||||
_fragColor2 = vec4(emissive, shininess / 128.0);
|
||||
}
|
||||
|
||||
void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess) {
|
||||
if (alpha <= alphaThreshold) {
|
||||
discard;
|
||||
}
|
||||
|
||||
_fragColor0 = vec4(diffuse.rgb, alpha);
|
||||
// _fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
|
||||
// _fragColor2 = vec4(specular, shininess / 128.0);
|
||||
}
|
||||
|
||||
<@endif@>
|
|
@ -470,9 +470,10 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
|
|||
|
||||
OctreeElementPointer element;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance,
|
||||
result.face, result.surfaceNormal,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
if (result.intersects && intersectedEntity) {
|
||||
result.entityID = intersectedEntity->getEntityItemID();
|
||||
result.properties = intersectedEntity->getProperties();
|
||||
|
|
|
@ -87,10 +87,10 @@ public:
|
|||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||
|
||||
signals:
|
||||
void mousePressOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mousePressOffEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mouseMoveOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mouseReleaseOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mouseMoveOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
||||
void mouseReleaseOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId);
|
||||
|
||||
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
|
|
|
@ -54,8 +54,9 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
|
|||
};
|
||||
|
||||
bool RenderableLightEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const {
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const {
|
||||
|
||||
// TODO: consider if this is really what we want to do. We've made it so that "lights are pickable" is a global state
|
||||
// this is probably reasonable since there's typically only one tree you'd be picking on at a time. Technically we could
|
||||
|
|
|
@ -26,7 +26,8 @@ public:
|
|||
virtual void render(RenderArgs* args);
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
|
||||
SIMPLE_RENDERABLE();
|
||||
|
|
|
@ -366,9 +366,9 @@ EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlag
|
|||
return properties;
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const {
|
||||
glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const {
|
||||
if (!_model) {
|
||||
return true;
|
||||
}
|
||||
|
@ -376,7 +376,8 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
// << precisionPicking;
|
||||
|
||||
QString extraInfo;
|
||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking);
|
||||
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance,
|
||||
face, surfaceNormal, extraInfo, precisionPicking);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
||||
|
|
|
@ -55,8 +55,9 @@ public:
|
|||
virtual void render(RenderArgs* args);
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
|
||||
Model* getModel(EntityTreeRenderer* renderer);
|
||||
|
||||
|
|
|
@ -211,9 +211,12 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
|
|||
_vertices.clear();
|
||||
|
||||
// build vertices from particle positions and radiuses
|
||||
const glm::vec3 up = frustum->getUp();
|
||||
const glm::vec3 right = frustum->getRight();
|
||||
glm::vec3 frustumPosition = frustum->getPosition();
|
||||
for (auto&& particle : particleDetails) {
|
||||
glm::vec3 particleDirection = particle.position - frustumPosition;
|
||||
glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), particleDirection));
|
||||
glm::vec3 up = glm::normalize(glm::cross(right, particleDirection));
|
||||
|
||||
glm::vec3 upOffset = up * particle.radius;
|
||||
glm::vec3 rightOffset = right * particle.radius;
|
||||
// generate corners of quad aligned to face the camera.
|
||||
|
|
|
@ -348,13 +348,10 @@ public:
|
|||
const PolyVox::SimpleVolume<uint8_t>* _vol = nullptr;
|
||||
};
|
||||
|
||||
bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
bool& keepSearching,
|
||||
OctreeElementPointer& element,
|
||||
float& distance, BoxFace& face,
|
||||
void** intersectedObject,
|
||||
bool precisionPicking) const
|
||||
bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const
|
||||
{
|
||||
// TODO -- correctly pick against marching-cube generated meshes
|
||||
if (!precisionPicking) {
|
||||
|
@ -392,7 +389,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
|
|||
|
||||
float voxelDistance;
|
||||
|
||||
bool hit = voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), voxelDistance, face);
|
||||
bool hit = voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), voxelDistance, face, surfaceNormal);
|
||||
|
||||
glm::vec4 voxelIntersectionPoint = glm::vec4(glm::vec3(originInVoxel) + glm::vec3(directionInVoxel) * voxelDistance, 1.0);
|
||||
glm::vec4 intersectionPoint = vtwMatrix * voxelIntersectionPoint;
|
||||
|
|
|
@ -64,8 +64,9 @@ public:
|
|||
void render(RenderArgs* args);
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
|
||||
virtual void setVoxelData(QByteArray voxelData);
|
||||
virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize);
|
||||
|
|
|
@ -206,7 +206,8 @@ public:
|
|||
|
||||
virtual bool supportsDetailedRayIntersection() const { return false; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const { return true; }
|
||||
|
||||
// attributes applicable to all entity types
|
||||
|
|
|
@ -91,8 +91,15 @@ CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE),
|
|||
CONSTRUCT_PROPERTY(maxParticles, ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES),
|
||||
CONSTRUCT_PROPERTY(lifespan, ParticleEffectEntityItem::DEFAULT_LIFESPAN),
|
||||
CONSTRUCT_PROPERTY(emitRate, ParticleEffectEntityItem::DEFAULT_EMIT_RATE),
|
||||
CONSTRUCT_PROPERTY(emitVelocity, ParticleEffectEntityItem::DEFAULT_EMIT_VELOCITY),
|
||||
CONSTRUCT_PROPERTY(velocitySpread, ParticleEffectEntityItem::DEFAULT_VELOCITY_SPREAD),
|
||||
CONSTRUCT_PROPERTY(emitSpeed, ParticleEffectEntityItem::DEFAULT_EMIT_SPEED),
|
||||
CONSTRUCT_PROPERTY(speedSpread, ParticleEffectEntityItem::DEFAULT_SPEED_SPREAD),
|
||||
CONSTRUCT_PROPERTY(emitOrientation, ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION),
|
||||
CONSTRUCT_PROPERTY(emitDimensions, ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS),
|
||||
CONSTRUCT_PROPERTY(emitRadiusStart, ParticleEffectEntityItem::DEFAULT_EMIT_RADIUS_START),
|
||||
CONSTRUCT_PROPERTY(polarStart, ParticleEffectEntityItem::DEFAULT_POLAR_START),
|
||||
CONSTRUCT_PROPERTY(polarFinish, ParticleEffectEntityItem::DEFAULT_POLAR_FINISH),
|
||||
CONSTRUCT_PROPERTY(azimuthStart, ParticleEffectEntityItem::DEFAULT_AZIMUTH_START),
|
||||
CONSTRUCT_PROPERTY(azimuthFinish, ParticleEffectEntityItem::DEFAULT_AZIMUTH_FINISH),
|
||||
CONSTRUCT_PROPERTY(emitAcceleration, ParticleEffectEntityItem::DEFAULT_EMIT_ACCELERATION),
|
||||
CONSTRUCT_PROPERTY(accelerationSpread, ParticleEffectEntityItem::DEFAULT_ACCELERATION_SPREAD),
|
||||
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
|
||||
|
@ -375,8 +382,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_MAX_PARTICLES, maxParticles);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LIFESPAN, lifespan);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_RATE, emitRate);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_VELOCITY, emitVelocity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VELOCITY_SPREAD, velocitySpread);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_SPEED, emitSpeed);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SPEED_SPREAD, speedSpread);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_ORIENTATION, emitOrientation);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_DIMENSIONS, emitDimensions);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_RADIUS_START, emitRadiusStart);
|
||||
CHECK_PROPERTY_CHANGE(PROP_POLAR_START, polarStart);
|
||||
CHECK_PROPERTY_CHANGE(PROP_POLAR_FINISH, polarFinish);
|
||||
CHECK_PROPERTY_CHANGE(PROP_AZIMUTH_START, azimuthStart);
|
||||
CHECK_PROPERTY_CHANGE(PROP_AZIMUTH_FINISH, azimuthFinish);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_ACCELERATION, emitAcceleration);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ACCELERATION_SPREAD, accelerationSpread);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius);
|
||||
|
@ -478,8 +492,15 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_PARTICLES, maxParticles);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFESPAN, lifespan);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RATE, emitRate);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_VELOCITY, emitVelocity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VELOCITY_SPREAD, velocitySpread);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_SPEED, emitSpeed);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPEED_SPREAD, speedSpread);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ORIENTATION, emitOrientation);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_DIMENSIONS, emitDimensions);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RADIUS_START, emitRadiusStart);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_START, polarStart);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_FINISH, polarFinish);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_START, azimuthStart);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_FINISH, azimuthFinish);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ACCELERATION, emitAcceleration);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACCELERATION_SPREAD, accelerationSpread);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_RADIUS, particleRadius);
|
||||
|
@ -579,8 +600,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths);
|
||||
}
|
||||
|
||||
//COPY_PROPERTY_TO_QSCRIPTVALUE(simulationOwner); // TODO: expose this for JSON saves?
|
||||
|
||||
// Sitting properties support
|
||||
if (!skipDefaults) {
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
|
@ -678,8 +697,15 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, float, setMaxParticles);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitRate, float, setEmitRate);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitVelocity, glmVec3, setEmitVelocity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(velocitySpread, glmVec3, setVelocitySpread);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitSpeed, float, setEmitSpeed);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(speedSpread, float, setSpeedSpread);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitOrientation, glmQuat, setEmitOrientation);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitDimensions, glmVec3, setEmitDimensions);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitRadiusStart, float, setEmitRadiusStart);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(polarStart, float, setPolarStart);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(polarFinish, float, setPolarFinish);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(azimuthStart, float, setAzimuthStart);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(azimuthFinish, float, setAzimuthFinish);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(emitAcceleration, glmVec3, setEmitAcceleration);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(accelerationSpread, glmVec3, setAccelerationSpread);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(particleRadius, float, setParticleRadius);
|
||||
|
@ -828,8 +854,15 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
ADD_PROPERTY_TO_MAP(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32);
|
||||
ADD_PROPERTY_TO_MAP(PROP_LIFESPAN, Lifespan, lifespan, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EMIT_RATE, EmitRate, emitRate, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EMIT_VELOCITY, EmitVelocity, emitVelocity, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_VELOCITY_SPREAD, VelocitySpread, velocitySpread, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_POLAR_START, EmitPolarStart, polarStart, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
|
||||
|
@ -1080,8 +1113,15 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, properties.getMaxParticles());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, properties.getLifespan());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, properties.getEmitRate());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_VELOCITY, properties.getEmitVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VELOCITY_SPREAD, properties.getVelocitySpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_SPEED, properties.getEmitSpeed());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SPEED_SPREAD, properties.getSpeedSpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, properties.getEmitOrientation());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, properties.getEmitDimensions());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, properties.getEmitRadiusStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_POLAR_START, properties.getPolarStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, properties.getPolarFinish());
|
||||
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, properties.getAzimuthStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, properties.getAzimuthFinish());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, properties.getEmitAcceleration());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, properties.getAccelerationSpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, properties.getParticleRadius());
|
||||
|
@ -1364,8 +1404,15 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, float, setMaxParticles);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFESPAN, float, setLifespan);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_VELOCITY, glm::vec3, setEmitVelocity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY_SPREAD, glm::vec3, setVelocitySpread);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_SPEED, float, setEmitSpeed);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPEED_SPREAD, float, setSpeedSpread);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ORIENTATION, glm::quat, setEmitOrientation);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_START, float, setPolarStart);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_FINISH, float, setPolarFinish);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_START, float, setAzimuthStart);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_FINISH, float, setAzimuthFinish);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
|
@ -1513,8 +1560,15 @@ void EntityItemProperties::markAllChanged() {
|
|||
_maxParticlesChanged = true;
|
||||
_lifespanChanged = true;
|
||||
_emitRateChanged = true;
|
||||
_emitVelocityChanged = true;
|
||||
_velocitySpreadChanged = true;
|
||||
_emitSpeedChanged = true;
|
||||
_speedSpreadChanged = true;
|
||||
_emitOrientationChanged = true;
|
||||
_emitDimensionsChanged = true;
|
||||
_emitRadiusStartChanged = true;
|
||||
_polarStartChanged = true;
|
||||
_polarFinishChanged = true;
|
||||
_azimuthStartChanged = true;
|
||||
_azimuthFinishChanged = true;
|
||||
_emitAccelerationChanged = true;
|
||||
_accelerationSpreadChanged = true;
|
||||
_particleRadiusChanged = true;
|
||||
|
|
|
@ -141,10 +141,17 @@ public:
|
|||
DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32);
|
||||
DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float);
|
||||
DEFINE_PROPERTY(PROP_EMIT_RATE, EmitRate, emitRate, float);
|
||||
DEFINE_PROPERTY_REF(PROP_EMIT_VELOCITY, EmitVelocity, emitVelocity, glm::vec3);
|
||||
DEFINE_PROPERTY_REF(PROP_VELOCITY_SPREAD, VelocitySpread, velocitySpread, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, float);
|
||||
DEFINE_PROPERTY(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, float);
|
||||
DEFINE_PROPERTY_REF(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat);
|
||||
DEFINE_PROPERTY_REF(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float);
|
||||
DEFINE_PROPERTY(PROP_POLAR_START, PolarStart, polarStart, float);
|
||||
DEFINE_PROPERTY(PROP_POLAR_FINISH, PolarFinish, polarFinish, float);
|
||||
DEFINE_PROPERTY(PROP_AZIMUTH_START, AzimuthStart, azimuthStart, float);
|
||||
DEFINE_PROPERTY(PROP_AZIMUTH_FINISH, AzimuthFinish, azimuthFinish, float);
|
||||
DEFINE_PROPERTY_REF(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3);
|
||||
DEFINE_PROPERTY_REF(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
|
||||
DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float);
|
||||
DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float);
|
||||
|
@ -344,7 +351,15 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MaxParticles, maxParticles, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifespan, lifespan, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitRate, emitRate, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitVelocity, emitVelocity, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitSpeed, emitSpeed, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SpeedSpread, speedSpread, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitOrientation, emitOrientation, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitDimensions, emitDimensions, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitRadiusStart, emitRadiusStart, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, PolarStart, polarStart, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, PolarFinish, polarFinish, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AzimuthStart, azimuthStart, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AzimuthFinish, azimuthFinish, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitAcceleration, emitAcceleration, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AccelerationSpread, accelerationSpread, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, "");
|
||||
|
|
|
@ -44,6 +44,14 @@
|
|||
} \
|
||||
}
|
||||
|
||||
#define SKIP_ENTITY_PROPERTY(P,T) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
T fromBuffer; \
|
||||
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer); \
|
||||
dataAt += bytes; \
|
||||
bytesRead += bytes; \
|
||||
}
|
||||
|
||||
#define DECODE_GROUP_PROPERTY_HAS_CHANGED(P,N) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
set##N##Changed(true); \
|
||||
|
|
|
@ -71,7 +71,7 @@ enum EntityPropertyList {
|
|||
PROP_MAX_PARTICLES,
|
||||
PROP_LIFESPAN,
|
||||
PROP_EMIT_RATE,
|
||||
PROP_EMIT_VELOCITY,
|
||||
PROP_EMIT_SPEED,
|
||||
PROP_EMIT_STRENGTH,
|
||||
PROP_EMIT_ACCELERATION,
|
||||
PROP_PARTICLE_RADIUS,
|
||||
|
@ -111,7 +111,7 @@ enum EntityPropertyList {
|
|||
PROP_STROKE_WIDTHS,
|
||||
|
||||
// used by particles
|
||||
PROP_VELOCITY_SPREAD,
|
||||
PROP_SPEED_SPREAD,
|
||||
PROP_ACCELERATION_SPREAD,
|
||||
|
||||
PROP_X_N_NEIGHBOR_ID, // used by PolyVox
|
||||
|
@ -135,6 +135,13 @@ enum EntityPropertyList {
|
|||
PROP_ALPHA_SPREAD,
|
||||
PROP_ALPHA_START,
|
||||
PROP_ALPHA_FINISH,
|
||||
PROP_EMIT_ORIENTATION,
|
||||
PROP_EMIT_DIMENSIONS,
|
||||
PROP_EMIT_RADIUS_START,
|
||||
PROP_POLAR_START,
|
||||
PROP_POLAR_FINISH,
|
||||
PROP_AZIMUTH_START,
|
||||
PROP_AZIMUTH_FINISH,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||
|
@ -169,7 +176,7 @@ enum EntityPropertyList {
|
|||
PROP_ATMOSPHERE_CENTER = PROP_MAX_PARTICLES,
|
||||
PROP_ATMOSPHERE_INNER_RADIUS = PROP_LIFESPAN,
|
||||
PROP_ATMOSPHERE_OUTER_RADIUS = PROP_EMIT_RATE,
|
||||
PROP_ATMOSPHERE_MIE_SCATTERING = PROP_EMIT_VELOCITY,
|
||||
PROP_ATMOSPHERE_MIE_SCATTERING = PROP_EMIT_SPEED,
|
||||
PROP_ATMOSPHERE_RAYLEIGH_SCATTERING = PROP_EMIT_STRENGTH,
|
||||
PROP_ATMOSPHERE_SCATTERING_WAVELENGTHS = PROP_EMIT_ACCELERATION,
|
||||
PROP_ATMOSPHERE_HAS_STARS = PROP_PARTICLE_RADIUS,
|
||||
|
|
|
@ -297,7 +297,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
|
|||
OctreeElementPointer element;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
result.surfaceNormal, (void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
if (result.intersects && intersectedEntity) {
|
||||
result.entityID = intersectedEntity->getEntityItemID();
|
||||
|
@ -393,6 +393,9 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c
|
|||
|
||||
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
|
||||
obj.setProperty("intersection", intersection);
|
||||
|
||||
QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal);
|
||||
obj.setProperty("surfaceNormal", surfaceNormal);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -426,6 +429,10 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
if (intersection.isValid()) {
|
||||
vec3FromScriptValue(intersection, value.intersection);
|
||||
}
|
||||
QScriptValue surfaceNormal = object.property("surfaceNormal");
|
||||
if (surfaceNormal.isValid()) {
|
||||
vec3FromScriptValue(surfaceNormal, value.surfaceNormal);
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
float distance;
|
||||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
EntityItemPointer entity;
|
||||
};
|
||||
|
||||
|
|
|
@ -492,9 +492,9 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3
|
|||
return false;
|
||||
}
|
||||
|
||||
bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
|
||||
bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching,
|
||||
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
|
||||
|
||||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
int entityNumber = 0;
|
||||
|
@ -503,9 +503,10 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
AABox entityBox = entity->getAABox();
|
||||
float localDistance;
|
||||
BoxFace localFace;
|
||||
glm::vec3 localSurfaceNormal;
|
||||
|
||||
// if the ray doesn't intersect with our cube, we can stop searching!
|
||||
if (!entityBox.findRayIntersection(origin, direction, localDistance, localFace)) {
|
||||
if (!entityBox.findRayIntersection(origin, direction, localDistance, localFace, localSurfaceNormal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -526,16 +527,18 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
|
||||
// we can use the AABox's ray intersection by mapping our origin and direction into the entity frame
|
||||
// and testing intersection there.
|
||||
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) {
|
||||
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance,
|
||||
localFace, localSurfaceNormal)) {
|
||||
if (localDistance < distance) {
|
||||
// now ask the entity if we actually intersect
|
||||
if (entity->supportsDetailedRayIntersection()) {
|
||||
if (entity->findDetailedRayIntersection(origin, direction, keepSearching, element, localDistance,
|
||||
localFace, intersectedObject, precisionPicking)) {
|
||||
localFace, localSurfaceNormal, intersectedObject, precisionPicking)) {
|
||||
|
||||
if (localDistance < distance) {
|
||||
distance = localDistance;
|
||||
face = localFace;
|
||||
surfaceNormal = localSurfaceNormal;
|
||||
*intersectedObject = (void*)entity.get();
|
||||
somethingIntersected = true;
|
||||
}
|
||||
|
@ -545,6 +548,7 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
if (localDistance < distance) {
|
||||
distance = localDistance;
|
||||
face = localFace;
|
||||
surfaceNormal = localSurfaceNormal;
|
||||
*intersectedObject = (void*)entity.get();
|
||||
somethingIntersected = true;
|
||||
}
|
||||
|
|
|
@ -143,7 +143,8 @@ public:
|
|||
|
||||
virtual bool canRayIntersect() const { return hasEntities(); }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
|
||||
|
||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
|
|
|
@ -64,8 +64,9 @@ class LineEntityItem : public EntityItem {
|
|||
// never have a ray intersection pick a LineEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const { return false; }
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const { return false; }
|
||||
|
||||
virtual void debugDump() const;
|
||||
static const float DEFAULT_LINE_WIDTH;
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
#include "EntityScriptingInterface.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
const glm::vec3 X_AXIS = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 Z_AXIS = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 };
|
||||
const xColor ParticleEffectEntityItem::DEFAULT_COLOR_SPREAD = { 0, 0, 0 };
|
||||
const float ParticleEffectEntityItem::DEFAULT_ALPHA = 1.0f;
|
||||
|
@ -55,8 +58,15 @@ const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FPS = 30.0f;
|
|||
const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000;
|
||||
const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f;
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_VELOCITY(0.0f, 5.0f, 0.0f);
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_VELOCITY_SPREAD(3.0f, 0.0f, 3.0f);
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_SPEED_SPREAD = 1.0f;
|
||||
const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, X_AXIS); // Vertical
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = glm::vec3(0.0f, 0.0f, 0.0f); // Emit from point
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_RADIUS_START = 1.0f; // Emit from surface (when emitDimensions > 0)
|
||||
const float ParticleEffectEntityItem::DEFAULT_POLAR_START = 0.0f; // Emit along z-axis
|
||||
const float ParticleEffectEntityItem::DEFAULT_POLAR_FINISH = 0.0f; // ""
|
||||
const float ParticleEffectEntityItem::DEFAULT_AZIMUTH_START = -PI; // Emit full circumference (when polarFinish > 0)
|
||||
const float ParticleEffectEntityItem::DEFAULT_AZIMUTH_FINISH = PI; // ""
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_ACCELERATION(0.0f, -9.8f, 0.0f);
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_ACCELERATION_SPREAD(0.0f, 0.0f, 0.0f);
|
||||
const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f;
|
||||
|
@ -104,13 +114,24 @@ ParticleEffectEntityItem::~ParticleEffectEntityItem() {
|
|||
}
|
||||
|
||||
|
||||
void ParticleEffectEntityItem::setEmitVelocity(const glm::vec3& emitVelocity) {
|
||||
_emitVelocity = emitVelocity;
|
||||
void ParticleEffectEntityItem::setEmitSpeed(float emitSpeed) {
|
||||
_emitSpeed = emitSpeed;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setVelocitySpread(const glm::vec3& velocitySpread) {
|
||||
_velocitySpread = velocitySpread;
|
||||
void ParticleEffectEntityItem::setSpeedSpread(float speedSpread) {
|
||||
_speedSpread = speedSpread;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setEmitOrientation(const glm::quat& emitOrientation) {
|
||||
_emitOrientation = emitOrientation;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
|
||||
void ParticleEffectEntityItem::setEmitDimensions(const glm::vec3& emitDimensions) {
|
||||
_emitDimensions = emitDimensions;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
|
@ -126,23 +147,18 @@ void ParticleEffectEntityItem::setAccelerationSpread(const glm::vec3& accelerati
|
|||
|
||||
void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
||||
const float time = _lifespan * 1.1f; // add 10% extra time to account for incremental timer accumulation error
|
||||
|
||||
float maxVelocityX = fabsf(_velocity.x) + _velocitySpread.x;
|
||||
float maxAccelerationX = fabsf(_acceleration.x) + _accelerationSpread.x;
|
||||
float maxXDistance = (maxVelocityX * time) + (0.5f * maxAccelerationX * time * time);
|
||||
|
||||
float maxVelocityY = fabsf(_velocity.y) + _velocitySpread.y;
|
||||
float maxAccelerationY = fabsf(_acceleration.y) + _accelerationSpread.y;
|
||||
float maxYDistance = (maxVelocityY * time) + (0.5f * maxAccelerationY * time * time);
|
||||
|
||||
float maxVelocityZ = fabsf(_velocity.z) + _velocitySpread.z;
|
||||
float maxAccelerationZ = fabsf(_acceleration.z) + _accelerationSpread.z;
|
||||
float maxZDistance = (maxVelocityZ * time) + (0.5f * maxAccelerationZ * time * time);
|
||||
|
||||
float maxDistance = std::max(maxXDistance, std::max(maxYDistance, maxZDistance));
|
||||
|
||||
|
||||
glm::vec3 velocity = _emitSpeed * (_emitOrientation * Z_AXIS);
|
||||
glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Z_AXIS);
|
||||
|
||||
glm::vec3 maxVelocity = glm::abs(velocity) + velocitySpread;
|
||||
glm::vec3 maxAccleration = glm::abs(_acceleration) + _accelerationSpread;
|
||||
glm::vec3 maxDistance = 0.5f * _emitDimensions + time * maxVelocity + (0.5f * time * time) * maxAccleration;
|
||||
|
||||
float maxDistanceValue = std::max(maxDistance.x, std::max(maxDistance.y, maxDistance.z));
|
||||
|
||||
//times 2 because dimensions are diameters not radii
|
||||
glm::vec3 dims(2.0f * maxDistance);
|
||||
glm::vec3 dims(2.0f * maxDistanceValue);
|
||||
EntityItem::setDimensions(dims);
|
||||
}
|
||||
|
||||
|
@ -161,8 +177,15 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifespan, getLifespan);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRate, getEmitRate);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitVelocity, getEmitVelocity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocitySpread, getVelocitySpread);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitSpeed, getEmitSpeed);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(speedSpread, getSpeedSpread);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitOrientation, getEmitOrientation);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitDimensions, getEmitDimensions);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRadiusStart, getEmitRadiusStart);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(polarStart, getPolarStart);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(polarFinish, getPolarFinish);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(azimuthStart, getAzimuthStart);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(azimuthFinish, getAzimuthFinish);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitAcceleration, getEmitAcceleration);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(accelerationSpread, getAccelerationSpread);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius);
|
||||
|
@ -194,8 +217,15 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRate, setEmitRate);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitVelocity, setEmitVelocity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocitySpread, setVelocitySpread);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitSpeed, setEmitSpeed);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(speedSpread, setSpeedSpread);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitOrientation, setEmitOrientation);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitDimensions, setEmitDimensions);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRadiusStart, setEmitRadiusStart);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(polarStart, setPolarStart);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(polarFinish, setPolarFinish);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(azimuthStart, setAzimuthStart);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(azimuthFinish, setAzimuthFinish);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitAcceleration, setEmitAcceleration);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(accelerationSpread, setAccelerationSpread);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius);
|
||||
|
@ -258,20 +288,25 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
|||
READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_VELOCITY, glm::vec3, setEmitVelocity);
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER) {
|
||||
// OLD PROP_EMIT_VELOCITY FAKEOUT
|
||||
SKIP_ENTITY_PROPERTY(PROP_EMIT_SPEED, glm::vec3);
|
||||
}
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_MODIFICATIONS) {
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread);
|
||||
READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY_SPREAD, glm::vec3, setVelocitySpread);
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER) {
|
||||
// OLD PROP_VELOCITY_SPREAD FAKEOUT
|
||||
SKIP_ENTITY_PROPERTY(PROP_SPEED_SPREAD, glm::vec3);
|
||||
}
|
||||
} else {
|
||||
// EMIT_STRENGTH FAKEOUT
|
||||
READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
// LOCAL_GRAVITY FAKEOUT
|
||||
READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
// ACTUALLY PARTICLE RADIUS
|
||||
// OLD PROP_EMIT_ACCELERATION FAKEOUT
|
||||
SKIP_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float);
|
||||
// OLD PROP_ACCELERATION_SPREAD FAKEOUT
|
||||
SKIP_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float);
|
||||
READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
||||
}
|
||||
|
@ -292,6 +327,18 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
|||
READ_ENTITY_PROPERTY(PROP_ALPHA_FINISH, float, setAlphaFinish);
|
||||
}
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER) {
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_SPEED, float, setEmitSpeed);
|
||||
READ_ENTITY_PROPERTY(PROP_SPEED_SPREAD, float, setSpeedSpread);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, glm::quat, setEmitOrientation);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart);
|
||||
READ_ENTITY_PROPERTY(PROP_POLAR_START, float, setPolarStart);
|
||||
READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish);
|
||||
READ_ENTITY_PROPERTY(PROP_AZIMUTH_START, float, setAzimuthStart);
|
||||
READ_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, float, setAzimuthFinish);
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
@ -309,12 +356,10 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
|
|||
requestedProperties += PROP_MAX_PARTICLES;
|
||||
requestedProperties += PROP_LIFESPAN;
|
||||
requestedProperties += PROP_EMIT_RATE;
|
||||
requestedProperties += PROP_EMIT_VELOCITY;
|
||||
requestedProperties += PROP_EMIT_ACCELERATION;
|
||||
requestedProperties += PROP_ACCELERATION_SPREAD;
|
||||
requestedProperties += PROP_PARTICLE_RADIUS;
|
||||
requestedProperties += PROP_TEXTURES;
|
||||
requestedProperties += PROP_VELOCITY_SPREAD;
|
||||
requestedProperties += PROP_RADIUS_SPREAD;
|
||||
requestedProperties += PROP_RADIUS_START;
|
||||
requestedProperties += PROP_RADIUS_FINISH;
|
||||
|
@ -325,6 +370,15 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
|
|||
requestedProperties += PROP_ALPHA_SPREAD;
|
||||
requestedProperties += PROP_ALPHA_START;
|
||||
requestedProperties += PROP_ALPHA_FINISH;
|
||||
requestedProperties += PROP_EMIT_SPEED;
|
||||
requestedProperties += PROP_SPEED_SPREAD;
|
||||
requestedProperties += PROP_EMIT_ORIENTATION;
|
||||
requestedProperties += PROP_EMIT_DIMENSIONS;
|
||||
requestedProperties += PROP_EMIT_RADIUS_START;
|
||||
requestedProperties += PROP_POLAR_START;
|
||||
requestedProperties += PROP_POLAR_FINISH;
|
||||
requestedProperties += PROP_AZIMUTH_START;
|
||||
requestedProperties += PROP_AZIMUTH_FINISH;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
@ -347,12 +401,10 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
|
|||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, getLifespan());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, getEmitRate());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_VELOCITY, getEmitVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, getEmitAcceleration());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, getAccelerationSpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, getParticleRadius());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VELOCITY_SPREAD, getVelocitySpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, getRadiusSpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, getRadiusStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, getRadiusFinish());
|
||||
|
@ -363,6 +415,15 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
|
|||
APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, getAlphaSpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, getAlphaStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, getAlphaFinish());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_SPEED, getEmitSpeed());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SPEED_SPREAD, getSpeedSpread());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, getEmitOrientation());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, getEmitDimensions());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, getEmitRadiusStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_POLAR_START, getPolarStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish());
|
||||
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart());
|
||||
APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish());
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::isAnimatingSomething() const {
|
||||
|
@ -614,29 +675,73 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) {
|
|||
_radiusMiddles[i] =_particleRadius;
|
||||
_radiusFinishes[i] = getRadiusFinish();
|
||||
} else {
|
||||
float spreadMultiplier = 1.0f + (2.0f * randFloat() - 1) * _radiusSpread / _particleRadius;
|
||||
float spreadMultiplier = 1.0f + (2.0f * randFloat() - 1.0f) * _radiusSpread / _particleRadius;
|
||||
_radiusStarts[i] = spreadMultiplier * getRadiusStart();
|
||||
_radiusMiddles[i] = spreadMultiplier * _particleRadius;
|
||||
_radiusFinishes[i] = spreadMultiplier * getRadiusFinish();
|
||||
}
|
||||
updateRadius(i, 0.0f);
|
||||
|
||||
// Velocity and acceleration
|
||||
glm::vec3 spreadOffset;
|
||||
spreadOffset.x = -_velocitySpread.x + randFloat() * (_velocitySpread.x * 2.0f);
|
||||
spreadOffset.y = -_velocitySpread.y + randFloat() * (_velocitySpread.y * 2.0f);
|
||||
spreadOffset.z = -_velocitySpread.z + randFloat() * (_velocitySpread.z * 2.0f);
|
||||
// Position, velocity, and acceleration
|
||||
if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) {
|
||||
// Emit along z-axis from position
|
||||
_particlePositions[i] = getPosition();
|
||||
_particleVelocities[i] =
|
||||
(_emitSpeed + (2.0f * randFloat() - 1.0f) * _speedSpread) * (_emitOrientation * Z_AXIS);
|
||||
_particleAccelerations[i] = _emitAcceleration + (2.0f * randFloat() - 1.0f) * _accelerationSpread;
|
||||
|
||||
// set initial conditions
|
||||
_particlePositions[i] = getPosition();
|
||||
_particleVelocities[i] = _emitVelocity + spreadOffset;
|
||||
|
||||
spreadOffset.x = -_accelerationSpread.x + randFloat() * (_accelerationSpread.x * 2.0f);
|
||||
spreadOffset.y = -_accelerationSpread.y + randFloat() * (_accelerationSpread.y * 2.0f);
|
||||
spreadOffset.z = -_accelerationSpread.z + randFloat() * (_accelerationSpread.z * 2.0f);
|
||||
|
||||
_particleAccelerations[i] = _emitAcceleration + spreadOffset;
|
||||
} else {
|
||||
// Emit around point or from ellipsoid
|
||||
// - Distribute directions evenly around point
|
||||
// - Distribute points relatively evenly over ellipsoid surface
|
||||
// - Distribute points relatively evenly within ellipsoid volume
|
||||
|
||||
float elevationMinZ = sin(PI_OVER_TWO - _polarFinish);
|
||||
float elevationMaxZ = sin(PI_OVER_TWO - _polarStart);
|
||||
float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat());
|
||||
|
||||
float azimuth;
|
||||
if (_azimuthFinish >= _azimuthStart) {
|
||||
azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat();
|
||||
} else {
|
||||
azimuth = _azimuthStart + (2.0f * PI + _azimuthFinish - _azimuthStart) * randFloat();
|
||||
}
|
||||
|
||||
glm::vec3 emitDirection;
|
||||
|
||||
if (_emitDimensions == glm::vec3()) {
|
||||
// Point
|
||||
emitDirection = glm::angleAxis(PI_OVER_TWO - elevation, X_AXIS) * Z_AXIS;
|
||||
emitDirection = glm::angleAxis(azimuth, Z_AXIS) * emitDirection;
|
||||
|
||||
_particlePositions[i] = getPosition();
|
||||
} else {
|
||||
// Ellipsoid
|
||||
float radiusScale = 1.0f;
|
||||
if (_emitRadiusStart < 1.0f) {
|
||||
float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center
|
||||
float randRadius = emitRadiusStart + (1.0f - emitRadiusStart) * randFloat();
|
||||
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
|
||||
}
|
||||
|
||||
glm::vec3 radiuses = radiusScale * 0.5f * _emitDimensions;
|
||||
float x = radiuses.x * glm::cos(elevation) * glm::cos(azimuth);
|
||||
float y = radiuses.y * glm::cos(elevation) * glm::sin(azimuth);
|
||||
float z = radiuses.z * glm::sin(elevation);
|
||||
glm::vec3 emitPosition = glm::vec3(x, y, z);
|
||||
emitDirection = glm::normalize(glm::vec3(
|
||||
radiuses.x > 0.0f ? x / (radiuses.x * radiuses.x) : 0.0f,
|
||||
radiuses.y > 0.0f ? y / (radiuses.y * radiuses.y) : 0.0f,
|
||||
radiuses.z > 0.0f ? z / (radiuses.z * radiuses.z) : 0.0f
|
||||
));
|
||||
|
||||
_particlePositions[i] = getPosition() + _emitOrientation * emitPosition;
|
||||
}
|
||||
|
||||
_particleVelocities[i] =
|
||||
(_emitSpeed + (2.0f * randFloat() - 1.0f) * _speedSpread) * (_emitOrientation * emitDirection);
|
||||
_particleAccelerations[i] = _emitAcceleration + (2.0f * randFloat() - 1.0f) * _accelerationSpread;
|
||||
}
|
||||
integrateParticle(i, timeLeftInFrame);
|
||||
extendBounds(_particlePositions[i]);
|
||||
|
||||
|
|
|
@ -128,13 +128,41 @@ public:
|
|||
void setEmitRate(float emitRate) { _emitRate = emitRate; }
|
||||
float getEmitRate() const { return _emitRate; }
|
||||
|
||||
static const glm::vec3 DEFAULT_EMIT_VELOCITY;
|
||||
void setEmitVelocity(const glm::vec3& emitVelocity);
|
||||
const glm::vec3& getEmitVelocity() const { return _emitVelocity; }
|
||||
|
||||
static const glm::vec3 DEFAULT_VELOCITY_SPREAD;
|
||||
void setVelocitySpread(const glm::vec3& velocitySpread);
|
||||
const glm::vec3& getVelocitySpread() const { return _velocitySpread; }
|
||||
static const float DEFAULT_EMIT_SPEED;
|
||||
void setEmitSpeed(float emitSpeed);
|
||||
float getEmitSpeed() const { return _emitSpeed; }
|
||||
|
||||
static const float DEFAULT_SPEED_SPREAD;
|
||||
void setSpeedSpread(float speedSpread);
|
||||
float getSpeedSpread() const { return _speedSpread; }
|
||||
|
||||
static const glm::quat DEFAULT_EMIT_ORIENTATION;
|
||||
void setEmitOrientation(const glm::quat& emitOrientation);
|
||||
const glm::quat& getEmitOrientation() const { return _emitOrientation; }
|
||||
|
||||
static const glm::vec3 DEFAULT_EMIT_DIMENSIONS;
|
||||
void setEmitDimensions(const glm::vec3& emitDimensions);
|
||||
const glm::vec3& getEmitDimensions() const { return _emitDimensions; }
|
||||
|
||||
static const float DEFAULT_EMIT_RADIUS_START;
|
||||
void setEmitRadiusStart(float emitRadiusStart) { _emitRadiusStart = emitRadiusStart; }
|
||||
float getEmitRadiusStart() const { return _emitRadiusStart; }
|
||||
|
||||
static const float DEFAULT_POLAR_START;
|
||||
void setPolarStart(float polarStart) { _polarStart = polarStart; }
|
||||
float getPolarStart() const { return _polarStart; }
|
||||
|
||||
static const float DEFAULT_POLAR_FINISH;
|
||||
void setPolarFinish(float polarFinish) { _polarFinish = polarFinish; }
|
||||
float getPolarFinish() const { return _polarFinish; }
|
||||
|
||||
static const float DEFAULT_AZIMUTH_START;
|
||||
void setAzimuthStart(float azimuthStart) { _azimuthStart = azimuthStart; }
|
||||
float getAzimuthStart() const { return _azimuthStart; }
|
||||
|
||||
static const float DEFAULT_AZIMUTH_FINISH;
|
||||
void setAzimuthFinish(float azimuthFinish) { _azimuthFinish = azimuthFinish; }
|
||||
float getAzimuthFinish() const { return _azimuthFinish; }
|
||||
|
||||
static const glm::vec3 DEFAULT_EMIT_ACCELERATION;
|
||||
void setEmitAcceleration(const glm::vec3& emitAcceleration);
|
||||
|
@ -202,8 +230,15 @@ protected:
|
|||
quint32 _maxParticles = DEFAULT_MAX_PARTICLES;
|
||||
float _lifespan = DEFAULT_LIFESPAN;
|
||||
float _emitRate = DEFAULT_EMIT_RATE;
|
||||
glm::vec3 _emitVelocity = DEFAULT_EMIT_VELOCITY;
|
||||
glm::vec3 _velocitySpread = DEFAULT_VELOCITY_SPREAD;
|
||||
float _emitSpeed = DEFAULT_EMIT_SPEED;
|
||||
float _speedSpread = DEFAULT_SPEED_SPREAD;
|
||||
glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION;
|
||||
glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS;
|
||||
float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START;
|
||||
float _polarStart = DEFAULT_POLAR_START;
|
||||
float _polarFinish = DEFAULT_POLAR_FINISH;
|
||||
float _azimuthStart = DEFAULT_AZIMUTH_START;
|
||||
float _azimuthFinish = DEFAULT_AZIMUTH_FINISH;
|
||||
glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION;
|
||||
glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD;
|
||||
float _particleRadius = DEFAULT_PARTICLE_RADIUS;
|
||||
|
|
|
@ -73,8 +73,9 @@ class PolyLineEntityItem : public EntityItem {
|
|||
// never have a ray intersection pick a PolyLineEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const { return false; }
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const { return false;}
|
||||
|
||||
virtual void debugDump() const;
|
||||
static const float DEFAULT_LINE_WIDTH;
|
||||
|
|
|
@ -44,8 +44,9 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
// never have a ray intersection pick a PolyVoxEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const { return false; }
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const { return false; }
|
||||
|
||||
virtual void debugDump() const;
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBi
|
|||
|
||||
bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element,
|
||||
float& distance, BoxFace& face,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const {
|
||||
// determine the ray in the frame of the entity transformed from a unit sphere
|
||||
glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix();
|
||||
|
@ -111,6 +111,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons
|
|||
// then translate back to work coordinates
|
||||
glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f));
|
||||
distance = glm::distance(origin, hitAt);
|
||||
surfaceNormal = glm::normalize(hitAt - getCenterPosition());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -54,8 +54,9 @@ public:
|
|||
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
|
||||
virtual void debugDump() const;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue