mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 01:36:56 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into red
This commit is contained in:
commit
6fdf1e6cf0
69 changed files with 981 additions and 598 deletions
|
@ -25,6 +25,11 @@ macro(GENERATE_INSTALLERS)
|
||||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME})
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME})
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
# include CMake module that will install compiler system libraries
|
||||||
|
# so that we have msvcr120 and msvcp120 installed with targets
|
||||||
|
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR})
|
||||||
|
include(InstallRequiredSystemLibraries)
|
||||||
|
|
||||||
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
|
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
|
||||||
|
|
||||||
# install and reference the Add/Remove icon
|
# install and reference the Add/Remove icon
|
||||||
|
|
|
@ -12,24 +12,17 @@
|
||||||
macro(install_beside_console)
|
macro(install_beside_console)
|
||||||
if (WIN32 OR APPLE)
|
if (WIN32 OR APPLE)
|
||||||
# install this component beside the installed server-console executable
|
# install this component beside the installed server-console executable
|
||||||
if (APPLE)
|
|
||||||
set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents")
|
|
||||||
set(COMPONENT_DESTINATION "${CONSOLE_APP_CONTENTS}/MacOS/Components.app/Contents/MacOS")
|
|
||||||
else ()
|
|
||||||
set(COMPONENT_DESTINATION ${CONSOLE_INSTALL_DIR})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
install(
|
install(
|
||||||
TARGETS ${TARGET_NAME}
|
TARGETS ${TARGET_NAME}
|
||||||
RUNTIME DESTINATION ${COMPONENT_DESTINATION}
|
RUNTIME DESTINATION ${COMPONENT_INSTALL_DIR}
|
||||||
COMPONENT ${SERVER_COMPONENT}
|
COMPONENT ${SERVER_COMPONENT}
|
||||||
)
|
)
|
||||||
else ()
|
else ()
|
||||||
# setup install of executable and things copied by fixup/windeployqt
|
# setup install of executable and things copied by fixup/windeployqt
|
||||||
install(
|
install(
|
||||||
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
|
||||||
DESTINATION ${COMPONENT_DESTINATION}
|
DESTINATION ${COMPONENT_INSTALL_DIR}
|
||||||
COMPONENT ${SERVER_COMPONENT}
|
COMPONENT ${SERVER_COMPONENT}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,33 +32,42 @@ macro(install_beside_console)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (TARGET_NAME STREQUAL domain-server)
|
if (TARGET_NAME STREQUAL domain-server)
|
||||||
if (APPLE)
|
|
||||||
set(RESOURCES_DESTINATION ${COMPONENT_DESTINATION})
|
|
||||||
else ()
|
|
||||||
set(RESOURCES_DESTINATION ${CONSOLE_INSTALL_DIR})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# install the resources folder for the domain-server where its executable will be
|
# install the resources folder for the domain-server where its executable will be
|
||||||
install(
|
install(
|
||||||
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/resources
|
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||||
DESTINATION ${RESOURCES_DESTINATION}
|
DESTINATION ${COMPONENT_INSTALL_DIR}
|
||||||
USE_SOURCE_PERMISSIONS
|
USE_SOURCE_PERMISSIONS
|
||||||
COMPONENT ${SERVER_COMPONENT}
|
COMPONENT ${SERVER_COMPONENT}
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
# during the install phase, call fixup to drop the shared libraries for these components in the right place
|
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
|
||||||
set(EXECUTABLE_NEEDING_FIXUP "\${CMAKE_INSTALL_PREFIX}/${COMPONENT_DESTINATION}/${TARGET_NAME}")
|
|
||||||
|
if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
|
||||||
|
message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\
|
||||||
|
It is required to produce a relocatable interface application.\
|
||||||
|
Check that the environment variable QT_DIR points to your Qt installation.\
|
||||||
|
")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# during the install phase, call macdeployqt to drop the shared libraries for these components in the right place
|
||||||
|
set(COMPONENTS_BUNDLE_PATH "\${CMAKE_INSTALL_PREFIX}/${COMPONENT_APP_PATH}")
|
||||||
|
string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${COMPONENTS_BUNDLE_PATH})
|
||||||
|
|
||||||
|
set(EXECUTABLE_NEEDING_FIXUP "\${CMAKE_INSTALL_PREFIX}/${COMPONENT_INSTALL_DIR}/${TARGET_NAME}")
|
||||||
|
string(REPLACE " " "\\ " ESCAPED_EXECUTABLE_NAME ${EXECUTABLE_NEEDING_FIXUP})
|
||||||
|
|
||||||
install(CODE "
|
install(CODE "
|
||||||
include(BundleUtilities)
|
execute_process(COMMAND ${MACDEPLOYQT_COMMAND} ${ESCAPED_BUNDLE_NAME} -verbose=2 -executable=${ESCAPED_EXECUTABLE_NAME})"
|
||||||
fixup_bundle(\"${EXECUTABLE_NEEDING_FIXUP}\" \"\" \"${FIXUP_LIBS}\")
|
COMPONENT ${SERVER_COMPONENT}
|
||||||
" COMPONENT ${SERVER_COMPONENT})
|
)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# set variables used by manual ssleay library copy
|
# set variables used by manual ssleay library copy
|
||||||
set(TARGET_INSTALL_DIR ${COMPONENT_DESTINATION})
|
set(TARGET_INSTALL_DIR ${COMPONENT_INSTALL_DIR})
|
||||||
set(TARGET_INSTALL_COMPONENT ${SERVER_COMPONENT})
|
set(TARGET_INSTALL_COMPONENT ${SERVER_COMPONENT})
|
||||||
manually_install_ssl_eay()
|
manually_install_ssl_eay()
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,24 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
|
|
||||||
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||||
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||||
|
|
||||||
|
set(CONSOLE_EXEC_NAME "Server Console.app")
|
||||||
|
set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}")
|
||||||
|
|
||||||
|
set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents")
|
||||||
|
set(COMPONENT_APP_PATH "${CONSOLE_APP_CONTENTS}/MacOS/Components.app")
|
||||||
|
set(COMPONENT_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/MacOS")
|
||||||
|
|
||||||
|
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
|
||||||
|
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
|
||||||
else ()
|
else ()
|
||||||
set(CONSOLE_INSTALL_DIR ".")
|
if (WIN32)
|
||||||
|
set(CONSOLE_INSTALL_DIR "server-console")
|
||||||
|
else ()
|
||||||
|
set(CONSOLE_INSTALL_DIR ".")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(COMPONENT_INSTALL_DIR ".")
|
||||||
set(INTERFACE_INSTALL_DIR ".")
|
set(INTERFACE_INSTALL_DIR ".")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
@ -88,15 +104,6 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
set(LAUNCH_NOW_REG_KEY "LaunchAfterInstall")
|
set(LAUNCH_NOW_REG_KEY "LaunchAfterInstall")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
|
|
||||||
set(CONSOLE_EXEC_NAME "Server Console.app")
|
|
||||||
set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}")
|
|
||||||
|
|
||||||
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
|
|
||||||
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# setup component categories for installer
|
# setup component categories for installer
|
||||||
set(DDE_COMPONENT dde)
|
set(DDE_COMPONENT dde)
|
||||||
set(CLIENT_COMPONENT client)
|
set(CLIENT_COMPONENT client)
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SM_SHORTCUT_NAME@")
|
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SM_SHORTCUT_NAME@")
|
||||||
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
|
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
|
||||||
|
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
|
||||||
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SM_SHORTCUT_NAME@")
|
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SM_SHORTCUT_NAME@")
|
||||||
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
|
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
|
||||||
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
|
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
|
||||||
|
|
|
@ -685,6 +685,31 @@ FunctionEnd
|
||||||
;Installer Sections
|
;Installer Sections
|
||||||
|
|
||||||
Section "-Core installation"
|
Section "-Core installation"
|
||||||
|
;The following delete blocks are temporary and can be removed once users who had the initial installer have updated
|
||||||
|
|
||||||
|
;Delete any server-console files installed before it was placed in sub-folder
|
||||||
|
Delete "$INSTDIR\server-console.exe"
|
||||||
|
RMDir /r "$INSTDIR\locales"
|
||||||
|
RMDir /r "$INSTDIR\resources\app"
|
||||||
|
Delete "$INSTDIR\resources\atom.asar"
|
||||||
|
Delete "$INSTDIR\build-info.json"
|
||||||
|
Delete "$INSTDIR\content_resources_200_percent.pak"
|
||||||
|
Delete "$INSTDIR\content_shell.pak"
|
||||||
|
Delete "$INSTDIR\LICENSE"
|
||||||
|
Delete "$INSTDIR\LICENSES.chromium.html"
|
||||||
|
Delete "$INSTDIR\natives_blob.bin"
|
||||||
|
Delete "$INSTDIR\node.dll"
|
||||||
|
Delete "$INSTDIR\pdf.dll"
|
||||||
|
Delete "$INSTDIR\snapshot_blob.bin"
|
||||||
|
Delete "$INSTDIR\ui_resources_200_percent.pak"
|
||||||
|
Delete "$INSTDIR\vccorlib120.dll"
|
||||||
|
Delete "$INSTDIR\version"
|
||||||
|
Delete "$INSTDIR\xinput1_3.dll"
|
||||||
|
|
||||||
|
; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console)
|
||||||
|
RMDir /r "$INSTDIR\Interface"
|
||||||
|
Delete "$INSTDIR\vcredist_x64.exe"
|
||||||
|
|
||||||
;Use the entire tree produced by the INSTALL target. Keep the
|
;Use the entire tree produced by the INSTALL target. Keep the
|
||||||
;list of directories here in sync with the RMDir commands below.
|
;list of directories here in sync with the RMDir commands below.
|
||||||
SetOutPath "$INSTDIR"
|
SetOutPath "$INSTDIR"
|
||||||
|
@ -769,7 +794,7 @@ Section "-Core installation"
|
||||||
; Conditional handling for server console shortcut
|
; Conditional handling for server console shortcut
|
||||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||||
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" \
|
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" \
|
||||||
"$INSTDIR\@CONSOLE_WIN_EXEC_NAME@"
|
"$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
||||||
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\@UNINSTALLER_NAME@"
|
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\@UNINSTALLER_NAME@"
|
||||||
|
@ -958,7 +983,7 @@ Function HandlePostInstallOptions
|
||||||
${NSD_GetState} $DesktopServerCheckbox $DesktopServerState
|
${NSD_GetState} $DesktopServerCheckbox $DesktopServerState
|
||||||
|
|
||||||
${If} $DesktopServerState == ${BST_CHECKED}
|
${If} $DesktopServerState == ${BST_CHECKED}
|
||||||
CreateShortCut "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_WIN_EXEC_NAME@"
|
CreateShortCut "$DESKTOP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
|
||||||
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
|
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
|
||||||
${Else}
|
${Else}
|
||||||
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
|
!insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
|
||||||
|
@ -968,7 +993,7 @@ Function HandlePostInstallOptions
|
||||||
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
|
${NSD_GetState} $ServerStartupCheckbox $ServerStartupState
|
||||||
|
|
||||||
${If} $ServerStartupState == ${BST_CHECKED}
|
${If} $ServerStartupState == ${BST_CHECKED}
|
||||||
CreateShortCut "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_WIN_EXEC_NAME@"
|
CreateShortCut "$SMSTARTUP\@CONSOLE_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
|
||||||
|
|
||||||
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
|
!insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES
|
||||||
${Else}
|
${Else}
|
||||||
|
@ -1012,7 +1037,7 @@ Function HandlePostInstallOptions
|
||||||
; both launches use the explorer trick in case the user has elevated permissions for the installer
|
; both launches use the explorer trick in case the user has elevated permissions for the installer
|
||||||
; it won't be possible to use this approach if either application should be launched with a command line param
|
; it won't be possible to use this approach if either application should be launched with a command line param
|
||||||
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@}
|
||||||
Exec '"$WINDIR\explorer.exe" "$INSTDIR\@CONSOLE_WIN_EXEC_NAME@"'
|
Exec '"$WINDIR\explorer.exe" "$INSTDIR\\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"'
|
||||||
${Else}
|
${Else}
|
||||||
Exec '"$WINDIR\explorer.exe" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"'
|
Exec '"$WINDIR\explorer.exe" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"'
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
|
|
@ -1148,13 +1148,22 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
|
||||||
return nodeJson;
|
return nodeJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment";
|
QDir pathForAssignmentScriptsDirectory() {
|
||||||
|
static const QString SCRIPTS_DIRECTORY_NAME = "/scripts/";
|
||||||
|
|
||||||
|
QDir directory(ServerPathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME);
|
||||||
|
if (!directory.exists()) {
|
||||||
|
directory.mkpath(".");
|
||||||
|
qInfo() << "Created path to " << directory.path();
|
||||||
|
}
|
||||||
|
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
QString pathForAssignmentScript(const QUuid& assignmentUUID) {
|
QString pathForAssignmentScript(const QUuid& assignmentUUID) {
|
||||||
QString newPath { ServerPathUtils::getDataDirectory() + "/" + QString(ASSIGNMENT_SCRIPT_HOST_LOCATION) };
|
QDir directory = pathForAssignmentScriptsDirectory();
|
||||||
newPath += "/scripts/";
|
|
||||||
// append the UUID for this script as the new filename, remove the curly braces
|
// append the UUID for this script as the new filename, remove the curly braces
|
||||||
newPath += uuidStringWithoutCurlyBraces(assignmentUUID);
|
return directory.absoluteFilePath(uuidStringWithoutCurlyBraces(assignmentUUID));
|
||||||
return newPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString URI_OAUTH = "/oauth";
|
const QString URI_OAUTH = "/oauth";
|
||||||
|
@ -1162,7 +1171,6 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
const QString JSON_MIME_TYPE = "application/json";
|
const QString JSON_MIME_TYPE = "application/json";
|
||||||
|
|
||||||
const QString URI_ASSIGNMENT = "/assignment";
|
const QString URI_ASSIGNMENT = "/assignment";
|
||||||
const QString URI_ASSIGNMENT_SCRIPTS = URI_ASSIGNMENT + "/scripts";
|
|
||||||
const QString URI_NODES = "/nodes";
|
const QString URI_NODES = "/nodes";
|
||||||
const QString URI_SETTINGS = "/settings";
|
const QString URI_SETTINGS = "/settings";
|
||||||
|
|
||||||
|
@ -1203,13 +1211,14 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
||||||
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
||||||
// via correct URL for the script so the client can download
|
// via correct URL for the script so the client can download
|
||||||
|
QFile scriptFile(pathForAssignmentScript(matchingAssignment->getUUID()));
|
||||||
QUrl scriptURL = url;
|
|
||||||
scriptURL.setPath(URI_ASSIGNMENT + "/scripts/"
|
if (scriptFile.exists() && scriptFile.open(QIODevice::ReadOnly)) {
|
||||||
+ uuidStringWithoutCurlyBraces(matchingAssignment->getUUID()));
|
connection->respond(HTTPConnection::StatusCode200, scriptFile.readAll(), "application/javascript");
|
||||||
|
} else {
|
||||||
// have the HTTPManager serve the appropriate script file
|
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||||
return _httpManager.handleHTTPRequest(connection, scriptURL, true);
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// request not handled
|
// request not handled
|
||||||
|
|
|
@ -165,6 +165,7 @@ var STATE_EQUIP_SPRING = 16;
|
||||||
// "collidesWith" is specified by comma-separated list of group names
|
// "collidesWith" is specified by comma-separated list of group names
|
||||||
// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar
|
// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar
|
||||||
var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar";
|
var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar";
|
||||||
|
var COLLIDES_WITH_WHILE_MULTI_GRABBED = "dynamic";
|
||||||
|
|
||||||
function stateToName(state) {
|
function stateToName(state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
@ -1636,6 +1637,13 @@ function MyController(hand) {
|
||||||
"collidesWith": COLLIDES_WITH_WHILE_GRABBED
|
"collidesWith": COLLIDES_WITH_WHILE_GRABBED
|
||||||
};
|
};
|
||||||
Entities.editEntity(entityID, whileHeldProperties);
|
Entities.editEntity(entityID, whileHeldProperties);
|
||||||
|
} else if (data["refCount"] > 1) {
|
||||||
|
// if an object is being grabbed by more than one person (or the same person twice, but nevermind), switch
|
||||||
|
// the collision groups so that it wont collide with "other" avatars. This avoids a situation where two
|
||||||
|
// people are holding something and one of them will be able (if the other releases at the right time) to
|
||||||
|
// bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in
|
||||||
|
// the collision mask hinges on who the physics simulation owner is.
|
||||||
|
Entities.editEntity(entityID, {"collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED});
|
||||||
}
|
}
|
||||||
|
|
||||||
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
||||||
|
@ -1746,4 +1754,4 @@ function cleanup() {
|
||||||
|
|
||||||
}
|
}
|
||||||
Script.scriptEnding.connect(cleanup);
|
Script.scriptEnding.connect(cleanup);
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
|
|
|
@ -105,8 +105,7 @@
|
||||||
alphaStart: 0.5,
|
alphaStart: 0.5,
|
||||||
alphaFinish: 0.5,
|
alphaFinish: 0.5,
|
||||||
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
|
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
|
||||||
emitterShouldTrail: false,
|
emitterShouldTrail: false
|
||||||
lifetime: 1000
|
|
||||||
}
|
}
|
||||||
this.beam = Entities.addEntity(props);
|
this.beam = Entities.addEntity(props);
|
||||||
|
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
//
|
|
||||||
// weatherBox.js
|
|
||||||
// examples/widgets/weatherBox
|
|
||||||
//
|
|
||||||
// Created by Eric Levin on 1/20/16.
|
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// This script creates a weatherBox widget which (in final version) will display the current weather
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
Script.include("../libraries/utils.js");
|
|
||||||
var weatherBox, boxDimensions;
|
|
||||||
var orientation = Camera.getOrientation();
|
|
||||||
orientation = Quat.safeEulerAngles(orientation);
|
|
||||||
orientation.x = 0;
|
|
||||||
orientation = Quat.fromVec3Degrees(orientation);
|
|
||||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
|
|
||||||
|
|
||||||
var emitters = [];
|
|
||||||
var spheres = [];
|
|
||||||
|
|
||||||
createWeatherBox(center);
|
|
||||||
center.y += boxDimensions.y / 2;
|
|
||||||
createCloud(center);
|
|
||||||
for (var i = 0; i < 7; i++) {
|
|
||||||
createLightningStrike(center)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLightningStrike(basePosition) {
|
|
||||||
var normal = Vec3.subtract(position, MyAvatar.position);
|
|
||||||
normal.y = 0;
|
|
||||||
var textureURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/textures/lightning.png"
|
|
||||||
var linePoints = [];
|
|
||||||
var normals = [];
|
|
||||||
var strokeWidths = [];
|
|
||||||
var strokeWidth = 0.02
|
|
||||||
var currentPointPosition = {x: 0, y: 0, z: 0};
|
|
||||||
for (var i = 0; i < 8; i++) {
|
|
||||||
linePoints.push(currentPointPosition);
|
|
||||||
currentPointPosition = Vec3.sum(currentPointPosition, {x: randFloat(-0.05, 0.05), y: randFloat(-0.1, -0.05), z: randFloat(-0.05, 0.05)});
|
|
||||||
linePoints.push(currentPointPosition);
|
|
||||||
normals.push(normal);
|
|
||||||
normals.push(normal);
|
|
||||||
strokeWidth -= 0.002;
|
|
||||||
strokeWidths.push(strokeWidth);
|
|
||||||
strokeWidths.push(strokeWidth);
|
|
||||||
}
|
|
||||||
var position = Vec3.sum(basePosition, {x: randFloat(-boxDimensions.x/20, boxDimensions.x/20), y: 0, z: randFloat(-boxDimensions.x/20, boxDimensions.x/20)});
|
|
||||||
var bolt = Entities.addEntity({
|
|
||||||
type: "PolyLine",
|
|
||||||
textures: textureURL,
|
|
||||||
position: position,
|
|
||||||
dimensions: {
|
|
||||||
x: 10,
|
|
||||||
y: 10,
|
|
||||||
z: 10
|
|
||||||
},
|
|
||||||
linePoints: linePoints,
|
|
||||||
normals: normals,
|
|
||||||
strokeWidths: strokeWidths,
|
|
||||||
lifetime: randFloat(0.01, 0.2)
|
|
||||||
});
|
|
||||||
|
|
||||||
Script.setTimeout(function() {
|
|
||||||
createLightningStrike(position)
|
|
||||||
}, randInt(500, 5000));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function createWeatherBox(position) {
|
|
||||||
var naturalDimensions = {
|
|
||||||
x: 1.11,
|
|
||||||
y: 1.3,
|
|
||||||
z: 1.11
|
|
||||||
};
|
|
||||||
boxDimensions = Vec3.multiply(naturalDimensions, 0.7);
|
|
||||||
var modelURL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/weatherBox.fbx"
|
|
||||||
weatherBox = Entities.addEntity({
|
|
||||||
type: "Model",
|
|
||||||
name: "Weather Box",
|
|
||||||
modelURL: modelURL,
|
|
||||||
position: position,
|
|
||||||
dimensions: boxDimensions
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function createCloud(position) {
|
|
||||||
var props = {
|
|
||||||
"type": "ParticleEffect",
|
|
||||||
"position": position,
|
|
||||||
"isEmitting": true,
|
|
||||||
"maxParticles": 10000,
|
|
||||||
"lifespan": 3,
|
|
||||||
"emitRate": 2000,
|
|
||||||
"emitSpeed": 0.025,
|
|
||||||
"speedSpread": 0,
|
|
||||||
"emitDimensions": {
|
|
||||||
x: boxDimensions.x,
|
|
||||||
y: boxDimensions.x,
|
|
||||||
z: 0.1
|
|
||||||
},
|
|
||||||
"emitRadiusStart": 1,
|
|
||||||
"polarStart": 0,
|
|
||||||
"polarFinish": 1.570796012878418,
|
|
||||||
"azimuthStart": -3.1415927410125732,
|
|
||||||
"azimuthFinish": 3.1415927410125732,
|
|
||||||
"emitAcceleration": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
"z": 0
|
|
||||||
},
|
|
||||||
"particleRadius": 0.04,
|
|
||||||
"radiusSpread": 0.02,
|
|
||||||
"radiusStart": 0.04,
|
|
||||||
radiusFinish: 0.04,
|
|
||||||
"colorStart": {
|
|
||||||
"red": 40,
|
|
||||||
"green": 40,
|
|
||||||
"blue": 100
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
red: 40,
|
|
||||||
green: 40,
|
|
||||||
blue: 100
|
|
||||||
},
|
|
||||||
"colorFinish": {
|
|
||||||
"red": 40,
|
|
||||||
"green": 40,
|
|
||||||
"blue": 100
|
|
||||||
},
|
|
||||||
"alpha": 1,
|
|
||||||
"alphaSpread": 0,
|
|
||||||
"alphaStart": 0.3,
|
|
||||||
"alphaFinish": 0,
|
|
||||||
"emitterShouldTrail": true,
|
|
||||||
"textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
var oceanEmitter = Entities.addEntity(props);
|
|
||||||
emitters.push(oceanEmitter);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
emitters.forEach(function(emitter) {
|
|
||||||
Entities.deleteEntity(emitter);
|
|
||||||
});
|
|
||||||
spheres.forEach(function(sphere) {
|
|
||||||
Entities.deleteEntity(sphere);
|
|
||||||
});
|
|
||||||
Entities.deleteEntity(weatherBox);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Script.scriptEnding.connect(cleanup);
|
|
|
@ -246,7 +246,8 @@
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -261,7 +262,8 @@
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -275,7 +277,8 @@
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -289,7 +292,8 @@
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -303,7 +307,8 @@
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -317,7 +322,8 @@
|
||||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -331,7 +337,8 @@
|
||||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
{ "var": "isTurningLeft", "state": "turnLeft" },
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -345,7 +352,8 @@
|
||||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
{ "var": "isTurningRight", "state": "turnRight" },
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
{ "var": "isAway", "state": "awayIntro" }
|
{ "var": "isAway", "state": "awayIntro" },
|
||||||
|
{ "var": "isFlying", "state": "fly" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -371,6 +379,14 @@
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{ "var": "awayOutroOnDone", "state": "idle" }
|
{ "var": "awayOutroOnDone", "state": "idle" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fly",
|
||||||
|
"interpTarget": 6,
|
||||||
|
"interpDuration": 6,
|
||||||
|
"transitions": [
|
||||||
|
{ "var": "isNotFlying", "state": "idle" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -657,6 +673,18 @@
|
||||||
"loopFlag": false
|
"loopFlag": false
|
||||||
},
|
},
|
||||||
"children": []
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fly",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/fly.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 80.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,9 +95,11 @@
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <PhysicsEngine.h>
|
#include <PhysicsEngine.h>
|
||||||
|
#include <PhysicsHelpers.h>
|
||||||
#include <plugins/PluginContainer.h>
|
#include <plugins/PluginContainer.h>
|
||||||
#include <plugins/PluginManager.h>
|
#include <plugins/PluginManager.h>
|
||||||
#include <RenderableWebEntityItem.h>
|
#include <RenderableWebEntityItem.h>
|
||||||
|
#include <RenderShadowTask.h>
|
||||||
#include <RenderDeferredTask.h>
|
#include <RenderDeferredTask.h>
|
||||||
#include <ResourceCache.h>
|
#include <ResourceCache.h>
|
||||||
#include <RenderScriptingInterface.h>
|
#include <RenderScriptingInterface.h>
|
||||||
|
@ -672,7 +674,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
initializeGL();
|
initializeGL();
|
||||||
|
|
||||||
// Start rendering
|
// Start rendering
|
||||||
_renderEngine->addTask(make_shared<RenderDeferredTask>(LODManager::shouldRender));
|
render::CullFunctor cullFunctor = LODManager::shouldRender;
|
||||||
|
_renderEngine->addTask(make_shared<RenderShadowTask>(cullFunctor));
|
||||||
|
_renderEngine->addTask(make_shared<RenderDeferredTask>(cullFunctor));
|
||||||
_renderEngine->registerScene(_main3DScene);
|
_renderEngine->registerScene(_main3DScene);
|
||||||
|
|
||||||
_offscreenContext->makeCurrent();
|
_offscreenContext->makeCurrent();
|
||||||
|
@ -3089,13 +3093,14 @@ void Application::update(float deltaTime) {
|
||||||
static VectorOfMotionStates motionStates;
|
static VectorOfMotionStates motionStates;
|
||||||
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
||||||
_physicsEngine->removeObjects(motionStates);
|
_physicsEngine->removeObjects(motionStates);
|
||||||
|
_entitySimulation.deleteObjectsRemovedFromPhysics();
|
||||||
|
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withReadLock([&] {
|
||||||
_entitySimulation.getObjectsToAddToPhysics(motionStates);
|
_entitySimulation.getObjectsToAddToPhysics(motionStates);
|
||||||
_physicsEngine->addObjects(motionStates);
|
_physicsEngine->addObjects(motionStates);
|
||||||
|
|
||||||
});
|
});
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withReadLock([&] {
|
||||||
_entitySimulation.getObjectsToChange(motionStates);
|
_entitySimulation.getObjectsToChange(motionStates);
|
||||||
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
||||||
_entitySimulation.setObjectsToChange(stillNeedChange);
|
_entitySimulation.setObjectsToChange(stillNeedChange);
|
||||||
|
@ -3125,7 +3130,7 @@ void Application::update(float deltaTime) {
|
||||||
PerformanceTimer perfTimer("havestChanges");
|
PerformanceTimer perfTimer("havestChanges");
|
||||||
if (_physicsEngine->hasOutgoingChanges()) {
|
if (_physicsEngine->hasOutgoingChanges()) {
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
_entitySimulation.handleOutgoingChanges(_physicsEngine->getOutgoingChanges(), _physicsEngine->getSessionID());
|
_entitySimulation.handleOutgoingChanges(_physicsEngine->getOutgoingChanges(), Physics::getSessionUUID());
|
||||||
avatarManager->handleOutgoingChanges(_physicsEngine->getOutgoingChanges());
|
avatarManager->handleOutgoingChanges(_physicsEngine->getOutgoingChanges());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3758,9 +3763,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
||||||
renderContext.setArgs(renderArgs);
|
renderContext.setArgs(renderArgs);
|
||||||
|
|
||||||
bool occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
bool occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||||
|
bool shadowStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugShadows);
|
||||||
bool antialiasingStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
bool antialiasingStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
||||||
bool showOwnedStatus = Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned);
|
bool showOwnedStatus = Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned);
|
||||||
renderContext.setOptions(occlusionStatus, antialiasingStatus, showOwnedStatus);
|
renderContext.setOptions(occlusionStatus, antialiasingStatus, showOwnedStatus, shadowStatus);
|
||||||
|
|
||||||
_renderEngine->setRenderContext(renderContext);
|
_renderEngine->setRenderContext(renderContext);
|
||||||
|
|
||||||
|
@ -4225,6 +4231,9 @@ bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setSessionUUID(const QUuid& sessionUUID) {
|
void Application::setSessionUUID(const QUuid& sessionUUID) {
|
||||||
|
// HACK: until we swap the library dependency order between physics and entities
|
||||||
|
// we cache the sessionID in two distinct places for physics.
|
||||||
|
Physics::setSessionUUID(sessionUUID); // TODO: remove this one
|
||||||
_physicsEngine->setSessionUUID(sessionUUID);
|
_physicsEngine->setSessionUUID(sessionUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -325,6 +325,7 @@ Menu::Menu() {
|
||||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
||||||
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugShadows);
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true);
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true);
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,7 @@ namespace MenuOption {
|
||||||
const QString CopyPath = "Copy Path to Clipboard";
|
const QString CopyPath = "Copy Path to Clipboard";
|
||||||
const QString CoupleEyelids = "Couple Eyelids";
|
const QString CoupleEyelids = "Couple Eyelids";
|
||||||
const QString CrashInterface = "Crash Interface";
|
const QString CrashInterface = "Crash Interface";
|
||||||
|
const QString DebugShadows = "Shadows";
|
||||||
const QString DebugAmbientOcclusion = "Ambient Occlusion";
|
const QString DebugAmbientOcclusion = "Ambient Occlusion";
|
||||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||||
const QString DeleteBookmark = "Delete Bookmark...";
|
const QString DeleteBookmark = "Delete Bookmark...";
|
||||||
|
|
|
@ -146,6 +146,6 @@ QUuid AvatarMotionState::getSimulatorID() const {
|
||||||
// virtual
|
// virtual
|
||||||
void AvatarMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const {
|
void AvatarMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const {
|
||||||
group = BULLET_COLLISION_GROUP_OTHER_AVATAR;
|
group = BULLET_COLLISION_GROUP_OTHER_AVATAR;
|
||||||
mask = PhysicsEngine::getCollisionMask(group);
|
mask = Physics::getDefaultCollisionMask(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,15 +221,13 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::reset(bool andReload) {
|
void MyAvatar::reset(bool andReload) {
|
||||||
|
|
||||||
if (andReload) {
|
if (andReload) {
|
||||||
qApp->setRawAvatarUpdateThreading(false);
|
qApp->setRawAvatarUpdateThreading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset dynamic state.
|
// Reset dynamic state.
|
||||||
_wasPushing = _isPushing = _isBraking = _billboardValid = false;
|
_wasPushing = _isPushing = _isBraking = _billboardValid = false;
|
||||||
_followVelocity = Vectors::ZERO;
|
_follow.deactivate();
|
||||||
_followSpeed = 0.0f;
|
|
||||||
_skeletonModel.reset();
|
_skeletonModel.reset();
|
||||||
getHead()->reset();
|
getHead()->reset();
|
||||||
_targetVelocity = glm::vec3(0.0f);
|
_targetVelocity = glm::vec3(0.0f);
|
||||||
|
@ -262,9 +260,22 @@ void MyAvatar::reset(bool andReload) {
|
||||||
|
|
||||||
void MyAvatar::update(float deltaTime) {
|
void MyAvatar::update(float deltaTime) {
|
||||||
|
|
||||||
|
// update moving average of HMD facing in xz plane.
|
||||||
|
const float HMD_FACING_TIMESCALE = 4.0f; // very slow average
|
||||||
|
float tau = deltaTime / HMD_FACING_TIMESCALE;
|
||||||
|
_hmdSensorFacingMovingAverage = lerp(_hmdSensorFacingMovingAverage, _hmdSensorFacing, tau);
|
||||||
|
|
||||||
|
#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE
|
||||||
|
glm::vec3 p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacingMovingAverage.x, 0.0f, _hmdSensorFacingMovingAverage.y));
|
||||||
|
DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f));
|
||||||
|
p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacing.x, 0.0f, _hmdSensorFacing.y));
|
||||||
|
DebugDraw::getInstance().addMarker("facing", getOrientation(), p, glm::vec4(1.0f));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_goToPending) {
|
if (_goToPending) {
|
||||||
setPosition(_goToPosition);
|
setPosition(_goToPosition);
|
||||||
setOrientation(_goToOrientation);
|
setOrientation(_goToOrientation);
|
||||||
|
_hmdSensorFacingMovingAverage = _hmdSensorFacing; // reset moving average
|
||||||
_goToPending = false;
|
_goToPending = false;
|
||||||
// updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes
|
// updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes
|
||||||
// that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so).
|
// that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so).
|
||||||
|
@ -365,46 +376,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
|
||||||
_hmdSensorMatrix = hmdSensorMatrix;
|
_hmdSensorMatrix = hmdSensorMatrix;
|
||||||
_hmdSensorPosition = extractTranslation(hmdSensorMatrix);
|
_hmdSensorPosition = extractTranslation(hmdSensorMatrix);
|
||||||
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
|
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
|
||||||
}
|
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
|
||||||
|
|
||||||
void MyAvatar::updateHMDFollowVelocity() {
|
|
||||||
// compute offset to body's target position (in sensor-frame)
|
|
||||||
auto sensorBodyMatrix = deriveBodyFromHMDSensor();
|
|
||||||
glm::vec3 offset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix);
|
|
||||||
_followOffsetDistance = glm::length(offset);
|
|
||||||
|
|
||||||
const float FOLLOW_TIMESCALE = 0.5f;
|
|
||||||
const float FOLLOW_THRESHOLD_SPEED = 0.2f;
|
|
||||||
const float FOLLOW_MIN_DISTANCE = 0.01f;
|
|
||||||
const float FOLLOW_THRESHOLD_DISTANCE = 0.2f;
|
|
||||||
const float FOLLOW_MAX_IDLE_DISTANCE = 0.1f;
|
|
||||||
|
|
||||||
bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation);
|
|
||||||
|
|
||||||
_followOffsetDistance = glm::length(offset);
|
|
||||||
if (_followOffsetDistance < FOLLOW_MIN_DISTANCE) {
|
|
||||||
// close enough
|
|
||||||
_followOffsetDistance = 0.0f;
|
|
||||||
} else {
|
|
||||||
bool avatarIsMoving = glm::length(_velocity - _followVelocity) > FOLLOW_THRESHOLD_SPEED;
|
|
||||||
bool shouldFollow = (hmdIsAtRest || avatarIsMoving) && _followOffsetDistance > FOLLOW_MAX_IDLE_DISTANCE;
|
|
||||||
|
|
||||||
glm::vec3 truncatedOffset = offset;
|
|
||||||
if (truncatedOffset.y < 0.0f) {
|
|
||||||
truncatedOffset.y = 0.0f;
|
|
||||||
}
|
|
||||||
float truncatedDistance = glm::length(truncatedOffset);
|
|
||||||
bool needsNewSpeed = truncatedDistance > FOLLOW_THRESHOLD_DISTANCE;
|
|
||||||
if (needsNewSpeed || (shouldFollow && _followSpeed == 0.0f)) {
|
|
||||||
// compute new speed
|
|
||||||
_followSpeed = _followOffsetDistance / FOLLOW_TIMESCALE;
|
|
||||||
}
|
|
||||||
if (_followSpeed > 0.0f) {
|
|
||||||
// to compute new velocity we must rotate offset into the world-frame
|
|
||||||
glm::quat sensorToWorldRotation = glm::normalize(glm::quat_cast(_sensorToWorldMatrix));
|
|
||||||
_followVelocity = _followSpeed * glm::normalize(sensorToWorldRotation * offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// best called at end of main loop, just before rendering.
|
// best called at end of main loop, just before rendering.
|
||||||
|
@ -1057,63 +1029,21 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
||||||
_characterController.setTargetVelocity(getTargetVelocity());
|
_characterController.setTargetVelocity(getTargetVelocity());
|
||||||
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
|
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
|
||||||
if (qApp->isHMDMode()) {
|
if (qApp->isHMDMode()) {
|
||||||
updateHMDFollowVelocity();
|
_follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix);
|
||||||
} else if (_followSpeed > 0.0f) {
|
} else {
|
||||||
_followVelocity = Vectors::ZERO;
|
_follow.deactivate();
|
||||||
_followSpeed = 0.0f;
|
|
||||||
}
|
}
|
||||||
_characterController.setFollowVelocity(_followVelocity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaType) {
|
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
||||||
glm::vec3 position = getPosition();
|
glm::vec3 position = getPosition();
|
||||||
glm::quat orientation = getOrientation();
|
glm::quat orientation = getOrientation();
|
||||||
_characterController.getPositionAndOrientation(position, orientation);
|
_characterController.getPositionAndOrientation(position, orientation);
|
||||||
nextAttitude(position, orientation);
|
nextAttitude(position, orientation);
|
||||||
if (_followSpeed > 0.0f) {
|
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
||||||
adjustSensorTransform();
|
|
||||||
setVelocity(_characterController.getLinearVelocity() + _followVelocity);
|
|
||||||
} else {
|
|
||||||
setVelocity(_characterController.getLinearVelocity());
|
|
||||||
}
|
|
||||||
|
|
||||||
// now that physics has adjusted our position, we can update attachements.
|
// now that physics has adjusted our position, we can update attachements.
|
||||||
Avatar::simulateAttachments(deltaType);
|
Avatar::simulateAttachments(deltaTime);
|
||||||
}
|
|
||||||
|
|
||||||
void MyAvatar::adjustSensorTransform() {
|
|
||||||
// compute blendFactor of latest hmdShift
|
|
||||||
// which we'll use to blend the rotation part
|
|
||||||
float linearDistance = _characterController.getFollowTime() * _followSpeed;
|
|
||||||
float blendFactor = linearDistance < _followOffsetDistance ? linearDistance / _followOffsetDistance : 1.0f;
|
|
||||||
|
|
||||||
auto newBodySensorMatrix = deriveBodyFromHMDSensor();
|
|
||||||
auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix;
|
|
||||||
glm::quat finalBodyRotation = glm::normalize(glm::quat_cast(worldBodyMatrix));
|
|
||||||
if (blendFactor >= 0.99f) {
|
|
||||||
// the "adjustment" is more or less complete so stop following
|
|
||||||
_followVelocity = Vectors::ZERO;
|
|
||||||
_followSpeed = 0.0f;
|
|
||||||
// and slam the body's transform anyway to eliminate any slight errors
|
|
||||||
glm::vec3 finalBodyPosition = extractTranslation(worldBodyMatrix);
|
|
||||||
nextAttitude(finalBodyPosition, finalBodyRotation);
|
|
||||||
_bodySensorMatrix = newBodySensorMatrix;
|
|
||||||
} else {
|
|
||||||
// physics already did the positional blending for us
|
|
||||||
glm::vec3 newBodyPosition = getPosition();
|
|
||||||
// but the rotational part must be done manually
|
|
||||||
glm::quat newBodyRotation = glm::normalize(safeMix(getOrientation(), finalBodyRotation, blendFactor));
|
|
||||||
nextAttitude(newBodyPosition, newBodyRotation);
|
|
||||||
|
|
||||||
// interp sensor matrix toward the desired
|
|
||||||
glm::vec3 prevPosition = extractTranslation(_bodySensorMatrix);
|
|
||||||
glm::quat prevRotation = glm::normalize(glm::quat_cast(_bodySensorMatrix));
|
|
||||||
glm::vec3 nextPosition = extractTranslation(newBodySensorMatrix);
|
|
||||||
glm::quat nextRotation = glm::normalize(glm::quat_cast(newBodySensorMatrix));
|
|
||||||
_bodySensorMatrix = createMatFromQuatAndPos(
|
|
||||||
glm::normalize(safeMix(prevRotation, nextRotation, blendFactor)),
|
|
||||||
lerp(prevPosition, nextPosition, blendFactor));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MyAvatar::getScriptedMotorFrame() const {
|
QString MyAvatar::getScriptedMotorFrame() const {
|
||||||
|
@ -1538,9 +1468,7 @@ void MyAvatar::updatePosition(float deltaTime) {
|
||||||
// rotate velocity into camera frame
|
// rotate velocity into camera frame
|
||||||
glm::quat rotation = getHead()->getCameraOrientation();
|
glm::quat rotation = getHead()->getCameraOrientation();
|
||||||
glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity;
|
glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity;
|
||||||
|
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering());
|
||||||
bool isHovering = _characterController.isHovering();
|
|
||||||
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering);
|
|
||||||
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
|
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
|
||||||
|
|
||||||
// rotate back into world-frame
|
// rotate back into world-frame
|
||||||
|
@ -1636,6 +1564,10 @@ void MyAvatar::doUpdateBillboard() {
|
||||||
sendBillboardPacket();
|
sendBillboardPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::isHovering() const {
|
||||||
|
return _characterController.isHovering();
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::increaseSize() {
|
void MyAvatar::increaseSize() {
|
||||||
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
|
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
|
||||||
_targetScale *= (1.0f + SCALING_RATIO);
|
_targetScale *= (1.0f + SCALING_RATIO);
|
||||||
|
@ -1831,3 +1763,78 @@ void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMod
|
||||||
void MyAvatar::lateUpdatePalms() {
|
void MyAvatar::lateUpdatePalms() {
|
||||||
Avatar::updatePalms();
|
Avatar::updatePalms();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const float FOLLOW_TIME = 0.5f;
|
||||||
|
|
||||||
|
void MyAvatar::FollowHelper::deactivate() {
|
||||||
|
_timeRemaining = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::FollowHelper::activate() {
|
||||||
|
// TODO: Perhaps, the follow time should be proportional to the displacement.
|
||||||
|
_timeRemaining = FOLLOW_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::FollowHelper::isActive() const {
|
||||||
|
return _timeRemaining > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MyAvatar::FollowHelper::shouldActivate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||||
|
|
||||||
|
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 4.0f);
|
||||||
|
|
||||||
|
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
|
||||||
|
if (glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float CYLINDER_TOP = 0.1f;
|
||||||
|
const float CYLINDER_BOTTOM = -0.5f;
|
||||||
|
const float CYLINDER_RADIUS = 0.15f;
|
||||||
|
|
||||||
|
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
||||||
|
glm::vec3 radialOffset(offset.x, 0.0f, offset.y);
|
||||||
|
float radialDistance = glm::length(radialOffset);
|
||||||
|
|
||||||
|
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM) || (radialDistance > CYLINDER_RADIUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) {
|
||||||
|
_desiredBodyMatrix = desiredBodyMatrix;
|
||||||
|
if (!isActive() && shouldActivate(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
|
||||||
|
activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isActive()) {
|
||||||
|
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix;
|
||||||
|
myAvatar.getCharacterController()->setFollowParameters(desiredWorldMatrix, _timeRemaining);
|
||||||
|
} else {
|
||||||
|
glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix;
|
||||||
|
myAvatar.getCharacterController()->setFollowParameters(currentWorldMatrix, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) {
|
||||||
|
if (isActive()) {
|
||||||
|
float dt = myAvatar.getCharacterController()->getFollowTime();
|
||||||
|
_timeRemaining -= dt;
|
||||||
|
|
||||||
|
// apply follow displacement to the body matrix.
|
||||||
|
glm::vec3 worldLinearDisplacement = myAvatar.getCharacterController()->getFollowLinearDisplacement();
|
||||||
|
glm::quat worldAngularDisplacement = myAvatar.getCharacterController()->getFollowAngularDisplacement();
|
||||||
|
glm::quat sensorToWorld = glmExtractRotation(myAvatar.getSensorToWorldMatrix());
|
||||||
|
glm::quat worldToSensor = glm::inverse(sensorToWorld);
|
||||||
|
|
||||||
|
glm::vec3 sensorLinearDisplacement = worldToSensor * worldLinearDisplacement;
|
||||||
|
glm::quat sensorAngularDisplacement = worldToSensor * worldAngularDisplacement * sensorToWorld;
|
||||||
|
|
||||||
|
glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix),
|
||||||
|
sensorLinearDisplacement + extractTranslation(currentBodyMatrix));
|
||||||
|
return newBodyMat;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return currentBodyMatrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,8 +96,10 @@ public:
|
||||||
const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
|
const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
|
||||||
const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
|
const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
|
||||||
const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; }
|
const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; }
|
||||||
|
const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; }
|
||||||
glm::mat4 getSensorToWorldMatrix() const;
|
glm::mat4 getSensorToWorldMatrix() const;
|
||||||
|
|
||||||
|
|
||||||
// Pass a recent sample of the HMD to the avatar.
|
// Pass a recent sample of the HMD to the avatar.
|
||||||
// This can also update the avatar's position to follow the HMD
|
// This can also update the avatar's position to follow the HMD
|
||||||
// as it moves through the world.
|
// as it moves through the world.
|
||||||
|
@ -204,10 +206,10 @@ public:
|
||||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
|
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
|
||||||
|
|
||||||
MyCharacterController* getCharacterController() { return &_characterController; }
|
MyCharacterController* getCharacterController() { return &_characterController; }
|
||||||
|
const MyCharacterController* getCharacterController() const { return &_characterController; }
|
||||||
|
|
||||||
void prepareForPhysicsSimulation();
|
void prepareForPhysicsSimulation();
|
||||||
void harvestResultsFromPhysicsSimulation(float deltaTime);
|
void harvestResultsFromPhysicsSimulation(float deltaTime);
|
||||||
void adjustSensorTransform();
|
|
||||||
|
|
||||||
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
|
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
|
||||||
void setCollisionSoundURL(const QString& url);
|
void setCollisionSoundURL(const QString& url);
|
||||||
|
@ -231,6 +233,8 @@ public:
|
||||||
glm::quat getCustomListenOrientation() { return _customListenOrientation; }
|
glm::quat getCustomListenOrientation() { return _customListenOrientation; }
|
||||||
void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; }
|
void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; }
|
||||||
|
|
||||||
|
bool isHovering() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
void decreaseSize();
|
void decreaseSize();
|
||||||
|
@ -295,11 +299,6 @@ private:
|
||||||
float scale = 1.0f, bool isSoft = false,
|
float scale = 1.0f, bool isSoft = false,
|
||||||
bool allowDuplicates = false, bool useSaved = true) override;
|
bool allowDuplicates = false, bool useSaved = true) override;
|
||||||
|
|
||||||
//void beginFollowingHMD();
|
|
||||||
//bool shouldFollowHMD() const;
|
|
||||||
//void followHMD(float deltaTime);
|
|
||||||
void updateHMDFollowVelocity();
|
|
||||||
|
|
||||||
bool cameraInsideHead() const;
|
bool cameraInsideHead() const;
|
||||||
|
|
||||||
// These are made private for MyAvatar so that you will use the "use" methods instead
|
// These are made private for MyAvatar so that you will use the "use" methods instead
|
||||||
|
@ -367,6 +366,8 @@ private:
|
||||||
glm::mat4 _hmdSensorMatrix;
|
glm::mat4 _hmdSensorMatrix;
|
||||||
glm::quat _hmdSensorOrientation;
|
glm::quat _hmdSensorOrientation;
|
||||||
glm::vec3 _hmdSensorPosition;
|
glm::vec3 _hmdSensorPosition;
|
||||||
|
glm::vec2 _hmdSensorFacing; // facing vector in xz plane
|
||||||
|
glm::vec2 _hmdSensorFacingMovingAverage { 0, 0 }; // facing vector in xz plane
|
||||||
|
|
||||||
// cache of the current body position and orientation of the avatar's body,
|
// cache of the current body position and orientation of the avatar's body,
|
||||||
// in sensor space.
|
// in sensor space.
|
||||||
|
@ -375,9 +376,18 @@ private:
|
||||||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||||
glm::mat4 _sensorToWorldMatrix;
|
glm::mat4 _sensorToWorldMatrix;
|
||||||
|
|
||||||
glm::vec3 _followVelocity { Vectors::ZERO };
|
struct FollowHelper {
|
||||||
float _followSpeed { 0.0f };
|
glm::mat4 _desiredBodyMatrix;
|
||||||
float _followOffsetDistance { 0.0f };
|
float _timeRemaining { 0.0f };
|
||||||
|
|
||||||
|
void deactivate();
|
||||||
|
void activate();
|
||||||
|
bool isActive() const;
|
||||||
|
bool shouldActivate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
|
||||||
|
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix);
|
||||||
|
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
|
||||||
|
};
|
||||||
|
FollowHelper _follow;
|
||||||
|
|
||||||
bool _goToPending;
|
bool _goToPending;
|
||||||
glm::vec3 _goToPosition;
|
glm::vec3 _goToPosition;
|
||||||
|
|
|
@ -133,7 +133,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
|
|
||||||
_rig->updateFromHandParameters(handParams, deltaTime);
|
_rig->updateFromHandParameters(handParams, deltaTime);
|
||||||
|
|
||||||
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), myAvatar->isHovering());
|
||||||
|
|
||||||
// evaluate AnimGraph animation and update jointStates.
|
// evaluate AnimGraph animation and update jointStates.
|
||||||
Model::updateRig(deltaTime, parentTransform);
|
Model::updateRig(deltaTime, parentTransform);
|
||||||
|
|
|
@ -500,7 +500,7 @@ static const std::vector<float> FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s
|
||||||
static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s
|
static const std::vector<float> BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s
|
||||||
static const std::vector<float> LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s
|
static const std::vector<float> LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s
|
||||||
|
|
||||||
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, bool isHovering) {
|
||||||
|
|
||||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||||
|
|
||||||
|
@ -568,36 +568,43 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
const float TURN_ENTER_SPEED_THRESHOLD = 0.5f; // rad/sec
|
const float TURN_ENTER_SPEED_THRESHOLD = 0.5f; // rad/sec
|
||||||
const float TURN_EXIT_SPEED_THRESHOLD = 0.2f; // rad/sec
|
const float TURN_EXIT_SPEED_THRESHOLD = 0.2f; // rad/sec
|
||||||
|
|
||||||
float moveThresh;
|
if (isHovering) {
|
||||||
if (_state != RigRole::Move) {
|
if (_desiredState != RigRole::Hover) {
|
||||||
moveThresh = MOVE_ENTER_SPEED_THRESHOLD;
|
|
||||||
} else {
|
|
||||||
moveThresh = MOVE_EXIT_SPEED_THRESHOLD;
|
|
||||||
}
|
|
||||||
|
|
||||||
float turnThresh;
|
|
||||||
if (_state != RigRole::Turn) {
|
|
||||||
turnThresh = TURN_ENTER_SPEED_THRESHOLD;
|
|
||||||
} else {
|
|
||||||
turnThresh = TURN_EXIT_SPEED_THRESHOLD;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (glm::length(localVel) > moveThresh) {
|
|
||||||
if (_desiredState != RigRole::Move) {
|
|
||||||
_desiredStateAge = 0.0f;
|
_desiredStateAge = 0.0f;
|
||||||
}
|
}
|
||||||
_desiredState = RigRole::Move;
|
_desiredState = RigRole::Hover;
|
||||||
} else {
|
} else {
|
||||||
if (fabsf(turningSpeed) > turnThresh) {
|
float moveThresh;
|
||||||
if (_desiredState != RigRole::Turn) {
|
if (_state != RigRole::Move) {
|
||||||
|
moveThresh = MOVE_ENTER_SPEED_THRESHOLD;
|
||||||
|
} else {
|
||||||
|
moveThresh = MOVE_EXIT_SPEED_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
float turnThresh;
|
||||||
|
if (_state != RigRole::Turn) {
|
||||||
|
turnThresh = TURN_ENTER_SPEED_THRESHOLD;
|
||||||
|
} else {
|
||||||
|
turnThresh = TURN_EXIT_SPEED_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glm::length(localVel) > moveThresh) {
|
||||||
|
if (_desiredState != RigRole::Move) {
|
||||||
_desiredStateAge = 0.0f;
|
_desiredStateAge = 0.0f;
|
||||||
}
|
}
|
||||||
_desiredState = RigRole::Turn;
|
_desiredState = RigRole::Move;
|
||||||
} else { // idle
|
} else {
|
||||||
if (_desiredState != RigRole::Idle) {
|
if (fabsf(turningSpeed) > turnThresh) {
|
||||||
_desiredStateAge = 0.0f;
|
if (_desiredState != RigRole::Turn) {
|
||||||
|
_desiredStateAge = 0.0f;
|
||||||
|
}
|
||||||
|
_desiredState = RigRole::Turn;
|
||||||
|
} else { // idle
|
||||||
|
if (_desiredState != RigRole::Idle) {
|
||||||
|
_desiredStateAge = 0.0f;
|
||||||
|
}
|
||||||
|
_desiredState = RigRole::Idle;
|
||||||
}
|
}
|
||||||
_desiredState = RigRole::Idle;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,6 +656,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isTurningLeft", false);
|
_animVars.set("isTurningLeft", false);
|
||||||
_animVars.set("isTurningRight", false);
|
_animVars.set("isTurningRight", false);
|
||||||
_animVars.set("isNotTurning", true);
|
_animVars.set("isNotTurning", true);
|
||||||
|
_animVars.set("isFlying", false);
|
||||||
|
_animVars.set("isNotFlying", true);
|
||||||
}
|
}
|
||||||
} else if (_state == RigRole::Turn) {
|
} else if (_state == RigRole::Turn) {
|
||||||
if (turningSpeed > 0.0f) {
|
if (turningSpeed > 0.0f) {
|
||||||
|
@ -667,7 +676,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isMovingRight", false);
|
_animVars.set("isMovingRight", false);
|
||||||
_animVars.set("isMovingLeft", false);
|
_animVars.set("isMovingLeft", false);
|
||||||
_animVars.set("isNotMoving", true);
|
_animVars.set("isNotMoving", true);
|
||||||
} else {
|
_animVars.set("isFlying", false);
|
||||||
|
_animVars.set("isNotFlying", true);
|
||||||
|
} else if (_state == RigRole::Idle ) {
|
||||||
// default anim vars to notMoving and notTurning
|
// default anim vars to notMoving and notTurning
|
||||||
_animVars.set("isMovingForward", false);
|
_animVars.set("isMovingForward", false);
|
||||||
_animVars.set("isMovingBackward", false);
|
_animVars.set("isMovingBackward", false);
|
||||||
|
@ -677,6 +688,20 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
||||||
_animVars.set("isTurningLeft", false);
|
_animVars.set("isTurningLeft", false);
|
||||||
_animVars.set("isTurningRight", false);
|
_animVars.set("isTurningRight", false);
|
||||||
_animVars.set("isNotTurning", true);
|
_animVars.set("isNotTurning", true);
|
||||||
|
_animVars.set("isFlying", false);
|
||||||
|
_animVars.set("isNotFlying", true);
|
||||||
|
} else {
|
||||||
|
// flying.
|
||||||
|
_animVars.set("isMovingForward", false);
|
||||||
|
_animVars.set("isMovingBackward", false);
|
||||||
|
_animVars.set("isMovingLeft", false);
|
||||||
|
_animVars.set("isMovingRight", false);
|
||||||
|
_animVars.set("isNotMoving", true);
|
||||||
|
_animVars.set("isTurningLeft", false);
|
||||||
|
_animVars.set("isTurningRight", false);
|
||||||
|
_animVars.set("isNotTurning", true);
|
||||||
|
_animVars.set("isFlying", true);
|
||||||
|
_animVars.set("isNotFlying", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
t += deltaTime;
|
t += deltaTime;
|
||||||
|
|
|
@ -141,7 +141,7 @@ public:
|
||||||
glm::mat4 getJointTransform(int jointIndex) const;
|
glm::mat4 getJointTransform(int jointIndex) const;
|
||||||
|
|
||||||
// Start or stop animations as needed.
|
// Start or stop animations as needed.
|
||||||
void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation);
|
void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, bool isHovering);
|
||||||
|
|
||||||
// Regardless of who started the animations or how many, update the joints.
|
// Regardless of who started the animations or how many, update the joints.
|
||||||
void updateAnimations(float deltaTime, glm::mat4 rootTransform);
|
void updateAnimations(float deltaTime, glm::mat4 rootTransform);
|
||||||
|
@ -268,7 +268,8 @@ public:
|
||||||
enum class RigRole {
|
enum class RigRole {
|
||||||
Idle = 0,
|
Idle = 0,
|
||||||
Turn,
|
Turn,
|
||||||
Move
|
Move,
|
||||||
|
Hover
|
||||||
};
|
};
|
||||||
RigRole _state { RigRole::Idle };
|
RigRole _state { RigRole::Idle };
|
||||||
RigRole _desiredState { RigRole::Idle };
|
RigRole _desiredState { RigRole::Idle };
|
||||||
|
|
|
@ -1507,6 +1507,33 @@ void EntityItem::updateCreated(uint64_t value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const {
|
||||||
|
// TODO: detect attachment status and adopt group of wearer
|
||||||
|
if (_collisionless) {
|
||||||
|
group = BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||||
|
mask = 0;
|
||||||
|
} else {
|
||||||
|
if (_dynamic) {
|
||||||
|
group = BULLET_COLLISION_GROUP_DYNAMIC;
|
||||||
|
} else if (isMoving() || hasActions()) {
|
||||||
|
group = BULLET_COLLISION_GROUP_KINEMATIC;
|
||||||
|
} else {
|
||||||
|
group = BULLET_COLLISION_GROUP_STATIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t userMask = getCollisionMask();
|
||||||
|
if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) !=
|
||||||
|
(bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
|
||||||
|
// asymmetric avatar collision mask bits
|
||||||
|
if (!getSimulatorID().isNull() && (!getSimulatorID().isNull()) && getSimulatorID() != Physics::getSessionUUID()) {
|
||||||
|
// someone else owns the simulation, so we toggle the avatar bits (swap interpretation)
|
||||||
|
userMask ^= USER_COLLISION_MASK_AVATARS | ~userMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) {
|
void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) {
|
||||||
if (wantTerseEditLogging() && (id != _simulationOwner.getID() || priority != _simulationOwner.getPriority())) {
|
if (wantTerseEditLogging() && (id != _simulationOwner.getID() || priority != _simulationOwner.getPriority())) {
|
||||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority;
|
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority;
|
||||||
|
|
|
@ -275,9 +275,10 @@ public:
|
||||||
void setCollisionless(bool value) { _collisionless = value; }
|
void setCollisionless(bool value) { _collisionless = value; }
|
||||||
|
|
||||||
uint8_t getCollisionMask() const { return _collisionMask; }
|
uint8_t getCollisionMask() const { return _collisionMask; }
|
||||||
uint8_t getFinalCollisionMask() const { return _collisionless ? 0 : _collisionMask; }
|
|
||||||
void setCollisionMask(uint8_t value) { _collisionMask = value; }
|
void setCollisionMask(uint8_t value) { _collisionMask = value; }
|
||||||
|
|
||||||
|
void computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const;
|
||||||
|
|
||||||
bool getDynamic() const { return _dynamic; }
|
bool getDynamic() const { return _dynamic; }
|
||||||
void setDynamic(bool value) { _dynamic = value; }
|
void setDynamic(bool value) { _dynamic = value; }
|
||||||
|
|
||||||
|
@ -370,8 +371,8 @@ public:
|
||||||
bool clearActions(EntitySimulation* simulation);
|
bool clearActions(EntitySimulation* simulation);
|
||||||
void setActionData(QByteArray actionData);
|
void setActionData(QByteArray actionData);
|
||||||
const QByteArray getActionData() const;
|
const QByteArray getActionData() const;
|
||||||
bool hasActions() { return !_objectActions.empty(); }
|
bool hasActions() const { return !_objectActions.empty(); }
|
||||||
QList<QUuid> getActionIDs() { return _objectActions.keys(); }
|
QList<QUuid> getActionIDs() const { return _objectActions.keys(); }
|
||||||
QVariantMap getActionArguments(const QUuid& actionID) const;
|
QVariantMap getActionArguments(const QUuid& actionID) const;
|
||||||
void deserializeActions();
|
void deserializeActions();
|
||||||
|
|
||||||
|
|
|
@ -160,8 +160,8 @@ QString EntityItemProperties::getCollisionMaskAsString() const {
|
||||||
void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) {
|
void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) {
|
||||||
QVector<QStringRef> groups = maskString.splitRef(',');
|
QVector<QStringRef> groups = maskString.splitRef(',');
|
||||||
uint8_t mask = 0x00;
|
uint8_t mask = 0x00;
|
||||||
for (auto group : groups) {
|
for (auto groupName : groups) {
|
||||||
mask |= getCollisionGroupAsBitMask(group);
|
mask |= getCollisionGroupAsBitMask(groupName);
|
||||||
}
|
}
|
||||||
_collisionMask = mask;
|
_collisionMask = mask;
|
||||||
_collisionMaskChanged = true;
|
_collisionMaskChanged = true;
|
||||||
|
|
|
@ -48,6 +48,7 @@ void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
void EntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
// remove from all internal lists except _entitiesToDelete
|
// remove from all internal lists except _entitiesToDelete
|
||||||
_mortalEntities.remove(entity);
|
_mortalEntities.remove(entity);
|
||||||
_entitiesToUpdate.remove(entity);
|
_entitiesToUpdate.remove(entity);
|
||||||
|
@ -61,6 +62,7 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
||||||
assert(entity);
|
assert(entity);
|
||||||
assert(entity->isDead());
|
assert(entity->isDead());
|
||||||
if (entity->isSimulated()) {
|
if (entity->isSimulated()) {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
entity->clearActions(this);
|
entity->clearActions(this);
|
||||||
removeEntityInternal(entity);
|
removeEntityInternal(entity);
|
||||||
_entitiesToDelete.insert(entity);
|
_entitiesToDelete.insert(entity);
|
||||||
|
@ -69,11 +71,13 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
|
||||||
|
|
||||||
void EntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
void EntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
_simpleKinematicEntities.insert(entity);
|
_simpleKinematicEntities.insert(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||||
_simpleKinematicEntities.insert(entity);
|
_simpleKinematicEntities.insert(entity);
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,6 +90,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
||||||
if (now > _nextExpiry) {
|
if (now > _nextExpiry) {
|
||||||
// only search for expired entities if we expect to find one
|
// only search for expired entities if we expect to find one
|
||||||
_nextExpiry = quint64(-1);
|
_nextExpiry = quint64(-1);
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
SetOfEntities::iterator itemItr = _mortalEntities.begin();
|
SetOfEntities::iterator itemItr = _mortalEntities.begin();
|
||||||
while (itemItr != _mortalEntities.end()) {
|
while (itemItr != _mortalEntities.end()) {
|
||||||
EntityItemPointer entity = *itemItr;
|
EntityItemPointer entity = *itemItr;
|
||||||
|
@ -108,6 +113,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
||||||
// protected
|
// protected
|
||||||
void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
||||||
PerformanceTimer perfTimer("updatingEntities");
|
PerformanceTimer perfTimer("updatingEntities");
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
SetOfEntities::iterator itemItr = _entitiesToUpdate.begin();
|
SetOfEntities::iterator itemItr = _entitiesToUpdate.begin();
|
||||||
while (itemItr != _entitiesToUpdate.end()) {
|
while (itemItr != _entitiesToUpdate.end()) {
|
||||||
EntityItemPointer entity = *itemItr;
|
EntityItemPointer entity = *itemItr;
|
||||||
|
@ -124,6 +130,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
||||||
|
|
||||||
// protected
|
// protected
|
||||||
void EntitySimulation::sortEntitiesThatMoved() {
|
void EntitySimulation::sortEntitiesThatMoved() {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
||||||
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
||||||
PerformanceTimer perfTimer("sortingEntities");
|
PerformanceTimer perfTimer("sortingEntities");
|
||||||
|
|
|
@ -23,6 +23,7 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
||||||
// has finished simulating it.
|
// has finished simulating it.
|
||||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin();
|
SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin();
|
||||||
while (itemItr != _entitiesWithSimulator.end()) {
|
while (itemItr != _entitiesWithSimulator.end()) {
|
||||||
EntityItemPointer entity = *itemItr;
|
EntityItemPointer entity = *itemItr;
|
||||||
|
@ -48,18 +49,21 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
||||||
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
EntitySimulation::addEntityInternal(entity);
|
EntitySimulation::addEntityInternal(entity);
|
||||||
if (!entity->getSimulatorID().isNull()) {
|
if (!entity->getSimulatorID().isNull()) {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
_entitiesWithSimulator.insert(entity);
|
_entitiesWithSimulator.insert(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||||
EntitySimulation::removeEntityInternal(entity);
|
EntitySimulation::removeEntityInternal(entity);
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
_entitiesWithSimulator.remove(entity);
|
_entitiesWithSimulator.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
EntitySimulation::changeEntityInternal(entity);
|
EntitySimulation::changeEntityInternal(entity);
|
||||||
if (!entity->getSimulatorID().isNull()) {
|
if (!entity->getSimulatorID().isNull()) {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
_entitiesWithSimulator.insert(entity);
|
_entitiesWithSimulator.insert(entity);
|
||||||
}
|
}
|
||||||
entity->clearDirtyFlags();
|
entity->clearDirtyFlags();
|
||||||
|
|
|
@ -1035,7 +1035,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
|
|
||||||
} else if (type.contains("transparentcolor")) { // it should be TransparentColor...
|
} else if (type.contains("transparentcolor")) { // it should be TransparentColor...
|
||||||
// THis is how Maya assign a texture that affect diffuse color AND transparency ?
|
// THis is how Maya assign a texture that affect diffuse color AND transparency ?
|
||||||
opacityTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
} else if (type.contains("bump")) {
|
} else if (type.contains("bump")) {
|
||||||
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
} else if (type.contains("normal")) {
|
} else if (type.contains("normal")) {
|
||||||
|
|
|
@ -402,7 +402,6 @@ public:
|
||||||
QHash<QString, QString> specularTextures;
|
QHash<QString, QString> specularTextures;
|
||||||
QHash<QString, QString> emissiveTextures;
|
QHash<QString, QString> emissiveTextures;
|
||||||
QHash<QString, QString> ambientTextures;
|
QHash<QString, QString> ambientTextures;
|
||||||
QHash<QString, QString> opacityTextures;
|
|
||||||
|
|
||||||
QHash<QString, FBXMaterial> _fbxMaterials;
|
QHash<QString, FBXMaterial> _fbxMaterials;
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,8 @@ void FBXReader::consolidateFBXMaterials() {
|
||||||
FBXTexture diffuseTexture;
|
FBXTexture diffuseTexture;
|
||||||
QString diffuseTextureID = diffuseTextures.value(material.materialID);
|
QString diffuseTextureID = diffuseTextures.value(material.materialID);
|
||||||
if (!diffuseTextureID.isNull()) {
|
if (!diffuseTextureID.isNull()) {
|
||||||
diffuseTexture = getTexture(diffuseTextureID);
|
diffuseTexture = getTexture(diffuseTextureID);
|
||||||
|
|
||||||
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
||||||
foreach (const QString& childTextureID, _connectionChildMap.values(diffuseTextureID)) {
|
foreach (const QString& childTextureID, _connectionChildMap.values(diffuseTextureID)) {
|
||||||
if (_textureFilenames.contains(childTextureID)) {
|
if (_textureFilenames.contains(childTextureID)) {
|
||||||
|
@ -79,16 +80,9 @@ void FBXReader::consolidateFBXMaterials() {
|
||||||
}
|
}
|
||||||
|
|
||||||
material.diffuseTexture = diffuseTexture;
|
material.diffuseTexture = diffuseTexture;
|
||||||
|
|
||||||
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
|
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
|
||||||
}
|
}
|
||||||
|
|
||||||
FBXTexture opacityTexture;
|
|
||||||
QString opacityTextureID = opacityTextures.value(material.materialID);
|
|
||||||
if (!opacityTextureID.isNull()) {
|
|
||||||
opacityTexture = getTexture(opacityTextureID);
|
|
||||||
material.opacityTexture = opacityTexture;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FBXTexture normalTexture;
|
FBXTexture normalTexture;
|
||||||
QString bumpTextureID = bumpTextures.value(material.materialID);
|
QString bumpTextureID = bumpTextures.value(material.materialID);
|
||||||
|
|
|
@ -63,10 +63,6 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) {
|
||||||
depthTexture->setSampler(Sampler(samplerDesc));
|
depthTexture->setSampler(Sampler(samplerDesc));
|
||||||
framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
|
framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
|
||||||
|
|
||||||
// Use a render buffer to allow use of the DebugDeferredBuffer Job
|
|
||||||
gpu::TexturePointer colorbuffer{gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, width)};
|
|
||||||
framebuffer->setRenderBuffer(0, colorbuffer);
|
|
||||||
|
|
||||||
return framebuffer;
|
return framebuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,8 +155,6 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u
|
||||||
material->specularTexture = textureCache->getTexture(url);
|
material->specularTexture = textureCache->getTexture(url);
|
||||||
} else if (material->emissiveTextureName == name) {
|
} else if (material->emissiveTextureName == name) {
|
||||||
material->emissiveTexture = textureCache->getTexture(url);
|
material->emissiveTexture = textureCache->getTexture(url);
|
||||||
} else if (material->opacityTextureName == name) {
|
|
||||||
material->opacityTexture = textureCache->getTexture(url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -288,16 +286,6 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const
|
||||||
|
|
||||||
material._material->setTextureMap(model::MaterialKey::DIFFUSE_MAP, diffuseMap);
|
material._material->setTextureMap(model::MaterialKey::DIFFUSE_MAP, diffuseMap);
|
||||||
}
|
}
|
||||||
if (!material.opacityTexture.filename.isEmpty()) {
|
|
||||||
networkMaterial->opacityTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.opacityTexture.filename)), DEFAULT_TEXTURE, material.opacityTexture.content);
|
|
||||||
networkMaterial->opacityTextureName = material.opacityTexture.name;
|
|
||||||
|
|
||||||
auto opacityMap = model::TextureMapPointer(new model::TextureMap());
|
|
||||||
opacityMap->setTextureSource(networkMaterial->opacityTexture->_textureSource);
|
|
||||||
opacityMap->setTextureTransform(material.opacityTexture.transform);
|
|
||||||
|
|
||||||
material._material->setTextureMap(model::MaterialKey::TRANSPARENT_MAP, opacityMap);
|
|
||||||
}
|
|
||||||
if (!material.normalTexture.filename.isEmpty()) {
|
if (!material.normalTexture.filename.isEmpty()) {
|
||||||
networkMaterial->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.normalTexture.filename)), (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE), material.normalTexture.content);
|
networkMaterial->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.normalTexture.filename)), (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE), material.normalTexture.content);
|
||||||
networkMaterial->normalTextureName = material.normalTexture.name;
|
networkMaterial->normalTextureName = material.normalTexture.name;
|
||||||
|
|
|
@ -182,8 +182,6 @@ public:
|
||||||
QSharedPointer<NetworkTexture> specularTexture;
|
QSharedPointer<NetworkTexture> specularTexture;
|
||||||
QString emissiveTextureName;
|
QString emissiveTextureName;
|
||||||
QSharedPointer<NetworkTexture> emissiveTexture;
|
QSharedPointer<NetworkTexture> emissiveTexture;
|
||||||
QString opacityTextureName;
|
|
||||||
QSharedPointer<NetworkTexture> opacityTexture;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,4 +32,54 @@ inline btQuaternion glmToBullet(const glm::quat& g) {
|
||||||
return btQuaternion(g.x, g.y, g.z, g.w);
|
return btQuaternion(g.x, g.y, g.z, g.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline btMatrix3x3 glmToBullet(const glm::mat3& m) {
|
||||||
|
return btMatrix3x3(m[0][0], m[1][0], m[2][0],
|
||||||
|
m[0][1], m[1][1], m[2][1],
|
||||||
|
m[0][2], m[1][2], m[2][2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// btTransform does not contain a full 4x4 matrix, so this transform is lossy.
|
||||||
|
// Affine transformations are OK but perspective transformations are not.
|
||||||
|
inline btTransform glmToBullet(const glm::mat4& m) {
|
||||||
|
glm::mat3 m3(m);
|
||||||
|
return btTransform(glmToBullet(m3), glmToBullet(glm::vec3(m[3][0], m[3][1], m[3][2])));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline glm::mat4 bulletToGLM(const btTransform& t) {
|
||||||
|
glm::mat4 m(glm::mat4::_null);
|
||||||
|
|
||||||
|
const btMatrix3x3& basis = t.getBasis();
|
||||||
|
// copy over 3x3 part
|
||||||
|
for (int r = 0; r < 3; r++) {
|
||||||
|
for (int c = 0; c < 3; c++) {
|
||||||
|
m[c][r] = basis[r][c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy traslation part
|
||||||
|
btVector3 origin = t.getOrigin();
|
||||||
|
m[3][0] = origin.getX();
|
||||||
|
m[3][1] = origin.getY();
|
||||||
|
m[3][2] = origin.getZ();
|
||||||
|
|
||||||
|
// set last row
|
||||||
|
m[0][3] = 0.0f;
|
||||||
|
m[1][3] = 0.0f;
|
||||||
|
m[2][3] = 0.0f;
|
||||||
|
m[3][3] = 1.0f;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline btVector3 rotateVector(const btQuaternion& q, const btVector3& vec) {
|
||||||
|
return glmToBullet(bulletToGLM(q) * bulletToGLM(vec));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline btVector3 clampLength(const btVector3& v, const float& maxLength) {
|
||||||
|
if (v.length() > maxLength) {
|
||||||
|
return v.normalized() * maxLength;
|
||||||
|
} else {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // hifi_BulletUtil_h
|
#endif // hifi_BulletUtil_h
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
#include "BulletUtil.h"
|
|
||||||
#include "PhysicsCollisionGroups.h"
|
#include "PhysicsCollisionGroups.h"
|
||||||
#include "ObjectMotionState.h"
|
#include "ObjectMotionState.h"
|
||||||
|
|
||||||
|
@ -21,7 +20,6 @@ const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
|
||||||
const float JUMP_SPEED = 3.5f;
|
const float JUMP_SPEED = 3.5f;
|
||||||
const float MAX_FALL_HEIGHT = 20.0f;
|
const float MAX_FALL_HEIGHT = 20.0f;
|
||||||
|
|
||||||
|
|
||||||
// helper class for simple ray-traces from character
|
// helper class for simple ray-traces from character
|
||||||
class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback {
|
class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback {
|
||||||
public:
|
public:
|
||||||
|
@ -50,7 +48,8 @@ CharacterController::CharacterController() {
|
||||||
_floorDistance = MAX_FALL_HEIGHT;
|
_floorDistance = MAX_FALL_HEIGHT;
|
||||||
|
|
||||||
_walkVelocity.setValue(0.0f, 0.0f, 0.0f);
|
_walkVelocity.setValue(0.0f, 0.0f, 0.0f);
|
||||||
_followVelocity.setValue(0.0f, 0.0f, 0.0f);
|
_followDesiredBodyTransform.setIdentity();
|
||||||
|
_followTimeRemaining = 0.0f;
|
||||||
_jumpSpeed = JUMP_SPEED;
|
_jumpSpeed = JUMP_SPEED;
|
||||||
_isOnGround = false;
|
_isOnGround = false;
|
||||||
_isJumping = false;
|
_isJumping = false;
|
||||||
|
@ -59,6 +58,8 @@ CharacterController::CharacterController() {
|
||||||
_isPushingUp = false;
|
_isPushingUp = false;
|
||||||
_jumpToHoverStart = 0;
|
_jumpToHoverStart = 0;
|
||||||
_followTime = 0.0f;
|
_followTime = 0.0f;
|
||||||
|
_followLinearDisplacement = btVector3(0, 0, 0);
|
||||||
|
_followAngularDisplacement = btQuaternion::getIdentity();
|
||||||
|
|
||||||
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
||||||
|
|
||||||
|
@ -189,12 +190,40 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rather than add _followVelocity to the velocity of the RigidBody, we explicitly teleport
|
// Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform.
|
||||||
// the RigidBody forward according to the formula: distance = rate * time
|
// Rather then add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal.
|
||||||
if (_followVelocity.length2() > 0.0f) {
|
// This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate().
|
||||||
|
// These two computations must be kept in sync.
|
||||||
|
const float MINIMUM_TIME_REMAINING = 0.005f;
|
||||||
|
const float MAX_DISPLACEMENT = 0.5f * _radius;
|
||||||
|
_followTimeRemaining -= dt;
|
||||||
|
if (_followTimeRemaining >= MINIMUM_TIME_REMAINING) {
|
||||||
btTransform bodyTransform = _rigidBody->getWorldTransform();
|
btTransform bodyTransform = _rigidBody->getWorldTransform();
|
||||||
bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _followVelocity);
|
|
||||||
_rigidBody->setWorldTransform(bodyTransform);
|
btVector3 startPos = bodyTransform.getOrigin();
|
||||||
|
btVector3 deltaPos = _followDesiredBodyTransform.getOrigin() - startPos;
|
||||||
|
btVector3 vel = deltaPos / _followTimeRemaining;
|
||||||
|
btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling.
|
||||||
|
btVector3 endPos = startPos + linearDisplacement;
|
||||||
|
|
||||||
|
btQuaternion startRot = bodyTransform.getRotation();
|
||||||
|
glm::vec2 currentFacing = getFacingDir2D(bulletToGLM(startRot));
|
||||||
|
glm::vec2 currentRight(currentFacing.y, -currentFacing.x);
|
||||||
|
glm::vec2 desiredFacing = getFacingDir2D(bulletToGLM(_followDesiredBodyTransform.getRotation()));
|
||||||
|
float deltaAngle = acosf(glm::clamp(glm::dot(currentFacing, desiredFacing), -1.0f, 1.0f));
|
||||||
|
float angularSpeed = deltaAngle / _followTimeRemaining;
|
||||||
|
float sign = copysignf(1.0f, glm::dot(desiredFacing, currentRight));
|
||||||
|
btQuaternion angularDisplacement = btQuaternion(btVector3(0.0f, 1.0f, 0.0f), sign * angularSpeed * dt);
|
||||||
|
btQuaternion endRot = angularDisplacement * startRot;
|
||||||
|
|
||||||
|
// in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account.
|
||||||
|
btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset);
|
||||||
|
btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset);
|
||||||
|
|
||||||
|
_followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement;
|
||||||
|
_followAngularDisplacement = angularDisplacement * _followAngularDisplacement;
|
||||||
|
|
||||||
|
_rigidBody->setWorldTransform(btTransform(endRot, endPos));
|
||||||
}
|
}
|
||||||
_followTime += dt;
|
_followTime += dt;
|
||||||
}
|
}
|
||||||
|
@ -323,8 +352,17 @@ void CharacterController::setTargetVelocity(const glm::vec3& velocity) {
|
||||||
_walkVelocity = glmToBullet(velocity);
|
_walkVelocity = glmToBullet(velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::setFollowVelocity(const glm::vec3& velocity) {
|
void CharacterController::setFollowParameters(const glm::mat4& desiredWorldBodyMatrix, float timeRemaining) {
|
||||||
_followVelocity = glmToBullet(velocity);
|
_followTimeRemaining = timeRemaining;
|
||||||
|
_followDesiredBodyTransform = glmToBullet(desiredWorldBodyMatrix) * btTransform(btQuaternion::getIdentity(), glmToBullet(_shapeLocalOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 CharacterController::getFollowLinearDisplacement() const {
|
||||||
|
return bulletToGLM(_followLinearDisplacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::quat CharacterController::getFollowAngularDisplacement() const {
|
||||||
|
return bulletToGLM(_followAngularDisplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 CharacterController::getLinearVelocity() const {
|
glm::vec3 CharacterController::getLinearVelocity() const {
|
||||||
|
@ -378,6 +416,9 @@ void CharacterController::preSimulation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_followTime = 0.0f;
|
_followTime = 0.0f;
|
||||||
|
|
||||||
|
_followLinearDisplacement = btVector3(0, 0, 0);
|
||||||
|
_followAngularDisplacement = btQuaternion::getIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::postSimulation() {
|
void CharacterController::postSimulation() {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <BulletDynamics/Character/btCharacterControllerInterface.h>
|
#include <BulletDynamics/Character/btCharacterControllerInterface.h>
|
||||||
|
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
|
#include "BulletUtil.h"
|
||||||
|
|
||||||
const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
|
const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
|
||||||
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
||||||
|
@ -66,8 +67,10 @@ public:
|
||||||
void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const;
|
void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const;
|
||||||
|
|
||||||
void setTargetVelocity(const glm::vec3& velocity);
|
void setTargetVelocity(const glm::vec3& velocity);
|
||||||
void setFollowVelocity(const glm::vec3& velocity);
|
void setFollowParameters(const glm::mat4& desiredWorldMatrix, float timeRemaining);
|
||||||
float getFollowTime() const { return _followTime; }
|
float getFollowTime() const { return _followTime; }
|
||||||
|
glm::vec3 getFollowLinearDisplacement() const;
|
||||||
|
glm::quat getFollowAngularDisplacement() const;
|
||||||
|
|
||||||
glm::vec3 getLinearVelocity() const;
|
glm::vec3 getLinearVelocity() const;
|
||||||
|
|
||||||
|
@ -87,7 +90,8 @@ protected:
|
||||||
protected:
|
protected:
|
||||||
btVector3 _currentUp;
|
btVector3 _currentUp;
|
||||||
btVector3 _walkVelocity;
|
btVector3 _walkVelocity;
|
||||||
btVector3 _followVelocity;
|
btTransform _followDesiredBodyTransform;
|
||||||
|
btScalar _followTimeRemaining;
|
||||||
btTransform _characterBodyTransform;
|
btTransform _characterBodyTransform;
|
||||||
|
|
||||||
glm::vec3 _shapeLocalOffset;
|
glm::vec3 _shapeLocalOffset;
|
||||||
|
@ -105,6 +109,8 @@ protected:
|
||||||
|
|
||||||
btScalar _jumpSpeed;
|
btScalar _jumpSpeed;
|
||||||
btScalar _followTime;
|
btScalar _followTime;
|
||||||
|
btVector3 _followLinearDisplacement;
|
||||||
|
btQuaternion _followAngularDisplacement;
|
||||||
|
|
||||||
bool _enabled;
|
bool _enabled;
|
||||||
bool _isOnGround;
|
bool _isOnGround;
|
||||||
|
|
|
@ -82,7 +82,7 @@ EntityMotionState::~EntityMotionState() {
|
||||||
|
|
||||||
void EntityMotionState::updateServerPhysicsVariables() {
|
void EntityMotionState::updateServerPhysicsVariables() {
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
if (_entity->getSimulatorID() == PhysicsEngine::getSessionID()) {
|
if (_entity->getSimulatorID() == Physics::getSessionUUID()) {
|
||||||
// don't slam these values if we are the simulation owner
|
// don't slam these values if we are the simulation owner
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
_outgoingPriority = NO_PRORITY;
|
_outgoingPriority = NO_PRORITY;
|
||||||
} else {
|
} else {
|
||||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||||
if (PhysicsEngine::getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
|
if (Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
|
||||||
// we own the simulation or our priority looses to (or ties with) remote
|
// we own the simulation or our priority looses to (or ties with) remote
|
||||||
_outgoingPriority = NO_PRORITY;
|
_outgoingPriority = NO_PRORITY;
|
||||||
}
|
}
|
||||||
|
@ -527,7 +527,7 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
||||||
if (dirtyFlags | Simulation::DIRTY_SIMULATOR_ID) {
|
if (dirtyFlags | Simulation::DIRTY_SIMULATOR_ID) {
|
||||||
// when SIMULATOR_ID changes we must check for reinterpretation of asymmetric collision mask
|
// when SIMULATOR_ID changes we must check for reinterpretation of asymmetric collision mask
|
||||||
// bits for the avatar groups (e.g. MY_AVATAR vs OTHER_AVATAR)
|
// bits for the avatar groups (e.g. MY_AVATAR vs OTHER_AVATAR)
|
||||||
uint8_t entityCollisionMask = _entity->getCollisionMask();
|
uint8_t entityCollisionMask = _entity->getCollisionless() ? 0 : _entity->getCollisionMask();
|
||||||
if ((bool)(entityCollisionMask & USER_COLLISION_GROUP_MY_AVATAR) !=
|
if ((bool)(entityCollisionMask & USER_COLLISION_GROUP_MY_AVATAR) !=
|
||||||
(bool)(entityCollisionMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
|
(bool)(entityCollisionMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
|
||||||
// bits are asymmetric --> flag for reinsertion in physics simulation
|
// bits are asymmetric --> flag for reinsertion in physics simulation
|
||||||
|
@ -622,39 +622,8 @@ QString EntityMotionState::getName() const {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const {
|
void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const {
|
||||||
group = BULLET_COLLISION_GROUP_STATIC;
|
assert(_entity);
|
||||||
if (_entity) {
|
_entity->computeCollisionGroupAndFinalMask(group, mask);
|
||||||
if (_entity->getCollisionless()) {
|
|
||||||
group = BULLET_COLLISION_GROUP_COLLISIONLESS;
|
|
||||||
}
|
|
||||||
switch (computePhysicsMotionType()){
|
|
||||||
case MOTION_TYPE_STATIC:
|
|
||||||
group = BULLET_COLLISION_GROUP_STATIC;
|
|
||||||
break;
|
|
||||||
case MOTION_TYPE_DYNAMIC:
|
|
||||||
group = BULLET_COLLISION_GROUP_DYNAMIC;
|
|
||||||
break;
|
|
||||||
case MOTION_TYPE_KINEMATIC:
|
|
||||||
group = BULLET_COLLISION_GROUP_KINEMATIC;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mask = PhysicsEngine::getCollisionMask(group);
|
|
||||||
if (_entity) {
|
|
||||||
uint8_t entityCollisionMask = _entity->getFinalCollisionMask();
|
|
||||||
if ((bool)(entityCollisionMask & USER_COLLISION_GROUP_MY_AVATAR) !=
|
|
||||||
(bool)(entityCollisionMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
|
|
||||||
// asymmetric avatar collision mask bits
|
|
||||||
if (!_entity->getSimulatorID().isNull() && _entity->getSimulatorID() != PhysicsEngine::getSessionID()) {
|
|
||||||
// someone else owns the simulation, so we swap the interpretation of the bits
|
|
||||||
entityCollisionMask ^= USER_COLLISION_MASK_AVATARS | ~entityCollisionMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mask &= (int16_t)(entityCollisionMask);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::setOutgoingPriority(quint8 priority) {
|
void EntityMotionState::setOutgoingPriority(quint8 priority) {
|
||||||
|
|
|
@ -43,6 +43,7 @@ void PhysicalEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
assert(entity);
|
assert(entity);
|
||||||
assert(!entity->isDead());
|
assert(!entity->isDead());
|
||||||
if (entity->shouldBePhysical()) {
|
if (entity->shouldBePhysical()) {
|
||||||
|
@ -57,6 +58,7 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
|
|
||||||
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||||
EntitySimulation::removeEntityInternal(entity);
|
EntitySimulation::removeEntityInternal(entity);
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
_entitiesToAddToPhysics.remove(entity);
|
_entitiesToAddToPhysics.remove(entity);
|
||||||
|
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
|
@ -78,8 +80,7 @@ void PhysicalEntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesTo
|
||||||
// rather than do it here
|
// rather than do it here
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
if (motionState) {
|
if (motionState) {
|
||||||
delete motionState;
|
_entitiesToRemoveFromPhysics.insert(entity);
|
||||||
entity->setPhysicsInfo(nullptr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_entitiesToDelete.clear();
|
_entitiesToDelete.clear();
|
||||||
|
@ -87,6 +88,7 @@ void PhysicalEntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesTo
|
||||||
|
|
||||||
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
|
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
assert(entity);
|
assert(entity);
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
if (motionState) {
|
if (motionState) {
|
||||||
|
@ -119,7 +121,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
||||||
// while it is in the middle of a simulation step. As it is, we're probably in shutdown mode
|
// while it is in the middle of a simulation step. As it is, we're probably in shutdown mode
|
||||||
// anyway, so maybe the simulation was already properly shutdown? Cross our fingers...
|
// anyway, so maybe the simulation was already properly shutdown? Cross our fingers...
|
||||||
|
|
||||||
// first disconnect each MotionStates from its Entity
|
// copy everything into _entitiesToDelete
|
||||||
for (auto stateItr : _physicalObjects) {
|
for (auto stateItr : _physicalObjects) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(&(*stateItr));
|
||||||
_entitiesToDelete.insert(motionState->getEntity());
|
_entitiesToDelete.insert(motionState->getEntity());
|
||||||
|
@ -134,8 +136,8 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
||||||
for (auto entity : _entitiesToDelete) {
|
for (auto entity : _entitiesToDelete) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
if (motionState) {
|
if (motionState) {
|
||||||
delete motionState;
|
|
||||||
entity->setPhysicsInfo(nullptr);
|
entity->setPhysicsInfo(nullptr);
|
||||||
|
delete motionState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,22 +160,39 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity)
|
||||||
// end EntitySimulation overrides
|
// end EntitySimulation overrides
|
||||||
|
|
||||||
void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||||
_entitiesToRelease.clear();
|
|
||||||
result.clear();
|
result.clear();
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
for (auto entity: _entitiesToRemoveFromPhysics) {
|
for (auto entity: _entitiesToRemoveFromPhysics) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
// make sure it isn't on any side lists
|
||||||
if (motionState) {
|
|
||||||
_pendingChanges.remove(motionState);
|
|
||||||
_physicalObjects.remove(motionState);
|
|
||||||
result.push_back(motionState);
|
|
||||||
}
|
|
||||||
_entitiesToAddToPhysics.remove(entity);
|
_entitiesToAddToPhysics.remove(entity);
|
||||||
if (entity->isDead()) {
|
|
||||||
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
|
assert(motionState);
|
||||||
|
_pendingChanges.remove(motionState);
|
||||||
|
_physicalObjects.remove(motionState);
|
||||||
|
result.push_back(motionState);
|
||||||
|
_entitiesToRelease.insert(entity);
|
||||||
|
|
||||||
|
if (entity->isSimulated() && entity->isDead()) {
|
||||||
_entitiesToDelete.insert(entity);
|
_entitiesToDelete.insert(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_entitiesToRemoveFromPhysics.swap(_entitiesToRelease);
|
_entitiesToRemoveFromPhysics.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() {
|
||||||
|
QMutexLocker lock(&_mutex);
|
||||||
|
for (auto entity: _entitiesToRelease) {
|
||||||
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
|
assert(motionState);
|
||||||
|
entity->setPhysicsInfo(nullptr);
|
||||||
|
delete motionState;
|
||||||
|
|
||||||
|
if (entity->isSimulated() && entity->isDead()) {
|
||||||
|
_entitiesToDelete.insert(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_entitiesToRelease.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||||
|
|
|
@ -49,6 +49,7 @@ public:
|
||||||
virtual void prepareEntityForDelete(EntityItemPointer entity) override;
|
virtual void prepareEntityForDelete(EntityItemPointer entity) override;
|
||||||
|
|
||||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& result);
|
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& result);
|
||||||
|
void deleteObjectsRemovedFromPhysics();
|
||||||
void getObjectsToAddToPhysics(VectorOfMotionStates& result);
|
void getObjectsToAddToPhysics(VectorOfMotionStates& result);
|
||||||
void setObjectsToChange(const VectorOfMotionStates& objectsToChange);
|
void setObjectsToChange(const VectorOfMotionStates& objectsToChange);
|
||||||
void getObjectsToChange(VectorOfMotionStates& result);
|
void getObjectsToChange(VectorOfMotionStates& result);
|
||||||
|
|
|
@ -18,47 +18,10 @@
|
||||||
#include "ThreadSafeDynamicsWorld.h"
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
uint32_t PhysicsEngine::getNumSubsteps() {
|
|
||||||
return _numSubsteps;
|
|
||||||
}
|
|
||||||
|
|
||||||
btHashMap<btHashInt, int16_t> _collisionMasks;
|
|
||||||
|
|
||||||
void initCollisionMaskTable() {
|
|
||||||
if (_collisionMasks.size() == 0) {
|
|
||||||
// build table of masks with their group as the key
|
|
||||||
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_DYNAMIC), BULLET_COLLISION_MASK_DYNAMIC);
|
|
||||||
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_STATIC), BULLET_COLLISION_MASK_STATIC);
|
|
||||||
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_KINEMATIC), BULLET_COLLISION_MASK_KINEMATIC);
|
|
||||||
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_MY_AVATAR), BULLET_COLLISION_MASK_MY_AVATAR);
|
|
||||||
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_OTHER_AVATAR), BULLET_COLLISION_MASK_OTHER_AVATAR);
|
|
||||||
_collisionMasks.insert(btHashInt((int)BULLET_COLLISION_GROUP_COLLISIONLESS), BULLET_COLLISION_MASK_COLLISIONLESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
int16_t PhysicsEngine::getCollisionMask(int16_t group) {
|
|
||||||
const int16_t* mask = _collisionMasks.find(btHashInt((int)group));
|
|
||||||
return mask ? *mask : BULLET_COLLISION_MASK_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUuid _sessionID;
|
|
||||||
|
|
||||||
// static
|
|
||||||
void PhysicsEngine::setSessionUUID(const QUuid& sessionID) {
|
|
||||||
_sessionID = sessionID;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
const QUuid& PhysicsEngine::getSessionID() {
|
|
||||||
return _sessionID;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PhysicsEngine::PhysicsEngine(const glm::vec3& offset) :
|
PhysicsEngine::PhysicsEngine(const glm::vec3& offset) :
|
||||||
_originOffset(offset),
|
_originOffset(offset),
|
||||||
|
_sessionID(),
|
||||||
_myAvatarController(nullptr) {
|
_myAvatarController(nullptr) {
|
||||||
initCollisionMaskTable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicsEngine::~PhysicsEngine() {
|
PhysicsEngine::~PhysicsEngine() {
|
||||||
|
@ -90,6 +53,10 @@ void PhysicsEngine::init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t PhysicsEngine::getNumSubsteps() {
|
||||||
|
return _numSubsteps;
|
||||||
|
}
|
||||||
|
|
||||||
// private
|
// private
|
||||||
void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
||||||
assert(motionState);
|
assert(motionState);
|
||||||
|
|
|
@ -45,16 +45,11 @@ typedef QVector<Collision> CollisionEvents;
|
||||||
|
|
||||||
class PhysicsEngine {
|
class PhysicsEngine {
|
||||||
public:
|
public:
|
||||||
static int16_t getCollisionMask(int16_t group);
|
|
||||||
|
|
||||||
uint32_t getNumSubsteps();
|
|
||||||
|
|
||||||
PhysicsEngine(const glm::vec3& offset);
|
PhysicsEngine(const glm::vec3& offset);
|
||||||
~PhysicsEngine();
|
~PhysicsEngine();
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
static void setSessionUUID(const QUuid& sessionID);
|
uint32_t getNumSubsteps();
|
||||||
static const QUuid& getSessionID();
|
|
||||||
|
|
||||||
void removeObjects(const VectorOfMotionStates& objects);
|
void removeObjects(const VectorOfMotionStates& objects);
|
||||||
void removeObjects(const SetOfMotionStates& objects); // only called during teardown
|
void removeObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||||
|
@ -95,6 +90,8 @@ public:
|
||||||
void removeAction(const QUuid actionID);
|
void removeAction(const QUuid actionID);
|
||||||
void forEachAction(std::function<void(EntityActionPointer)> actor);
|
void forEachAction(std::function<void(EntityActionPointer)> actor);
|
||||||
|
|
||||||
|
void setSessionUUID(const QUuid& sessionID) { _sessionID = sessionID; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||||
void removeObjectFromDynamicsWorld(ObjectMotionState* motionState);
|
void removeObjectFromDynamicsWorld(ObjectMotionState* motionState);
|
||||||
|
@ -111,23 +108,21 @@ private:
|
||||||
ThreadSafeDynamicsWorld* _dynamicsWorld = NULL;
|
ThreadSafeDynamicsWorld* _dynamicsWorld = NULL;
|
||||||
btGhostPairCallback* _ghostPairCallback = NULL;
|
btGhostPairCallback* _ghostPairCallback = NULL;
|
||||||
|
|
||||||
glm::vec3 _originOffset;
|
|
||||||
|
|
||||||
ContactMap _contactMap;
|
ContactMap _contactMap;
|
||||||
uint32_t _numContactFrames = 0;
|
CollisionEvents _collisionEvents;
|
||||||
|
QHash<QUuid, EntityActionPointer> _objectActions;
|
||||||
|
|
||||||
|
glm::vec3 _originOffset;
|
||||||
|
QUuid _sessionID;
|
||||||
|
|
||||||
/// character collisions
|
|
||||||
CharacterController* _myAvatarController;
|
CharacterController* _myAvatarController;
|
||||||
|
|
||||||
|
uint32_t _numContactFrames = 0;
|
||||||
|
uint32_t _numSubsteps;
|
||||||
|
|
||||||
bool _dumpNextStats = false;
|
bool _dumpNextStats = false;
|
||||||
bool _hasOutgoingChanges = false;
|
bool _hasOutgoingChanges = false;
|
||||||
|
|
||||||
CollisionEvents _collisionEvents;
|
|
||||||
|
|
||||||
QHash<QUuid, EntityActionPointer> _objectActions;
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t _numSubsteps;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<PhysicsEngine> PhysicsEnginePointer;
|
typedef std::shared_ptr<PhysicsEngine> PhysicsEnginePointer;
|
||||||
|
|
|
@ -70,12 +70,16 @@ static const std::string DEFAULT_LIGHTING_SHADER {
|
||||||
" return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);"
|
" return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);"
|
||||||
" }"
|
" }"
|
||||||
};
|
};
|
||||||
|
static const std::string DEFAULT_SHADOW_SHADER {
|
||||||
static const std::string DEFAULT_SHADOW_SHADER{
|
"uniform sampler2DShadow shadowMap;"
|
||||||
"uniform sampler2D shadowMapColor;"
|
|
||||||
// The actual shadowMap is a sampler2DShadow, so we cannot normally sample it
|
|
||||||
"vec4 getFragmentColor() {"
|
"vec4 getFragmentColor() {"
|
||||||
" return vec4(texture(shadowMapColor, uv).xyz, 1.0);"
|
" for (int i = 255; i >= 0; --i) {"
|
||||||
|
" float depth = i / 255.0;"
|
||||||
|
" if (texture(shadowMap, vec3(uv, depth)) > 0.5) {"
|
||||||
|
" return vec4(vec3(depth), 1.0);"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
" return vec4(vec3(0.0), 1.0);"
|
||||||
" }"
|
" }"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -194,7 +198,7 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::st
|
||||||
slotBindings.insert(gpu::Shader::Binding("specularMap", Specular));
|
slotBindings.insert(gpu::Shader::Binding("specularMap", Specular));
|
||||||
slotBindings.insert(gpu::Shader::Binding("depthMap", Depth));
|
slotBindings.insert(gpu::Shader::Binding("depthMap", Depth));
|
||||||
slotBindings.insert(gpu::Shader::Binding("lightingMap", Lighting));
|
slotBindings.insert(gpu::Shader::Binding("lightingMap", Lighting));
|
||||||
slotBindings.insert(gpu::Shader::Binding("shadowMapColor", Shadow));
|
slotBindings.insert(gpu::Shader::Binding("shadowMap", Shadow));
|
||||||
slotBindings.insert(gpu::Shader::Binding("pyramidMap", Pyramid));
|
slotBindings.insert(gpu::Shader::Binding("pyramidMap", Pyramid));
|
||||||
slotBindings.insert(gpu::Shader::Binding("occlusionMap", AmbientOcclusion));
|
slotBindings.insert(gpu::Shader::Binding("occlusionMap", AmbientOcclusion));
|
||||||
slotBindings.insert(gpu::Shader::Binding("occlusionBlurredMap", AmbientOcclusionBlurred));
|
slotBindings.insert(gpu::Shader::Binding("occlusionBlurredMap", AmbientOcclusionBlurred));
|
||||||
|
@ -253,7 +257,7 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
|
||||||
batch.setResourceTexture(Specular, framebufferCache->getDeferredSpecularTexture());
|
batch.setResourceTexture(Specular, framebufferCache->getDeferredSpecularTexture());
|
||||||
batch.setResourceTexture(Depth, framebufferCache->getPrimaryDepthTexture());
|
batch.setResourceTexture(Depth, framebufferCache->getPrimaryDepthTexture());
|
||||||
batch.setResourceTexture(Lighting, framebufferCache->getLightingTexture());
|
batch.setResourceTexture(Lighting, framebufferCache->getLightingTexture());
|
||||||
batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getRenderBuffer(0));
|
batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getDepthStencilBuffer());
|
||||||
batch.setResourceTexture(Pyramid, framebufferCache->getDepthPyramidTexture());
|
batch.setResourceTexture(Pyramid, framebufferCache->getDepthPyramidTexture());
|
||||||
batch.setResourceTexture(AmbientOcclusion, framebufferCache->getOcclusionTexture());
|
batch.setResourceTexture(AmbientOcclusion, framebufferCache->getOcclusionTexture());
|
||||||
batch.setResourceTexture(AmbientOcclusionBlurred, framebufferCache->getOcclusionBlurredTexture());
|
batch.setResourceTexture(AmbientOcclusionBlurred, framebufferCache->getOcclusionBlurredTexture());
|
||||||
|
|
|
@ -64,14 +64,21 @@ void DeferredLightingEffect::init() {
|
||||||
_directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();
|
_directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();
|
||||||
_directionalSkyboxLightLocations = std::make_shared<LightLocations>();
|
_directionalSkyboxLightLocations = std::make_shared<LightLocations>();
|
||||||
|
|
||||||
|
_directionalLightShadowLocations = std::make_shared<LightLocations>();
|
||||||
|
_directionalAmbientSphereLightShadowLocations = std::make_shared<LightLocations>();
|
||||||
|
_directionalSkyboxLightShadowLocations = std::make_shared<LightLocations>();
|
||||||
|
|
||||||
_pointLightLocations = std::make_shared<LightLocations>();
|
_pointLightLocations = std::make_shared<LightLocations>();
|
||||||
_spotLightLocations = std::make_shared<LightLocations>();
|
_spotLightLocations = std::make_shared<LightLocations>();
|
||||||
|
|
||||||
// TODO: To use shadowmaps, replace directional_*_light_frag with directional_*_light_shadow_frag shaders.
|
|
||||||
loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations);
|
loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations);
|
||||||
loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
|
loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
|
||||||
loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
|
loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
|
||||||
|
|
||||||
|
loadLightProgram(deferred_light_vert, directional_light_shadow_frag, false, _directionalLightShadow, _directionalLightShadowLocations);
|
||||||
|
loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations);
|
||||||
|
loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations);
|
||||||
|
|
||||||
loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations);
|
loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations);
|
||||||
loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations);
|
loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations);
|
||||||
|
|
||||||
|
@ -306,18 +313,27 @@ void DeferredLightingEffect::render(const render::RenderContextPointer& renderCo
|
||||||
{
|
{
|
||||||
bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap());
|
bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap());
|
||||||
|
|
||||||
auto& program = _directionalLight;
|
auto& program = _shadowMapStatus ? _directionalLightShadow : _directionalLight;
|
||||||
LightLocationsPtr locations = _directionalLightLocations;
|
LightLocationsPtr locations = _shadowMapStatus ? _directionalLightShadowLocations : _directionalLightLocations;
|
||||||
|
|
||||||
// TODO: At some point bring back the shadows...
|
|
||||||
// Setup the global directional pass pipeline
|
// Setup the global directional pass pipeline
|
||||||
{
|
{
|
||||||
if (useSkyboxCubemap) {
|
if (_shadowMapStatus) {
|
||||||
program = _directionalSkyboxLight;
|
if (useSkyboxCubemap) {
|
||||||
locations = _directionalSkyboxLightLocations;
|
program = _directionalSkyboxLightShadow;
|
||||||
} else if (_ambientLightMode > -1) {
|
locations = _directionalSkyboxLightShadowLocations;
|
||||||
program = _directionalAmbientSphereLight;
|
} else if (_ambientLightMode > -1) {
|
||||||
locations = _directionalAmbientSphereLightLocations;
|
program = _directionalAmbientSphereLightShadow;
|
||||||
|
locations = _directionalAmbientSphereLightShadowLocations;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (useSkyboxCubemap) {
|
||||||
|
program = _directionalSkyboxLight;
|
||||||
|
locations = _directionalSkyboxLightLocations;
|
||||||
|
} else if (_ambientLightMode > -1) {
|
||||||
|
program = _directionalAmbientSphereLight;
|
||||||
|
locations = _directionalAmbientSphereLightLocations;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locations->shadowTransformBuffer >= 0) {
|
if (locations->shadowTransformBuffer >= 0) {
|
||||||
|
|
|
@ -54,27 +54,37 @@ public:
|
||||||
void setGlobalSkybox(const model::SkyboxPointer& skybox);
|
void setGlobalSkybox(const model::SkyboxPointer& skybox);
|
||||||
|
|
||||||
const LightStage& getLightStage() { return _lightStage; }
|
const LightStage& getLightStage() { return _lightStage; }
|
||||||
|
void setShadowMapStatus(bool enable) { _shadowMapStatus = enable; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LightStage _lightStage;
|
LightStage _lightStage;
|
||||||
|
bool _shadowMapStatus{ false };
|
||||||
|
|
||||||
DeferredLightingEffect() = default;
|
DeferredLightingEffect() = default;
|
||||||
|
|
||||||
model::MeshPointer _spotLightMesh;
|
model::MeshPointer _spotLightMesh;
|
||||||
model::MeshPointer getSpotLightMesh();
|
model::MeshPointer getSpotLightMesh();
|
||||||
|
|
||||||
gpu::PipelinePointer _directionalSkyboxLight;
|
gpu::PipelinePointer _directionalSkyboxLight;
|
||||||
LightLocationsPtr _directionalSkyboxLightLocations;
|
|
||||||
|
|
||||||
gpu::PipelinePointer _directionalAmbientSphereLight;
|
gpu::PipelinePointer _directionalAmbientSphereLight;
|
||||||
LightLocationsPtr _directionalAmbientSphereLightLocations;
|
|
||||||
|
|
||||||
gpu::PipelinePointer _directionalLight;
|
gpu::PipelinePointer _directionalLight;
|
||||||
LightLocationsPtr _directionalLightLocations;
|
|
||||||
|
gpu::PipelinePointer _directionalSkyboxLightShadow;
|
||||||
|
gpu::PipelinePointer _directionalAmbientSphereLightShadow;
|
||||||
|
gpu::PipelinePointer _directionalLightShadow;
|
||||||
|
|
||||||
gpu::PipelinePointer _pointLight;
|
gpu::PipelinePointer _pointLight;
|
||||||
LightLocationsPtr _pointLightLocations;
|
|
||||||
gpu::PipelinePointer _spotLight;
|
gpu::PipelinePointer _spotLight;
|
||||||
|
|
||||||
|
LightLocationsPtr _directionalSkyboxLightLocations;
|
||||||
|
LightLocationsPtr _directionalAmbientSphereLightLocations;
|
||||||
|
LightLocationsPtr _directionalLightLocations;
|
||||||
|
|
||||||
|
LightLocationsPtr _directionalSkyboxLightShadowLocations;
|
||||||
|
LightLocationsPtr _directionalAmbientSphereLightShadowLocations;
|
||||||
|
LightLocationsPtr _directionalLightShadowLocations;
|
||||||
|
|
||||||
|
LightLocationsPtr _pointLightLocations;
|
||||||
LightLocationsPtr _spotLightLocations;
|
LightLocationsPtr _spotLightLocations;
|
||||||
|
|
||||||
using Lights = std::vector<model::LightPointer>;
|
using Lights = std::vector<model::LightPointer>;
|
||||||
|
|
|
@ -27,7 +27,9 @@ void LightStage::Shadow::setKeylightFrustum(ViewFrustum* viewFrustum, float near
|
||||||
const auto& direction = glm::normalize(_light->getDirection());
|
const auto& direction = glm::normalize(_light->getDirection());
|
||||||
glm::quat orientation;
|
glm::quat orientation;
|
||||||
if (direction == IDENTITY_UP) {
|
if (direction == IDENTITY_UP) {
|
||||||
orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_UP, IDENTITY_FRONT));
|
orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FRONT, -IDENTITY_UP));
|
||||||
|
} else if (direction == -IDENTITY_UP) {
|
||||||
|
orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FRONT, IDENTITY_UP));
|
||||||
} else {
|
} else {
|
||||||
auto side = glm::normalize(glm::cross(direction, IDENTITY_UP));
|
auto side = glm::normalize(glm::cross(direction, IDENTITY_UP));
|
||||||
auto up = glm::normalize(glm::cross(side, direction));
|
auto up = glm::normalize(glm::cross(side, direction));
|
||||||
|
@ -70,6 +72,9 @@ void LightStage::Shadow::setKeylightFrustum(ViewFrustum* viewFrustum, float near
|
||||||
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, -max.z, -min.z);
|
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, -max.z, -min.z);
|
||||||
_frustum->setProjection(ortho);
|
_frustum->setProjection(ortho);
|
||||||
|
|
||||||
|
// Calculate the frustum's internal state
|
||||||
|
_frustum->calculate();
|
||||||
|
|
||||||
// Update the buffer
|
// Update the buffer
|
||||||
_schemaBuffer.edit<Schema>().projection = ortho;
|
_schemaBuffer.edit<Schema>().projection = ortho;
|
||||||
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
||||||
|
|
|
@ -215,7 +215,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations) const {
|
void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool canCauterize) const {
|
||||||
batch.setModelTransform(_drawTransform);
|
batch.setModelTransform(_drawTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,25 +442,25 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations) const {
|
void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool canCauterize) const {
|
||||||
// Still relying on the raw data from the model
|
// Still relying on the raw data from the model
|
||||||
const Model::MeshState& state = _model->_meshStates.at(_meshIndex);
|
const Model::MeshState& state = _model->_meshStates.at(_meshIndex);
|
||||||
|
|
||||||
Transform transform;
|
Transform transform;
|
||||||
if (state.clusterBuffer) {
|
if (state.clusterBuffer) {
|
||||||
if (_model->_cauterizeBones) {
|
if (canCauterize && _model->getCauterizeBones()) {
|
||||||
batch.setUniformBuffer(ShapePipeline::Slot::SKINNING_GPU, state.cauterizedClusterBuffer);
|
batch.setUniformBuffer(ShapePipeline::Slot::SKINNING_GPU, state.cauterizedClusterBuffer);
|
||||||
} else {
|
} else {
|
||||||
batch.setUniformBuffer(ShapePipeline::Slot::SKINNING_GPU, state.clusterBuffer);
|
batch.setUniformBuffer(ShapePipeline::Slot::SKINNING_GPU, state.clusterBuffer);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_model->_cauterizeBones) {
|
if (canCauterize && _model->getCauterizeBones()) {
|
||||||
transform = Transform(state.cauterizedClusterMatrices[0]);
|
transform = Transform(state.cauterizedClusterMatrices[0]);
|
||||||
} else {
|
} else {
|
||||||
transform = Transform(state.clusterMatrices[0]);
|
transform = Transform(state.clusterMatrices[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// transform.preTranslate(_modelPosition);
|
|
||||||
transform.preTranslate(_transform.getTranslation());
|
transform.preTranslate(_transform.getTranslation());
|
||||||
batch.setModelTransform(transform);
|
batch.setModelTransform(transform);
|
||||||
}
|
}
|
||||||
|
@ -507,8 +507,9 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
|
||||||
assert(locations);
|
assert(locations);
|
||||||
|
|
||||||
// Bind the model transform and the skinCLusterMatrices if needed
|
// Bind the model transform and the skinCLusterMatrices if needed
|
||||||
|
bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE;
|
||||||
_model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation());
|
_model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation());
|
||||||
bindTransform(batch, locations);
|
bindTransform(batch, locations, canCauterize);
|
||||||
|
|
||||||
//Bind the index buffer and vertex buffer and Blend shapes if needed
|
//Bind the index buffer and vertex buffer and Blend shapes if needed
|
||||||
bindMesh(batch);
|
bindMesh(batch);
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
void drawCall(gpu::Batch& batch) const;
|
void drawCall(gpu::Batch& batch) const;
|
||||||
virtual void bindMesh(gpu::Batch& batch) const;
|
virtual void bindMesh(gpu::Batch& batch) const;
|
||||||
virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const;
|
virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const;
|
||||||
virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const;
|
virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const;
|
||||||
|
|
||||||
// Payload resource cached values
|
// Payload resource cached values
|
||||||
model::MeshPointer _drawMesh;
|
model::MeshPointer _drawMesh;
|
||||||
|
@ -88,15 +88,17 @@ public:
|
||||||
|
|
||||||
// ModelMeshPartPayload functions to perform render
|
// ModelMeshPartPayload functions to perform render
|
||||||
void bindMesh(gpu::Batch& batch) const override;
|
void bindMesh(gpu::Batch& batch) const override;
|
||||||
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const override;
|
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize) const override;
|
||||||
|
|
||||||
|
|
||||||
void initCache();
|
void initCache();
|
||||||
|
|
||||||
Model* _model;
|
Model* _model;
|
||||||
|
|
||||||
int _meshIndex;
|
int _meshIndex;
|
||||||
int _shapeID;
|
int _shapeID;
|
||||||
bool _isSkinned = false;
|
|
||||||
bool _isBlendShaped = false;
|
bool _isSkinned{ false };
|
||||||
|
bool _isBlendShaped{ false };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_MeshPartPayload_h
|
#endif // hifi_MeshPartPayload_h
|
||||||
|
|
|
@ -191,6 +191,9 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
|
||||||
setToneMappingToneCurve(renderContext->getTone().toneCurve);
|
setToneMappingToneCurve(renderContext->getTone().toneCurve);
|
||||||
// TODO: Allow runtime manipulation of culling ShouldRenderFunctor
|
// TODO: Allow runtime manipulation of culling ShouldRenderFunctor
|
||||||
|
|
||||||
|
// TODO: For now, lighting is controlled through a singleton, so it is distinct
|
||||||
|
DependencyManager::get<DeferredLightingEffect>()->setShadowMapStatus(renderContext->getShadowMapStatus());
|
||||||
|
|
||||||
renderContext->getArgs()->_context->syncCache();
|
renderContext->getArgs()->_context->syncCache();
|
||||||
|
|
||||||
for (auto job : _jobs) {
|
for (auto job : _jobs) {
|
||||||
|
|
|
@ -124,15 +124,12 @@ public:
|
||||||
void setDrawHitEffect(bool draw) { enableJob(_drawHitEffectJobIndex, draw); }
|
void setDrawHitEffect(bool draw) { enableJob(_drawHitEffectJobIndex, draw); }
|
||||||
bool doDrawHitEffect() const { return getEnableJob(_drawHitEffectJobIndex); }
|
bool doDrawHitEffect() const { return getEnableJob(_drawHitEffectJobIndex); }
|
||||||
|
|
||||||
|
|
||||||
void setOcclusionStatus(bool draw) { enableJob(_occlusionJobIndex, draw); }
|
void setOcclusionStatus(bool draw) { enableJob(_occlusionJobIndex, draw); }
|
||||||
bool doOcclusionStatus() const { return getEnableJob(_occlusionJobIndex); }
|
bool doOcclusionStatus() const { return getEnableJob(_occlusionJobIndex); }
|
||||||
|
|
||||||
|
|
||||||
void setAntialiasingStatus(bool draw) { enableJob(_antialiasingJobIndex, draw); }
|
void setAntialiasingStatus(bool draw) { enableJob(_antialiasingJobIndex, draw); }
|
||||||
bool doAntialiasingStatus() const { return getEnableJob(_antialiasingJobIndex); }
|
bool doAntialiasingStatus() const { return getEnableJob(_antialiasingJobIndex); }
|
||||||
|
|
||||||
|
|
||||||
void setToneMappingExposure(float exposure);
|
void setToneMappingExposure(float exposure);
|
||||||
float getToneMappingExposure() const;
|
float getToneMappingExposure() const;
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,11 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render
|
||||||
assert(sceneContext);
|
assert(sceneContext);
|
||||||
RenderArgs* args = renderContext->getArgs();
|
RenderArgs* args = renderContext->getArgs();
|
||||||
|
|
||||||
|
// This feature is in a debugging stage - it must be turned on explicitly
|
||||||
|
if (!renderContext->getShadowMapStatus()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if (!sceneContext->_scene || !args) {
|
if (!sceneContext->_scene || !args) {
|
||||||
return;
|
return;
|
||||||
|
@ -136,21 +141,26 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache old render args
|
||||||
ViewFrustum* viewFrustum = args->_viewFrustum;
|
ViewFrustum* viewFrustum = args->_viewFrustum;
|
||||||
|
RenderArgs::RenderMode mode = args->_renderMode;
|
||||||
|
|
||||||
auto nearClip = viewFrustum->getNearClip();
|
auto nearClip = viewFrustum->getNearClip();
|
||||||
const int SHADOW_NEAR_DEPTH = -2;
|
const int SHADOW_NEAR_DEPTH = -2;
|
||||||
const int SHADOW_FAR_DEPTH = 20;
|
const int SHADOW_FAR_DEPTH = 20;
|
||||||
globalLight->shadow.setKeylightFrustum(viewFrustum, nearClip + SHADOW_NEAR_DEPTH, nearClip + SHADOW_FAR_DEPTH);
|
globalLight->shadow.setKeylightFrustum(viewFrustum, nearClip + SHADOW_NEAR_DEPTH, nearClip + SHADOW_FAR_DEPTH);
|
||||||
|
|
||||||
// Set the keylight frustum
|
// Set the keylight render args
|
||||||
args->_viewFrustum = globalLight->shadow.getFrustum().get();
|
args->_viewFrustum = globalLight->shadow.getFrustum().get();
|
||||||
|
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
|
||||||
|
|
||||||
// TODO: Allow runtime manipulation of culling ShouldRenderFunctor
|
// TODO: Allow runtime manipulation of culling ShouldRenderFunctor
|
||||||
|
|
||||||
for (auto job : _jobs) {
|
for (auto job : _jobs) {
|
||||||
job.run(sceneContext, renderContext);
|
job.run(sceneContext, renderContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the view frustum
|
// Reset the render args
|
||||||
args->_viewFrustum = viewFrustum;
|
args->_viewFrustum = viewFrustum;
|
||||||
|
args->_renderMode = mode;
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,44 +60,39 @@ float fetchShadow(vec3 shadowTexcoord) {
|
||||||
return texture(shadowMap, shadowTexcoord);
|
return texture(shadowMap, shadowTexcoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 samples[8] = vec2[8](
|
vec2 PCFkernel[4] = vec2[4](
|
||||||
vec2(-2.0, -2.0),
|
vec2(-1.5, 0.5),
|
||||||
vec2(2.0, -2.0),
|
vec2(0.5, 0.5),
|
||||||
vec2(2.0, 2.0),
|
vec2(-1.5, -1.5),
|
||||||
vec2(-2.0, 2.0),
|
vec2(0.5, -1.5)
|
||||||
vec2(1.0, 0.0),
|
|
||||||
vec2(0.0, 1.0),
|
|
||||||
vec2(-1.0, 0.0),
|
|
||||||
vec2(0.0, -1.0)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
float evalShadowAttenuationSampling(vec4 shadowTexcoord) {
|
float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) {
|
||||||
|
float pcfRadius = 3.0;
|
||||||
float shadowScale = getShadowScale();
|
float shadowScale = getShadowScale();
|
||||||
|
|
||||||
float shadowAttenuation = (0.25 * (
|
// Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[0], 0.0)) +
|
vec2 offset = pcfRadius * step(fract(position.xy), vec2(0.5, 0.5));
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[1], 0.0)) +
|
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[2], 0.0)) +
|
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[3], 0.0))
|
|
||||||
));
|
|
||||||
|
|
||||||
// Check for early bailing
|
float shadowAttenuation = (0.25 * (
|
||||||
if ((shadowAttenuation > 0) && (shadowAttenuation < 1.0)) {
|
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) +
|
||||||
shadowAttenuation = 0.5 * shadowAttenuation + (0.125 * (
|
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) +
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[4], 0.0)) +
|
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) +
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[5], 0.0)) +
|
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0))
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[6], 0.0)) +
|
));
|
||||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[7], 0.0))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return shadowAttenuation;
|
return shadowAttenuation;
|
||||||
}
|
}
|
||||||
|
|
||||||
float evalShadowAttenuation(vec4 position) {
|
float evalShadowAttenuation(vec4 position) {
|
||||||
vec4 shadowTexcoord = evalShadowTexcoord(position);
|
vec4 shadowTexcoord = evalShadowTexcoord(position);
|
||||||
|
if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 ||
|
||||||
|
shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0) {
|
||||||
|
// If a point is not in the map, do not attenuate
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
return evalShadowAttenuationSampling(shadowTexcoord);
|
return evalShadowAttenuationPCF(position, shadowTexcoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
layout(location = 0) out vec4 _fragColor;
|
layout(location = 0) out vec4 _fragColor;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
// stencil in solid color for debugging
|
// pass-through to set z-buffer
|
||||||
_fragColor = vec4(0.0, 0.0, 1.0, 1.0);
|
_fragColor = vec4(1.0, 1.0, 1.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
layout(location = 0) out vec4 _fragColor;
|
layout(location = 0) out vec4 _fragColor;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
// stencil in solid color for debugging
|
// pass-through to set z-buffer
|
||||||
_fragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
_fragColor = vec4(1.0, 1.0, 1.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,10 @@ RenderContext::RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao,
|
||||||
_drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect },
|
_drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect },
|
||||||
_items{ items }, _tone{ tone }, _ambientOcclusion{ ao } {}
|
_items{ items }, _tone{ tone }, _ambientOcclusion{ ao } {}
|
||||||
|
|
||||||
void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) {
|
void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned, bool shadowMap) {
|
||||||
_occlusionStatus = occlusion;
|
_occlusionStatus = occlusion;
|
||||||
_fxaaStatus = fxaa;
|
_fxaaStatus = fxaa;
|
||||||
|
_shadowMapStatus = shadowMap;
|
||||||
|
|
||||||
if (showOwned) {
|
if (showOwned) {
|
||||||
_drawStatus |= render::showNetworkStatusFlag;
|
_drawStatus |= render::showNetworkStatusFlag;
|
||||||
|
|
|
@ -100,7 +100,8 @@ public:
|
||||||
bool getDrawHitEffect() { return _drawHitEffect; }
|
bool getDrawHitEffect() { return _drawHitEffect; }
|
||||||
bool getOcclusionStatus() { return _occlusionStatus; }
|
bool getOcclusionStatus() { return _occlusionStatus; }
|
||||||
bool getFxaaStatus() { return _fxaaStatus; }
|
bool getFxaaStatus() { return _fxaaStatus; }
|
||||||
void setOptions(bool occlusion, bool fxaa, bool showOwned);
|
bool getShadowMapStatus() { return _shadowMapStatus; }
|
||||||
|
void setOptions(bool occlusion, bool fxaa, bool showOwned, bool shadowMap);
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
int _deferredDebugMode;
|
int _deferredDebugMode;
|
||||||
|
@ -114,6 +115,7 @@ protected:
|
||||||
bool _drawHitEffect;
|
bool _drawHitEffect;
|
||||||
bool _occlusionStatus { false };
|
bool _occlusionStatus { false };
|
||||||
bool _fxaaStatus { false };
|
bool _fxaaStatus { false };
|
||||||
|
bool _shadowMapStatus { false };
|
||||||
|
|
||||||
ItemsConfig _items;
|
ItemsConfig _items;
|
||||||
Tone _tone;
|
Tone _tone;
|
||||||
|
|
|
@ -236,6 +236,9 @@ void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const Rend
|
||||||
}
|
}
|
||||||
|
|
||||||
void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapesIDsBounds& inShapes, ShapesIDsBounds& outShapes) {
|
void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapesIDsBounds& inShapes, ShapesIDsBounds& outShapes) {
|
||||||
|
outShapes.clear();
|
||||||
|
outShapes.reserve(inShapes.size());
|
||||||
|
|
||||||
for (auto& pipeline : inShapes) {
|
for (auto& pipeline : inShapes) {
|
||||||
auto& inItems = pipeline.second;
|
auto& inItems = pipeline.second;
|
||||||
auto outItems = outShapes.find(pipeline.first);
|
auto outItems = outShapes.find(pipeline.first);
|
||||||
|
|
|
@ -478,3 +478,7 @@ AACube& AACube::operator += (const glm::vec3& point) {
|
||||||
|
|
||||||
return (*this);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AACube::containsNaN() const {
|
||||||
|
return isNaN(_corner) || isNaN(_scale);
|
||||||
|
}
|
||||||
|
|
|
@ -66,6 +66,8 @@ public:
|
||||||
|
|
||||||
AACube& operator += (const glm::vec3& point);
|
AACube& operator += (const glm::vec3& point);
|
||||||
|
|
||||||
|
bool containsNaN() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||||
|
|
|
@ -425,3 +425,33 @@ void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& seconda
|
||||||
vAxisOut = glm::cross(wAxisOut, uAxisOut);
|
vAxisOut = glm::cross(wAxisOut, uAxisOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec2 getFacingDir2D(const glm::quat& rot) {
|
||||||
|
glm::vec3 facing3D = rot * Vectors::UNIT_NEG_Z;
|
||||||
|
glm::vec2 facing2D(facing3D.x, facing3D.z);
|
||||||
|
const float ALMOST_ZERO = 0.0001f;
|
||||||
|
if (glm::length(facing2D) < ALMOST_ZERO) {
|
||||||
|
return glm::vec2(1.0f, 0.0f);
|
||||||
|
} else {
|
||||||
|
return glm::normalize(facing2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 getFacingDir2D(const glm::mat4& m) {
|
||||||
|
glm::vec3 facing3D = transformVector(m, Vectors::UNIT_NEG_Z);
|
||||||
|
glm::vec2 facing2D(facing3D.x, facing3D.z);
|
||||||
|
const float ALMOST_ZERO = 0.0001f;
|
||||||
|
if (glm::length(facing2D) < ALMOST_ZERO) {
|
||||||
|
return glm::vec2(1.0f, 0.0f);
|
||||||
|
} else {
|
||||||
|
return glm::normalize(facing2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNaN(glm::vec3 value) {
|
||||||
|
return isNaN(value.x) || isNaN(value.y) || isNaN(value.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNaN(glm::quat value) {
|
||||||
|
return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,4 +224,10 @@ glm::vec3 transformVector(const glm::mat4& m, const glm::vec3& v);
|
||||||
void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& secondaryAxis,
|
void generateBasisVectors(const glm::vec3& primaryAxis, const glm::vec3& secondaryAxis,
|
||||||
glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut);
|
glm::vec3& uAxisOut, glm::vec3& vAxisOut, glm::vec3& wAxisOut);
|
||||||
|
|
||||||
|
glm::vec2 getFacingDir2D(const glm::quat& rot);
|
||||||
|
glm::vec2 getFacingDir2D(const glm::mat4& m);
|
||||||
|
|
||||||
|
bool isNaN(glm::vec3 value);
|
||||||
|
bool isNaN(glm::quat value);
|
||||||
|
|
||||||
#endif // hifi_GLMHelpers_h
|
#endif // hifi_GLMHelpers_h
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
|
|
||||||
#include "PhysicsHelpers.h"
|
#include "PhysicsHelpers.h"
|
||||||
#include "NumericalConstants.h"
|
#include "NumericalConstants.h"
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include "PhysicsCollisionGroups.h"
|
||||||
|
|
||||||
// This chunk of code was copied from Bullet-2.82, so we include the Bullet license here:
|
// This chunk of code was copied from Bullet-2.82, so we include the Bullet license here:
|
||||||
/*
|
/*
|
||||||
|
@ -19,12 +22,12 @@
|
||||||
*
|
*
|
||||||
* This software is provided 'as-is', without any express or implied warranty.
|
* This software is provided 'as-is', without any express or implied warranty.
|
||||||
* In no event will the authors be held liable for any damages arising from the use of this software.
|
* In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
* including commercial applications, and to alter it and redistribute it freely,
|
* including commercial applications, and to alter it and redistribute it freely,
|
||||||
* subject to the following restrictions:
|
* subject to the following restrictions:
|
||||||
*
|
*
|
||||||
* 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
* 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||||||
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but
|
* If you use this software in a product, an acknowledgment in the product documentation would be appreciated but
|
||||||
* is not required.
|
* is not required.
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
@ -56,3 +59,33 @@ glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float time
|
||||||
}
|
}
|
||||||
return glm::quat(cosf(0.5f * speed * timeStep), axis.x, axis.y, axis.z);
|
return glm::quat(cosf(0.5f * speed * timeStep), axis.x, axis.y, axis.z);
|
||||||
}
|
}
|
||||||
|
/* end Bullet code derivation*/
|
||||||
|
|
||||||
|
int16_t Physics::getDefaultCollisionMask(int16_t group) {
|
||||||
|
switch(group) {
|
||||||
|
case BULLET_COLLISION_GROUP_STATIC:
|
||||||
|
return BULLET_COLLISION_MASK_STATIC;
|
||||||
|
case BULLET_COLLISION_GROUP_DYNAMIC:
|
||||||
|
return BULLET_COLLISION_MASK_DYNAMIC;
|
||||||
|
case BULLET_COLLISION_GROUP_KINEMATIC:
|
||||||
|
return BULLET_COLLISION_MASK_KINEMATIC;
|
||||||
|
case BULLET_COLLISION_GROUP_MY_AVATAR:
|
||||||
|
return BULLET_COLLISION_MASK_MY_AVATAR;
|
||||||
|
case BULLET_COLLISION_GROUP_OTHER_AVATAR:
|
||||||
|
return BULLET_COLLISION_MASK_OTHER_AVATAR;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
return BULLET_COLLISION_MASK_COLLISIONLESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid _sessionID;
|
||||||
|
|
||||||
|
void Physics::setSessionUUID(const QUuid& sessionID) {
|
||||||
|
_sessionID = sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUuid& Physics::getSessionUUID() {
|
||||||
|
return _sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
// TODO: move everything in here to the physics library after the physics/entities library
|
||||||
|
// dependency order is swapped.
|
||||||
|
|
||||||
const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 6; // Bullet will start to "lose time" at 10 FPS.
|
const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 6; // Bullet will start to "lose time" at 10 FPS.
|
||||||
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 90.0f;
|
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 90.0f;
|
||||||
|
@ -21,4 +25,11 @@ const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 90.0f;
|
||||||
// return incremental rotation (Bullet-style) caused by angularVelocity over timeStep
|
// return incremental rotation (Bullet-style) caused by angularVelocity over timeStep
|
||||||
glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float timeStep);
|
glm::quat computeBulletRotationStep(const glm::vec3& angularVelocity, float timeStep);
|
||||||
|
|
||||||
|
namespace Physics {
|
||||||
|
int16_t getDefaultCollisionMask(int16_t group);
|
||||||
|
|
||||||
|
void setSessionUUID(const QUuid& sessionID);
|
||||||
|
const QUuid& getSessionUUID();
|
||||||
|
};
|
||||||
|
|
||||||
#endif // hifi_PhysicsHelpers_h
|
#endif // hifi_PhysicsHelpers_h
|
||||||
|
|
|
@ -624,8 +624,8 @@ void debug::checkDeadBeef(void* memoryVoid, int size) {
|
||||||
assert(memcmp((unsigned char*)memoryVoid, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0);
|
assert(memcmp((unsigned char*)memoryVoid, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isNaN(float value) {
|
bool isNaN(float value) {
|
||||||
return value != value;
|
return value != value;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString formatUsecTime(float usecs, int prec) {
|
QString formatUsecTime(float usecs, int prec) {
|
||||||
|
|
|
@ -314,6 +314,11 @@ glm::vec3 SpatiallyNestable::getPosition(int jointIndex, bool& success) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setPosition(const glm::vec3& position, bool& success) {
|
void SpatiallyNestable::setPosition(const glm::vec3& position, bool& success) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (isNaN(position)) {
|
||||||
|
success = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
Transform parentTransform = getParentTransform(success);
|
Transform parentTransform = getParentTransform(success);
|
||||||
Transform myWorldTransform;
|
Transform myWorldTransform;
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
|
@ -358,6 +363,12 @@ glm::quat SpatiallyNestable::getOrientation(int jointIndex, bool& success) const
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setOrientation(const glm::quat& orientation, bool& success) {
|
void SpatiallyNestable::setOrientation(const glm::quat& orientation, bool& success) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (isNaN(orientation)) {
|
||||||
|
success = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Transform parentTransform = getParentTransform(success);
|
Transform parentTransform = getParentTransform(success);
|
||||||
Transform myWorldTransform;
|
Transform myWorldTransform;
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
|
@ -408,6 +419,10 @@ const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success) c
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setTransform(const Transform& transform, bool& success) {
|
void SpatiallyNestable::setTransform(const Transform& transform, bool& success) {
|
||||||
|
if (transform.containsNaN()) {
|
||||||
|
success = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
Transform parentTransform = getParentTransform(success);
|
Transform parentTransform = getParentTransform(success);
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
Transform::inverseMult(_transform, parentTransform, transform);
|
Transform::inverseMult(_transform, parentTransform, transform);
|
||||||
|
@ -432,6 +447,11 @@ glm::vec3 SpatiallyNestable::getScale(int jointIndex) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setScale(const glm::vec3& scale) {
|
void SpatiallyNestable::setScale(const glm::vec3& scale) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (isNaN(scale)) {
|
||||||
|
qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN";
|
||||||
|
return;
|
||||||
|
}
|
||||||
// TODO: scale
|
// TODO: scale
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
_transform.setScale(scale);
|
_transform.setScale(scale);
|
||||||
|
@ -448,6 +468,11 @@ const Transform SpatiallyNestable::getLocalTransform() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setLocalTransform(const Transform& transform) {
|
void SpatiallyNestable::setLocalTransform(const Transform& transform) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (transform.containsNaN()) {
|
||||||
|
qDebug() << "SpatiallyNestable::setLocalTransform -- transform contains NaN";
|
||||||
|
return;
|
||||||
|
}
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
_transform = transform;
|
_transform = transform;
|
||||||
});
|
});
|
||||||
|
@ -463,6 +488,11 @@ glm::vec3 SpatiallyNestable::getLocalPosition() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setLocalPosition(const glm::vec3& position) {
|
void SpatiallyNestable::setLocalPosition(const glm::vec3& position) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (isNaN(position)) {
|
||||||
|
qDebug() << "SpatiallyNestable::setLocalPosition -- position contains NaN";
|
||||||
|
return;
|
||||||
|
}
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
_transform.setTranslation(position);
|
_transform.setTranslation(position);
|
||||||
});
|
});
|
||||||
|
@ -478,6 +508,11 @@ glm::quat SpatiallyNestable::getLocalOrientation() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) {
|
void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (isNaN(orientation)) {
|
||||||
|
qDebug() << "SpatiallyNestable::setLocalOrientation -- orientation contains NaN";
|
||||||
|
return;
|
||||||
|
}
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
_transform.setRotation(orientation);
|
_transform.setRotation(orientation);
|
||||||
});
|
});
|
||||||
|
@ -494,6 +529,11 @@ glm::vec3 SpatiallyNestable::getLocalScale() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setLocalScale(const glm::vec3& scale) {
|
void SpatiallyNestable::setLocalScale(const glm::vec3& scale) {
|
||||||
|
// guard against introducing NaN into the transform
|
||||||
|
if (isNaN(scale)) {
|
||||||
|
qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN";
|
||||||
|
return;
|
||||||
|
}
|
||||||
// TODO: scale
|
// TODO: scale
|
||||||
_transformLock.withWriteLock([&] {
|
_transformLock.withWriteLock([&] {
|
||||||
_transform.setScale(scale);
|
_transform.setScale(scale);
|
||||||
|
@ -561,6 +601,10 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) {
|
void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) {
|
||||||
|
if (queryAACube.containsNaN()) {
|
||||||
|
qDebug() << "SpatiallyNestable::setQueryAACube -- cube contains NaN";
|
||||||
|
return;
|
||||||
|
}
|
||||||
_queryAACube = queryAACube;
|
_queryAACube = queryAACube;
|
||||||
if (queryAACube.getScale() > 0.0f) {
|
if (queryAACube.getScale() > 0.0f) {
|
||||||
_queryAACubeSet = true;
|
_queryAACubeSet = true;
|
||||||
|
|
|
@ -150,3 +150,7 @@ QJsonObject Transform::toJson(const Transform& transform) {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Transform::containsNaN() const {
|
||||||
|
return isNaN(_rotation) || isNaN(_scale) || isNaN(_translation);
|
||||||
|
}
|
||||||
|
|
|
@ -145,6 +145,8 @@ public:
|
||||||
Vec4 transform(const Vec4& pos) const;
|
Vec4 transform(const Vec4& pos) const;
|
||||||
Vec3 transform(const Vec3& pos) const;
|
Vec3 transform(const Vec3& pos) const;
|
||||||
|
|
||||||
|
bool containsNaN() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
enum Flag {
|
enum Flag {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <QtQml/QtQml>
|
#include <QtQml/QtQml>
|
||||||
#include <QtQuick/QQuickWindow>
|
#include <QtQuick/QQuickWindow>
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
#include <AbstractUriHandler.h>
|
#include <AbstractUriHandler.h>
|
||||||
#include <AccountManager.h>
|
#include <AccountManager.h>
|
||||||
|
@ -233,6 +234,58 @@ void OffscreenUi::setNavigationFocused(bool focused) {
|
||||||
offscreenFlags->setNavigationFocused(focused);
|
offscreenFlags->setNavigationFocused(focused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME HACK....
|
||||||
|
// This hack is an attempt to work around the 'offscreen UI can't gain keyboard focus' bug
|
||||||
|
// https://app.asana.com/0/27650181942747/83176475832393
|
||||||
|
// The problem seems related to https://bugreports.qt.io/browse/QTBUG-50309
|
||||||
|
//
|
||||||
|
// The workaround seems to be to give some other window (same process or another process doesn't seem to matter)
|
||||||
|
// focus and then put focus back on the interface main window.
|
||||||
|
//
|
||||||
|
// If I could reliably reproduce this bug I could eventually track down what state change is occuring
|
||||||
|
// during the process of the main window losing and then gaining focus, but failing that, here's a
|
||||||
|
// brute force way of triggering that state change at application start in a way that should be nearly
|
||||||
|
// imperceptible to the user.
|
||||||
|
class KeyboardFocusHack : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
KeyboardFocusHack() {
|
||||||
|
Q_ASSERT(_mainWindow);
|
||||||
|
QTimer::singleShot(200, [=] {
|
||||||
|
_hackWindow = new QWindow();
|
||||||
|
_hackWindow->setFlags(Qt::FramelessWindowHint);
|
||||||
|
_hackWindow->setGeometry(_mainWindow->x(), _mainWindow->y(), 10, 10);
|
||||||
|
_hackWindow->show();
|
||||||
|
_hackWindow->requestActivate();
|
||||||
|
QTimer::singleShot(200, [=] {
|
||||||
|
_hackWindow->hide();
|
||||||
|
_hackWindow->deleteLater();
|
||||||
|
_hackWindow = nullptr;
|
||||||
|
_mainWindow->requestActivate();
|
||||||
|
this->deleteLater();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static QWindow* findMainWindow() {
|
||||||
|
auto windows = qApp->topLevelWindows();
|
||||||
|
QWindow* result = nullptr;
|
||||||
|
for (auto window : windows) {
|
||||||
|
QVariant isMainWindow = window->property("MainWindow");
|
||||||
|
if (!qobject_cast<QQuickWindow*>(window)) {
|
||||||
|
result = window;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWindow* const _mainWindow { findMainWindow() };
|
||||||
|
QWindow* _hackWindow { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
void OffscreenUi::createDesktop(const QUrl& url) {
|
void OffscreenUi::createDesktop(const QUrl& url) {
|
||||||
if (_desktop) {
|
if (_desktop) {
|
||||||
qDebug() << "Desktop already created";
|
qDebug() << "Desktop already created";
|
||||||
|
@ -254,6 +307,8 @@ void OffscreenUi::createDesktop(const QUrl& url) {
|
||||||
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
|
||||||
|
|
||||||
new VrMenu(this);
|
new VrMenu(this);
|
||||||
|
|
||||||
|
new KeyboardFocusHack();
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickItem* OffscreenUi::getDesktop() {
|
QQuickItem* OffscreenUi::getDesktop() {
|
||||||
|
|
|
@ -52,6 +52,7 @@ ul.tabs li.current {
|
||||||
display: none;
|
display: none;
|
||||||
background-color: #ededed;
|
background-color: #ededed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -63,7 +64,7 @@ ul.tabs li.current {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: #ededed;
|
background-color: #ededed;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +82,11 @@ ul.tabs li.current {
|
||||||
}
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
display: none;
|
|
||||||
float: right;
|
float: right;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.log-line {
|
||||||
|
font-family: "Consolas", monospace;
|
||||||
|
font-size: 9.5pt;
|
||||||
|
}
|
||||||
|
|
|
@ -154,9 +154,7 @@ ready = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendLogMessage(pid, msg, name) {
|
function appendLogMessage(pid, msg, name) {
|
||||||
console.log(pid, msg, name);
|
var id = name == "ds" ? "domain-server" : "assignment-client";
|
||||||
var id = "pid-" + pid;
|
|
||||||
id = name == "ds" ? "domain-server" : "assignment-client";
|
|
||||||
var $pidLog = $('#' + id);
|
var $pidLog = $('#' + id);
|
||||||
|
|
||||||
var size = ++tabStates[id].size;
|
var size = ++tabStates[id].size;
|
||||||
|
@ -167,8 +165,7 @@ ready = function() {
|
||||||
|
|
||||||
var wasAtBottom = false;
|
var wasAtBottom = false;
|
||||||
if (currentTab == id) {
|
if (currentTab == id) {
|
||||||
var padding = 15;
|
wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height());
|
||||||
wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height() - (2 * padding));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var $logLine = $('<div class="log-line">').text(msg);
|
var $logLine = $('<div class="log-line">').text(msg);
|
||||||
|
|
|
@ -23,9 +23,10 @@ exports.searchPaths = function(name, binaryType, releaseType) {
|
||||||
|
|
||||||
if (!releaseType) {
|
if (!releaseType) {
|
||||||
// check in the developer build tree for binaries
|
// check in the developer build tree for binaries
|
||||||
|
var typeSpecificPath = (binaryType == "local-release" ? "Release/" : "Debug/")
|
||||||
paths = [
|
paths = [
|
||||||
devBasePath + name + extension,
|
devBasePath + name + extension,
|
||||||
devBasePath + (binaryType == "local-release" ? "Release/" : "Debug/") + name + extension
|
devBasePath + typeSpecificPath + name + extension
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
// check directly beside the binary
|
// check directly beside the binary
|
||||||
|
@ -33,6 +34,11 @@ exports.searchPaths = function(name, binaryType, releaseType) {
|
||||||
path.join(path.dirname(process.execPath), name + extension)
|
path.join(path.dirname(process.execPath), name + extension)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (process.platform == "win32") {
|
||||||
|
// check a level back in case we're packaged on windows
|
||||||
|
paths.push(path.resolve(path.dirname(process.execPath), '../' + name + extension))
|
||||||
|
}
|
||||||
|
|
||||||
// assume we're inside an app bundle on OS X
|
// assume we're inside an app bundle on OS X
|
||||||
if (process.platform == "darwin") {
|
if (process.platform == "darwin") {
|
||||||
var contentPath = ".app/Contents/";
|
var contentPath = ".app/Contents/";
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <BulletUtil.h>
|
#include <BulletUtil.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
// Add additional qtest functionality (the include order is important!)
|
// Add additional qtest functionality (the include order is important!)
|
||||||
#include "../QTestExtensions.h"
|
#include "../QTestExtensions.h"
|
||||||
|
@ -46,12 +47,49 @@ void BulletUtilTests::fromBulletToGLM() {
|
||||||
QCOMPARE(gQ.y, bQ.getY());
|
QCOMPARE(gQ.y, bQ.getY());
|
||||||
QCOMPARE(gQ.z, bQ.getZ());
|
QCOMPARE(gQ.z, bQ.getZ());
|
||||||
QCOMPARE(gQ.w, bQ.getW());
|
QCOMPARE(gQ.w, bQ.getW());
|
||||||
|
|
||||||
|
// mat4
|
||||||
|
btMatrix3x3 basis(bQ);
|
||||||
|
btVector3 origin(100.0f, 200.0f, 300.0f);
|
||||||
|
btTransform bM(basis, origin);
|
||||||
|
|
||||||
|
glm::mat4 gM = bulletToGLM(bM);
|
||||||
|
|
||||||
|
glm::vec3 translation = extractTranslation(gM);
|
||||||
|
QCOMPARE(translation.x, bM.getOrigin().getX());
|
||||||
|
QCOMPARE(translation.y, bM.getOrigin().getY());
|
||||||
|
QCOMPARE(translation.z, bM.getOrigin().getZ());
|
||||||
|
|
||||||
|
glm::quat rotation = glmExtractRotation(gM);
|
||||||
|
QCOMPARE(rotation.x, bM.getRotation().getX());
|
||||||
|
QCOMPARE(rotation.y, bM.getRotation().getY());
|
||||||
|
QCOMPARE(rotation.z, bM.getRotation().getZ());
|
||||||
|
QCOMPARE(rotation.w, bM.getRotation().getW());
|
||||||
|
|
||||||
|
// As a sanity check, transform vectors by their corresponding matrices and compare the result.
|
||||||
|
btVector3 bX = bM * btVector3(1.0f, 0.0f, 0.0f);
|
||||||
|
btVector3 bY = bM * btVector3(0.0f, 1.0f, 0.0f);
|
||||||
|
btVector3 bZ = bM * btVector3(0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
glm::vec3 gX = transformPoint(gM, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
glm::vec3 gY = transformPoint(gM, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
glm::vec3 gZ = transformPoint(gM, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
QCOMPARE(gX.x, bX.getX());
|
||||||
|
QCOMPARE(gX.y, bX.getY());
|
||||||
|
QCOMPARE(gX.z, bX.getZ());
|
||||||
|
QCOMPARE(gY.x, bY.getX());
|
||||||
|
QCOMPARE(gY.y, bY.getY());
|
||||||
|
QCOMPARE(gY.z, bY.getZ());
|
||||||
|
QCOMPARE(gZ.x, bZ.getX());
|
||||||
|
QCOMPARE(gZ.y, bZ.getY());
|
||||||
|
QCOMPARE(gZ.z, bZ.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BulletUtilTests::fromGLMToBullet() {
|
void BulletUtilTests::fromGLMToBullet() {
|
||||||
glm::vec3 gV(1.23f, 4.56f, 7.89f);
|
glm::vec3 gV(1.23f, 4.56f, 7.89f);
|
||||||
btVector3 bV = glmToBullet(gV);
|
btVector3 bV = glmToBullet(gV);
|
||||||
|
|
||||||
QCOMPARE(gV.x, bV.getX());
|
QCOMPARE(gV.x, bV.getX());
|
||||||
QCOMPARE(gV.y, bV.getY());
|
QCOMPARE(gV.y, bV.getY());
|
||||||
QCOMPARE(gV.z, bV.getZ());
|
QCOMPARE(gV.z, bV.getZ());
|
||||||
|
@ -66,4 +104,60 @@ void BulletUtilTests::fromGLMToBullet() {
|
||||||
QCOMPARE(gQ.y, bQ.getY());
|
QCOMPARE(gQ.y, bQ.getY());
|
||||||
QCOMPARE(gQ.z, bQ.getZ());
|
QCOMPARE(gQ.z, bQ.getZ());
|
||||||
QCOMPARE(gQ.w, bQ.getW());
|
QCOMPARE(gQ.w, bQ.getW());
|
||||||
|
|
||||||
|
// mat3
|
||||||
|
glm::mat3 gM3 = glm::mat3_cast(gQ);
|
||||||
|
btMatrix3x3 bM3 = glmToBullet(gM3);
|
||||||
|
bM3.getRotation(bQ);
|
||||||
|
QCOMPARE(gQ.x, bQ.getX());
|
||||||
|
QCOMPARE(gQ.y, bQ.getY());
|
||||||
|
QCOMPARE(gQ.z, bQ.getZ());
|
||||||
|
QCOMPARE(gQ.w, bQ.getW());
|
||||||
|
|
||||||
|
// mat4
|
||||||
|
glm::mat4 gM4 = createMatFromQuatAndPos(gQ, gV);
|
||||||
|
btTransform bM4 = glmToBullet(gM4);
|
||||||
|
bQ = bM4.getRotation();
|
||||||
|
bV = bM4.getOrigin();
|
||||||
|
QCOMPARE(gQ.x, bQ.getX());
|
||||||
|
QCOMPARE(gQ.y, bQ.getY());
|
||||||
|
QCOMPARE(gQ.z, bQ.getZ());
|
||||||
|
QCOMPARE(gQ.w, bQ.getW());
|
||||||
|
QCOMPARE(gV.x, bV.getX());
|
||||||
|
QCOMPARE(gV.y, bV.getY());
|
||||||
|
QCOMPARE(gV.z, bV.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BulletUtilTests::rotateVectorTest() {
|
||||||
|
|
||||||
|
float angle = 0.317f * PI;
|
||||||
|
btVector3 axis(1.23f, 2.34f, 3.45f);
|
||||||
|
axis.normalize();
|
||||||
|
btQuaternion q(axis, angle);
|
||||||
|
|
||||||
|
btVector3 xAxis(1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
btVector3 result0 = rotateVector(q, xAxis);
|
||||||
|
|
||||||
|
btTransform m(q);
|
||||||
|
btVector3 result1 = m * xAxis;
|
||||||
|
|
||||||
|
QCOMPARE(result0.getX(), result0.getX());
|
||||||
|
QCOMPARE(result0.getY(), result1.getY());
|
||||||
|
QCOMPARE(result0.getZ(), result1.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BulletUtilTests::clampLengthTest() {
|
||||||
|
btVector3 vec(1.0f, 3.0f, 2.0f);
|
||||||
|
btVector3 clampedVec1 = clampLength(vec, 1.0f);
|
||||||
|
btVector3 clampedVec2 = clampLength(vec, 2.0f);
|
||||||
|
btVector3 normalizedVec = vec.normalized();
|
||||||
|
|
||||||
|
QCOMPARE(clampedVec1.getX(), normalizedVec.getX());
|
||||||
|
QCOMPARE(clampedVec1.getY(), normalizedVec.getY());
|
||||||
|
QCOMPARE(clampedVec1.getZ(), normalizedVec.getZ());
|
||||||
|
|
||||||
|
QCOMPARE(clampedVec2.getX(), normalizedVec.getX() * 2.0f);
|
||||||
|
QCOMPARE(clampedVec2.getY(), normalizedVec.getY() * 2.0f);
|
||||||
|
QCOMPARE(clampedVec2.getZ(), normalizedVec.getZ() * 2.0f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ class BulletUtilTests : public QObject {
|
||||||
private slots:
|
private slots:
|
||||||
void fromBulletToGLM();
|
void fromBulletToGLM();
|
||||||
void fromGLMToBullet();
|
void fromGLMToBullet();
|
||||||
|
void rotateVectorTest();
|
||||||
|
void clampLengthTest();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_BulletUtilTests_h
|
#endif // hifi_BulletUtilTests_h
|
||||||
|
|
Loading…
Reference in a new issue